瀏覽代碼

Merge pull request #931 from hathach/more-hid-host

More hid host update
Ha Thach 4 年之前
父節點
當前提交
0a230d57ee

+ 102 - 66
examples/host/cdc_msc_hid/src/hid_app.c

@@ -39,11 +39,15 @@
 static uint8_t const keycode2ascii[128][2] =  { HID_KEYCODE_TO_ASCII };
 
 // Each HID instance can has multiple reports
-static uint8_t _report_count[CFG_TUH_HID];
-static tuh_hid_report_info_t _report_info_arr[CFG_TUH_HID][MAX_REPORT];
+static struct
+{
+  uint8_t report_count;
+  tuh_hid_report_info_t report_info[MAX_REPORT];
+}hid_info[CFG_TUH_HID];
 
 static void process_kbd_report(hid_keyboard_report_t const *report);
 static void process_mouse_report(hid_mouse_report_t const * report);
+static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
 
 void hid_app_task(void)
 {
@@ -61,13 +65,19 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
 {
   printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
 
-  // Interface protocol
-  const char* protocol_str[] = { "None", "Keyboard", "Mouse" }; // hid_protocol_type_t
-  uint8_t const interface_protocol = tuh_hid_interface_protocol(dev_addr, instance);
+  // Interface protocol (hid_interface_protocol_enum_t)
+  const char* protocol_str[] = { "None", "Keyboard", "Mouse" };
+  uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
+
+  printf("HID Interface Protocol = %s\r\n", protocol_str[itf_protocol]);
 
-  // Parse report descriptor with built-in parser
-  _report_count[instance] = tuh_hid_parse_report_descriptor(_report_info_arr[instance], MAX_REPORT, desc_report, desc_len);
-  printf("HID has %u reports and interface protocol = %s\r\n", _report_count[instance], protocol_str[interface_protocol]);
+  // By default host stack will use activate boot protocol on supported interface.
+  // Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser)
+  if ( itf_protocol == HID_ITF_PROTOCOL_NONE )
+  {
+    hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len);
+    printf("HID has %u reports \r\n", hid_info[instance].report_count);
+  }
 }
 
 // Invoked when device with hid interface is un-mounted
@@ -79,66 +89,24 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
 // Invoked when received report from device via interrupt endpoint
 void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
 {
-  (void) dev_addr;
-
-  uint8_t const rpt_count = _report_count[instance];
-  tuh_hid_report_info_t* rpt_info_arr = _report_info_arr[instance];
-  tuh_hid_report_info_t* rpt_info = NULL;
-
-  if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0)
-  {
-    // Simple report without report ID as 1st byte
-    rpt_info = &rpt_info_arr[0];
-  }else
-  {
-    // Composite report, 1st byte is report ID, data starts from 2nd byte
-    uint8_t const rpt_id = report[0];
-
-    // Find report id in the arrray
-    for(uint8_t i=0; i<rpt_count; i++)
-    {
-      if (rpt_id == rpt_info_arr[i].report_id )
-      {
-        rpt_info = &rpt_info_arr[i];
-        break;
-      }
-    }
-
-    report++;
-    len--;
-  }
-
-  if (!rpt_info)
-  {
-    printf("Couldn't find the report info for this report !\r\n");
-    return;
-  }
+  uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
 
