From cfea14ce6d98f0d2752f03474ebdb0a7d1c0a58e Mon Sep 17 00:00:00 2001 From: George Beckstein Date: Thu, 21 Feb 2019 21:02:10 -0500 Subject: [PATCH] Added simulated status stage interrupt --- .../TARGET_MCU_NRF52840/USBPhyHw.h | 4 +- .../TARGET_MCU_NRF52840/USBPhy_Nordic.cpp | 86 ++++++++++++++----- 2 files changed, 67 insertions(+), 23 deletions(-) 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 1417117c45..a6d2ff9473 100644 --- a/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhyHw.h +++ b/usb/device/targets/TARGET_NORDIC/TARGET_MCU_NRF52840/USBPhyHw.h @@ -64,6 +64,7 @@ public: static void _usb_event_handler(nrf_drv_usbd_evt_t const * const p_event); static void _usb_power_event_handler(nrf_drv_power_usb_evt_t event); + static void _usb_virtual_status_event_handler(void); private: USBPhyEvents *events; @@ -74,7 +75,8 @@ private: typedef enum usb_hw_event_type_t { USB_HW_EVENT_NONE = 0, USB_HW_EVENT_USBD = 1, - USB_HW_EVENT_POWER = 2 + USB_HW_EVENT_POWER = 2, + USB_HW_EVENT_VIRTUAL_STATUS = 3 } usb_hw_event_type_t; // Event type to process 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 1fec148f06..90d9025f4d 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 @@ -65,7 +65,7 @@ void USBD_HAL_IRQHandler(void); static USBPhyHw *instance = 0; -static bool virtual_status_xfer_event; +static volatile bool virtual_status_xfer_event; 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); @@ -291,13 +291,18 @@ void USBPhyHw::ep0_read(uint8_t *data, uint32_t size) { if(setup_buf.bmRequestType & SETUP_TRANSFER_DIR_MASK) { // This is the status stage -- trigger the status task and notify the Mbed stack - nrf_usbd_task_trigger(NRF_USBD_TASK_EP0STATUS); + // Don't trigger status stage unless endpoint is not busy! + // (Causes an undocumented hardware-initiated stall on the control endpoint) + if(nrf_drv_usbd_ep_is_busy(NRF_DRV_USBD_EPIN0)) + nrf_usbd_shorts_enable(NRF_USBD_SHORT_EP0DATADONE_EP0STATUS_MASK); + else + nrf_usbd_task_trigger(NRF_USBD_TASK_EP0STATUS); + virtual_status_xfer_event = true; - // The status stage flag will be handled during the next interrupt + // Trigger an interrupt to process the virtual status event NVIC_SetPendingIRQ(USBD_IRQn); - //events->ep0_out(); return; } } @@ -325,14 +330,21 @@ void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size) { if(setup_buf.wLength == 0 || ((setup_buf.bmRequestType & SETUP_TRANSFER_DIR_MASK) == 0)) { + // This is the status stage -- trigger the status task and notify the Mbed stack - nrf_usbd_task_trigger(NRF_USBD_TASK_EP0STATUS); + + // Don't trigger status stage unless endpoint is not busy! + // (Causes an undocumented hardware-initiated stall on the control endpoint) + if(nrf_drv_usbd_ep_is_busy(NRF_DRV_USBD_EPOUT0)) + nrf_usbd_shorts_enable(NRF_USBD_SHORT_EP0DATADONE_EP0STATUS_MASK); + else + nrf_usbd_task_trigger(NRF_USBD_TASK_EP0STATUS); + virtual_status_xfer_event = true; - // The status stage flag will be handled during the next interrupt - //NVIC_SetPendingIRQ(USBD_IRQn); + // Trigger an interrupt to process the virtual status event + NVIC_SetPendingIRQ(USBD_IRQn); - //events->ep0_in(); return; } } @@ -415,17 +427,7 @@ void USBPhyHw::endpoint_abort(usb_ep_t endpoint) { void USBPhyHw::process() { - // Process virtual status transfers - if(virtual_status_xfer_event) - { - virtual_status_xfer_event = false; - // Notify Mbed stack of status stage transfer completion - if(setup_buf.bmRequestType & SETUP_TRANSFER_DIR_MASK) // DATA IN transfer, Status OUT transfer - events->ep0_out(); - else // DATA OUT transfer, Status IN transfer - events->ep0_in(); - } - else if (usb_event_type == USB_HW_EVENT_USBD) { + if (usb_event_type == USB_HW_EVENT_USBD) { // Process regular USBD events switch (usb_event.type) { @@ -451,14 +453,36 @@ void USBPhyHw::process() { if(IS_IN_EP(usb_event.data.eptransfer.ep)) { if((usb_event.data.eptransfer.ep & 0x7F) == 0) + { events->ep0_in(); + // Check for pending virtual status transfer + if(virtual_status_xfer_event) + { + // Notify the upper stack that the status transfer is done + // as well at this point + virtual_status_xfer_event = false; + events->ep0_out(); + + } + } else events->in((usb_ep_t) usb_event.data.eptransfer.ep); } else { if((usb_event.data.eptransfer.ep & 0x7F) == 0) + { events->ep0_out(); + + // Check for pending virtual status transfer + if(virtual_status_xfer_event) + { + // Notify the upper stack that the status transfer is done + // as well at this point + virtual_status_xfer_event = false; + events->ep0_in(); + } + } else events->out((usb_ep_t) usb_event.data.eptransfer.ep); } @@ -502,6 +526,14 @@ void USBPhyHw::process() { ASSERT(false); } } + else if (usb_event_type == USB_HW_EVENT_VIRTUAL_STATUS) + { + // Notify Mbed stack of status stage transfer completion + if(setup_buf.bmRequestType & SETUP_TRANSFER_DIR_MASK) // DATA IN transfer, Status OUT transfer + events->ep0_out(); + else // DATA OUT transfer, Status IN transfer + events->ep0_in(); + } // Unflag the event type usb_event_type = USB_HW_EVENT_NONE; @@ -529,6 +561,14 @@ void USBPhyHw::_usb_power_event_handler(nrf_drv_power_usb_evt_t event) { instance->events->start_process(); } +void USBPhyHw::_usb_virtual_status_event_handler(void) { + disable_usb_interrupts(); + + // Tell the upper layers of the stack to process the event + instance->usb_event_type = USB_HW_EVENT_VIRTUAL_STATUS; + instance->events->start_process(); +} + nrf_drv_usbd_transfer_t* USBPhyHw::get_transfer_buffer(usb_ep_t endpoint) { // Index is base endpoint number * 2 (output), add 1 for input endpoints return &transfer_buf[(((endpoint & 0x7F) << 1) + ((endpoint & 0x80) >> 7))]; @@ -589,9 +629,11 @@ void USBD_HAL_IRQHandler(void) // Process the virtual status stage transfer event if(virtual_status_xfer_event) { - // Trigger a dummy event handler - nrf_drv_usbd_evt_t dummy; - usbd_event_handler(&dummy); + if(instance) { + instance->_usb_virtual_status_event_handler(); + } + + virtual_status_xfer_event = false; } // Call Nordic driver IRQ handler USBD_IRQHandler();