diff options
| author | Ha Thach <[email protected]> | 2022-03-20 00:17:39 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-03-20 00:17:39 +0700 |
| commit | ae531a79f654d566790a4daae350730cdc0a01e9 (patch) | |
| tree | a67cd9c595a0038f7eecf6f4fcaadf4064a9ac9b /examples | |
| parent | 1915d69cb8da8372b35a5067420c57ddb65d2cb1 (diff) | |
| parent | a270d8d6236124244f5a1447d57b35f4cfa4909c (diff) | |
Merge pull request #1403 from hathach/host-edpt-xfer
Host edpt xfer
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/host/bare_api/src/main.c | 360 | ||||
| -rw-r--r-- | examples/host/bare_api/src/tusb_config.h | 8 |
2 files changed, 290 insertions, 78 deletions
diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c index 9e6209991..eb3f7d8d7 100644 --- a/examples/host/bare_api/src/main.c +++ b/examples/host/bare_api/src/main.c @@ -35,11 +35,28 @@ #include "bsp/board.h" #include "tusb.h" +// English +#define LANGUAGE_ID 0x0409 +#define BUF_COUNT 4 + + +tusb_desc_device_t desc_device; + +uint8_t buf_pool[BUF_COUNT][64]; +uint8_t buf_owner[BUF_COUNT] = { 0 }; // device address that owns buffer + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTYPES //--------------------------------------------------------------------+ void led_blinking_task(void); +static void print_utf16(uint16_t *temp_buf, size_t buf_len); +void print_device_descriptor(tuh_xfer_t* xfer); +void parse_config_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); + +uint8_t* get_hid_buf(uint8_t daddr); +void free_hid_buf(uint8_t daddr); + /*------------- MAIN -------------*/ int main(void) { @@ -58,73 +75,39 @@ int main(void) return 0; } -//--------------------------------------------------------------------+ -// TinyUSB Callbacks -//--------------------------------------------------------------------+ +/*------------- TinyUSB Callbacks -------------*/ -// English -#define LANGUAGE_ID 0x0409 - -//uint8_t usb_buf[256] TU_ATTR_ALIGNED(4); -TU_ATTR_ALIGNED(4) -tusb_desc_device_t desc_device; - -static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) { - // TODO: Check for runover. - (void)utf8_len; - // Get the UTF-16 length out of the data itself. +// Invoked when device is mounted (configured) +void tuh_mount_cb (uint8_t daddr) +{ + printf("Device attached, address = %d\r\n", daddr); - for (size_t i = 0; i < utf16_len; i++) { - uint16_t chr = utf16[i]; - if (chr < 0x80) { - *utf8++ = chr & 0xff; - } else if (chr < 0x800) { - *utf8++ = (uint8_t)(0xC0 | (chr >> 6 & 0x1F)); - *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F)); - } else { - // TODO: Verify surrogate. - *utf8++ = (uint8_t)(0xE0 | (chr >> 12 & 0x0F)); - *utf8++ = (uint8_t)(0x80 | (chr >> 6 & 0x3F)); - *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F)); - } - // TODO: Handle UTF-16 code points that take two entries. - } + // Get Device Descriptor sync API + // TODO: invoking control trannsfer now has issue with mounting hub with multiple devices attached, fix later + tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0); } -// Count how many bytes a utf-16-le encoded string will take in utf-8. -static int _count_utf8_bytes(const uint16_t *buf, size_t len) { - size_t total_bytes = 0; - for (size_t i = 0; i < len; i++) { - uint16_t chr = buf[i]; - if (chr < 0x80) { - total_bytes += 1; - } else if (chr < 0x800) { - total_bytes += 2; - } else { - total_bytes += 3; - } - // TODO: Handle UTF-16 code points that take two entries. - } - return total_bytes; +/// Invoked when device is unmounted (bus reset/unplugged) +void tuh_umount_cb(uint8_t daddr) +{ + printf("Device removed, address = %d\r\n", daddr); + free_hid_buf(daddr); } -static void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len) { - size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t); - size_t utf8_len = _count_utf8_bytes(temp_buf + 1, utf16_len); - _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len); - ((uint8_t*) temp_buf)[utf8_len] = '\0'; -} +//--------------------------------------------------------------------+ +// Device Descriptor +//--------------------------------------------------------------------+ -bool print_device_descriptor(uint8_t daddr, tuh_control_xfer_t const * xfer, xfer_result_t result) +void print_device_descriptor(tuh_xfer_t* xfer) { - (void) xfer; - - if ( XFER_RESULT_SUCCESS != result ) + if ( XFER_RESULT_SUCCESS != xfer->result ) { printf("Failed to get device descriptor\r\n"); - return false; + return; } + uint8_t const daddr = xfer->daddr; + printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct); printf("Device Descriptor:\r\n"); printf(" bLength %u\r\n" , desc_device.bLength); @@ -138,51 +121,228 @@ bool print_device_descriptor(uint8_t daddr, tuh_control_xfer_t const * xfer, xfe printf(" idProduct 0x%04x\r\n" , desc_device.idProduct); printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice); - uint32_t timeout_ms = 10; + // Get String descriptor using Sync API uint16_t temp_buf[128]; printf(" iManufacturer %u " , desc_device.iManufacturer); - if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, TU_ARRAY_SIZE(temp_buf), timeout_ms) ) + if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf)) ) { - utf16_to_utf8(temp_buf, TU_ARRAY_SIZE(temp_buf)); - printf((const char*) temp_buf); + print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf)); } printf("\r\n"); printf(" iProduct %u " , desc_device.iProduct); - if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, temp_buf, TU_ARRAY_SIZE(temp_buf), timeout_ms)) + if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf))) { - utf16_to_utf8(temp_buf, TU_ARRAY_SIZE(temp_buf)); - printf((const char*) temp_buf); + print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf)); } printf("\r\n"); printf(" iSerialNumber %u " , desc_device.iSerialNumber); - if (XFER_RESULT_SUCCESS == tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, temp_buf, TU_ARRAY_SIZE(temp_buf), timeout_ms)) + if (XFER_RESULT_SUCCESS == tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf))) { - utf16_to_utf8(temp_buf, TU_ARRAY_SIZE(temp_buf)); - printf((const char*) temp_buf); + print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf)); } printf("\r\n"); printf(" bNumConfigurations %u\r\n" , desc_device.bNumConfigurations); - return true; + // Get configuration descriptor with sync API + if (XFER_RESULT_SUCCESS == tuh_descriptor_get_configuration_sync(daddr, 0, temp_buf, sizeof(temp_buf))) + { + parse_config_descriptor(daddr, (tusb_desc_configuration_t*) temp_buf); + } } -// Invoked when device is mounted (configured) -void tuh_mount_cb (uint8_t daddr) +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +// count total length of an interface +uint16_t count_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len); + +void open_hid_interface(uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); + +// simple configuration parser to open and listen to HID Endpoint IN +void parse_config_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg) { - printf("Device attached, address = %d\r\n", daddr); + uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength); + uint8_t const* p_desc = tu_desc_next(desc_cfg); - // Get Device Descriptor using asynchronous API - tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0); + // parse each interfaces + while( p_desc < desc_end ) + { + uint8_t assoc_itf_count = 1; + + // Class will always starts with Interface Association (if any) and then Interface descriptor + if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) + { + tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc; + assoc_itf_count = desc_iad->bInterfaceCount; + + p_desc = tu_desc_next(p_desc); // next to Interface + } + + // must be interface from now + if( TUSB_DESC_INTERFACE != tu_desc_type(p_desc) ) return; + tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc; + + uint16_t const drv_len = count_interface_total_len(desc_itf, assoc_itf_count, desc_end-p_desc); + + // probably corrupted descriptor + if(drv_len < sizeof(tusb_desc_interface_t)) return; + + // only open and listen to HID endpoint IN + if (desc_itf->bInterfaceClass == TUSB_CLASS_HID) + { + open_hid_interface(dev_addr, desc_itf, drv_len); + } + + // next Interface or IAD descriptor + p_desc += drv_len; + } } -/// Invoked when device is unmounted (bus reset/unplugged) -void tuh_umount_cb(uint8_t daddr) +uint16_t count_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) { - printf("Device removed, address = %d\r\n", daddr); + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint16_t len = 0; + + while (itf_count--) + { + // Next on interface desc + len += tu_desc_len(desc_itf); + p_desc = tu_desc_next(p_desc); + + while (len < max_len) + { + // return on IAD regardless of itf count + if ( tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION ) return len; + + if ( (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) && + ((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0 ) + { + break; + } + + len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + } + + return len; +} + +//--------------------------------------------------------------------+ +// HID Interface +//--------------------------------------------------------------------+ + +void hid_report_received(tuh_xfer_t* xfer); + +void open_hid_interface(uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) +{ + // len = interface + hid + n*endpoints + uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t); + + // corrupted descriptor + if (max_len < drv_len) return; + + uint8_t const *p_desc = (uint8_t const *) desc_itf; + + // HID descriptor + p_desc = tu_desc_next(p_desc); + tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc; + if(HID_DESC_TYPE_HID != desc_hid->bDescriptorType) return; + + // Endpoint descriptor + p_desc = tu_desc_next(p_desc); + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; + + for(int i = 0; i < desc_itf->bNumEndpoints; i++) + { + if (TUSB_DESC_ENDPOINT != desc_ep->bDescriptorType) return; + + if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) + { + // skip if failed to open endpoint + if ( ! tuh_edpt_open(daddr, desc_ep) ) return; + + uint8_t* buf = get_hid_buf(daddr); + if (!buf) return; // out of memory + + tuh_xfer_t xfer = + { + .daddr = daddr, + .ep_addr = desc_ep->bEndpointAddress, + .buflen = 64, + .buffer = buf, + .complete_cb = hid_report_received, + .user_data = (uintptr_t) buf, // since buffer is not available in callback, use user data to store the buffer + }; + + // submit transfer for this EP + tuh_edpt_xfer(&xfer); + + printf("Listen to [dev %u: ep %02x]\r\n", daddr, desc_ep->bEndpointAddress); + } + + p_desc = tu_desc_next(p_desc); + desc_ep = (tusb_desc_endpoint_t const *) p_desc; + } +} + +void hid_report_received(tuh_xfer_t* xfer) +{ + // Note: not all field in xfer is available for use (i.e filled by tinyusb stack) in callback to save sram + // For instance, xfer->buffer is NULL. We have used user_data to store buffer when submitted callback + uint8_t* buf = (uint8_t*) xfer->user_data; + + if (xfer->result == XFER_RESULT_SUCCESS) + { + printf("[dev %u: ep %02x] HID Report:", xfer->daddr, xfer->ep_addr); + for(uint32_t i=0; i<xfer->actual_len; i++) + { + if (i%16 == 0) printf("\r\n "); + printf("%02X ", buf[i]); + } + printf("\r\n"); + } + + // continue to submit transfer, with updated buffer + // other field remain the same + xfer->buflen = 64; + xfer->buffer = buf; + + tuh_edpt_xfer(xfer); +} + +//--------------------------------------------------------------------+ +// Buffer helper +//--------------------------------------------------------------------+ + +// get an buffer from pool +uint8_t* get_hid_buf(uint8_t daddr) +{ + for(size_t i=0; i<BUF_COUNT; i++) + { + if (buf_owner[i] == 0) + { + buf_owner[i] = daddr; + return buf_pool[i]; + } + } + + // out of memory, increase BUF_COUNT + return NULL; +} + +// free all buffer owned by device +void free_hid_buf(uint8_t daddr) +{ + for(size_t i=0; i<BUF_COUNT; i++) + { + if (buf_owner[i] == daddr) buf_owner[i] = 0; + } } //--------------------------------------------------------------------+ @@ -202,3 +362,55 @@ void led_blinking_task(void) board_led_write(led_state); led_state = 1 - led_state; // toggle } + +//--------------------------------------------------------------------+ +// String Descriptor Helper +//--------------------------------------------------------------------+ + +static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) { + // TODO: Check for runover. + (void)utf8_len; + // Get the UTF-16 length out of the data itself. + + for (size_t i = 0; i < utf16_len; i++) { + uint16_t chr = utf16[i]; + if (chr < 0x80) { + *utf8++ = chr & 0xff; + } else if (chr < 0x800) { + *utf8++ = (uint8_t)(0xC0 | (chr >> 6 & 0x1F)); + *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F)); + } else { + // TODO: Verify surrogate. + *utf8++ = (uint8_t)(0xE0 | (chr >> 12 & 0x0F)); + *utf8++ = (uint8_t)(0x80 | (chr >> 6 & 0x3F)); + *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F)); + } + // TODO: Handle UTF-16 code points that take two entries. + } +} + +// Count how many bytes a utf-16-le encoded string will take in utf-8. +static int _count_utf8_bytes(const uint16_t *buf, size_t len) { + size_t total_bytes = 0; + for (size_t i = 0; i < len; i++) { + uint16_t chr = buf[i]; + if (chr < 0x80) { + total_bytes += 1; + } else if (chr < 0x800) { + total_bytes += 2; + } else { + total_bytes += 3; + } + // TODO: Handle UTF-16 code points that take two entries. + } + return total_bytes; +} + +static void print_utf16(uint16_t *temp_buf, size_t buf_len) { + size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t); + size_t utf8_len = _count_utf8_bytes(temp_buf + 1, utf16_len); + _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len); + ((uint8_t*) temp_buf)[utf8_len] = '\0'; + + printf((char*)temp_buf); +} diff --git a/examples/host/bare_api/src/tusb_config.h b/examples/host/bare_api/src/tusb_config.h index 234eca402..9b789290d 100644 --- a/examples/host/bare_api/src/tusb_config.h +++ b/examples/host/bare_api/src/tusb_config.h @@ -81,11 +81,11 @@ // 1 hub typically has 4 ports #define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) -#define CFG_TUH_ENDPOINT_MAX 8 +// Max endpoint per device +#define CFG_TUH_ENDPOINT_MAX 8 -//------------- HID -------------// - -#define CFG_TUH_HID_EP_BUFSIZE 64 +// Enable tuh_edpt_xfer() API +#define CFG_TUH_API_EDPT_XFER 1 #ifdef __cplusplus } |
