diff --git a/drivers/usb/udc/Kconfig.mcux b/drivers/usb/udc/Kconfig.mcux index 590e772600e..abbd44e1c14 100644 --- a/drivers/usb/udc/Kconfig.mcux +++ b/drivers/usb/udc/Kconfig.mcux @@ -7,6 +7,7 @@ config UDC_NXP_EHCI depends on DT_HAS_NXP_EHCI_ENABLED select NOCACHE_MEMORY if HAS_MCUX_CACHE && CPU_HAS_DCACHE imply UDC_BUF_FORCE_NOCACHE + imply UDC_WORKQUEUE help NXP MCUX USB Device Controller Driver for EHCI. @@ -14,5 +15,14 @@ config UDC_NXP_IP3511 bool "NXP MCUX USB IP3511 Device controller driver" default y depends on DT_HAS_NXP_LPCIP3511_ENABLED + imply UDC_WORKQUEUE help - NXP MCUX USB Device Controller Driver for KHCI. + NXP MCUX USB Device Controller Driver for IP3511. + +config UDC_NXP_EVENT_COUNT + int "Number or blocks in event slab" + depends on UDC_NXP_EHCI || UDC_NXP_IP3511 + range 4 16 + default 4 + help + Number of blocks in slab for internal controller events. diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index 920279e6f3c..3379a3b88e0 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -49,9 +49,31 @@ struct udc_mcux_config { struct udc_mcux_data { const struct device *dev; usb_device_struct_t mcux_device; + struct k_work work; + struct k_fifo fifo; uint8_t controller_id; /* 0xFF is invalid value */ }; +/* Structure for driver's events */ +struct udc_mcux_event { + sys_snode_t node; + const struct device *dev; + usb_device_callback_message_struct_t mcux_msg; +}; + +K_MEM_SLAB_DEFINE(udc_event_slab, sizeof(struct udc_mcux_event), + CONFIG_UDC_NXP_EVENT_COUNT, sizeof(void *)); + +static int udc_mcux_lock(const struct device *dev) +{ + return udc_lock_internal(dev, K_FOREVER); +} + +static int udc_mcux_unlock(const struct device *dev) +{ + return udc_unlock_internal(dev); +} + static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command, void *param) { @@ -81,7 +103,6 @@ static int udc_mcux_ep_feed(const struct device *dev, usb_status_t status = kStatus_USB_Success; uint8_t *data; uint32_t len; - unsigned int key; usb_device_endpoint_status_struct_t ep_status; ep_status.endpointAddress = cfg->addr; @@ -90,10 +111,10 @@ static int udc_mcux_ep_feed(const struct device *dev, return -EACCES; /* stalled */ } - key = irq_lock(); + udc_mcux_lock(dev); if (!udc_ep_is_busy(dev, cfg->addr)) { udc_ep_set_busy(dev, cfg->addr, true); - irq_unlock(key); + udc_mcux_unlock(dev); if (USB_EP_DIR_IS_OUT(cfg->addr)) { len = net_buf_tailroom(buf); @@ -107,13 +128,13 @@ static int udc_mcux_ep_feed(const struct device *dev, cfg->addr, data, len); } - key = irq_lock(); + udc_mcux_lock(dev); if (status != kStatus_USB_Success) { udc_ep_set_busy(dev, cfg->addr, false); } - irq_unlock(key); + udc_mcux_unlock(dev); } else { - irq_unlock(key); + udc_mcux_unlock(dev); return -EBUSY; } @@ -303,13 +324,12 @@ static int udc_mcux_handler_out(const struct device *dev, uint8_t ep, { int err; struct net_buf *buf; - unsigned int key; buf = udc_buf_get(dev, ep); - key = irq_lock(); + udc_mcux_lock(dev); udc_ep_set_busy(dev, ep, false); - irq_unlock(key); + udc_mcux_unlock(dev); if (buf == NULL) { udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); @@ -357,7 +377,6 @@ static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, { int err; struct net_buf *buf; - unsigned int key; buf = udc_buf_peek(dev, ep); if (buf == NULL) { @@ -371,9 +390,9 @@ static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, buf = udc_buf_get(dev, ep); - key = irq_lock(); + udc_mcux_lock(dev); udc_ep_set_busy(dev, ep, false); - irq_unlock(key); + udc_mcux_unlock(dev); if (buf == NULL) { udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); @@ -388,16 +407,95 @@ static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, return err; } +static void udc_mcux_event_submit(const struct device *dev, + const usb_device_callback_message_struct_t *mcux_msg) +{ + struct udc_mcux_data *priv = udc_get_private(dev); + struct udc_mcux_event *ev; + int ret; + + ret = k_mem_slab_alloc(&udc_event_slab, (void **)&ev, K_NO_WAIT); + if (ret) { + udc_submit_event(dev, UDC_EVT_ERROR, ret); + LOG_ERR("Failed to allocate slab"); + return; + } + + ev->dev = dev; + ev->mcux_msg = *mcux_msg; + k_fifo_put(&priv->fifo, ev); + k_work_submit_to_queue(udc_get_work_q(), &priv->work); +} + +static void udc_mcux_work_handler(struct k_work *item) +{ + struct udc_mcux_event *ev; + struct udc_mcux_data *priv; + usb_device_callback_message_struct_t *mcux_msg; + int err; + uint8_t ep; + + priv = CONTAINER_OF(item, struct udc_mcux_data, work); + while ((ev = k_fifo_get(&priv->fifo, K_NO_WAIT)) != NULL) { + mcux_msg = &ev->mcux_msg; + + if (mcux_msg->code == kUSB_DeviceNotifyBusReset) { + struct udc_ep_config *cfg; + + udc_mcux_control(ev->dev, kUSB_DeviceControlSetDefaultStatus, NULL); + cfg = udc_get_ep_cfg(ev->dev, USB_CONTROL_EP_OUT); + if (cfg->stat.enabled) { + udc_ep_disable_internal(ev->dev, USB_CONTROL_EP_OUT); + } + cfg = udc_get_ep_cfg(ev->dev, USB_CONTROL_EP_IN); + if (cfg->stat.enabled) { + udc_ep_disable_internal(ev->dev, USB_CONTROL_EP_IN); + } + if (udc_ep_enable_internal(ev->dev, USB_CONTROL_EP_OUT, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + } + + if (udc_ep_enable_internal(ev->dev, USB_CONTROL_EP_IN, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + } + udc_submit_event(ev->dev, UDC_EVT_RESET, 0); + } else { + ep = mcux_msg->code; + + if (mcux_msg->isSetup) { + struct usb_setup_packet *setup = + (struct usb_setup_packet *)mcux_msg->buffer; + + err = udc_mcux_handler_setup(ev->dev, setup); + } else if (USB_EP_DIR_IS_IN(ep)) { + err = udc_mcux_handler_in(ev->dev, ep, mcux_msg->buffer, + mcux_msg->length); + } else { + err = udc_mcux_handler_out(ev->dev, ep, mcux_msg->buffer, + mcux_msg->length); + } + + if (unlikely(err)) { + udc_submit_event(ev->dev, UDC_EVT_ERROR, err); + } + } + + k_mem_slab_free(&udc_event_slab, (void *)ev); + } +} + /* NXP MCUX controller driver notify transfers/status through this interface */ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) { usb_device_callback_message_struct_t *mcux_msg = msg; - uint8_t ep; usb_device_notification_t mcux_notify; struct udc_mcux_data *priv; const struct device *dev; usb_status_t mcux_status = kStatus_USB_Success; - int err = 0; if ((NULL == msg) || (NULL == handle)) { return kStatus_USB_InvalidHandle; @@ -409,31 +507,7 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) switch (mcux_notify) { case kUSB_DeviceNotifyBusReset: - struct udc_ep_config *cfg; - - udc_mcux_control(dev, kUSB_DeviceControlSetDefaultStatus, NULL); - cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - if (cfg->stat.enabled) { - udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT); - } - cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - if (cfg->stat.enabled) { - udc_ep_disable_internal(dev, USB_CONTROL_EP_IN); - } - if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, - USB_EP_TYPE_CONTROL, - USB_MCUX_EP0_SIZE, 0)) { - LOG_ERR("Failed to enable control endpoint"); - return -EIO; - } - - if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, - USB_EP_TYPE_CONTROL, - USB_MCUX_EP0_SIZE, 0)) { - LOG_ERR("Failed to enable control endpoint"); - return -EIO; - } - udc_submit_event(dev, UDC_EVT_RESET, 0); + udc_mcux_event_submit(dev, mcux_msg); break; case kUSB_DeviceNotifyError: udc_submit_event(dev, UDC_EVT_ERROR, -EIO); @@ -458,25 +532,10 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) udc_submit_event(dev, UDC_EVT_SOF, 0); break; default: - ep = mcux_msg->code; - if (mcux_msg->isSetup) { - struct usb_setup_packet *setup = - (struct usb_setup_packet *)mcux_msg->buffer; - - err = udc_mcux_handler_setup(dev, setup); - } else if (USB_EP_DIR_IS_IN(ep)) { - err = udc_mcux_handler_in(dev, ep, mcux_msg->buffer, mcux_msg->length); - } else { - err = udc_mcux_handler_out(dev, ep, mcux_msg->buffer, mcux_msg->length); - } - + udc_mcux_event_submit(dev, mcux_msg); break; } - if (unlikely(err)) { - udc_submit_event(dev, UDC_EVT_ERROR, err); - mcux_status = kStatus_USB_Error; - } return mcux_status; } @@ -532,7 +591,6 @@ static int udc_mcux_ep_dequeue(const struct device *dev, struct udc_ep_config *const cfg) { struct net_buf *buf; - unsigned int key; cfg->stat.halted = false; buf = udc_buf_get_all(dev, cfg->addr); @@ -540,9 +598,9 @@ static int udc_mcux_ep_dequeue(const struct device *dev, udc_submit_ep_event(dev, buf, -ECONNABORTED); } - key = irq_lock(); + udc_mcux_lock(dev); udc_ep_set_busy(dev, cfg->addr, false); - irq_unlock(key); + udc_mcux_unlock(dev); return 0; } @@ -676,16 +734,6 @@ static int udc_mcux_shutdown(const struct device *dev) return 0; } -static int udc_mcux_lock(const struct device *dev) -{ - return udc_lock_internal(dev, K_FOREVER); -} - -static int udc_mcux_unlock(const struct device *dev) -{ - return udc_unlock_internal(dev); -} - static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv, const struct udc_mcux_config *config) { @@ -723,6 +771,8 @@ static int udc_mcux_driver_preinit(const struct device *dev) } k_mutex_init(&data->mutex); + k_fifo_init(&priv->fifo); + k_work_init(&priv->work, udc_mcux_work_handler); for (int i = 0; i < config->num_of_eps; i++) { config->ep_cfg_out[i].caps.out = 1; diff --git a/drivers/usb/udc/udc_mcux_ip3511.c b/drivers/usb/udc/udc_mcux_ip3511.c index 724c4221641..d30438319b0 100644 --- a/drivers/usb/udc/udc_mcux_ip3511.c +++ b/drivers/usb/udc/udc_mcux_ip3511.c @@ -49,9 +49,31 @@ struct udc_mcux_config { struct udc_mcux_data { const struct device *dev; usb_device_struct_t mcux_device; + struct k_work work; + struct k_fifo fifo; uint8_t controller_id; /* 0xFF is invalid value */ }; +/* Structure for driver's events */ +struct udc_mcux_event { + sys_snode_t node; + const struct device *dev; + usb_device_callback_message_struct_t mcux_msg; +}; + +K_MEM_SLAB_DEFINE(udc_event_slab, sizeof(struct udc_mcux_event), + CONFIG_UDC_NXP_EVENT_COUNT, sizeof(void *)); + +static int udc_mcux_lock(const struct device *dev) +{ + return udc_lock_internal(dev, K_FOREVER); +} + +static int udc_mcux_unlock(const struct device *dev) +{ + return udc_unlock_internal(dev); +} + static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command, void *param) { @@ -81,7 +103,6 @@ static int udc_mcux_ep_feed(const struct device *dev, usb_status_t status = kStatus_USB_Success; uint8_t *data; uint32_t len; - unsigned int key; usb_device_endpoint_status_struct_t ep_status; ep_status.endpointAddress = cfg->addr; @@ -90,10 +111,10 @@ static int udc_mcux_ep_feed(const struct device *dev, return -EACCES; /* stalled */ } - key = irq_lock(); + udc_mcux_lock(dev); if (!udc_ep_is_busy(dev, cfg->addr)) { udc_ep_set_busy(dev, cfg->addr, true); - irq_unlock(key); + udc_mcux_unlock(dev); if (USB_EP_DIR_IS_OUT(cfg->addr)) { len = net_buf_tailroom(buf); @@ -107,13 +128,13 @@ static int udc_mcux_ep_feed(const struct device *dev, cfg->addr, data, len); } - key = irq_lock(); + udc_mcux_lock(dev); if (status != kStatus_USB_Success) { udc_ep_set_busy(dev, cfg->addr, false); } - irq_unlock(key); + udc_mcux_unlock(dev); } else { - irq_unlock(key); + udc_mcux_unlock(dev); return -EBUSY; } @@ -303,13 +324,12 @@ static int udc_mcux_handler_out(const struct device *dev, uint8_t ep, { int err; struct net_buf *buf; - unsigned int key; buf = udc_buf_get(dev, ep); - key = irq_lock(); + udc_mcux_lock(dev); udc_ep_set_busy(dev, ep, false); - irq_unlock(key); + udc_mcux_unlock(dev); if (buf == NULL) { udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); @@ -357,7 +377,6 @@ static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, { int err; struct net_buf *buf; - unsigned int key; buf = udc_buf_peek(dev, ep); if (buf == NULL) { @@ -371,9 +390,9 @@ static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, buf = udc_buf_get(dev, ep); - key = irq_lock(); + udc_mcux_lock(dev); udc_ep_set_busy(dev, ep, false); - irq_unlock(key); + udc_mcux_unlock(dev); if (buf == NULL) { udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); @@ -388,16 +407,95 @@ static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, return err; } +static void udc_mcux_event_submit(const struct device *dev, + const usb_device_callback_message_struct_t *mcux_msg) +{ + struct udc_mcux_data *priv = udc_get_private(dev); + struct udc_mcux_event *ev; + int ret; + + ret = k_mem_slab_alloc(&udc_event_slab, (void **)&ev, K_NO_WAIT); + if (ret) { + udc_submit_event(dev, UDC_EVT_ERROR, ret); + LOG_ERR("Failed to allocate slab"); + return; + } + + ev->dev = dev; + ev->mcux_msg = *mcux_msg; + k_fifo_put(&priv->fifo, ev); + k_work_submit_to_queue(udc_get_work_q(), &priv->work); +} + +static void udc_mcux_work_handler(struct k_work *item) +{ + struct udc_mcux_event *ev; + struct udc_mcux_data *priv; + usb_device_callback_message_struct_t *mcux_msg; + int err; + uint8_t ep; + + priv = CONTAINER_OF(item, struct udc_mcux_data, work); + while ((ev = k_fifo_get(&priv->fifo, K_NO_WAIT)) != NULL) { + mcux_msg = &ev->mcux_msg; + + if (mcux_msg->code == kUSB_DeviceNotifyBusReset) { + struct udc_ep_config *cfg; + + udc_mcux_control(ev->dev, kUSB_DeviceControlSetDefaultStatus, NULL); + cfg = udc_get_ep_cfg(ev->dev, USB_CONTROL_EP_OUT); + if (cfg->stat.enabled) { + udc_ep_disable_internal(ev->dev, USB_CONTROL_EP_OUT); + } + cfg = udc_get_ep_cfg(ev->dev, USB_CONTROL_EP_IN); + if (cfg->stat.enabled) { + udc_ep_disable_internal(ev->dev, USB_CONTROL_EP_IN); + } + if (udc_ep_enable_internal(ev->dev, USB_CONTROL_EP_OUT, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + } + + if (udc_ep_enable_internal(ev->dev, USB_CONTROL_EP_IN, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + } + udc_submit_event(ev->dev, UDC_EVT_RESET, 0); + } else { + ep = mcux_msg->code; + + if (mcux_msg->isSetup) { + struct usb_setup_packet *setup = + (struct usb_setup_packet *)mcux_msg->buffer; + + err = udc_mcux_handler_setup(ev->dev, setup); + } else if (USB_EP_DIR_IS_IN(ep)) { + err = udc_mcux_handler_in(ev->dev, ep, mcux_msg->buffer, + mcux_msg->length); + } else { + err = udc_mcux_handler_out(ev->dev, ep, mcux_msg->buffer, + mcux_msg->length); + } + + if (unlikely(err)) { + udc_submit_event(ev->dev, UDC_EVT_ERROR, err); + } + } + + k_mem_slab_free(&udc_event_slab, (void *)ev); + } +} + /* NXP MCUX controller driver notify transfers/status through this interface */ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) { usb_device_callback_message_struct_t *mcux_msg = msg; - uint8_t ep; usb_device_notification_t mcux_notify; struct udc_mcux_data *priv; const struct device *dev; usb_status_t mcux_status = kStatus_USB_Success; - int err = 0; if ((NULL == msg) || (NULL == handle)) { return kStatus_USB_InvalidHandle; @@ -409,31 +507,7 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) switch (mcux_notify) { case kUSB_DeviceNotifyBusReset: - struct udc_ep_config *cfg; - - udc_mcux_control(dev, kUSB_DeviceControlSetDefaultStatus, NULL); - cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - if (cfg->stat.enabled) { - udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT); - } - cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - if (cfg->stat.enabled) { - udc_ep_disable_internal(dev, USB_CONTROL_EP_IN); - } - if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, - USB_EP_TYPE_CONTROL, - USB_MCUX_EP0_SIZE, 0)) { - LOG_ERR("Failed to enable control endpoint"); - return -EIO; - } - - if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, - USB_EP_TYPE_CONTROL, - USB_MCUX_EP0_SIZE, 0)) { - LOG_ERR("Failed to enable control endpoint"); - return -EIO; - } - udc_submit_event(dev, UDC_EVT_RESET, 0); + udc_mcux_event_submit(dev, mcux_msg); break; case kUSB_DeviceNotifyError: udc_submit_event(dev, UDC_EVT_ERROR, -EIO); @@ -458,25 +532,10 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) udc_submit_event(dev, UDC_EVT_SOF, 0); break; default: - ep = mcux_msg->code; - if (mcux_msg->isSetup) { - struct usb_setup_packet *setup = - (struct usb_setup_packet *)mcux_msg->buffer; - - err = udc_mcux_handler_setup(dev, setup); - } else if (USB_EP_DIR_IS_IN(ep)) { - err = udc_mcux_handler_in(dev, ep, mcux_msg->buffer, mcux_msg->length); - } else { - err = udc_mcux_handler_out(dev, ep, mcux_msg->buffer, mcux_msg->length); - } - + udc_mcux_event_submit(dev, mcux_msg); break; } - if (unlikely(err)) { - udc_submit_event(dev, UDC_EVT_ERROR, err); - mcux_status = kStatus_USB_Error; - } return mcux_status; } @@ -532,7 +591,6 @@ static int udc_mcux_ep_dequeue(const struct device *dev, struct udc_ep_config *const cfg) { struct net_buf *buf; - unsigned int key; cfg->stat.halted = false; buf = udc_buf_get_all(dev, cfg->addr); @@ -540,9 +598,9 @@ static int udc_mcux_ep_dequeue(const struct device *dev, udc_submit_ep_event(dev, buf, -ECONNABORTED); } - key = irq_lock(); + udc_mcux_lock(dev); udc_ep_set_busy(dev, cfg->addr, false); - irq_unlock(key); + udc_mcux_unlock(dev); return 0; } @@ -676,16 +734,6 @@ static int udc_mcux_shutdown(const struct device *dev) return 0; } -static int udc_mcux_lock(const struct device *dev) -{ - return udc_lock_internal(dev, K_FOREVER); -} - -static int udc_mcux_unlock(const struct device *dev) -{ - return udc_unlock_internal(dev); -} - static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv, const struct udc_mcux_config *config) { @@ -737,6 +785,8 @@ static int udc_mcux_driver_preinit(const struct device *dev) } k_mutex_init(&data->mutex); + k_fifo_init(&priv->fifo); + k_work_init(&priv->work, udc_mcux_work_handler); for (int i = 0; i < config->num_of_eps; i++) { config->ep_cfg_out[i].caps.out = 1;