diff --git a/examples/device/cdc_msc_hid/src/main.c b/examples/device/cdc_msc_hid/src/main.c index 53161fe83..1fa0fead1 100644 --- a/examples/device/cdc_msc_hid/src/main.c +++ b/examples/device/cdc_msc_hid/src/main.c @@ -38,7 +38,7 @@ /* Blink pattern * - 250 ms : device not mounted * - 1000 ms : device mounted - * - 2000 ms : device is suspended + * - 2500 ms : device is suspended */ static uint32_t blink_interval_ms = 250; @@ -210,6 +210,18 @@ void tud_umount_cb(void) blink_interval_ms = 250; } +// Invoked when device is suspended +void tud_suspend_cb(bool remote_wakeup_en) +{ + (void) remote_wakeup_en; + blink_interval_ms = 2500; +} + +void tud_resume_cb(void) +{ + blink_interval_ms = 1000; +} + //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ diff --git a/src/device/dcd.h b/src/device/dcd.h index f47c3714a..8dafef66d 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -48,7 +48,7 @@ typedef enum DCD_EVENT_SETUP_RECEIVED, DCD_EVENT_XFER_COMPLETE, - USBD_EVT_FUNC_CALL + USBD_EVENT_FUNC_CALL } dcd_eventid_t; typedef struct ATTR_ALIGNED(4) @@ -67,7 +67,7 @@ typedef struct ATTR_ALIGNED(4) uint32_t len; }xfer_complete; - // USBD_EVT_FUNC_CALL + // USBD_EVENT_FUNC_CALL struct { void (*func) (void*); void* param; diff --git a/src/device/usbd.c b/src/device/usbd.c index 7ca81e8d7..4089cf72c 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -40,14 +40,20 @@ // Device Data //--------------------------------------------------------------------+ typedef struct { - volatile uint8_t config_num; // 0 is non-configured ~ disconnect - bool remote_wakeup_en; + volatile uint8_t config_num; - uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) - uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid ) + struct ATTR_PACKED + { + volatile uint8_t connected : 1; + volatile uint8_t suspended : 1; + uint8_t remote_wakeup_en : 1; + }; // uint8_t ep_busy_mask[2]; // bit mask for busy endpoint uint8_t ep_stall_mask[2]; // bit mask for stalled endpoint + + uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) + uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid ) }usbd_device_t; static usbd_device_t _usbd_dev = { 0 }; @@ -246,8 +252,27 @@ void tud_task (void) if ( !osal_queue_receive(_usbd_q, &event) ) return; + // Skip event if device is not connected except BUS_RESET & UNPLUGGED & FUNC_CALL + if ( !(_usbd_dev.connected || event.event_id == DCD_EVENT_UNPLUGGED || + event.event_id == DCD_EVENT_BUS_RESET || event.event_id == USBD_EVENT_FUNC_CALL) ) + { + return; + } + switch ( event.event_id ) { + case DCD_EVENT_BUS_RESET: + usbd_reset(event.rhport); + _usbd_dev.connected = 1; // connected after bus reset + break; + + case DCD_EVENT_UNPLUGGED: + usbd_reset(event.rhport); + + // invoke callback + if (tud_umount_cb) tud_umount_cb(); + break; + case DCD_EVENT_SETUP_RECEIVED: // Process control request if ( !process_control_request(event.rhport, &event.setup_received) ) @@ -278,19 +303,15 @@ void tud_task (void) } break; - case DCD_EVENT_BUS_RESET: - usbd_reset(event.rhport); - // TODO remove since if task is too slow, we could clear the event of the new attached - osal_queue_reset(_usbd_q); + // NOTE: When unplugging device, the D+/D- state are unstable and can accidentally meet the + // SUSPEND condition ( Idle for 3ms ). Most likely when this happen both suspend and resume + // are submitted by DCD before the actual UNPLUGGED + case DCD_EVENT_SUSPEND: + if (tud_suspend_cb) tud_suspend_cb( _usbd_dev.remote_wakeup_en ); break; - case DCD_EVENT_UNPLUGGED: - usbd_reset(event.rhport); - // TODO remove since if task is too slow, we could clear the event of the new attached - osal_queue_reset(_usbd_q); - - // invoke callback - if (tud_umount_cb) tud_umount_cb(); + case DCD_EVENT_RESUME: + if (tud_resume_cb) tud_resume_cb(); break; case DCD_EVENT_SOF: @@ -303,7 +324,7 @@ void tud_task (void) } break; - case USBD_EVT_FUNC_CALL: + case USBD_EVENT_FUNC_CALL: if ( event.func_call.func ) event.func_call.func(event.func_call.param); break; @@ -555,9 +576,8 @@ static void const* get_descriptor(tusb_control_request_t const * p_request, uint }else { // out of range - /* The 0xEE index string is a Microsoft USB extension. - * It can be used to tell Windows what driver it should use for the device !!! - */ + // The 0xEE index string is a Microsoft USB extension. + // It can be used to tell Windows what driver it should use for the device !!! return NULL; } break; @@ -587,7 +607,8 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) break; case DCD_EVENT_UNPLUGGED: - _usbd_dev.config_num = 0; // mark disconnected + _usbd_dev.connected = 0; + _usbd_dev.config_num = 0; osal_queue_send(_usbd_q, event, in_isr); break; @@ -595,9 +616,23 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) // nothing to do now break; + // NOTE: When unplugging device, the D+/D- state are unstable and can accidentally meet the + // SUSPEND condition ( Idle for 3ms ). Most likely when this happen both suspend and resume + // are submitted by DCD before the actual UNPLUGGED case DCD_EVENT_SUSPEND: + if (_usbd_dev.connected ) // skip event if disconnected + { + _usbd_dev.suspended = 1; + osal_queue_send(_usbd_q, event, in_isr); + } + break; + case DCD_EVENT_RESUME: - osal_queue_send(_usbd_q, event, in_isr); + if (_usbd_dev.connected ) // skip event if disconnected + { + _usbd_dev.suspended = 0; + osal_queue_send(_usbd_q, event, in_isr); + } break; case DCD_EVENT_SETUP_RECEIVED: @@ -606,14 +641,14 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) case DCD_EVENT_XFER_COMPLETE: // skip zero-length control status complete event, should dcd notifies us. - if ( 0 == tu_edpt_number(event->xfer_complete.ep_addr) && event->xfer_complete.len == 0) break; + if ( (0 == tu_edpt_number(event->xfer_complete.ep_addr)) && (event->xfer_complete.len == 0) ) break; osal_queue_send(_usbd_q, event, in_isr); TU_ASSERT(event->xfer_complete.result == XFER_RESULT_SUCCESS,); break; - // Not an DCD event, just a convenient way to defer ISR function should we need - case USBD_EVT_FUNC_CALL: + // Not an DCD event, just a convenient way to defer ISR function should we need to + case USBD_EVENT_FUNC_CALL: osal_queue_send(_usbd_q, event, in_isr); break; @@ -683,7 +718,7 @@ void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) dcd_event_t event = { .rhport = 0, - .event_id = USBD_EVT_FUNC_CALL, + .event_id = USBD_EVENT_FUNC_CALL, }; event.func_call.func = func; diff --git a/src/osal/osal.h b/src/osal/osal.h index 7ceb22125..d6c33de3f 100644 --- a/src/osal/osal.h +++ b/src/osal/osal.h @@ -61,7 +61,6 @@ typedef void (*osal_task_func_t)( void * ); * osal_queue_t osal_queue_create(osal_queue_def_t* qdef) * osal_queue_receive (osal_queue_t const queue_hdl, void *p_data, uint32_t msec, uint32_t *p_error) * bool osal_queue_send(osal_queue_t const queue_hdl, void const * data, bool in_isr) - * osal_queue_reset() * * Semaphore * osal_semaphore_def_t, osal_semaphore_t diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h index 639501bbf..973a18546 100644 --- a/src/osal/osal_freertos.h +++ b/src/osal/osal_freertos.h @@ -125,11 +125,6 @@ static inline bool osal_queue_send(osal_queue_t const queue_hdl, void const * da return in_isr ? xQueueSendToBackFromISR(queue_hdl, data, NULL) : xQueueSendToBack(queue_hdl, data, OSAL_TIMEOUT_WAIT_FOREVER); } -static inline void osal_queue_reset(osal_queue_t const queue_hdl) -{ - xQueueReset(queue_hdl); -} - #ifdef __cplusplus } #endif diff --git a/src/osal/osal_mynewt.h b/src/osal/osal_mynewt.h index 9fa9f541e..6f3d05325 100644 --- a/src/osal/osal_mynewt.h +++ b/src/osal/osal_mynewt.h @@ -161,11 +161,6 @@ static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, b return true; } -static inline void osal_queue_reset(osal_queue_t const queue_hdl) -{ - // TODO implement later -} - #ifdef __cplusplus } #endif diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h index 6144f3e5c..d1d365bbe 100644 --- a/src/osal/osal_none.h +++ b/src/osal/osal_none.h @@ -190,13 +190,6 @@ static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, b return success; } -static inline void osal_queue_reset(osal_queue_t const qhdl) -{ - // tusb_hal_int_disable_all(); - tu_fifo_clear(&qhdl->ff); - // tusb_hal_int_enable_all(); -} - #ifdef __cplusplus } #endif diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index edaef7cca..e7e9ae016 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -205,13 +205,14 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num) { (void) rhport; (void) config_num; - // Nothing to do - // Clear current pending + // Enable usbevent for suspend and resume detection + // Since the bus signal D+/D- are stable from now on. + + // Clear current pending first NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE; NRF_USBD->EVENTS_USBEVENT = 0; - // Enable usb event for suspend and resume NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk; } @@ -377,19 +378,18 @@ void USBD_IRQHandler(void) if ( int_status & USBD_INTEN_USBEVENT_Msk ) { - uint32_t const evt_cause = NRF_USBD->EVENTCAUSE; + uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & (USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk); + NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt - if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk ) + if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk ) { dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); } - - if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk ) + + if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk ) { dcd_event_bus_signal(0, DCD_EVENT_RESUME , true); } - - NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt } if ( int_status & EDPT_END_ALL_MASK ) @@ -397,7 +397,7 @@ void USBD_IRQHandler(void) // DMA complete move data from SRAM -> Endpoint edpt_dma_end(); } - + // Setup tokens are specific to the Control endpoint. if ( int_status & USBD_INTEN_EP0SETUP_Msk ) {