fix enumeration issue when plugging hub with multiple devices attached

This commit is contained in:
hathach 2022-03-10 23:16:59 +07:00
parent 171d021ab5
commit 66c933fb61
4 changed files with 70 additions and 60 deletions

View File

@ -189,7 +189,7 @@ void hub_close(uint8_t dev_addr)
if (p_hub->ep_in) tu_memclr(p_hub, sizeof( hub_interface_t));
}
bool hub_status_pipe_queue(uint8_t dev_addr)
bool hub_edpt_status_xfer(uint8_t dev_addr)
{
hub_interface_t* hub_itf = get_itf(dev_addr);
return usbh_edpt_xfer(dev_addr, hub_itf->ep_in, &hub_itf->status_change, 1);
@ -324,7 +324,7 @@ static bool connection_get_status_complete (uint8_t dev_addr, tusb_control_reque
// prepare for next hub status
// TODO continue with status_change, or maybe we can do it again with status
hub_status_pipe_queue(dev_addr);
hub_edpt_status_xfer(dev_addr);
}
return true;

View File

@ -176,7 +176,7 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature, t
bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, tuh_control_complete_cb_t complete_cb);
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, tuh_control_complete_cb_t complete_cb);
bool hub_status_pipe_queue(uint8_t dev_addr);
bool hub_edpt_status_xfer(uint8_t dev_addr);
//--------------------------------------------------------------------+
// Internal Class Driver API

View File

@ -543,7 +543,7 @@ void tuh_task(void)
if ( event.connection.hub_addr != 0)
{
// done with hub, waiting for next data on status pipe
(void) hub_status_pipe_queue( event.connection.hub_addr );
(void) hub_edpt_status_xfer( event.connection.hub_addr );
}
#endif
break;
@ -904,6 +904,10 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
return true;
}
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
// a device unplugged from rhport:hub_addr:hub_port
static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port)
{
@ -938,49 +942,6 @@ static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t h
}
}
//--------------------------------------------------------------------+
// INTERNAL HELPER
//--------------------------------------------------------------------+
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 const addr = start + i;
if (!get_device(addr)->connected) return addr;
}
return ADDR_INVALID;
}
void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
{
usbh_device_t* dev = get_device(dev_addr);
for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++)
{
// continue with next valid interface
// TODO skip IAD binding interface such as CDCs
uint8_t const drv_id = dev->itf2drv[itf_num];
if (drv_id != DRVID_INVALID)
{
usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num);
driver->set_config(dev_addr, itf_num);
break;
}
}
// all interface are configured
if (itf_num == CFG_TUH_INTERFACE_MAX)
{
// Invoke callback if available
if (tuh_mount_cb) tuh_mount_cb(dev_addr);
}
}
//--------------------------------------------------------------------+
// Enumeration Process
// is a lengthy process with a series of control transfer to configure
@ -991,9 +952,9 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
//--------------------------------------------------------------------+
static bool enum_request_addr0_device_desc(void);
static bool enum_request_set_addr(void);
static bool enum_get_addr0_device_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
static bool enum_request_set_addr(void);
static bool enum_set_address_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
static bool enum_get_device_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
static bool enum_get_9byte_config_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
@ -1001,6 +962,9 @@ static bool enum_get_config_desc_complete (uint8_t dev_addr, tusb_control_
static bool enum_set_config_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
static bool parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
static uint8_t get_new_address(bool is_hub);
static void enum_full_complete(void);
#if CFG_TUH_HUB
// Enum sequence:
@ -1015,7 +979,7 @@ static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_reques
static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
{
(void) dev_addr; (void) request;
(void) request;
TU_ASSERT(XFER_RESULT_SUCCESS == result);
hub_port_status_response_t port_status;
@ -1024,7 +988,8 @@ static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request
if ( !port_status.status.connection )
{
// device unplugged while delaying, nothing else to do, queue hub status
return hub_status_pipe_queue(dev_addr);
enum_full_complete();
return false;
}
_dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
@ -1083,9 +1048,6 @@ static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_reques
enum_request_set_addr();
// done with hub, waiting for next data on status pipe
(void) hub_status_pipe_queue( _dev0.hub_addr );
return true;
}
@ -1144,11 +1106,8 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r
if (XFER_RESULT_SUCCESS != result)
{
#if CFG_TUH_HUB
// TODO remove, waiting for next data on status pipe
if (_dev0.hub_addr != 0) hub_status_pipe_queue(_dev0.hub_addr);
#endif
// stop enumeration, maybe we could retry this
enum_full_complete();
return false;
}
@ -1407,4 +1366,54 @@ static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configura
return true;
}
void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
{
usbh_device_t* dev = get_device(dev_addr);
for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++)
{
// continue with next valid interface
// TODO skip IAD binding interface such as CDCs
uint8_t const drv_id = dev->itf2drv[itf_num];
if (drv_id != DRVID_INVALID)
{
usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num);
driver->set_config(dev_addr, itf_num);
break;
}
}
// all interface are configured
if (itf_num == CFG_TUH_INTERFACE_MAX)
{
enum_full_complete();
// Invoke callback if available
if (tuh_mount_cb) tuh_mount_cb(dev_addr);
}
}
static void enum_full_complete(void)
{
#if CFG_TUH_HUB
// get next hub status
if (_dev0.hub_addr) hub_edpt_status_xfer(_dev0.hub_addr);
#endif
}
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 const addr = start + i;
if (!get_device(addr)->connected) return addr;
}
return ADDR_INVALID;
}
#endif

View File

@ -80,7 +80,8 @@ static inline bool tuh_ready(uint8_t daddr)
return tuh_mounted(daddr) && !tuh_suspended(daddr);
}
// Carry out control transfer
// Carry out a control transfer
// true on success, false if there is on-going control trasnfer
bool tuh_control_xfer (uint8_t daddr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb);
// Set Configuration