-  // For complete list of Usage Page & Usage checkout src/class/hid/hid.h. For examples:
-  // - Keyboard                     : Desktop, Keyboard
-  // - Mouse                        : Desktop, Mouse
-  // - Gamepad                      : Desktop, Gamepad
-  // - Consumer Control (Media Key) : Consumer, Consumer Control
-  // - System Control (Power key)   : Desktop, System Control
-  // - Generic (vendor)             : 0xFFxx, xx
-  if ( rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP )
+  switch (itf_protocol)
   {
-    switch (rpt_info->usage)
-    {
-      case HID_USAGE_DESKTOP_KEYBOARD:
-        TU_LOG1("HID receive keyboard report\r\n");
-        // Assume keyboard follow boot report layout
-        process_kbd_report( (hid_keyboard_report_t const*) report );
-      break;
-
-      case HID_USAGE_DESKTOP_MOUSE:
-        TU_LOG1("HID receive mouse report\r\n");
-        // Assume mouse follow boot report layout
-        process_mouse_report( (hid_mouse_report_t const*) report );
-      break;
-
-      default: break;
-    }
+    case HID_ITF_PROTOCOL_KEYBOARD:
+      TU_LOG2("HID receive boot keyboard report\r\n");
+      process_kbd_report( (hid_keyboard_report_t const*) report );
+    break;
+
+    case HID_ITF_PROTOCOL_MOUSE:
+      TU_LOG2("HID receive boot mouse report\r\n");
+      process_mouse_report( (hid_mouse_report_t const*) report );
+    break;
+
+    default:
+      // Generic report requires matching ReportID and contents with previous parsed report info
+      process_generic_report(dev_addr, instance, report, len);
+    break;
   }
 }
 
@@ -243,3 +211,71 @@ static void process_mouse_report(hid_mouse_report_t const * report)
   //------------- cursor movement -------------//
   cursor_movement(report->x, report->y, report->wheel);
 }
+
+//--------------------------------------------------------------------+
+// Generic Report
+//--------------------------------------------------------------------+
+static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
+{
+  (void) dev_addr;
+
+  uint8_t const rpt_count = hid_info[instance].report_count;
+  tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info;
+  tuh_hid_report_info_t* rpt_info = NULL;
+
+  if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0)
+  {
+    // Simple report without report ID as 1st byte
+    rpt_info = &rpt_info_arr[0];
+  }else
+  {
+    // Composite report, 1st byte is report ID, data starts from 2nd byte
+    uint8_t const rpt_id = report[0];
+
+    // Find report id in the arrray
+    for(uint8_t i=0; i<rpt_count; i++)
+    {
+      if (rpt_id == rpt_info_arr[i].report_id )
+      {
+        rpt_info = &rpt_info_arr[i];
+        break;
+      }
+    }
+
+    report++;
+    len--;
+  }
+
+  if (!rpt_info)
+  {
+    printf("Couldn't find the report info for this report !\r\n");
+    return;
+  }
+
+  // For complete list of Usage Page & Usage checkout src/class/hid/hid.h. For examples:
+  // - Keyboard                     : Desktop, Keyboard
+  // - Mouse                        : Desktop, Mouse
+  // - Gamepad                      : Desktop, Gamepad
+  // - Consumer Control (Media Key) : Consumer, Consumer Control
+  // - System Control (Power key)   : Desktop, System Control
+  // - Generic (vendor)             : 0xFFxx, xx
+  if ( rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP )
+  {
+    switch (rpt_info->usage)
+    {
+      case HID_USAGE_DESKTOP_KEYBOARD:
+        TU_LOG1("HID receive keyboard report\r\n");
+        // Assume keyboard follow boot report layout
+        process_kbd_report( (hid_keyboard_report_t const*) report );
+      break;
+
+      case HID_USAGE_DESKTOP_MOUSE:
+        TU_LOG1("HID receive mouse report\r\n");
+        // Assume mouse follow boot report layout
+        process_mouse_report( (hid_mouse_report_t const*) report );
+      break;
+
+      default: break;
+    }
+  }
+}

+ 4 - 6
src/class/cdc/cdc_device.c

@@ -273,9 +273,6 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
   TU_VERIFY( TUSB_CLASS_CDC                           == itf_desc->bInterfaceClass &&
              CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
 
-  // Note: 0xFF can be used with RNDIS
-  TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA), 0);
-
   // Find available interface
   cdcd_interface_t * p_cdc = NULL;
   for(uint8_t cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
@@ -303,10 +300,11 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
 
   if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
   {
-    // notification endpoint if any
-    TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+    // notification endpoint
+    tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
 
-    p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+    TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 );
+    p_cdc->ep_notif = desc_ep->bEndpointAddress;
 
     drv_len += tu_desc_len(p_desc);
     p_desc   = tu_desc_next(p_desc);

+ 30 - 32
src/class/cdc/cdc_host.c

@@ -149,29 +149,27 @@ void cdch_init(void)
   tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX);
 }
 
-bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
+uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
 {
-  // Only support ACM
-  TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
+  (void) max_len;
 
-  // Only support AT commands, no protocol and vendor specific commands.
-  TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
-            0xff == itf_desc->bInterfaceProtocol);
+  // Only support ACM subclass
+  // Protocol 0xFF can be RNDIS device for windows XP
+  TU_VERIFY( TUSB_CLASS_CDC                           == itf_desc->bInterfaceClass &&
+             CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
+             0xFF                                     != itf_desc->bInterfaceProtocol, 0);
 
-  uint8_t const * p_desc;
-  cdch_data_t * p_cdc;
-
-  p_desc = tu_desc_next(itf_desc);
-  p_cdc  = get_itf(dev_addr);
+  cdch_data_t * p_cdc = get_itf(dev_addr);
 
-  p_cdc->itf_num   = itf_desc->bInterfaceNumber;
-  p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com
+  p_cdc->itf_num      = itf_desc->bInterfaceNumber;
+  p_cdc->itf_protocol = itf_desc->bInterfaceProtocol;
 
   //------------- Communication Interface -------------//
-  (*p_length) = sizeof(tusb_desc_interface_t);
+  uint16_t drv_len = tu_desc_len(itf_desc);
+  uint8_t const * p_desc = tu_desc_next(itf_desc);
 
   // Communication Functional Descriptors
-  while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] )
+  while( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
   {
     if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
     {
@@ -179,52 +177,52 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it
       p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
     }
 
-    (*p_length) += p_desc[DESC_OFFSET_LEN];
+    drv_len += tu_desc_len(p_desc);
     p_desc = tu_desc_next(p_desc);
   }
 
-  if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
+  if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
   {
     // notification endpoint
-    tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc;
+    tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
 
-    TU_ASSERT( usbh_edpt_open(rhport, dev_addr, ep_desc) );
-    p_cdc->ep_notif = ep_desc->bEndpointAddress;
+    TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep), 0 );
+    p_cdc->ep_notif = desc_ep->bEndpointAddress;
 
-    (*p_length) += p_desc[DESC_OFFSET_LEN];
+    drv_len += tu_desc_len(p_desc);
     p_desc = tu_desc_next(p_desc);
   }
 
   //------------- Data Interface (if any) -------------//
-  if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
+  if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
        (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
   {
-    (*p_length) += p_desc[DESC_OFFSET_LEN];
+    // next to endpoint descriptor
+    drv_len += tu_desc_len(p_desc);
     p_desc = tu_desc_next(p_desc);
 
     // data endpoints expected to be in pairs
     for(uint32_t i=0; i<2; i++)
     {
-      tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc;
-      TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
-      TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
+      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, 0);
 
-      TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
+      TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep), 0);
 
-      if ( tu_edpt_dir(ep_desc->bEndpointAddress) ==  TUSB_DIR_IN )
+      if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
       {
-        p_cdc->ep_in = ep_desc->bEndpointAddress;
+        p_cdc->ep_in = desc_ep->bEndpointAddress;
       }else
       {
-        p_cdc->ep_out = ep_desc->bEndpointAddress;
+        p_cdc->ep_out = desc_ep->bEndpointAddress;
       }
 
-      (*p_length) += p_desc[DESC_OFFSET_LEN];
+      drv_len += tu_desc_len(p_desc);
       p_desc = tu_desc_next( p_desc );
     }
   }
 
-  return true;
+  return drv_len;
 }
 
 bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num)

+ 5 - 5
src/class/cdc/cdc_host.h

@@ -121,11 +121,11 @@ void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_i
 //--------------------------------------------------------------------+
 // Internal Class Driver API
 //--------------------------------------------------------------------+
