Merge pull request #3096 from hathach/usbh-improve-hub
Usbh improve hub
This commit is contained in:
4
.idea/cmake.xml
generated
4
.idea/cmake.xml
generated
@@ -6,9 +6,9 @@
|
||||
<configuration PROFILE_NAME="raspberrypi_zero2" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberrypi_zero2 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberrypi_cm4" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberrypi_cm4 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico2" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico2 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico2_riscv" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico2_riscv -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico-pio_host" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico -DLOG=1 -DCFLAGS_CLI="-DCFG_TUH_RPI_PIO_USB=1"" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico2" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico2 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico2-pio_host" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico2 -DLOG=1 -DCFLAGS_CLI="-DCFG_TUH_RPI_PIO_USB=1"" />
|
||||
<configuration PROFILE_NAME="feather_rp2040" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=pico_sdk -DPICO_BOARD=adafruit_feather_rp2040 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="feather_rp2040_max3421" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=feather_rp2040_max3421 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="metro_rp2040" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=pico_sdk -DPICO_BOARD=adafruit_metro_rp2040 -DLOG=1 -DMAX3421_HOST=1" />
|
||||
|
230
src/host/hub.c
230
src/host/hub.c
@@ -57,9 +57,11 @@ typedef struct {
|
||||
TUH_EPBUF_DEF(ctrl_buf, CFG_TUH_HUB_BUFSIZE);
|
||||
} hub_epbuf_t;
|
||||
|
||||
static tuh_xfer_cb_t user_complete_cb = NULL;
|
||||
static hub_interface_t hub_itfs[CFG_TUH_HUB];
|
||||
CFG_TUH_MEM_SECTION static hub_epbuf_t hub_epbufs[CFG_TUH_HUB];
|
||||
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline hub_interface_t* get_hub_itf(uint8_t daddr) {
|
||||
return &hub_itfs[daddr-1-CFG_TUH_DEVICE_MAX];
|
||||
}
|
||||
@@ -142,10 +144,23 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
};
|
||||
|
||||
TU_LOG_DRV("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void port_get_status_complete (tuh_xfer_t* xfer) {
|
||||
if (xfer->result == XFER_RESULT_SUCCESS) {
|
||||
hub_interface_t* p_hub = get_hub_itf(xfer->daddr);
|
||||
p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
|
||||
}
|
||||
|
||||
xfer->complete_cb = user_complete_cb;
|
||||
user_complete_cb = NULL;
|
||||
if (xfer->complete_cb) {
|
||||
xfer->complete_cb(xfer);
|
||||
}
|
||||
}
|
||||
|
||||
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
tusb_control_request_t const request = {
|
||||
@@ -169,8 +184,26 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
if (hub_port != 0) {
|
||||
// intercept complete callback to save port status, ignore resp
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(hub_addr);
|
||||
xfer.complete_cb = port_get_status_complete;
|
||||
xfer.buffer = p_epbuf->ctrl_buf;
|
||||
user_complete_cb = complete_cb;
|
||||
} else {
|
||||
user_complete_cb = NULL;
|
||||
}
|
||||
|
||||
TU_LOG_DRV("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
|
||||
TU_VERIFY( tuh_control_xfer(&xfer) );
|
||||
TU_VERIFY(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp) {
|
||||
(void) hub_port;
|
||||
TU_VERIFY(hub_addr > CFG_TUH_DEVICE_MAX);
|
||||
hub_interface_t* p_hub = get_hub_itf(hub_addr);
|
||||
*resp = p_hub->port_status;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -238,10 +271,10 @@ bool hub_edpt_status_xfer(uint8_t daddr) {
|
||||
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) {
|
||||
hub_interface_t* p_hub = get_hub_itf(dev_addr);
|
||||
bool hub_set_config(uint8_t daddr, uint8_t itf_num) {
|
||||
hub_interface_t* p_hub = get_hub_itf(daddr);
|
||||
TU_ASSERT(itf_num == p_hub->itf_num);
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(dev_addr);
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(daddr);
|
||||
|
||||
// Get Hub Descriptor
|
||||
tusb_control_request_t const request = {
|
||||
@@ -257,7 +290,7 @@ bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) {
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = dev_addr,
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = p_epbuf->ctrl_buf,
|
||||
@@ -312,11 +345,15 @@ static void config_port_power_complete (tuh_xfer_t* xfer) {
|
||||
//--------------------------------------------------------------------+
|
||||
// Connection Changes
|
||||
//--------------------------------------------------------------------+
|
||||
static void get_status_complete (tuh_xfer_t* xfer);
|
||||
static void port_get_status_complete (tuh_xfer_t* xfer);
|
||||
static void port_clear_feature_complete_stub(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);
|
||||
enum {
|
||||
STATE_IDLE = 0,
|
||||
STATE_HUB_STATUS,
|
||||
STATE_CLEAR_CHANGE,
|
||||
STATE_CHECK_CONN,
|
||||
STATE_COMPLETE
|
||||
};
|
||||
|
||||
static void process_new_status(tuh_xfer_t* xfer);
|
||||
|
||||
// callback as response of interrupt endpoint polling
|
||||
bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
@@ -337,12 +374,12 @@ bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
|
||||
processed = false;
|
||||
} else if (tu_bit_test(status_change, 0)) {
|
||||
// Hub bit 0 is for the hub device events
|
||||
processed = hub_get_status(daddr, p_epbuf->ctrl_buf, get_status_complete, 0);
|
||||
processed = hub_get_status(daddr, p_epbuf->ctrl_buf, process_new_status, STATE_HUB_STATUS);
|
||||
} else {
|
||||
// Hub bits 1 to n are hub port events
|
||||
for (uint8_t port=1; port <= p_hub->bNbrPorts; port++) {
|
||||
if (tu_bit_test(status_change, port)) {
|
||||
processed = hub_port_get_status(daddr, port, p_epbuf->ctrl_buf, port_get_status_complete, 0);
|
||||
processed = hub_port_get_status(daddr, port, NULL, process_new_status, STATE_CLEAR_CHANGE);
|
||||
break; // after completely processed one port, we will re-queue the status poll and handle next one
|
||||
}
|
||||
}
|
||||
@@ -358,117 +395,84 @@ bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
|
||||
return true;
|
||||
}
|
||||
|
||||
static void port_clear_feature_complete_stub(tuh_xfer_t* xfer) {
|
||||
hub_edpt_status_xfer(xfer->daddr);
|
||||
}
|
||||
|
||||
static void get_status_complete(tuh_xfer_t *xfer) {
|
||||
const uint8_t daddr = xfer->daddr;
|
||||
|
||||
bool processed = false; // true if new status is processed
|
||||
if (xfer->result == XFER_RESULT_SUCCESS) {
|
||||
hub_status_response_t hub_status = *((const hub_status_response_t *) (uintptr_t) xfer->buffer);
|
||||
|
||||
TU_LOG_DRV("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, hub_status.change.value);
|
||||
|
||||
if (hub_status.change.local_power_source) {
|
||||
TU_LOG_DRV(" Local Power Change\r\n");
|
||||
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE, port_clear_feature_complete_stub, 0);
|
||||
} else if (hub_status.change.over_current) {
|
||||
TU_LOG_DRV(" Over Current\r\n");
|
||||
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE, port_clear_feature_complete_stub, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!processed) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr), );
|
||||
}
|
||||
}
|
||||
|
||||
static void port_get_status_complete(tuh_xfer_t *xfer) {
|
||||
const uint8_t daddr = xfer->daddr;
|
||||
bool processed = false; // true if new status is processed
|
||||
|
||||
if (xfer->result == XFER_RESULT_SUCCESS) {
|
||||
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
hub_interface_t *p_hub = get_hub_itf(daddr);
|
||||
p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
|
||||
|
||||
// Clear port status change interrupts
|
||||
if (p_hub->port_status.change.connection) {
|
||||
// Connection change
|
||||
// Port is powered and enabled
|
||||
//TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
|
||||
|
||||
// Acknowledge Port Connection Change
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0);
|
||||
} else if (p_hub->port_status.change.port_enable) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE, port_clear_feature_complete_stub, 0);
|
||||
} else if (p_hub->port_status.change.suspend) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE, port_clear_feature_complete_stub, 0);
|
||||
} else if (p_hub->port_status.change.over_current) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE, port_clear_feature_complete_stub, 0);
|
||||
} else if (p_hub->port_status.change.reset) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE, port_clear_feature_complete_stub, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!processed) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr), );
|
||||
}
|
||||
}
|
||||
|
||||
static void connection_clear_conn_change_complete (tuh_xfer_t* xfer) {
|
||||
static void process_new_status(tuh_xfer_t* xfer) {
|
||||
const uint8_t daddr = xfer->daddr;
|
||||
|
||||
if (xfer->result != XFER_RESULT_SUCCESS) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr), );
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr),);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
hub_interface_t *p_hub = get_hub_itf(daddr);
|
||||
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
const uintptr_t state = xfer->user_data;
|
||||
bool processed = false; // true if new status is processed
|
||||
|
||||
if (p_hub->port_status.status.connection) {
|
||||
// Reset port if attach event
|
||||
hub_port_reset(daddr, port_num, connection_port_reset_complete, 0);
|
||||
} else {
|
||||
// submit detach event
|
||||
const hcd_event_t event = {
|
||||
.rhport = usbh_get_rhport(daddr),
|
||||
.event_id = HCD_EVENT_DEVICE_REMOVE,
|
||||
.connection = {
|
||||
.hub_addr = daddr,
|
||||
.hub_port = port_num
|
||||
}
|
||||
};
|
||||
hcd_event_handler(&event, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void connection_port_reset_complete (tuh_xfer_t* xfer) {
|
||||
const uint8_t daddr = xfer->daddr;
|
||||
|
||||
if (xfer->result != XFER_RESULT_SUCCESS) {
|
||||
// retry port reset if failed
|
||||
if (!tuh_control_xfer(xfer)) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr), ); // back to status poll if failed to queue request
|
||||
switch (state) {
|
||||
case STATE_HUB_STATUS: {
|
||||
hub_status_response_t hub_status = *((const hub_status_response_t *) (uintptr_t) xfer->buffer);
|
||||
TU_LOG_DRV("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, hub_status.change.value);
|
||||
if (hub_status.change.local_power_source) {
|
||||
TU_LOG_DRV(" Local Power Change\r\n");
|
||||
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
} else if (hub_status.change.over_current) {
|
||||
TU_LOG_DRV(" Over Current\r\n");
|
||||
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
case STATE_CLEAR_CHANGE:
|
||||
// Get port status complete --> clear change
|
||||
if (p_hub->port_status.change.connection) {
|
||||
// Connection change
|
||||
// Port is powered and enabled
|
||||
//TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
|
||||
|
||||
// Acknowledge Port Connection Change
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE,
|
||||
process_new_status, STATE_CHECK_CONN);
|
||||
} else if (p_hub->port_status.change.port_enable) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
} else if (p_hub->port_status.change.suspend) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
} else if (p_hub->port_status.change.over_current) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
} else if (p_hub->port_status.change.reset) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_CHECK_CONN: {
|
||||
const hcd_event_t event = {
|
||||
.rhport = usbh_get_rhport(daddr),
|
||||
.event_id = p_hub->port_status.status.connection ? HCD_EVENT_DEVICE_ATTACH : HCD_EVENT_DEVICE_REMOVE,
|
||||
.connection = {
|
||||
.hub_addr = daddr,
|
||||
.hub_port = port_num
|
||||
}
|
||||
};
|
||||
hcd_event_handler(&event, false);
|
||||
processed = true; // usbh queue status after handled this in (de)enumeration
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_COMPLETE:
|
||||
default:
|
||||
processed = false; // complete this status, queue next status
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
|
||||
// submit attach event
|
||||
hcd_event_t event = {
|
||||
.rhport = usbh_get_rhport(daddr),
|
||||
.event_id = HCD_EVENT_DEVICE_ATTACH,
|
||||
.connection = {
|
||||
.hub_addr = daddr,
|
||||
.hub_port = port_num
|
||||
}
|
||||
};
|
||||
hcd_event_handler(&event, false);
|
||||
if (!processed) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr),);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -170,9 +170,13 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get port status
|
||||
// If hub_port != 0, resp is ignored. hub_port_get_status_local() can be used to retrieve the status
|
||||
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void *resp,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get port status from local cache. This does not send a request to the device
|
||||
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp);
|
||||
|
||||
// Get status from Interrupt endpoint
|
||||
bool hub_edpt_status_xfer(uint8_t daddr);
|
||||
|
||||
@@ -188,7 +192,7 @@ bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb
|
||||
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Get Hub status
|
||||
// Get Hub status (port = 0)
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool hub_get_status(uint8_t hub_addr, void* resp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return hub_port_get_status(hub_addr, 0, resp, complete_cb, user_data);
|
||||
@@ -205,7 +209,7 @@ bool hub_clear_feature(uint8_t hub_addr, uint8_t feature, tuh_xfer_cb_t complete
|
||||
bool hub_init (void);
|
||||
bool hub_deinit (void);
|
||||
bool 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_set_config (uint8_t daddr, uint8_t itf_num);
|
||||
bool hub_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
void hub_close (uint8_t dev_addr);
|
||||
|
||||
|
379
src/host/usbh.c
379
src/host/usbh.c
@@ -105,13 +105,11 @@ typedef struct {
|
||||
|
||||
// Device Descriptor
|
||||
uint8_t ep0_size;
|
||||
|
||||
uint16_t vid;
|
||||
uint16_t pid;
|
||||
|
||||
uint8_t i_manufacturer;
|
||||
uint8_t i_product;
|
||||
uint8_t i_serial;
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
uint8_t iManufacturer;
|
||||
uint8_t iProduct;
|
||||
uint8_t iSerialNumber;
|
||||
uint8_t bNumConfigurations;
|
||||
|
||||
// Configuration Descriptor
|
||||
@@ -331,10 +329,10 @@ bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t *vid, uint16_t *pid) {
|
||||
*vid = *pid = 0;
|
||||
|
||||
usbh_device_t const *dev = get_device(dev_addr);
|
||||
TU_VERIFY(dev && dev->addressed && dev->vid != 0);
|
||||
TU_VERIFY(dev && dev->addressed && dev->idVendor != 0);
|
||||
|
||||
*vid = dev->vid;
|
||||
*pid = dev->pid;
|
||||
*vid = dev->idVendor;
|
||||
*pid = dev->idProduct;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -362,7 +360,6 @@ bool tuh_rhport_reset_bus(uint8_t rhport, bool active) {
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC API (Parameter Verification is required)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param) {
|
||||
return hcd_configure(rhport, cfg_id, cfg_param);
|
||||
}
|
||||
@@ -536,7 +533,6 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
|
||||
}
|
||||
} else {
|
||||
TU_LOG1("[%u:] USBH Device Attach\r\n", event.rhport);
|
||||
_usbh_data.enumerating_daddr = 0;
|
||||
enum_new_device(&event);
|
||||
}
|
||||
break;
|
||||
@@ -1024,9 +1020,11 @@ bool tuh_bus_info_get(uint8_t daddr, tuh_bus_info_t* bus_info) {
|
||||
|
||||
TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) {
|
||||
switch (event->event_id) {
|
||||
case HCD_EVENT_DEVICE_ATTACH:
|
||||
|
||||
break;
|
||||
|
||||
case HCD_EVENT_DEVICE_REMOVE:
|
||||
// FIXME device remove from a hub need an HCD API for hcd to free up endpoint
|
||||
// mark device as removing to prevent further xfer before the event is processed in usbh task
|
||||
break;
|
||||
|
||||
default: break;
|
||||
@@ -1094,24 +1092,24 @@ bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id,
|
||||
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);
|
||||
TU_VERIFY(dev && dev->iManufacturer);
|
||||
return tuh_descriptor_get_string(daddr, dev->iManufacturer, 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);
|
||||
TU_VERIFY(dev && dev->iProduct);
|
||||
return tuh_descriptor_get_string(daddr, dev->iProduct, 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);
|
||||
TU_VERIFY(dev && dev->iSerialNumber);
|
||||
return tuh_descriptor_get_string(daddr, dev->iSerialNumber, language_id, buffer, len, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Get HID report descriptor
|
||||
@@ -1142,6 +1140,33 @@ bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
bool tuh_address_set(uint8_t daddr, uint8_t new_addr,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_USBH("Set Address = %d\r\n", new_addr);
|
||||
const tusb_control_request_t request = {
|
||||
.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
|
||||
};
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_USBH("Set Configuration = %d\r\n", config_num);
|
||||
@@ -1203,8 +1228,7 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
|
||||
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 tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len) {
|
||||
_CONTROL_SYNC_API(tuh_descriptor_get, daddr, type, index, buffer, len);
|
||||
}
|
||||
|
||||
@@ -1212,33 +1236,27 @@ uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len
|
||||
_CONTROL_SYNC_API(tuh_descriptor_get_device, daddr, buffer, len);
|
||||
}
|
||||
|
||||
uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index,
|
||||
void* buffer, uint16_t len) {
|
||||
uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len) {
|
||||
_CONTROL_SYNC_API(tuh_descriptor_get_configuration, daddr, index, buffer, len);
|
||||
}
|
||||
|
||||
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 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) {
|
||||
_CONTROL_SYNC_API(tuh_descriptor_get_hid_report, 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 tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len) {
|
||||
_CONTROL_SYNC_API(tuh_descriptor_get_string, 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 tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
|
||||
_CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, 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 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);
|
||||
}
|
||||
|
||||
uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id,
|
||||
void* buffer, uint16_t len) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1336,27 +1354,25 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Enumeration Process
|
||||
// is a lengthy process with a series of control transfer to configure
|
||||
// newly attached device.
|
||||
// is a lengthy process with a series of control transfer to configure newly attached device.
|
||||
// NOTE: due to the shared control buffer, we must complete enumerating
|
||||
// one device before enumerating another one.
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
ENUM_DEBOUNCING_DELAY_MS = 200, // when plug/unplug a device, physical connection can be bouncing and may
|
||||
// generate a series of attach/detach event. This delay wait for stable connection
|
||||
ENUM_RESET_DELAY_MS = 50, // USB specs: 10 to 50ms
|
||||
enum { // USB 2.0 specs 7.1.7 for timing
|
||||
ENUM_DEBOUNCING_DELAY_MS = 150, // T(ATTDB) minimum 100 ms for stable connection
|
||||
ENUM_RESET_ROOT_DELAY_MS = 50, // T(DRSTr) minimum 50 ms for reset from root port
|
||||
ENUM_RESET_HUB_DELAY_MS = 20, // T(DRST) 10-20 ms for hub reset
|
||||
ENUM_RESET_RECOVERY_DELAY_MS = 10, // T(RSTRCY) minimum 10 ms for reset recovery
|
||||
};
|
||||
|
||||
enum {
|
||||
ENUM_IDLE,
|
||||
ENUM_RESET_1, // 1st reset when attached
|
||||
//ENUM_HUB_GET_STATUS_1,
|
||||
ENUM_HUB_CLEAR_RESET_1,
|
||||
ENUM_HUB_RERSET,
|
||||
ENUM_HUB_GET_STATUS_AFTER_RESET,
|
||||
ENUM_HUB_CLEAR_RESET,
|
||||
ENUM_HUB_CLEAR_RESET_COMPLETE,
|
||||
|
||||
ENUM_ADDR0_DEVICE_DESC,
|
||||
ENUM_RESET_2, // 2nd reset before set address (not used)
|
||||
ENUM_HUB_GET_STATUS_2,
|
||||
ENUM_HUB_CLEAR_RESET_2,
|
||||
ENUM_SET_ADDR,
|
||||
ENUM_GET_DEVICE_DESC,
|
||||
ENUM_GET_STRING_LANGUAGE_ID_LEN,
|
||||
@@ -1373,9 +1389,66 @@ enum {
|
||||
ENUM_CONFIG_DRIVER
|
||||
};
|
||||
|
||||
static bool enum_request_set_addr(tusb_desc_device_t const* desc_device);
|
||||
static uint8_t enum_get_new_address(bool is_hub);
|
||||
static bool enum_parse_configuration_desc (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
|
||||
static void enum_full_complete(void);
|
||||
static void process_enumeration(tuh_xfer_t* xfer);
|
||||
|
||||
// start a new enumeration process
|
||||
static bool enum_new_device(hcd_event_t* event) {
|
||||
tuh_bus_info_t* dev0_bus = &_usbh_data.dev0_bus;
|
||||
dev0_bus->rhport = event->rhport;
|
||||
dev0_bus->hub_addr = event->connection.hub_addr;
|
||||
dev0_bus->hub_port = event->connection.hub_port;
|
||||
|
||||
_usbh_data.enumerating_daddr = 0;
|
||||
|
||||
// wait until device connection is stable TODO non blocking
|
||||
tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
|
||||
|
||||
if (dev0_bus->hub_addr == 0) {
|
||||
// connected directly to roothub
|
||||
// USB bus not active and frame number is not available yet.
|
||||
// need to depend on tusb_time_millis_api() TODO non blocking
|
||||
|
||||
if (!hcd_port_connect_status(dev0_bus->rhport)) {
|
||||
TU_LOG_USBH("Device unplugged while debouncing\r\n");
|
||||
enum_full_complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// reset device
|
||||
hcd_port_reset(dev0_bus->rhport);
|
||||
tusb_time_delay_ms_api(ENUM_RESET_ROOT_DELAY_MS);
|
||||
hcd_port_reset_end(dev0_bus->rhport);
|
||||
|
||||
if (!hcd_port_connect_status(dev0_bus->rhport)) {
|
||||
// device unplugged while delaying
|
||||
enum_full_complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
dev0_bus->speed = hcd_port_speed_get(dev0_bus->rhport);
|
||||
TU_LOG_USBH("%s Speed\r\n", tu_str_speed[dev0_bus->speed]);
|
||||
|
||||
// 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
|
||||
else {
|
||||
// connected via hub
|
||||
TU_VERIFY(dev0_bus->hub_port != 0);
|
||||
TU_ASSERT(hub_port_get_status(dev0_bus->hub_addr, dev0_bus->hub_port, NULL,
|
||||
process_enumeration, ENUM_HUB_RERSET));
|
||||
}
|
||||
#endif // hub
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// process device enumeration
|
||||
static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
@@ -1406,6 +1479,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
uintptr_t const state = xfer->user_data;
|
||||
usbh_device_t* dev = get_device(daddr);
|
||||
tuh_bus_info_t* dev0_bus = &_usbh_data.dev0_bus;
|
||||
if (daddr > 0) {
|
||||
TU_ASSERT(dev,);
|
||||
}
|
||||
@@ -1413,62 +1487,87 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
|
||||
switch (state) {
|
||||
#if CFG_TUH_HUB
|
||||
//case ENUM_HUB_GET_STATUS_1: break;
|
||||
case ENUM_HUB_CLEAR_RESET_1: {
|
||||
case ENUM_HUB_RERSET: {
|
||||
hub_port_status_response_t port_status;
|
||||
memcpy(&port_status, _usbh_epbuf.ctrl, sizeof(hub_port_status_response_t));
|
||||
hub_port_get_status_local(dev0_bus->hub_addr, dev0_bus->hub_port, &port_status);
|
||||
|
||||
if (!port_status.status.connection) {
|
||||
// device unplugged while delaying, nothing else to do
|
||||
TU_LOG_USBH("Device unplugged from hub while debouncing\r\n");
|
||||
enum_full_complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_usbh_data.dev0_bus.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
|
||||
(port_status.status.low_speed) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
|
||||
|
||||
// Acknowledge Port Reset Change
|
||||
if (port_status.change.reset) {
|
||||
hub_port_clear_reset_change(_usbh_data.dev0_bus.hub_addr, _usbh_data.dev0_bus.hub_port,
|
||||
process_enumeration, ENUM_ADDR0_DEVICE_DESC);
|
||||
}
|
||||
TU_ASSERT(hub_port_reset(dev0_bus->hub_addr, dev0_bus->hub_port, process_enumeration, ENUM_HUB_GET_STATUS_AFTER_RESET),);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENUM_HUB_GET_STATUS_2:
|
||||
tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
|
||||
TU_ASSERT(hub_port_get_status(_usbh_data.dev0_bus.hub_addr, _usbh_data.dev0_bus.hub_port, _usbh_epbuf.ctrl,
|
||||
process_enumeration, ENUM_HUB_CLEAR_RESET_2),);
|
||||
break;
|
||||
case ENUM_HUB_GET_STATUS_AFTER_RESET: {
|
||||
tusb_time_delay_ms_api(ENUM_RESET_HUB_DELAY_MS); // wait for reset to take effect
|
||||
|
||||
case ENUM_HUB_CLEAR_RESET_2: {
|
||||
// get status to check for reset change
|
||||
TU_ASSERT(hub_port_get_status(dev0_bus->hub_addr, dev0_bus->hub_port, NULL, process_enumeration, ENUM_HUB_CLEAR_RESET),);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENUM_HUB_CLEAR_RESET: {
|
||||
hub_port_status_response_t port_status;
|
||||
memcpy(&port_status, _usbh_epbuf.ctrl, sizeof(hub_port_status_response_t));
|
||||
hub_port_get_status_local(dev0_bus->hub_addr, dev0_bus->hub_port, &port_status);
|
||||
|
||||
// Acknowledge Port Reset Change if Reset Successful
|
||||
if (port_status.change.reset) {
|
||||
TU_ASSERT(hub_port_clear_reset_change(_usbh_data.dev0_bus.hub_addr, _usbh_data.dev0_bus.hub_port,
|
||||
process_enumeration, ENUM_SET_ADDR),);
|
||||
// Acknowledge Port Reset Change
|
||||
TU_ASSERT(hub_port_clear_reset_change(dev0_bus->hub_addr, dev0_bus->hub_port, process_enumeration, ENUM_HUB_CLEAR_RESET_COMPLETE),);
|
||||
} else {
|
||||
// maybe retry if reset change not set but we need timeout to prevent infinite loop
|
||||
// TU_ASSERT(hub_port_get_status(dev0_bus->hub_addr, dev0_bus->hub_port, NULL, process_enumeration, ENUM_HUB_CLEAR_RESET_COMPLETE),);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ENUM_HUB_CLEAR_RESET_COMPLETE: {
|
||||
hub_port_status_response_t port_status;
|
||||
hub_port_get_status_local(dev0_bus->hub_addr, dev0_bus->hub_port, &port_status);
|
||||
|
||||
if (!port_status.status.connection) {
|
||||
TU_LOG_USBH("Device unplugged from hub (not addressed yet)\r\n");
|
||||
enum_full_complete();
|
||||
return;
|
||||
}
|
||||
|
||||
dev0_bus->speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
|
||||
(port_status.status.low_speed) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
|
||||
|
||||
TU_ATTR_FALLTHROUGH;
|
||||
}
|
||||
#endif
|
||||
|
||||
case ENUM_ADDR0_DEVICE_DESC: {
|
||||
tusb_time_delay_ms_api(ENUM_RESET_RECOVERY_DELAY_MS); // reset recovery
|
||||
|
||||
// TODO probably doesn't need to open/close each enumeration
|
||||
uint8_t const addr0 = 0;
|
||||
TU_ASSERT(usbh_edpt_control_open(addr0, 8),);
|
||||
|
||||
// Get first 8 bytes of device descriptor for Control Endpoint size
|
||||
// Get first 8 bytes of device descriptor for control endpoint size
|
||||
TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n");
|
||||
TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_epbuf.ctrl, 8,
|
||||
process_enumeration, ENUM_SET_ADDR),);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENUM_SET_ADDR:
|
||||
enum_request_set_addr((tusb_desc_device_t*) _usbh_epbuf.ctrl);
|
||||
case ENUM_SET_ADDR: {
|
||||
const tusb_desc_device_t *desc_device = (const tusb_desc_device_t *) _usbh_epbuf.ctrl;
|
||||
const uint8_t new_addr = enum_get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
|
||||
TU_ASSERT(new_addr != 0,);
|
||||
|
||||
usbh_device_t* new_dev = get_device(new_addr);
|
||||
new_dev->bus_info = *dev0_bus;
|
||||
new_dev->connected = 1;
|
||||
new_dev->ep0_size = desc_device->bMaxPacketSize0;
|
||||
|
||||
TU_ASSERT(tuh_address_set(0, new_addr, process_enumeration, ENUM_GET_DEVICE_DESC),);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENUM_GET_DEVICE_DESC: {
|
||||
// Allow 2ms for address recovery time, Ref USB Spec 9.2.6.3
|
||||
@@ -1480,7 +1579,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
new_dev->addressed = 1;
|
||||
_usbh_data.enumerating_daddr = new_addr;
|
||||
|
||||
hcd_device_close(_usbh_data.dev0_bus.rhport, 0); // close dev0_bus
|
||||
hcd_device_close(dev0_bus->rhport, 0); // close dev0
|
||||
|
||||
TU_ASSERT(usbh_edpt_control_open(new_addr, new_dev->ep0_size),); // open new control endpoint
|
||||
|
||||
@@ -1495,11 +1594,11 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
case ENUM_GET_STRING_LANGUAGE_ID_LEN: {
|
||||
// save the received device descriptor
|
||||
tusb_desc_device_t const *desc_device = (tusb_desc_device_t const *) _usbh_epbuf.ctrl;
|
||||
dev->vid = desc_device->idVendor;
|
||||
dev->pid = desc_device->idProduct;
|
||||
dev->i_manufacturer = desc_device->iManufacturer;
|
||||
dev->i_product = desc_device->iProduct;
|
||||
dev->i_serial = desc_device->iSerialNumber;
|
||||
dev->idVendor = desc_device->idVendor;
|
||||
dev->idProduct = desc_device->idProduct;
|
||||
dev->iManufacturer = desc_device->iManufacturer;
|
||||
dev->iProduct = desc_device->iProduct;
|
||||
dev->iSerialNumber = desc_device->iSerialNumber;
|
||||
dev->bNumConfigurations = desc_device->bNumConfigurations;
|
||||
|
||||
tuh_enum_descriptor_device_cb(daddr, desc_device); // callback
|
||||
@@ -1520,8 +1619,8 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
if (desc_langid->bLength >= 4) {
|
||||
langid = tu_le16toh(desc_langid->utf16le[0]); // previous request is langid
|
||||
}
|
||||
if (dev->i_manufacturer != 0) {
|
||||
tuh_descriptor_get_string(daddr, dev->i_manufacturer, langid, _usbh_epbuf.ctrl, 2,
|
||||
if (dev->iManufacturer != 0) {
|
||||
tuh_descriptor_get_string(daddr, dev->iManufacturer, langid, _usbh_epbuf.ctrl, 2,
|
||||
process_enumeration, ENUM_GET_STRING_MANUFACTURER);
|
||||
break;
|
||||
}else {
|
||||
@@ -1530,10 +1629,10 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
}
|
||||
|
||||
case ENUM_GET_STRING_MANUFACTURER: {
|
||||
if (dev->i_manufacturer != 0) {
|
||||
if (dev->iManufacturer != 0) {
|
||||
langid = tu_le16toh(xfer->setup->wIndex); // langid from length's request
|
||||
const uint8_t str_len = xfer->buffer[0];
|
||||
tuh_descriptor_get_string(daddr, dev->i_manufacturer, langid, _usbh_epbuf.ctrl, str_len,
|
||||
tuh_descriptor_get_string(daddr, dev->iManufacturer, langid, _usbh_epbuf.ctrl, str_len,
|
||||
process_enumeration, ENUM_GET_STRING_PRODUCT_LEN);
|
||||
break;
|
||||
} else {
|
||||
@@ -1542,11 +1641,11 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
}
|
||||
|
||||
case ENUM_GET_STRING_PRODUCT_LEN:
|
||||
if (dev->i_product != 0) {
|
||||
if (dev->iProduct != 0) {
|
||||
if (state == ENUM_GET_STRING_PRODUCT_LEN) {
|
||||
langid = tu_le16toh(xfer->setup->wIndex); // get langid from previous setup packet if not fall through
|
||||
}
|
||||
tuh_descriptor_get_string(daddr, dev->i_product, langid, _usbh_epbuf.ctrl, 2,
|
||||
tuh_descriptor_get_string(daddr, dev->iProduct, langid, _usbh_epbuf.ctrl, 2,
|
||||
process_enumeration, ENUM_GET_STRING_PRODUCT);
|
||||
break;
|
||||
} else {
|
||||
@@ -1554,10 +1653,10 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
}
|
||||
|
||||
case ENUM_GET_STRING_PRODUCT: {
|
||||
if (dev->i_product != 0) {
|
||||
if (dev->iProduct != 0) {
|
||||
langid = tu_le16toh(xfer->setup->wIndex); // langid from length's request
|
||||
const uint8_t str_len = xfer->buffer[0];
|
||||
tuh_descriptor_get_string(daddr, dev->i_product, langid, _usbh_epbuf.ctrl, str_len,
|
||||
tuh_descriptor_get_string(daddr, dev->iProduct, langid, _usbh_epbuf.ctrl, str_len,
|
||||
process_enumeration, ENUM_GET_STRING_SERIAL_LEN);
|
||||
break;
|
||||
} else {
|
||||
@@ -1566,11 +1665,11 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
}
|
||||
|
||||
case ENUM_GET_STRING_SERIAL_LEN:
|
||||
if (dev->i_serial != 0) {
|
||||
if (dev->iSerialNumber != 0) {
|
||||
if (state == ENUM_GET_STRING_SERIAL_LEN) {
|
||||
langid = tu_le16toh(xfer->setup->wIndex); // get langid from previous setup packet if not fall through
|
||||
}
|
||||
tuh_descriptor_get_string(daddr, dev->i_serial, langid, _usbh_epbuf.ctrl, 2,
|
||||
tuh_descriptor_get_string(daddr, dev->iSerialNumber, langid, _usbh_epbuf.ctrl, 2,
|
||||
process_enumeration, ENUM_GET_STRING_SERIAL);
|
||||
break;
|
||||
} else {
|
||||
@@ -1578,10 +1677,10 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
}
|
||||
|
||||
case ENUM_GET_STRING_SERIAL: {
|
||||
if (dev->i_serial != 0) {
|
||||
if (dev->iSerialNumber != 0) {
|
||||
langid = tu_le16toh(xfer->setup->wIndex); // langid from length's request
|
||||
const uint8_t str_len = xfer->buffer[0];
|
||||
tuh_descriptor_get_string(daddr, dev->i_serial, langid, _usbh_epbuf.ctrl, str_len,
|
||||
tuh_descriptor_get_string(daddr, dev->iSerialNumber, langid, _usbh_epbuf.ctrl, str_len,
|
||||
process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC);
|
||||
break;
|
||||
} else {
|
||||
@@ -1651,64 +1750,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool enum_new_device(hcd_event_t* event) {
|
||||
tuh_bus_info_t* dev0_bus = &_usbh_data.dev0_bus;
|
||||
dev0_bus->rhport = event->rhport;
|
||||
dev0_bus->hub_addr = event->connection.hub_addr;
|
||||
dev0_bus->hub_port = event->connection.hub_port;
|
||||
|
||||
if (dev0_bus->hub_addr == 0) {
|
||||
// connected directly to roothub
|
||||
|
||||
// wait until device connection is stable TODO non blocking
|
||||
tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
|
||||
|
||||
if (!hcd_port_connect_status(dev0_bus->rhport)) {
|
||||
TU_LOG_USBH("Device unplugged while debouncing\r\n");
|
||||
enum_full_complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
hcd_port_reset(dev0_bus->rhport); // reset device
|
||||
|
||||
// Since we are in middle of rhport reset, frame number is not available yet.
|
||||
// need to depend on tusb_time_millis_api() TODO non blocking
|
||||
tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
|
||||
|
||||
hcd_port_reset_end(dev0_bus->rhport);
|
||||
|
||||
// device unplugged while delaying
|
||||
if (!hcd_port_connect_status(dev0_bus->rhport)) {
|
||||
enum_full_complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
dev0_bus->speed = hcd_port_speed_get(dev0_bus->rhport);
|
||||
TU_LOG_USBH("%s Speed\r\n", tu_str_speed[dev0_bus->speed]);
|
||||
|
||||
// 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
|
||||
else {
|
||||
// connected via external hub
|
||||
// wait until device connection is stable TODO non blocking
|
||||
tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
|
||||
|
||||
// ENUM_HUB_GET_STATUS
|
||||
TU_ASSERT(hub_port_get_status(dev0_bus->hub_addr, dev0_bus->hub_port, _usbh_epbuf.ctrl,
|
||||
process_enumeration, ENUM_HUB_CLEAR_RESET_1));
|
||||
}
|
||||
#endif // hub
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t get_new_address(bool is_hub) {
|
||||
static uint8_t enum_get_new_address(bool is_hub) {
|
||||
uint8_t start;
|
||||
uint8_t end;
|
||||
|
||||
@@ -1721,47 +1763,14 @@ static uint8_t get_new_address(bool is_hub) {
|
||||
}
|
||||
|
||||
for (uint8_t idx = start; idx < end; idx++) {
|
||||
if (!_usbh_devices[idx].connected) return (idx+1);
|
||||
if (!_usbh_devices[idx].connected) {
|
||||
return (idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // invalid address
|
||||
}
|
||||
|
||||
static bool enum_request_set_addr(tusb_desc_device_t const* desc_device) {
|
||||
// Get new address
|
||||
uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
|
||||
TU_ASSERT(new_addr != 0);
|
||||
TU_LOG_USBH("Set Address = %d\r\n", new_addr);
|
||||
|
||||
usbh_device_t* new_dev = get_device(new_addr);
|
||||
new_dev->bus_info = _usbh_data.dev0_bus;
|
||||
new_dev->connected = 1;
|
||||
new_dev->ep0_size = desc_device->bMaxPacketSize0;
|
||||
|
||||
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_ADDRESS,
|
||||
.wValue = tu_htole16(new_addr),
|
||||
.wIndex = 0,
|
||||
.wLength = 0
|
||||
};
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = 0,
|
||||
.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 enum_parse_configuration_desc(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg) {
|
||||
usbh_device_t* dev = get_device(dev_addr);
|
||||
uint16_t const total_len = tu_le16toh(desc_cfg->wTotalLength);
|
||||
|
@@ -250,6 +250,10 @@ bool tuh_edpt_close(uint8_t daddr, uint8_t ep_addr);
|
||||
// Return true if a queued transfer is aborted, false if there is no transfer to abort
|
||||
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr);
|
||||
|
||||
// Set Address (control transfer)
|
||||
bool tuh_address_set(uint8_t daddr, uint8_t new_addr,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// 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
|
||||
|
Reference in New Issue
Block a user