drivers: udc: mcux: move isr context codes to work context

Move the transfer process codes to work context becuase
it lock the mutex, move bus reset process codes too
because it calls some common udc apis (they may add
mutex lock in future).

Signed-off-by: Mark Wang <yichang.wang@nxp.com>
This commit is contained in:
Mark Wang 2024-09-05 14:54:08 +08:00 committed by Anas Nashif
commit f378de7e46
3 changed files with 247 additions and 137 deletions

View file

@ -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.

View file

@ -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;

View file

@ -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;