-void cdch_init(void);
-bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
-bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num);
-bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void cdch_close(uint8_t dev_addr);
+void     cdch_init       (void);
+uint16_t cdch_open       (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
+bool     cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
+bool     cdch_xfer_cb    (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void     cdch_close      (uint8_t dev_addr);
 
 #ifdef __cplusplus
  }

+ 34 - 23
src/class/hid/hid_host.c

@@ -98,7 +98,7 @@ uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance)
   return hid_itf->itf_protocol;
 }
 
-bool tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance)
+uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance)
 {
   hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
   return hid_itf->protocol_mode;
@@ -243,33 +243,37 @@ void hidh_close(uint8_t dev_addr)
 // Enumeration
 //--------------------------------------------------------------------+
 
-static bool config_get_protocol             (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool config_set_protocol             (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
 static bool config_get_report_desc          (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
 static bool config_get_report_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
 
-bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length)
+uint16_t hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
 {
-  TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
+  (void) max_len;
 
+  TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
+
+  uint16_t drv_len = sizeof(tusb_desc_interface_t);
   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;
-  TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
+  TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType, 0);
 
   // not enough interface, try to increase CFG_TUH_HID
   // TODO multiple devices
   hidh_device_t* hid_dev = get_dev(dev_addr);
-  TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID);
+  TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID, 0);
 
   //------------- Endpoint Descriptor -------------//
+  drv_len += tu_desc_len(p_desc);
   p_desc = tu_desc_next(p_desc);
   tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
-  TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
+  TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType, 0);
 
   // TODO also open endpoint OUT
-  TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) );
+  TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep), 0 );
 
   hidh_interface_t* hid_itf = get_instance(dev_addr, hid_dev->inst_count);
   hid_dev->inst_count++;
@@ -282,12 +286,13 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
   hid_itf->report_desc_type = desc_hid->bReportType;
   hid_itf->report_desc_len  = tu_unaligned_read16(&desc_hid->wReportLength);
 
-  hid_itf->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
+  // Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
+  hid_itf->protocol_mode = HID_PROTOCOL_BOOT;
   if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass ) hid_itf->itf_protocol = desc_itf->bInterfaceProtocol;
 
-  *p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+  drv_len += desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
 
-  return true;
+  return drv_len;
 }
 
 bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
@@ -314,43 +319,49 @@ bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
     .wLength  = 0
   };
 
-  TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_get_protocol : config_get_report_desc) );
+  TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_set_protocol : config_get_report_desc) );
 
   return true;
 }
 
-static bool config_get_protocol(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+// Force device to work in BOOT protocol
+static bool config_set_protocol(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
 {
-  // Stall is a valid response for SET_IDLE GET_PROTOCOL, therefore we could ignore its result
+  // Stall is a valid response for SET_IDLE, therefore we could ignore its result
   (void) result;
 
   uint8_t const itf_num     = (uint8_t) 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 Get Protocol\r\n");
+  TU_LOG2("HID Set Protocol\r\n");
+  hid_itf->protocol_mode = HID_PROTOCOL_BOOT;
   tusb_control_request_t const new_request =
   {
     .bmRequestType_bit =
     {
       .recipient = TUSB_REQ_RCPT_INTERFACE,
       .type      = TUSB_REQ_TYPE_CLASS,
-      .direction = TUSB_DIR_IN
+      .direction = TUSB_DIR_OUT
     },
-    .bRequest = HID_REQ_CONTROL_GET_PROTOCOL,
-    .wValue   = 0,
+    .bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
+    .wValue   = HID_PROTOCOL_BOOT,
     .wIndex   = hid_itf->itf_num,
-    .wLength  = 1
+    .wLength  = 0
   };
 
-  TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, &hid_itf->protocol_mode, config_get_report_desc) );
-  return false;
+  TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, NULL, config_get_report_desc) );
+  return true;
 }
 
 static bool config_get_report_desc(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
 {
-  // Stall is a valid response for SET_IDLE GET_PROTOCOL, therefore we could ignore its result
-  (void) result;
+  // We can be here after SET_IDLE or SET_PROTOCOL (boot device)
+  // Trigger assert if result is not successful with set protocol
+  if ( request->bRequest != HID_REQ_CONTROL_SET_IDLE )
+  {
+    TU_ASSERT(result == XFER_RESULT_SUCCESS);
+  }
 
   uint8_t const itf_num     = (uint8_t) request->wIndex;
   uint8_t const instance    = get_instance_id_by_itfnum(dev_addr, itf_num);

+ 9 - 8
src/class/hid/hid_host.h

@@ -66,9 +66,10 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance);
 // Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values
 uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance);
 
