drivers: usb: nordic: Fix for events when cable is detached

When user disconnects the USB cable, peripheral should be
immediately disabled, howewer a delay may occur when driver
events are processed from a workqueue or higher-priority
thread/ISR is active. This may lead to a fake resume/reset
event (peripheral-specific behavior). This fix drops such
events when cable is detached.

Fixes #13822

Signed-off-by: Paweł Zadrożniak <pawel.zadrozniak@nordicsemi.no>
This commit is contained in:
Paweł Zadrożniak 2019-03-13 11:36:04 +01:00 committed by Carles Cufí
parent bcf3d8e16b
commit 79dc1feeb3

View file

@ -283,12 +283,21 @@ static struct nrf_usbd_ctx usbd_ctx = {
.ready = false,
};
static inline struct nrf_usbd_ctx *get_usbd_ctx(void)
{
return &usbd_ctx;
}
static inline bool dev_attached(void)
{
return get_usbd_ctx()->attached;
}
static inline bool dev_ready(void)
{
return get_usbd_ctx()->ready;
}
static inline nrfx_usbd_ep_t ep_addr_to_nrfx(uint8_t ep)
{
return (nrfx_usbd_ep_t)ep;
@ -750,45 +759,48 @@ static inline void usbd_work_process_pwr_events(struct usbd_pwr_event *pwr_evt)
nrfx_usbd_enable();
(void) hf_clock_enable(true, false);
if (ctx->status_cb) {
ctx->status_cb(USB_DC_CONNECTED, NULL);
}
/* No callback here.
* Stack will be notified when the peripheral is ready.
*/
break;
case USBD_POWERED:
LOG_DBG("USB Powered");
usbd_enable_endpoints(ctx);
nrfx_usbd_start(true);
ctx->ready = true;
LOG_DBG("USB Powered");
if (ctx->status_cb) {
ctx->status_cb(USB_DC_CONNECTED, NULL);
}
break;
case USBD_DETACHED:
LOG_DBG("USB Removed");
ctx->ready = false;
nrfx_usbd_disable();
(void) hf_clock_enable(false, false);
LOG_DBG("USB Removed");
if (ctx->status_cb) {
ctx->status_cb(USB_DC_DISCONNECTED, NULL);
}
break;
case USBD_SUSPENDED:
LOG_DBG("USB Suspend state");
nrfx_usbd_suspend();
if (dev_ready()) {
nrfx_usbd_suspend();
LOG_DBG("USB Suspend state");
if (ctx->status_cb) {
ctx->status_cb(USB_DC_SUSPEND, NULL);
if (ctx->status_cb) {
ctx->status_cb(USB_DC_SUSPEND, NULL);
}
}
break;
case USBD_RESUMED:
LOG_DBG("USB resume");
if (ctx->status_cb) {
if (ctx->status_cb && dev_ready()) {
LOG_DBG("USB resume");
ctx->status_cb(USB_DC_RESUME, NULL);
}
break;
@ -909,16 +921,6 @@ static inline void usbd_work_process_ep_events(struct usbd_ep_event *ep_evt)
}
}
static inline bool dev_attached(void)
{
return get_usbd_ctx()->attached;
}
static inline bool dev_ready(void)
{
return get_usbd_ctx()->ready;
}
static void usbd_event_transfer_ctrl(nrfx_usbd_evt_t const *const p_event)
{
struct nrf_usbd_ep_ctx *ep_ctx =
@ -1235,6 +1237,10 @@ static void usbd_work_handler(struct k_work *item)
ctx = CONTAINER_OF(item, struct nrf_usbd_ctx, usb_work);
while ((ev = usbd_evt_get()) != NULL) {
if (!dev_ready() && ev->evt_type != USBD_EVT_POWER) {
/* Drop non-power events when cable is detached. */
continue;
}
switch (ev->evt_type) {
case USBD_EVT_EP: