summaryrefslogtreecommitdiff
path: root/src/host
diff options
context:
space:
mode:
authortobozo <[email protected]>2023-03-30 19:30:24 +0000
committerGitHub <[email protected]>2023-03-30 19:30:24 +0000
commit290f18a1fe4551cb0376d356627bda595c621518 (patch)
tree789f93ee268cb9c9cccc03702f8a1769ae4a3d9d /src/host
parent9e38b4cc68d9eeeb055349a97caf30d1cf4000e0 (diff)
parent5add4c97fa834004e6a3b267345a13c6d3c9514a (diff)
Merge branch 'hathach:master' into master
Diffstat (limited to 'src/host')
-rw-r--r--src/host/hcd.h56
-rw-r--r--src/host/hub.c101
-rw-r--r--src/host/usbh.c535
-rw-r--r--src/host/usbh.h55
-rw-r--r--src/host/usbh_classdriver.h5
5 files changed, 476 insertions, 276 deletions
diff --git a/src/host/hcd.h b/src/host/hcd.h
index c40bea64c..623c12a12 100644
--- a/src/host/hcd.h
+++ b/src/host/hcd.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -48,7 +48,7 @@
// #endif
#endif
- //--------------------------------------------------------------------+
+//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef enum
@@ -106,6 +106,9 @@ typedef struct
// Controller API
//--------------------------------------------------------------------+
+// optional hcd configuration, called by tuh_configure()
+bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) TU_ATTR_WEAK;
+
// Initialize controller to host mode
bool hcd_init(uint8_t rhport);
@@ -144,9 +147,16 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr);
// Endpoints API
//--------------------------------------------------------------------+
-bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
+// Open an endpoint
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
+
+// clear stall, data toggle is also reset to DATA0
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr);
//--------------------------------------------------------------------+
@@ -164,13 +174,47 @@ extern void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_i
extern void hcd_event_handler(hcd_event_t const* event, bool in_isr);
// Helper to send device attach event
-extern void hcd_event_device_attach(uint8_t rhport, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline
+void hcd_event_device_attach(uint8_t rhport, bool in_isr)
+{
+ hcd_event_t event;
+ event.rhport = rhport;
+ event.event_id = HCD_EVENT_DEVICE_ATTACH;
+ event.connection.hub_addr = 0;
+ event.connection.hub_port = 0;
+ hcd_event_handler(&event, in_isr);
+}
// Helper to send device removal event
-extern void hcd_event_device_remove(uint8_t rhport, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline
+void hcd_event_device_remove(uint8_t rhport, bool in_isr)
+{
+ hcd_event_t event;
+ event.rhport = rhport;
+ event.event_id = HCD_EVENT_DEVICE_REMOVE;
+ event.connection.hub_addr = 0;
+ event.connection.hub_port = 0;
+
+ hcd_event_handler(&event, in_isr);
+}
// Helper to send USB transfer event
-extern void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline
+void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr)
+{
+ hcd_event_t event =
+ {
+ .rhport = 0, // TODO correct rhport
+ .event_id = HCD_EVENT_XFER_COMPLETE,
+ .dev_addr = dev_addr,
+ };
+ event.xfer_complete.ep_addr = ep_addr;
+ event.xfer_complete.result = result;
+ event.xfer_complete.len = xferred_bytes;
+
+
+ hcd_event_handler(&event, in_isr);
+}
#ifdef __cplusplus
}
diff --git a/src/host/hub.c b/src/host/hub.c
index 9e546f6f7..386ad6aae 100644
--- a/src/host/hub.c
+++ b/src/host/hub.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -28,6 +28,7 @@
#if (CFG_TUH_ENABLED && CFG_TUH_HUB)
+#include "hcd.h"
#include "usbh.h"
#include "usbh_classdriver.h"
#include "hub.h"
@@ -42,11 +43,12 @@ typedef struct
uint8_t port_count;
uint8_t status_change; // data from status change interrupt endpoint
- hub_port_status_response_t port_status;
+ CFG_TUH_MEM_ALIGN hub_port_status_response_t port_status;
+ CFG_TUH_MEM_ALIGN hub_status_response_t hub_status;
} hub_interface_t;
-CFG_TUSB_MEM_SECTION static hub_interface_t hub_data[CFG_TUH_HUB];
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)];
+CFG_TUH_MEM_SECTION static hub_interface_t hub_data[CFG_TUH_HUB];
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)];
TU_ATTR_ALWAYS_INLINE
static inline hub_interface_t* get_itf(uint8_t dev_addr)
@@ -84,7 +86,7 @@ bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
{
.bmRequestType_bit =
{
- .recipient = TUSB_REQ_RCPT_OTHER,
+ .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
},
@@ -116,7 +118,7 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
{
.bmRequestType_bit =
{
- .recipient = TUSB_REQ_RCPT_OTHER,
+ .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
},
@@ -148,7 +150,7 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
{
.bmRequestType_bit =
{
- .recipient = TUSB_REQ_RCPT_OTHER,
+ .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_IN
},
@@ -169,7 +171,7 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
};
TU_LOG2("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
- TU_ASSERT( tuh_control_xfer(&xfer) );
+ TU_VERIFY( tuh_control_xfer(&xfer) );
return true;
}
@@ -200,7 +202,7 @@ bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
-
+
TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep));
hub_interface_t* p_hub = get_itf(dev_addr);
@@ -312,7 +314,8 @@ static void config_port_power_complete (tuh_xfer_t* xfer)
// Connection Changes
//--------------------------------------------------------------------+
-static void connection_get_status_complete (tuh_xfer_t* xfer);
+static void hub_port_get_status_complete (tuh_xfer_t* xfer);
+static void hub_get_status_complete (tuh_xfer_t* xfer);
static void connection_clear_conn_change_complete (tuh_xfer_t* xfer);
static void connection_port_reset_complete (tuh_xfer_t* xfer);
@@ -325,15 +328,31 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32
hub_interface_t* p_hub = get_itf(dev_addr);
- TU_LOG2(" Port Status Change = 0x%02X\r\n", p_hub->status_change);
+ TU_LOG2(" Hub Status Change = 0x%02X\r\n", p_hub->status_change);
- // Hub ignore bit0 in status change
- for (uint8_t port=1; port <= p_hub->port_count; port++)
+ // Hub bit 0 is for the hub device events
+ if (tu_bit_test(p_hub->status_change, 0))
+ {
+ if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false)
+ {
+ //Hub status control transfer failed, retry
+ hub_edpt_status_xfer(dev_addr);
+ }
+ }
+ else
{
- if ( tu_bit_test(p_hub->status_change, port) )
+ // Hub bits 1 to n are hub port events
+ for (uint8_t port=1; port <= p_hub->port_count; port++)
{
- hub_port_get_status(dev_addr, port, &p_hub->port_status, connection_get_status_complete, 0);
- break;
+ if ( tu_bit_test(p_hub->status_change, port) )
+ {
+ if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false)
+ {
+ //Hub status control transfer failed, retry
+ hub_edpt_status_xfer(dev_addr);
+ }
+ break;
+ }
}
}
@@ -342,7 +361,36 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32
return true;
}
-static void connection_get_status_complete (tuh_xfer_t* xfer)
+static void hub_clear_feature_complete_stub(tuh_xfer_t* xfer)
+{
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
+ hub_edpt_status_xfer(xfer->daddr);
+}
+
+static void hub_get_status_complete (tuh_xfer_t* xfer)
+{
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
+
+ uint8_t const daddr = xfer->daddr;
+ hub_interface_t* p_hub = get_itf(daddr);
+ uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ TU_ASSERT(port_num == 0 , );
+
+ TU_LOG2("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, p_hub->hub_status.change.value);
+
+ if (p_hub->hub_status.change.local_power_source)
+ {
+ TU_LOG2("HUB Local Power Change, addr = %u\r\n", daddr);
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ else if (p_hub->hub_status.change.over_current)
+ {
+ TU_LOG1("HUB Over Current, addr = %u\r\n", daddr);
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+}
+
+static void hub_port_get_status_complete (tuh_xfer_t* xfer)
{
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
@@ -360,7 +408,24 @@ static void connection_get_status_complete (tuh_xfer_t* xfer)
hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0);
}else
{
- // Other changes are: Enable, Suspend, Over Current, Reset, L1 state
+ // Clear other port status change interrupts. TODO Not currently handled - just cleared.
+ if (p_hub->port_status.change.port_enable)
+ {
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ else if (p_hub->port_status.change.suspend)
+ {
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ else if (p_hub->port_status.change.over_current)
+ {
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ else if (p_hub->port_status.change.reset)
+ {
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ // Other changes are: L1 state
// TODO clear change
// prepare for next hub status
diff --git a/src/host/usbh.c b/src/host/usbh.c
index 80d176142..3be662c63 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -28,10 +28,8 @@
#if CFG_TUH_ENABLED
+#include "host/hcd.h"
#include "tusb.h"
-#include "common/tusb_private.h"
-
-#include "host/usbh.h"
#include "host/usbh_classdriver.h"
#include "hub.h"
@@ -47,15 +45,15 @@
#define CFG_TUH_INTERFACE_MAX 8
#endif
-// Debug level of USBD
-#define USBH_DBG_LVL 2
+// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
+#define USBH_DEBUG 2
+
+#define TU_LOG_USBH(...) TU_LOG(USBH_DEBUG, __VA_ARGS__)
//--------------------------------------------------------------------+
// USBH-HCD common data structure
//--------------------------------------------------------------------+
-// device0 struct must be strictly a subset of normal device struct
-// TODO refactor later
typedef struct
{
// port
@@ -63,15 +61,13 @@ typedef struct
uint8_t hub_addr;
uint8_t hub_port;
uint8_t speed;
+ volatile uint8_t enumerating;
- struct TU_ATTR_PACKED
- {
- volatile uint8_t connected : 1;
- volatile uint8_t addressed : 1;
- volatile uint8_t configured : 1;
- volatile uint8_t suspended : 1;
- };
-
+// struct TU_ATTR_PACKED {
+// uint8_t speed : 4; // packed speed to save footprint
+// volatile uint8_t enumerating : 1;
+// uint8_t TU_RESERVED : 3;
+// };
} usbh_dev0_t;
typedef struct {
@@ -122,10 +118,6 @@ typedef struct {
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-// Invalid driver ID in itf2drv[] ep2drv[][] mapping
-enum { DRVID_INVALID = 0xFFu };
-enum { ADDR_INVALID = 0xFFu };
-
#if CFG_TUSB_DEBUG >= 2
#define DRIVER_NAME(_name) .name = _name,
#else
@@ -203,7 +195,7 @@ enum { CONFIG_NUM = 1 }; // default to use configuration 1
// sum of end device + hub
#define TOTAL_DEVICES (CFG_TUH_DEVICE_MAX + CFG_TUH_HUB)
-static bool _usbh_initialized = false;
+static uint8_t _usbh_controller = TUSB_INDEX_INVALID_8;
// Device with address = 0 for enumeration
static usbh_dev0_t _dev0;
@@ -211,30 +203,14 @@ static usbh_dev0_t _dev0;
// all devices excluding zero-address
// hub address start from CFG_TUH_DEVICE_MAX+1
// TODO: hub can has its own simpler struct to save memory
-CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[TOTAL_DEVICES];
-
-// Mutex for claiming endpoint, only needed when using with preempted RTOS
-#if TUSB_OPT_MUTEX
-static osal_mutex_def_t _usbh_mutexdef;
-static osal_mutex_t _usbh_mutex;
-
-TU_ATTR_ALWAYS_INLINE static inline void usbh_lock(void)
-{
- osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void usbh_unlock(void)
-{
- osal_mutex_unlock(_usbh_mutex);
-}
+static usbh_device_t _usbh_devices[TOTAL_DEVICES];
+// Mutex for claiming endpoint
+#if OSAL_MUTEX_REQUIRED
+ static osal_mutex_def_t _usbh_mutexdef;
+ static osal_mutex_t _usbh_mutex;
#else
-
-#define _usbh_mutex NULL
-
-#define usbh_lock()
-#define usbh_unlock()
-
+ #define _usbh_mutex NULL
#endif
// Event queue
@@ -242,15 +218,15 @@ TU_ATTR_ALWAYS_INLINE static inline void usbh_unlock(void)
OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
static osal_queue_t _usbh_q;
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
-// Control transfer: since most controller does not support multiple control transfer
-// on multiple devices concurrently. And control transfer is not used much except enumeration
-// We will only execute control transfer one at a time.
-struct
+// 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.
+CFG_TUH_MEM_SECTION struct
{
- tusb_control_request_t request TU_ATTR_ALIGNED(4);
+ CFG_TUH_MEM_ALIGN tusb_control_request_t request;
uint8_t* buffer;
tuh_xfer_cb_t complete_cb;
uintptr_t user_data;
@@ -274,9 +250,30 @@ static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t h
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);
+#if CFG_TUSB_OS == OPT_OS_NONE
+// TODO rework time-related function later
+void osal_task_delay(uint32_t msec)
+{
+ const uint32_t start = hcd_frame_number(_usbh_controller);
+ while ( ( hcd_frame_number(_usbh_controller) - start ) < msec ) {}
+}
+#endif
+
//--------------------------------------------------------------------+
// PUBLIC API (Parameter Verification is required)
//--------------------------------------------------------------------+
+
+bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param)
+{
+ if (hcd_configure)
+ {
+ return hcd_configure(rhport, cfg_id, cfg_param);
+ }else
+ {
+ return false;
+ }
+}
+
bool tuh_mounted(uint8_t dev_addr)
{
usbh_device_t* dev = get_device(dev_addr);
@@ -303,49 +300,37 @@ tusb_speed_t tuh_speed_get (uint8_t dev_addr)
return (tusb_speed_t) (dev ? get_device(dev_addr)->speed : _dev0.speed);
}
-#if CFG_TUSB_OS == OPT_OS_NONE
-void osal_task_delay(uint32_t msec)
-{
- (void) msec;
-
- const uint32_t start = hcd_frame_number(TUH_OPT_RHPORT);
- while ( ( hcd_frame_number(TUH_OPT_RHPORT) - start ) < msec ) {}
-}
-#endif
-
-//--------------------------------------------------------------------+
-// CLASS-USBD API (don't require to verify parameters)
-//--------------------------------------------------------------------+
-
static void clear_device(usbh_device_t* dev)
{
tu_memclr(dev, sizeof(usbh_device_t));
- memset(dev->itf2drv, DRVID_INVALID, sizeof(dev->itf2drv)); // invalid mapping
- memset(dev->ep2drv , DRVID_INVALID, sizeof(dev->ep2drv )); // invalid mapping
+ memset(dev->itf2drv, TUSB_INDEX_INVALID_8, sizeof(dev->itf2drv)); // invalid mapping
+ memset(dev->ep2drv , TUSB_INDEX_INVALID_8, sizeof(dev->ep2drv )); // invalid mapping
}
bool tuh_inited(void)
{
- return _usbh_initialized;
+ return _usbh_controller != TUSB_INDEX_INVALID_8;
}
-bool tuh_init(uint8_t rhport)
+bool tuh_init(uint8_t controller_id)
{
// skip if already initialized
- if (_usbh_initialized) return _usbh_initialized;
+ if ( tuh_inited() ) return true;
- TU_LOG2("USBH init\r\n");
- TU_LOG2_INT(sizeof(usbh_device_t));
- TU_LOG2_INT(sizeof(hcd_event_t));
- TU_LOG2_INT(sizeof(_ctrl_xfer));
- TU_LOG2_INT(sizeof(tuh_xfer_t));
+ TU_LOG_USBH("USBH init on controller %u\r\n", controller_id);
+ TU_LOG_INT(USBH_DEBUG, sizeof(usbh_device_t));
+ TU_LOG_INT(USBH_DEBUG, sizeof(hcd_event_t));
+ TU_LOG_INT(USBH_DEBUG, sizeof(_ctrl_xfer));
+ TU_LOG_INT(USBH_DEBUG, sizeof(tuh_xfer_t));
+ TU_LOG_INT(USBH_DEBUG, sizeof(tu_fifo_t));
+ TU_LOG_INT(USBH_DEBUG, sizeof(tu_edpt_stream_t));
// Event queue
_usbh_q = osal_queue_create( &_usbh_qdef );
TU_ASSERT(_usbh_q != NULL);
-#if TUSB_OPT_MUTEX
- // Mutex
+#if OSAL_MUTEX_REQUIRED
+ // Init mutex
_usbh_mutex = osal_mutex_create(&_usbh_mutexdef);
TU_ASSERT(_usbh_mutex);
#endif
@@ -363,17 +348,26 @@ bool tuh_init(uint8_t rhport)
// Class drivers
for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
{
- TU_LOG2("%s init\r\n", usbh_class_drivers[drv_id].name);
+ TU_LOG_USBH("%s init\r\n", usbh_class_drivers[drv_id].name);
usbh_class_drivers[drv_id].init();
}
- TU_ASSERT(hcd_init(rhport));
- hcd_int_enable(rhport);
+ _usbh_controller = controller_id;;
+
+ TU_ASSERT(hcd_init(controller_id));
+ hcd_int_enable(controller_id);
- _usbh_initialized = true;
return true;
}
+bool tuh_task_event_ready(void)
+{
+ // Skip if stack is not initialized
+ if ( !tuh_inited() ) return false;
+
+ return !osal_queue_empty(_usbh_q);
+}
+
/* USB Host Driver task
* This top level thread manages all host controller event and delegates events to class-specific drivers.
* This should be called periodically within the mainloop or rtos thread.
@@ -397,7 +391,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
(void) in_isr; // not implemented yet
// Skip if stack is not initialized
- if ( !tusb_inited() ) return;
+ if ( !tuh_inited() ) return;
// Loop until there is no more events in the queue
while (1)
@@ -408,14 +402,22 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
switch (event.event_id)
{
case HCD_EVENT_DEVICE_ATTACH:
- // TODO due to the shared _usbh_ctrl_buf, we must complete enumerating
+ // due to the shared _usbh_ctrl_buf, we must complete enumerating
// one device before enumerating another one.
- TU_LOG2("USBH DEVICE ATTACH\r\n");
- enum_new_device(&event);
+ if ( _dev0.enumerating )
+ {
+ TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport);
+ osal_queue_send(_usbh_q, &event, in_isr);
+ }else
+ {
+ TU_LOG_USBH("[%u:] USBH DEVICE ATTACH\r\n", event.rhport);
+ _dev0.enumerating = 1;
+ enum_new_device(&event);
+ }
break;
case HCD_EVENT_DEVICE_REMOVE:
- TU_LOG2("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
+ TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
process_device_unplugged(event.rhport, event.connection.hub_addr, event.connection.hub_port);
#if CFG_TUH_HUB
@@ -434,13 +436,13 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const ep_dir = tu_edpt_dir(ep_addr);
- TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
+ TU_LOG_USBH("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
if (event.dev_addr == 0)
{
// device 0 only has control endpoint
TU_ASSERT(epnum == 0, );
- usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
+ usbh_control_xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
}
else
{
@@ -452,14 +454,14 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
if ( 0 == epnum )
{
- usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
+ usbh_control_xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
}else
{
uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
if(drv_id < USBH_CLASS_DRIVER_COUNT)
{
- TU_LOG2("%s xfer callback\r\n", usbh_class_drivers[drv_id].name);
- usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
+ TU_LOG_USBH("%s xfer callback\r\n", usbh_class_drivers[drv_id].name);
+ usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
}
else
{
@@ -528,8 +530,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer)
uint8_t const daddr = xfer->daddr;
- // TODO probably better to use semaphore as resource management than mutex
- usbh_lock();
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
if (is_idle)
@@ -544,18 +545,20 @@ bool tuh_control_xfer (tuh_xfer_t* xfer)
_ctrl_xfer.user_data = xfer->user_data;
}
- usbh_unlock();
+ (void) osal_mutex_unlock(_usbh_mutex);
TU_VERIFY(is_idle);
const uint8_t rhport = usbh_get_rhport(daddr);
- TU_LOG2("[%u:%u] %s: ", rhport, daddr, xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[xfer->setup->bRequest] : "Unknown Request");
- TU_LOG2_VAR(&xfer->setup);
- TU_LOG2("\r\n");
+ TU_LOG_USBH("[%u:%u] %s: ", rhport, daddr,
+ (xfer->setup->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME) ?
+ tu_str_std_request[xfer->setup->bRequest] : "Class Request");
+ TU_LOG_PTR(USBH_DEBUG, xfer->setup);
+ TU_LOG_USBH("\r\n");
if (xfer->complete_cb)
{
- TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) );
+ TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.request) );
}else
{
// blocking if complete callback is not provided
@@ -570,10 +573,12 @@ bool tuh_control_xfer (tuh_xfer_t* xfer)
while (result == XFER_RESULT_INVALID)
{
- // only need to call task if not preempted RTOS
- #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO
- tuh_task();
- #endif
+ // Note: this can be called within an callback ie. part of tuh_task()
+ // therefore event with RTOS tuh_task() still need to be invoked
+ if (tuh_task_event_ready())
+ {
+ tuh_task();
+ }
// TODO probably some timeout to prevent hanged
}
@@ -588,14 +593,14 @@ bool tuh_control_xfer (tuh_xfer_t* xfer)
TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage)
{
- usbh_lock();
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
_ctrl_xfer.stage = stage;
- usbh_unlock();
+ (void) osal_mutex_unlock(_usbh_mutex);
}
static void _xfer_complete(uint8_t daddr, xfer_result_t result)
{
- TU_LOG2("\r\n");
+ TU_LOG_USBH("\r\n");
// duplicate xfer since user can execute control transfer within callback
tusb_control_request_t const request = _ctrl_xfer.request;
@@ -611,9 +616,7 @@ static void _xfer_complete(uint8_t daddr, xfer_result_t result)
.user_data = _ctrl_xfer.user_data
};
- usbh_lock();
- _ctrl_xfer.stage = CONTROL_STAGE_IDLE;
- usbh_unlock();
+ _set_control_xfer_stage(CONTROL_STAGE_IDLE);
if (xfer_temp.complete_cb)
{
@@ -630,7 +633,11 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
if (XFER_RESULT_SUCCESS != result)
{
- TU_LOG2("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED");
+ TU_LOG1("[%u:%u] Control %s, xferred_bytes = %lu\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
+ #if CFG_TUSB_DEBUG == 1
+ TU_LOG1_PTR(request);
+ TU_LOG1("\r\n");
+ #endif
// terminate transfer if any stage failed
_xfer_complete(dev_addr, result);
@@ -643,22 +650,23 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
{
// DATA stage: initial data toggle is always 1
_set_control_xfer_stage(CONTROL_STAGE_DATA);
- return hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength);
+ TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) );
+ return true;
}
- __attribute__((fallthrough));
+ TU_ATTR_FALLTHROUGH;
case CONTROL_STAGE_DATA:
- if (xferred_bytes)
+ if (request->wLength)
{
- TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr);
- TU_LOG2_MEM(_ctrl_xfer.buffer, xferred_bytes, 2);
+ TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, dev_addr);
+ TU_LOG_MEM(USBH_DEBUG, _ctrl_xfer.buffer, xferred_bytes, 2);
}
- _ctrl_xfer.actual_len = xferred_bytes;
+ _ctrl_xfer.actual_len = (uint16_t) xferred_bytes;
// ACK stage: toggle is always 1
_set_control_xfer_stage(CONTROL_STAGE_ACK);
- hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0);
+ TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0) );
break;
case CONTROL_STAGE_ACK:
@@ -685,7 +693,7 @@ bool tuh_edpt_xfer(tuh_xfer_t* xfer)
TU_VERIFY(usbh_edpt_claim(daddr, ep_addr));
- if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, xfer->buflen, xfer->complete_cb, xfer->user_data) )
+ if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen, xfer->complete_cb, xfer->user_data) )
{
usbh_edpt_release(daddr, ep_addr);
return false;
@@ -711,13 +719,13 @@ uint8_t* usbh_get_enum_buf(void)
void usbh_int_set(bool enabled)
{
- // TODO all host controller
+ // TODO all host controller if multiple is used
if (enabled)
{
- hcd_int_enable(TUH_OPT_RHPORT);
+ hcd_int_enable(_usbh_controller);
}else
{
- hcd_int_disable(TUH_OPT_RHPORT);
+ hcd_int_disable(_usbh_controller);
}
}
@@ -767,7 +775,7 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b
uint8_t const dir = tu_edpt_dir(ep_addr);
tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir];
- TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes);
+ TU_LOG_USBH(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes);
// Attempt to transfer on a busy endpoint, sound like an race condition !
TU_ASSERT(ep_state->busy == 0);
@@ -783,7 +791,7 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b
if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) )
{
- TU_LOG2("OK\r\n");
+ TU_LOG_USBH("OK\r\n");
return true;
}else
{
@@ -798,7 +806,7 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b
static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size)
{
- TU_LOG2("Open EP0 with Size = %u (addr = %u)\r\n", max_packet_size, dev_addr);
+ TU_LOG_USBH("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size);
tusb_desc_endpoint_t ep0_desc =
{
@@ -854,7 +862,7 @@ void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info)
}
}
-void hcd_event_handler(hcd_event_t const* event, bool in_isr)
+TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
{
switch (event->event_id)
{
@@ -864,52 +872,6 @@ void hcd_event_handler(hcd_event_t const* event, bool in_isr)
}
}
-void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr)
-{
- hcd_event_t event =
- {
- .rhport = 0, // TODO correct rhport
- .event_id = HCD_EVENT_XFER_COMPLETE,
- .dev_addr = dev_addr,
- .xfer_complete =
- {
- .ep_addr = ep_addr,
- .result = result,
- .len = xferred_bytes
- }
- };
-
- hcd_event_handler(&event, in_isr);
-}
-
-void hcd_event_device_attach(uint8_t rhport, bool in_isr)
-{
- hcd_event_t event =
- {
- .rhport = rhport,
- .event_id = HCD_EVENT_DEVICE_ATTACH
- };
-
- event.connection.hub_addr = 0;
- event.connection.hub_port = 0;
-
- hcd_event_handler(&event, in_isr);
-}
-
-void hcd_event_device_remove(uint8_t hostid, bool in_isr)
-{
- hcd_event_t event =
- {
- .rhport = hostid,
- .event_id = HCD_EVENT_DEVICE_REMOVE
- };
-
- event.connection.hub_addr = 0;
- event.connection.hub_port = 0;
-
- hcd_event_handler(&event, in_isr);
-}
-
//--------------------------------------------------------------------+
// Descriptors Async
//--------------------------------------------------------------------+
@@ -1013,7 +975,7 @@ bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void*
bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len,
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
- TU_LOG2("HID Get Report Descriptor\r\n");
+ TU_LOG_USBH("HID Get Report Descriptor\r\n");
tusb_control_request_t const request =
{
.bmRequestType_bit =
@@ -1052,7 +1014,7 @@ bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
- TU_LOG2("Set Configuration = %d\r\n", config_num);
+ TU_LOG_USBH("Set Configuration = %d\r\n", config_num);
tusb_control_request_t const request =
{
@@ -1078,7 +1040,55 @@ bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
.user_data = user_data
};
- return tuh_control_xfer(&xfer);
+ bool ret = tuh_control_xfer(&xfer);
+
+ // if blocking, user_data could be pointed to xfer_result
+ if ( !complete_cb && user_data )
+ {
+ *((xfer_result_t*) user_data) = xfer.result;
+ }
+
+ return ret;
+}
+
+bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_LOG_USBH("Set Interface %u Alternate %u\r\n", itf_num, itf_alt);
+
+ 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_INTERFACE,
+ .wValue = tu_htole16(itf_alt),
+ .wIndex = tu_htole16(itf_num),
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ bool ret = tuh_control_xfer(&xfer);
+
+ // if blocking, user_data could be pointed to xfer_result
+ if ( !complete_cb && user_data )
+ {
+ *((xfer_result_t*) user_data) = xfer.result;
+ }
+
+ return ret;
}
//--------------------------------------------------------------------+
@@ -1134,6 +1144,12 @@ uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_i
//
//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE
+static inline bool is_hub_addr(uint8_t daddr)
+{
+ return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX);
+}
+
// 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)
{
@@ -1146,19 +1162,28 @@ static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t h
// TODO Hub multiple level
if (dev->rhport == rhport &&
- (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr == 0 & hub_port == 0 means roothub
- (hub_port == 0 || dev->hub_port == hub_port) &&
+ (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr = 0 means roothub
+ (hub_port == 0 || dev->hub_port == hub_port) && // hub_port = 0 means all devices of downstream hub
dev->connected)
{
- TU_LOG2(" Address = %u\r\n", dev_addr);
+ TU_LOG_USBH(" Address = %u\r\n", dev_addr);
- // Invoke callback before close driver
- if (tuh_umount_cb) tuh_umount_cb(dev_addr);
+ if (is_hub_addr(dev_addr))
+ {
+ TU_LOG(USBH_DEBUG, "HUB address = %u is unmounted\r\n", dev_addr);
+ // If the device itself is a usb hub, unplug downstream devices.
+ // FIXME un-roll recursive calls to prevent potential stack overflow
+ process_device_unplugged(rhport, dev_addr, 0);
+ }else
+ {
+ // Invoke callback before closing driver
+ if (tuh_umount_cb) tuh_umount_cb(dev_addr);
+ }
// Close class driver
for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
{
- TU_LOG2("%s close\r\n", usbh_class_drivers[drv_id].name);
+ TU_LOG_USBH("%s close\r\n", usbh_class_drivers[drv_id].name);
usbh_class_drivers[drv_id].close(dev_addr);
}
@@ -1184,7 +1209,7 @@ enum {
//ENUM_HUB_GET_STATUS_1,
ENUM_HUB_CLEAR_RESET_1,
ENUM_ADDR0_DEVICE_DESC,
- ENUM_RESET_2, // 2nd reset before set address
+ ENUM_RESET_2, // 2nd reset before set address (not used)
ENUM_HUB_GET_STATUS_2,
ENUM_HUB_CLEAR_RESET_2,
ENUM_SET_ADDR,
@@ -1203,12 +1228,28 @@ static void enum_full_complete(void);
// process device enumeration
static void process_enumeration(tuh_xfer_t* xfer)
{
+ // Retry a few times with transfers in enumeration since device can be unstable when starting up
+ enum {
+ ATTEMPT_COUNT_MAX = 3,
+ ATTEMPT_DELAY_MS = 100
+ };
+ static uint8_t failed_count = 0;
+
if (XFER_RESULT_SUCCESS != xfer->result)
{
- // stop enumeration, maybe we could retry this
- enum_full_complete();
+ // retry if not reaching max attempt
+ if ( failed_count < ATTEMPT_COUNT_MAX )
+ {
+ failed_count++;
+ osal_task_delay(ATTEMPT_DELAY_MS); // delay a bit
+ TU_ASSERT(tuh_control_xfer(xfer), );
+ }else
+ {
+ enum_full_complete();
+ }
return;
}
+ failed_count = 0;
uint8_t const daddr = xfer->daddr;
uintptr_t const state = xfer->user_data;
@@ -1267,20 +1308,23 @@ static void process_enumeration(tuh_xfer_t* xfer)
TU_ASSERT( usbh_edpt_control_open(addr0, 8), );
// Get first 8 bytes of device descriptor for Control Endpoint size
- TU_LOG2("Get 8 byte of Device Descriptor\r\n");
+ TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n");
TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8, process_enumeration, ENUM_SET_ADDR), );
}
break;
+#if 0
case ENUM_RESET_2:
+ // TODO not used by now, but may be needed for some devices !?
// Reset device again before Set Address
- TU_LOG2("Port reset \r\n");
+ TU_LOG_USBH("Port reset2 \r\n");
if (_dev0.hub_addr == 0)
{
// connected directly to roothub
hcd_port_reset( _dev0.rhport );
- osal_task_delay(RESET_DELAY);
-
+ osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
+ // sof of controller may not running while resetting
+ hcd_port_reset_end(_dev0.rhport);
// TODO: fall through to SET ADDRESS, refactor later
}
#if CFG_TUH_HUB
@@ -1291,7 +1335,8 @@ static void process_enumeration(tuh_xfer_t* xfer)
break;
}
#endif
- __attribute__((fallthrough));
+ TU_ATTR_FALLTHROUGH;
+#endif
case ENUM_SET_ADDR:
enum_request_set_addr();
@@ -1305,14 +1350,14 @@ static void process_enumeration(tuh_xfer_t* xfer)
TU_ASSERT(new_dev, );
new_dev->addressed = 1;
- // TODO close device 0, may not be needed
+ // Close device 0
hcd_device_close(_dev0.rhport, 0);
// open control pipe for new address
TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size), );
// Get full device descriptor
- TU_LOG2("Get Device Descriptor\r\n");
+ TU_LOG_USBH("Get Device Descriptor\r\n");
TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC), );
}
break;
@@ -1333,7 +1378,7 @@ static void process_enumeration(tuh_xfer_t* xfer)
// Get 9-byte for total length
uint8_t const config_idx = CONFIG_NUM - 1;
- TU_LOG2("Get Configuration[0] Descriptor (9 bytes)\r\n");
+ TU_LOG_USBH("Get Configuration[0] Descriptor (9 bytes)\r\n");
TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, 9, process_enumeration, ENUM_GET_FULL_CONFIG_DESC), );
}
break;
@@ -1350,7 +1395,7 @@ static void process_enumeration(tuh_xfer_t* xfer)
// Get full configuration descriptor
uint8_t const config_idx = CONFIG_NUM - 1;
- TU_LOG2("Get Configuration[0] Descriptor\r\n");
+ TU_LOG_USBH("Get Configuration[0] Descriptor\r\n");
TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, total_len, process_enumeration, ENUM_SET_CONFIG), );
}
break;
@@ -1365,17 +1410,17 @@ static void process_enumeration(tuh_xfer_t* xfer)
case ENUM_CONFIG_DRIVER:
{
- TU_LOG2("Device configured\r\n");
+ TU_LOG_USBH("Device configured\r\n");
usbh_device_t* dev = get_device(daddr);
TU_ASSERT(dev, );
dev->configured = 1;
- // Start the Set Configuration process for interfaces (itf = DRVID_INVALID)
+ // Start the Set Configuration process for interfaces (itf = TUSB_INDEX_INVALID_8)
// Since driver can perform control transfer within its set_config, this is done asynchronously.
// The process continue with next interface when class driver complete its sequence with usbh_driver_set_config_complete()
- // TODO use separated API instead of using DRVID_INVALID
- usbh_driver_set_config_complete(daddr, DRVID_INVALID);
+ // TODO use separated API instead of using TUSB_INDEX_INVALID_8
+ usbh_driver_set_config_complete(daddr, TUSB_INDEX_INVALID_8);
}
break;
@@ -1396,13 +1441,16 @@ static bool enum_new_device(hcd_event_t* event)
{
// connected/disconnected directly with roothub
// wait until device is stable TODO non blocking
- osal_task_delay(RESET_DELAY);
+ hcd_port_reset(_dev0.rhport);
+ osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
+ // sof of controller may not running while resetting
+ hcd_port_reset_end( _dev0.rhport);
// device unplugged while delaying
if ( !hcd_port_connect_status(_dev0.rhport) ) return true;
_dev0.speed = hcd_port_speed_get(_dev0.rhport );
- TU_LOG2("%s Speed\r\n", tu_str_speed[_dev0.speed]);
+ TU_LOG_USBH("%s Speed\r\n", tu_str_speed[_dev0.speed]);
// fake transfer to kick-off the enumeration process
tuh_xfer_t xfer;
@@ -1429,12 +1477,6 @@ static bool enum_new_device(hcd_event_t* event)
return true;
}
-TU_ATTR_ALWAYS_INLINE
-static inline bool is_hub_addr(uint8_t daddr)
-{
- return daddr > CFG_TUH_DEVICE_MAX;
-}
-
static uint8_t get_new_address(bool is_hub)
{
uint8_t start;
@@ -1453,7 +1495,8 @@ static uint8_t get_new_address(bool is_hub)
{
if (!_usbh_devices[idx].connected) return (idx+1);
}
- return ADDR_INVALID;
+
+ return 0; // invalid address
}
static bool enum_request_set_addr(void)
@@ -1462,9 +1505,9 @@ static bool enum_request_set_addr(void)
// Get new address
uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
- TU_ASSERT(new_addr != ADDR_INVALID);
+ TU_ASSERT(new_addr != 0);
- TU_LOG2("Set Address = %d\r\n", new_addr);
+ TU_LOG_USBH("Set Address = %d\r\n", new_addr);
usbh_device_t* new_dev = get_device(new_addr);
@@ -1508,9 +1551,12 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
{
usbh_device_t* dev = get_device(dev_addr);
- uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength);
+ uint16_t const total_len = tu_le16toh(desc_cfg->wTotalLength);
+ uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + total_len;
uint8_t const* p_desc = tu_desc_next(desc_cfg);
+ TU_LOG_USBH("Parsing Configuration descriptor (wTotalLength = %u)\r\n", total_len);
+
// parse each interfaces
while( p_desc < desc_end )
{
@@ -1534,7 +1580,7 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
#if CFG_TUH_MIDI
// MIDI has 2 interfaces (Audio Control v1 + MIDIStreaming) but does not have IAD
- // manually increase the associated count
+ // manually force associated count = 2
if (1 == assoc_itf_count &&
TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
@@ -1544,48 +1590,49 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
}
#endif
- uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, assoc_itf_count, desc_end-p_desc);
- TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t));
-
- if (desc_itf->bInterfaceClass == TUSB_CLASS_HUB && dev->hub_addr != 0)
+#if CFG_TUH_CDC
+ // Some legacy CDC device does not use IAD but rather use device class as hint to combine 2 interfaces
+ // manually force associated count = 2
+ if (1 == assoc_itf_count &&
+ TUSB_CLASS_CDC == desc_itf->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == desc_itf->bInterfaceSubClass)
{
- // TODO Attach hub to Hub is not currently supported
- // skip this interface
- TU_LOG(USBH_DBG_LVL, "Only 1 level of HUB is supported\r\n");
+ assoc_itf_count = 2;
}
- else
+#endif
+
+ uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, assoc_itf_count, (uint16_t) (desc_end-p_desc));
+ TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t));
+
+ // Find driver for this interface
+ for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
{
- // Find driver for this interface
- uint8_t drv_id;
- for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
+ usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
+
+ if ( driver->open(dev->rhport, dev_addr, desc_itf, drv_len) )
{
- usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
+ // open successfully
+ TU_LOG_USBH(" %s opened\r\n", driver->name);
- if ( driver->open(dev->rhport, dev_addr, desc_itf, drv_len) )
+ // bind (associated) interfaces to found driver
+ for(uint8_t i=0; i<assoc_itf_count; i++)
{
- // open successfully
- TU_LOG2(" %s opened\r\n", driver->name);
-
- // bind (associated) interfaces to found driver
- for(uint8_t i=0; i<assoc_itf_count; i++)
- {
- uint8_t const itf_num = desc_itf->bInterfaceNumber+i;
+ uint8_t const itf_num = desc_itf->bInterfaceNumber+i;
- // Interface number must not be used already
- TU_ASSERT( DRVID_INVALID == dev->itf2drv[itf_num] );
- dev->itf2drv[itf_num] = drv_id;
- }
+ // Interface number must not be used already
+ TU_ASSERT( TUSB_INDEX_INVALID_8 == dev->itf2drv[itf_num] );
+ dev->itf2drv[itf_num] = drv_id;
+ }
- // bind all endpoints to found driver
- tu_edpt_bind_driver(dev->ep2drv, desc_itf, drv_len, drv_id);
+ // bind all endpoints to found driver
+ tu_edpt_bind_driver(dev->ep2drv, desc_itf, drv_len, drv_id);
- break; // exit driver find loop
- }
+ break; // exit driver find loop
}
if( drv_id >= USBH_CLASS_DRIVER_COUNT )
{
- TU_LOG(USBH_DBG_LVL, "Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n",
+ TU_LOG(USBH_DEBUG, "Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n",
desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol);
}
}
@@ -1604,12 +1651,13 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++)
{
// continue with next valid interface
- // TODO skip IAD binding interface such as CDCs
+ // IAD binding interface such as CDCs should return itf_num + 1 when complete
+ // with usbh_driver_set_config_complete()
uint8_t const drv_id = dev->itf2drv[itf_num];
- if (drv_id != DRVID_INVALID)
+ if (drv_id != TUSB_INDEX_INVALID_8)
{
usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
- TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num);
+ TU_LOG_USBH("%s set config: itf = %u\r\n", driver->name, itf_num);
driver->set_config(dev_addr, itf_num);
break;
}
@@ -1620,10 +1668,10 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
{
enum_full_complete();
-#if CFG_TUH_HUB
- // skip device mount callback for hub
- if ( !is_hub_addr(dev_addr) )
-#endif
+ if (is_hub_addr(dev_addr))
+ {
+ TU_LOG(USBH_DEBUG, "HUB address = %u is mounted\r\n", dev_addr);
+ }else
{
// Invoke callback if available
if (tuh_mount_cb) tuh_mount_cb(dev_addr);
@@ -1633,6 +1681,9 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
static void enum_full_complete(void)
{
+ // mark enumeration as complete
+ _dev0.enumerating = 0;
+
#if CFG_TUH_HUB
// get next hub status
if (_dev0.hub_addr) hub_edpt_status_xfer(_dev0.hub_addr);
diff --git a/src/host/usbh.h b/src/host/usbh.h
index c6d36fb7f..0f969a46a 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -32,7 +32,6 @@
#endif
#include "common/tusb_common.h"
-#include "hcd.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
@@ -46,20 +45,21 @@ typedef void (*tuh_xfer_cb_t)(tuh_xfer_t* xfer);
// Note1: layout and order of this will be changed in near future
// it is advised to initialize it using member name
-// Note2: not all field is available/meaningful in callback, some info is not saved by
-// usbh to save SRAM
+// Note2: not all field is available/meaningful in callback,
+// some info is not saved by usbh to save SRAM
struct tuh_xfer_s
{
uint8_t daddr;
uint8_t ep_addr;
-
+ uint8_t TU_RESERVED; // reserved
xfer_result_t result;
+
uint32_t actual_len; // excluding setup packet
union
{
tusb_control_request_t const* setup; // setup packet pointer if control transfer
- uint32_t buflen; // expected length if not control transfer (not available in callback)
+ uint32_t buflen; // expected length if not control transfer (not available in callback)
};
uint8_t* buffer; // not available in callback if not control transfer
@@ -69,24 +69,46 @@ struct tuh_xfer_s
// uint32_t timeout_ms; // place holder, not supported yet
};
+// Subject to change
+typedef struct
+{
+ uint8_t daddr;
+ tusb_desc_interface_t desc;
+} tuh_itf_info_t;
+
+// ConfigID for tuh_config()
+enum
+{
+ TUH_CFGID_RPI_PIO_USB_CONFIGURATION = OPT_MCU_RP2040 << 8 // cfg_param: pio_usb_configuration_t
+};
+
//--------------------------------------------------------------------+
// APPLICATION CALLBACK
//--------------------------------------------------------------------+
//TU_ATTR_WEAK uint8_t tuh_attach_cb (tusb_desc_device_t const *desc_device);
-// Invoked when device is mounted (configured)
+// Invoked when a device is mounted (configured)
TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
-/// Invoked when device is unmounted (bus reset/unplugged)
+// Invoked when a device failed to mount during enumeration process
+// TU_ATTR_WEAK void tuh_mount_failed_cb (uint8_t daddr);
+
+/// Invoked when a device is unmounted (detached)
TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr);
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
+// Configure host stack behavior with dynamic or port-specific parameters.
+// Should be called before tuh_init()
+// - cfg_id : configure ID (TBD)
+// - cfg_param: configure data, structure depends on the ID
+bool tuh_configure(uint8_t controller_id, uint32_t cfg_id, const void* cfg_param);
+
// Init host stack
-bool tuh_init(uint8_t rhport);
+bool tuh_init(uint8_t controller_id);
// Check if host stack is already initialized
bool tuh_inited(void);
@@ -103,8 +125,14 @@ void tuh_task(void)
tuh_task_ext(UINT32_MAX, false);
}
-// Interrupt handler, name alias to HCD
+// Check if there is pending events need processing by tuh_task()
+bool tuh_task_event_ready(void);
+
+#ifndef _TUSB_HCD_H_
extern void hcd_int_handler(uint8_t rhport);
+#endif
+
+// Interrupt handler, name alias to HCD
#define tuh_int_handler hcd_int_handler
bool tuh_vid_pid_get(uint8_t daddr, uint16_t* vid, uint16_t* pid);
@@ -150,9 +178,16 @@ bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep);
// Set Configuration (control transfer)
// config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1
// true on success, false if there is on-going control transfer or incorrect parameters
+// if complete_cb == NULL i.e blocking, user_data should be pointed to xfer_reuslt_t*
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+// Set Interface (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+// if complete_cb == NULL i.e blocking, user_data should be pointed to xfer_reuslt_t*
+bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
//--------------------------------------------------------------------+
// Descriptors Asynchronous (non-blocking)
//--------------------------------------------------------------------+
diff --git a/src/host/usbh_classdriver.h b/src/host/usbh_classdriver.h
index c156afea0..be9811641 100644
--- a/src/host/usbh_classdriver.h
+++ b/src/host/usbh_classdriver.h
@@ -29,11 +29,16 @@
#include "osal/osal.h"
#include "common/tusb_fifo.h"
+#include "common/tusb_private.h"
#ifdef __cplusplus
extern "C" {
#endif
+enum {
+ USBH_EPSIZE_BULK_MAX = (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
+};
+
//--------------------------------------------------------------------+
// Class Driver API
//--------------------------------------------------------------------+