diff --git a/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhyHw.h b/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhyHw.h index 1e62ab146e..75e5ea6f33 100644 --- a/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhyHw.h +++ b/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhyHw.h @@ -68,6 +68,7 @@ private: USBPhyEvents *events; bool sof_enabled; + bool connect_enabled; typedef enum usb_hw_event_type_t { USB_HW_EVENT_NONE = 0, @@ -96,6 +97,11 @@ private: // Returns the corresponding enumeration given an mbed endpoint number static nrf_drv_usbd_ep_t get_nordic_endpoint(usb_ep_t endpoint); + void _reset(void); + + static void enable_usb_interrupts(void); + static void disable_usb_interrupts(void); + }; #endif diff --git a/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhy_Nordic.cpp b/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhy_Nordic.cpp index a383c2a209..0d3816e9bd 100644 --- a/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhy_Nordic.cpp +++ b/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhy_Nordic.cpp @@ -26,21 +26,32 @@ #define IS_IN_EP(ep) (ep & 0x80) // Checks if the given endpoint is an IN endpoint (MSB set) #define IS_OUT_EP(ep) (ep & ~0x80) // Checks if the given endpoint is an OUT endpoint (MSB clear) +// Debugging flag for tracking USB events +#define USBD_DEBUG 1 + // Nordic USBD driver IRQ handler extern "C" void USBD_IRQHandler(void); -static USBPhyHw *instance; +static USBPhyHw *instance = 0; static void usbd_event_handler(nrf_drv_usbd_evt_t const * const p_event); static void power_usb_event_handler(nrf_drv_power_usb_evt_t event); +#if USBD_DEBUG + +// Static array of saved events to track what happens +static nrf_drv_usbd_evt_t debug_events[32]; +static uint8_t debug_evt_index = 0; + +#endif + USBPhy *get_usb_phy() { static USBPhyHw usbphy; return &usbphy; } USBPhyHw::USBPhyHw() : - events(NULL), sof_enabled(false), + events(NULL), sof_enabled(false), connect_enabled(false), usb_event_type(USB_HW_EVENT_NONE), usb_power_event(NRF_DRV_POWER_USB_EVT_REMOVED) { @@ -77,11 +88,12 @@ void USBPhyHw::init(USBPhyEvents *events) { ret = nrf_drv_usbd_init(usbd_event_handler); APP_ERROR_CHECK(ret); + // Store a reference to this instance instance = this; // Enable IRQ NVIC_SetVector(USBD_IRQn, (uint32_t)USBD_IRQHandler); - NVIC_SetPriority(USBD_IRQn, 6); + NVIC_SetPriority(USBD_IRQn, 7); NVIC_EnableIRQ(USBD_IRQn); } @@ -91,7 +103,10 @@ void USBPhyHw::deinit() { // Disable the USB Device driver ret_code_t ret = nrf_drv_usbd_uninit(); APP_ERROR_CHECK(ret); - //NVIC_DisableIRQ(USBD_IRQn); + NVIC_DisableIRQ(USBD_IRQn); + + // Clear the instance pointer? + //instance = 0; } bool USBPhyHw::powered() { @@ -104,13 +119,27 @@ bool USBPhyHw::powered() { void USBPhyHw::connect() { - /*if(!nrf_drv_usbd_is_enabled()) - nrf_drv_usbd_enable(); - if(!nrf_drv_usbd_is_started()) - nrf_drv_usbd_start(true);*/ + // To save power, we only enable the USBD peripheral + // when there's actually VBUS detected + + // So flag that the USB stack is ready to connect + this->connect_enabled = true; + + // If VBUS is already available, enable immediately + if(nrf_drv_power_usbstatus_get() == NRF_DRV_POWER_USB_STATE_CONNECTED) + { + // Enabling USB will cause NRF_DRV_POWER_USB_EVT_READY + // to occur, which will start the USBD peripheral + // when the internal regulator has settled + if(!nrf_drv_usbd_is_enabled()) + nrf_drv_usbd_enable(); + } } void USBPhyHw::disconnect() { + + this->connect_enabled = false; + if(nrf_drv_usbd_is_started()) nrf_drv_usbd_stop(); if(nrf_drv_usbd_is_enabled()) @@ -268,6 +297,15 @@ void USBPhyHw::process() { if (usb_event_type == USB_HW_EVENT_NONE) return; else if (usb_event_type == USB_HW_EVENT_USBD) { + +#ifdef USBD_DEBUG + // Save this event to the static log + memcpy(&debug_events[debug_evt_index++], &usb_event, sizeof(nrf_drv_usbd_evt_t)); + // Reset index if we overflow the buffer + if(debug_evt_index >= 32) + debug_evt_index = 0; +#endif + // Process regular USBD events switch (usb_event.type) { case NRF_DRV_USBD_EVT_SUSPEND: @@ -279,6 +317,7 @@ void USBPhyHw::process() { case NRF_DRV_USBD_EVT_WUREQ: break; case NRF_DRV_USBD_EVT_RESET: + this->_reset(); events->reset(); break; case NRF_DRV_USBD_EVT_SOF: @@ -298,13 +337,29 @@ void USBPhyHw::process() { else { if((usb_event.data.eptransfer.ep & 0x7F) == 0) + { + /* NOTE: Data values or size may be tested here to decide if clear or stall. + * If errata 154 is present the data transfer is acknowledged by the hardware. */ + if (!nrf_drv_usbd_errata_154()) { + /* Transfer ok - allow status stage */ + nrf_drv_usbd_setup_clear(); + } + events->ep0_out(); + } else events->out((usb_ep_t) usb_event.data.eptransfer.ep); } } break; case NRF_DRV_USBD_EVT_SETUP: { + + // Clear endpoint 0 stalls on setup packet receive + if(nrf_drv_usbd_ep_stall_check(NRF_DRV_USBD_EPOUT0)) + nrf_drv_usbd_ep_stall_clear(NRF_DRV_USBD_EPOUT0); + if(nrf_drv_usbd_ep_stall_check(NRF_DRV_USBD_EPIN0)) + nrf_drv_usbd_ep_stall_clear(NRF_DRV_USBD_EPIN0); + // Copy the setup packet into the internal buffer nrf_drv_usbd_setup_get(&setup_buf); events->ep0_setup(); @@ -313,9 +368,6 @@ void USBPhyHw::process() { default: break; - // Re-enable interrupt - //NVIC_ClearPendingIRQ(USB_IRQn); - //NVIC_EnableIRQ(USB_IRQn); } } else if (usb_event_type == USB_HW_EVENT_POWER) @@ -323,16 +375,20 @@ void USBPhyHw::process() { // Process USB power-related events switch (usb_power_event) { case NRF_DRV_POWER_USB_EVT_DETECTED: - if(!nrf_drv_usbd_is_enabled()) - nrf_drv_usbd_enable(); - events->power(true); + if(this->connect_enabled) { + if(!nrf_drv_usbd_is_enabled()) + nrf_drv_usbd_enable(); + events->power(true); + } break; case NRF_DRV_POWER_USB_EVT_REMOVED: events->power(false); break; case NRF_DRV_POWER_USB_EVT_READY: - if(!nrf_drv_usbd_is_started()) - nrf_drv_usbd_start(true); + if(this->connect_enabled) { + if(!nrf_drv_usbd_is_started()) + nrf_drv_usbd_start(false);//nrf_drv_usbd_start(true); + } break; default: ASSERT(false); @@ -342,10 +398,14 @@ void USBPhyHw::process() { // Unflag the event type usb_event_type = USB_HW_EVENT_NONE; + // Re-enable interrupt + NVIC_ClearPendingIRQ(USBD_IRQn); + enable_usb_interrupts(); } void USBPhyHw::_usb_event_handler( nrf_drv_usbd_evt_t const * const p_event) { + disable_usb_interrupts(); // Copy the event data into internal memory memcpy(&instance->usb_event, p_event, sizeof(instance->usb_event)); // Tell the upper layers of the stack to process the event @@ -354,6 +414,7 @@ void USBPhyHw::_usb_event_handler( } void USBPhyHw::_usb_power_event_handler(nrf_drv_power_usb_evt_t event) { + disable_usb_interrupts(); // Copy the event data into internal memory instance->usb_power_event = event; // Tell the upper layers of the stack to process the event @@ -374,16 +435,70 @@ nrf_drv_usbd_ep_t USBPhyHw::get_nordic_endpoint(usb_ep_t endpoint) { return (nrf_drv_usbd_ep_t) endpoint; } +void USBPhyHw::_reset(void) +{ + // Disable all endpoints except for control endpoints + nrf_drv_usbd_ep_default_config(); + + // TODO - Clear all endpoint interrupts? +} + +void USBPhyHw::enable_usb_interrupts(void) { + // Enable USB and USB-related power interrupts + NVIC_EnableIRQ(USBD_IRQn); + nrf_power_int_enable(NRF_POWER_INT_USBDETECTED_MASK | + NRF_POWER_INT_USBREMOVED_MASK | + NRF_POWER_INT_USBPWRRDY_MASK); +} + +void USBPhyHw::disable_usb_interrupts(void) { + // Disable USB and USB-related power interrupts + NVIC_DisableIRQ(USBD_IRQn); + nrf_power_int_disable(NRF_POWER_INT_USBDETECTED_MASK | + NRF_POWER_INT_USBREMOVED_MASK | + NRF_POWER_INT_USBPWRRDY_MASK); +} + static void power_usb_event_handler(nrf_drv_power_usb_evt_t event) { - // Pass the event on to the USBPhyHW instance - instance->_usb_power_event_handler(event); + if(instance) { + // Pass the event on to the USBPhyHW instance + instance->_usb_power_event_handler(event); + } } static void usbd_event_handler(nrf_drv_usbd_evt_t const * const p_event) { - // Pass the event on to the USBPhyHW instance - instance->_usb_event_handler(p_event); + if(instance) { + NVIC_DisableIRQ(USBD_IRQn); + // Pass the event on to the USBPhyHW instance + instance->_usb_event_handler(p_event); + } } extern "C" { -void USBD_IRQHandler_v(void) { } + + /** + * @brief Feeder passing data between mbed and nordic USB stacks + * + * @param[out] p_next See @ref nrf_drv_usbd_feeder_t documentation. + * @param[in,out] p_context See @ref nrf_drv_usbd_feeder_t documentation. + * @param[in] ep_size See @ref nrf_drv_usbd_feeder_t documentation. + * + * @retval true Continue transfer. + * @retval false This was the last transfer. + */ + bool mbed_nrf_feeder(nrf_drv_usbd_ep_transfer_t * p_next, + void * p_context, + size_t ep_size) + { + + + // We don't know if this is the last transfer or not at this level -- mbed doesn't tell us... + // So just tell the nordic layer that the transfer isn't over yet (so don't NAK/STALL) + return true; + } + } + +//extern "C" { +//void USBD_IRQHandler_v(void) { } +//}