diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c index 9e620999..eb3f7d8d 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,16 +75,297 @@ int main(void) return 0; } +/*------------- TinyUSB Callbacks -------------*/ + +// Invoked when device is mounted (configured) +void tuh_mount_cb (uint8_t daddr) +{ + printf("Device attached, address = %d\r\n", daddr); + + // 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); +} + +/// 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); +} + //--------------------------------------------------------------------+ -// TinyUSB Callbacks +// Device Descriptor //--------------------------------------------------------------------+ -// English -#define LANGUAGE_ID 0x0409 +void print_device_descriptor(tuh_xfer_t* xfer) +{ + if ( XFER_RESULT_SUCCESS != xfer->result ) + { + printf("Failed to get device descriptor\r\n"); + return; + } -//uint8_t usb_buf[256] TU_ATTR_ALIGNED(4); -TU_ATTR_ALIGNED(4) -tusb_desc_device_t desc_device; + 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); + printf(" bDescriptorType %u\r\n" , desc_device.bDescriptorType); + printf(" bcdUSB %04x\r\n" , desc_device.bcdUSB); + printf(" bDeviceClass %u\r\n" , desc_device.bDeviceClass); + printf(" bDeviceSubClass %u\r\n" , desc_device.bDeviceSubClass); + printf(" bDeviceProtocol %u\r\n" , desc_device.bDeviceProtocol); + printf(" bMaxPacketSize0 %u\r\n" , desc_device.bMaxPacketSize0); + printf(" idVendor 0x%04x\r\n" , desc_device.idVendor); + printf(" idProduct 0x%04x\r\n" , desc_device.idProduct); + printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice); + + // 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, sizeof(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, sizeof(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, sizeof(temp_buf))) + { + print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf)); + } + printf("\r\n"); + + printf(" bNumConfigurations %u\r\n" , desc_device.bNumConfigurations); + + // 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); + } +} + +//--------------------------------------------------------------------+ +// 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) +{ + 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); + + // 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; + } +} + +uint16_t count_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) +{ + 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; iactual_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; iitf_num, - .wLength = 0 + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT }, - - .buffer = NULL, - .complete_cb = complete_cb, - .user_arg = 0 + .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, + .wValue = (rts ? 2 : 0) | (dtr ? 1 : 0), + .wIndex = p_cdc->itf_num, + .wLength = 0 }; - return tuh_control_xfer(dev_addr, &xfer); + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = 0 + }; + + return tuh_control_xfer(&xfer); } //--------------------------------------------------------------------+ @@ -158,6 +161,7 @@ void cdch_init(void) bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { + (void) rhport; (void) max_len; // Only support ACM subclass @@ -193,7 +197,7 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it // notification endpoint tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) ); + TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) ); p_cdc->ep_notif = desc_ep->bEndpointAddress; drv_len += tu_desc_len(p_desc); @@ -214,7 +218,7 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc; TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep)); + TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep)); if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) { diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 67162a0c..33dbd2ef 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -42,14 +42,14 @@ * \defgroup CDC_Serial_Host Host * @{ */ -bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_xfer_cb_t complete_cb); +bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb); -static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_control_xfer_cb_t complete_cb) +static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb) { return tuh_cdc_set_control_line_state(dev_addr, true, true, complete_cb); } -static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_control_xfer_cb_t complete_cb) +static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb) { return tuh_cdc_set_control_line_state(dev_addr, false, false, complete_cb); } diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index 940454bd..44a464be 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -43,7 +43,7 @@ /** \defgroup ClassDriver_HID_Common Common Definitions * @{ */ - /// USB HID Descriptor +/// USB HID Descriptor typedef struct TU_ATTR_PACKED { uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */ diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index a825858a..ce3d1598 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -103,19 +103,54 @@ uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance) return hid_itf->protocol_mode; } -static bool set_protocol_complete(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void set_protocol_complete(tuh_xfer_t* xfer) { - uint8_t const itf_num = (uint8_t) xfer->request.wIndex; - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); - hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const daddr = xfer->daddr; + uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num); + hidh_interface_t* hid_itf = get_instance(daddr, instance); - if (XFER_RESULT_SUCCESS == result) hid_itf->protocol_mode = (uint8_t) xfer->request.wValue; + if (XFER_RESULT_SUCCESS == xfer->result) + { + hid_itf->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue); + } if (tuh_hid_set_protocol_complete_cb) { - tuh_hid_set_protocol_complete_cb(dev_addr, instance, hid_itf->protocol_mode); + tuh_hid_set_protocol_complete_cb(daddr, instance, hid_itf->protocol_mode); } +} + +static bool _hidh_set_protocol(uint8_t dev_addr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_LOG2("HID Set Protocol = %d\r\n", protocol); + + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, + .wValue = protocol, + .wIndex = itf_num, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); return true; } @@ -124,49 +159,24 @@ bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol) hidh_interface_t* hid_itf = get_instance(dev_addr, instance); TU_VERIFY(hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE); - TU_LOG2("HID Set Protocol = %d\r\n", protocol); - - tuh_control_xfer_t const xfer = - { - .request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, - .wValue = protocol, - .wIndex = hid_itf->itf_num, - .wLength = 0 - }, - - .buffer = NULL, - .complete_cb = set_protocol_complete, - .user_arg = 0 - }; - - TU_ASSERT( tuh_control_xfer(dev_addr, &xfer) ); - return true; + return _hidh_set_protocol(dev_addr, hid_itf->itf_num, protocol, set_protocol_complete, 0); } -static bool set_report_complete(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void set_report_complete(tuh_xfer_t* xfer) { TU_LOG2("HID Set Report complete\r\n"); if (tuh_hid_set_report_complete_cb) { - uint8_t const itf_num = (uint8_t) xfer->request.wIndex; - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const instance = get_instance_id_by_itfnum(xfer->daddr, itf_num); - uint8_t const report_type = tu_u16_high(xfer->request.wValue); - uint8_t const report_id = tu_u16_low(xfer->request.wValue); + uint8_t const report_type = tu_u16_high(xfer->setup->wValue); + uint8_t const report_id = tu_u16_low(xfer->setup->wValue); - tuh_hid_set_report_complete_cb(dev_addr, instance, report_id, report_type, (result == XFER_RESULT_SUCCESS) ? xfer->request.wLength : 0); + tuh_hid_set_report_complete_cb(xfer->daddr, instance, report_id, report_type, + (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0); } - - return true; } bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) @@ -174,28 +184,64 @@ bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, u hidh_interface_t* hid_itf = get_instance(dev_addr, instance); TU_LOG2("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len); - tuh_control_xfer_t const xfer = + tusb_control_request_t const request = { - .request = + .bmRequestType_bit = { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = HID_REQ_CONTROL_SET_REPORT, - .wValue = tu_u16(report_type, report_id), - .wIndex = hid_itf->itf_num, - .wLength = len + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT }, - - .buffer = report, - .complete_cb = set_report_complete, - .user_arg = 0 + .bRequest = HID_REQ_CONTROL_SET_REPORT, + .wValue = tu_u16(report_type, report_id), + .wIndex = hid_itf->itf_num, + .wLength = len }; - TU_ASSERT( tuh_control_xfer(dev_addr, &xfer) ); + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = report, + .complete_cb = set_report_complete, + .user_data = 0 + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + return true; +} + +static bool _hidh_set_idle(uint8_t dev_addr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + // SET IDLE request, device can stall if not support this request + TU_LOG2("HID Set Idle \r\n"); + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = HID_REQ_CONTROL_SET_IDLE, + .wValue = idle_rate, + .wIndex = itf_num, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + return true; } @@ -210,7 +256,13 @@ bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance) // claim endpoint TU_VERIFY( usbh_edpt_claim(dev_addr, hid_itf->ep_in) ); - return usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size); + if ( !usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size) ) + { + usbh_edpt_claim(dev_addr, hid_itf->ep_in); + return false; + } + + return true; } //bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance) @@ -270,14 +322,9 @@ void hidh_close(uint8_t dev_addr) // Enumeration //--------------------------------------------------------------------+ -static bool config_set_protocol (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); -static bool config_get_report_desc (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); -static bool config_get_report_desc_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); - -static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len); - bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) { + (void) rhport; (void) max_len; TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass); @@ -309,7 +356,7 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de for(int i = 0; i < desc_itf->bNumEndpoints; i++) { TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType); - TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) ); + TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) ); if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { @@ -341,121 +388,93 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de return true; } +//--------------------------------------------------------------------+ +// Set Configure +//--------------------------------------------------------------------+ + +enum { + CONFG_SET_IDLE, + CONFIG_SET_PROTOCOL, + CONFIG_GET_REPORT_DESC, + CONFIG_COMPLETE +}; + +static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len); +static void process_set_config(tuh_xfer_t* xfer); + bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num) { - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); - hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + tusb_control_request_t request; + request.wIndex = tu_htole16((uint16_t) itf_num); - // Idle rate = 0 mean only report when there is changes - uint16_t const idle_rate = 0; + tuh_xfer_t xfer; + xfer.daddr = dev_addr; + xfer.result = XFER_RESULT_SUCCESS; + xfer.setup = &request; + xfer.user_data = CONFG_SET_IDLE; - // SET IDLE request, device can stall if not support this request - TU_LOG2("HID Set Idle \r\n"); - tuh_control_xfer_t const xfer = - { - .request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = HID_REQ_CONTROL_SET_IDLE, - .wValue = idle_rate, - .wIndex = itf_num, - .wLength = 0 - }, - - .buffer = NULL, - .complete_cb = (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_set_protocol : config_get_report_desc, - .user_arg = 0 - }; - - TU_ASSERT( tuh_control_xfer(dev_addr, &xfer) ); + // fake request to kick-off the set config process + process_set_config(&xfer); return true; } -// Force device to work in BOOT protocol -static bool config_set_protocol(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void process_set_config(tuh_xfer_t* xfer) { // Stall is a valid response for SET_IDLE, therefore we could ignore its result - (void) result; - - uint8_t const itf_num = (uint8_t) xfer->request.wIndex; - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); - hidh_interface_t* hid_itf = get_instance(dev_addr, instance); - - TU_LOG2("HID Set Protocol to Boot Mode\r\n"); - hid_itf->protocol_mode = HID_PROTOCOL_BOOT; - tuh_control_xfer_t const new_xfer = + if ( xfer->setup->bRequest != HID_REQ_CONTROL_SET_IDLE ) { - .request = + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); + } + + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const daddr = xfer->daddr; + + uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num); + hidh_interface_t* hid_itf = get_instance(daddr, instance); + + switch(state) + { + case CONFG_SET_IDLE: { - .bmRequestType_bit = + // Idle rate = 0 mean only report when there is changes + const uint16_t idle_rate = 0; + const uintptr_t next_state = (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC; + _hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state); + } + break; + + case CONFIG_SET_PROTOCOL: + _hidh_set_protocol(daddr, hid_itf->itf_num, HID_PROTOCOL_BOOT, process_set_config, CONFIG_GET_REPORT_DESC); + break; + + case CONFIG_GET_REPORT_DESC: + // Get Report Descriptor if possible + // using usbh enumeration buffer since report descriptor can be very long + if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE ) { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, - .wValue = HID_PROTOCOL_BOOT, - .wIndex = hid_itf->itf_num, - .wLength = 0 - }, + TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len); - .buffer = NULL, - .complete_cb = config_get_report_desc, - .user_arg = 0 - }; + // Driver is mounted without report descriptor + config_driver_mount_complete(daddr, instance, NULL, 0); + }else + { + tuh_descriptor_get_hid_report(daddr, itf_num, hid_itf->report_desc_type, 0, usbh_get_enum_buf(), hid_itf->report_desc_len, process_set_config, CONFIG_COMPLETE); + } + break; - TU_ASSERT( tuh_control_xfer(dev_addr, &new_xfer) ); - return true; -} + case CONFIG_COMPLETE: + { + uint8_t const* desc_report = usbh_get_enum_buf(); + uint16_t const desc_len = tu_le16toh(xfer->setup->wLength); -static bool config_get_report_desc(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) -{ - // We can be here after SET_IDLE or SET_PROTOCOL (boot device) - // Trigger assert if result is not successful with set protocol - if ( xfer->request.bRequest != HID_REQ_CONTROL_SET_IDLE ) - { - TU_ASSERT(result == XFER_RESULT_SUCCESS); + config_driver_mount_complete(daddr, instance, desc_report, desc_len); + } + break; + + default: break; } - - uint8_t const itf_num = (uint8_t) xfer->request.wIndex; - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); - hidh_interface_t* hid_itf = get_instance(dev_addr, instance); - - // Get Report Descriptor if possible - // using usbh enumeration buffer since report descriptor can be very long - if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE ) - { - TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len); - - // Driver is mounted without report descriptor - config_driver_mount_complete(dev_addr, instance, NULL, 0); - }else - { - TU_ASSERT(tuh_descriptor_get_hid_report(dev_addr, itf_num, hid_itf->report_desc_type, 0, usbh_get_enum_buf(), hid_itf->report_desc_len, config_get_report_desc_complete, 0)); - } - - return true; -} - -static bool config_get_report_desc_complete(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) -{ - TU_ASSERT(XFER_RESULT_SUCCESS == result); - - uint8_t const itf_num = (uint8_t) xfer->request.wIndex; - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); - - uint8_t const* desc_report = usbh_get_enum_buf(); - uint16_t const desc_len = xfer->request.wLength; - - config_driver_mount_complete(dev_addr, instance, desc_report, desc_len); - - return true; } static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index c009e508..c54a63f3 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -358,13 +358,14 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32 // MSC Enumeration //--------------------------------------------------------------------+ -static bool config_get_maxlun_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); +static void config_get_maxlun_complete (tuh_xfer_t* xfer); static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) { + (void) rhport; TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass && MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol); @@ -378,7 +379,7 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de for(uint32_t i=0; i<2; i++) { TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc)); + TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc)); if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) { @@ -405,47 +406,47 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) //------------- Get Max Lun -------------// TU_LOG2("MSC Get Max Lun\r\n"); - tuh_control_xfer_t const xfer = + tusb_control_request_t const request = { - .request = + .bmRequestType_bit = { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_IN - }, - .bRequest = MSC_REQ_GET_MAX_LUN, - .wValue = 0, - .wIndex = itf_num, - .wLength = 1 + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN }, + .bRequest = MSC_REQ_GET_MAX_LUN, + .wValue = 0, + .wIndex = itf_num, + .wLength = 1 + }; + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, .buffer = &p_msc->max_lun, .complete_cb = config_get_maxlun_complete, - .user_arg = 0 + .user_data = 0 }; - TU_ASSERT(tuh_control_xfer(dev_addr, &xfer)); + TU_ASSERT(tuh_control_xfer(&xfer)); return true; } -static bool config_get_maxlun_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void config_get_maxlun_complete (tuh_xfer_t* xfer) { - (void) xfer; - - msch_interface_t* p_msc = get_itf(dev_addr); + uint8_t const daddr = xfer->daddr; + msch_interface_t* p_msc = get_itf(daddr); // STALL means zero - p_msc->max_lun = (XFER_RESULT_SUCCESS == result) ? _msch_buffer[0] : 0; + p_msc->max_lun = (XFER_RESULT_SUCCESS == xfer->result) ? _msch_buffer[0] : 0; p_msc->max_lun++; // MAX LUN is minus 1 by specs // TODO multiple LUN support TU_LOG2("SCSI Test Unit Ready\r\n"); uint8_t const lun = 0; - tuh_msc_test_unit_ready(dev_addr, lun, config_test_unit_ready_complete); - - return true; + tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete); } static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw) @@ -483,7 +484,7 @@ static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw // Capacity response field: Block size and Last LBA are both Big-Endian scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer); p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1; - p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size); + p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size); // Mark enumeration is complete p_msc->mounted = true; diff --git a/src/host/hub.c b/src/host/hub.c index c1909cb5..9e546f6f 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -78,89 +78,98 @@ static char const* const _hub_feature_str[] = // HUB //--------------------------------------------------------------------+ bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - tuh_control_xfer_t const xfer = + tusb_control_request_t const request = { - .request = + .bmRequestType_bit = { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_OTHER, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = HUB_REQUEST_CLEAR_FEATURE, - .wValue = feature, - .wIndex = hub_port, - .wLength = 0 + .recipient = TUSB_REQ_RCPT_OTHER, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT }, + .bRequest = HUB_REQUEST_CLEAR_FEATURE, + .wValue = feature, + .wIndex = hub_port, + .wLength = 0 + }; + tuh_xfer_t xfer = + { + .daddr = hub_addr, + .ep_addr = 0, + .setup = &request, .buffer = NULL, .complete_cb = complete_cb, - .user_arg = user_arg + .user_data = user_data }; TU_LOG2("HUB Clear Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port); - TU_ASSERT( tuh_control_xfer(hub_addr, &xfer) ); + TU_ASSERT( tuh_control_xfer(&xfer) ); return true; } bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - tuh_control_xfer_t const xfer = + tusb_control_request_t const request = { - .request = + .bmRequestType_bit = { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_OTHER, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = HUB_REQUEST_SET_FEATURE, - .wValue = feature, - .wIndex = hub_port, - .wLength = 0 + .recipient = TUSB_REQ_RCPT_OTHER, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT }, + .bRequest = HUB_REQUEST_SET_FEATURE, + .wValue = feature, + .wIndex = hub_port, + .wLength = 0 + }; + tuh_xfer_t xfer = + { + .daddr = hub_addr, + .ep_addr = 0, + .setup = &request, .buffer = NULL, .complete_cb = complete_cb, - .user_arg = user_arg + .user_data = user_data }; TU_LOG2("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port); - TU_ASSERT( tuh_control_xfer(hub_addr, &xfer) ); + TU_ASSERT( tuh_control_xfer(&xfer) ); return true; } bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - tuh_control_xfer_t const xfer = + tusb_control_request_t const request = { - .request = + .bmRequestType_bit = { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_OTHER, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_IN - }, - .bRequest = HUB_REQUEST_GET_STATUS, - .wValue = 0, - .wIndex = hub_port, - .wLength = 4 + .recipient = TUSB_REQ_RCPT_OTHER, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN }, + .bRequest = HUB_REQUEST_GET_STATUS, + .wValue = 0, + .wIndex = hub_port, + .wLength = 4 + }; + tuh_xfer_t xfer = + { + .daddr = hub_addr, + .ep_addr = 0, + .setup = &request, .buffer = resp, .complete_cb = complete_cb, - .user_arg = user_arg + .user_data = user_data }; TU_LOG2("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port); - TU_ASSERT( tuh_control_xfer( hub_addr, &xfer) ); + TU_ASSERT( tuh_control_xfer(&xfer) ); return true; } @@ -174,6 +183,8 @@ void hub_init(void) bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { + (void) rhport; + TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass && 0 == itf_desc->bInterfaceSubClass); @@ -190,7 +201,7 @@ bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep)); + TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep)); hub_interface_t* p_hub = get_itf(dev_addr); @@ -219,8 +230,8 @@ bool hub_edpt_status_xfer(uint8_t dev_addr) // Set Configure //--------------------------------------------------------------------+ -static bool config_set_port_power (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); -static bool config_port_power_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); +static void config_set_port_power (tuh_xfer_t* xfer); +static void config_port_power_complete (tuh_xfer_t* xfer); bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) { @@ -228,38 +239,41 @@ bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) TU_ASSERT(itf_num == p_hub->itf_num); // Get Hub Descriptor - tuh_control_xfer_t const xfer = + tusb_control_request_t const request = { - .request = + .bmRequestType_bit = { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_DEVICE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_IN - }, - .bRequest = HUB_REQUEST_GET_DESCRIPTOR, - .wValue = 0, - .wIndex = 0, - .wLength = sizeof(descriptor_hub_desc_t) + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN }, - - .buffer = _hub_buffer, - .complete_cb = config_set_port_power, - .user_arg = 0 + .bRequest = HUB_REQUEST_GET_DESCRIPTOR, + .wValue = 0, + .wIndex = 0, + .wLength = sizeof(descriptor_hub_desc_t) }; - TU_ASSERT( tuh_control_xfer(dev_addr, &xfer) ); + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = _hub_buffer, + .complete_cb = config_set_port_power, + .user_data = 0 + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); return true; } -static bool config_set_port_power (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void config_set_port_power (tuh_xfer_t* xfer) { - (void) xfer; - TU_ASSERT(XFER_RESULT_SUCCESS == result); + TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, ); - hub_interface_t* p_hub = get_itf(dev_addr); + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); // only use number of ports in hub descriptor descriptor_hub_desc_t const* desc_hub = (descriptor_hub_desc_t const*) _hub_buffer; @@ -269,38 +283,38 @@ static bool config_set_port_power (uint8_t dev_addr, tuh_control_xfer_t const * // Set Port Power to be able to detect connection, starting with port 1 uint8_t const hub_port = 1; - return hub_port_set_feature(dev_addr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0); + hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0); } -static bool config_port_power_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void config_port_power_complete (tuh_xfer_t* xfer) { - TU_ASSERT(XFER_RESULT_SUCCESS == result); - hub_interface_t* p_hub = get_itf(dev_addr); + TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, ); - if (xfer->request.wIndex == p_hub->port_count) + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); + + if (xfer->setup->wIndex == p_hub->port_count) { // All ports are power -> queue notification status endpoint and // complete the SET CONFIGURATION - TU_ASSERT( usbh_edpt_xfer(dev_addr, p_hub->ep_in, &p_hub->status_change, 1) ); + TU_ASSERT( usbh_edpt_xfer(daddr, p_hub->ep_in, &p_hub->status_change, 1), ); - usbh_driver_set_config_complete(dev_addr, p_hub->itf_num); + usbh_driver_set_config_complete(daddr, p_hub->itf_num); }else { // power next port - uint8_t const hub_port = (uint8_t) (xfer->request.wIndex + 1); - return hub_port_set_feature(dev_addr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0); + uint8_t const hub_port = (uint8_t) (xfer->setup->wIndex + 1); + hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0); } - - return true; } //--------------------------------------------------------------------+ // Connection Changes //--------------------------------------------------------------------+ -static bool connection_get_status_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); -static bool connection_clear_conn_change_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); -static bool connection_port_reset_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result); +static void connection_get_status_complete (tuh_xfer_t* xfer); +static void connection_clear_conn_change_complete (tuh_xfer_t* xfer); +static void connection_port_reset_complete (tuh_xfer_t* xfer); // callback as response of interrupt endpoint polling bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) @@ -328,12 +342,13 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32 return true; } -static bool connection_get_status_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void connection_get_status_complete (tuh_xfer_t* xfer) { - TU_ASSERT(result == XFER_RESULT_SUCCESS); + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); - hub_interface_t* p_hub = get_itf(dev_addr); - uint8_t const port_num = (uint8_t) xfer->request.wIndex; + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); + uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); // Connection change if (p_hub->port_status.change.connection) @@ -342,7 +357,7 @@ static bool connection_get_status_complete (uint8_t dev_addr, tuh_control_xfer_t //TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, ); // Acknowledge Port Connection Change - hub_port_clear_feature(dev_addr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0); + hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0); }else { // Other changes are: Enable, Suspend, Over Current, Reset, L1 state @@ -350,65 +365,61 @@ static bool connection_get_status_complete (uint8_t dev_addr, tuh_control_xfer_t // prepare for next hub status // TODO continue with status_change, or maybe we can do it again with status - hub_edpt_status_xfer(dev_addr); + hub_edpt_status_xfer(daddr); } - - return true; } -static bool connection_clear_conn_change_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void connection_clear_conn_change_complete (tuh_xfer_t* xfer) { - TU_ASSERT(result == XFER_RESULT_SUCCESS); + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); - hub_interface_t* p_hub = get_itf(dev_addr); - uint8_t const port_num = (uint8_t) xfer->request.wIndex; + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); + uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); if ( p_hub->port_status.status.connection ) { // Reset port if attach event - hub_port_reset(dev_addr, port_num, connection_port_reset_complete, 0); + hub_port_reset(daddr, port_num, connection_port_reset_complete, 0); }else { // submit detach event hcd_event_t event = { - .rhport = usbh_get_rhport(dev_addr), + .rhport = usbh_get_rhport(daddr), .event_id = HCD_EVENT_DEVICE_REMOVE, .connection = { - .hub_addr = dev_addr, + .hub_addr = daddr, .hub_port = port_num } }; hcd_event_handler(&event, false); } - - return true; } -static bool connection_port_reset_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void connection_port_reset_complete (tuh_xfer_t* xfer) { - TU_ASSERT(result == XFER_RESULT_SUCCESS); + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); - // hub_interface_t* p_hub = get_itf(dev_addr); - uint8_t const port_num = (uint8_t) xfer->request.wIndex; + uint8_t const daddr = xfer->daddr; + // hub_interface_t* p_hub = get_itf(daddr); + uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); // submit attach event hcd_event_t event = { - .rhport = usbh_get_rhport(dev_addr), + .rhport = usbh_get_rhport(daddr), .event_id = HCD_EVENT_DEVICE_ATTACH, .connection = { - .hub_addr = dev_addr, + .hub_addr = daddr, .hub_port = port_num } }; hcd_event_handler(&event, false); - - return true; } #endif diff --git a/src/host/hub.h b/src/host/hub.h index 30cf3dd1..390740e1 100644 --- a/src/host/hub.h +++ b/src/host/hub.h @@ -173,31 +173,31 @@ TU_VERIFY_STATIC( sizeof(hub_port_status_response_t) == 4, "size is not correct" // Clear feature bool hub_port_clear_feature (uint8_t hub_addr, uint8_t hub_port, uint8_t feature, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Set feature bool hub_port_set_feature (uint8_t hub_addr, uint8_t hub_port, uint8_t feature, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get port status bool hub_port_get_status (uint8_t hub_addr, uint8_t hub_port, void* resp, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get status from Interrupt endpoint bool hub_edpt_status_xfer(uint8_t dev_addr); // Reset a port static inline bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - return hub_port_set_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET, complete_cb, user_arg); + return hub_port_set_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET, complete_cb, user_data); } // Clear Reset Change static inline bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_arg); + return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data); } diff --git a/src/host/usbh.c b/src/host/usbh.c index c7dfaddf..f534070d 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -82,8 +82,7 @@ typedef struct { uint8_t speed; // Device State - struct TU_ATTR_PACKED - { + struct TU_ATTR_PACKED { volatile uint8_t connected : 1; volatile uint8_t addressed : 1; volatile uint8_t configured : 1; @@ -109,6 +108,14 @@ typedef struct { tu_edpt_state_t ep_status[CFG_TUH_ENDPOINT_MAX][2]; +#if CFG_TUH_API_EDPT_XFER + // TODO array can be CFG_TUH_ENDPOINT_MAX-1 + struct { + tuh_xfer_cb_t complete_cb; + uintptr_t user_data; + }ep_callback[CFG_TUH_ENDPOINT_MAX][2]; +#endif + } usbh_device_t; //--------------------------------------------------------------------+ @@ -223,6 +230,8 @@ TU_ATTR_ALWAYS_INLINE static inline void usbh_unlock(void) #else +#define _usbh_mutex NULL + #define usbh_lock() #define usbh_unlock() @@ -241,9 +250,14 @@ static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; // We will only execute control transfer one at a time. struct { - tuh_control_xfer_t xfer; - uint8_t daddr; // device address that is transferring + tusb_control_request_t request TU_ATTR_ALIGNED(4); + uint8_t* buffer; + tuh_xfer_cb_t complete_cb; + uintptr_t user_data; + + uint8_t daddr; volatile uint8_t stage; + volatile uint16_t actual_len; }_ctrl_xfer; //------------- Helper Function -------------// @@ -251,7 +265,7 @@ struct TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr) { - TU_ASSERT(dev_addr, NULL); + TU_VERIFY(dev_addr > 0 && dev_addr <= TOTAL_DEVICES, NULL); return &_usbh_devices[dev_addr-1]; } @@ -265,15 +279,17 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t //--------------------------------------------------------------------+ bool tuh_mounted(uint8_t dev_addr) { - return get_device(dev_addr)->configured; + usbh_device_t* dev = get_device(dev_addr); + TU_VERIFY(dev); + return dev->configured; } bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid) { *vid = *pid = 0; - TU_VERIFY(tuh_mounted(dev_addr)); usbh_device_t const* dev = get_device(dev_addr); + TU_VERIFY(dev && dev->configured); *vid = dev->vid; *pid = dev->pid; @@ -283,7 +299,8 @@ bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid) tusb_speed_t tuh_speed_get (uint8_t dev_addr) { - return (tusb_speed_t) (dev_addr ? get_device(dev_addr)->speed : _dev0.speed); + usbh_device_t* dev = get_device(dev_addr); + return (tusb_speed_t) (dev ? get_device(dev_addr)->speed : _dev0.speed); } #if CFG_TUSB_OS == OPT_OS_NONE @@ -296,210 +313,6 @@ void osal_task_delay(uint32_t msec) } #endif -//--------------------------------------------------------------------+ -// Descriptors -//--------------------------------------------------------------------+ - -static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - tuh_control_xfer_t const xfer = - { - .request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_DEVICE, - .type = TUSB_REQ_TYPE_STANDARD, - .direction = TUSB_DIR_IN - }, - .bRequest = TUSB_REQ_GET_DESCRIPTOR, - .wValue = tu_htole16( TU_U16(type, index) ), - .wIndex = tu_htole16(language_id), - .wLength = tu_htole16(len) - }, - - .buffer = buffer, - .complete_cb = complete_cb, - .user_arg = user_arg - }; - - return tuh_control_xfer(daddr, &xfer); -} - -bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - return _get_descriptor(daddr, type, index, 0x0000, buffer, len, complete_cb, user_arg); -} - -bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - len = tu_min16(len, sizeof(tusb_desc_device_t)); - return tuh_descriptor_get(daddr, TUSB_DESC_DEVICE, 0, buffer, len, complete_cb, user_arg); -} - -bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - return tuh_descriptor_get(daddr, TUSB_DESC_CONFIGURATION, index, buffer, len, complete_cb, user_arg); -} - -//------------- String Descriptor -------------// - -bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - return _get_descriptor(daddr, TUSB_DESC_STRING, index, language_id, buffer, len, complete_cb, user_arg); -} - -// Get manufacturer string descriptor -bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - TU_VERIFY(tuh_mounted(daddr)); - usbh_device_t const* dev = get_device(daddr); - if (dev->i_manufacturer == 0) { - return false; - } - return tuh_descriptor_get_string(daddr, dev->i_manufacturer, language_id, buffer, len, complete_cb, user_arg); -} - -// Get product string descriptor -bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - TU_VERIFY(tuh_mounted(daddr)); - usbh_device_t const* dev = get_device(daddr); - if (dev->i_product == 0) { - return false; - } - return tuh_descriptor_get_string(daddr, dev->i_product, language_id, buffer, len, complete_cb, user_arg); -} - -// Get serial string descriptor -bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - TU_VERIFY(tuh_mounted(daddr)); - usbh_device_t const* dev = get_device(daddr); - if (dev->i_serial == 0) { - return false; - } - return tuh_descriptor_get_string(daddr, dev->i_serial, language_id, buffer, len, complete_cb, user_arg); -} - -// Get HID report descriptor -bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - TU_LOG2("HID Get Report Descriptor\r\n"); - tuh_control_xfer_t const xfer = - { - .request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_STANDARD, - .direction = TUSB_DIR_IN - }, - .bRequest = TUSB_REQ_GET_DESCRIPTOR, - .wValue = tu_htole16(TU_U16(desc_type, index)), - .wIndex = tu_htole16((uint16_t) itf_num), - .wLength = len - }, - - .buffer = buffer, - .complete_cb = complete_cb, - .user_arg = user_arg - }; - - return tuh_control_xfer(daddr, &xfer); -} - -bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg) -{ - TU_LOG2("Set Configuration = %d\r\n", config_num); - - tuh_control_xfer_t const xfer = - { - .request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_DEVICE, - .type = TUSB_REQ_TYPE_STANDARD, - .direction = TUSB_DIR_OUT - }, - .bRequest = TUSB_REQ_SET_CONFIGURATION, - .wValue = tu_htole16(config_num), - .wIndex = 0, - .wLength = 0 - }, - - .buffer = NULL, - .complete_cb = complete_cb, - .user_arg = user_arg - }; - - return tuh_control_xfer(daddr, &xfer); -} - -//--------------------------------------------------------------------+ -// Asynchronous -//--------------------------------------------------------------------+ - -#define _CONTROL_SYNC_API(_async_func, _timeout, ...) \ - (void) _timeout; \ - xfer_result_t result = XFER_RESULT_INVALID;\ - /* TODO use timeout to wait */ \ - TU_VERIFY(_async_func(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \ - return (uint8_t) result - -uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms) -{ - _CONTROL_SYNC_API(tuh_descriptor_get, timeout_ms, daddr, type, index, buffer, len); -} - -uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len, uint8_t timeout_ms) -{ - len = tu_min16(len, sizeof(tusb_desc_device_t)); - return tuh_descriptor_get_sync(daddr, TUSB_DESC_DEVICE, 0, buffer, len, timeout_ms); -} - -uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms) -{ - return tuh_descriptor_get_sync(daddr, TUSB_DESC_CONFIGURATION, index, buffer, len, timeout_ms); -} - -uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms) -{ - _CONTROL_SYNC_API(tuh_descriptor_get_hid_report, timeout_ms, daddr, itf_num, desc_type, index, buffer, len); -} - -uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms) -{ - _CONTROL_SYNC_API(tuh_descriptor_get_string, timeout_ms, daddr, index, language_id, buffer, len); -} - -uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms) -{ - _CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, timeout_ms, daddr, language_id, buffer, len); -} - -uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms) -{ - _CONTROL_SYNC_API(tuh_descriptor_get_product_string, timeout_ms, daddr, language_id, buffer, len); -} - -uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms) -{ - _CONTROL_SYNC_API(tuh_descriptor_get_serial_string, timeout_ms, daddr, language_id, buffer, len); -} - //--------------------------------------------------------------------+ // CLASS-USBD API (don't require to verify parameters) //--------------------------------------------------------------------+ @@ -524,7 +337,8 @@ bool tuh_init(uint8_t rhport) TU_LOG2("USBH init\r\n"); TU_LOG2_INT(sizeof(usbh_device_t)); TU_LOG2_INT(sizeof(hcd_event_t)); - TU_LOG2_INT(sizeof(tuh_control_xfer_t)); + TU_LOG2_INT(sizeof(_ctrl_xfer)); + TU_LOG2_INT(sizeof(tuh_xfer_t)); // Event queue _usbh_q = osal_queue_create( &_usbh_qdef ); @@ -629,6 +443,8 @@ void tuh_task(void) else { usbh_device_t* dev = get_device(event.dev_addr); + TU_ASSERT(dev, ); + dev->ep_status[epnum][ep_dir].busy = 0; dev->ep_status[epnum][ep_dir].claimed = 0; @@ -638,10 +454,38 @@ void tuh_task(void) }else { uint8_t drv_id = dev->ep2drv[epnum][ep_dir]; - TU_ASSERT(drv_id < USBH_CLASS_DRIVER_COUNT, ); + if(drv_id < USBH_CLASS_DRIVER_COUNT) + { + TU_LOG2("%s xfer callback\r\n", usbh_class_drivers[drv_id].name); + usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + } + else + { +#if CFG_TUH_API_EDPT_XFER + tuh_xfer_cb_t complete_cb = dev->ep_callback[epnum][ep_dir].complete_cb; + if ( complete_cb ) + { + tuh_xfer_t xfer = + { + .daddr = event.dev_addr, + .ep_addr = ep_addr, + .result = event.xfer_complete.result, + .actual_len = event.xfer_complete.len, + .buflen = 0, // not available + .buffer = NULL, // not available + .complete_cb = complete_cb, + .user_data = dev->ep_callback[epnum][ep_dir].user_data + }; - TU_LOG2("%s xfer callback\r\n", usbh_class_drivers[drv_id].name); - usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + complete_cb(&xfer); + }else +#endif + { + // no driver/callback responsible for this transfer + TU_ASSERT(false, ); + } + + } } } } @@ -656,13 +500,201 @@ void tuh_task(void) } } +//--------------------------------------------------------------------+ +// Control transfer +//--------------------------------------------------------------------+ + +static void _control_blocking_complete_cb(tuh_xfer_t* xfer) +{ + // update result + *((xfer_result_t*) xfer->user_data) = xfer->result; +} + +// TODO timeout_ms is not supported yet +bool tuh_control_xfer (tuh_xfer_t* xfer) +{ + // EP0 with setup packet + TU_VERIFY(xfer->ep_addr == 0 && xfer->setup); + + // pre-check to help reducing mutex lock + TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE); + + uint8_t const daddr = xfer->daddr; + + // TODO probably better to use semaphore as resource management than mutex + usbh_lock(); + + bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE); + if (is_idle) + { + _ctrl_xfer.stage = CONTROL_STAGE_SETUP; + _ctrl_xfer.daddr = daddr; + _ctrl_xfer.actual_len = 0; + + _ctrl_xfer.request = (*xfer->setup); + _ctrl_xfer.buffer = xfer->buffer; + _ctrl_xfer.complete_cb = xfer->complete_cb; + _ctrl_xfer.user_data = xfer->user_data; + } + + usbh_unlock(); + + TU_VERIFY(is_idle); + const uint8_t rhport = usbh_get_rhport(daddr); + + TU_LOG2("[%u:%u] %s: ", rhport, daddr, xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[xfer->setup->bRequest] : "Unknown Request"); + TU_LOG2_VAR(&xfer->setup); + TU_LOG2("\r\n"); + + if (xfer->complete_cb) + { + TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) ); + }else + { + // blocking if complete callback is not provided + // change callback to internal blocking, and result as user argument + volatile xfer_result_t result = XFER_RESULT_INVALID; + + // use user_data to point to xfer_result_t + _ctrl_xfer.user_data = (uintptr_t) &result; + _ctrl_xfer.complete_cb = _control_blocking_complete_cb; + + TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) ); + + while (result == XFER_RESULT_INVALID) + { + // only need to call task if not preempted RTOS + #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO + tuh_task(); + #endif + + // TODO probably some timeout to prevent hanged + } + + // update transfer result + xfer->result = result; + xfer->actual_len = _ctrl_xfer.actual_len; + } + + return true; +} + +TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage) +{ + usbh_lock(); + _ctrl_xfer.stage = stage; + usbh_unlock(); +} + +static void _xfer_complete(uint8_t daddr, xfer_result_t result) +{ + TU_LOG2("\r\n"); + + // duplicate xfer since user can execute control transfer within callback + tusb_control_request_t const request = _ctrl_xfer.request; + tuh_xfer_t xfer_temp = + { + .daddr = daddr, + .ep_addr = 0, + .result = result, + .setup = &request, + .actual_len = (uint32_t) _ctrl_xfer.actual_len, + .buffer = _ctrl_xfer.buffer, + .complete_cb = _ctrl_xfer.complete_cb, + .user_data = _ctrl_xfer.user_data + }; + + usbh_lock(); + _ctrl_xfer.stage = CONTROL_STAGE_IDLE; + usbh_unlock(); + + if (xfer_temp.complete_cb) + { + xfer_temp.complete_cb(&xfer_temp); + } +} + +static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) ep_addr; + + const uint8_t rhport = usbh_get_rhport(dev_addr); + tusb_control_request_t const * request = &_ctrl_xfer.request; + + if (XFER_RESULT_SUCCESS != result) + { + TU_LOG2("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED"); + + // terminate transfer if any stage failed + _xfer_complete(dev_addr, result); + }else + { + switch(_ctrl_xfer.stage) + { + case CONTROL_STAGE_SETUP: + if (request->wLength) + { + // DATA stage: initial data toggle is always 1 + _set_control_xfer_stage(CONTROL_STAGE_DATA); + return hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength); + } + __attribute__((fallthrough)); + + case CONTROL_STAGE_DATA: + if (xferred_bytes) + { + TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr); + TU_LOG2_MEM(_ctrl_xfer.buffer, xferred_bytes, 2); + } + + _ctrl_xfer.actual_len = xferred_bytes; + + // ACK stage: toggle is always 1 + _set_control_xfer_stage(CONTROL_STAGE_ACK); + hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0); + break; + + case CONTROL_STAGE_ACK: + _xfer_complete(dev_addr, result); + break; + + default: return false; + } + } + + return true; +} + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +bool tuh_edpt_xfer(tuh_xfer_t* xfer) +{ + uint8_t const daddr = xfer->daddr; + uint8_t const ep_addr = xfer->ep_addr; + + TU_VERIFY(daddr && ep_addr); + + TU_VERIFY(usbh_edpt_claim(daddr, ep_addr)); + + if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, xfer->buflen, xfer->complete_cb, xfer->user_data) ) + { + usbh_edpt_release(daddr, ep_addr); + return false; + } + + return true; +} + //--------------------------------------------------------------------+ // USBH API For Class Driver //--------------------------------------------------------------------+ uint8_t usbh_get_rhport(uint8_t dev_addr) { - return (dev_addr == 0) ? _dev0.rhport : get_device(dev_addr)->rhport; + usbh_device_t* dev = get_device(dev_addr); + return dev ? dev->rhport : _dev0.rhport; } uint8_t* usbh_get_enum_buf(void) @@ -682,16 +714,126 @@ void usbh_int_set(bool enabled) } } +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) +{ + usbh_device_t* dev = get_device(dev_addr); + + // addr0 only use tuh_control_xfer + TU_ASSERT(dev); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex); +} + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) +{ + usbh_device_t* dev = get_device(dev_addr); + + // addr0 only use tuh_control_xfer + TU_ASSERT(dev); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex); +} + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + (void) complete_cb; + (void) user_data; + + usbh_device_t* dev = get_device(dev_addr); + TU_VERIFY(dev); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; + + TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(ep_state->busy == 0); + + // Set busy first since the actual transfer can be complete before hcd_edpt_xfer() + // could return and USBH task can preempt and clear the busy + ep_state->busy = 1; + +#if CFG_TUH_API_EDPT_XFER + dev->ep_callback[epnum][dir].complete_cb = complete_cb; + dev->ep_callback[epnum][dir].user_data = user_data; +#endif + + if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) ) + { + TU_LOG2("OK\r\n"); + return true; + }else + { + // HCD error, mark endpoint as ready to allow next transfer + ep_state->busy = 0; + ep_state->claimed = 0; + TU_LOG1("Failed\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) +{ + TU_LOG2("Open EP0 with Size = %u (addr = %u)\r\n", max_packet_size, dev_addr); + + tusb_desc_endpoint_t ep0_desc = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, + .wMaxPacketSize = max_packet_size, + .bInterval = 0 + }; + + return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, &ep0_desc); +} + +bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) +{ + TU_ASSERT( tu_edpt_validate(desc_ep, tuh_speed_get(dev_addr)) ); + + return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, desc_ep); +} + +bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + usbh_device_t* dev = get_device(dev_addr); + TU_VERIFY(dev); + + return dev->ep_status[epnum][dir].busy; +} + //--------------------------------------------------------------------+ // HCD Event Handler //--------------------------------------------------------------------+ void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info) { - if (dev_addr) - { - usbh_device_t const* dev = get_device(dev_addr); + usbh_device_t const* dev = get_device(dev_addr); + if (dev) + { devtree_info->rhport = dev->rhport; devtree_info->hub_addr = dev->hub_addr; devtree_info->hub_port = dev->hub_port; @@ -762,269 +904,223 @@ void hcd_event_device_remove(uint8_t hostid, bool in_isr) } //--------------------------------------------------------------------+ -// Endpoint API +// Descriptors Async //--------------------------------------------------------------------+ -// TODO has some duplication code with device, refactor later -bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) +// generic helper to get a descriptor +// if blocking, user_data could be pointed to xfer_result +static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - // addr0 is always available - if (dev_addr == 0) return true; - - usbh_device_t* dev = get_device(dev_addr); - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; - -#if TUSB_OPT_MUTEX - return tu_edpt_claim(ep_state, _usbh_mutex); -#else - return tu_edpt_claim(ep_state, NULL); -#endif -} - -// TODO has some duplication code with device, refactor later -bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) -{ - // addr0 is always available - if (dev_addr == 0) return true; - - usbh_device_t* dev = get_device(dev_addr); - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; - -#if TUSB_OPT_MUTEX - return tu_edpt_release(ep_state, _usbh_mutex); -#else - return tu_edpt_release(ep_state, NULL); -#endif -} - -// TODO has some duplication code with device, refactor later -bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - usbh_device_t* dev = get_device(dev_addr); - - TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); - - // Attempt to transfer on a busy endpoint, sound like an race condition ! - TU_ASSERT(dev->ep_status[epnum][dir].busy == 0); - - // Set busy first since the actual transfer can be complete before hcd_edpt_xfer() - // could return and USBH task can preempt and clear the busy - dev->ep_status[epnum][dir].busy = true; - - if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) ) + tusb_control_request_t const request = { - TU_LOG2("OK\r\n"); - return true; - }else - { - // HCD error, mark endpoint as ready to allow next transfer - dev->ep_status[epnum][dir].busy = false; - dev->ep_status[epnum][dir].claimed = 0; - TU_LOG2("failed\r\n"); - TU_BREAKPOINT(); - return false; - } -} - -static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) -{ - TU_LOG2("Open EP0 with Size = %u (addr = %u)\r\n", max_packet_size, dev_addr); - - tusb_desc_endpoint_t ep0_desc = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = 0, - .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, - .wMaxPacketSize = max_packet_size, - .bInterval = 0 + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_IN + }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = tu_htole16( TU_U16(type, index) ), + .wIndex = tu_htole16(language_id), + .wLength = tu_htole16(len) }; - return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, &ep0_desc); + tuh_xfer_t xfer = + { + .daddr = daddr, + .ep_addr = 0, + .setup = &request, + .buffer = buffer, + .complete_cb = complete_cb, + .user_data = user_data + }; + + bool const ret = tuh_control_xfer(&xfer); + + // if blocking, user_data could be pointed to xfer_result + if ( !complete_cb && user_data ) + { + *((xfer_result_t*) user_data) = xfer.result; + } + + return ret; } -bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) +bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - usbh_device_t* dev = get_device(dev_addr); - TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) dev->speed)); - - return hcd_edpt_open(rhport, dev_addr, desc_ep); + return _get_descriptor(daddr, type, index, 0x0000, buffer, len, complete_cb, user_data); } -bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) +bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + len = tu_min16(len, sizeof(tusb_desc_device_t)); + return tuh_descriptor_get(daddr, TUSB_DESC_DEVICE, 0, buffer, len, complete_cb, user_data); +} - usbh_device_t* dev = get_device(dev_addr); +bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return tuh_descriptor_get(daddr, TUSB_DESC_CONFIGURATION, index, buffer, len, complete_cb, user_data); +} - return dev->ep_status[epnum][dir].busy; +//------------- String Descriptor -------------// + +bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return _get_descriptor(daddr, TUSB_DESC_STRING, index, language_id, buffer, len, complete_cb, user_data); +} + +// Get manufacturer string descriptor +bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + usbh_device_t const* dev = get_device(daddr); + TU_VERIFY(dev && dev->i_manufacturer); + return tuh_descriptor_get_string(daddr, dev->i_manufacturer, language_id, buffer, len, complete_cb, user_data); +} + +// Get product string descriptor +bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + usbh_device_t const* dev = get_device(daddr); + TU_VERIFY(dev && dev->i_product); + return tuh_descriptor_get_string(daddr, dev->i_product, language_id, buffer, len, complete_cb, user_data); +} + +// Get serial string descriptor +bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + usbh_device_t const* dev = get_device(daddr); + TU_VERIFY(dev && dev->i_serial); + return tuh_descriptor_get_string(daddr, dev->i_serial, language_id, buffer, len, complete_cb, user_data); +} + +// Get HID report descriptor +// if blocking, user_data could be pointed to xfer_result +bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_LOG2("HID Get Report Descriptor\r\n"); + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_IN + }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = tu_htole16(TU_U16(desc_type, index)), + .wIndex = tu_htole16((uint16_t) itf_num), + .wLength = len + }; + + tuh_xfer_t xfer = + { + .daddr = daddr, + .ep_addr = 0, + .setup = &request, + .buffer = buffer, + .complete_cb = complete_cb, + .user_data = user_data + }; + + bool const ret = tuh_control_xfer(&xfer); + + // if blocking, user_data could be pointed to xfer_result + if ( !complete_cb && user_data ) + { + *((xfer_result_t*) user_data) = xfer.result; + } + + return ret; +} + +bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_LOG2("Set Configuration = %d\r\n", config_num); + + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_OUT + }, + .bRequest = TUSB_REQ_SET_CONFIGURATION, + .wValue = tu_htole16(config_num), + .wIndex = 0, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); } //--------------------------------------------------------------------+ -// Control transfer +// Descriptor Sync //--------------------------------------------------------------------+ -static bool _control_blocking_complete_cb(uint8_t daddr, tuh_control_xfer_t const * xfer, xfer_result_t result) +#define _CONTROL_SYNC_API(_async_func, ...) \ + xfer_result_t result = XFER_RESULT_INVALID;\ + TU_VERIFY(_async_func(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \ + return (uint8_t) result + +uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len) { - (void) daddr; - - // update result - *((xfer_result_t*) xfer->user_arg) = result; - - return true; + _CONTROL_SYNC_API(tuh_descriptor_get, daddr, type, index, buffer, len); } -bool tuh_control_xfer (uint8_t daddr, tuh_control_xfer_t const* xfer) +uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len) { - // pre-check to help reducing mutex lock - TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE); - - // TODO probably better to use semaphore as resource management than mutex - usbh_lock(); - - bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE); - if (is_idle) _ctrl_xfer.stage = CONTROL_STAGE_SETUP; - - usbh_unlock(); - - TU_VERIFY(is_idle); - - const uint8_t rhport = usbh_get_rhport(daddr); - - TU_LOG2("[%u:%u] %s: ", rhport, daddr, xfer->request.bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[xfer->request.bRequest] : "Unknown Request"); - TU_LOG2_VAR(&xfer->request); - TU_LOG2("\r\n"); - - _ctrl_xfer.daddr = daddr; - _ctrl_xfer.xfer = (*xfer); - - if (xfer->complete_cb) - { - TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.xfer.request) ); - }else - { - // user_arg must point to xfer_result_t to hold result - TU_VERIFY(xfer->user_arg); - - // blocking if complete callback is not provided - // change callback to internal blocking, and result as user argument - volatile xfer_result_t* result = (volatile xfer_result_t*) xfer->user_arg; - - _ctrl_xfer.xfer.complete_cb = _control_blocking_complete_cb; - *result = XFER_RESULT_INVALID; - - TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.xfer.request) ); - - while ((*result) == XFER_RESULT_INVALID) - { - // only need to call task if not preempted RTOS - #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO - tuh_task(); - #endif - - // TODO probably some timeout to prevent hanged - } - } - - return true; + _CONTROL_SYNC_API(tuh_descriptor_get_device, daddr, buffer, len); } -uint8_t tuh_control_xfer_sync(uint8_t daddr, tuh_control_xfer_t const* xfer, uint32_t timeout_ms) +uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len) { - (void) timeout_ms; - - xfer_result_t result = XFER_RESULT_INVALID; - tuh_control_xfer_t xfer_sync = (*xfer); - - xfer_sync.complete_cb = NULL; - xfer_sync.user_arg = (uintptr_t) &result; - - // TODO use timeout to wait - TU_VERIFY(tuh_control_xfer(daddr, &xfer_sync), XFER_RESULT_TIMEOUT); - - return result; + _CONTROL_SYNC_API(tuh_descriptor_get_configuration, daddr, index, buffer, len); } -TU_ATTR_ALWAYS_INLINE static inline void set_control_xfer_stage(uint8_t stage) +uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len) { - usbh_lock(); - _ctrl_xfer.stage = stage; - usbh_unlock(); + _CONTROL_SYNC_API(tuh_descriptor_get_hid_report, daddr, itf_num, desc_type, index, buffer, len); } -static void _xfer_complete(uint8_t dev_addr, xfer_result_t result) +uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len) { - TU_LOG2("\r\n"); - - // duplicate xfer since user can execute control transfer within callback - tuh_control_xfer_t const xfer_temp = _ctrl_xfer.xfer; - - usbh_lock(); - _ctrl_xfer.stage = CONTROL_STAGE_IDLE; - usbh_unlock(); - - if (xfer_temp.complete_cb) - { - xfer_temp.complete_cb(dev_addr, &xfer_temp, result); - } + _CONTROL_SYNC_API(tuh_descriptor_get_string, daddr, index, language_id, buffer, len); } -static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) { - (void) ep_addr; - (void) xferred_bytes; + _CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, daddr, language_id, buffer, len); +} - const uint8_t rhport = usbh_get_rhport(dev_addr); - tusb_control_request_t const * request = &_ctrl_xfer.xfer.request; +uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_product_string, daddr, language_id, buffer, len); +} - if (XFER_RESULT_SUCCESS != result) - { - TU_LOG2("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED"); - - // terminate transfer if any stage failed - _xfer_complete(dev_addr, result); - }else - { - switch(_ctrl_xfer.stage) - { - case CONTROL_STAGE_SETUP: - if (request->wLength) - { - // DATA stage: initial data toggle is always 1 - set_control_xfer_stage(CONTROL_STAGE_DATA); - return hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.xfer.buffer, request->wLength); - } - __attribute__((fallthrough)); - - case CONTROL_STAGE_DATA: - if (request->wLength) - { - TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr); - TU_LOG2_MEM(_ctrl_xfer.xfer.buffer, request->wLength, 2); - } - - // ACK stage: toggle is always 1 - set_control_xfer_stage(CONTROL_STAGE_ACK); - hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0); - break; - - case CONTROL_STAGE_ACK: - _xfer_complete(dev_addr, result); - break; - - default: return false; - } - } - - return true; +uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_serial_string, daddr, language_id, buffer, len); } //--------------------------------------------------------------------+ @@ -1062,7 +1158,7 @@ static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t h hcd_device_close(rhport, dev_addr); clear_device(dev); // abort on-going control xfer if any - if (_ctrl_xfer.daddr == dev_addr) set_control_xfer_stage(CONTROL_STAGE_IDLE); + if (_ctrl_xfer.daddr == dev_addr) _set_control_xfer_stage(CONTROL_STAGE_IDLE); } } } @@ -1094,20 +1190,22 @@ enum { }; static bool enum_request_set_addr(void); -static bool parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); +static bool _parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); static void enum_full_complete(void); // process device enumeration -static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result) +static void process_enumeration(tuh_xfer_t* xfer) { - if (XFER_RESULT_SUCCESS != result) + if (XFER_RESULT_SUCCESS != xfer->result) { // stop enumeration, maybe we could retry this enum_full_complete(); - return false; + return; } - uintptr_t const state = xfer->user_arg; + uint8_t const daddr = xfer->daddr; + uintptr_t const state = xfer->user_data; + switch(state) { #if CFG_TUH_HUB @@ -1122,7 +1220,7 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe { // device unplugged while delaying, nothing else to do enum_full_complete(); - return false; + return; } _dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH : @@ -1138,7 +1236,7 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe case ENUM_HUB_GET_STATUS_2: osal_task_delay(RESET_DELAY); - TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_2) ); + TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_2), ); break; case ENUM_HUB_CLEAR_RESET_2: @@ -1149,7 +1247,7 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe // Acknowledge Port Reset Change if Reset Successful if (port_status.change.reset) { - TU_ASSERT( hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_SET_ADDR) ); + TU_ASSERT( hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_SET_ADDR), ); } } break; @@ -1159,11 +1257,11 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe { // TODO probably doesn't need to open/close each enumeration uint8_t const addr0 = 0; - TU_ASSERT( usbh_edpt_control_open(addr0, 8) ); + TU_ASSERT( usbh_edpt_control_open(addr0, 8), ); // Get first 8 bytes of device descriptor for Control Endpoint size TU_LOG2("Get 8 byte of Device Descriptor\r\n"); - TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8, process_enumeration, ENUM_SET_ADDR)); + TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8, process_enumeration, ENUM_SET_ADDR), ); } break; @@ -1182,7 +1280,7 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe else { // after RESET_DELAY the hub_port_reset() already complete - TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_HUB_GET_STATUS_2) ); + TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_HUB_GET_STATUS_2), ); break; } #endif @@ -1194,27 +1292,29 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe case ENUM_GET_DEVICE_DESC: { - uint8_t const new_addr = (uint8_t const) xfer->request.wValue; + uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue); usbh_device_t* new_dev = get_device(new_addr); + TU_ASSERT(new_dev, ); new_dev->addressed = 1; // TODO close device 0, may not be needed hcd_device_close(_dev0.rhport, 0); // open control pipe for new address - TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size) ); + TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size), ); // Get full device descriptor TU_LOG2("Get Device Descriptor\r\n"); - TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC)); + TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC), ); } break; case ENUM_GET_9BYTE_CONFIG_DESC: { tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf; - usbh_device_t* dev = get_device(dev_addr); + usbh_device_t* dev = get_device(daddr); + TU_ASSERT(dev, ); dev->vid = desc_device->idVendor; dev->pid = desc_device->idProduct; @@ -1227,7 +1327,7 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe // Get 9-byte for total length uint8_t const config_idx = CONFIG_NUM - 1; TU_LOG2("Get Configuration[0] Descriptor (9 bytes)\r\n"); - TU_ASSERT( tuh_descriptor_get_configuration(dev_addr, config_idx, _usbh_ctrl_buf, 9, process_enumeration, ENUM_GET_FULL_CONFIG_DESC) ); + TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, 9, process_enumeration, ENUM_GET_FULL_CONFIG_DESC), ); } break; @@ -1239,34 +1339,36 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe uint16_t const total_len = tu_le16toh( tu_unaligned_read16(desc_config + offsetof(tusb_desc_configuration_t, wTotalLength)) ); // TODO not enough buffer to hold configuration descriptor - TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSIZE); + TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSIZE, ); // Get full configuration descriptor uint8_t const config_idx = CONFIG_NUM - 1; TU_LOG2("Get Configuration[0] Descriptor\r\n"); - TU_ASSERT( tuh_descriptor_get_configuration(dev_addr, config_idx, _usbh_ctrl_buf, total_len, process_enumeration, ENUM_SET_CONFIG) ); + TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, total_len, process_enumeration, ENUM_SET_CONFIG), ); } break; case ENUM_SET_CONFIG: // Parse configuration & set up drivers // Driver open aren't allowed to make any usb transfer yet - TU_ASSERT( parse_configuration_descriptor(dev_addr, (tusb_desc_configuration_t*) _usbh_ctrl_buf) ); + TU_ASSERT( _parse_configuration_descriptor(daddr, (tusb_desc_configuration_t*) _usbh_ctrl_buf), ); - TU_ASSERT( tuh_configuration_set(dev_addr, CONFIG_NUM, process_enumeration, ENUM_CONFIG_DRIVER) ); + TU_ASSERT( tuh_configuration_set(daddr, CONFIG_NUM, process_enumeration, ENUM_CONFIG_DRIVER), ); break; case ENUM_CONFIG_DRIVER: { TU_LOG2("Device configured\r\n"); - usbh_device_t* dev = get_device(dev_addr); + usbh_device_t* dev = get_device(daddr); + TU_ASSERT(dev, ); + dev->configured = 1; // Start the Set Configuration process for interfaces (itf = DRVID_INVALID) // Since driver can perform control transfer within its set_config, this is done asynchronously. // The process continue with next interface when class driver complete its sequence with usbh_driver_set_config_complete() // TODO use separated API instead of using DRVID_INVALID - usbh_driver_set_config_complete(dev_addr, DRVID_INVALID); + usbh_driver_set_config_complete(daddr, DRVID_INVALID); } break; @@ -1275,8 +1377,6 @@ static bool process_enumeration(uint8_t dev_addr, tuh_control_xfer_t const * xfe enum_full_complete(); break; } - - return true; } static bool enum_new_device(hcd_event_t* event) @@ -1297,13 +1397,13 @@ static bool enum_new_device(hcd_event_t* event) _dev0.speed = hcd_port_speed_get(_dev0.rhport ); TU_LOG2("%s Speed\r\n", tu_str_speed[_dev0.speed]); - //enum_request_addr0_device_desc(); - tuh_control_xfer_t const xfer = - { - .complete_cb = process_enumeration, - .user_arg = ENUM_ADDR0_DEVICE_DESC - }; - process_enumeration(0, &xfer, XFER_RESULT_SUCCESS); + // fake transfer to kick-off the enumeration process + tuh_xfer_t xfer; + xfer.daddr = 0; + xfer.result = XFER_RESULT_SUCCESS; + xfer.user_data = ENUM_ADDR0_DEVICE_DESC; + + process_enumeration(&xfer); } #if CFG_TUH_HUB @@ -1322,15 +1422,29 @@ static bool enum_new_device(hcd_event_t* event) return true; } +TU_ATTR_ALWAYS_INLINE +static inline bool is_hub_addr(uint8_t daddr) +{ + return daddr > CFG_TUH_DEVICE_MAX; +} + static uint8_t get_new_address(bool is_hub) { - uint8_t const start = (is_hub ? CFG_TUH_DEVICE_MAX : 0) + 1; - uint8_t const count = (is_hub ? CFG_TUH_HUB : CFG_TUH_DEVICE_MAX); - - for (uint8_t i=0; i < count; i++) + uint8_t start; + uint8_t end; + if ( is_hub ) { - uint8_t const addr = start + i; - if (!get_device(addr)->connected) return addr; + start = CFG_TUH_DEVICE_MAX; + end = start + CFG_TUH_HUB; + }else + { + start = 0; + end = start + CFG_TUH_DEVICE_MAX; + } + + for ( uint8_t idx = start; idx < end; idx++) + { + if (!_usbh_devices[idx].connected) return (idx+1); } return ADDR_INVALID; } @@ -1354,34 +1468,36 @@ static bool enum_request_set_addr(void) new_dev->connected = 1; new_dev->ep0_size = desc_device->bMaxPacketSize0; - tuh_control_xfer_t const xfer = + tusb_control_request_t const request = { - .request = + .bmRequestType_bit = { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_DEVICE, - .type = TUSB_REQ_TYPE_STANDARD, - .direction = TUSB_DIR_OUT - }, - .bRequest = TUSB_REQ_SET_ADDRESS, - .wValue = tu_htole16(new_addr), - .wIndex = 0, - .wLength = 0 + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_OUT }, - - .buffer = NULL, - .complete_cb = process_enumeration, - .user_arg = ENUM_GET_DEVICE_DESC + .bRequest = TUSB_REQ_SET_ADDRESS, + .wValue = tu_htole16(new_addr), + .wIndex = 0, + .wLength = 0 }; - uint8_t const addr0 = 0; - TU_ASSERT( tuh_control_xfer(addr0, &xfer) ); + tuh_xfer_t xfer = + { + .daddr = 0, // dev0 + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = process_enumeration, + .user_data = ENUM_GET_DEVICE_DESC + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); return true; } -static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg) +static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg) { usbh_device_t* dev = get_device(dev_addr); @@ -1497,8 +1613,14 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) { enum_full_complete(); - // Invoke callback if available - if (tuh_mount_cb) tuh_mount_cb(dev_addr); +#if CFG_TUH_HUB + // skip device mount callback for hub + if ( !is_hub_addr(dev_addr) ) +#endif + { + // Invoke callback if available + if (tuh_mount_cb) tuh_mount_cb(dev_addr); + } } } diff --git a/src/host/usbh.h b/src/host/usbh.h index 3bd40c50..e883ac90 100644 --- a/src/host/usbh.h +++ b/src/host/usbh.h @@ -39,17 +39,34 @@ //--------------------------------------------------------------------+ // forward declaration -struct tuh_control_xfer_s; -typedef struct tuh_control_xfer_s tuh_control_xfer_t; +struct tuh_xfer_s; +typedef struct tuh_xfer_s tuh_xfer_t; -typedef bool (*tuh_control_xfer_cb_t)(uint8_t daddr, tuh_control_xfer_t const * xfer, xfer_result_t result); +typedef void (*tuh_xfer_cb_t)(tuh_xfer_t* xfer); -struct tuh_control_xfer_s +// Note1: layout and order of this will be changed in near future +// it is advised to initialize it using member name +// Note2: not all field is available/meaningful in callback, some info is not saved by +// usbh to save SRAM +struct tuh_xfer_s { - tusb_control_request_t request TU_ATTR_ALIGNED(4); - uint8_t* buffer; - tuh_control_xfer_cb_t complete_cb; - uintptr_t user_arg; + uint8_t daddr; + uint8_t ep_addr; + + xfer_result_t result; + uint32_t actual_len; // excluding setup packet + + union + { + tusb_control_request_t const* setup; // setup packet pointer if control transfer + uint32_t buflen; // expected length if not control transfer (not available in callback) + }; + + uint8_t* buffer; // not available in callback if not control transfer + tuh_xfer_cb_t complete_cb; + uintptr_t user_data; + + // uint32_t timeout_ms; // place holder, not supported yet }; //--------------------------------------------------------------------+ @@ -104,21 +121,28 @@ static inline bool tuh_ready(uint8_t daddr) return tuh_mounted(daddr) && !tuh_suspended(daddr); } -// Carry out a control transfer -// true on success, false if there is on-going control transfer or incorrect parameters -// Blocking if complete callback is NULL, in this case 'user_arg' must contain xfer_result_t variable -bool tuh_control_xfer (uint8_t daddr, tuh_control_xfer_t const* xfer); +//--------------------------------------------------------------------+ +// Transfer API +//--------------------------------------------------------------------+ -// Sync (blocking) version of tuh_control_xfer() -// return transfer result -uint8_t tuh_control_xfer_sync(uint8_t daddr, tuh_control_xfer_t const* xfer, uint32_t timeout_ms); +// Submit a control transfer +// - async: complete callback invoked when finished. +// - sync : blocking if complete callback is NULL. +bool tuh_control_xfer(tuh_xfer_t* xfer); + +// Submit a bulk/interrupt transfer +// - async: complete callback invoked when finished. +// - sync : blocking if complete callback is NULL. +bool tuh_edpt_xfer(tuh_xfer_t* xfer); + +// Open an non-control endpoint +bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep); // Set Configuration (control transfer) // config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1 // true on success, false if there is on-going control transfer or incorrect parameters -// Blocking if complete callback is NULL, in this case 'user_arg' must contain xfer_result_t variable bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); //--------------------------------------------------------------------+ // Descriptors Asynchronous (non-blocking) @@ -127,43 +151,43 @@ bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, // Get an descriptor (control transfer) // true on success, false if there is on-going control transfer or incorrect parameters bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get device descriptor (control transfer) // true on success, false if there is on-going control transfer or incorrect parameters bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get configuration descriptor (control transfer) // true on success, false if there is on-going control transfer or incorrect parameters bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get HID report descriptor (control transfer) // true on success, false if there is on-going control transfer or incorrect parameters bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get string descriptor (control transfer) // true on success, false if there is on-going control transfer or incorrect parameters -// Blocking if complete callback is NULL, in this case 'user_arg' must contain xfer_result_t variable +// Blocking if complete callback is NULL, in this case 'user_data' must contain xfer_result_t variable bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get manufacturer string descriptor (control transfer) // true on success, false if there is on-going control transfer or incorrect parameters bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get product string descriptor (control transfer) // true on success, false if there is on-going control transfer or incorrect parameters bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Get serial string descriptor (control transfer) // true on success, false if there is on-going control transfer or incorrect parameters bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, - tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg); + tuh_xfer_cb_t complete_cb, uintptr_t user_data); //--------------------------------------------------------------------+ // Descriptors Synchronous (blocking) @@ -171,35 +195,35 @@ bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* // Sync (blocking) version of tuh_descriptor_get() // return transfer result -uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms); +uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len); // Sync (blocking) version of tuh_descriptor_get_device() // return transfer result -uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len, uint8_t timeout_ms); +uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len); // Sync (blocking) version of tuh_descriptor_get_configuration() // return transfer result -uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms); +uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len); // Sync (blocking) version of tuh_descriptor_get_hid_report() // return transfer result -uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms); +uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len); // Sync (blocking) version of tuh_descriptor_get_string() // return transfer result -uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms); +uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len); // Sync (blocking) version of tuh_descriptor_get_manufacturer_string() // return transfer result -uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms); +uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len); // Sync (blocking) version of tuh_descriptor_get_product_string() // return transfer result -uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms); +uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len); // Sync (blocking) version of tuh_descriptor_get_serial_string() // return transfer result -uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms); +uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len); #ifdef __cplusplus } diff --git a/src/host/usbh_classdriver.h b/src/host/usbh_classdriver.h index 0435eae7..c156afea 100644 --- a/src/host/usbh_classdriver.h +++ b/src/host/usbh_classdriver.h @@ -63,11 +63,16 @@ void usbh_int_set(bool enabled); // USBH Endpoint API //--------------------------------------------------------------------+ -// Open an endpoint -bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep); +// Submit a usb transfer with callback support, require CFG_TUH_API_EDPT_XFER +bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +TU_ATTR_ALWAYS_INLINE +static inline bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + return usbh_edpt_xfer_with_callback(dev_addr, ep_addr, buffer, total_bytes, NULL, 0); +} -// Submit a usb transfer -bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); // Claim an endpoint before submitting a transfer. // If caller does not make any transfer, it must release endpoint for others. diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 8d420f26..665f2380 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -656,6 +656,26 @@ static void xfer_error_isr(uint8_t hostid) } } +#if CFG_TUSB_DEBUG >= EHCI_DBG + +static inline void print_portsc(ehci_registers_t* regs) +{ + TU_LOG_HEX(EHCI_DBG, regs->portsc); + TU_LOG(EHCI_DBG, " Current Connect Status: %u\r\n", regs->portsc_bm.current_connect_status); + TU_LOG(EHCI_DBG, " Connect Status Change : %u\r\n", regs->portsc_bm.connect_status_change); + TU_LOG(EHCI_DBG, " Port Enabled : %u\r\n", regs->portsc_bm.port_enabled); + TU_LOG(EHCI_DBG, " Port Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change); + + TU_LOG(EHCI_DBG, " Port Reset : %u\r\n", regs->portsc_bm.port_reset); + TU_LOG(EHCI_DBG, " Port Power : %u\r\n", regs->portsc_bm.port_power); +} + +#else + +#define print_portsc(_reg) + +#endif + //------------- Host Controller Driver's Interrupt Handler -------------// void hcd_int_handler(uint8_t rhport) { @@ -675,9 +695,8 @@ void hcd_int_handler(uint8_t rhport) if (int_status & EHCI_INT_MASK_PORT_CHANGE) { - uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL; - - TU_LOG_HEX(EHCI_DBG, regs->portsc); + uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_ALL; + print_portsc(regs); if (regs->portsc_bm.connect_status_change) { diff --git a/src/tusb.h b/src/tusb.h index 222855fc..b776d7d0 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -38,6 +38,8 @@ #include "osal/osal.h" #include "common/tusb_fifo.h" +#include "class/hid/hid.h" + //------------- HOST -------------// #if CFG_TUH_ENABLED #include "host/usbh.h" diff --git a/src/tusb_option.h b/src/tusb_option.h index 0352faae..bd87a953 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -392,6 +392,10 @@ #define CFG_TUH_VENDOR 0 #endif +#ifndef CFG_TUH_API_EDPT_XFER +#define CFG_TUH_API_EDPT_XFER 0 +#endif + //------------------------------------------------------------------ // Configuration Validation //------------------------------------------------------------------