-// Get current active protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
-// Note: as HID spec, device will be initialized in Report mode
-bool tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance);
+// Get current protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
+// Note: Device will be initialized in Boot protocol for simplicity.
+//       Application can use set_protocol() to switch back to Report protocol.
+uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance);
 
 // Set protocol to HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
 // This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE)
@@ -118,11 +119,11 @@ TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t ins
 //--------------------------------------------------------------------+
 // Internal Class Driver API
 //--------------------------------------------------------------------+
-void hidh_init(void);
-bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length);
-bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
-bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-void hidh_close(uint8_t dev_addr);
+void     hidh_init       (void);
+uint16_t hidh_open       (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
+bool     hidh_set_config (uint8_t dev_addr, uint8_t itf_num);
+bool     hidh_xfer_cb    (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void     hidh_close      (uint8_t dev_addr);
 
 #ifdef __cplusplus
 }

+ 8 - 5
src/class/msc/msc_host.c

@@ -360,18 +360,22 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* c
 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 *p_length)
+uint16_t msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
 {
   TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
              MSC_PROTOCOL_BOT  == desc_itf->bInterfaceProtocol);
 
+  // msc driver length is fixed
+  uint16_t const drv_len = sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+  TU_ASSERT(drv_len <= max_len, 0);
+
   msch_interface_t* p_msc = get_itf(dev_addr);
   tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf);
 
   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(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer, 0);
+    TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc), 0);
 
     if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
     {
@@ -385,9 +389,8 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
   }
 
   p_msc->itf_num = desc_itf->bInterfaceNumber;
-  (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
 
-  return true;
+  return drv_len;
 }
 
 bool msch_set_config(uint8_t dev_addr, uint8_t itf_num)

+ 5 - 15
src/class/msc/msc_host.h

@@ -41,13 +41,6 @@
 #define CFG_TUH_MSC_MAXLUN  4
 #endif
 
-
-/** \addtogroup ClassDriver_MSC
- *  @{
- * \defgroup MSC_Host Host
- *  The interface API includes status checking function, data transferring function and callback functions
- *  @{ */
-
 typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
 
 //--------------------------------------------------------------------+
@@ -113,17 +106,14 @@ TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
 // Internal Class Driver API
 //--------------------------------------------------------------------+
 
-void msch_init(void);
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t *p_length);
-bool msch_set_config(uint8_t dev_addr, uint8_t itf_num);
-bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void msch_close(uint8_t dev_addr);
+void     msch_init       (void);
+uint16_t msch_open       (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
+bool     msch_set_config (uint8_t dev_addr, uint8_t itf_num);
+bool     msch_xfer_cb    (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void     msch_close      (uint8_t dev_addr);
 
 #ifdef __cplusplus
  }
 #endif
 
 #endif /* _TUSB_MSC_HOST_H_ */
-
-/// @}
-/// @}

+ 17 - 14
src/host/hub.c

@@ -144,29 +144,32 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, tuh_con
 //--------------------------------------------------------------------+
 void hub_init(void)
 {
-  tu_memclr(hub_data, CFG_TUSB_HOST_DEVICE_MAX*sizeof( hub_interface_t));
+  tu_memclr(hub_data, CFG_TUSB_HOST_DEVICE_MAX*sizeof(hub_interface_t));
 }
 
-bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
+uint16_t hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
 {
-  // not support multiple TT yet
-  if ( itf_desc->bInterfaceProtocol > 1 ) return false;
+  // hub driver does not support multiple TT yet
+  TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass &&
+            0              == itf_desc->bInterfaceSubClass &&
+            1              <= itf_desc->bInterfaceProtocol, 0);
 
-  //------------- Open Interrupt Status Pipe -------------//
-  tusb_desc_endpoint_t const *ep_desc;
-  ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+  // msc driver length is fixed
+  uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t);
+  TU_ASSERT(drv_len <= max_len, 0);
 
