mirror of
https://github.com/hathach/tinyusb.git
synced 2025-04-16 05:42:56 +00:00
refactor and integrate usbh control xfer back to usbh.c
fix enumeration with hub when reset port before set address
This commit is contained in:
parent
b9ca301527
commit
171d021ab5
@ -80,7 +80,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
|
||||
// If caller does not make any transfer, it must release endpoint for others.
|
||||
bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Release an endpoint without submitting a transfer
|
||||
// Release claimed endpoint without submitting a transfer
|
||||
bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Check if endpoint is busy transferring
|
||||
|
447
src/host/usbh.c
447
src/host/usbh.c
@ -72,7 +72,6 @@ typedef struct
|
||||
volatile uint8_t suspended : 1;
|
||||
};
|
||||
|
||||
uint8_t control_stage; // state of control transfer
|
||||
} usbh_dev0_t;
|
||||
|
||||
typedef struct {
|
||||
@ -91,13 +90,12 @@ typedef struct {
|
||||
volatile uint8_t suspended : 1;
|
||||
};
|
||||
|
||||
uint8_t control_stage; // state of control transfer
|
||||
|
||||
// Device Descriptor
|
||||
uint8_t ep0_size;
|
||||
|
||||
uint16_t vid;
|
||||
uint16_t pid;
|
||||
|
||||
uint8_t ep0_size;
|
||||
uint8_t i_manufacturer;
|
||||
uint8_t i_product;
|
||||
uint8_t i_serial;
|
||||
@ -113,6 +111,14 @@ typedef struct {
|
||||
|
||||
} usbh_device_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tusb_control_request_t request TU_ATTR_ALIGNED(4);
|
||||
uint8_t* buffer;
|
||||
tuh_control_complete_cb_t complete_cb;
|
||||
|
||||
uint8_t daddr;
|
||||
} usbh_control_xfer_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
@ -211,8 +217,24 @@ 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[TOTAL_DEVICES];
|
||||
static osal_mutex_t _usbh_mutex[TOTAL_DEVICES];
|
||||
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);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define usbh_lock()
|
||||
#define usbh_unlock()
|
||||
|
||||
#endif
|
||||
|
||||
// Event queue
|
||||
@ -223,6 +245,15 @@ static osal_queue_t _usbh_q;
|
||||
CFG_TUSB_MEM_SECTION CFG_TUSB_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
|
||||
{
|
||||
usbh_control_xfer_t xfer;
|
||||
uint8_t stage;
|
||||
}_ctrl_xfer;
|
||||
|
||||
//------------- Helper Function -------------//
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
@ -235,10 +266,7 @@ static inline usbh_device_t* get_device(uint8_t dev_addr)
|
||||
static bool enum_new_device(hcd_event_t* event);
|
||||
static void process_device_unplugged(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);
|
||||
|
||||
// from usbh_control.c
|
||||
extern bool usbh_control_xfer (uint8_t daddr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb);
|
||||
extern uint8_t usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, uint8_t* stage, xfer_result_t result, uint32_t xferred_bytes);
|
||||
static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC API (Parameter Verification is required)
|
||||
@ -276,24 +304,6 @@ void osal_task_delay(uint32_t msec)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool tuh_control_xfer (uint8_t daddr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb)
|
||||
{
|
||||
TU_LOG2("[%u:%u] %s: ", usbh_get_rhport(daddr), daddr, request->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[request->bRequest] : "Unknown Request");
|
||||
TU_LOG2_VAR(request);
|
||||
TU_LOG2("\r\n");
|
||||
|
||||
if (daddr)
|
||||
{
|
||||
get_device(daddr)->control_stage = CONTROL_STAGE_SETUP;
|
||||
}else
|
||||
{
|
||||
_dev0.control_stage = CONTROL_STAGE_SETUP;
|
||||
}
|
||||
|
||||
return usbh_control_xfer(daddr, request, buffer, complete_cb);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
@ -449,26 +459,30 @@ bool tuh_init(uint8_t rhport)
|
||||
|
||||
TU_LOG2("USBH init\r\n");
|
||||
TU_LOG2_INT(sizeof(usbh_device_t));
|
||||
TU_LOG2_INT(sizeof(hcd_event_t));
|
||||
TU_LOG2_INT(sizeof(usbh_control_xfer_t));
|
||||
|
||||
//------------- Enumeration & Reporter Task init -------------//
|
||||
// Event queue
|
||||
_usbh_q = osal_queue_create( &_usbh_qdef );
|
||||
TU_ASSERT(_usbh_q != NULL);
|
||||
|
||||
//------------- Semaphore, Mutex for Control Pipe -------------//
|
||||
#if TUSB_OPT_MUTEX
|
||||
// Mutex
|
||||
_usbh_mutex = osal_mutex_create(&_usbh_mutexdef);
|
||||
TU_ASSERT(_usbh_mutex);
|
||||
#endif
|
||||
|
||||
// Device
|
||||
tu_memclr(&_dev0, sizeof(_dev0));
|
||||
tu_memclr(_usbh_devices, sizeof(_usbh_devices));
|
||||
tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
|
||||
|
||||
for(uint8_t i=0; i<TOTAL_DEVICES; i++)
|
||||
{
|
||||
clear_device(&_usbh_devices[i]);
|
||||
|
||||
#if TUSB_OPT_MUTEX
|
||||
_usbh_mutex[i] = osal_mutex_create(&_usbh_mutexdef[i]);
|
||||
TU_ASSERT(_usbh_mutex[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Class drivers init
|
||||
// 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);
|
||||
@ -546,7 +560,7 @@ void tuh_task(void)
|
||||
{
|
||||
// device 0 only has control endpoint
|
||||
TU_ASSERT(epnum == 0, );
|
||||
usbh_control_xfer_cb(event.dev_addr, ep_addr, &_dev0.control_stage, event.xfer_complete.result, event.xfer_complete.len);
|
||||
usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -556,7 +570,7 @@ void tuh_task(void)
|
||||
|
||||
if ( 0 == epnum )
|
||||
{
|
||||
usbh_control_xfer_cb(event.dev_addr, ep_addr, &dev->control_stage, event.xfer_complete.result, event.xfer_complete.len);
|
||||
usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
|
||||
}else
|
||||
{
|
||||
uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
|
||||
@ -683,81 +697,6 @@ void hcd_event_device_remove(uint8_t hostid, bool in_isr)
|
||||
hcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
//------------- find the all devices (star-network) under port that is unplugged -------------//
|
||||
// TODO mark as disconnected in ISR, also handle dev0
|
||||
for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ )
|
||||
{
|
||||
usbh_device_t* dev = &_usbh_devices[dev_id];
|
||||
uint8_t const dev_addr = dev_id+1;
|
||||
|
||||
// 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) &&
|
||||
dev->connected)
|
||||
{
|
||||
// Invoke callback before close 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);
|
||||
usbh_class_drivers[drv_id].close(dev_addr);
|
||||
}
|
||||
|
||||
hcd_device_close(rhport, dev_addr);
|
||||
clear_device(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
static uint8_t get_new_address(bool is_hub)
|
||||
{
|
||||
uint8_t const start = (is_hub ? CFG_TUH_DEVICE_MAX : 0) + 1;
|
||||
uint8_t const count = (is_hub ? CFG_TUH_HUB : CFG_TUH_DEVICE_MAX);
|
||||
|
||||
for (uint8_t i=0; i < count; i++)
|
||||
{
|
||||
uint8_t const addr = start + i;
|
||||
if (!get_device(addr)->connected) return addr;
|
||||
}
|
||||
return ADDR_INVALID;
|
||||
}
|
||||
|
||||
void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
|
||||
{
|
||||
usbh_device_t* dev = get_device(dev_addr);
|
||||
|
||||
for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++)
|
||||
{
|
||||
// continue with next valid interface
|
||||
// TODO skip IAD binding interface such as CDCs
|
||||
uint8_t const drv_id = dev->itf2drv[itf_num];
|
||||
if (drv_id != DRVID_INVALID)
|
||||
{
|
||||
usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
|
||||
TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num);
|
||||
driver->set_config(dev_addr, itf_num);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// all interface are configured
|
||||
if (itf_num == CFG_TUH_INTERFACE_MAX)
|
||||
{
|
||||
// Invoke callback if available
|
||||
if (tuh_mount_cb) tuh_mount_cb(dev_addr);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
@ -774,7 +713,7 @@ bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr)
|
||||
tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir];
|
||||
|
||||
#if TUSB_OPT_MUTEX
|
||||
return tu_edpt_claim(ep_state, _usbh_mutex[dev_addr-1]);
|
||||
return tu_edpt_claim(ep_state, _usbh_mutex);
|
||||
#else
|
||||
return tu_edpt_claim(ep_state, NULL);
|
||||
#endif
|
||||
@ -792,7 +731,7 @@ bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
|
||||
tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir];
|
||||
|
||||
#if TUSB_OPT_MUTEX
|
||||
return tu_edpt_release(ep_state, _usbh_mutex[dev_addr-1]);
|
||||
return tu_edpt_release(ep_state, _usbh_mutex);
|
||||
#else
|
||||
return tu_edpt_release(ep_state, NULL);
|
||||
#endif
|
||||
@ -865,6 +804,183 @@ bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
|
||||
return dev->ep_status[epnum][dir].busy;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control transfer
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tuh_control_xfer (uint8_t daddr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb)
|
||||
{
|
||||
// pre-check to help reducing mutex lock
|
||||
TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
|
||||
|
||||
usbh_lock();
|
||||
|
||||
bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
|
||||
if (is_idle) _ctrl_xfer.stage = CONTROL_STAGE_SETUP;
|
||||
|
||||
usbh_unlock();
|
||||
|
||||
TU_VERIFY(is_idle);
|
||||
|
||||
const uint8_t rhport = usbh_get_rhport(daddr);
|
||||
|
||||
TU_LOG2("[%u:%u] %s: ", rhport, daddr, request->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[request->bRequest] : "Unknown Request");
|
||||
TU_LOG2_VAR(request);
|
||||
TU_LOG2("\r\n");
|
||||
|
||||
_ctrl_xfer.xfer.request = (*request);
|
||||
_ctrl_xfer.xfer.buffer = buffer;
|
||||
_ctrl_xfer.xfer.complete_cb = complete_cb;
|
||||
_ctrl_xfer.xfer.daddr = daddr;
|
||||
|
||||
return hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.xfer.request);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void set_control_xfer_stage(uint8_t stage)
|
||||
{
|
||||
usbh_lock();
|
||||
_ctrl_xfer.stage = stage;
|
||||
usbh_unlock();
|
||||
}
|
||||
|
||||
static void _xfer_complete(uint8_t dev_addr, xfer_result_t result)
|
||||
{
|
||||
TU_LOG2("\r\n");
|
||||
|
||||
usbh_lock();
|
||||
_ctrl_xfer.stage = CONTROL_STAGE_IDLE;
|
||||
usbh_unlock();
|
||||
|
||||
if (_ctrl_xfer.xfer.complete_cb) _ctrl_xfer.xfer.complete_cb(dev_addr, &_ctrl_xfer.xfer.request, result);
|
||||
}
|
||||
|
||||
static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
(void) ep_addr;
|
||||
(void) xferred_bytes;
|
||||
|
||||
const uint8_t rhport = usbh_get_rhport(dev_addr);
|
||||
tusb_control_request_t const * request = &_ctrl_xfer.xfer.request;
|
||||
|
||||
if (XFER_RESULT_SUCCESS != result)
|
||||
{
|
||||
TU_LOG2("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED");
|
||||
|
||||
// terminate transfer if any stage failed
|
||||
_xfer_complete(dev_addr, result);
|
||||
}else
|
||||
{
|
||||
switch(_ctrl_xfer.stage)
|
||||
{
|
||||
case CONTROL_STAGE_SETUP:
|
||||
if (request->wLength)
|
||||
{
|
||||
// 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.xfer.buffer, request->wLength);
|
||||
}
|
||||
__attribute__((fallthrough));
|
||||
|
||||
case CONTROL_STAGE_DATA:
|
||||
if (request->wLength)
|
||||
{
|
||||
TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr);
|
||||
TU_LOG2_MEM(_ctrl_xfer.xfer.buffer, request->wLength, 2);
|
||||
}
|
||||
|
||||
// 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);
|
||||
break;
|
||||
|
||||
case CONTROL_STAGE_ACK:
|
||||
_xfer_complete(dev_addr, result);
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// a device unplugged from rhport:hub_addr:hub_port
|
||||
static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port)
|
||||
{
|
||||
//------------- find the all devices (star-network) under port that is unplugged -------------//
|
||||
// TODO mark as disconnected in ISR, also handle dev0
|
||||
for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ )
|
||||
{
|
||||
usbh_device_t* dev = &_usbh_devices[dev_id];
|
||||
uint8_t const dev_addr = dev_id+1;
|
||||
|
||||
// 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) &&
|
||||
dev->connected)
|
||||
{
|
||||
// Invoke callback before close 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);
|
||||
usbh_class_drivers[drv_id].close(dev_addr);
|
||||
}
|
||||
|
||||
hcd_device_close(rhport, dev_addr);
|
||||
clear_device(dev);
|
||||
// abort on-going control xfer if any
|
||||
if (_ctrl_xfer.xfer.daddr == dev_addr) set_control_xfer_stage(CONTROL_STAGE_IDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static uint8_t get_new_address(bool is_hub)
|
||||
{
|
||||
uint8_t const start = (is_hub ? CFG_TUH_DEVICE_MAX : 0) + 1;
|
||||
uint8_t const count = (is_hub ? CFG_TUH_HUB : CFG_TUH_DEVICE_MAX);
|
||||
|
||||
for (uint8_t i=0; i < count; i++)
|
||||
{
|
||||
uint8_t const addr = start + i;
|
||||
if (!get_device(addr)->connected) return addr;
|
||||
}
|
||||
return ADDR_INVALID;
|
||||
}
|
||||
|
||||
void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
|
||||
{
|
||||
usbh_device_t* dev = get_device(dev_addr);
|
||||
|
||||
for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++)
|
||||
{
|
||||
// continue with next valid interface
|
||||
// TODO skip IAD binding interface such as CDCs
|
||||
uint8_t const drv_id = dev->itf2drv[itf_num];
|
||||
if (drv_id != DRVID_INVALID)
|
||||
{
|
||||
usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
|
||||
TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num);
|
||||
driver->set_config(dev_addr, itf_num);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// all interface are configured
|
||||
if (itf_num == CFG_TUH_INTERFACE_MAX)
|
||||
{
|
||||
// Invoke callback if available
|
||||
if (tuh_mount_cb) tuh_mount_cb(dev_addr);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Enumeration Process
|
||||
// is a lengthy process with a series of control transfer to configure
|
||||
@ -886,43 +1002,16 @@ static bool enum_set_config_complete (uint8_t dev_addr, tusb_control_
|
||||
static bool parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
|
||||
|
||||
#if CFG_TUH_HUB
|
||||
static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
|
||||
{
|
||||
(void) dev_addr; (void) request;
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
enum_request_addr0_device_desc();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
|
||||
{
|
||||
(void) dev_addr; (void) request;
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
// Enum sequence:
|
||||
// New device (reset on the way) -> Get Status 0 -> Clear Reset 0 -> Get 8byte Device Descriptor
|
||||
// -> Port Reset 1 -> reset delay -> Get Status 1 -> Clear Reset 1 -> queue hub interrupt endpoint
|
||||
|
||||
enum_request_set_addr();
|
||||
|
||||
// done with hub, waiting for next data on status pipe
|
||||
(void) hub_status_pipe_queue( _dev0.hub_addr );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
|
||||
{
|
||||
(void) dev_addr; (void) request;
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
|
||||
hub_port_status_response_t port_status;
|
||||
memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
|
||||
|
||||
// Acknowledge Port Reset Change if Reset Successful
|
||||
if (port_status.change.reset)
|
||||
{
|
||||
TU_ASSERT( hub_port_clear_feature(_dev0.hub_addr, _dev0.hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset1_complete) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
|
||||
static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
|
||||
static bool enum_hub_set_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
|
||||
static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
|
||||
static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
|
||||
|
||||
static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
|
||||
{
|
||||
@ -949,6 +1038,57 @@ static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
|
||||
{
|
||||
(void) dev_addr; (void) request;
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
enum_request_addr0_device_desc();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enum_hub_set_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
|
||||
{
|
||||
(void) dev_addr; (void) request;
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
|
||||
osal_task_delay(RESET_DELAY);
|
||||
|
||||
TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status1_complete) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
|
||||
{
|
||||
(void) dev_addr; (void) request;
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
|
||||
hub_port_status_response_t port_status;
|
||||
memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
|
||||
|
||||
// Acknowledge Port Reset Change if Reset Successful
|
||||
if (port_status.change.reset)
|
||||
{
|
||||
TU_ASSERT( hub_port_clear_feature(_dev0.hub_addr, _dev0.hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset1_complete) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
|
||||
{
|
||||
(void) dev_addr; (void) request;
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
|
||||
enum_request_set_addr();
|
||||
|
||||
// done with hub, waiting for next data on status pipe
|
||||
(void) hub_status_pipe_queue( _dev0.hub_addr );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // hub
|
||||
|
||||
static bool enum_new_device(hcd_event_t* event)
|
||||
@ -1030,12 +1170,7 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r
|
||||
else
|
||||
{
|
||||
// after RESET_DELAY the hub_port_reset() already complete
|
||||
TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, NULL) );
|
||||
osal_task_delay(RESET_DELAY);
|
||||
|
||||
tuh_task(); // FIXME temporarily to clean up port_reset control transfer
|
||||
|
||||
TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status1_complete) );
|
||||
TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, enum_hub_set_reset1_complete) );
|
||||
}
|
||||
#endif // hub
|
||||
|
||||
|
@ -73,6 +73,7 @@ bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_
|
||||
// If caller does not make any transfer, it must release endpoint for others.
|
||||
bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr);
|
||||
|
||||
// Release claimed endpoint without submitting a transfer
|
||||
bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr);
|
||||
|
||||
// Check if endpoint transferring is complete
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUH_ENABLED
|
||||
#if CFG_TUH_ENABLED && 0
|
||||
|
||||
#include "tusb.h"
|
||||
#include "usbh_classdriver.h"
|
||||
@ -36,86 +36,50 @@ typedef struct
|
||||
tusb_control_request_t request TU_ATTR_ALIGNED(4);
|
||||
uint8_t* buffer;
|
||||
tuh_control_complete_cb_t complete_cb;
|
||||
|
||||
uint8_t daddr;
|
||||
} usbh_control_xfer_t;
|
||||
|
||||
static usbh_control_xfer_t _ctrl_xfer;
|
||||
static usbh_control_xfer_t _xfer;
|
||||
static uint8_t _stage = CONTROL_STAGE_IDLE;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t usbh_control_xfer_stage(void)
|
||||
{
|
||||
return _stage;
|
||||
}
|
||||
|
||||
bool usbh_control_xfer (uint8_t dev_addr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb)
|
||||
{
|
||||
// TODO need to claim the endpoint first
|
||||
const uint8_t rhport = usbh_get_rhport(dev_addr);
|
||||
|
||||
_ctrl_xfer.request = (*request);
|
||||
_ctrl_xfer.buffer = buffer;
|
||||
_ctrl_xfer.complete_cb = complete_cb;
|
||||
_ctrl_xfer.xfer.daddr = dev_addr;
|
||||
_ctrl_xfer.xfer.request = (*request);
|
||||
_ctrl_xfer.xfer.buffer = buffer;
|
||||
_ctrl_xfer.xfer.complete_cb = complete_cb;
|
||||
|
||||
_stage = CONTROL_STAGE_SETUP;
|
||||
|
||||
// Send setup packet
|
||||
TU_ASSERT( hcd_setup_send(rhport, dev_addr, (uint8_t const*) &_ctrl_xfer.request) );
|
||||
TU_ASSERT( hcd_setup_send(rhport, dev_addr, (uint8_t const*) &_ctrl_xfer.xfer.request) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void usbh_control_xfer_abort(uint8_t dev_addr)
|
||||
{
|
||||
if (_ctrl_xfer.xfer.daddr == dev_addr) _stage = CONTROL_STAGE_IDLE;
|
||||
}
|
||||
|
||||
static void _xfer_complete(uint8_t dev_addr, xfer_result_t result)
|
||||
{
|
||||
TU_LOG2("\r\n");
|
||||
if (_ctrl_xfer.complete_cb) _ctrl_xfer.complete_cb(dev_addr, &_ctrl_xfer.request, result);
|
||||
if (_ctrl_xfer.xfer.complete_cb) _ctrl_xfer.xfer.complete_cb(dev_addr, &_ctrl_xfer.xfer.request, result);
|
||||
}
|
||||
|
||||
bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, uint8_t* stage, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
(void) ep_addr;
|
||||
(void) xferred_bytes;
|
||||
|
||||
const uint8_t rhport = usbh_get_rhport(dev_addr);
|
||||
tusb_control_request_t const * request = &_ctrl_xfer.request;
|
||||
|
||||
if (XFER_RESULT_SUCCESS != result)
|
||||
{
|
||||
TU_LOG2("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED");
|
||||
|
||||
// terminate transfer if any stage failed
|
||||
*stage = CONTROL_STAGE_IDLE;
|
||||
_xfer_complete(dev_addr, result);
|
||||
}else
|
||||
{
|
||||
switch(*stage)
|
||||
{
|
||||
case CONTROL_STAGE_SETUP:
|
||||
*stage = CONTROL_STAGE_DATA;
|
||||
if (request->wLength)
|
||||
{
|
||||
// DATA stage: initial data toggle is always 1
|
||||
hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength);
|
||||
return true;
|
||||
}
|
||||
__attribute__((fallthrough));
|
||||
|
||||
case CONTROL_STAGE_DATA:
|
||||
*stage = CONTROL_STAGE_ACK;
|
||||
if (request->wLength)
|
||||
{
|
||||
TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr);
|
||||
TU_LOG2_MEM(_ctrl_xfer.buffer, request->wLength, 2);
|
||||
}
|
||||
|
||||
// ACK stage: toggle is always 1
|
||||
hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0);
|
||||
break;
|
||||
|
||||
case CONTROL_STAGE_ACK:
|
||||
*stage = CONTROL_STAGE_IDLE;
|
||||
_xfer_complete(dev_addr, result);
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -111,7 +111,7 @@ typedef struct
|
||||
|
||||
typedef osal_queue_def_t* osal_queue_t;
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
// _int_set is used as mutex in OS NONE (disable/enable USB ISR)
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
uint8_t _name##_buf[_depth*sizeof(_type)]; \
|
||||
osal_queue_def_t _name = { \
|
||||
|
Loading…
x
Reference in New Issue
Block a user