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.
|
// If caller does not make any transfer, it must release endpoint for others.
|
||||||
bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr);
|
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);
|
bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr);
|
||||||
|
|
||||||
// Check if endpoint is busy transferring
|
// 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;
|
volatile uint8_t suspended : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t control_stage; // state of control transfer
|
|
||||||
} usbh_dev0_t;
|
} usbh_dev0_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -91,13 +90,12 @@ typedef struct {
|
|||||||
volatile uint8_t suspended : 1;
|
volatile uint8_t suspended : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t control_stage; // state of control transfer
|
|
||||||
|
|
||||||
// Device Descriptor
|
// Device Descriptor
|
||||||
|
uint8_t ep0_size;
|
||||||
|
|
||||||
uint16_t vid;
|
uint16_t vid;
|
||||||
uint16_t pid;
|
uint16_t pid;
|
||||||
|
|
||||||
uint8_t ep0_size;
|
|
||||||
uint8_t i_manufacturer;
|
uint8_t i_manufacturer;
|
||||||
uint8_t i_product;
|
uint8_t i_product;
|
||||||
uint8_t i_serial;
|
uint8_t i_serial;
|
||||||
@ -113,6 +111,14 @@ typedef struct {
|
|||||||
|
|
||||||
} usbh_device_t;
|
} 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
|
// 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
|
// Mutex for claiming endpoint, only needed when using with preempted RTOS
|
||||||
#if TUSB_OPT_MUTEX
|
#if TUSB_OPT_MUTEX
|
||||||
static osal_mutex_def_t _usbh_mutexdef[TOTAL_DEVICES];
|
static osal_mutex_def_t _usbh_mutexdef;
|
||||||
static osal_mutex_t _usbh_mutex[TOTAL_DEVICES];
|
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
|
#endif
|
||||||
|
|
||||||
// Event queue
|
// Event queue
|
||||||
@ -223,6 +245,15 @@ static osal_queue_t _usbh_q;
|
|||||||
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN
|
||||||
static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
|
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 -------------//
|
//------------- Helper Function -------------//
|
||||||
|
|
||||||
TU_ATTR_ALWAYS_INLINE
|
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 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 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);
|
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);
|
||||||
// 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);
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
// PUBLIC API (Parameter Verification is required)
|
// PUBLIC API (Parameter Verification is required)
|
||||||
@ -276,24 +304,6 @@ void osal_task_delay(uint32_t msec)
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
// Descriptors
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
@ -449,26 +459,30 @@ bool tuh_init(uint8_t rhport)
|
|||||||
|
|
||||||
TU_LOG2("USBH init\r\n");
|
TU_LOG2("USBH init\r\n");
|
||||||
TU_LOG2_INT(sizeof(usbh_device_t));
|
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 );
|
_usbh_q = osal_queue_create( &_usbh_qdef );
|
||||||
TU_ASSERT(_usbh_q != NULL);
|
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(&_dev0, sizeof(_dev0));
|
||||||
tu_memclr(_usbh_devices, sizeof(_usbh_devices));
|
tu_memclr(_usbh_devices, sizeof(_usbh_devices));
|
||||||
|
tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
|
||||||
|
|
||||||
for(uint8_t i=0; i<TOTAL_DEVICES; i++)
|
for(uint8_t i=0; i<TOTAL_DEVICES; i++)
|
||||||
{
|
{
|
||||||
clear_device(&_usbh_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++)
|
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_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
|
// device 0 only has control endpoint
|
||||||
TU_ASSERT(epnum == 0, );
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -556,7 +570,7 @@ void tuh_task(void)
|
|||||||
|
|
||||||
if ( 0 == epnum )
|
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
|
}else
|
||||||
{
|
{
|
||||||
uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
|
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);
|
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
|
// 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];
|
tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir];
|
||||||
|
|
||||||
#if TUSB_OPT_MUTEX
|
#if TUSB_OPT_MUTEX
|
||||||
return tu_edpt_claim(ep_state, _usbh_mutex[dev_addr-1]);
|
return tu_edpt_claim(ep_state, _usbh_mutex);
|
||||||
#else
|
#else
|
||||||
return tu_edpt_claim(ep_state, NULL);
|
return tu_edpt_claim(ep_state, NULL);
|
||||||
#endif
|
#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];
|
tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir];
|
||||||
|
|
||||||
#if TUSB_OPT_MUTEX
|
#if TUSB_OPT_MUTEX
|
||||||
return tu_edpt_release(ep_state, _usbh_mutex[dev_addr-1]);
|
return tu_edpt_release(ep_state, _usbh_mutex);
|
||||||
#else
|
#else
|
||||||
return tu_edpt_release(ep_state, NULL);
|
return tu_edpt_release(ep_state, NULL);
|
||||||
#endif
|
#endif
|
||||||
@ -865,6 +804,183 @@ bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
|
|||||||
return dev->ep_status[epnum][dir].busy;
|
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
|
// Enumeration Process
|
||||||
// is a lengthy process with a series of control transfer to configure
|
// 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);
|
static bool parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
|
||||||
|
|
||||||
#if CFG_TUH_HUB
|
#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)
|
// Enum sequence:
|
||||||
{
|
// New device (reset on the way) -> Get Status 0 -> Clear Reset 0 -> Get 8byte Device Descriptor
|
||||||
(void) dev_addr; (void) request;
|
// -> Port Reset 1 -> reset delay -> Get Status 1 -> Clear Reset 1 -> queue hub interrupt endpoint
|
||||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
|
||||||
|
|
||||||
enum_request_set_addr();
|
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);
|
||||||
// done with hub, waiting for next data on status pipe
|
static bool enum_hub_set_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
|
||||||
(void) hub_status_pipe_queue( _dev0.hub_addr );
|
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);
|
||||||
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_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;
|
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
|
#endif // hub
|
||||||
|
|
||||||
static bool enum_new_device(hcd_event_t* event)
|
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
|
else
|
||||||
{
|
{
|
||||||
// after RESET_DELAY the hub_port_reset() already complete
|
// after RESET_DELAY the hub_port_reset() already complete
|
||||||
TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, NULL) );
|
TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, enum_hub_set_reset1_complete) );
|
||||||
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) );
|
|
||||||
}
|
}
|
||||||
#endif // hub
|
#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.
|
// 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);
|
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);
|
bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr);
|
||||||
|
|
||||||
// Check if endpoint transferring is complete
|
// Check if endpoint transferring is complete
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
#include "tusb_option.h"
|
#include "tusb_option.h"
|
||||||
|
|
||||||
#if CFG_TUH_ENABLED
|
#if CFG_TUH_ENABLED && 0
|
||||||
|
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "usbh_classdriver.h"
|
#include "usbh_classdriver.h"
|
||||||
@ -36,86 +36,50 @@ typedef struct
|
|||||||
tusb_control_request_t request TU_ATTR_ALIGNED(4);
|
tusb_control_request_t request TU_ATTR_ALIGNED(4);
|
||||||
uint8_t* buffer;
|
uint8_t* buffer;
|
||||||
tuh_control_complete_cb_t complete_cb;
|
tuh_control_complete_cb_t complete_cb;
|
||||||
|
|
||||||
|
uint8_t daddr;
|
||||||
} usbh_control_xfer_t;
|
} 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
|
// 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)
|
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
|
// TODO need to claim the endpoint first
|
||||||
const uint8_t rhport = usbh_get_rhport(dev_addr);
|
const uint8_t rhport = usbh_get_rhport(dev_addr);
|
||||||
|
|
||||||
_ctrl_xfer.request = (*request);
|
_ctrl_xfer.xfer.daddr = dev_addr;
|
||||||
_ctrl_xfer.buffer = buffer;
|
_ctrl_xfer.xfer.request = (*request);
|
||||||
_ctrl_xfer.complete_cb = complete_cb;
|
_ctrl_xfer.xfer.buffer = buffer;
|
||||||
|
_ctrl_xfer.xfer.complete_cb = complete_cb;
|
||||||
|
|
||||||
|
_stage = CONTROL_STAGE_SETUP;
|
||||||
|
|
||||||
// Send setup packet
|
// 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;
|
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)
|
static void _xfer_complete(uint8_t dev_addr, xfer_result_t result)
|
||||||
{
|
{
|
||||||
TU_LOG2("\r\n");
|
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
|
#endif
|
||||||
|
@ -111,7 +111,7 @@ typedef struct
|
|||||||
|
|
||||||
typedef osal_queue_def_t* osal_queue_t;
|
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) \
|
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||||
uint8_t _name##_buf[_depth*sizeof(_type)]; \
|
uint8_t _name##_buf[_depth*sizeof(_type)]; \
|
||||||
osal_queue_def_t _name = { \
|
osal_queue_def_t _name = { \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user