From d381a5bf95275878595d7a54e3a06b695420a487 Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Wed, 1 Nov 2017 16:41:54 -0700 Subject: [PATCH 01/11] WIP - Add support fo CH341 Serial boards Added support for a another Serial type CH341. Wip - But I do have it working so far at least with a loop back test (RX shorted to TX) Also Rearranged some of the Claim code and added a VID:DID to Serial Type table that I use to map, such that I know some of these devices have multiple valid setups. Will test more. I have several more cheap USB to serial adapter arriving tomorrow. --- USBHost_t36.h | 18 +- serial.cpp | 453 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 354 insertions(+), 117 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index 90355a9..127528a 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -56,7 +56,7 @@ // your best effort to read chapter 4 before asking USB questions! -//#define USBHOST_PRINT_DEBUG +#define USBHOST_PRINT_DEBUG /************************************************/ /* Data Types */ @@ -889,7 +889,9 @@ private: //-------------------------------------------------------------------------- class USBSerial: public USBDriver, public Stream { -public: + public: + + // FIXME: need different USBSerial, with bigger buffers for 480 Mbit & faster speed enum { BUFFER_SIZE = 648 }; // must hold at least 6 max size packets, plus 2 extra bytes USBSerial(USBHost &host) : txtimer(this) { init(); } @@ -916,6 +918,7 @@ private: void init(); static bool check_rxtx_ep(uint32_t &rxep, uint32_t &txep); bool init_buffers(uint32_t rsize, uint32_t tsize); + void ch341_setBaud(uint8_t byte_index); private: Pipe_t mypipes[3] __attribute__ ((aligned(32))); Transfer_t mytransfers[7] __attribute__ ((aligned(32))); @@ -947,7 +950,16 @@ private: uint8_t pl2303_v2; uint8_t interface; bool control_queued; - enum { CDCACM, FTDI, PL2303, CH341 } sertype; + typedef enum { UNKNOWN=0, CDCACM, FTDI, PL2303, CH341 } sertype_t; + sertype_t sertype; + + typedef struct { + uint16_t idVendor; + uint16_t idProduct; + sertype_t sertype; + } product_vendor_mapping_t; + static product_vendor_mapping_t pid_vid_mapping[]; + }; //-------------------------------------------------------------------------- diff --git a/serial.cpp b/serial.cpp index ffd7893..3353c87 100644 --- a/serial.cpp +++ b/serial.cpp @@ -28,14 +28,21 @@ #define println USBHost::println_ /************************************************************/ -// Control Transfer For Configuration +// Define mapping VID/PID - to Serial Device type. /************************************************************/ -typedef struct { - uint32_t dwDTERate; // Data Terminal Rate in bits per second - uint8_t bCharFormat; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits - uint8_t bParityType; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space - uint8_t bDataBits; // Data bits (5, 6, 7, 8 or 16) -} LINE_CODING; +USBSerial::product_vendor_mapping_t USBSerial::pid_vid_mapping[] = { + // FTDI mappings. + {0x0403, 0x6001, USBSerial::FTDI}, + + // PL2303 + {0x67B,0x2303, USBSerial::PL2303}, + + // CH341 + {0x4348, 0x5523, USBSerial::CH341 }, + {0x1a86, 0x7523, USBSerial::CH341 }, + {0x1a86, 0x5523, USBSerial::CH341 } +}; + /************************************************************/ // Initialization and claiming of devices & interfaces @@ -60,51 +67,9 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 println(", bDeviceProtocol = ", dev->bDeviceProtocol); print_hexbytes(descriptors, len); if (type == 0) { - if (dev->idVendor == 0x0403 && dev->idProduct == 0x6001) { - // FTDI FT232 - println("len = ", len); - if (len < 23) return false; - if (descriptors[0] != 9) return false; // length 9 - if (descriptors[9] != 7) return false; // length 7 - if (descriptors[10] != 5) return false; // ep desc - uint32_t rxep = descriptors[11]; - if (descriptors[12] != 2) return false; // bulk type - if (descriptors[13] != 64) return false; // size 64 - if (descriptors[14] != 0) return false; - if (descriptors[16] != 7) return false; // length 7 - if (descriptors[17] != 5) return false; // ep desc - uint32_t txep = descriptors[18]; - if (descriptors[19] != 2) return false; // bulk type - if (descriptors[20] != 64) return false; // size 64 - if (descriptors[21] != 0) return false; - if (!check_rxtx_ep(rxep, txep)) return false; - print("FTDI, rxep=", rxep & 15); - println(", txep=", txep); - if (!init_buffers(64, 64)) return false; - rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); - if (!rxpipe) return false; - txpipe = new_Pipe(dev, 2, txep, 0, 64); - if (!txpipe) { - // TODO: free rxpipe - return false; - } - sertype = FTDI; - rxpipe->callback_function = rx_callback; - queue_Data_Transfer(rxpipe, rx1, 64, this); - rxstate = 1; - if (rxsize > 128) { - queue_Data_Transfer(rxpipe, rx2, 64, this); - rxstate = 3; - } - txstate = 0; - txpipe->callback_function = tx_callback; - baudrate = 115200; - pending_control = 0x0F; - mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port - queue_Control_Transfer(dev, &setup, NULL, this); - control_queued = true; - return true; - } else if ((dev->bDeviceClass == 2) && (dev->bDeviceSubClass == 0)) { + //--------------------------------------------------------------------- + // CDCACM + if ((dev->bDeviceClass == 2) && (dev->bDeviceSubClass == 0)) { // It is a communication device see if we can extract the data... // Try some ttyACM types? // This code may be similar to MIDI code. @@ -226,72 +191,198 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 return true; } - // TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them - if (dev->idVendor == 0x67B && dev->idProduct == 0x2303) { - // Prolific Technology, Inc. PL2303 Serial Port - println("len = ", len); - uint8_t count_end_points = descriptors[4]; - if (count_end_points < 2) return false; // not enough end points - if (len < 23) return false; - if (descriptors[0] != 9) return false; // length 9 - - // Lets walk through end points and see if we - // can find an RX and TX bulk transfer end point. - //vid=67B, pid=2303 - // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 - //09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00 - uint32_t rxep = 0; - uint32_t txep = 0; - uint32_t descriptor_index = 9; - while (count_end_points-- && ((rxep == 0) || txep == 0)) { - if (descriptors[descriptor_index] != 7) return false; // length 7 - if (descriptors[descriptor_index+1] != 5) return false; // ep desc - if ((descriptors[descriptor_index+3] == 2) - && (descriptors[descriptor_index+4] == 64) - && (descriptors[descriptor_index+5] == 0)) { - // have a bulk EP size - if (descriptors[descriptor_index+2] & 0x80 ) { - rxep = descriptors[descriptor_index+2]; - } else { - txep = descriptors[descriptor_index+2]; - } + // See if the vendor_id:product_id is in our list of products. + sertype = UNKNOWN; + for (uint8_t i = 0; i < (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0])); i++) { + if ((dev->idVendor == pid_vid_mapping[i].idVendor) && (dev->idProduct == pid_vid_mapping[i].idProduct)) { + sertype = pid_vid_mapping[i].sertype; + break; + } + } + switch (sertype) { + //--------------------------------------------------------------------- + // FTDI + case FTDI: + { + // FTDI FT232 + println("len = ", len); + if (len < 23) return false; + if (descriptors[0] != 9) return false; // length 9 + if (descriptors[9] != 7) return false; // length 7 + if (descriptors[10] != 5) return false; // ep desc + uint32_t rxep = descriptors[11]; + if (descriptors[12] != 2) return false; // bulk type + if (descriptors[13] != 64) return false; // size 64 + if (descriptors[14] != 0) return false; + if (descriptors[16] != 7) return false; // length 7 + if (descriptors[17] != 5) return false; // ep desc + uint32_t txep = descriptors[18]; + if (descriptors[19] != 2) return false; // bulk type + if (descriptors[20] != 64) return false; // size 64 + if (descriptors[21] != 0) return false; + if (!check_rxtx_ep(rxep, txep)) return false; + print("FTDI, rxep=", rxep & 15); + println(", txep=", txep); + if (!init_buffers(64, 64)) return false; + rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); + if (!rxpipe) return false; + txpipe = new_Pipe(dev, 2, txep, 0, 64); + if (!txpipe) { + // TODO: free rxpipe + return false; } - descriptor_index += 7; // setup to look at next one... - } - // Try to verify the end points. - if (!check_rxtx_ep(rxep, txep)) return false; - print("FTDI, rxep=", rxep & 15); - println(", txep=", txep); - if (!init_buffers(64, 64)) return false; - rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); - if (!rxpipe) return false; - txpipe = new_Pipe(dev, 2, txep, 0, 64); - if (!txpipe) { - // TODO: free rxpipe - return false; + sertype = FTDI; + rxpipe->callback_function = rx_callback; + rxsize = 64; + queue_Data_Transfer(rxpipe, rx1, 64, this); + rxstate = 1; + txsize = 64; + txstate = 0; + txpipe->callback_function = tx_callback; + baudrate = 115200; + pending_control = 0x0F; + mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port + queue_Control_Transfer(dev, &setup, NULL, this); + control_queued = true; + return true; } + //------------------------------------------------------------------------ + // Prolific + // TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them + case PL2303: + { + // Prolific Technology, Inc. PL2303 Serial Port + println("len = ", len); + uint8_t count_end_points = descriptors[4]; + if (count_end_points < 2) return false; // not enough end points + if (len < 23) return false; + if (descriptors[0] != 9) return false; // length 9 - sertype = PL2303; - rxpipe->callback_function = rx_callback; - queue_Data_Transfer(rxpipe, rx1, 64, this); - rxstate = 1; - if (rxsize > 128) { - queue_Data_Transfer(rxpipe, rx2, 64, this); - rxstate = 3; - } - txstate = 0; - txpipe->callback_function = tx_callback; - baudrate = 115200; + // Lets walk through end points and see if we + // can find an RX and TX bulk transfer end point. + //vid=67B, pid=2303 + // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 + //09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00 + uint32_t rxep = 0; + uint32_t txep = 0; + uint32_t descriptor_index = 9; + while (count_end_points-- && ((rxep == 0) || txep == 0)) { + if (descriptors[descriptor_index] != 7) return false; // length 7 + if (descriptors[descriptor_index+1] != 5) return false; // ep desc + if ((descriptors[descriptor_index+3] == 2) + && (descriptors[descriptor_index+4] == 64) + && (descriptors[descriptor_index+5] == 0)) { + // have a bulk EP size + if (descriptors[descriptor_index+2] & 0x80 ) { + rxep = descriptors[descriptor_index+2]; + rxsize = descriptors[descriptor_index+4]; + } else { + txep = descriptors[descriptor_index+2]; + txsize = descriptors[descriptor_index+4]; + } + } + descriptor_index += 7; // setup to look at next one... + } + // Try to verify the end points. + if (!check_rxtx_ep(rxep, txep)) return false; + print("PL2303, rxep=", rxep & 15); + println(", txep=", txep); + if (!init_buffers(64, 64)) return false; + rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); + if (!rxpipe) return false; + txpipe = new_Pipe(dev, 2, txep, 0, 64); + if (!txpipe) { + // TODO: free rxpipe + return false; + } - // Lets see if it will handle the same CDCACM - messages? - println("PL2303: readRegister(0x04)"); - // Need to setup the data the line coding data - mk_setup(setup, 0xC0, 0x1, 0x8484, 0, 1); - queue_Control_Transfer(dev, &setup, setupdata, this); - control_queued = true; - setup_state = 1; // We are at step one of setup... - pending_control = 0x3f; // Maybe don't need to do... - return true; + sertype = PL2303; + rxpipe->callback_function = rx_callback; + queue_Data_Transfer(rxpipe, rx1, 64, this); + rxstate = 1; + txstate = 0; + txpipe->callback_function = tx_callback; + baudrate = 115200; + + // Lets see if it will handle the same CDCACM - messages? + println("PL2303: readRegister(0x04)"); + // Need to setup the data the line coding data + mk_setup(setup, 0xC0, 0x1, 0x8484, 0, 1); + queue_Control_Transfer(dev, &setup, setupdata, this); + control_queued = true; + setup_state = 1; // We are at step one of setup... + pending_control = 0x3f; + return true; + } + //------------------------------------------------------------------------ + // CH341 + case CH341: + { + println("len = ", len); + uint8_t count_end_points = descriptors[4]; + if (count_end_points < 2) return false; // not enough end points + if (len < 23) return false; + if (descriptors[0] != 9) return false; // length 9 + + // Lets walk through end points and see if we + // can find an RX and TX bulk transfer end point. + // vid=1A86, pid=7523, bDeviceClass = 255, bDeviceSubClass = 0, bDeviceProtocol = 0 + // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 + //09 04 00 00 03 FF 01 02 00 07 05 82 02 20 00 00 07 05 02 02 20 00 00 07 05 81 03 08 00 01 + uint32_t rxep = 0; + uint32_t txep = 0; + uint32_t descriptor_index = 9; + while (count_end_points-- && ((rxep == 0) || txep == 0)) { + if (descriptors[descriptor_index] != 7) return false; // length 7 + if (descriptors[descriptor_index+1] != 5) return false; // ep desc + if ((descriptors[descriptor_index+3] == 2) + && (descriptors[descriptor_index+4] <= 64) + && (descriptors[descriptor_index+5] == 0)) { + // have a bulk EP size + if (descriptors[descriptor_index+2] & 0x80 ) { + rxep = descriptors[descriptor_index+2]; + rxsize = descriptors[descriptor_index+4]; + } else { + txep = descriptors[descriptor_index+2]; + txsize = descriptors[descriptor_index+4]; + } + } + descriptor_index += 7; // setup to look at next one... + } + // Try to verify the end points. + if (!check_rxtx_ep(rxep, txep)) return false; + print("ch341, rxep=", rxep & 15); + println(", txep=", txep); + if (!init_buffers(rxsize, txsize)) return false; + rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rxsize); + if (!rxpipe) return false; + txpipe = new_Pipe(dev, 2, txep, 0, txsize); + if (!txpipe) { + // TODO: free rxpipe + return false; + } + + rxpipe->callback_function = rx_callback; + queue_Data_Transfer(rxpipe, rx1, rxsize, this); + rxstate = 1; + txstate = 0; + txpipe->callback_function = tx_callback; + baudrate = 115200; + + // Lets see if it will handle the same CDCACM - messages? + println("CH341: 0xC0, 0x5f, 0, 0, 8"); + // Need to setup the data the line coding data + mk_setup(setup, 0xC0, 0x5f, 0, 0, sizeof(setupdata)); + queue_Control_Transfer(dev, &setup, setupdata, this); + control_queued = true; + setup_state = 1; // We are at step one of setup... + pending_control = 0x3f; + return true; + } + //------------------------------------------------------------------------ + // PID:VID - not in our product list. + default: + return false; } } else if (type != 1) return false; // TTYACM: @@ -653,6 +744,140 @@ void USBSerial::control(const Transfer_t *transfer) control_queued = true; } } + + if (sertype == CH341) { + print(" Transfer: "); + print_hexbytes(&transfer->setup, sizeof(setup_t)); + if (transfer->length) { + print(" data: "); + print_hexbytes(transfer->buffer, transfer->length); + } + if (pending_control & 1) { + // Still in larger setup state mode + switch (setup_state) { + case 1: + print(" Returned: "); + print_hexbytes(transfer->buffer, transfer->length); + println("CH341: 40, a1, 0, 0, 0"); + mk_setup(setup, 0x40, 0xa1, 0, 0, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + setup_state = 2; + control_queued = true; + return; + case 2: + ch341_setBaud(0); // send the first byte of the baud rate + control_queued = true; + setup_state = 3; + return; + case 3: + ch341_setBaud(1); // send the second byte of the baud rate + control_queued = true; + setup_state = 4; + return; + case 4: + println("CH341: c0, 95, 2518, 0, 8"); + mk_setup(setup, 0xc0, 0x95, 0x2518, 0, sizeof(setup)); // + queue_Control_Transfer(device, &setup, setupdata, this); + setup_state = 5; + control_queued = true; + return; + case 5: + print(" Returned: "); + print_hexbytes(transfer->buffer, transfer->length); + println("CH341: 40, 0x9a, 0x2518, 0x0050, 0"); + mk_setup(setup, 0x40, 0x9a, 0x2518, 0x0050, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + setup_state = 6; + control_queued = true; + return; + case 6: + println("CH341: c0, 95, 0x706, 0, 8 - get status"); + mk_setup(setup, 0xc0, 0x95, 0x706, 0, sizeof(setup)); // + queue_Control_Transfer(device, &setup, setupdata, this); + setup_state = 7; + control_queued = true; + return; + case 7: + print(" Returned: "); + print_hexbytes(transfer->buffer, transfer->length); + println("CH341: 40, 0xa1, 0x501f, 0xd90a, 0"); + mk_setup(setup, 0x40, 0xa1, 0x501f, 0xd90a, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + setup_state = 8; + control_queued = true; + break; + } + pending_control &= ~1; // We are finally going to leave this list and join the rest + if (control_queued) return; + } + // set baud rate + if (pending_control & 2) { + pending_control &= ~2; + ch341_setBaud(0); // send the first byte of the baud rate + control_queued = true; + return; + } + if (pending_control & 4) { + pending_control &= ~4; + ch341_setBaud(1); // send the first byte of the baud rate + control_queued = true; + return; + } + if (pending_control & 8) { + pending_control &= ~8; + // This is setting handshake need to figure out what... + println("CH341: 0x40, 0xa4, 0xff9f, 0, 0 - Handshake"); + mk_setup(setup, 0x40, 0xa4, 0xff9f, 0, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } + if (pending_control & 0x10) { + pending_control &= ~0x10; + // This is setting handshake need to figure out what... + println("CH341: c0, 95, 0x706, 0, 8 - get status"); + mk_setup(setup, 0xc0, 0x95, 0x706, 0, sizeof(setup)); // + queue_Control_Transfer(device, &setup, setupdata, this); + control_queued = true; + return; + } + if (pending_control & 0x30) { + pending_control &= ~0x30; + print(" Returned: "); + print_hexbytes(transfer->buffer, transfer->length); + return; + } + } +} + +#define CH341_BAUDBASE_FACTOR 1532620800 +#define CH341_BAUDBASE_DIVMAX 3 +void USBSerial::ch341_setBaud(uint8_t byte_index) { + uint16_t a, b; + uint32_t factor; + uint16_t divisor; + + factor = (CH341_BAUDBASE_FACTOR / baudrate); + divisor = CH341_BAUDBASE_DIVMAX; + + while ((factor > 0xfff0) && divisor) { + factor >>= 3; + divisor--; + } + + factor = 0x10000 - factor; + a = (factor & 0xff00) | divisor; + b = factor & 0xff; + + if (byte_index == 0) { + println("CH341: 40, 0x9a, 0x1312... (Baud byte 0):", a, HEX); + mk_setup(setup, 0x40, 0x9a, 0x1312, a, 0); // + } else { + println("CH341: 40, 0x9a, 0x0f2c... (Baud byte 1):", b, HEX); + mk_setup(setup, 0x40, 0x9a, 0x0f2c, b, 0); // + } + queue_Control_Transfer(device, &setup, setupdata, this); + control_queued = true; } From e2eba5c00da1be972f7b179974f4002af872a4b6 Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Wed, 1 Nov 2017 16:41:54 -0700 Subject: [PATCH 02/11] This commit has a few different parts. It is still a WIP This commit adds support for at least some of the Serial boards with the CH341 chipset, tested using a board from sparkfun as well as one from Amazon.com The code was rearranged some of the Claim code and added a VID:DID to Serial Type table that I use to map, such that I know some of these devices have multiple valid setups. Support Begin/End/Begin - Change baud Added code to support switching baud rates, or more particular be able to call end() and then call begin(...) with same or different baud rate. Hopefully some support for also releasing DTR when the end is called. WIP - Start support for the begin(baud, format) Adding some support to the code to handle some of the different possible format capabilities. In particualar trying to handle the parity, number of bits and number of stop bits. hacked up test app, such that if you type in command line like: "#9600, 7e1" It will extract the 9600 as the new baud and try to use format 7e1. I only hard coded a few of these in the test app (8n1, 7e1, 7e2, 8n2)... Again work in progress. Took me awhile tofigure out how to do this for ch341 boards as I did not see any documents or code that handled this. So had to deduce it from differences in USB packets. --- USBHost_t36.h | 38 +- examples/SerialTest/SerialTest.ino | 83 ++++- serial.cpp | 572 ++++++++++++++++++++++------- 3 files changed, 545 insertions(+), 148 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index 90355a9..e96ea87 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -56,7 +56,7 @@ // your best effort to read chapter 4 before asking USB questions! -//#define USBHOST_PRINT_DEBUG +#define USBHOST_PRINT_DEBUG /************************************************/ /* Data Types */ @@ -91,6 +91,7 @@ class USBDriverTimer; /************************************************/ /* Added Defines */ /************************************************/ +// Keyboard special Keys #define KEYD_UP 0xDA #define KEYD_DOWN 0xD9 #define KEYD_LEFT 0xD8 @@ -114,6 +115,22 @@ class USBDriverTimer; #define KEYD_F11 0xCC #define KEYD_F12 0xCD + +// USBSerial formats - Lets encode format into bits +// Bits: 0-4 - Number of data bits +// Bits: 5-7 - Parity (0=none, 1=odd, 2 = even) +// bits: 8-9 - Stop bits. 0=1, 1=2 + + +#define USBHOST_SERIAL_7E1 0x027 +#define USBHOST_SERIAL_7O1 0x047 +#define USBHOST_SERIAL_8N1 0x08 +#define USBHOST_SERIAL_8N2 0x108 +#define USBHOST_SERIAL_8E1 0x028 +#define USBHOST_SERIAL_8O1 0x048 + + + /************************************************/ /* Data Structure Definitions */ /************************************************/ @@ -889,11 +906,13 @@ private: //-------------------------------------------------------------------------- class USBSerial: public USBDriver, public Stream { -public: + public: + + // FIXME: need different USBSerial, with bigger buffers for 480 Mbit & faster speed enum { BUFFER_SIZE = 648 }; // must hold at least 6 max size packets, plus 2 extra bytes USBSerial(USBHost &host) : txtimer(this) { init(); } - void begin(uint32_t baud, uint32_t format=0); + void begin(uint32_t baud, uint32_t format=USBHOST_SERIAL_8N1); void end(void); virtual int available(void); virtual int peek(void); @@ -916,6 +935,7 @@ private: void init(); static bool check_rxtx_ep(uint32_t &rxep, uint32_t &txep); bool init_buffers(uint32_t rsize, uint32_t tsize); + void ch341_setBaud(uint8_t byte_index); private: Pipe_t mypipes[3] __attribute__ ((aligned(32))); Transfer_t mytransfers[7] __attribute__ ((aligned(32))); @@ -925,6 +945,7 @@ private: setup_t setup; uint8_t setupdata[8]; uint32_t baudrate; + uint32_t format_; Pipe_t *rxpipe; Pipe_t *txpipe; uint8_t *rx1; // location for first incoming packet @@ -947,7 +968,16 @@ private: uint8_t pl2303_v2; uint8_t interface; bool control_queued; - enum { CDCACM, FTDI, PL2303, CH341 } sertype; + typedef enum { UNKNOWN=0, CDCACM, FTDI, PL2303, CH341 } sertype_t; + sertype_t sertype; + + typedef struct { + uint16_t idVendor; + uint16_t idProduct; + sertype_t sertype; + } product_vendor_mapping_t; + static product_vendor_mapping_t pid_vid_mapping[]; + }; //-------------------------------------------------------------------------- diff --git a/examples/SerialTest/SerialTest.ino b/examples/SerialTest/SerialTest.ino index b745e1f..0928ae2 100644 --- a/examples/SerialTest/SerialTest.ino +++ b/examples/SerialTest/SerialTest.ino @@ -4,6 +4,8 @@ #include "USBHost_t36.h" #define USBBAUD 115200 +uint32_t baud = USBBAUD; +uint32_t format = USBHOST_SERIAL_8N1; USBHost myusb; USBHub hub1(myusb); USBHub hub2(myusb); @@ -60,23 +62,8 @@ void loop() // If this is a new Serial device. if (drivers[i] == &userial) { // Lets try first outputting something to our USerial to see if it will go out... - userial.begin(USBBAUD); + userial.begin(baud); -// delay(5); -// userial.println("ver"); -#if 0 - userial.println("abcdefghijklmnopqrstuvwxyz"); - userial.println("ABCDEFGHIJKLMNOPQURSTUVWYZ"); - userial.flush(); // force it out now. - userial.println("0123456789"); - userial.flush(); - delay(2); - userial.println("abcdefghijklmnopqrstuvwxyz"); - userial.println("ABCDEFGHIJKLMNOPQURSTUVWYZ"); - delay(2); - userial.println("!@#$%^&*()"); - userial.flush(); -#endif } } } @@ -89,8 +76,55 @@ void loop() if (ch == '$') { BioloidTest(); while (Serial.read() != -1); + } else if (ch == '#') { + // Lets see if we have a baud rate specified here... + uint32_t new_baud = 0; + for(;;) { + ch = Serial.read(); + if ((ch < '0') || (ch > '9')) + break; + new_baud = new_baud*10 + ch - '0'; + } + // See if the user is specifying a format: 8n1, 7e1, 7e2, 8n2 + // Note this is Quick and very dirty code... + // + if (ch == ',') { + char command_line[10]; + ch = Serial.read(); + while (ch == ' ') Serial.read(); // ignore any spaces. + uint8_t cb = 0; + while ((ch > ' ') && (cb < sizeof(command_line))) { + command_line[cb++] = ch; + ch = Serial.read(); + } + command_line[cb] = '\0'; + if (CompareStrings(command_line, "8N1")) format = USBHOST_SERIAL_8N1; + else if (CompareStrings(command_line, "8N2")) format = USBHOST_SERIAL_8N2; + else if (CompareStrings(command_line, "7E1")) format = USBHOST_SERIAL_7E1; + else if (CompareStrings(command_line, "7O1")) format = USBHOST_SERIAL_7O1; + } + Serial.println("\n*** Set new Baud command ***\n do userial.end()"); + digitalWriteFast(2, HIGH); + userial.end(); // Do the end statement; + digitalWriteFast(2, LOW); + if (new_baud) { + baud = new_baud; + Serial.print(" New Baud: "); + Serial.println(baud); + Serial.print(" Format: "); + Serial.println(format); + digitalWriteFast(3, HIGH); + userial.begin(baud, format); + digitalWriteFast(3, LOW); + Serial.println(" Completed "); + } else { + Serial.println(" New Baud 0 - leave disabled"); + } + + while (Serial.read() != -1); + } else { + userial.write(ch); } - else userial.write(ch); } } @@ -106,14 +140,25 @@ void loop() } } +bool CompareStrings(const char *sz1, const char *sz2) { + while (*sz2 != 0) { + if (toupper(*sz1) != toupper(*sz2)) + return false; + sz1++; + sz2++; + } + return true; // end of string so show as match + + +} + //#define ID_MASTER 200 #define ID_MASTER 0xfd // Extract stuff from Bioloid library.. #define AX12_BUFFER_SIZE 128 #define COUNTER_TIMEOUT 12000 -/** Instruction Set **/ -#define AX_PING 1 +/** Instruction Set **/ #define AX_PING 1 #define AX_READ_DATA 2 #define AX_WRITE_DATA 3 #define AX_REG_WRITE 4 diff --git a/serial.cpp b/serial.cpp index ffd7893..8e7189a 100644 --- a/serial.cpp +++ b/serial.cpp @@ -19,6 +19,8 @@ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: special thanks to the Linux kernel for the CH341's method of operation, particularly how the baud rate is encoded. */ #include @@ -28,14 +30,21 @@ #define println USBHost::println_ /************************************************************/ -// Control Transfer For Configuration +// Define mapping VID/PID - to Serial Device type. /************************************************************/ -typedef struct { - uint32_t dwDTERate; // Data Terminal Rate in bits per second - uint8_t bCharFormat; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits - uint8_t bParityType; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space - uint8_t bDataBits; // Data bits (5, 6, 7, 8 or 16) -} LINE_CODING; +USBSerial::product_vendor_mapping_t USBSerial::pid_vid_mapping[] = { + // FTDI mappings. + {0x0403, 0x6001, USBSerial::FTDI}, + + // PL2303 + {0x67B,0x2303, USBSerial::PL2303}, + + // CH341 + {0x4348, 0x5523, USBSerial::CH341 }, + {0x1a86, 0x7523, USBSerial::CH341 }, + {0x1a86, 0x5523, USBSerial::CH341 } +}; + /************************************************************/ // Initialization and claiming of devices & interfaces @@ -47,6 +56,7 @@ void USBSerial::init() contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); driver_ready_for_device(this); + format_ = USBHOST_SERIAL_8N1; } bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) @@ -60,51 +70,9 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 println(", bDeviceProtocol = ", dev->bDeviceProtocol); print_hexbytes(descriptors, len); if (type == 0) { - if (dev->idVendor == 0x0403 && dev->idProduct == 0x6001) { - // FTDI FT232 - println("len = ", len); - if (len < 23) return false; - if (descriptors[0] != 9) return false; // length 9 - if (descriptors[9] != 7) return false; // length 7 - if (descriptors[10] != 5) return false; // ep desc - uint32_t rxep = descriptors[11]; - if (descriptors[12] != 2) return false; // bulk type - if (descriptors[13] != 64) return false; // size 64 - if (descriptors[14] != 0) return false; - if (descriptors[16] != 7) return false; // length 7 - if (descriptors[17] != 5) return false; // ep desc - uint32_t txep = descriptors[18]; - if (descriptors[19] != 2) return false; // bulk type - if (descriptors[20] != 64) return false; // size 64 - if (descriptors[21] != 0) return false; - if (!check_rxtx_ep(rxep, txep)) return false; - print("FTDI, rxep=", rxep & 15); - println(", txep=", txep); - if (!init_buffers(64, 64)) return false; - rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); - if (!rxpipe) return false; - txpipe = new_Pipe(dev, 2, txep, 0, 64); - if (!txpipe) { - // TODO: free rxpipe - return false; - } - sertype = FTDI; - rxpipe->callback_function = rx_callback; - queue_Data_Transfer(rxpipe, rx1, 64, this); - rxstate = 1; - if (rxsize > 128) { - queue_Data_Transfer(rxpipe, rx2, 64, this); - rxstate = 3; - } - txstate = 0; - txpipe->callback_function = tx_callback; - baudrate = 115200; - pending_control = 0x0F; - mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port - queue_Control_Transfer(dev, &setup, NULL, this); - control_queued = true; - return true; - } else if ((dev->bDeviceClass == 2) && (dev->bDeviceSubClass == 0)) { + //--------------------------------------------------------------------- + // CDCACM + if ((dev->bDeviceClass == 2) && (dev->bDeviceSubClass == 0)) { // It is a communication device see if we can extract the data... // Try some ttyACM types? // This code may be similar to MIDI code. @@ -226,72 +194,196 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 return true; } - // TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them - if (dev->idVendor == 0x67B && dev->idProduct == 0x2303) { - // Prolific Technology, Inc. PL2303 Serial Port - println("len = ", len); - uint8_t count_end_points = descriptors[4]; - if (count_end_points < 2) return false; // not enough end points - if (len < 23) return false; - if (descriptors[0] != 9) return false; // length 9 - - // Lets walk through end points and see if we - // can find an RX and TX bulk transfer end point. - //vid=67B, pid=2303 - // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 - //09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00 - uint32_t rxep = 0; - uint32_t txep = 0; - uint32_t descriptor_index = 9; - while (count_end_points-- && ((rxep == 0) || txep == 0)) { - if (descriptors[descriptor_index] != 7) return false; // length 7 - if (descriptors[descriptor_index+1] != 5) return false; // ep desc - if ((descriptors[descriptor_index+3] == 2) - && (descriptors[descriptor_index+4] == 64) - && (descriptors[descriptor_index+5] == 0)) { - // have a bulk EP size - if (descriptors[descriptor_index+2] & 0x80 ) { - rxep = descriptors[descriptor_index+2]; - } else { - txep = descriptors[descriptor_index+2]; - } + // See if the vendor_id:product_id is in our list of products. + sertype = UNKNOWN; + for (uint8_t i = 0; i < (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0])); i++) { + if ((dev->idVendor == pid_vid_mapping[i].idVendor) && (dev->idProduct == pid_vid_mapping[i].idProduct)) { + sertype = pid_vid_mapping[i].sertype; + break; + } + } + switch (sertype) { + //--------------------------------------------------------------------- + // FTDI + case FTDI: + { + // FTDI FT232 + println("len = ", len); + if (len < 23) return false; + if (descriptors[0] != 9) return false; // length 9 + if (descriptors[9] != 7) return false; // length 7 + if (descriptors[10] != 5) return false; // ep desc + uint32_t rxep = descriptors[11]; + if (descriptors[12] != 2) return false; // bulk type + if (descriptors[13] != 64) return false; // size 64 + if (descriptors[14] != 0) return false; + if (descriptors[16] != 7) return false; // length 7 + if (descriptors[17] != 5) return false; // ep desc + uint32_t txep = descriptors[18]; + if (descriptors[19] != 2) return false; // bulk type + if (descriptors[20] != 64) return false; // size 64 + if (descriptors[21] != 0) return false; + if (!check_rxtx_ep(rxep, txep)) return false; + print("FTDI, rxep=", rxep & 15); + println(", txep=", txep); + if (!init_buffers(64, 64)) return false; + rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); + if (!rxpipe) return false; + txpipe = new_Pipe(dev, 2, txep, 0, 64); + if (!txpipe) { + // TODO: free rxpipe + return false; } - descriptor_index += 7; // setup to look at next one... - } - // Try to verify the end points. - if (!check_rxtx_ep(rxep, txep)) return false; - print("FTDI, rxep=", rxep & 15); - println(", txep=", txep); - if (!init_buffers(64, 64)) return false; - rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); - if (!rxpipe) return false; - txpipe = new_Pipe(dev, 2, txep, 0, 64); - if (!txpipe) { - // TODO: free rxpipe - return false; + sertype = FTDI; + rxpipe->callback_function = rx_callback; + rxsize = 64; + queue_Data_Transfer(rxpipe, rx1, 64, this); + rxstate = 1; + txsize = 64; + txstate = 0; + txpipe->callback_function = tx_callback; + baudrate = 115200; + pending_control = 0x0F; + mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port + queue_Control_Transfer(dev, &setup, NULL, this); + control_queued = true; + return true; } + //------------------------------------------------------------------------ + // Prolific + // TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them + case PL2303: + { + // Prolific Technology, Inc. PL2303 Serial Port + println("len = ", len); + uint8_t count_end_points = descriptors[4]; + if (count_end_points < 2) return false; // not enough end points + if (len < 23) return false; + if (descriptors[0] != 9) return false; // length 9 - sertype = PL2303; - rxpipe->callback_function = rx_callback; - queue_Data_Transfer(rxpipe, rx1, 64, this); - rxstate = 1; - if (rxsize > 128) { - queue_Data_Transfer(rxpipe, rx2, 64, this); - rxstate = 3; - } - txstate = 0; - txpipe->callback_function = tx_callback; - baudrate = 115200; + // Lets walk through end points and see if we + // can find an RX and TX bulk transfer end point. + //vid=67B, pid=2303 + // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 + //09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00 + uint32_t rxep = 0; + uint32_t txep = 0; + uint32_t descriptor_index = 9; + while (count_end_points-- && ((rxep == 0) || txep == 0)) { + if (descriptors[descriptor_index] != 7) return false; // length 7 + if (descriptors[descriptor_index+1] != 5) return false; // ep desc + if ((descriptors[descriptor_index+3] == 2) + && (descriptors[descriptor_index+4] == 64) + && (descriptors[descriptor_index+5] == 0)) { + // have a bulk EP size + if (descriptors[descriptor_index+2] & 0x80 ) { + rxep = descriptors[descriptor_index+2]; + rxsize = descriptors[descriptor_index+4]; + } else { + txep = descriptors[descriptor_index+2]; + txsize = descriptors[descriptor_index+4]; + } + } + descriptor_index += 7; // setup to look at next one... + } + // Try to verify the end points. + if (!check_rxtx_ep(rxep, txep)) return false; + print("PL2303, rxep=", rxep & 15); + println(", txep=", txep); + if (!init_buffers(64, 64)) return false; + rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); + if (!rxpipe) return false; + txpipe = new_Pipe(dev, 2, txep, 0, 64); + if (!txpipe) { + // TODO: free rxpipe + return false; + } - // Lets see if it will handle the same CDCACM - messages? - println("PL2303: readRegister(0x04)"); - // Need to setup the data the line coding data - mk_setup(setup, 0xC0, 0x1, 0x8484, 0, 1); - queue_Control_Transfer(dev, &setup, setupdata, this); - control_queued = true; - setup_state = 1; // We are at step one of setup... - pending_control = 0x3f; // Maybe don't need to do... - return true; + sertype = PL2303; + rxpipe->callback_function = rx_callback; + queue_Data_Transfer(rxpipe, rx1, 64, this); + rxstate = 1; + txstate = 0; + txpipe->callback_function = tx_callback; + baudrate = 115200; + + println("PL2303: readRegister(0x04)"); + // Need to setup the data the line coding data + mk_setup(setup, 0xC0, 0x1, 0x8484, 0, 1); + queue_Control_Transfer(dev, &setup, setupdata, this); + control_queued = true; + setup_state = 1; // We are at step one of setup... + pending_control = 0x3f; + return true; + } + //------------------------------------------------------------------------ + // CH341 + case CH341: + { + println("len = ", len); + uint8_t count_end_points = descriptors[4]; + if (count_end_points < 2) return false; // not enough end points + if (len < 23) return false; + if (descriptors[0] != 9) return false; // length 9 + + // Lets walk through end points and see if we + // can find an RX and TX bulk transfer end point. + // vid=1A86, pid=7523, bDeviceClass = 255, bDeviceSubClass = 0, bDeviceProtocol = 0 + // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 + //09 04 00 00 03 FF 01 02 00 07 05 82 02 20 00 00 07 05 02 02 20 00 00 07 05 81 03 08 00 01 + uint32_t rxep = 0; + uint32_t txep = 0; + uint32_t descriptor_index = 9; + while (count_end_points-- && ((rxep == 0) || txep == 0)) { + if (descriptors[descriptor_index] != 7) return false; // length 7 + if (descriptors[descriptor_index+1] != 5) return false; // ep desc + if ((descriptors[descriptor_index+3] == 2) + && (descriptors[descriptor_index+4] <= 64) + && (descriptors[descriptor_index+5] == 0)) { + // have a bulk EP size + if (descriptors[descriptor_index+2] & 0x80 ) { + rxep = descriptors[descriptor_index+2]; + rxsize = descriptors[descriptor_index+4]; + } else { + txep = descriptors[descriptor_index+2]; + txsize = descriptors[descriptor_index+4]; + } + } + descriptor_index += 7; // setup to look at next one... + } + // Try to verify the end points. + if (!check_rxtx_ep(rxep, txep)) return false; + print("ch341, rxep=", rxep & 15); + println(", txep=", txep); + if (!init_buffers(rxsize, txsize)) return false; + rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rxsize); + if (!rxpipe) return false; + txpipe = new_Pipe(dev, 2, txep, 0, txsize); + if (!txpipe) { + // TODO: free rxpipe + return false; + } + + rxpipe->callback_function = rx_callback; + queue_Data_Transfer(rxpipe, rx1, rxsize, this); + rxstate = 1; + txstate = 0; + txpipe->callback_function = tx_callback; + baudrate = 115200; + + println("CH341: 0xC0, 0x5f, 0, 0, 8"); + // Need to setup the data the line coding data + mk_setup(setup, 0xC0, 0x5f, 0, 0, sizeof(setupdata)); + queue_Control_Transfer(dev, &setup, setupdata, this); + control_queued = true; + setup_state = 1; // We are at step one of setup... + pending_control = 0x7f; + return true; + } + //------------------------------------------------------------------------ + // PID:VID - not in our product list. + default: + return false; } } else if (type != 1) return false; // TTYACM: @@ -424,7 +516,15 @@ void USBSerial::control(const Transfer_t *transfer) if (pending_control & 1) { pending_control &= ~1; // set data format - mk_setup(setup, 0x40, 4, 8, 0, 0); // data format 8N1 + uint16_t ftdi_format = format_ & 0xf; // This should give us the number of bits. + + // now lets extract the parity from our encoding + ftdi_format |= (format_ & 0xe0) << 3; // they encode bits 9-11 + + // See if two stop bits + if (format_ & 0x100) ftdi_format |= (0x2 << 11); + + mk_setup(setup, 0x40, 4, ftdi_format, 0, 0); // data format 8N1 queue_Control_Transfer(device, &setup, NULL, this); control_queued = true; return; @@ -454,6 +554,15 @@ void USBSerial::control(const Transfer_t *transfer) control_queued = true; return; } + // clear DTR + if (pending_control & 0x80) { + pending_control &= ~0x80; + println("FTDI clear DTR"); + mk_setup(setup, 0x40, 1, 0x0100, 0, 0); + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } } @@ -467,9 +576,9 @@ void USBSerial::control(const Transfer_t *transfer) setupdata[1] = (baudrate >> 8) & 0xff; setupdata[2] = (baudrate >> 16) & 0xff; setupdata[3] = (baudrate >> 24) & 0xff; - setupdata[4] = 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits - setupdata[5] = 0; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space - setupdata[6] = 8; // Data bits (5, 6, 7, 8 or 16) + setupdata[4] = (format_ & 0x100)? 2 : 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits + setupdata[5] = (format_ & 0xe0) >> 5; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space + setupdata[6] = format_ & 0x1f; // Data bits (5, 6, 7, 8 or 16) print("CDCACM setup: "); print_hexbytes(&setupdata, 7); mk_setup(setup, 0x21, 0x20, 0, 0, 7); @@ -487,6 +596,15 @@ void USBSerial::control(const Transfer_t *transfer) control_queued = true; return; } + if (pending_control & 0x80) { + pending_control &= ~0x80; + println("Control - 0x21,0x22, 0x0 - clear DTR"); + // Need to setup the data the line coding data + mk_setup(setup, 0x21, 0x22, 0, 0, 0); + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } } //------------------------------------------------------------------------- @@ -607,9 +725,9 @@ void USBSerial::control(const Transfer_t *transfer) setupdata[1] = (baudrate >> 8) & 0xff; setupdata[2] = (baudrate >> 16) & 0xff; setupdata[3] = (baudrate >> 24) & 0xff; - setupdata[4] = 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits - setupdata[5] = 0; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space - setupdata[6] = 8; // Data bits (5, 6, 7, 8 or 16) + setupdata[4] = (format_ & 0x100)? 2 : 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits + setupdata[5] = (format_ & 0xe0) >> 5; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space + setupdata[6] = format_ & 0x1f; // Data bits (5, 6, 7, 8 or 16) print("PL2303: Set baud/control: ", baudrate, HEX); print(" = "); print_hexbytes(&setupdata, 7); @@ -639,20 +757,198 @@ void USBSerial::control(const Transfer_t *transfer) print("PL2303: Returned configuration data: "); print_hexbytes(setupdata, 7); + // This sets the control lines (0x1=DTR, 0x2=RTS) println("PL2303: 0x21, 0x22, 0x3"); mk_setup(setup, 0x21, 0x22, 3, 0, 0); // queue_Control_Transfer(device, &setup, NULL, this); control_queued = true; return; } - if (pending_control & 0x30) { - pending_control &= ~0x30; + if (pending_control & 0x20) { + pending_control &= ~0x20; println("PL2303: 0x21, 0x22, 0x3"); mk_setup(setup, 0x21, 0x22, 3, 0, 0); // queue_Control_Transfer(device, &setup, NULL, this); control_queued = true; } + if (pending_control & 0x80) { + pending_control &= ~0x80; + println("PL2303: 0x21, 0x22, 0x0"); // Clear DTR/RTS + mk_setup(setup, 0x21, 0x22, 0, 0, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + } } + + if (sertype == CH341) { +#if 0 + print(" Transfer: "); + print_hexbytes(&transfer->setup, sizeof(setup_t)); + if (transfer->length) { + print(" data: "); + print_hexbytes(transfer->buffer, transfer->length); + } +#endif + if (pending_control & 1) { + // Still in larger setup state mode + switch (setup_state) { + case 1: + print(" Returned: "); + print_hexbytes(transfer->buffer, transfer->length); + println("CH341: 40, a1, 0, 0, 0"); + mk_setup(setup, 0x40, 0xa1, 0, 0, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + setup_state = 2; + control_queued = true; + return; + case 2: + ch341_setBaud(0); // send the first byte of the baud rate + control_queued = true; + setup_state = 3; + return; + case 3: + ch341_setBaud(1); // send the second byte of the baud rate + control_queued = true; + setup_state = 4; + return; + case 4: + println("CH341: c0, 95, 2518, 0, 8"); + mk_setup(setup, 0xc0, 0x95, 0x2518, 0, sizeof(setup)); // + queue_Control_Transfer(device, &setup, setupdata, this); + setup_state = 5; + control_queued = true; + return; + case 5: + print(" Returned: "); + print_hexbytes(transfer->buffer, transfer->length); + println("CH341: 40, 0x9a, 0x2518, 0x0050, 0"); + mk_setup(setup, 0x40, 0x9a, 0x2518, 0x0050, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + setup_state = 6; + control_queued = true; + return; + case 6: + println("CH341: c0, 95, 0x706, 0, 8 - get status"); + mk_setup(setup, 0xc0, 0x95, 0x706, 0, sizeof(setup)); // + queue_Control_Transfer(device, &setup, setupdata, this); + setup_state = 7; + control_queued = true; + return; + case 7: + print(" Returned: "); + print_hexbytes(transfer->buffer, transfer->length); + println("CH341: 40, 0xa1, 0x501f, 0xd90a, 0"); + mk_setup(setup, 0x40, 0xa1, 0x501f, 0xd90a, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + setup_state = 8; + control_queued = true; + break; + } + pending_control &= ~1; // We are finally going to leave this list and join the rest + if (control_queued) return; + } + // set baud rate + if (pending_control & 2) { + pending_control &= ~2; + ch341_setBaud(0); // send the first byte of the baud rate + control_queued = true; + return; + } + if (pending_control & 4) { + pending_control &= ~4; + ch341_setBaud(1); // send the first byte of the baud rate + control_queued = true; + return; + } + + if (pending_control & 8) { + pending_control &= ~8; + uint16_t ch341_format; + switch (format_) { + default: + // These values were observed when used on PC... Need to flush out others. + case USBHOST_SERIAL_8N1: ch341_format = 0xc3; break; + case USBHOST_SERIAL_7E1: ch341_format = 0xda; break; + case USBHOST_SERIAL_7O1: ch341_format = 0xca; break; + case USBHOST_SERIAL_8N2: ch341_format = 0xc7; break; + } + println("CH341: 40, 0x9a, 0x2518: ", ch341_format, HEX); + mk_setup(setup, 0x40, 0x9a, 0x2518, ch341_format, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } + if (pending_control & 0x10) { + pending_control &= ~0x10; + // This is setting handshake need to figure out what... + // 0x20=DTR, 0x40=RTS send ~ of values. + println("CH341: 0x40, 0xa4, 0xff9f, 0, 0 - Handshake"); + mk_setup(setup, 0x40, 0xa4, 0xff9f, 0, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } + if (pending_control & 0x20) { + pending_control &= ~0x20; + // This is setting handshake need to figure out what... + println("CH341: c0, 95, 0x706, 0, 8 - get status"); + mk_setup(setup, 0xc0, 0x95, 0x706, 0, sizeof(setup)); // + queue_Control_Transfer(device, &setup, setupdata, this); + control_queued = true; + return; + } + if (pending_control & 0x40) { + pending_control &= ~0x40; + print(" Returned: "); + print_hexbytes(transfer->buffer, transfer->length); + println("CH341: 0x40, 0x9a, 0x2727, 0, 0"); + mk_setup(setup, 0x40, 0x9a, 0x2727, 0, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + + return; + } + + if (pending_control & 0x80) { + pending_control &= ~0x80; + println("CH341: 0x40, 0xa4, 0xffff, 0, 0 - Handshake"); + mk_setup(setup, 0x40, 0xa4, 0xffff, 0, 0); // + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } + } +} + +#define CH341_BAUDBASE_FACTOR 1532620800 +#define CH341_BAUDBASE_DIVMAX 3 +void USBSerial::ch341_setBaud(uint8_t byte_index) { + if (byte_index == 0) { + uint32_t factor; + uint16_t divisor; + + factor = (CH341_BAUDBASE_FACTOR / baudrate); + divisor = CH341_BAUDBASE_DIVMAX; + + while ((factor > 0xfff0) && divisor) { + factor >>= 3; + divisor--; + } + + factor = 0x10000 - factor; + + factor = (factor & 0xff00) | divisor; + setupdata[0] = factor & 0xff; // save away the low byte for 2nd message + + + println("CH341: 40, 0x9a, 0x1312... (Baud word 0):", factor, HEX); + mk_setup(setup, 0x40, 0x9a, 0x1312, factor, 0); // + } else { + // Second packet use the byte we saved away during the calculation above + println("CH341: 40, 0x9a, 0x0f2c... (Baud word 1):", setupdata[0], HEX); + mk_setup(setup, 0x40, 0x9a, 0x0f2c, setupdata[0], 0); // + } + queue_Control_Transfer(device, &setup, setupdata, this); + control_queued = true; } @@ -866,14 +1162,40 @@ void USBSerial::begin(uint32_t baud, uint32_t format) { NVIC_DISABLE_IRQ(IRQ_USBHS); baudrate = baud; - pending_control |= 2; + bool format_changed = format != format_; + format_ = format; + switch (sertype) { + default: + case CDCACM: pending_control |= 0x6; break; + case FTDI: pending_control |= (format_changed? 0xf : 0xe); break; // Set BAUD, FLOW, DTR + case PL2303: pending_control |= 0x1e; break; // set more stuff... + case CH341: pending_control |= 0x1e; break; + } if (!control_queued) control(NULL); NVIC_ENABLE_IRQ(IRQ_USBHS); + // Wait until all packets have been queued before we return to caller. + while (pending_control) { + yield(); // not sure if we want to yield or what? + } } void USBSerial::end(void) { - // TODO: lower DTR + NVIC_DISABLE_IRQ(IRQ_USBHS); + switch (sertype) { + default: + case CDCACM: pending_control |= 0x80; break; + case FTDI: pending_control |= 0x80; break; // clear DTR + case PL2303: pending_control |= 0x80; break; + case CH341: pending_control |= 0x80; break; + } + if (!control_queued) control(NULL); + NVIC_ENABLE_IRQ(IRQ_USBHS); + + // Wait until all packets have been queued before we return to caller. + while (pending_control) { + yield(); // not sure if we want to yield or what? + } } int USBSerial::available(void) From 12be685ab1dc9e5ee23a5fb7740e190544c817fe Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Sun, 5 Nov 2017 10:53:54 -0800 Subject: [PATCH 03/11] Test fixes for different parity and like I did a quick test using the Serial test app, where I can type in lines like: #38400,7E1 And it reopens the serial port at the specified baud and format. Note the formats the test app looks for is 7E1, 7O1, 8N1, 8N2. Tested on an FTDI, PL2303 and CH341. I also tested going to Teensy 3.2 CDCACM. But as far as I know the teensy could not care less about Baud/Format. Unless of course there is a way to query the value and use it to set a Hardware Serial port to it. --- USBHost_t36.h | 10 +++++----- examples/SerialTest/SerialTest.ino | 2 +- keywords.txt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index e96ea87..d2e92da 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -56,7 +56,7 @@ // your best effort to read chapter 4 before asking USB questions! -#define USBHOST_PRINT_DEBUG +//#define USBHOST_PRINT_DEBUG /************************************************/ /* Data Types */ @@ -122,12 +122,12 @@ class USBDriverTimer; // bits: 8-9 - Stop bits. 0=1, 1=2 -#define USBHOST_SERIAL_7E1 0x027 -#define USBHOST_SERIAL_7O1 0x047 +#define USBHOST_SERIAL_7E1 0x047 +#define USBHOST_SERIAL_7O1 0x027 #define USBHOST_SERIAL_8N1 0x08 #define USBHOST_SERIAL_8N2 0x108 -#define USBHOST_SERIAL_8E1 0x028 -#define USBHOST_SERIAL_8O1 0x048 +#define USBHOST_SERIAL_8E1 0x048 +#define USBHOST_SERIAL_8O1 0x028 diff --git a/examples/SerialTest/SerialTest.ino b/examples/SerialTest/SerialTest.ino index 0928ae2..2b6ae6a 100644 --- a/examples/SerialTest/SerialTest.ino +++ b/examples/SerialTest/SerialTest.ino @@ -112,7 +112,7 @@ void loop() Serial.print(" New Baud: "); Serial.println(baud); Serial.print(" Format: "); - Serial.println(format); + Serial.println(format, HEX); digitalWriteFast(3, HIGH); userial.begin(baud, format); digitalWriteFast(3, LOW); diff --git a/keywords.txt b/keywords.txt index 5b3b4ea..a7d6416 100644 --- a/keywords.txt +++ b/keywords.txt @@ -85,4 +85,4 @@ USBHOST_SERIAL_7O1 LITERAL1 USBHOST_SERIAL_8N1 LITERAL1 USBHOST_SERIAL_8N2 LITERAL1 USBHOST_SERIAL_8E1 LITERAL1 -USBHOST_SERIAL_8O1 LITERAL1 \ No newline at end of file +USBHOST_SERIAL_8O1 LITERAL1 From ec4a7ac24ccbcd37a0d8f54b2a1bafb9c783c29e Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Sun, 5 Nov 2017 16:25:59 -0800 Subject: [PATCH 04/11] Support for Silicon labs: CP210x Did a quick add of support for the Slicon Labs CP210x drivers. Did similar like messages as FTDI, based on their documents on line: https://www.silabs.com/documents/public/application-notes/AN571.pdf --- USBHost_t36.h | 4 +- serial.cpp | 132 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 3 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index d2e92da..300a324 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -943,7 +943,7 @@ private: USBDriverTimer txtimer; uint32_t bigbuffer[(BUFFER_SIZE+3)/4]; setup_t setup; - uint8_t setupdata[8]; + uint8_t setupdata[16]; // uint32_t baudrate; uint32_t format_; Pipe_t *rxpipe; @@ -968,7 +968,7 @@ private: uint8_t pl2303_v2; uint8_t interface; bool control_queued; - typedef enum { UNKNOWN=0, CDCACM, FTDI, PL2303, CH341 } sertype_t; + typedef enum { UNKNOWN=0, CDCACM, FTDI, PL2303, CH341, CP210X } sertype_t; sertype_t sertype; typedef struct { diff --git a/serial.cpp b/serial.cpp index 8e7189a..1ef2bc6 100644 --- a/serial.cpp +++ b/serial.cpp @@ -42,7 +42,10 @@ USBSerial::product_vendor_mapping_t USBSerial::pid_vid_mapping[] = { // CH341 {0x4348, 0x5523, USBSerial::CH341 }, {0x1a86, 0x7523, USBSerial::CH341 }, - {0x1a86, 0x5523, USBSerial::CH341 } + {0x1a86, 0x5523, USBSerial::CH341 }, + + // Silex CP210... + {0x10c4, 0xea60, USBSerial::CP210X } }; @@ -307,6 +310,7 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 txpipe->callback_function = tx_callback; baudrate = 115200; + // First attempt keep it simple... println("PL2303: readRegister(0x04)"); // Need to setup the data the line coding data mk_setup(setup, 0xC0, 0x1, 0x8484, 0, 1); @@ -381,6 +385,71 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 return true; } //------------------------------------------------------------------------ + // CP210X + case CP210X: + { + println("len = ", len); + uint8_t count_end_points = descriptors[4]; + if (count_end_points < 2) return false; // not enough end points + if (len < 23) return false; + if (descriptors[0] != 9) return false; // length 9 + + // Lets walk through end points and see if we + // can find an RX and TX bulk transfer end point. + // BUGBUG: should factor out this code as duplicated... + // vid=10C4, pid=EA60, bDeviceClass = 0, bDeviceSubClass = 0, bDeviceProtocol = 0 + // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 + //09 04 00 00 02 FF 00 00 02 07 05 81 02 40 00 00 07 05 01 02 40 00 00 + uint32_t rxep = 0; + uint32_t txep = 0; + uint32_t descriptor_index = 9; + while (count_end_points-- && ((rxep == 0) || txep == 0)) { + if (descriptors[descriptor_index] != 7) return false; // length 7 + if (descriptors[descriptor_index+1] != 5) return false; // ep desc + if ((descriptors[descriptor_index+3] == 2) + && (descriptors[descriptor_index+4] <= 64) + && (descriptors[descriptor_index+5] == 0)) { + // have a bulk EP size + if (descriptors[descriptor_index+2] & 0x80 ) { + rxep = descriptors[descriptor_index+2]; + rxsize = descriptors[descriptor_index+4]; + } else { + txep = descriptors[descriptor_index+2]; + txsize = descriptors[descriptor_index+4]; + } + } + descriptor_index += 7; // setup to look at next one... + } + // Try to verify the end points. + if (!check_rxtx_ep(rxep, txep)) return false; + print("CP210X, rxep=", rxep & 15); + println(", txep=", txep); + if (!init_buffers(rxsize, txsize)) return false; + rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rxsize); + if (!rxpipe) return false; + txpipe = new_Pipe(dev, 2, txep, 0, txsize); + if (!txpipe) { + // TODO: free rxpipe + return false; + } + + rxpipe->callback_function = rx_callback; + queue_Data_Transfer(rxpipe, rx1, rxsize, this); + rxstate = 1; + txstate = 0; + txpipe->callback_function = tx_callback; + baudrate = 115200; + + println("CP210X: 0x41, 0x11, 0, 0, 0 - reset port"); + // Need to setup the data the line coding data + mk_setup(setup, 0x41, 0x11, 0, 0, 0); + queue_Control_Transfer(dev, &setup, NULL, this); + control_queued = true; + setup_state = 1; // We are at step one of setup... + pending_control = 0xf; + return true; + } + //------------------------------------------------------------------------ // PID:VID - not in our product list. default: return false; @@ -917,6 +986,66 @@ void USBSerial::control(const Transfer_t *transfer) return; } } + //------------------------------------------------------------------------- + // First CP210X + if (sertype == CP210X) { + if (pending_control & 1) { + pending_control &= ~1; + // set data format + uint16_t cp210x_format = (format_ & 0xf) << 8; // This should give us the number of bits. + + // now lets extract the parity from our encoding bits 5-7 and in theres 4-7 + cp210x_format |= (format_ & 0xe0) >> 1; // they encode bits 9-11 + + // See if two stop bits + if (format_ & 0x100) cp210x_format |= 2; + + mk_setup(setup, 0x41, 3, cp210x_format, 0, 0); // data format 8N1 + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } + // set baud rate + if (pending_control & 2) { + pending_control &= ~2; + setupdata[0] = (baudrate) & 0xff; // Setup baud rate 115200 - 0x1C200 + setupdata[1] = (baudrate >> 8) & 0xff; + setupdata[2] = (baudrate >> 16) & 0xff; + setupdata[3] = (baudrate >> 24) & 0xff; + mk_setup(setup, 0x40, 0x1e, 0, 0, 4); + queue_Control_Transfer(device, &setup, setupdata, this); + control_queued = true; + return; + } + // configure flow control + if (pending_control & 4) { + pending_control &= ~4; + memset(setupdata, 0, sizeof(setupdata)); // clear out the data + setupdata[0] = 1; // Set dtr active? + mk_setup(setup, 0x41, 13, 0, 0, 0x10); + queue_Control_Transfer(device, &setup, setupdata, this); + control_queued = true; + return; + } + // set DTR + if (pending_control & 8) { + pending_control &= ~8; + mk_setup(setup, 0x41, 7, 0x0101, 0, 0); + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } + // clear DTR + if (pending_control & 0x80) { + pending_control &= ~0x80; + println("CP210x clear DTR"); + mk_setup(setup, 0x40, 1, 0x0100, 0, 0); + queue_Control_Transfer(device, &setup, NULL, this); + control_queued = true; + return; + } + + } } #define CH341_BAUDBASE_FACTOR 1532620800 @@ -1170,6 +1299,7 @@ void USBSerial::begin(uint32_t baud, uint32_t format) case FTDI: pending_control |= (format_changed? 0xf : 0xe); break; // Set BAUD, FLOW, DTR case PL2303: pending_control |= 0x1e; break; // set more stuff... case CH341: pending_control |= 0x1e; break; + case CP210X: pending_control |= 0xf; break; } if (!control_queued) control(NULL); NVIC_ENABLE_IRQ(IRQ_USBHS); From d3e21b04c0fd27535fabeccded6fb7ff92cc3c34 Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Tue, 7 Nov 2017 07:08:36 -0800 Subject: [PATCH 05/11] FIX - Keyboard LEDS The main issue was I used a setup structure on the stack which turned into garbage. Earlier I thought the data was copied, but it was not... Found that fixing this resolved the earlier issue where I needed to defer the update. So removed the defered code, plus use member variable for the one byte output instead of static variable. --- USBHost_t36.h | 2 -- keyboard.cpp | 24 ++---------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index 300a324..500b4c6 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -669,8 +669,6 @@ private: uint8_t keyOEM; uint8_t prev_report[8]; KBDLeds_t leds_ = {0}; - bool update_leds_ = false; - bool processing_new_data_ = false; Pipe_t mypipes[2] __attribute__ ((aligned(32))); Transfer_t mytransfers[4] __attribute__ ((aligned(32))); strbuf_t mystring_bufs[1]; diff --git a/keyboard.cpp b/keyboard.cpp index b3e9123..93bdabc 100644 --- a/keyboard.cpp +++ b/keyboard.cpp @@ -179,7 +179,6 @@ static bool contains(uint8_t b, const uint8_t *data) void KeyboardController::new_data(const Transfer_t *transfer) { - processing_new_data_ = true; println("KeyboardController Callback (member)"); print(" KB Data: "); print_hexbytes(transfer->buffer, 8); @@ -197,12 +196,6 @@ void KeyboardController::new_data(const Transfer_t *transfer) } memcpy(prev_report, report, 8); queue_Data_Transfer(datapipe, report, 8, this); - processing_new_data_ = false; - - // See if we have any outstanding leds to update - if (update_leds_) { - updateLEDS(); - } } @@ -328,22 +321,9 @@ void KeyboardController::LEDS(uint8_t leds) { } void KeyboardController::updateLEDS() { - println("KBD: Update LEDS", leds_.byte, HEX); - if (processing_new_data_) { - println(" Update defered"); - update_leds_ = true; - return; // defer until later - } - // Now lets tell keyboard new state. - static uint8_t keyboard_keys_report[1] = {0}; - setup_t keys_setup; - keyboard_keys_report[0] = leds_.byte; - queue_Data_Transfer(datapipe, report, 8, this); - mk_setup(keys_setup, 0x21, 9, 0x200, 0, sizeof(keyboard_keys_report)); // hopefully this sets leds - queue_Control_Transfer(device, &keys_setup, keyboard_keys_report, this); - - update_leds_ = false; + mk_setup(setup, 0x21, 9, 0x200, 0, sizeof(leds_.byte)); // hopefully this sets leds + queue_Control_Transfer(device, &setup, &leds_.byte, this); } From 4eee8627d2563b5f8a6bace1dfdef98a5abb9f1e Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Fri, 10 Nov 2017 16:37:12 -0800 Subject: [PATCH 06/11] Serial cleanup - flush, timeout As per the forum posts, worked on combining the claim code for extracting which endpoints are RX and TX and create the pipes and the like. Also worked on live cases. Reading and writing to AX servos... Found some issues Probably more to find. I left in some debug code to help find cases of deleting a pipe may hang. Also macros in serial.cpp to set or toggle IO pins (when in debug mode). This helps find where things are happing using Logic Analyzer as a way to find the approriate USB packets. --- USBHost_t36.h | 5 + examples/SerialTest/SerialTest.ino | 2 +- serial.cpp | 338 +++++++++++------------------ 3 files changed, 138 insertions(+), 207 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index 500b4c6..6e03b83 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -909,14 +909,18 @@ class USBSerial: public USBDriver, public Stream { // FIXME: need different USBSerial, with bigger buffers for 480 Mbit & faster speed enum { BUFFER_SIZE = 648 }; // must hold at least 6 max size packets, plus 2 extra bytes + enum { DEFAULT_WRITE_TIMEOUT = 3500}; USBSerial(USBHost &host) : txtimer(this) { init(); } void begin(uint32_t baud, uint32_t format=USBHOST_SERIAL_8N1); void end(void); + uint32_t writeTimeout() {return write_timeout_;} + void writeTimeOut(uint32_t write_timeout) {write_timeout_ = write_timeout;} // Will not impact current ones. virtual int available(void); virtual int peek(void); virtual int read(void); virtual int availableForWrite(); virtual size_t write(uint8_t c); + virtual void flush(void); using Print::write; protected: @@ -944,6 +948,7 @@ private: uint8_t setupdata[16]; // uint32_t baudrate; uint32_t format_; + uint32_t write_timeout_ = DEFAULT_WRITE_TIMEOUT; Pipe_t *rxpipe; Pipe_t *txpipe; uint8_t *rx1; // location for first incoming packet diff --git a/examples/SerialTest/SerialTest.ino b/examples/SerialTest/SerialTest.ino index 2b6ae6a..45189b0 100644 --- a/examples/SerialTest/SerialTest.ino +++ b/examples/SerialTest/SerialTest.ino @@ -289,7 +289,7 @@ int ax12ReadPacket(int length) { while ((ch = userial.read()) == -1) { if ((millis() - ulStart) > 10) { //if (!--ulCounter) { - // Serial.println("Timeout"); + Serial.println("Timeout"); return 0; // Timeout } } diff --git a/serial.cpp b/serial.cpp index 1ef2bc6..dd53639 100644 --- a/serial.cpp +++ b/serial.cpp @@ -29,6 +29,16 @@ #define print USBHost::print_ #define println USBHost::println_ +//#define ENABLE_DEBUG_PINS + +#ifdef ENABLE_DEBUG_PINS +#define debugDigitalToggle(pin) {digitalWriteFast(pin, !digitalReadFast(pin));} +#define debugDigitalWrite(pin, state) {digitalWriteFast(pin, state);} +#else +#define debugDigitalToggle(pin) {;} +#define debugDigitalWrite(pin, state) {;} +#endif + /************************************************************/ // Define mapping VID/PID - to Serial Device type. /************************************************************/ @@ -168,6 +178,8 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 println(", tx:", tx_ep); if (!rx_ep || !tx_ep) return false; // did not get our two end points if (!init_buffers(rx_size, tx_size)) return false; + println(" rx buffer size:", rxsize); + println(" tx buffer size:", txsize); rxpipe = new_Pipe(dev, 2, rx_ep & 15, 1, rx_size); if (!rxpipe) return false; txpipe = new_Pipe(dev, 2, tx_ep, 0, tx_size); @@ -205,47 +217,74 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 break; } } + if (sertype == UNKNOWN) return false; // not one of ours + + // Lets try to locate the end points. Code is common across these devices + println("len = ", len); + uint8_t count_end_points = descriptors[4]; + if (count_end_points < 2) return false; // not enough end points + if (len < 23) return false; + if (descriptors[0] != 9) return false; // length 9 + + // Lets walk through end points and see if we + // can find an RX and TX bulk transfer end point. + //Example vid=67B, pid=2303 + // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 + //09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00 + uint32_t rxep = 0; + uint32_t txep = 0; + uint16_t rx_size = 0; + uint16_t tx_size = 0; + uint32_t descriptor_index = 9; + while (count_end_points-- && ((rxep == 0) || txep == 0)) { + if (descriptors[descriptor_index] != 7) return false; // length 7 + if (descriptors[descriptor_index+1] != 5) return false; // ep desc + if ((descriptors[descriptor_index+3] == 2) + && (descriptors[descriptor_index+4] <= 64) + && (descriptors[descriptor_index+5] == 0)) { + // have a bulk EP size + if (descriptors[descriptor_index+2] & 0x80 ) { + rxep = descriptors[descriptor_index+2]; + rx_size = descriptors[descriptor_index+4]; + } else { + txep = descriptors[descriptor_index+2]; + tx_size = descriptors[descriptor_index+4]; + } + } + descriptor_index += 7; // setup to look at next one... + } + // Try to verify the end points. + if (!check_rxtx_ep(rxep, txep)) return false; + print("USBSerial, rxep=", rxep & 15); + print("(", rx_size); + print("), txep=", txep); + print("(", tx_size); + println(")"); + + if (!init_buffers(rx_size, tx_size)) return false; + println(" rx buffer size:", rxsize); + println(" tx buffer size:", txsize); + + rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rx_size); + if (!rxpipe) return false; + txpipe = new_Pipe(dev, 2, txep, 0, tx_size); + if (!txpipe) { + //free_Pipe(rxpipe); + return false; + } + rxpipe->callback_function = rx_callback; + queue_Data_Transfer(rxpipe, rx1, rx_size, this); + rxstate = 1; + txstate = 0; + txpipe->callback_function = tx_callback; + baudrate = 115200; + + // Now do specific setup per type switch (sertype) { //--------------------------------------------------------------------- // FTDI case FTDI: { - // FTDI FT232 - println("len = ", len); - if (len < 23) return false; - if (descriptors[0] != 9) return false; // length 9 - if (descriptors[9] != 7) return false; // length 7 - if (descriptors[10] != 5) return false; // ep desc - uint32_t rxep = descriptors[11]; - if (descriptors[12] != 2) return false; // bulk type - if (descriptors[13] != 64) return false; // size 64 - if (descriptors[14] != 0) return false; - if (descriptors[16] != 7) return false; // length 7 - if (descriptors[17] != 5) return false; // ep desc - uint32_t txep = descriptors[18]; - if (descriptors[19] != 2) return false; // bulk type - if (descriptors[20] != 64) return false; // size 64 - if (descriptors[21] != 0) return false; - if (!check_rxtx_ep(rxep, txep)) return false; - print("FTDI, rxep=", rxep & 15); - println(", txep=", txep); - if (!init_buffers(64, 64)) return false; - rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); - if (!rxpipe) return false; - txpipe = new_Pipe(dev, 2, txep, 0, 64); - if (!txpipe) { - // TODO: free rxpipe - return false; - } - sertype = FTDI; - rxpipe->callback_function = rx_callback; - rxsize = 64; - queue_Data_Transfer(rxpipe, rx1, 64, this); - rxstate = 1; - txsize = 64; - txstate = 0; - txpipe->callback_function = tx_callback; - baudrate = 115200; pending_control = 0x0F; mk_setup(setup, 0x40, 0, 0, 0, 0); // reset port queue_Control_Transfer(dev, &setup, NULL, this); @@ -257,59 +296,6 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 // TODO: Note: there are probably more vendor/product pairs.. Maybe should create table of them case PL2303: { - // Prolific Technology, Inc. PL2303 Serial Port - println("len = ", len); - uint8_t count_end_points = descriptors[4]; - if (count_end_points < 2) return false; // not enough end points - if (len < 23) return false; - if (descriptors[0] != 9) return false; // length 9 - - // Lets walk through end points and see if we - // can find an RX and TX bulk transfer end point. - //vid=67B, pid=2303 - // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 - //09 04 00 00 03 FF 00 00 00 07 05 81 03 0A 00 01 07 05 02 02 40 00 00 07 05 83 02 40 00 00 - uint32_t rxep = 0; - uint32_t txep = 0; - uint32_t descriptor_index = 9; - while (count_end_points-- && ((rxep == 0) || txep == 0)) { - if (descriptors[descriptor_index] != 7) return false; // length 7 - if (descriptors[descriptor_index+1] != 5) return false; // ep desc - if ((descriptors[descriptor_index+3] == 2) - && (descriptors[descriptor_index+4] == 64) - && (descriptors[descriptor_index+5] == 0)) { - // have a bulk EP size - if (descriptors[descriptor_index+2] & 0x80 ) { - rxep = descriptors[descriptor_index+2]; - rxsize = descriptors[descriptor_index+4]; - } else { - txep = descriptors[descriptor_index+2]; - txsize = descriptors[descriptor_index+4]; - } - } - descriptor_index += 7; // setup to look at next one... - } - // Try to verify the end points. - if (!check_rxtx_ep(rxep, txep)) return false; - print("PL2303, rxep=", rxep & 15); - println(", txep=", txep); - if (!init_buffers(64, 64)) return false; - rxpipe = new_Pipe(dev, 2, rxep & 15, 1, 64); - if (!rxpipe) return false; - txpipe = new_Pipe(dev, 2, txep, 0, 64); - if (!txpipe) { - // TODO: free rxpipe - return false; - } - - sertype = PL2303; - rxpipe->callback_function = rx_callback; - queue_Data_Transfer(rxpipe, rx1, 64, this); - rxstate = 1; - txstate = 0; - txpipe->callback_function = tx_callback; - baudrate = 115200; - // First attempt keep it simple... println("PL2303: readRegister(0x04)"); // Need to setup the data the line coding data @@ -324,57 +310,6 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 // CH341 case CH341: { - println("len = ", len); - uint8_t count_end_points = descriptors[4]; - if (count_end_points < 2) return false; // not enough end points - if (len < 23) return false; - if (descriptors[0] != 9) return false; // length 9 - - // Lets walk through end points and see if we - // can find an RX and TX bulk transfer end point. - // vid=1A86, pid=7523, bDeviceClass = 255, bDeviceSubClass = 0, bDeviceProtocol = 0 - // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 - //09 04 00 00 03 FF 01 02 00 07 05 82 02 20 00 00 07 05 02 02 20 00 00 07 05 81 03 08 00 01 - uint32_t rxep = 0; - uint32_t txep = 0; - uint32_t descriptor_index = 9; - while (count_end_points-- && ((rxep == 0) || txep == 0)) { - if (descriptors[descriptor_index] != 7) return false; // length 7 - if (descriptors[descriptor_index+1] != 5) return false; // ep desc - if ((descriptors[descriptor_index+3] == 2) - && (descriptors[descriptor_index+4] <= 64) - && (descriptors[descriptor_index+5] == 0)) { - // have a bulk EP size - if (descriptors[descriptor_index+2] & 0x80 ) { - rxep = descriptors[descriptor_index+2]; - rxsize = descriptors[descriptor_index+4]; - } else { - txep = descriptors[descriptor_index+2]; - txsize = descriptors[descriptor_index+4]; - } - } - descriptor_index += 7; // setup to look at next one... - } - // Try to verify the end points. - if (!check_rxtx_ep(rxep, txep)) return false; - print("ch341, rxep=", rxep & 15); - println(", txep=", txep); - if (!init_buffers(rxsize, txsize)) return false; - rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rxsize); - if (!rxpipe) return false; - txpipe = new_Pipe(dev, 2, txep, 0, txsize); - if (!txpipe) { - // TODO: free rxpipe - return false; - } - - rxpipe->callback_function = rx_callback; - queue_Data_Transfer(rxpipe, rx1, rxsize, this); - rxstate = 1; - txstate = 0; - txpipe->callback_function = tx_callback; - baudrate = 115200; - println("CH341: 0xC0, 0x5f, 0, 0, 8"); // Need to setup the data the line coding data mk_setup(setup, 0xC0, 0x5f, 0, 0, sizeof(setupdata)); @@ -388,58 +323,6 @@ bool USBSerial::claim(Device_t *dev, int type, const uint8_t *descriptors, uint3 // CP210X case CP210X: { - println("len = ", len); - uint8_t count_end_points = descriptors[4]; - if (count_end_points < 2) return false; // not enough end points - if (len < 23) return false; - if (descriptors[0] != 9) return false; // length 9 - - // Lets walk through end points and see if we - // can find an RX and TX bulk transfer end point. - // BUGBUG: should factor out this code as duplicated... - // vid=10C4, pid=EA60, bDeviceClass = 0, bDeviceSubClass = 0, bDeviceProtocol = 0 - // 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 - //09 04 00 00 02 FF 00 00 02 07 05 81 02 40 00 00 07 05 01 02 40 00 00 - uint32_t rxep = 0; - uint32_t txep = 0; - uint32_t descriptor_index = 9; - while (count_end_points-- && ((rxep == 0) || txep == 0)) { - if (descriptors[descriptor_index] != 7) return false; // length 7 - if (descriptors[descriptor_index+1] != 5) return false; // ep desc - if ((descriptors[descriptor_index+3] == 2) - && (descriptors[descriptor_index+4] <= 64) - && (descriptors[descriptor_index+5] == 0)) { - // have a bulk EP size - if (descriptors[descriptor_index+2] & 0x80 ) { - rxep = descriptors[descriptor_index+2]; - rxsize = descriptors[descriptor_index+4]; - } else { - txep = descriptors[descriptor_index+2]; - txsize = descriptors[descriptor_index+4]; - } - } - descriptor_index += 7; // setup to look at next one... - } - // Try to verify the end points. - if (!check_rxtx_ep(rxep, txep)) return false; - print("CP210X, rxep=", rxep & 15); - println(", txep=", txep); - if (!init_buffers(rxsize, txsize)) return false; - rxpipe = new_Pipe(dev, 2, rxep & 15, 1, rxsize); - if (!rxpipe) return false; - txpipe = new_Pipe(dev, 2, txep, 0, txsize); - if (!txpipe) { - // TODO: free rxpipe - return false; - } - - rxpipe->callback_function = rx_callback; - queue_Data_Transfer(rxpipe, rx1, rxsize, this); - rxstate = 1; - txstate = 0; - txpipe->callback_function = tx_callback; - baudrate = 115200; - println("CP210X: 0x41, 0x11, 0, 0, 0 - reset port"); // Need to setup the data the line coding data mk_setup(setup, 0x41, 0x11, 0, 0, 0); @@ -1102,6 +985,7 @@ void USBSerial::rx_data(const Transfer_t *transfer) { uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF); + debugDigitalToggle(6); // first update rxstate bitmask, since buffer is no longer queued if (transfer->buffer == rx1) { rxstate &= 0xFE; @@ -1119,6 +1003,11 @@ void USBSerial::rx_data(const Transfer_t *transfer) } } if (len > 0) { + print("rx token: ", transfer->qtd.token, HEX); + print(" transfer length: ", transfer->length, DEC); + print(" len:", len, DEC); + print(" - ", *p, HEX); + println(" ", *(p+1), HEX); print("rx: "); print_hexbytes(p, len); } @@ -1185,6 +1074,7 @@ void USBSerial::tx_data(const Transfer_t *transfer) { uint32_t mask; uint8_t *p = (uint8_t *)transfer->buffer; + debugDigitalWrite(5, HIGH); if (p == tx1) { println("tx1:"); mask = 1; @@ -1194,6 +1084,7 @@ void USBSerial::tx_data(const Transfer_t *transfer) mask = 2; //txstate &= 0xFD; } else { + debugDigitalWrite(5, LOW); return; // should never happen } // check how much more data remains in the transmit buffer @@ -1206,50 +1097,72 @@ void USBSerial::tx_data(const Transfer_t *transfer) count = txsize + head - tail; } uint32_t packetsize = tx2 - tx1; - if (count < packetsize) { + // Only output full packets unless the flush bit was set. + if ((count == 0) || ((count < packetsize) && ((txstate & 0x4) == 0) )) { // not enough data in buffer to fill a full packet - txstate &= ~mask; + txstate &= ~(mask | 4); // turn off that transfer and make sure the flush bit is not set + debugDigitalWrite(5, LOW); return; } // immediately transmit another full packet, if we have enough data + if (count >= packetsize) count = packetsize; + else txstate &= ~(mask | 4); // This packet will complete any outstanding flush + println("TX:moar data!!!!"); if (++tail >= txsize) tail = 0; uint32_t n = txsize - tail; - if (n > packetsize) n = packetsize; + if (n > count) n = count; memcpy(p, txbuf + tail, n); - if (n >= packetsize) { + if (n >= count) { tail += n - 1; if (tail >= txsize) tail = 0; } else { - uint32_t len = packetsize - n; + uint32_t len = count - n; memcpy(p + n, txbuf, len); tail = len - 1; } txtail = tail; - queue_Data_Transfer(txpipe, p, packetsize, this); + queue_Data_Transfer(txpipe, p, count, this); + debugDigitalWrite(5, LOW); } +void USBSerial::flush() +{ + print("USBSerial::flush"); + if (txhead == txtail) { + println(" - Empty"); + return; // empty. + } + debugDigitalWrite(32, HIGH); + NVIC_DISABLE_IRQ(IRQ_USBHS); + txtimer.stop(); // Stop longer timer. + txtimer.start(100); // Start a mimimal timeout +// timer_event(nullptr); // Try calling direct - fails to work + NVIC_ENABLE_IRQ(IRQ_USBHS); + while (txstate & 3) ; // wait for all of the USB packets to be sent. + println(" completed"); + debugDigitalWrite(32, LOW); +} + + void USBSerial::timer_event(USBDriverTimer *whichTimer) { + debugDigitalWrite(7, HIGH); println("txtimer"); uint32_t count; uint32_t head = txhead; uint32_t tail = txtail; - if (pending_control) { - // We are still doing setup postpone for awhile.. - txtimer.start(1200); - println(" Postpone: setup pending_control"); - return; // no outgoing buffers available, try again later - } if (head == tail) { println(" *** Empty ***"); + debugDigitalWrite(7, LOW); return; // nothing to transmit } else if (head > tail) { count = head - tail; } else { count = txsize + head - tail; } + uint8_t *p; if ((txstate & 0x01) == 0) { p = tx1; @@ -1258,10 +1171,20 @@ void USBSerial::timer_event(USBDriverTimer *whichTimer) p = tx2; txstate |= 0x02; } else { - txtimer.start(1200); + txstate |= 4; // Tell the TX code to do flush code. println(" *** No buffers ***"); + debugDigitalWrite(7, LOW); return; // no outgoing buffers available, try again later } + + uint32_t packetsize = tx2 - tx1; + + // Possible for remaining ? packet size and not have both? + if (count > packetsize) { + txstate |= 4; // One of the active transfers will handle the remaining parts + count = packetsize; + } + if (++tail >= txsize) tail = 0; uint32_t n = txsize - tail; if (n > count) n = count; @@ -1279,6 +1202,7 @@ void USBSerial::timer_event(USBDriverTimer *whichTimer) print(") "); print_hexbytes(p, count); queue_Data_Transfer(txpipe, p, count, this); + debugDigitalWrite(7, LOW); } @@ -1427,14 +1351,16 @@ size_t USBSerial::write(uint8_t c) } txtail = tail; //println("queue tx packet, newtail=", tail); + debugDigitalWrite(7, HIGH); queue_Data_Transfer(txpipe, p, packetsize, this); + debugDigitalWrite(7, LOW); NVIC_ENABLE_IRQ(IRQ_USBHS); return 1; } } // otherwise, set a latency timer to later transmit partial packet txtimer.stop(); - txtimer.start(3500); + txtimer.start(write_timeout_); NVIC_ENABLE_IRQ(IRQ_USBHS); return 1; } From ca070ebcd41e3df01bb2a74f4883dd42f76bb335 Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Sat, 11 Nov 2017 09:56:46 -0800 Subject: [PATCH 07/11] Fix delete delete hang on serial ports Found the issue where it was freeing transfers from the follow up list, that were also on the QH list which also tried to free it again. Fix was in the first free list, it checks to see if the item is also in the qh list and does not free it then. --- ehci.cpp | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/ehci.cpp b/ehci.cpp index 2fb2949..21c97eb 100644 --- a/ehci.cpp +++ b/ehci.cpp @@ -1252,12 +1252,38 @@ void USBHost::delete_Pipe(Pipe_t *pipe) // TODO: does this write interfere UPI & UAI (bits 18 & 19) ?? } // find & free all the transfers which completed + println(" Free transfers"); Transfer_t *t = async_followup_first; +#if 0 + if (t) { + println(" (Look at QH list first)"); + Transfer_t *tr = (Transfer_t *)(pipe->qh.next); + while ((uint32_t)tr & 0xFFFFFFE0) { + println(" $ ", (uint32_t)tr); + tr = (Transfer_t *)(tr->qtd.next); + } + } +#endif while (t) { + print(" * ", (uint32_t)t); Transfer_t *next = t->next_followup; if (t->pipe == pipe) { + print(" * remove"); remove_from_async_followup_list(t); - free_Transfer(t); + + // Only free if not in QH list + Transfer_t *tr = (Transfer_t *)(pipe->qh.next); + while (((uint32_t)tr & 0xFFFFFFE0) && (tr != t)){ + tr = (Transfer_t *)(tr->qtd.next); + } + if (tr == t) { + println(" * defer free until QH"); + } else { + println(" * free"); + free_Transfer(t); // The later code should actually free it... + } + } else { + println(""); } t = next; } @@ -1300,14 +1326,17 @@ void USBHost::delete_Pipe(Pipe_t *pipe) // TODO: do we need to look at pipe->qh.current ?? // // free all the transfers still attached to the QH + println(" Free transfers attached to QH"); Transfer_t *tr = (Transfer_t *)(pipe->qh.next); while ((uint32_t)tr & 0xFFFFFFE0) { + println(" * ", (uint32_t)tr); Transfer_t *next = (Transfer_t *)(tr->qtd.next); free_Transfer(tr); tr = next; } // hopefully we found everything... free_Pipe(pipe); + println("* Delete Pipe completed"); } From 5737d12b622e8f3e2a4c737f2c2aa3179dbcace7 Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Sat, 11 Nov 2017 13:20:04 -0800 Subject: [PATCH 08/11] Fix hang part 2... I am not sure if you can also get the same hang in the periodic update list as well, but to be on the safe side added same checks as the one that was hanging. --- ehci.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/ehci.cpp b/ehci.cpp index 21c97eb..d0d5427 100644 --- a/ehci.cpp +++ b/ehci.cpp @@ -1254,16 +1254,6 @@ void USBHost::delete_Pipe(Pipe_t *pipe) // find & free all the transfers which completed println(" Free transfers"); Transfer_t *t = async_followup_first; -#if 0 - if (t) { - println(" (Look at QH list first)"); - Transfer_t *tr = (Transfer_t *)(pipe->qh.next); - while ((uint32_t)tr & 0xFFFFFFE0) { - println(" $ ", (uint32_t)tr); - tr = (Transfer_t *)(tr->qtd.next); - } - } -#endif while (t) { print(" * ", (uint32_t)t); Transfer_t *next = t->next_followup; @@ -1312,12 +1302,28 @@ void USBHost::delete_Pipe(Pipe_t *pipe) // TODO: subtract bandwidth from uframe_bandwidth array // find & free all the transfers which completed + println(" Free transfers"); Transfer_t *t = periodic_followup_first; while (t) { + print(" * ", (uint32_t)t); Transfer_t *next = t->next_followup; if (t->pipe == pipe) { + print(" * remove"); remove_from_periodic_followup_list(t); - free_Transfer(t); + + // Only free if not in QH list + Transfer_t *tr = (Transfer_t *)(pipe->qh.next); + while (((uint32_t)tr & 0xFFFFFFE0) && (tr != t)){ + tr = (Transfer_t *)(tr->qtd.next); + } + if (tr == t) { + println(" * defer free until QH"); + } else { + println(" * free"); + free_Transfer(t); // The later code should actually free it... + } + } else { + println(""); } t = next; } From 8aa67ff939b00911de37bee2b82c44fc1aa5fddd Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Tue, 14 Nov 2017 08:42:28 -0800 Subject: [PATCH 09/11] RAWHID Support Needed HID Parser to support Bidirectional Transfers The HidParser code was setup such that the claim for a report, the caller could say I want to claim the whole thinig and allowed callback functions for processing of in buffer and out buffer. Allow RawHID to contribute Transfer_t Since RawHID may need more resources than most, maybe it should contribute the additional structures The constructor for a RAWHID object allows you to specify the top usage that it wishes to connect to. I used this for example to be able to connect to a Teensy with the RAWHID associated with emulating the Serial object. If a HID Input class says that it wants to claim the whole interface, I reuse the buffer associated with holding the HID descriptor and use it for output buffers. --- USBHost_t36.h | 60 +++++++++++++++++-- examples/Mouse/Mouse.ino | 58 +++++++++++++++++- examples/Mouse/defs.h | 0 examples/SerialTest/defs.h | 0 hid.cpp | 67 +++++++++++++++++---- joystick.cpp | 8 +-- keyboardHIDExtras.cpp | 8 +-- keywords.txt | 6 ++ mouse.cpp | 8 +-- rawhid.cpp | 120 +++++++++++++++++++++++++++++++++++++ 10 files changed, 305 insertions(+), 30 deletions(-) create mode 100644 examples/Mouse/defs.h create mode 100644 examples/SerialTest/defs.h create mode 100644 rawhid.cpp diff --git a/USBHost_t36.h b/USBHost_t36.h index 6e03b83..b36b45c 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -80,6 +80,7 @@ class USBHost; typedef struct Device_struct Device_t; typedef struct Pipe_struct Pipe_t; typedef struct Transfer_struct Transfer_t; +typedef enum { CLAIM_NO=0, CLAIM_REPORT, CLAIM_INTERFACE} hidclaim_t; // All USB device drivers inherit use these classes. // Drivers build user-visible functionality on top @@ -269,11 +270,12 @@ protected: static void disconnect_Device(Device_t *dev); static void enumeration(const Transfer_t *transfer); static void driver_ready_for_device(USBDriver *driver); + static volatile bool enumeration_busy; +public: // Maybe others may want/need to contribute memory example HID devices may want to add transfers. static void contribute_Devices(Device_t *devices, uint32_t num); static void contribute_Pipes(Pipe_t *pipes, uint32_t num); static void contribute_Transfers(Transfer_t *transfers, uint32_t num); static void contribute_String_Buffers(strbuf_t *strbuf, uint32_t num); - static volatile bool enumeration_busy; private: static void isr(); static void convertStringDescriptorToASCIIString(uint8_t string_index, Device_t *dev, const Transfer_t *transfer); @@ -469,6 +471,8 @@ private: // Device drivers may inherit from this base class, if they wish to receive // HID input data fully decoded by the USBHIDParser driver +class USBHIDParser; + class USBHIDInput { public: operator bool() { return (mydevice != nullptr); } @@ -482,7 +486,9 @@ public: { return ((mydevice == nullptr) || (mydevice->strbuf == nullptr)) ? nullptr : &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; } private: - virtual bool claim_collection(Device_t *dev, uint32_t topusage); + virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); + virtual bool hid_process_in_data(const Transfer_t *transfer) {return false;} + virtual bool hid_process_out_data(const Transfer_t *transfer) {return false;} virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); virtual void hid_input_data(uint32_t usage, int32_t value); virtual void hid_input_end(); @@ -576,10 +582,12 @@ private: //-------------------------------------------------------------------------- + class USBHIDParser : public USBDriver { public: USBHIDParser(USBHost &host) { init(); } static void driver_ready_for_hid_collection(USBHIDInput *driver); + bool sendPacket(const uint8_t *buffer); protected: enum { TOPUSAGE_LIST_LEN = 4 }; enum { USAGE_LIST_LEN = 24 }; @@ -595,6 +603,14 @@ protected: USBHIDInput * find_driver(uint32_t topusage); void parse(uint16_t type_and_report_id, const uint8_t *data, uint32_t len); void init(); + + // Atempt for RAWhid to take over processing of data + // + uint16_t inSize(void) {return in_size;} + uint16_t outSize(void) {return out_size;} + + uint8_t activeSendMask(void) {return txstate;} + private: Pipe_t *in_pipe; Pipe_t *out_pipe; @@ -611,6 +627,10 @@ private: Pipe_t mypipes[3] __attribute__ ((aligned(32))); Transfer_t mytransfers[4] __attribute__ ((aligned(32))); strbuf_t mystring_bufs[1]; + uint8_t txstate = 0; + uint8_t *tx1 = nullptr; + uint8_t *tx2 = nullptr; + bool hid_driver_claimed_control_ = false; }; //-------------------------------------------------------------------------- @@ -686,7 +706,7 @@ public: enum {MAX_KEYS_DOWN=4}; // uint32_t buttons() { return buttons_; } protected: - virtual bool claim_collection(Device_t *dev, uint32_t topusage); + virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); virtual void hid_input_data(uint32_t usage, int32_t value); virtual void hid_input_end(); @@ -716,7 +736,7 @@ public: int getWheel() { return wheel; } int getWheelH() { return wheelH; } protected: - virtual bool claim_collection(Device_t *dev, uint32_t topusage); + virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); virtual void hid_input_data(uint32_t usage, int32_t value); virtual void hid_input_end(); @@ -742,7 +762,7 @@ public: uint32_t getButtons() { return buttons; } int getAxis(uint32_t index) { return (index < (sizeof(axis)/sizeof(axis[0]))) ? axis[index] : 0; } protected: - virtual bool claim_collection(Device_t *dev, uint32_t topusage); + virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); virtual void hid_input_data(uint32_t usage, int32_t value); virtual void hid_input_end(); @@ -1204,5 +1224,35 @@ private: uint16_t wheelCircumference; // default is WHEEL_CIRCUMFERENCE (2122cm) }; +//-------------------------------------------------------------------------- + +class RawHIDController : public USBHIDInput { +public: + RawHIDController(USBHost &host, uint32_t usage = 0) : fixed_usage_(usage) { init(); } + uint32_t usage(void) {return usage_;} + void attachReceive(bool (*f)(uint32_t usage, const uint8_t *data, uint32_t len)) {receiveCB = f;} + bool sendPacket(const uint8_t *buffer); +protected: + virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); + virtual bool hid_process_in_data(const Transfer_t *transfer); + virtual bool hid_process_out_data(const Transfer_t *transfer); + virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); + virtual void hid_input_data(uint32_t usage, int32_t value); + virtual void hid_input_end(); + virtual void disconnect_collection(Device_t *dev); +private: + void init(); + USBHIDParser *driver_; + enum { MAX_PACKET_SIZE = 64 }; + bool (*receiveCB)(uint32_t usage, const uint8_t *data, uint32_t len) = nullptr; + uint8_t collections_claimed = 0; + //volatile bool hid_input_begin_ = false; + uint32_t fixed_usage_; + uint32_t usage_ = 0; + + // See if we can contribute transfers + Transfer_t mytransfers[2] __attribute__ ((aligned(32))); + +}; #endif diff --git a/examples/Mouse/Mouse.ino b/examples/Mouse/Mouse.ino index 14955a9..f829d20 100644 --- a/examples/Mouse/Mouse.ino +++ b/examples/Mouse/Mouse.ino @@ -19,6 +19,8 @@ USBHIDParser hid4(myusb); USBHIDParser hid5(myusb); MouseController mouse1(myusb); JoystickController joystick1(myusb); +RawHIDController rawhid1(myusb); +RawHIDController rawhid2(myusb, 0xffc90004); USBDriver *drivers[] = {&hub1, &hub2, &hub3, &hub4, &keyboard1, &keyboard2, &hid1, &hid2, &hid3, &hid4, &hid5}; #define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0])) @@ -26,9 +28,9 @@ const char * driver_names[CNT_DEVICES] = {"Hub1","Hub2", "Hub3", "Hub4" "KB1", " bool driver_active[CNT_DEVICES] = {false, false, false, false}; // Lets also look at HID Input devices -USBHIDInput *hiddrivers[] = {&mouse1, &joystick1}; +USBHIDInput *hiddrivers[] = {&mouse1, &joystick1, &rawhid1, &rawhid2}; #define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) -const char * hid_driver_names[CNT_DEVICES] = {"Mouse1","Joystick1"}; +const char * hid_driver_names[CNT_DEVICES] = {"Mouse1","Joystick1", "RawHid1", "RawHid2"}; bool hid_driver_active[CNT_DEVICES] = {false, false}; @@ -42,6 +44,9 @@ void setup() keyboard2.attachPress(OnPress); hidextras.attachPress(OnHIDExtrasPress); hidextras.attachRelease(OnHIDExtrasRelease); + + rawhid1.attachReceive(OnReceiveHidData); + rawhid2.attachReceive(OnReceiveHidData); } @@ -123,6 +128,20 @@ void loop() Serial.println(); joystick1.joystickDataClear(); } + + // See if we have some RAW data + if (rawhid1) { + int ch; + uint8_t buffer[64]; + uint8_t count_chars = 0; + memset(buffer, 0, sizeof(buffer)); + if (Serial.available()) { + while (((ch = Serial.read()) != -1) && (count_chars < sizeof(buffer))) { + buffer[count_chars++] = ch; + } + rawhid1.sendPacket(buffer); + } + } } @@ -421,3 +440,38 @@ void OnHIDExtrasRelease(uint32_t top, uint16_t key) Serial.print(") key release:"); Serial.println(key, HEX); } + +bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) { + // Called for maybe both HIDS for rawhid basic test. One is for the Teensy + // to output to Serial. while still having Raw Hid... + if (usage == 0xffc90004) { + // Lets trim off trailing null characters. + while ((len > 0) && (data[len-1] == 0)) { + len--; + } + if (len) { + Serial.print("RawHid Serial: "); + Serial.write(data, len); + } + } else { + Serial.print("RawHID data: "); + Serial.println(usage, HEX); + while (len) { + uint8_t cb = (len > 16)? 16 : len; + const uint8_t *p = data; + uint8_t i; + for (i = 0; i < cb; i++) { + Serial.printf("%02x ", *p++); + } + Serial.print(": "); + for (i = 0; i < cb; i++) { + Serial.write(((*data >= ' ')&&(*data <= '~'))? *data : '.'); + data++; + } + len -= cb; + Serial.println(); + } + } + + return true; +} diff --git a/examples/Mouse/defs.h b/examples/Mouse/defs.h new file mode 100644 index 0000000..e69de29 diff --git a/examples/SerialTest/defs.h b/examples/SerialTest/defs.h new file mode 100644 index 0000000..e69de29 diff --git a/hid.cpp b/hid.cpp index 2d320b9..3a8bd0a 100644 --- a/hid.cpp +++ b/hid.cpp @@ -128,21 +128,19 @@ bool USBHIDParser::claim(Device_t *dev, int type, const uint8_t *descriptors, ui if (((endpoint1 & 0xF0) == 0x80) && ((endpoint2 & 0xF0) == 0)) { // first endpoint is IN, second endpoint is OUT in_pipe = new_Pipe(dev, 3, endpoint1 & 0x0F, 1, size1, interval1); - //out_pipe = new_Pipe(dev, 3, endpoint2, 0, size2, interval2); - out_pipe = NULL; // TODO; fixme + out_pipe = new_Pipe(dev, 3, endpoint2, 0, size2, interval2); in_size = size1; out_size = size2; } else if (((endpoint1 & 0xF0) == 0) && ((endpoint2 & 0xF0) == 0x80)) { // first endpoint is OUT, second endpoint is IN in_pipe = new_Pipe(dev, 3, endpoint2 & 0x0F, 1, size2, interval2); - //out_pipe = new_Pipe(dev, 3, endpoint1, 0, size1, interval1); - out_pipe = NULL; // TODO; fixme + out_pipe = new_Pipe(dev, 3, endpoint1, 0, size1, interval1); in_size = size2; out_size = size1; } else { return false; } - //out_pipe->callback_function = out_callback; + out_pipe->callback_function = out_callback; } in_pipe->callback_function = in_callback; for (uint32_t i=0; i < TOPUSAGE_LIST_LEN; i++) { @@ -185,6 +183,7 @@ void USBHIDParser::in_callback(const Transfer_t *transfer) void USBHIDParser::out_callback(const Transfer_t *transfer) { + //println("USBHIDParser:: out_callback (static)"); if (transfer->driver) { ((USBHIDParser*)(transfer->driver))->out_data(transfer); } @@ -215,14 +214,22 @@ void USBHIDParser::in_data(const Transfer_t *transfer) Serial.println(); */ print("HID: "); + print(use_report_id); + print(" - "); print_hexbytes(transfer->buffer, transfer->length); const uint8_t *buf = (const uint8_t *)transfer->buffer; uint32_t len = transfer->length; - if (use_report_id == false) { - parse(0x0100, buf, len); - } else { - if (len > 1) { - parse(0x0100 | buf[0], buf + 1, len - 1); + + // See if the first top report wishes to bypass the + // parse... + if (!(topusage_drivers[0] && topusage_drivers[0]->hid_process_in_data(transfer))) { + + if (use_report_id == false) { + parse(0x0100, buf, len); + } else { + if (len > 1) { + parse(0x0100 | buf[0], buf + 1, len - 1); + } } } queue_Data_Transfer(in_pipe, report, in_size, this); @@ -231,8 +238,44 @@ void USBHIDParser::in_data(const Transfer_t *transfer) void USBHIDParser::out_data(const Transfer_t *transfer) { + println("USBHIDParser:out_data called (instance)"); + // A packet completed. lets mark it as done and call back + // to top reports handler. We unmark our checkmark to + // handle case where they may want to queue up another one. + if (transfer->buffer == tx1) txstate &= ~1; + if (transfer->buffer == tx2) txstate &= ~2; + if (topusage_drivers[0]) { + topusage_drivers[0]->hid_process_out_data(transfer); + } } +bool USBHIDParser::sendPacket(const uint8_t *buffer) { + if (!out_size || !out_pipe) return false; + if (!tx1) { + // Was not init before, for now lets put it at end of descriptor + // TODO: should verify that either don't exceed overlap descsize + // Or that we have taken over this device + tx1 = &descriptor[sizeof(descriptor) - out_size]; + tx2 = tx1 - out_size; + } + if ((txstate & 3) == 3) return false; // both transmit buffers are full + uint8_t *p = tx1; + if ((txstate & 1) == 0) { + txstate |= 1; + } else { + txstate |= 2; + p = tx2; + } + // copy the users data into our out going buffer + memcpy(p, buffer, out_size); + println("USBHIDParser Send packet"); + print_hexbytes(buffer, out_size); + queue_Data_Transfer(out_pipe, p, out_size, this); + println(" Queue_data transfer returned"); + return true; +} + + // This no-inputs parse is meant to be used when we first get the // HID report descriptor. It finds all the top level collections // and allows drivers to claim them. This is always where we @@ -333,9 +376,11 @@ USBHIDInput * USBHIDParser::find_driver(uint32_t topusage) { println("find_driver"); USBHIDInput *driver = available_hid_drivers_list; + hidclaim_t claim_type; while (driver) { println(" driver ", (uint32_t)driver, HEX); - if (driver->claim_collection(device, topusage)) { + if ((claim_type = driver->claim_collection(this, device, topusage)) != CLAIM_NO) { + if (claim_type == CLAIM_INTERFACE) hid_driver_claimed_control_ = true; return driver; } driver = driver->next; diff --git a/joystick.cpp b/joystick.cpp index 1c179c7..6617867 100644 --- a/joystick.cpp +++ b/joystick.cpp @@ -26,16 +26,16 @@ -bool JoystickController::claim_collection(Device_t *dev, uint32_t topusage) +hidclaim_t JoystickController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) { // only claim Desktop/Joystick and Desktop/Gamepad - if (topusage != 0x10004 && topusage != 0x10005) return false; + if (topusage != 0x10004 && topusage != 0x10005) return CLAIM_NO; // only claim from one physical device - if (mydevice != NULL && dev != mydevice) return false; + if (mydevice != NULL && dev != mydevice) return CLAIM_NO; mydevice = dev; collections_claimed++; anychange = true; // always report values on first read - return true; + return CLAIM_REPORT; } void JoystickController::disconnect_collection(Device_t *dev) diff --git a/keyboardHIDExtras.cpp b/keyboardHIDExtras.cpp index f98eed1..1a14612 100644 --- a/keyboardHIDExtras.cpp +++ b/keyboardHIDExtras.cpp @@ -27,19 +27,19 @@ #define TOPUSAGE_SYS_CONTROL 0x10080 #define TOPUSAGE_CONSUMER_CONTROL 0x0c0001 -bool KeyboardHIDExtrasController::claim_collection(Device_t *dev, uint32_t topusage) +hidclaim_t KeyboardHIDExtrasController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) { // Lets try to claim a few specific Keyboard related collection/reports //Serial.printf("KBH Claim %x\n", topusage); if ((topusage != TOPUSAGE_SYS_CONTROL) && (topusage != TOPUSAGE_CONSUMER_CONTROL) - ) return false; + ) return CLAIM_NO; // only claim from one physical device //Serial.println("KeyboardHIDExtrasController claim collection"); - if (mydevice != NULL && dev != mydevice) return false; + if (mydevice != NULL && dev != mydevice) return CLAIM_NO; mydevice = dev; collections_claimed_++; - return true; + return CLAIM_REPORT; } void KeyboardHIDExtrasController::disconnect_collection(Device_t *dev) diff --git a/keywords.txt b/keywords.txt index a7d6416..b7d2538 100644 --- a/keywords.txt +++ b/keywords.txt @@ -9,6 +9,7 @@ USBSerial KEYWORD1 AntPlus KEYWORD1 JoystickController KEYWORD1 KeyboardHIDExtrasController KEYWORD1 +RawHIDController KEYWORD1 # Common Functions Task KEYWORD2 @@ -86,3 +87,8 @@ USBHOST_SERIAL_8N1 LITERAL1 USBHOST_SERIAL_8N2 LITERAL1 USBHOST_SERIAL_8E1 LITERAL1 USBHOST_SERIAL_8O1 LITERAL1 + +# RAWHid +usage KEYWORD2 +attachReceive KEYWORD2 +sendPacket KEYWORD2 diff --git a/mouse.cpp b/mouse.cpp index 8ab1219..79fa395 100644 --- a/mouse.cpp +++ b/mouse.cpp @@ -26,15 +26,15 @@ -bool MouseController::claim_collection(Device_t *dev, uint32_t topusage) +hidclaim_t MouseController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) { // only claim Desktop/Mouse - if (topusage != 0x10002) return false; + if (topusage != 0x10002) return CLAIM_NO; // only claim from one physical device - if (mydevice != NULL && dev != mydevice) return false; + if (mydevice != NULL && dev != mydevice) return CLAIM_NO; mydevice = dev; collections_claimed++; - return true; + return CLAIM_REPORT; } void MouseController::disconnect_collection(Device_t *dev) diff --git a/rawhid.cpp b/rawhid.cpp new file mode 100644 index 0000000..29439b1 --- /dev/null +++ b/rawhid.cpp @@ -0,0 +1,120 @@ +/* USB EHCI Host for Teensy 3.6 + * Copyright 2017 Paul Stoffregen (paul@pjrc.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "USBHost_t36.h" // Read this header first for key info + +void RawHIDController::init() +{ + USBHost::contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); + USBHIDParser::driver_ready_for_hid_collection(this); +} + +hidclaim_t RawHIDController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) +{ + // only claim RAWHID devices currently: 16c0:0486 +#ifdef USBHOST_PRINT_DEBUG + Serial.printf("Rawhid Claim: %x:%x usage: %x\n", dev->idVendor, dev->idProduct, topusage); +#endif + + if ((dev->idVendor != 0x16c0 || (dev->idProduct) != 0x486)) return CLAIM_NO; + if (mydevice != NULL && dev != mydevice) return CLAIM_NO; + if (usage_) return CLAIM_NO; // Only claim one + if (fixed_usage_ && (fixed_usage_ != topusage)) return CLAIM_NO; // See if we want specific one and if so is it this one + mydevice = dev; + collections_claimed++; + usage_ = topusage; + driver_ = driver; // remember the driver. + return CLAIM_INTERFACE; // We wa +} + +void RawHIDController::disconnect_collection(Device_t *dev) +{ + if (--collections_claimed == 0) { + mydevice = NULL; + usage_ = 0; + } +} + +bool RawHIDController::hid_process_in_data(const Transfer_t *transfer) +{ +#ifdef USBHOST_PRINT_DEBUG + Serial.printf("RawHIDController::hid_process_in_data: %x\n", usage_); +#endif + + if (receiveCB) { + return (*receiveCB)(usage_, (const uint8_t *)transfer->buffer, transfer->length); + } + return true; +} + +bool RawHIDController::hid_process_out_data(const Transfer_t *transfer) +{ +#ifdef USBHOST_PRINT_DEBUG + Serial.printf("RawHIDController::hid_process_out_data: %x\n", usage_); +#endif + return true; +} + +bool RawHIDController::sendPacket(const uint8_t *buffer) +{ + if (!driver_) return false; + return driver_->sendPacket(buffer); +} + + + +void RawHIDController::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) +{ + // These should not be called as we are claiming the whole interface and not + // allowing the parse to happen +#ifdef USBHOST_PRINT_DEBUG + Serial.printf("RawHID::hid_input_begin %x %x %x %x\n", topusage, type, lgmin, lgmax); +#endif + //hid_input_begin_ = true; +} + +void RawHIDController::hid_input_data(uint32_t usage, int32_t value) +{ + // These should not be called as we are claiming the whole interface and not + // allowing the parse to happen +#ifdef USBHOST_PRINT_DEBUG + Serial.printf("RawHID: usage=%X, value=%d", usage, value); + if ((value >= ' ') && (value <='~')) Serial.printf("(%c)", value); + Serial.println(); +#endif +} + +void RawHIDController::hid_input_end() +{ + // These should not be called as we are claiming the whole interface and not + // allowing the parse to happen +#ifdef USBHOST_PRINT_DEBUG + Serial.println("RawHID::hid_input_end"); +#endif +// if (hid_input_begin_) { +// hid_input_begin_ = false; +// } +} + + From c5edb1c25f80895376c5330aa808bb913a2999e4 Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Mon, 20 Nov 2017 09:19:25 -0800 Subject: [PATCH 10/11] Add Xbox One controller support While the other Joystick objects currently supported by the USB Host code are driven by HID data, the Xbox does not contain HID data and instead works at the top level. To handle this I made the Joystick object use multiple InHeritence like: class JoystickController : public USBDriver, public USBHIDInput This allowed me to have the object work either way. This did add some complexity in that some of the methods like is the object connected (The bool operator) had to be overwritten as both of the bases classes had it. In addition needed to update the other query functions to be able to grab the data from mydevice or device depending on which type of controller was connected. Since this looked like a valid way, I then merged the Keyboard Extras code that Keyboard and made it also do a similar multiple inheritance. In this case however I restricted the HID top level report handle code to only accept it if on the same object that claimed the keyboard. The mouse test app was updated as well --- USBHost_t36.h | 107 +++++++++++++------ examples/Mouse/Mouse.ino | 34 +++--- joystick.cpp | 222 ++++++++++++++++++++++++++++++++++++++- keyboard.cpp | 90 ++++++++++++++++ keyboardHIDExtras.cpp | 123 ---------------------- keywords.txt | 4 +- 6 files changed, 404 insertions(+), 176 deletions(-) delete mode 100644 keyboardHIDExtras.cpp diff --git a/USBHost_t36.h b/USBHost_t36.h index b36b45c..bb0ac03 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -635,7 +635,7 @@ private: //-------------------------------------------------------------------------- -class KeyboardController : public USBDriver /* , public USBHIDInput */ { +class KeyboardController : public USBDriver , public USBHIDInput { public: typedef union { struct { @@ -651,8 +651,10 @@ typedef union { public: KeyboardController(USBHost &host) { init(); } KeyboardController(USBHost *host) { init(); } - int available(); - int read(); + + // Some methods are in both public classes so we need to figure out which one to use + operator bool() { return (device != nullptr); } + // Main boot keyboard functions. uint16_t getKey() { return keyCode; } uint8_t getModifiers() { return modifiers; } uint8_t getOemKey() { return keyOEM; } @@ -667,6 +669,13 @@ public: void numLock(bool f); void capsLock(bool f); void scrollLock(bool f); + + // Added for extras information. + void attachExtrasPress(void (*f)(uint32_t top, uint16_t code)) { extrasKeyPressedFunction = f; } + void attachExtrasRelease(void (*f)(uint32_t top, uint16_t code)) { extrasKeyReleasedFunction = f; } + enum {MAX_KEYS_DOWN=4}; + + protected: virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); virtual void control(const Transfer_t *transfer); @@ -674,6 +683,14 @@ protected: static void callback(const Transfer_t *transfer); void new_data(const Transfer_t *transfer); void init(); + +protected: // HID functions for extra keyboard data. + virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); + virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); + virtual void hid_input_data(uint32_t usage, int32_t value); + virtual void hid_input_end(); + virtual void disconnect_collection(Device_t *dev); + private: void update(); uint16_t convert_to_unicode(uint32_t mod, uint32_t key); @@ -692,38 +709,19 @@ private: Pipe_t mypipes[2] __attribute__ ((aligned(32))); Transfer_t mytransfers[4] __attribute__ ((aligned(32))); strbuf_t mystring_bufs[1]; -}; -//-------------------------------------------------------------------------- - -class KeyboardHIDExtrasController : public USBHIDInput { -public: - KeyboardHIDExtrasController(USBHost &host) { USBHIDParser::driver_ready_for_hid_collection(this); } - void clear() { event_ = false;} - bool available() { return event_; } - void attachPress(void (*f)(uint32_t top, uint16_t code)) { keyPressedFunction = f; } - void attachRelease(void (*f)(uint32_t top, uint16_t code)) { keyReleasedFunction = f; } - enum {MAX_KEYS_DOWN=4}; -// uint32_t buttons() { return buttons_; } -protected: - virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); - virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); - virtual void hid_input_data(uint32_t usage, int32_t value); - virtual void hid_input_end(); - virtual void disconnect_collection(Device_t *dev); -private: - void (*keyPressedFunction)(uint32_t top, uint16_t code); - void (*keyReleasedFunction)(uint32_t top, uint16_t code); + // Added to process secondary HID data. + void (*extrasKeyPressedFunction)(uint32_t top, uint16_t code); + void (*extrasKeyReleasedFunction)(uint32_t top, uint16_t code); uint32_t topusage_ = 0; // What top report am I processing? uint8_t collections_claimed_ = 0; - volatile bool event_ = false; volatile bool hid_input_begin_ = false; volatile bool hid_input_data_ = false; // did we receive any valid data with report? uint8_t count_keys_down_ = 0; uint16_t keys_down[MAX_KEYS_DOWN]; + }; -//-------------------------------------------------------------------------- class MouseController : public USBHIDInput { public: @@ -754,27 +752,76 @@ private: //-------------------------------------------------------------------------- -class JoystickController : public USBHIDInput { +class JoystickController : public USBDriver, public USBHIDInput { public: - JoystickController(USBHost &host) { USBHIDParser::driver_ready_for_hid_collection(this); } + JoystickController(USBHost &host) { init(); } + + uint16_t idVendor(); + uint16_t idProduct(); + + const uint8_t *manufacturer(); + const uint8_t *product(); + const uint8_t *serialNumber(); + operator bool() { return ((device != nullptr) || (mydevice != nullptr)); } // override as in both USBDriver and in USBHIDInput + bool available() { return joystickEvent; } void joystickDataClear(); uint32_t getButtons() { return buttons; } int getAxis(uint32_t index) { return (index < (sizeof(axis)/sizeof(axis[0]))) ? axis[index] : 0; } + uint32_t axisMask() {return axis_mask_;} + enum { AXIS_COUNT = 10 }; protected: + // From USBDriver + virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); + virtual void control(const Transfer_t *transfer); + virtual void disconnect(); + + // From USBHIDInput virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); virtual void hid_input_data(uint32_t usage, int32_t value); virtual void hid_input_end(); virtual void disconnect_collection(Device_t *dev); private: - uint8_t collections_claimed = 0; + + // Class specific + void init(); + bool anychange = false; volatile bool joystickEvent = false; uint32_t buttons = 0; - int16_t axis[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int axis[AXIS_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t axis_mask_ = 0; // which axis have valid data + + // Used by HID code + uint8_t collections_claimed = 0; + + // Used by USBDriver code + static void rx_callback(const Transfer_t *transfer); + static void tx_callback(const Transfer_t *transfer); + void rx_data(const Transfer_t *transfer); + void tx_data(const Transfer_t *transfer); + + Pipe_t mypipes[3] __attribute__ ((aligned(32))); + Transfer_t mytransfers[7] __attribute__ ((aligned(32))); + strbuf_t mystring_bufs[1]; + + uint16_t rx_size_ = 0; + uint16_t tx_size_ = 0; + Pipe_t *rxpipe_; + Pipe_t *txpipe_; + uint8_t rxbuf_[64]; // receive circular buffer + + // Mapping table to say which devices we handle + typedef struct { + uint16_t idVendor; + uint16_t idProduct; + } product_vendor_mapping_t; + static product_vendor_mapping_t pid_vid_mapping[]; + }; + //-------------------------------------------------------------------------- class MIDIDevice : public USBDriver { diff --git a/examples/Mouse/Mouse.ino b/examples/Mouse/Mouse.ino index f829d20..8950e2f 100644 --- a/examples/Mouse/Mouse.ino +++ b/examples/Mouse/Mouse.ino @@ -7,11 +7,9 @@ USBHost myusb; USBHub hub1(myusb); USBHub hub2(myusb); -USBHub hub3(myusb); -USBHub hub4(myusb); KeyboardController keyboard1(myusb); KeyboardController keyboard2(myusb); -KeyboardHIDExtrasController hidextras(myusb); +//KeyboardHIDExtrasController hidextras(myusb); USBHIDParser hid1(myusb); USBHIDParser hid2(myusb); USBHIDParser hid3(myusb); @@ -22,9 +20,9 @@ JoystickController joystick1(myusb); RawHIDController rawhid1(myusb); RawHIDController rawhid2(myusb, 0xffc90004); -USBDriver *drivers[] = {&hub1, &hub2, &hub3, &hub4, &keyboard1, &keyboard2, &hid1, &hid2, &hid3, &hid4, &hid5}; +USBDriver *drivers[] = {&hub1, &hub2,&keyboard1, &keyboard2, &joystick1, &hid1, &hid2, &hid3, &hid4, &hid5}; #define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0])) -const char * driver_names[CNT_DEVICES] = {"Hub1","Hub2", "Hub3", "Hub4" "KB1", "KB2", "HID1", "HID2", "HID3", "HID4", "HID5" }; +const char * driver_names[CNT_DEVICES] = {"Hub1","Hub2", "KB1", "KB2", "JOY1D", "HID1", "HID2", "HID3", "HID4", "HID5"}; bool driver_active[CNT_DEVICES] = {false, false, false, false}; // Lets also look at HID Input devices @@ -42,8 +40,10 @@ void setup() myusb.begin(); keyboard1.attachPress(OnPress); keyboard2.attachPress(OnPress); - hidextras.attachPress(OnHIDExtrasPress); - hidextras.attachRelease(OnHIDExtrasRelease); + keyboard1.attachExtrasPress(OnHIDExtrasPress); + keyboard1.attachExtrasRelease(OnHIDExtrasRelease); + keyboard2.attachExtrasPress(OnHIDExtrasPress); + keyboard2.attachExtrasRelease(OnHIDExtrasRelease); rawhid1.attachReceive(OnReceiveHidData); rawhid2.attachReceive(OnReceiveHidData); @@ -109,22 +109,14 @@ void loop() mouse1.mouseDataClear(); } if (joystick1.available()) { + uint32_t axis_mask = joystick1.axisMask(); Serial.print("Joystick: buttons = "); Serial.print(joystick1.getButtons(), HEX); - Serial.print(", X = "); - Serial.print(joystick1.getAxis(0)); - Serial.print(", Y = "); - Serial.print(joystick1.getAxis(1)); - Serial.print(", Z = "); - Serial.print(joystick1.getAxis(2)); - Serial.print(", Rz = "); - Serial.print(joystick1.getAxis(5)); - Serial.print(", Rx = "); - Serial.print(joystick1.getAxis(3)); - Serial.print(", Ry = "); - Serial.print(joystick1.getAxis(4)); - Serial.print(", Hat = "); - Serial.print(joystick1.getAxis(9)); + for (uint8_t i = 0; axis_mask != 0; i++, axis_mask >>= 1) { + if (axis_mask & 1) { + Serial.printf(" %d:%d", i, joystick1.getAxis(i)); + } + } Serial.println(); joystick1.joystickDataClear(); } diff --git a/joystick.cpp b/joystick.cpp index 6617867..ecb0ed5 100644 --- a/joystick.cpp +++ b/joystick.cpp @@ -24,7 +24,62 @@ #include #include "USBHost_t36.h" // Read this header first for key info +#define print USBHost::print_ +#define println USBHost::println_ +void JoystickController::init() +{ + contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); + contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); + contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); + driver_ready_for_device(this); + USBHIDParser::driver_ready_for_hid_collection(this); +} + +//***************************************************************************** +// Some simple query functions depend on which interface we are using... +//***************************************************************************** + +uint16_t JoystickController::idVendor() +{ + if (device != nullptr) return device->idVendor; + if (mydevice != nullptr) return mydevice->idVendor; + return 0; +} + +uint16_t JoystickController::idProduct() +{ + if (device != nullptr) return device->idProduct; + if (mydevice != nullptr) return mydevice->idProduct; + return 0; +} + +const uint8_t *JoystickController::manufacturer() +{ + if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; + if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; + return nullptr; +} + +const uint8_t *JoystickController::product() +{ + if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; + if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; + return nullptr; +} + +const uint8_t *JoystickController::serialNumber() +{ + if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; + if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; + return nullptr; +} + + + +//***************************************************************************** +// Support for Joysticks that USe HID data. +//***************************************************************************** hidclaim_t JoystickController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) { @@ -42,6 +97,7 @@ void JoystickController::disconnect_collection(Device_t *dev) { if (--collections_claimed == 0) { mydevice = NULL; + axis_mask_ = 0; } } @@ -70,8 +126,9 @@ void JoystickController::hid_input_data(uint32_t usage, int32_t value) } } else if (usage_page == 1 && usage >= 0x30 && usage <= 0x39) { // TODO: need scaling of value to consistent API, 16 bit signed? - // TODO: many joysticks repeat slider usage. Detect & map to axes? + // TODO: many joysticks repeat slider usage. Detect & map to axis? uint32_t i = usage - 0x30; + axis_mask_ |= (1 << i); // Keep record of which axis we have data on. if (axis[i] != value) { axis[i] = value; anychange = true; @@ -92,4 +149,167 @@ void JoystickController::joystickDataClear() { anychange = false; } +//***************************************************************************** +// Support for Joysticks that are class specific and do not use HID +// Example: XBox One controller. +//***************************************************************************** +// Note: currently just XBOX one. +JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = { + {0x045e, 0x02ea} }; + +static uint8_t start_input[] = {0x05, 0x20, 0x00, 0x01, 0x00}; + +bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) +{ + println("JoystickController claim this=", (uint32_t)this, HEX); + + // only claim at device level + if (type != 0) return false; + print_hexbytes(descriptors, len); + + uint8_t i = 0; + for (; i < (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0])); i++) { + if ((dev->idVendor == pid_vid_mapping[i].idVendor) && (dev->idProduct == pid_vid_mapping[i].idProduct)) { + break; + } + } + if (i == (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0]))) return false; // Not in our list + + // 0 1 2 3 4 5 6 7 8 *9 10 1 2 3 4 5 *6 7 8 9 20 1 2 3 4 5 6 7 8 9 30 1... + // 09 04 00 00 02 FF 47 D0 00 07 05 02 03 40 00 04 07 05 82 03 40 00 04 09 04 01 00 00 FF 47 D0 00 + // Lets do some verifications to make sure. + + if (len < 9+7+7) return false; + + uint32_t count_end_points = descriptors[4]; + if (count_end_points < 2) return false; + if (descriptors[5] != 0xff) return false; // bInterfaceClass, 3 = HID + uint32_t rxep = 0; + uint32_t txep = 0; + rx_size_ = 0; + tx_size_ = 0; + uint32_t descriptor_index = 9; + while (count_end_points-- && ((rxep == 0) || txep == 0)) { + if (descriptors[descriptor_index] != 7) return false; // length 7 + if (descriptors[descriptor_index+1] != 5) return false; // ep desc + if ((descriptors[descriptor_index+3] == 3) // Type 3... + && (descriptors[descriptor_index+4] <= 64) + && (descriptors[descriptor_index+5] == 0)) { + // have a bulk EP size + if (descriptors[descriptor_index+2] & 0x80 ) { + rxep = descriptors[descriptor_index+2]; + rx_size_ = descriptors[descriptor_index+4]; + } else { + txep = descriptors[descriptor_index+2]; + tx_size_ = descriptors[descriptor_index+4]; + } + } + descriptor_index += 7; // setup to look at next one... + } + if ((rxep == 0) || (txep == 0)) return false; // did not find two end points. + print("JoystickController, rxep=", rxep & 15); + print("(", rx_size_); + print("), txep=", txep); + print("(", tx_size_); + println(")"); + rxpipe_ = new_Pipe(dev, 2, rxep & 15, 1, rx_size_); + if (!rxpipe_) return false; + txpipe_ = new_Pipe(dev, 2, txep, 0, tx_size_); + if (!txpipe_) { + //free_Pipe(rxpipe_); + return false; + } + rxpipe_->callback_function = rx_callback; + queue_Data_Transfer(rxpipe_, rxbuf_, rx_size_, this); + + txpipe_->callback_function = tx_callback; + + queue_Data_Transfer(txpipe_, start_input, sizeof(start_input), this); + memset(axis, 0, sizeof(axis)); // clear out any data. + return true; +} + +void JoystickController::control(const Transfer_t *transfer) +{ +} + + +/************************************************************/ +// Interrupt-based Data Movement +/************************************************************/ + +void JoystickController::rx_callback(const Transfer_t *transfer) +{ + if (!transfer->driver) return; + ((JoystickController *)(transfer->driver))->rx_data(transfer); +} + +void JoystickController::tx_callback(const Transfer_t *transfer) +{ + if (!transfer->driver) return; + ((JoystickController *)(transfer->driver))->tx_data(transfer); +} + + + +/************************************************************/ +// Interrupt-based Data Movement +// XBox one input data when type == 0x20 +// Information came from several places on the web including: +// https://github.com/quantus/xbox-one-controller-protocol +/************************************************************/ +typedef struct { + uint8_t type; + uint8_t const_0; + uint16_t id; + // From online references button order: + // sync, dummy, start, back, a, b, x, y + // dpad up, down left, right + // lb, rb, left stick, right stick + // Axis: + // lt, rt, lx, xy, rx, ry + // + uint16_t buttons; + int16_t axis[6]; +} xbox1data20_t; + +static const uint8_t xbox_axis_order_mapping[] = {4, 5, 0, 1, 2, 3}; + +void JoystickController::rx_data(const Transfer_t *transfer) +{ +// print("JoystickController::rx_data: "); +// print_hexbytes((uint8_t*)transfer->buffer, transfer->length); + axis_mask_ = 0x3f; + xbox1data20_t *xb1d = (xbox1data20_t *)transfer->buffer; + if ((xb1d->type == 0x20) && (transfer->length >= sizeof (xbox1data20_t))) { + // We have a data transfer. Lets see what is new... + if (xb1d->buttons != buttons) { + buttons = xb1d->buttons; + anychange = true; + } + for (uint8_t i = 0; i < sizeof (xbox_axis_order_mapping); i++) { + // The first two values were unsigned. + int axis_value = (i < 2)? (int)(uint16_t)xb1d->axis[i] : xb1d->axis[i]; + if (axis_value != axis[xbox_axis_order_mapping[i]]) { + axis[xbox_axis_order_mapping[i]] = axis_value; + anychange = true; + } + } + joystickEvent = true; + } + + + queue_Data_Transfer(rxpipe_, rxbuf_, rx_size_, this); +} + +void JoystickController::tx_data(const Transfer_t *transfer) +{ +} + +void JoystickController::disconnect() +{ + axis_mask_ = 0; + // TODO: free resources +} + diff --git a/keyboard.cpp b/keyboard.cpp index 93bdabc..bfc127c 100644 --- a/keyboard.cpp +++ b/keyboard.cpp @@ -98,6 +98,7 @@ void KeyboardController::init() contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); driver_ready_for_device(this); + USBHIDParser::driver_ready_for_hid_collection(this); } bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) @@ -326,13 +327,102 @@ void KeyboardController::updateLEDS() { queue_Control_Transfer(device, &setup, &leds_.byte, this); } +//============================================================================= +// Keyboard Extras - Combined from other object +//============================================================================= +#define TOPUSAGE_SYS_CONTROL 0x10080 +#define TOPUSAGE_CONSUMER_CONTROL 0x0c0001 +hidclaim_t KeyboardController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) +{ + // Lets try to claim a few specific Keyboard related collection/reports + //Serial.printf("KBH Claim %x\n", topusage); + if ((topusage != TOPUSAGE_SYS_CONTROL) + && (topusage != TOPUSAGE_CONSUMER_CONTROL) + ) return CLAIM_NO; + // only claim from one physical device + //Serial.println("KeyboardController claim collection"); + // Lets only claim if this is the same device as claimed Keyboard... + if (dev != device) return CLAIM_NO; + if (mydevice != NULL && dev != mydevice) return CLAIM_NO; + mydevice = dev; + collections_claimed_++; + return CLAIM_REPORT; +} +void KeyboardController::disconnect_collection(Device_t *dev) +{ + if (--collections_claimed_ == 0) { + mydevice = NULL; + } +} +void KeyboardController::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) +{ + //Serial.printf("KPC:hid_input_begin TUSE: %x TYPE: %x Range:%x %x\n", topusage, type, lgmin, lgmax); + topusage_ = topusage; // remember which report we are processing. + hid_input_begin_ = true; + hid_input_data_ = false; +} +void KeyboardController::hid_input_data(uint32_t usage, int32_t value) +{ + // Hack ignore 0xff00 high words as these are user values... + if ((usage & 0xffff0000) == 0xff000000) return; + //Serial.printf("KeyboardController: topusage= %x usage=%X, value=%d\n", topusage_, usage, value); + // See if the value is in our keys_down list + usage &= 0xffff; // only keep the actual key + if (usage == 0) return; // lets not process 0, if only 0 happens, we will handle it on the end to remove existing pressed items. + // Remember if we have received any logical key up events. Some keyboard appear to send them + // others do no... + hid_input_data_ = true; + uint8_t key_index; + for (key_index = 0; key_index < count_keys_down_; key_index++) { + if (keys_down[key_index] == usage) { + if (value) return; // still down + if (extrasKeyReleasedFunction) { + extrasKeyReleasedFunction(topusage_, usage); + } + // Remove from list + count_keys_down_--; + for (;key_index < count_keys_down_; key_index++) { + keys_down[key_index] = keys_down[key_index+1]; + } + return; + } + } + // Was not in list + if (!value) return; // still 0 + if (extrasKeyPressedFunction) { + extrasKeyPressedFunction(topusage_, usage); + } + if (count_keys_down_ < MAX_KEYS_DOWN) { + keys_down[count_keys_down_++] = usage; + } +} + +void KeyboardController::hid_input_end() +{ + //Serial.println("KPC:hid_input_end"); + if (hid_input_begin_) { + + // See if we received any data from parser if not, assume all keys released... + if (!hid_input_data_ ) { + if (extrasKeyReleasedFunction) { + while (count_keys_down_) { + count_keys_down_--; + extrasKeyReleasedFunction(topusage_, keys_down[count_keys_down_]); + } + } + count_keys_down_ = 0; + } + + hid_input_begin_ = false; + } +} diff --git a/keyboardHIDExtras.cpp b/keyboardHIDExtras.cpp deleted file mode 100644 index 1a14612..0000000 --- a/keyboardHIDExtras.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* USB keyboard power Host for Teensy 3.6 - * Copyright 2017 Paul Stoffregen (paul@pjrc.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include "USBHost_t36.h" // Read this header first for key info - -#define TOPUSAGE_SYS_CONTROL 0x10080 -#define TOPUSAGE_CONSUMER_CONTROL 0x0c0001 - -hidclaim_t KeyboardHIDExtrasController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) -{ - // Lets try to claim a few specific Keyboard related collection/reports - //Serial.printf("KBH Claim %x\n", topusage); - if ((topusage != TOPUSAGE_SYS_CONTROL) - && (topusage != TOPUSAGE_CONSUMER_CONTROL) - ) return CLAIM_NO; - // only claim from one physical device - //Serial.println("KeyboardHIDExtrasController claim collection"); - if (mydevice != NULL && dev != mydevice) return CLAIM_NO; - mydevice = dev; - collections_claimed_++; - return CLAIM_REPORT; -} - -void KeyboardHIDExtrasController::disconnect_collection(Device_t *dev) -{ - if (--collections_claimed_ == 0) { - mydevice = NULL; - } -} - -void KeyboardHIDExtrasController::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) -{ - //Serial.printf("KPC:hid_input_begin TUSE: %x TYPE: %x Range:%x %x\n", topusage, type, lgmin, lgmax); - topusage_ = topusage; // remember which report we are processing. - hid_input_begin_ = true; - hid_input_data_ = false; -} - -void KeyboardHIDExtrasController::hid_input_data(uint32_t usage, int32_t value) -{ - // Hack ignore 0xff00 high words as these are user values... - if ((usage & 0xffff0000) == 0xff000000) return; - //Serial.printf("KeyboardHIDExtrasController: topusage= %x usage=%X, value=%d\n", topusage_, usage, value); - - // See if the value is in our keys_down list - usage &= 0xffff; // only keep the actual key - if (usage == 0) return; // lets not process 0, if only 0 happens, we will handle it on the end to remove existing pressed items. - - // Remember if we have received any logical key up events. Some keyboard appear to send them - // others do no... - hid_input_data_ = true; - - uint8_t key_index; - for (key_index = 0; key_index < count_keys_down_; key_index++) { - if (keys_down[key_index] == usage) { - if (value) return; // still down - - if (keyReleasedFunction) { - keyReleasedFunction(topusage_, usage); - } - - // Remove from list - count_keys_down_--; - for (;key_index < count_keys_down_; key_index++) { - keys_down[key_index] = keys_down[key_index+1]; - } - return; - } - } - // Was not in list - if (!value) return; // still 0 - if (keyPressedFunction) { - keyPressedFunction(topusage_, usage); - } - if (count_keys_down_ < MAX_KEYS_DOWN) { - keys_down[count_keys_down_++] = usage; - } -} - -void KeyboardHIDExtrasController::hid_input_end() -{ - //Serial.println("KPC:hid_input_end"); - if (hid_input_begin_) { - - // See if we received any data from parser if not, assume all keys released... - if (!hid_input_data_ ) { - if (keyPressedFunction) { - while (count_keys_down_) { - count_keys_down_--; - keyReleasedFunction(topusage_, keys_down[count_keys_down_]); - } - } - count_keys_down_ = 0; - } - - event_ = true; - hid_input_begin_ = false; - } -} - - - diff --git a/keywords.txt b/keywords.txt index b7d2538..5df2d68 100644 --- a/keywords.txt +++ b/keywords.txt @@ -8,7 +8,6 @@ MIDIDevice KEYWORD1 USBSerial KEYWORD1 AntPlus KEYWORD1 JoystickController KEYWORD1 -KeyboardHIDExtrasController KEYWORD1 RawHIDController KEYWORD1 # Common Functions @@ -25,6 +24,8 @@ getModifiers KEYWORD2 getOemKey KEYWORD2 attachPress KEYWORD2 attachRelease KEYWORD2 +attachExtrasPress KEYWORD2 +attachExtrasRelease KEYWORD2 LEDS KEYWORD2 updateLEDS KEYWORD2 numLock KEYWORD2 @@ -79,6 +80,7 @@ getWheelH KEYWORD2 # JoystickController joystickDataClear KEYWORD2 getAxis KEYWORD2 +axisMask KEYWORD2 # USBSerial USBHOST_SERIAL_7E1 LITERAL1 From a1986c3d30e87feda1f2b30db37e6da4581c95bb Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Mon, 11 Dec 2017 06:26:36 -0800 Subject: [PATCH 11/11] Add 2nd VID:PID for XBox One As per forum post by linarism, added 2nd XBox controller PID --- USBHost_t36.h | 2 +- joystick.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index bb0ac03..131cff5 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -56,7 +56,7 @@ // your best effort to read chapter 4 before asking USB questions! -//#define USBHOST_PRINT_DEBUG +#define USBHOST_PRINT_DEBUG /************************************************/ /* Data Types */ diff --git a/joystick.cpp b/joystick.cpp index ecb0ed5..9513127 100644 --- a/joystick.cpp +++ b/joystick.cpp @@ -155,7 +155,7 @@ void JoystickController::joystickDataClear() { //***************************************************************************** // Note: currently just XBOX one. JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = { - {0x045e, 0x02ea} }; + { 0x045e, 0x02ea },{ 0x045e, 0x02dd } }; static uint8_t start_input[] = {0x05, 0x20, 0x00, 0x01, 0x00};