move usbh ctrl_xfer into usbh_data

This commit is contained in:
hathach
2025-04-23 16:50:58 +07:00
parent a2da575793
commit 9a1f690ec4

View File

@@ -263,7 +263,7 @@ static osal_queue_t _usbh_q;
// Control transfers: since most controllers do not support multiple control transfers
// on multiple devices concurrently and control transfers are not used much except for
// enumeration, we will only execute control transfers one at a time.
static struct {
typedef struct {
uint8_t* buffer;
tuh_xfer_cb_t complete_cb;
uintptr_t user_data;
@@ -272,26 +272,25 @@ static struct {
uint8_t daddr;
volatile uint16_t actual_len;
uint8_t failed_count;
} _ctrl_xfer;
} usbh_ctrl_xfer_info_t;
typedef struct {
TUH_EPBUF_TYPE_DEF(tusb_control_request_t, request);
TUH_EPBUF_DEF(ctrl, CFG_TUH_ENUMERATION_BUFSIZE);
} usbh_epbuf_t;
CFG_TUH_MEM_SECTION static usbh_epbuf_t _usbh_epbuf;
typedef struct {
uint8_t controller_id; // controller ID
uint8_t enumerating_daddr; // device address of the device being enumerated
tuh_bus_info_t dev0_bus; // bus info for dev0 in enumeration
uint8_t controller_id; // controller ID
uint8_t enumerating_daddr; // device address of the device being enumerated
tuh_bus_info_t dev0_bus; // bus info for dev0 in enumeration
usbh_ctrl_xfer_info_t ctrl_xfer_info; // control transfer
} usbh_data_t;
static usbh_data_t _usbh_data = {
.controller_id = TUSB_INDEX_INVALID_8,
};
typedef struct {
TUH_EPBUF_TYPE_DEF(tusb_control_request_t, request);
TUH_EPBUF_DEF(ctrl, CFG_TUH_ENUMERATION_BUFSIZE);
} usbh_epbuf_t;
CFG_TUH_MEM_SECTION static usbh_epbuf_t _usbh_epbuf;
//------------- Helper Function -------------//
TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr) {
TU_VERIFY(dev_addr > 0 && dev_addr <= TOTAL_DEVICES, NULL);
@@ -299,7 +298,7 @@ TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr)
}
static bool enum_new_device(hcd_event_t* event);
static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size);
static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
@@ -388,9 +387,9 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Init host stack if not already
if (!tuh_inited()) {
TU_LOG_INT_USBH(sizeof(usbh_data_t));
TU_LOG_INT_USBH(sizeof(usbh_device_t));
TU_LOG_INT_USBH(sizeof(hcd_event_t));
TU_LOG_INT_USBH(sizeof(_ctrl_xfer));
TU_LOG_INT_USBH(sizeof(tuh_xfer_t));
TU_LOG_INT_USBH(sizeof(tu_fifo_t));
TU_LOG_INT_USBH(sizeof(tu_edpt_stream_t));
@@ -412,7 +411,6 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Device
tu_memclr(_usbh_devices, sizeof(_usbh_devices));
tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
tu_memclr(&_usbh_data, sizeof(_usbh_data));
_usbh_data.controller_id = TUSB_INDEX_INVALID_8;
@@ -451,7 +449,7 @@ bool tuh_deinit(uint8_t rhport) {
_usbh_data.controller_id = TUSB_INDEX_INVALID_8;
// "unplug" all devices on this rhport (hub_addr = 0, hub_port = 0)
process_removing_device(rhport, 0, 0);
process_removed_device(rhport, 0, 0);
// deinit host stack if no controller is active
if (!tuh_inited()) {
@@ -545,7 +543,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
case HCD_EVENT_DEVICE_REMOVE:
TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
process_removing_device(event.rhport, event.connection.hub_addr, event.connection.hub_port);
process_removed_device(event.rhport, event.connection.hub_addr, event.connection.hub_port);
#if CFG_TUH_HUB
// TODO remove
@@ -638,9 +636,9 @@ static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
}
TU_ATTR_ALWAYS_INLINE static inline void _control_set_xfer_stage(uint8_t stage) {
if (_ctrl_xfer.stage != stage) {
if (_usbh_data.ctrl_xfer_info.stage != stage) {
(void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
_ctrl_xfer.stage = stage;
_usbh_data.ctrl_xfer_info.stage = stage;
(void) osal_mutex_unlock(_usbh_mutex);
}
}
@@ -660,23 +658,22 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
const uint8_t daddr = xfer->daddr;
TU_VERIFY(tuh_connected(daddr));
// pre-check to help reducing mutex lock
TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
TU_VERIFY(ctrl_info->stage == CONTROL_STAGE_IDLE); // pre-check to help reducing mutex lock
(void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
bool const is_idle = (ctrl_info->stage == CONTROL_STAGE_IDLE);
if (is_idle) {
_ctrl_xfer.stage = CONTROL_STAGE_SETUP;
_ctrl_xfer.daddr = daddr;
_ctrl_xfer.actual_len = 0;
_ctrl_xfer.failed_count = 0;
ctrl_info->stage = CONTROL_STAGE_SETUP;
ctrl_info->daddr = daddr;
ctrl_info->actual_len = 0;
ctrl_info->failed_count = 0;
_ctrl_xfer.buffer = xfer->buffer;
_ctrl_xfer.complete_cb = xfer->complete_cb;
_ctrl_xfer.user_data = xfer->user_data;
ctrl_info->buffer = xfer->buffer;
ctrl_info->complete_cb = xfer->complete_cb;
ctrl_info->user_data = xfer->user_data;
_usbh_epbuf.request = (*xfer->setup);
}
(void) osal_mutex_unlock(_usbh_mutex);
TU_VERIFY(is_idle);
@@ -693,8 +690,8 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
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;
ctrl_info->user_data = (uintptr_t) &result;
ctrl_info->complete_cb = _control_blocking_complete_cb;
TU_ASSERT(usbh_setup_send(daddr, (uint8_t const *) &_usbh_epbuf.request));
@@ -712,7 +709,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
*((xfer_result_t*) xfer->user_data) = result;
}
xfer->result = result;
xfer->actual_len = _ctrl_xfer.actual_len;
xfer->actual_len = ctrl_info->actual_len;
}
return true;
@@ -720,6 +717,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
static void _control_xfer_complete(uint8_t daddr, xfer_result_t result) {
TU_LOG_USBH("\r\n");
usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
// duplicate xfer since user can execute control transfer within callback
tusb_control_request_t const request = _usbh_epbuf.request;
@@ -728,10 +726,10 @@ static void _control_xfer_complete(uint8_t daddr, xfer_result_t result) {
.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
.actual_len = (uint32_t) ctrl_info->actual_len,
.buffer = ctrl_info->buffer,
.complete_cb = ctrl_info->complete_cb,
.user_data = ctrl_info->user_data
};
_control_set_xfer_stage(CONTROL_STAGE_IDLE);
@@ -746,6 +744,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
const uint8_t rhport = usbh_get_rhport(daddr);
tusb_control_request_t const * request = &_usbh_epbuf.request;
usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
switch (result) {
case XFER_RESULT_STALLED:
@@ -755,12 +754,12 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
break;
case XFER_RESULT_FAILED:
if (tuh_connected(daddr) && _ctrl_xfer.failed_count < USBH_CONTROL_RETRY_MAX) {
TU_LOG_USBH("[%u:%u] Control FAILED %u/%u, retrying\r\n", rhport, daddr, _ctrl_xfer.failed_count+1, USBH_CONTROL_RETRY_MAX);
if (tuh_connected(daddr) && ctrl_info->failed_count < USBH_CONTROL_RETRY_MAX) {
TU_LOG_USBH("[%u:%u] Control FAILED %u/%u, retrying\r\n", rhport, daddr, ctrl_info->failed_count+1, USBH_CONTROL_RETRY_MAX);
(void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
_ctrl_xfer.stage = CONTROL_STAGE_SETUP;
_ctrl_xfer.failed_count++;
_ctrl_xfer.actual_len = 0; // reset actual_len
ctrl_info->stage = CONTROL_STAGE_SETUP;
ctrl_info->failed_count++;
ctrl_info->actual_len = 0; // reset actual_len
(void) osal_mutex_unlock(_usbh_mutex);
TU_ASSERT(usbh_setup_send(daddr, (uint8_t const *) request));
@@ -772,28 +771,29 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
break;
case XFER_RESULT_SUCCESS:
switch(_ctrl_xfer.stage) {
switch(ctrl_info->stage) {
case CONTROL_STAGE_SETUP:
if (request->wLength) {
// DATA stage: initial data toggle is always 1
_control_set_xfer_stage(CONTROL_STAGE_DATA);
TU_ASSERT(hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength));
const uint8_t ep_data = tu_edpt_addr(0, request->bmRequestType_bit.direction);
TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_data, ctrl_info->buffer, request->wLength));
return true;
}
TU_ATTR_FALLTHROUGH;
TU_ATTR_FALLTHROUGH;
case CONTROL_STAGE_DATA:
if (request->wLength) {
TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr);
TU_LOG_MEM_USBH(_ctrl_xfer.buffer, xferred_bytes, 2);
TU_LOG_MEM_USBH(ctrl_info->buffer, xferred_bytes, 2);
}
ctrl_info->actual_len = (uint16_t) xferred_bytes;
_ctrl_xfer.actual_len = (uint16_t) xferred_bytes;
// ACK stage: toggle is always 1
_control_set_xfer_stage(CONTROL_STAGE_ACK);
TU_ASSERT( hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction), NULL, 0) );
break;
// ACK stage: toggle is always 1
_control_set_xfer_stage(CONTROL_STAGE_ACK);
const uint8_t ep_status = tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction);
TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_status, NULL, 0));
break;
case CONTROL_STAGE_ACK: {
// Abort all pending transfers if SET_CONFIGURATION request
@@ -842,16 +842,16 @@ bool tuh_edpt_xfer(tuh_xfer_t* xfer) {
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
TU_LOG_USBH("[%u] Aborted transfer on EP %02X\r\n", daddr, ep_addr);
const uint8_t epnum = tu_edpt_number(ep_addr);
const uint8_t dir = tu_edpt_dir(ep_addr);
if (epnum == 0) {
// Also include dev0_bus for aborting enumerating
// Also include dev0 for aborting enumerating
const uint8_t rhport = usbh_get_rhport(daddr);
// control transfer: only 1 control at a time, check if we are aborting the current one
TU_VERIFY(daddr == _ctrl_xfer.daddr && _ctrl_xfer.stage != CONTROL_STAGE_IDLE);
const usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
TU_VERIFY(daddr == ctrl_info->daddr && ctrl_info->stage != CONTROL_STAGE_IDLE);
hcd_edpt_abort_xfer(rhport, daddr, ep_addr);
_control_set_xfer_stage(CONTROL_STAGE_IDLE); // reset control transfer state to idle
} else {
@@ -859,9 +859,8 @@ bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
TU_VERIFY(dev);
TU_VERIFY(dev->ep_status[epnum][dir].busy); // non-control skip if not busy
// abort then mark as ready and release endpoint
hcd_edpt_abort_xfer(dev->bus_info.rhport, daddr, ep_addr);
// mark as ready and release endpoint if transfer is aborted
dev->ep_status[epnum][dir].busy = false;
tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex);
}
@@ -1252,16 +1251,18 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_hub_addr(uint8_t daddr) {
}
// a device unplugged from rhport:hub_addr:hub_port
static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) {
// dev0_bus is unplugged
if ((_usbh_data.enumerating_daddr == 0) && (rhport == _usbh_data.dev0_bus.rhport) &&
(hub_addr == _usbh_data.dev0_bus.hub_addr) && (hub_port == _usbh_data.dev0_bus.hub_port)) {
hcd_device_close(_usbh_data.dev0_bus.rhport, 0);
if (_ctrl_xfer.daddr == 0) {
_control_set_xfer_stage(CONTROL_STAGE_IDLE);
static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) {
// if dev0 is unplugged while enumerating (not yet assigned an address)
if (_usbh_data.enumerating_daddr == 0) {
const tuh_bus_info_t* dev0_bus = &_usbh_data.dev0_bus;
if ((rhport == dev0_bus->rhport) && (hub_addr == dev0_bus->hub_addr) && (hub_port == dev0_bus->hub_port)) {
hcd_device_close(dev0_bus->rhport, 0);
if (_usbh_data.ctrl_xfer_info.daddr == 0) {
_control_set_xfer_stage(CONTROL_STAGE_IDLE);
}
_usbh_data.enumerating_daddr = TUSB_INDEX_INVALID_8;
return;
}
_usbh_data.enumerating_daddr = TUSB_INDEX_INVALID_8;
return;
}
//------------- find the all devices (star-network) under port that is unplugged -------------//
@@ -1298,8 +1299,8 @@ static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hu
hcd_device_close(rhport, daddr);
clear_device(dev);
// abort on-going control xfer on this device if any
if (daddr == _ctrl_xfer.daddr) {
// abort ongoing control xfer on this device if any
if (daddr == _usbh_data.ctrl_xfer_info.daddr) {
_control_set_xfer_stage(CONTROL_STAGE_IDLE);
}
@@ -1651,45 +1652,45 @@ static void process_enumeration(tuh_xfer_t* xfer) {
}
static bool enum_new_device(hcd_event_t* event) {
_usbh_data.dev0_bus.rhport = event->rhport;
_usbh_data.dev0_bus.hub_addr = event->connection.hub_addr;
_usbh_data.dev0_bus.hub_port = event->connection.hub_port;
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 (_usbh_data.dev0_bus.hub_addr == 0) {
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);
// device unplugged while delaying
if (!hcd_port_connect_status(_usbh_data.dev0_bus.rhport)) {
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(_usbh_data.dev0_bus.rhport); // reset device
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()
// need to depend on tusb_time_millis_api() TODO non blocking
tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
hcd_port_reset_end(_usbh_data.dev0_bus.rhport);
hcd_port_reset_end(dev0_bus->rhport);
// device unplugged while delaying
if (!hcd_port_connect_status(_usbh_data.dev0_bus.rhport)) {
if (!hcd_port_connect_status(dev0_bus->rhport)) {
enum_full_complete();
return true;
}
_usbh_data.dev0_bus.speed = hcd_port_speed_get(_usbh_data.dev0_bus.rhport);
TU_LOG_USBH("%s Speed\r\n", tu_str_speed[_usbh_data.dev0.speed]);
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
@@ -1699,7 +1700,7 @@ static bool enum_new_device(hcd_event_t* event) {
tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
// ENUM_HUB_GET_STATUS
TU_ASSERT(hub_port_get_status(_usbh_data.dev0_bus.hub_addr, _usbh_data.dev0_bus.hub_port, _usbh_epbuf.ctrl,
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