-  TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
-  TU_ASSERT(TUSB_XFER_INTERRUPT == ep_desc->bmAttributes.xfer);
+  //------------- Interrupt Status endpoint -------------//
+  tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+  TU_ASSERT(TUSB_DESC_ENDPOINT  == desc_ep->bDescriptorType &&
+            TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
   
-  TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
+  TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep));
 
   hub_data[dev_addr-1].itf_num = itf_desc->bInterfaceNumber;
-  hub_data[dev_addr-1].ep_in   = ep_desc->bEndpointAddress;
-
-  (*p_length) = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t);
+  hub_data[dev_addr-1].ep_in   = desc_ep->bEndpointAddress;
 
-  return true;
+  return drv_len;
 }
 
 void hub_close(uint8_t dev_addr)

+ 5 - 5
src/host/hub.h

@@ -181,11 +181,11 @@ bool hub_status_pipe_queue(uint8_t dev_addr);
 //--------------------------------------------------------------------+
 // Internal Class Driver API
 //--------------------------------------------------------------------+
-void hub_init(void);
-bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
-bool hub_set_config(uint8_t dev_addr, uint8_t itf_num);
-bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void hub_close(uint8_t dev_addr);
+void     hub_init       (void);
+uint16_t hub_open       (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
+bool     hub_set_config (uint8_t dev_addr, uint8_t itf_num);
+bool     hub_xfer_cb    (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void     hub_close      (uint8_t dev_addr);
 
 #ifdef __cplusplus
  }

+ 8 - 7
src/host/usbh.c

@@ -984,11 +984,12 @@ static bool enum_set_config_complete(uint8_t dev_addr, tusb_control_request_t co
 static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg)
 {
   usbh_device_t* dev = &_usbh_devices[dev_addr];
-  uint8_t const* p_desc = (uint8_t const*) desc_cfg;
-  p_desc = tu_desc_next(p_desc);
+
+  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 < _usbh_ctrl_buf + desc_cfg->wTotalLength )
+  while( p_desc < desc_end )
   {
     // TODO Do we need to use IAD
     // tusb_desc_interface_assoc_t const * desc_itf_assoc = NULL;
@@ -1003,8 +1004,9 @@ static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configura
     TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) );
 
     tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc;
+    uint16_t const remaining_len = desc_end-p_desc;
 
-    // Check if class is supported
+    // Check if class is supported TODO drop class_code
     uint8_t drv_id;
     for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
     {
@@ -1034,9 +1036,8 @@ static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configura
       {
         TU_LOG2("%s open\r\n", driver->name);
 
-        uint16_t itf_len = 0;
-        TU_ASSERT( driver->open(dev->rhport, dev_addr, desc_itf, &itf_len) );
-        TU_ASSERT( itf_len >= sizeof(tusb_desc_interface_t) );
+        uint16_t const itf_len = driver->open(dev->rhport, dev_addr, desc_itf, remaining_len);
+        TU_ASSERT( sizeof(tusb_desc_interface_t) <= itf_len && itf_len <= remaining_len);
         p_desc += itf_len;
       }
     }

+ 5 - 5
src/host/usbh_classdriver.h

@@ -45,11 +45,11 @@ typedef struct {
 
   uint8_t class_code;
 
-  void (* const init       )(void);
-  bool (* const open       )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t* outlen);
-  bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num);
-  bool (* const xfer_cb    )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-  void (* const close      )(uint8_t dev_addr);
+  void     (* const init       )(void);
+  uint16_t (* const open       )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+  bool     (* const set_config )(uint8_t dev_addr, uint8_t itf_num);
+  bool     (* const xfer_cb    )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+  void     (* const close      )(uint8_t dev_addr);
 } usbh_class_driver_t;
 
 // Call by class driver to tell USBH that it has complete the enumeration