diff --git a/drivers/usb/udc/udc_ambiq.c b/drivers/usb/udc/udc_ambiq.c index f98b62271f3be..ca1673e0ab59d 100644 --- a/drivers/usb/udc/udc_ambiq.c +++ b/drivers/usb/udc/udc_ambiq.c @@ -51,10 +51,7 @@ struct udc_ambiq_data { void *usb_handle; am_hal_usb_dev_speed_e usb_speed; uint8_t setup[8]; - uint8_t ctrl_pending_setup_buffer[8]; - bool ctrl_pending_in_ack; - bool ctrl_pending_setup; - bool ctrl_setup_recv_at_status_in; + bool ignore_status_in; }; struct udc_ambiq_config { @@ -70,26 +67,6 @@ struct udc_ambiq_config { void (*callback_register_func)(const struct device *dev); }; -static int udc_ambiq_rx(const struct device *dev, uint8_t ep, struct net_buf *buf); - -static int usbd_ctrl_feed_dout(const struct device *dev, const size_t length) -{ - struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - k_fifo_put(&cfg->fifo, buf); - if (length) { - udc_ambiq_rx(dev, cfg->addr, buf); - } - - return 0; -} - static int udc_ambiq_tx(const struct device *dev, uint8_t ep, struct net_buf *buf) { const struct udc_ambiq_data *priv = udc_get_private(dev); @@ -206,20 +183,6 @@ static void udc_ambiq_ep0_setup_callback(const struct device *dev, uint8_t *usb_ struct udc_ambiq_event evt = {.type = UDC_AMBIQ_EVT_HAL_SETUP}; struct udc_ambiq_data *priv = udc_get_private(dev); - /* Defer Setup Packet that arrives when we are waiting for - * status stage for OUT data control transfer to be completed - */ - if (priv->ctrl_pending_in_ack) { - priv->ctrl_pending_setup = true; - memcpy(priv->ctrl_pending_setup_buffer, usb_setup, 8); - return; - } - - /* Check whether we received SETUP packet during OUT_ACK (a.k.a STATUS_IN) - * state. If so, it might be inversion caused by register reading sequence. - * Raise flag accordingly and handle later. - */ - priv->ctrl_setup_recv_at_status_in = udc_ctrl_stage_is_status_in(dev); memcpy(priv->setup, usb_setup, sizeof(struct usb_setup_packet)); k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); } @@ -267,7 +230,6 @@ static enum udc_bus_speed udc_ambiq_device_speed(const struct device *dev) static int udc_ambiq_ep_enqueue(const struct device *dev, struct udc_ep_config *ep_cfg, struct net_buf *buf) { - struct udc_ambiq_data *priv = udc_get_private(dev); struct udc_ambiq_event evt = { .ep = ep_cfg->addr, .type = UDC_AMBIQ_EVT_XFER, @@ -275,10 +237,14 @@ static int udc_ambiq_ep_enqueue(const struct device *dev, struct udc_ep_config * LOG_DBG("%p enqueue %x %p", dev, ep_cfg->addr, buf); udc_buf_put(ep_cfg, buf); - if (ep_cfg->addr == USB_CONTROL_EP_IN && buf->len == 0 && priv->ctrl_pending_in_ack) { - priv->ctrl_pending_in_ack = false; - udc_ambiq_ep_xfer_complete_callback(dev, USB_CONTROL_EP_IN, 0, 0, NULL); - return 0; + + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + /* SETUP can be received without any action */ + return 0; + } } if (!ep_cfg->stat.halted) { @@ -704,40 +670,23 @@ static void udc_ambiq_unlock(const struct device *dev) static void ambiq_handle_evt_setup(const struct device *dev) { + struct usb_setup_packet *setup; struct udc_ambiq_data *priv = udc_get_private(dev); - struct net_buf *buf; - int err; - /* Create network buffer for SETUP packet and pass into UDC framework */ - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate for setup"); - return; - } - net_buf_add_mem(buf, priv->setup, sizeof(priv->setup)); - udc_ep_buf_set_setup(buf); - LOG_HEXDUMP_DBG(buf->data, buf->len, "setup"); - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - err = usbd_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - priv->ctrl_pending_in_ack = true; - if (err == -ENOMEM) { - udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - /* Submit event for data IN stage */ - LOG_DBG("s:%p|feed for -in-status", buf); - udc_ctrl_submit_s_in_status(dev); + setup = (struct usb_setup_packet *)priv->setup; + if (USB_REQTYPE_GET_DIR(setup->bmRequestType) == USB_REQTYPE_DIR_TO_DEVICE && + setup->wLength) { + /* Status IN after Data OUT is automatically handled. + * This should not be the case because it does not allow USB + * stack to stall Status stage in case the data is invalid + * (determined by handler). + */ + priv->ignore_status_in = true; } else { - /* Submit event for no-data stage */ - LOG_DBG("s:%p|feed >setup", buf); - udc_ctrl_submit_s_status(dev); + priv->ignore_status_in = false; } + + udc_setup_received(dev, priv->setup); } static inline void ambiq_handle_evt_dout(const struct device *dev, struct udc_ep_config *const cfg) @@ -755,20 +704,7 @@ static inline void ambiq_handle_evt_dout(const struct device *dev, struct udc_ep udc_ep_set_busy(cfg, false); /* Handle transfer complete event */ - if (cfg->addr == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - udc_ctrl_update_stage(dev, buf); - udc_ctrl_submit_status(dev, buf); - } else { - udc_ctrl_update_stage(dev, buf); - } - - if (udc_ctrl_stage_is_status_in(dev)) { - udc_ctrl_submit_s_out_status(dev, buf); - } - } else { - udc_submit_ep_event(dev, buf, 0); - } + udc_submit_ep_event(dev, buf, 0); } static void ambiq_handle_zlp_tx(const struct device *dev, struct udc_ep_config *const cfg) @@ -778,10 +714,7 @@ static void ambiq_handle_zlp_tx(const struct device *dev, struct udc_ep_config * static void ambiq_handle_evt_din(const struct device *dev, struct udc_ep_config *const cfg) { - struct udc_ambiq_data *priv = udc_get_private(dev); - struct udc_data *data = dev->data; struct net_buf *buf; - bool udc_ambiq_rx_status_in_completed = false; /* Clear endpoint busy status */ udc_ep_set_busy(cfg, false); @@ -805,47 +738,7 @@ static void ambiq_handle_evt_din(const struct device *dev, struct udc_ep_config LOG_DBG("DataIn ep 0x%02x len %u", cfg->addr, buf->size); /* Handle transfer complete event */ - if (cfg->addr == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - if (data->caps.out_ack == 0) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - if (udc_ctrl_stage_is_status_in(dev)) { - udc_ambiq_rx_status_in_completed = true; - } - } - - if (priv->ctrl_setup_recv_at_status_in && (buf->len == 0)) { - priv->ctrl_setup_recv_at_status_in = false; - net_buf_unref(buf); - return; - } - priv->ctrl_setup_recv_at_status_in = false; - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (((data->caps.out_ack == false) && udc_ctrl_stage_is_status_out(dev)) || - ((data->caps.out_ack == true) && (data->stage == CTRL_PIPE_STAGE_SETUP))) { - /* - * IN transfer finished, release buffer, - * control OUT buffer should be already fed. - */ - net_buf_unref(buf); - } - - /* - * Trigger deferred SETUP that was hold back if we are - * waiting for DATA_OUT status stage to be completed - */ - if (udc_ambiq_rx_status_in_completed && priv->ctrl_pending_setup) { - priv->ctrl_pending_setup = false; - udc_ambiq_ep0_setup_callback(dev, priv->ctrl_pending_setup_buffer); - } - } else { - udc_submit_ep_event(dev, buf, 0); - } + udc_submit_ep_event(dev, buf, 0); } static void udc_event_xfer(const struct device *dev, struct udc_ep_config *const cfg) @@ -858,6 +751,20 @@ static void udc_event_xfer(const struct device *dev, struct udc_ep_config *const return; } + if (cfg->addr == USB_CONTROL_EP_IN) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->status) { + struct udc_ambiq_data *priv = udc_get_private(dev); + + if (priv->ignore_status_in) { + buf = udc_buf_get(cfg); + udc_submit_ep_event(dev, buf, 0); + return; + } + } + } + if (USB_EP_DIR_IS_IN(cfg->addr)) { udc_ambiq_tx(dev, cfg->addr, buf); } else { diff --git a/drivers/usb/udc/udc_common.c b/drivers/usb/udc/udc_common.c index a2dc4fc1cfeef..499790ab44000 100644 --- a/drivers/usb/udc/udc_common.c +++ b/drivers/usb/udc/udc_common.c @@ -113,15 +113,6 @@ void udc_buf_put(struct udc_ep_config *const ep_cfg, k_fifo_put(&ep_cfg->fifo, buf); } -void udc_ep_buf_set_setup(struct net_buf *const buf) -{ - struct udc_buf_info *bi = udc_get_buf_info(buf); - - bi->setup = 1; - bi->data = 0; - bi->status = 0; -} - bool udc_ep_buf_has_zlp(const struct net_buf *const buf) { const struct udc_buf_info *bi = udc_get_buf_info(buf); @@ -136,6 +127,60 @@ void udc_ep_buf_clear_zlp(const struct net_buf *const buf) bi->zlp = false; } +void udc_setup_received(const struct device *dev, const void *const setup) +{ + struct udc_ep_config *cfg_out = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + struct udc_ep_config *cfg_in = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + struct udc_data *data = dev->data; + struct net_buf *buf; + + udc_lock_internal(dev, K_FOREVER); + + /* Cancel obsolete data/status stage requests */ + for (buf = udc_buf_get(cfg_in); buf; buf = udc_buf_get(cfg_in)) { + udc_submit_ep_event(dev, buf, -ECONNRESET); + } + + for (buf = udc_buf_get(cfg_out); buf; buf = udc_buf_get(cfg_out)) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + break; + } + + udc_submit_ep_event(dev, buf, -ECONNRESET); + } + + udc_ep_set_busy(cfg_out, false); + udc_ep_set_busy(cfg_in, false); + + if (buf == NULL) { + /* USB stack is not ready, cache SETUP data */ + data->setup_pending = true; + + /* Copy received payload only if it is valid. If SETUP is not + * valid then USB stack effectively will reject it and it will + * enqueue new setup buffer. Enqueuing new setup buffers does + * matter for drivers that are unable to receive SETUP data + * without arming control OUT endpoint. + */ + if (setup != NULL) { + memcpy(data->setup, setup, 8); + data->setup_valid = true; + } else { + data->setup_valid = false; + } + } else { + if (setup != NULL) { + net_buf_add_mem(buf, setup, 8); + } + + udc_submit_ep_event(dev, buf, 0); + } + + udc_unlock_internal(dev); +} + int udc_submit_event(const struct device *dev, const enum udc_event_type type, const int status) @@ -509,6 +554,7 @@ static void udc_debug_ep_enqueue(const struct device *dev, int udc_ep_enqueue(const struct device *dev, struct net_buf *const buf) { const struct udc_api *api = dev->api; + struct udc_data *data = dev->data; struct udc_ep_config *cfg; struct udc_buf_info *bi; int ret; @@ -521,11 +567,6 @@ int udc_ep_enqueue(const struct device *dev, struct net_buf *const buf) } bi = udc_get_buf_info(buf); - if (bi->ep == USB_CONTROL_EP_OUT) { - ret = -EPERM; - goto ep_enqueue_error; - } - cfg = udc_get_ep_cfg(dev, bi->ep); if (cfg == NULL) { ret = -ENODEV; @@ -540,8 +581,25 @@ int udc_ep_enqueue(const struct device *dev, struct net_buf *const buf) LOG_DBG("Queue ep 0x%02x %p len %u", cfg->addr, buf, USB_EP_DIR_IS_IN(cfg->addr) ? buf->len : buf->size); - bi->setup = 0; - ret = api->ep_enqueue(dev, cfg, buf); + if ((bi->ep == USB_CONTROL_EP_OUT || bi->ep == USB_CONTROL_EP_IN) && + data->setup_pending) { + if (bi->setup) { + /* Provide cached setup data only if valid */ + if (data->setup_valid) { + net_buf_add_mem(buf, data->setup, 8); + } + + data->setup_pending = false; + ret = udc_submit_ep_event(dev, buf, 0); + } else { + /* Host did timeout while the USB stack was busy. + * Data or status buffer is no longer relevant. + */ + ret = udc_submit_ep_event(dev, buf, -ECONNRESET); + } + } else { + ret = api->ep_enqueue(dev, cfg, buf); + } ep_enqueue_error: api->unlock(dev); @@ -549,6 +607,60 @@ int udc_ep_enqueue(const struct device *dev, struct net_buf *const buf) return ret; } +int udc_purge_queues(const struct device *dev) +{ + const struct udc_api *api = dev->api; + struct udc_ep_config *cfg; + struct net_buf *buf; + int ret; + + api->lock(dev); + + if (udc_is_initialized(dev)) { + ret = -EBUSY; + goto purge_error; + } + + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + if (cfg) { + for (buf = udc_buf_get(cfg); buf; buf = udc_buf_get(cfg)) { + net_buf_unref(buf); + } + } + + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + if (cfg) { + for (buf = udc_buf_get(cfg); buf; buf = udc_buf_get(cfg)) { + net_buf_unref(buf); + } + } + + ret = 0; + +purge_error: + api->unlock(dev); + + return ret; +} + +bool udc_ep_queue_is_empty(const struct device *dev, const uint8_t ep) +{ + const struct udc_api *api = dev->api; + struct udc_ep_config *cfg; + bool empty = true; + + api->lock(dev); + + cfg = udc_get_ep_cfg(dev, ep); + if (cfg) { + empty = udc_buf_peek(cfg) == NULL; + } + + api->unlock(dev); + + return empty; +} + int udc_ep_dequeue(const struct device *dev, const uint8_t ep) { const struct udc_api *api = dev->api; @@ -622,6 +734,76 @@ struct net_buf *udc_ctrl_alloc(const struct device *dev, return udc_ep_buf_alloc(dev, ep, size); } +struct net_buf *udc_ctrl_setup_alloc(const struct device *dev) +{ + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + struct net_buf *buf; + + /* Allocate bMaxPacketSize0 despite SETUP being just 8 bytes */ + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, ep_cfg->mps); + if (buf) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + bi->setup = 1; + bi->data = 0; + bi->status = 0; + } + + return buf; +} + +struct net_buf *udc_ctrl_data_alloc(const struct device *dev, + const uint8_t ep, + const size_t size) +{ + struct udc_buf_info *bi; + struct net_buf *buf; + size_t alloc_len = size; + + if (ep == USB_CONTROL_EP_OUT) { + struct udc_ep_config *ep_cfg; + + /* Round up to bMaxPacketSize0 */ + ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + alloc_len = ROUND_UP(size, ep_cfg->mps); + } + + buf = udc_ctrl_alloc(dev, ep, alloc_len); + if (buf) { + bi = udc_get_buf_info(buf); + + bi->setup = 0; + bi->data = 1; + bi->status = 0; + } + + return buf; +} + +struct net_buf *udc_ctrl_status_alloc(const struct device *dev, const uint8_t ep) +{ + struct net_buf *buf; + size_t alloc_len = 0; + + if (ep == USB_CONTROL_EP_OUT) { + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep); + + /* Allocate bMaxPacketSize0 despite Status being ZLP */ + alloc_len = ep_cfg->mps; + } + + buf = udc_ctrl_alloc(dev, ep, alloc_len); + if (buf) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + bi->setup = 0; + bi->data = 0; + bi->status = 1; + } + + return buf; +} + static inline void udc_buf_destroy(struct net_buf *buf) { /* Adjust level and use together with the log in udc_ep_buf_alloc() */ @@ -683,8 +865,6 @@ int udc_enable(const struct device *dev) goto udc_enable_error; } - data->stage = CTRL_PIPE_STAGE_SETUP; - ret = api->enable(dev); if (ret == 0) { atomic_set_bit(&data->status, UDC_STATUS_ENABLED); @@ -777,295 +957,6 @@ int udc_shutdown(const struct device *dev) return ret; } -static ALWAYS_INLINE -struct net_buf *udc_ctrl_alloc_stage(const struct device *dev, - struct net_buf *const parent, - const uint8_t ep, - const size_t size) -{ - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, ep, size); - if (buf == NULL) { - return NULL; - } - - if (parent) { - net_buf_frag_add(parent, buf); - } - - return buf; -} - -static struct net_buf *udc_ctrl_alloc_data(const struct device *dev, - struct net_buf *const setup, - const uint8_t ep) -{ - size_t size = udc_data_stage_length(setup); - struct udc_buf_info *bi; - struct net_buf *buf; - - buf = udc_ctrl_alloc_stage(dev, setup, ep, size); - if (buf) { - bi = udc_get_buf_info(buf); - bi->data = true; - } - - return buf; -} - -static struct net_buf *udc_ctrl_alloc_status(const struct device *dev, - struct net_buf *const parent, - const uint8_t ep) -{ - size_t size = (ep == USB_CONTROL_EP_OUT) ? 64 : 0; - struct udc_buf_info *bi; - struct net_buf *buf; - - buf = udc_ctrl_alloc_stage(dev, parent, ep, size); - if (buf) { - bi = udc_get_buf_info(buf); - bi->status = true; - } - - return buf; -} - -int udc_ctrl_submit_s_out_status(const struct device *dev, - struct net_buf *const dout) -{ - struct udc_buf_info *bi = udc_get_buf_info(dout); - struct udc_data *data = dev->data; - struct net_buf *buf; - int ret = 0; - - bi->data = true; - net_buf_frag_add(data->setup, dout); - - buf = udc_ctrl_alloc_status(dev, dout, USB_CONTROL_EP_IN); - if (buf == NULL) { - ret = -ENOMEM; - } - - return udc_submit_ep_event(dev, data->setup, ret); -} - -int udc_ctrl_submit_s_in_status(const struct device *dev) -{ - struct udc_data *data = dev->data; - struct net_buf *buf; - int ret = 0; - - if (!udc_ctrl_stage_is_data_in(dev)) { - return -ENOTSUP; - } - - /* Allocate buffer for data stage IN */ - buf = udc_ctrl_alloc_data(dev, data->setup, USB_CONTROL_EP_IN); - if (buf == NULL) { - ret = -ENOMEM; - } - - return udc_submit_ep_event(dev, data->setup, ret); -} - -int udc_ctrl_submit_s_status(const struct device *dev) -{ - struct udc_data *data = dev->data; - struct net_buf *buf; - int ret = 0; - - /* Allocate buffer for possible status IN */ - buf = udc_ctrl_alloc_status(dev, data->setup, USB_CONTROL_EP_IN); - if (buf == NULL) { - ret = -ENOMEM; - } - - return udc_submit_ep_event(dev, data->setup, ret); -} - -int udc_ctrl_submit_status(const struct device *dev, - struct net_buf *const buf) -{ - struct udc_buf_info *bi = udc_get_buf_info(buf); - - bi->status = true; - - return udc_submit_ep_event(dev, buf, 0); -} - -bool udc_ctrl_stage_is_data_out(const struct device *dev) -{ - struct udc_data *data = dev->data; - - return data->stage == CTRL_PIPE_STAGE_DATA_OUT ? true : false; -} - -bool udc_ctrl_stage_is_data_in(const struct device *dev) -{ - struct udc_data *data = dev->data; - - return data->stage == CTRL_PIPE_STAGE_DATA_IN ? true : false; -} - -bool udc_ctrl_stage_is_status_out(const struct device *dev) -{ - struct udc_data *data = dev->data; - - return data->stage == CTRL_PIPE_STAGE_STATUS_OUT ? true : false; -} - -bool udc_ctrl_stage_is_status_in(const struct device *dev) -{ - struct udc_data *data = dev->data; - - return data->stage == CTRL_PIPE_STAGE_STATUS_IN ? true : false; -} - -bool udc_ctrl_stage_is_no_data(const struct device *dev) -{ - struct udc_data *data = dev->data; - - return data->stage == CTRL_PIPE_STAGE_NO_DATA ? true : false; -} - -static bool udc_data_stage_to_host(const struct net_buf *const buf) -{ - struct usb_setup_packet *setup = (void *)buf->data; - - return USB_REQTYPE_GET_DIR(setup->bmRequestType); -} - -void udc_ctrl_update_stage(const struct device *dev, - struct net_buf *const buf) -{ - struct udc_buf_info *bi = udc_get_buf_info(buf); - struct udc_device_caps caps = udc_caps(dev); - uint8_t next_stage = CTRL_PIPE_STAGE_ERROR; - struct udc_data *data = dev->data; - - __ASSERT(USB_EP_GET_IDX(bi->ep) == 0, - "0x%02x is not a control endpoint", bi->ep); - - if (bi->setup && bi->ep == USB_CONTROL_EP_OUT) { - uint16_t length = udc_data_stage_length(buf); - - if (data->stage != CTRL_PIPE_STAGE_SETUP) { - LOG_INF("Sequence %u not completed", data->stage); - - if (data->stage == CTRL_PIPE_STAGE_DATA_OUT) { - /* - * The last setup packet is "floating" because - * DATA OUT stage was awaited. This setup - * packet must be removed here because it will - * never reach the stack. - */ - LOG_INF("Drop setup packet (%p)", (void *)data->setup); - net_buf_unref(data->setup); - } - - data->stage = CTRL_PIPE_STAGE_SETUP; - } - - data->setup = buf; - - /* - * Setup Stage has been completed (setup packet received), - * regardless of the previous stage, this is now being reset. - * Next state depends on wLength and the direction bit (D7). - */ - if (length == 0) { - /* - * No Data Stage, next is Status Stage - * complete sequence: s->status - */ - LOG_DBG("s->(status)"); - next_stage = CTRL_PIPE_STAGE_NO_DATA; - } else if (udc_data_stage_to_host(buf)) { - /* - * Next is Data Stage (to host / IN) - * complete sequence: s->in->status - */ - LOG_DBG("s->(in)"); - next_stage = CTRL_PIPE_STAGE_DATA_IN; - } else { - /* - * Next is Data Stage (to device / OUT) - * complete sequence: s->out->status - */ - LOG_DBG("s->(out)"); - next_stage = CTRL_PIPE_STAGE_DATA_OUT; - } - - } else if (bi->ep == USB_CONTROL_EP_OUT) { - if (data->stage == CTRL_PIPE_STAGE_DATA_OUT) { - /* - * Next sequence is Status Stage if request is okay, - * (IN ZLP status to host) - */ - next_stage = CTRL_PIPE_STAGE_STATUS_IN; - } else if (data->stage == CTRL_PIPE_STAGE_STATUS_OUT) { - /* - * End of a sequence: s->in->status, - * We should check the length here because we always - * submit a OUT request with the minimum length - * of the control endpoint. - */ - if (buf->len == 0) { - LOG_DBG("s-in-status"); - next_stage = CTRL_PIPE_STAGE_SETUP; - } else { - LOG_WRN("ZLP expected"); - next_stage = CTRL_PIPE_STAGE_ERROR; - } - } else { - LOG_ERR("Cannot determine the next stage"); - next_stage = CTRL_PIPE_STAGE_ERROR; - } - - } else { /* if (bi->ep == USB_CONTROL_EP_IN) */ - if (data->stage == CTRL_PIPE_STAGE_STATUS_IN) { - /* - * End of a sequence: setup->out->in - */ - LOG_DBG("s-out-status"); - next_stage = CTRL_PIPE_STAGE_SETUP; - } else if (data->stage == CTRL_PIPE_STAGE_DATA_IN) { - /* - * Data IN stage completed, next sequence - * is Status Stage (OUT ZLP status to device). - * over-engineered controllers can send status - * on their own, skip this state then. - */ - if (caps.out_ack) { - LOG_DBG("s-in->[status]"); - next_stage = CTRL_PIPE_STAGE_SETUP; - } else { - LOG_DBG("s-in->(status)"); - next_stage = CTRL_PIPE_STAGE_STATUS_OUT; - } - } else if (data->stage == CTRL_PIPE_STAGE_NO_DATA) { - /* - * End of a sequence (setup->in) - * Previous NO Data stage was completed and - * we confirmed it with an IN ZLP. - */ - LOG_DBG("s-status"); - next_stage = CTRL_PIPE_STAGE_SETUP; - } else { - LOG_ERR("Cannot determine the next stage"); - next_stage = CTRL_PIPE_STAGE_ERROR; - } - } - - - if (next_stage == data->stage) { - LOG_WRN("State not changed!"); - } - - data->stage = next_stage; -} - #if defined(CONFIG_UDC_WORKQUEUE) K_KERNEL_STACK_DEFINE(udc_work_q_stack, CONFIG_UDC_WORKQUEUE_STACK_SIZE); diff --git a/drivers/usb/udc/udc_common.h b/drivers/usb/udc/udc_common.h index ed6712e95ae89..a490484ae3b92 100644 --- a/drivers/usb/udc/udc_common.h +++ b/drivers/usb/udc/udc_common.h @@ -15,14 +15,6 @@ #include #include -#define CTRL_PIPE_STAGE_SETUP 0 -#define CTRL_PIPE_STAGE_DATA_OUT 1 -#define CTRL_PIPE_STAGE_DATA_IN 2 -#define CTRL_PIPE_STAGE_NO_DATA 3 -#define CTRL_PIPE_STAGE_STATUS_OUT 4 -#define CTRL_PIPE_STAGE_STATUS_IN 5 -#define CTRL_PIPE_STAGE_ERROR 6 - /** * @brief Get driver's private data * @@ -230,16 +222,6 @@ int udc_ep_disable_internal(const struct device *dev, int udc_register_ep(const struct device *dev, struct udc_ep_config *const cfg); -/** - * @brief Set setup flag in requests metadata. - * - * A control transfer can be either setup or data OUT, - * use this function to mark request as setup packet. - * - * @param[in] buf Pointer to UDC request buffer - */ -void udc_ep_buf_set_setup(struct net_buf *const buf); - /** * @brief Checks whether the driver must finish transfer with a ZLP * @@ -256,6 +238,25 @@ bool udc_ep_buf_has_zlp(const struct net_buf *const buf); */ void udc_ep_buf_clear_zlp(const struct net_buf *const buf); +/** + * @brief Submit control transfer data to USB stack + * + * UDC driver must ensure that driver will not access any of queued control + * endpoint buffers before calling this function. + * + * This function completes any pending data/status requests, marks endpoints as + * not busy and submits data to USB stack. If USB stack is not ready to process + * setup data, this function will cache received setup data and submit it once + * USB stack is ready. + * + * This function can only be called from thread context because it depends on + * UDC lock for synchronization. + * + * @param[in] dev Pointer to device struct of the driver instance + * @param[in] setup Pointer to received SETUP data (NULL if not valid) + */ +void udc_setup_received(const struct device *dev, const void *const setup); + /** * @brief Locking function for the drivers. * @@ -301,208 +302,6 @@ struct net_buf *udc_ctrl_alloc(const struct device *dev, const uint8_t ep, const size_t size); -static inline uint16_t udc_data_stage_length(const struct net_buf *const buf) -{ - struct usb_setup_packet *setup = (void *)buf->data; - - return sys_le16_to_cpu(setup->wLength); -} - -/** - * @brief Checks whether the current control transfer stage is Data Stage OUT - * - * @param[in] dev Pointer to device struct of the driver instance - * - * @return true if stage is Data Stage OUT - */ -bool udc_ctrl_stage_is_data_out(const struct device *dev); - -/** - * @brief Checks whether the current control transfer stage is Data Stage IN - * - * @param[in] dev Pointer to device struct of the driver instance - * - * @return true if stage is Data Stage IN - */ -bool udc_ctrl_stage_is_data_in(const struct device *dev); - -/** - * @brief Checks whether the current control transfer stage is Status IN - * - * @param[in] dev Pointer to device struct of the driver instance - * - * @return true if stage is Data Stage IN - */ -bool udc_ctrl_stage_is_status_in(const struct device *dev); - -/** - * @brief Checks whether the current control transfer stage is Status OUT - * - * @param[in] dev Pointer to device struct of the driver instance - * - * @return true if stage is Data Stage OUT - */ -bool udc_ctrl_stage_is_status_out(const struct device *dev); - -/** - * @brief Checks whether the current control transfer stage is Status no-data - * - * @param[in] dev Pointer to device struct of the driver instance - * - * @return true if stage is Status no-data - */ -bool udc_ctrl_stage_is_no_data(const struct device *dev); - -/** - * @brief Submit Control Write (s-out-status) transfer - * - * Allocate buffer for data stage IN, - * submit both setup and data buffer to upper layer. - * - * @param[in] dev Pointer to device struct of the driver instance - * @param[in] dout Pointer to UDC buffer containing data transaction - * - * @return 0 on success, all other values should be treated as error. - */ -int udc_ctrl_submit_s_out_status(const struct device *dev, - struct net_buf *const dout); - -/** - * @brief Prepare control data IN stage - * - * Allocate buffer for data stage IN, - * submit both setup and data buffer to upper layer. - * - * @param[in] dev Pointer to device struct of the driver instance - * - * @return 0 on success, all other values should be treated as error. - */ -int udc_ctrl_submit_s_in_status(const struct device *dev); - -/** - * @brief Prepare control (no-data) status stage - * - * Allocate buffer for status stage IN, - * submit both setup and status buffer to upper layer. - * - * @param[in] dev Pointer to device struct of the driver instance - * - * @return 0 on success, all other values should be treated as error. - */ -int udc_ctrl_submit_s_status(const struct device *dev); - -/** - * @brief Submit status transaction - * - * Submit both status transaction to upper layer. - * - * @param[in] dev Pointer to device struct of the driver instance - * @param[in] dout Pointer to UDC buffer containing data transaction - * - * @return 0 on success, all other values should be treated as error. - */ -int udc_ctrl_submit_status(const struct device *dev, - struct net_buf *const buf); - -/** - * @brief Update internal control stage status based on the net_buf metadata - * - * Use it in the driver to update the stage, typically there are - * three places where this function should be called: - * - when a setup packet is received - * - when a data stage is completed (all data stage transactions) - * - when a status stage transaction is finished - * - * The functions of type udc_ctrl_stage_is_*() can be called before or - * after this function, depending on the desired action. - * To keep protocol processing running the following should be taken - * into account: - * - * - Upper layer may not allocate buffers but remove or release buffers - * from the chain that are no longer needed. Only control IN transfers may - * be enqueued by the upper layer. - * - * - For "Control Write" (s-out-status), the driver should allocate the buffer, - * insert it as a fragment to setup buffer and perform the Data Stage - * transaction. Allocate and insert a fragment for the status (IN) stage to - * setup buffer, and then pass setup packet with the chain of s-out-status to - * upper layer. Upper layer should either halt control endpoint or - * enqueue status buffer for status stage. There should be second - * notification to upper layer when the status transaction is finished. - * - * ->driver_foo_setup_rcvd(dev) - * ->udc_ctrl_update_stage(dev, buf) - * ->udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, wLength) - * ->driver_foo_xfer_start(dev, USB_CONTROL_EP_OUT) - * - * ->driver_foo_dout_rcvd(dev) - * -... - * ->driver_foo_feed_next_dout(dev, ....) - * -... - * ->udc_ctrl_update_stage(dev, dout_buf) - * -... - * ->udc_ctrl_submit_s_out_status(dev, dout_buf); - * - * ->driver_foo_din_rcvd(dev) - * -... - * ->udc_ctrl_submit_status(dev, status_buf); - * -... - * ->udc_ctrl_update_stage(dev, status_buf) - * - * - For "Control Read" (s-in-status), depending on the controller, - * the driver should reserve the buffers for subsequent status stage and - * setup packet and prepare everything. The driver should allocate the buffer - * for IN transaction insert it as a fragment to setup buffer, and pass - * the chain of s-in to upper layer. Upper layer should either halt control - * endpoint or enqueue (in) buffer. There should be second - * notification to upper layer when the status transaction is finished. - * - * ->driver_foo_setup_rcvd(dev) - * ->udc_ctrl_update_stage(dev, buf) - * ->driver_foo_feed_next_dout(dev, ....) - * -... - * ->udc_ctrl_submit_s_in_status(dev); - * - * ->driver_foo_din_rcvd(dev) - * -... - * ->udc_ctrl_update_stage(dev, dout_buf) - * -... - * - * ->driver_foo_dout_rcvd(dev) - * -... - * ->udc_ctrl_submit_status(dev, status_buf); - * -... - * ->udc_ctrl_update_stage(dev, dout_buf) - * - * - For "No-data Control" (s-status), the driver should allocate the buffer - * for the status (IN) stage, insert it as a fragment to setup buffer, - * and then pass setup packet with the chain of s-status to - * upper layer. Upper layer should either halt control endpoint or - * enqueue status buffer for status stage. There should be second - * notification to upper layer when the status transaction is finished. - * - * ->driver_foo_setup_rcvd(dev) - * ->udc_ctrl_update_stage(dev, buf) - * ->driver_foo_feed_next_dout(dev, ....) - * -... - * ->udc_ctrl_submit_s_status(dev); - * - * ->driver_foo_din_rcvd(dev) - * -... - * ->udc_ctrl_submit_status(dev, status_buf); - * -... - * ->udc_ctrl_update_stage(dev, status_buf) - * - * Please refer to Chapter 8.5.3 Control Transfers USB 2.0 spec. - * - * @param[in] dev Pointer to device struct of the driver instance - * @param[in] buf Buffer containing setup packet - * - * @return 0 on success, all other values should be treated as error. - */ -void udc_ctrl_update_stage(const struct device *dev, - struct net_buf *const buf); - #if defined(CONFIG_UDC_WORKQUEUE) extern struct k_work_q udc_work_q; diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index c83e6671c29b8..6acdd598218b3 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -143,7 +143,6 @@ struct udc_dwc2_data { unsigned int hibernated : 1; unsigned int enumdone : 1; unsigned int enumspd : 2; - unsigned int pending_dout_feed : 1; unsigned int ignore_ep0_nakeff : 1; enum dwc2_suspend_type suspend_type; /* Number of endpoints including control endpoint */ @@ -405,57 +404,6 @@ static bool dwc2_ep_is_iso(struct udc_ep_config *const cfg) return (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) == USB_EP_TYPE_ISO; } -static int dwc2_ctrl_feed_dout(const struct device *dev, const size_t length) -{ - struct udc_dwc2_data *const priv = udc_get_private(dev); - struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - size_t alloc_len = length; - - if (dwc2_in_buffer_dma_mode(dev)) { - /* Control OUT buffers must be multiple of bMaxPacketSize0 */ - alloc_len = ROUND_UP(length, 64); - } - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, alloc_len); - if (buf == NULL) { - return -ENOMEM; - } - - if (dwc2_in_buffer_dma_mode(dev)) { - /* Get rid of all dirty cache lines */ - sys_cache_data_invd_range(buf->data, net_buf_tailroom(buf)); - } - - udc_buf_put(ep_cfg, buf); - atomic_set_bit(&priv->xfer_new, 16); - k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_XFER)); - - return 0; -} - -static void dwc2_ensure_setup_ready(const struct device *dev) -{ - if (dwc2_in_completer_mode(dev)) { - /* In Completer mode EP0 can always receive SETUP data */ - return; - } else { - struct udc_dwc2_data *const priv = udc_get_private(dev); - - if (udc_ep_is_busy(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT))) { - /* There is already buffer queued */ - return; - } - - /* Enable EP0 OUT only if there is no pending EP0 IN transfer - * after which the stack has to enable EP0 OUT. - */ - if (!priv->pending_dout_feed) { - dwc2_ctrl_feed_dout(dev, 8); - } - } -} - static void dwc2_clear_control_in_nak(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); @@ -717,7 +665,7 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, doepctl = sys_read32(doepctl_reg); doepctl |= USB_DWC2_DEPCTL_EPENA; if (cfg->addr == USB_CONTROL_EP_OUT) { - struct udc_data *data = dev->data; + struct udc_buf_info *bi = udc_get_buf_info(buf); /* During OUT Data Stage every packet has to have CNAK set. * In Buffer DMA mode the OUT endpoint is armed during IN Data @@ -732,9 +680,7 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, * subsequent control transfer OUT Data Stage packet (SETUP DATA * is unconditionally ACKed regardless of software state). */ - if (data->stage == CTRL_PIPE_STAGE_DATA_OUT || - data->stage == CTRL_PIPE_STAGE_DATA_IN || - data->stage == CTRL_PIPE_STAGE_STATUS_OUT) { + if (bi->data || bi->status) { doepctl |= USB_DWC2_DEPCTL_CNAK; } } else { @@ -816,45 +762,40 @@ static void dwc2_handle_xfer_next(const struct device *dev, return; } - if (USB_EP_DIR_IS_OUT(cfg->addr)) { + if (cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + if (dwc2_in_completer_mode(dev)) { + /* SETUP can be received without enabling EP */ + return; + } + } + + dwc2_prep_rx(dev, buf, cfg); + } else if (USB_EP_DIR_IS_OUT(cfg->addr)) { dwc2_prep_rx(dev, buf, cfg); } else { int err; - if (cfg->addr == USB_CONTROL_EP_IN && - udc_ctrl_stage_is_status_in(dev)) { + if (cfg->addr == USB_CONTROL_EP_IN) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + /* It was observed that EPENA results in INEPNAKEFF * interrupt which leads to endpoint disable. It is not * clear how to prevent this without violating sequence * described in Programming Guide, so just set a flag * for interrupt handler to ignore it. */ - priv->ignore_ep0_nakeff = 1; + if (bi->status) { + priv->ignore_ep0_nakeff = 1; + } + + dwc2_clear_control_in_nak(dev); } err = dwc2_tx_fifo_write(dev, cfg, buf); - if (cfg->addr == USB_CONTROL_EP_IN) { - /* Feed a buffer for the next setup packet after arming - * IN endpoint with the data. This is necessary both in - * IN Data Stage (Control Read Transfer) and IN Status - * Stage (Control Write Transfers and Control Transfers - * without Data Stage). - * - * The buffer must be fed here in Buffer DMA mode to - * allow receiving premature SETUP. This inevitably does - * automatically arm the buffer for OUT Status Stage. - * - * The buffer MUST NOT be fed here in Completer mode to - * avoid race condition where the next Control Write - * Transfer Data Stage is received into the buffer. - */ - if (dwc2_in_buffer_dma_mode(dev) && priv->pending_dout_feed) { - priv->pending_dout_feed = 0; - dwc2_ctrl_feed_dout(dev, 8); - } - } - if (err) { LOG_ERR("Failed to start write to TX FIFO, ep 0x%02x (err: %d)", cfg->addr, err); @@ -874,10 +815,7 @@ static void dwc2_handle_xfer_next(const struct device *dev, static int dwc2_handle_evt_setup(const struct device *dev) { struct udc_dwc2_data *const priv = udc_get_private(dev); - struct udc_ep_config *cfg_out = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); struct udc_ep_config *cfg_in = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - struct net_buf *buf; - int err; /* In Completer mode SETUP data is received without preparing endpoint 0 * transfer beforehand. In Buffer DMA the SETUP can be copied to any EP0 @@ -887,82 +825,14 @@ static int dwc2_handle_evt_setup(const struct device *dev) udc_dwc2_ep_disable(dev, cfg_in, false, true); atomic_and(&priv->xfer_finished, ~(BIT(0) | BIT(16))); - buf = udc_buf_get_all(cfg_out); - if (buf) { - net_buf_unref(buf); - } - - buf = udc_buf_get_all(cfg_in); - if (buf) { - net_buf_unref(buf); - } - - udc_ep_set_busy(cfg_out, false); - udc_ep_set_busy(cfg_in, false); - - /* Allocate buffer and copy received SETUP for processing */ - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, 8); - if (buf == NULL) { - LOG_ERR("No buffer available for control ep"); - return -ENODATA; - } - - net_buf_add_mem(buf, priv->setup, sizeof(priv->setup)); - udc_ep_buf_set_setup(buf); - LOG_HEXDUMP_DBG(buf->data, buf->len, "setup"); - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - /* We always allocate and feed buffer large enough for a setup packet. */ - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - - priv->pending_dout_feed = 0; - - if (dwc2_in_completer_mode(dev)) { - /* Programming Guide does not clearly describe to clear - * control IN endpoint NAK for Control Write Transfers - * when operating in Completer mode. Set CNAK here, - * because IN endpoint is not armed at this point and - * forced NAKs are not necessary. IN Status stage will - * only finish after IN endpoint is armed. - */ - dwc2_clear_control_in_nak(dev); - } + udc_setup_received(dev, priv->setup); - err = dwc2_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - LOG_DBG("s:%p|feed for -in-status", buf); - - dwc2_clear_control_in_nak(dev); - - err = udc_ctrl_submit_s_in_status(dev); - - priv->pending_dout_feed = 1; - } else { - LOG_DBG("s:%p|feed >setup", buf); - - dwc2_clear_control_in_nak(dev); - - err = udc_ctrl_submit_s_status(dev); - - priv->pending_dout_feed = 1; - } - - return err; + return 0; } static inline int dwc2_handle_evt_dout(const struct device *dev, struct udc_ep_config *const cfg) { - struct udc_dwc2_data *const priv = udc_get_private(dev); - struct udc_data *data = dev->data; struct net_buf *buf; int err = 0; @@ -978,39 +848,7 @@ static inline int dwc2_handle_evt_dout(const struct device *dev, udc_ep_set_busy(cfg, false); - if (cfg->addr == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - /* s-in-status finished */ - LOG_DBG("dout:%p| status, feed >s", buf); - - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - - if (dwc2_in_buffer_dma_mode(dev)) { - dwc2_ctrl_feed_dout(dev, 8); - } - } else { - LOG_DBG("dout:%p| data, feed >s", buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - - priv->pending_dout_feed = 1; - } - - if (data->stage == CTRL_PIPE_STAGE_ERROR) { - /* Allow receiving next SETUP. USB stack won't queue any - * buffer because it has no clue about this transfer. - */ - dwc2_ensure_setup_ready(dev); - } - } else { - err = udc_submit_ep_event(dev, buf, 0); - } + err = udc_submit_ep_event(dev, buf, 0); return err; } @@ -1040,29 +878,6 @@ static int dwc2_handle_evt_din(const struct device *dev, buf = udc_buf_get(cfg); udc_ep_set_busy(cfg, false); - if (cfg->addr == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - if (dwc2_in_completer_mode(dev)) { - /* Allow OUT status stage */ - dwc2_ctrl_feed_dout(dev, 8); - } - - /* IN transfer finished, release buffer. */ - net_buf_unref(buf); - } - - return 0; - } - return udc_submit_ep_event(dev, buf, 0); } @@ -1784,12 +1599,6 @@ static int udc_dwc2_ep_set_halt(const struct device *dev, LOG_DBG("Set halt ep 0x%02x", cfg->addr); if (ep_idx != 0) { cfg->stat.halted = true; - } else { - struct udc_dwc2_data *const priv = udc_get_private(dev); - - /* Data/Status stage is STALLed, allow receiving next SETUP */ - priv->pending_dout_feed = 0; - dwc2_ensure_setup_ready(dev); } return 0; @@ -2365,9 +2174,8 @@ static int udc_dwc2_disable(const struct device *dev) /* Enable soft disconnect */ sys_set_bits(dctl_reg, USB_DWC2_DCTL_SFTDISCON); - /* OUT endpoint 0 cannot be disabled by software. The buffer allocated - * in dwc2_ctrl_feed_dout() can only be freed after core reset if the - * core was in Buffer DMA mode. + /* OUT endpoint 0 cannot be disabled by software. The enqueued buffer + * can only be freed after core reset if core was in Buffer DMA mode. * * Soft Reset does timeout if PHY clock is not running. However, just * triggering Soft Reset seems to be enough on shutdown clean up. @@ -3460,10 +3268,6 @@ static ALWAYS_INLINE void dwc2_thread_handler(void *const arg) if (evt & BIT(DWC2_DRV_EVT_ENUM_DONE)) { k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_ENUM_DONE)); - - /* Any potential transfer on control IN endpoint is cancelled */ - priv->pending_dout_feed = 0; - dwc2_ensure_setup_ready(dev); } udc_unlock_internal(dev); diff --git a/drivers/usb/udc/udc_it82xx2.c b/drivers/usb/udc/udc_it82xx2.c index 6b41cb162d49c..145ff4900fd68 100644 --- a/drivers/usb/udc/udc_it82xx2.c +++ b/drivers/usb/udc/udc_it82xx2.c @@ -480,6 +480,25 @@ static int it82xx2_ep_enqueue(const struct device *dev, struct udc_ep_config *co { udc_buf_put(cfg, buf); + if (cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup || bi->status) { + /* SETUP can be received without any action. + * The OUT buffer is queued before the data-in stage finishes. To avoid + * missing OUT status, firmware enables `EP_READY_ENABLE` in + * `work_handler_in()` instead of immediately after queuing the OUT status + * buffer. Therefore, no action is needed for OUT status here. + * + * If the ACK handshake of the last IN data transaction is corrupted, + * hardware will not generate the xfer_done interrupt and will not clear + * the `EP_READY_ENABLE` bit set for the IN data stage. In this case, the + * device still responds with ACK when the host initiates OUT status stage. + */ + return 0; + } + } + it82xx2_event_submit(dev, cfg->addr, IT82xx2_EVT_XFER); return 0; } @@ -761,6 +780,9 @@ static int it82xx2_xfer_in_data(const struct device *dev, uint8_t ep, struct net fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; if (ep_idx == 0) { ff_regs[ep_idx].ep_tx_fifo_ctrl = FIFO_FORCE_EMPTY; + if (udc_get_buf_info(buf)->status) { + it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_1, true); + } } else { k_sem_take(&priv->fifo_sem[fifo_idx - 1], K_FOREVER); key = irq_lock(); @@ -783,7 +805,8 @@ static int it82xx2_xfer_in_data(const struct device *dev, uint8_t ep, struct net return 0; } -static int it82xx2_xfer_out_data(const struct device *dev, uint8_t ep, struct net_buf *buf) +static int it82xx2_xfer_out_data(const struct device *dev, uint8_t ep, struct net_buf *buf, + size_t len) { const struct usb_it82xx2_config *config = dev->config; struct usb_it82xx2_regs *const usb_regs = config->base; @@ -791,7 +814,6 @@ static int it82xx2_xfer_out_data(const struct device *dev, uint8_t ep, struct ne struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; const uint8_t ep_idx = USB_EP_GET_IDX(ep); uint8_t fifo_idx; - size_t len; fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; if (ep_regs[fifo_idx].ep_status & EP_STATUS_ERROR) { @@ -799,10 +821,8 @@ static int it82xx2_xfer_out_data(const struct device *dev, uint8_t ep, struct ne return -EINVAL; } - len = (uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_lsb + - (((uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_msb) << 8); - len = MIN(net_buf_tailroom(buf), len); + uint8_t *data_ptr = net_buf_tail(buf); for (size_t idx = 0; idx < len; idx++) { @@ -871,26 +891,6 @@ static int work_handler_xfer_next(const struct device *dev, return work_handler_xfer_continue(dev, ep_cfg->addr, buf); } -/* - * Allocate buffer and initiate a new control OUT transfer, - * use successive buffer descriptor when next is true. - */ -static int it82xx2_ctrl_feed_dout(const struct device *dev, const size_t length) -{ - struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - udc_buf_put(cfg, buf); - - it82xx2_usb_set_ep_ctrl(dev, 0, EP_READY_ENABLE, true); - - return 0; -} - static bool get_extend_enable_bit(const struct device *dev, const uint8_t ep_idx) { union epn_extend_ctrl1_reg *epn_ext_ctrl1 = NULL; @@ -929,15 +929,26 @@ static bool it82xx2_fake_token(const struct device *dev, const uint8_t ep, const fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + /* On xfer_done interrupt, the firmware polls all four fifos to identify the completed + * endpoint. Because hardware doesn't clear the xfer_done flags (enabled but not ready) or + * transfer type, stale transfer type information may persist. As a result, endpoint fifo + * may be falsely detected as having completed transfer. + * + * To prevent this for the control endpoint fifo(fifo 0), we use the presence of a buffer to + * filter in and out fake tokens. For shared fifos(fifo1 to 3 are shared among ep1 to 15), + * firmware checks fifo control register for in fifo and the `SHARE_FIFO_BUSY` bit for out + * fifos. + */ switch (token_type) { case DC_IN_TRANS: if (ep_idx == 0) { + struct udc_ep_config *ep_cfg; + if (priv->stall_is_sent) { return true; } - is_fake = !udc_ctrl_stage_is_data_in(dev) && - !udc_ctrl_stage_is_status_in(dev) && - !udc_ctrl_stage_is_no_data(dev); + ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + is_fake = udc_buf_peek(ep_cfg) ? false : true; } else { if (get_fifo_ctrl(dev, fifo_idx) != BIT(ep_idx)) { is_fake = true; @@ -946,8 +957,16 @@ static bool it82xx2_fake_token(const struct device *dev, const uint8_t ep, const break; case DC_OUTDATA_TRANS: if (ep_idx == 0) { - is_fake = !udc_ctrl_stage_is_data_out(dev) && - !udc_ctrl_stage_is_status_out(dev); + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + struct net_buf *buf = udc_buf_peek(ep_cfg); + + if (!buf) { + return true; + } + + if (udc_get_buf_info(buf)->setup) { + is_fake = true; + } } else { if (!atomic_test_bit(&priv->out_fifo_state, IT82xx2_STATE_OUT_SHARED_FIFO_BUSY)) { @@ -970,7 +989,6 @@ static inline int work_handler_in(const struct device *dev, uint8_t ep) struct udc_ep_config *ep_cfg; struct net_buf *buf; uint8_t fifo_idx; - int err = 0; if (it82xx2_fake_token(dev, ep, DC_IN_TRANS)) { return 0; @@ -1012,23 +1030,19 @@ static inline int work_handler_in(const struct device *dev, uint8_t ep) udc_ep_set_busy(ep_cfg, false); if (ep == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* - * IN transfer finished, release buffer, - * Feed control OUT buffer for status stage. + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->data) { + /* The `EP_READY_ENABLE` bit enables responses to host-initiated + * transactions and is shared by all transaction types (SETUP, IN, and OUT). + * The bit is automatically cleared to 0 when the transaction completes. + * + * The `EP_READY_ENABLE` for OUT status stage must be enabled only after the + * IN data token interrupt is received; otherwise the OUT status transaction + * may be missed. */ - net_buf_unref(buf); - err = it82xx2_ctrl_feed_dout(dev, 0U); + it82xx2_usb_set_ep_ctrl(dev, 0, EP_READY_ENABLE, true); } - return err; } return udc_submit_ep_event(dev, buf, 0); @@ -1036,62 +1050,38 @@ static inline int work_handler_in(const struct device *dev, uint8_t ep) static inline int work_handler_setup(const struct device *dev, uint8_t ep) { + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; struct it82xx2_data *priv = udc_get_private(dev); - struct net_buf *buf; - int err = 0; - - if (udc_ctrl_stage_is_status_out(dev)) { - struct udc_ep_config *cfg_out; + uint8_t setup[sizeof(struct usb_setup_packet)]; + size_t len; - /* out -> setup */ - cfg_out = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - buf = udc_buf_get(cfg_out); - if (buf) { - udc_ep_set_busy(cfg_out, false); - net_buf_unref(buf); - } + if (ep_regs[0].ep_status & EP_STATUS_ERROR) { + LOG_WRN("EP0 error status 0x%02x", ep_regs[0].ep_status); + return -EINVAL; } - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - /* in -> setup */ - work_handler_in(dev, USB_CONTROL_EP_IN); - } + len = (uint16_t)ff_regs[0].ep_rx_fifo_dcnt_lsb + + (((uint16_t)ff_regs[0].ep_rx_fifo_dcnt_msb) << 8); - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate buffer"); - return -ENOMEM; + if (len != sizeof(struct usb_setup_packet)) { + LOG_DBG("setup: %d bytes read from chip", len); + return 0; } - udc_ep_buf_set_setup(buf); - it82xx2_xfer_out_data(dev, ep, buf); - if (buf->len != sizeof(struct usb_setup_packet)) { - LOG_DBG("buffer length %d read from chip", buf->len); - net_buf_unref(buf); - return 0; + for (size_t idx = 0; idx < len; idx++) { + setup[idx] = ff_regs[0].ep_rx_fifo_data; } + LOG_HEXDUMP_DBG(setup, len, "setup:"); priv->stall_is_sent = false; - LOG_HEXDUMP_DBG(buf->data, buf->len, "setup:"); - - udc_ctrl_update_stage(dev, buf); - - it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_1, true); + it82xx2_usb_set_ep_ctrl(dev, USB_CONTROL_EP_OUT, EP_DATA_SEQ_1, true); - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - err = it82xx2_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - udc_ctrl_submit_s_in_status(dev); - } else { - udc_ctrl_submit_s_status(dev); - } + udc_setup_received(dev, setup); - return err; + return 0; } static inline int work_handler_out(const struct device *dev, uint8_t ep) @@ -1121,25 +1111,24 @@ static inline int work_handler_out(const struct device *dev, uint8_t ep) len = (uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_lsb + (((uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_msb) << 8); + if (len > udc_mps_ep_size(ep_cfg)) { + LOG_ERR("Failed to handle this packet due to the packet size"); + return -ENOBUFS; + } + if (ep == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev) && len != 0) { - LOG_DBG("Handle early setup token"); + if (udc_get_buf_info(buf)->status && len != 0) { + LOG_DBG("handle early setup token, %d", len); buf = udc_buf_get(ep_cfg); - /* Notify upper layer */ - udc_ctrl_submit_status(dev, buf); - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - return 0; + return udc_submit_ep_event(dev, buf, 0); } } - if (len > udc_mps_ep_size(ep_cfg)) { - LOG_ERR("Failed to handle this packet due to the packet size"); - return -ENOBUFS; + err = it82xx2_xfer_out_data(dev, ep, buf, len); + if (err) { + return err; } - it82xx2_xfer_out_data(dev, ep, buf); - LOG_DBG("Handle data OUT, %zu | %zu", len, net_buf_tailroom(buf)); if (net_buf_tailroom(buf) && len == udc_mps_ep_size(ep_cfg)) { @@ -1157,24 +1146,12 @@ static inline int work_handler_out(const struct device *dev, uint8_t ep) udc_ep_set_busy(ep_cfg, false); - if (ep == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_1, true); - err = udc_ctrl_submit_s_out_status(dev, buf); - } - } else { + if (ep != USB_CONTROL_EP_OUT) { atomic_clear_bit(&priv->out_fifo_state, IT82xx2_STATE_OUT_SHARED_FIFO_BUSY); - err = udc_submit_ep_event(dev, buf, 0); } + err = udc_submit_ep_event(dev, buf, 0); + return err; } @@ -1200,6 +1177,9 @@ static void xfer_work_handler(const struct device *dev) err = work_handler_out(evt.dev, evt.ep); break; case IT82xx2_EVT_XFER: + if (evt.ep == USB_CONTROL_EP_OUT) { + it82xx2_usb_set_ep_ctrl(dev, 0, EP_READY_ENABLE, true); + } break; default: LOG_ERR("Unknown event type 0x%x", evt.event); diff --git a/drivers/usb/udc/udc_kinetis.c b/drivers/usb/udc/udc_kinetis.c index cb44857871aba..9f42cb55eb206 100644 --- a/drivers/usb/udc/udc_kinetis.c +++ b/drivers/usb/udc/udc_kinetis.c @@ -100,8 +100,6 @@ enum usbfsotg_event_type { USBFSOTG_EVT_DOUT, /* IN transaction for specific endpoint is finished */ USBFSOTG_EVT_DIN, - /* Workaround for clear halt in ISR */ - USBFSOTG_EVT_CLEAR_HALT, }; /* Structure for driver's endpoint events */ @@ -118,12 +116,8 @@ K_MEM_SLAB_DEFINE(usbfsotg_ee_slab, sizeof(struct usbfsotg_ep_event), struct usbfsotg_data { struct k_work work; struct k_fifo fifo; - /* - * Buffer pointers and busy flags used only for control OUT - * to map the buffers to BDs when both are occupied - */ - struct net_buf *out_buf[2]; - bool busy[2]; + struct usb_setup_packet setup; + bool setup_valid; }; static int usbfsotg_ep_clear_halt(const struct device *dev, @@ -145,6 +139,16 @@ static struct usbfsotg_bd *usbfsotg_get_ebd(const struct device *const dev, return &config->bdt[bd_idx]; } +static void usbfsotg_ebd_ctrl_discard(const struct device *const dev) +{ + const struct usbfsotg_config *config = dev->config; + + config->bdt[0].set.bd_ctrl = 0; + config->bdt[1].set.bd_ctrl = 0; + config->bdt[2].set.bd_ctrl = 0; + config->bdt[3].set.bd_ctrl = 0; +} + static bool usbfsotg_bd_is_busy(const struct usbfsotg_bd *const bd) { /* Do not use it for control OUT endpoint */ @@ -177,6 +181,22 @@ static ALWAYS_INLINE void usbfsotg_resume_tx(const struct device *dev) base->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK; } +static bool usbfsotg_is_tx_suspended(const struct device *dev) +{ + const struct usbfsotg_config *config = dev->config; + USB_Type *base = config->base; + + return base->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK; +} + +static ALWAYS_INLINE void set_control_in_pid_data1(const struct device *dev) +{ + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + + /* Set DATA1 PID for data or status stage */ + ep_cfg->stat.data1 = true; +} + static int usbfsotg_xfer_continue(const struct device *dev, struct udc_ep_config *const cfg, struct net_buf *const buf) @@ -204,10 +224,6 @@ static int usbfsotg_xfer_continue(const struct device *dev, usbfsotg_bd_set_ctrl(bd, len, data_ptr, cfg->stat.data1); - if (USB_EP_GET_IDX(cfg->addr) == 0U) { - usbfsotg_resume_tx(dev); - } - LOG_DBG("xfer %p, bd %p, ENDPT 0x%x, bd field 0x%02x", buf, bd, base->ENDPOINT[USB_EP_GET_IDX(cfg->addr)].ENDPT, bd->bd_fields); @@ -229,212 +245,28 @@ static int usbfsotg_xfer_next(const struct device *dev, return usbfsotg_xfer_continue(dev, cfg, buf); } -static inline int usbfsotg_ctrl_feed_start(const struct device *dev, - struct net_buf *const buf) -{ - struct usbfsotg_data *priv = udc_get_private(dev); - struct udc_ep_config *cfg; - struct usbfsotg_bd *bd; - size_t length; - - cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - if (priv->busy[cfg->stat.odd]) { - return -EBUSY; - } - - bd = usbfsotg_get_ebd(dev, cfg, false); - length = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); - - priv->out_buf[cfg->stat.odd] = buf; - priv->busy[cfg->stat.odd] = true; - usbfsotg_bd_set_ctrl(bd, length, net_buf_tail(buf), cfg->stat.data1); - LOG_DBG("ep0 %p|odd: %u|d: %u", buf, cfg->stat.odd, cfg->stat.data1); - - return 0; -} - -static inline int usbfsotg_ctrl_feed_start_next(const struct device *dev, - struct net_buf *const buf) +static inline int work_handler_setup(const struct device *dev) { struct usbfsotg_data *priv = udc_get_private(dev); - struct udc_ep_config *cfg; - struct usbfsotg_bd *bd; - size_t length; - cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - if (priv->busy[!cfg->stat.odd]) { - return -EBUSY; - } - - bd = usbfsotg_get_ebd(dev, cfg, true); - length = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); + usbfsotg_ebd_ctrl_discard(dev); + set_control_in_pid_data1(dev); - priv->out_buf[!cfg->stat.odd] = buf; - priv->busy[!cfg->stat.odd] = true; - usbfsotg_bd_set_ctrl(bd, length, net_buf_tail(buf), cfg->stat.data1); - LOG_DBG("ep0 %p|odd: %u|d: %u (n)", buf, cfg->stat.odd, cfg->stat.data1); + udc_setup_received(dev, priv->setup_valid ? &priv->setup : NULL); return 0; } -/* - * Allocate buffer and initiate a new control OUT transfer, - * use successive buffer descriptor when next is true. - */ -static int usbfsotg_ctrl_feed_dout(const struct device *dev, - const size_t length, - const bool next, - const bool resume_tx) +static inline int work_handler_data(const struct device *dev, + struct udc_ep_config *ep_cfg) { struct net_buf *buf; - int ret; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - if (next) { - ret = usbfsotg_ctrl_feed_start_next(dev, buf); - } else { - ret = usbfsotg_ctrl_feed_start(dev, buf); - } - - if (ret) { - net_buf_unref(buf); - return ret; - } - - if (resume_tx) { - usbfsotg_resume_tx(dev); - } - - return 0; -} - -static inline int work_handler_setup(const struct device *dev) -{ - struct net_buf *buf; - int err; - - buf = udc_buf_get(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); - if (buf == NULL) { - return -ENODATA; - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - size_t length = ROUND_UP(udc_data_stage_length(buf), USBFSOTG_EP0_SIZE); - - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - err = usbfsotg_ctrl_feed_dout(dev, length, false, true); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - /* - * Here we have to feed both descriptor tables so that - * no setup packets are lost in case of successive - * status OUT stage and next setup. - */ - LOG_DBG("s:%p|feed for -in-status >setup", buf); - err = usbfsotg_ctrl_feed_dout(dev, 8U, false, false); - if (err == 0) { - err = usbfsotg_ctrl_feed_dout(dev, 8U, true, true); - } - - /* Finally alloc buffer for IN and submit to upper layer */ - if (err == 0) { - err = udc_ctrl_submit_s_in_status(dev); - } - } else { - LOG_DBG("s:%p|feed >setup", buf); - /* - * For all other cases we feed with a buffer - * large enough for setup packet. - */ - err = usbfsotg_ctrl_feed_dout(dev, 8U, false, true); - if (err == 0) { - err = udc_ctrl_submit_s_status(dev); - } - } - - return err; -} - -static inline int work_handler_out(const struct device *dev, - struct udc_ep_config *ep_cfg) -{ - struct net_buf *buf; - int err = 0; buf = udc_buf_get(ep_cfg); if (buf == NULL) { return -ENODATA; } - if (ep_cfg->addr == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - /* s-in-status finished, next bd is already fed */ - LOG_DBG("dout:%p|no feed", buf); - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } else { - /* - * For all other cases we feed with a buffer - * large enough for setup packet. - */ - LOG_DBG("dout:%p|feed >setup", buf); - err = usbfsotg_ctrl_feed_dout(dev, 8U, false, false); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - } - } else { - err = udc_submit_ep_event(dev, buf, 0); - } - - return err; -} - -static inline int work_handler_in(const struct device *dev, - struct udc_ep_config *ep_cfg) -{ - struct net_buf *buf; - - buf = udc_buf_get(ep_cfg); - if (buf == NULL) { - return -ENODATA; - } - - if (ep_cfg->addr == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* - * IN transfer finished, release buffer, - * control OUT buffer should be already fed. - */ - net_buf_unref(buf); - } - - return 0; - } - return udc_submit_ep_event(dev, buf, 0); } @@ -483,16 +315,12 @@ static void xfer_work_handler(struct k_work *item) err = work_handler_setup(ev->dev); break; case USBFSOTG_EVT_DOUT: - err = work_handler_out(ev->dev, ep_cfg); - udc_ep_set_busy(ep_cfg, false); - break; case USBFSOTG_EVT_DIN: - err = work_handler_in(ev->dev, ep_cfg); + err = work_handler_data(ev->dev, ep_cfg); udc_ep_set_busy(ep_cfg, false); break; - case USBFSOTG_EVT_CLEAR_HALT: - err = usbfsotg_ep_clear_halt(ev->dev, ep_cfg); case USBFSOTG_EVT_XFER: + break; default: break; } @@ -525,14 +353,6 @@ static ALWAYS_INLINE bool stat_reg_is_odd(const uint8_t status) return (status & USB_STAT_ODD_MASK) >> USB_STAT_ODD_SHIFT; } -static ALWAYS_INLINE void set_control_in_pid_data1(const struct device *dev) -{ - struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - - /* Set DATA1 PID for data or status stage */ - ep_cfg->stat.data1 = true; -} - static ALWAYS_INLINE void isr_handle_xfer_done(const struct device *dev, const uint8_t istatus, const uint8_t status) @@ -560,32 +380,22 @@ static ALWAYS_INLINE void isr_handle_xfer_done(const struct device *dev, case USBFSOTG_SETUP_TOKEN: ep_cfg->stat.odd = !odd; ep_cfg->stat.data1 = true; - set_control_in_pid_data1(dev); - - if (priv->out_buf[odd] != NULL) { - net_buf_add(priv->out_buf[odd], len); - udc_ep_buf_set_setup(priv->out_buf[odd]); - udc_buf_put(ep_cfg, priv->out_buf[odd]); - priv->busy[odd] = false; - priv->out_buf[odd] = NULL; - usbfsotg_event_submit(dev, ep, USBFSOTG_EVT_SETUP); - } else { - LOG_ERR("No buffer for ep 0x00"); - udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + + /* Record whether the number of received setup data was valid */ + priv->setup_valid = len == sizeof(struct usb_setup_packet); + + /* Only copy the SETUP data if it is valid length */ + if (len == sizeof(priv->setup)) { + memcpy(&priv->setup, UINT_TO_POINTER(bd->buf_addr), sizeof(priv->setup)); } + usbfsotg_event_submit(dev, ep, USBFSOTG_EVT_SETUP); break; case USBFSOTG_OUT_TOKEN: ep_cfg->stat.odd = !odd; ep_cfg->stat.data1 = !data1; - if (ep == USB_CONTROL_EP_OUT) { - buf = priv->out_buf[odd]; - priv->busy[odd] = false; - priv->out_buf[odd] = NULL; - } else { - buf = udc_buf_peek(ep_cfg); - } + buf = udc_buf_peek(ep_cfg); if (buf == NULL) { LOG_ERR("No buffer for ep 0x%02x", ep); @@ -596,16 +406,8 @@ static ALWAYS_INLINE void isr_handle_xfer_done(const struct device *dev, net_buf_add(buf, len); if (net_buf_tailroom(buf) >= udc_mps_ep_size(ep_cfg) && len == udc_mps_ep_size(ep_cfg)) { - if (ep == USB_CONTROL_EP_OUT) { - usbfsotg_ctrl_feed_start(dev, buf); - } else { - usbfsotg_xfer_continue(dev, ep_cfg, buf); - } + usbfsotg_xfer_continue(dev, ep_cfg, buf); } else { - if (ep == USB_CONTROL_EP_OUT) { - udc_buf_put(ep_cfg, buf); - } - usbfsotg_event_submit(dev, ep, USBFSOTG_EVT_DOUT); } @@ -663,25 +465,7 @@ static void usbfsotg_isr_handler(const struct device *dev) } if (istatus & USB_ISTAT_STALL_MASK) { - struct udc_ep_config *ep_cfg; - LOG_DBG("STALL sent"); - - ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - if (ep_cfg->stat.halted) { - /* - * usbfsotg_ep_clear_halt(dev, ep_cfg); cannot - * be called in ISR context - */ - usbfsotg_event_submit(dev, USB_CONTROL_EP_OUT, - USBFSOTG_EVT_CLEAR_HALT); - } - - ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - if (ep_cfg->stat.halted) { - usbfsotg_event_submit(dev, USB_CONTROL_EP_IN, - USBFSOTG_EVT_CLEAR_HALT); - } } if (istatus & USB_ISTAT_TOKDNE_MASK) { @@ -714,8 +498,57 @@ static int usbfsotg_ep_enqueue(const struct device *dev, struct udc_ep_config *const cfg, struct net_buf *const buf) { + /* Control transfer handling code synchronization relies on USBFSOTG CTL + * TXSUSPENDTOKENBUSY bit and USB stack enqueue order. + */ + if (cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + struct usbfsotg_bd *bd; + size_t length; + bool data1 = bi->setup ? false : true; + bool tx_suspended = usbfsotg_is_tx_suspended(dev); + + if (udc_buf_peek(cfg)) { + /* STATUS OUT is already queued */ + bd = usbfsotg_get_ebd(dev, cfg, true); + } else { + bd = usbfsotg_get_ebd(dev, cfg, false); + } + + udc_buf_put(cfg, buf); + + length = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); + usbfsotg_bd_set_ctrl(bd, length, net_buf_tail(buf), data1); + + LOG_DBG("xfer %p, bd %p, ep 0x%02x, bd field 0x%02x", + buf, bd, cfg->addr, bd->bd_fields); + + if (bi->setup) { + struct udc_ep_config *cfg_in; + + cfg_in = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + usbfsotg_xfer_next(dev, cfg_in); + } + + if (bi->data || bi->setup) { + /* Signal resume only if CTL TXSUSPENDTOKENBUSY was set + * before EP0OUT BD was updated. + */ + if (tx_suspended) { + usbfsotg_resume_tx(dev); + } + } + + return 0; + } udc_buf_put(cfg, buf); + + if (cfg->addr == USB_CONTROL_EP_IN) { + /* Update BDT after USB stack is ready to process next SETUP */ + return 0; + } + if (cfg->stat.halted) { LOG_DBG("ep 0x%02x halted", cfg->addr); return 0; @@ -750,27 +583,6 @@ static int usbfsotg_ep_dequeue(const struct device *dev, return 0; } -static void ctrl_drop_out_successor(const struct device *dev) -{ - struct usbfsotg_data *priv = udc_get_private(dev); - struct udc_ep_config *cfg; - struct usbfsotg_bd *bd; - struct net_buf *buf; - - cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - - if (priv->busy[!cfg->stat.odd]) { - bd = usbfsotg_get_ebd(dev, cfg, true); - buf = priv->out_buf[!cfg->stat.odd]; - - bd->bd_fields = 0U; - priv->busy[!cfg->stat.odd] = false; - if (buf) { - net_buf_unref(buf); - } - } -} - static int usbfsotg_ep_set_halt(const struct device *dev, struct udc_ep_config *const cfg) { @@ -781,15 +593,6 @@ static int usbfsotg_ep_set_halt(const struct device *dev, cfg->stat.halted = true; LOG_DBG("Halt ep 0x%02x bd %p", cfg->addr, bd); - if (cfg->addr == USB_CONTROL_EP_IN) { - /* Drop subsequent out transfer, current can be re-used */ - ctrl_drop_out_successor(dev); - } - - if (USB_EP_GET_IDX(cfg->addr) == 0U) { - usbfsotg_resume_tx(dev); - } - return 0; } @@ -797,7 +600,6 @@ static int usbfsotg_ep_clear_halt(const struct device *dev, struct udc_ep_config *const cfg) { const struct usbfsotg_config *config = dev->config; - struct usbfsotg_data *priv = udc_get_private(dev); USB_Type *base = config->base; uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); struct usbfsotg_bd *bd; @@ -816,18 +618,7 @@ static int usbfsotg_ep_clear_halt(const struct device *dev, cfg->stat.halted = false; base->ENDPOINT[ep_idx].ENDPT &= ~USB_ENDPT_EPSTALL_MASK; - if (cfg->addr == USB_CONTROL_EP_OUT) { - if (priv->busy[cfg->stat.odd]) { - LOG_DBG("bd %p restarted", bd); - bd->set.bd_ctrl = USBFSOTG_BD_DTS | USBFSOTG_BD_OWN; - } else { - usbfsotg_ctrl_feed_dout(dev, 8U, false, false); - } - } - - if (USB_EP_GET_IDX(cfg->addr) == 0U) { - usbfsotg_resume_tx(dev); - } else { + if (USB_EP_GET_IDX(cfg->addr) != 0) { /* trigger queued transfers */ usbfsotg_event_submit(dev, cfg->addr, USBFSOTG_EVT_XFER); } @@ -839,7 +630,6 @@ static int usbfsotg_ep_enable(const struct device *dev, struct udc_ep_config *const cfg) { const struct usbfsotg_config *config = dev->config; - struct usbfsotg_data *priv = udc_get_private(dev); USB_Type *base = config->base; const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); struct usbfsotg_bd *bd_even, *bd_odd; @@ -879,20 +669,6 @@ static int usbfsotg_ep_enable(const struct device *dev, return -EINVAL; } - if (cfg->addr == USB_CONTROL_EP_OUT) { - struct net_buf *buf; - - priv->busy[0] = false; - priv->busy[1] = false; - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, USBFSOTG_EP0_SIZE); - if (buf == NULL) { - return -ENOMEM; - } - - priv->out_buf[0] = buf; - usbfsotg_bd_set_ctrl(bd_even, USBFSOTG_EP0_SIZE, buf->data, false); - } - return 0; } @@ -1069,6 +845,15 @@ static int usbfsotg_shutdown(const struct device *dev) SIM->SOPT1 &= ~SIM_SOPT1_USBREGEN_MASK; #endif + /* Cleanup BDT */ + memset(config->bdt, 0, sizeof(struct usbfsotg_bd) * config->num_of_eps * 2 * 2); + + /* All controller odd bits are reset, update internal tracking */ + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_in[i].stat.odd = false; + config->ep_cfg_out[i].stat.odd = false; + } + return 0; } diff --git a/drivers/usb/udc/udc_max32.c b/drivers/usb/udc/udc_max32.c index e2bb673c2e860..9b289fc382be2 100644 --- a/drivers/usb/udc/udc_max32.c +++ b/drivers/usb/udc/udc_max32.c @@ -64,28 +64,6 @@ struct udc_max32_data { struct req_cb_data *req_cb_data; }; -static void udc_event_xfer_ctrl_status(const struct device *dev, struct net_buf *const buf) -{ - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - MXC_USB_Ackstat(0); - - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - if (udc_ctrl_stage_is_data_in(dev)) { - /* - * s-in-[status] finished, release buffer. - * Since the controller supports auto-status we cannot use - * if (udc_ctrl_stage_is_status_out()) after state update. - */ - net_buf_unref(buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); -} - /* * ISR-context callbacks: These are called from MXC_USB_EventHandler() in * interrupt context. @@ -137,12 +115,6 @@ static void udc_event_xfer_in(const struct device *dev, struct udc_ep_config *ep return; } - if (buf->len == 0 && ep_cfg->addr == USB_CONTROL_EP_IN) { - buf = udc_buf_get(ep_cfg); - udc_event_xfer_ctrl_status(dev, buf); - return; - } - req_cb_data->dev = dev; req_cb_data->ep = ep_cfg->addr; @@ -184,6 +156,22 @@ static void udc_event_xfer_out(const struct device *dev, struct udc_ep_config *e return; } + /* + * Skip ReadEndpoint call for STATUS OUT xfers with no data. + * These are auto-ACKED by hardware. If ReadEndpoint is called with a + * zero-length buffer, the driver will treat it as a normal OUT transfer + * and wait for data, which will never come, causing a timeout. + * + * For setup events, they are handled by a separate interrupt, SUDAV. + */ + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (!bi->data) { + return; + } + } + req_cb_data->dev = dev; req_cb_data->ep = ep_cfg->addr; @@ -194,7 +182,12 @@ static void udc_event_xfer_out(const struct device *dev, struct udc_ep_config *e ep_request->error_code = 0; ep_request->callback = udc_event_xfer_out_callback; ep_request->cbdata = req_cb_data; - ep_request->type = MAXUSB_TYPE_PKT; + + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + ep_request->type = MAXUSB_TYPE_TRANS; + } else { + ep_request->type = MAXUSB_TYPE_PKT; + } udc_ep_set_busy(ep_cfg, true); ret = MXC_USB_ReadEndpoint(ep_request); @@ -228,11 +221,7 @@ static void udc_event_xfer_in_done(const struct device *dev, struct udc_ep_confi udc_ep_buf_clear_zlp(buf); } - if (ep_cfg->addr == USB_CONTROL_EP_IN) { - udc_event_xfer_ctrl_status(dev, buf); - } else { - udc_submit_ep_event(dev, buf, 0); - } + udc_submit_ep_event(dev, buf, 0); /* Start the next transfer if there is another waiting */ if (ep_cfg->addr != USB_CONTROL_EP_IN && udc_buf_peek(ep_cfg) != NULL) { @@ -257,14 +246,7 @@ static void udc_event_xfer_out_done(const struct device *dev, struct udc_ep_conf return; } - if (ep_cfg->addr == USB_CONTROL_EP_OUT) { - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - udc_ctrl_submit_s_out_status(dev, buf); - } else { - udc_submit_ep_event(dev, buf, 0); - } + udc_submit_ep_event(dev, buf, 0); /* Start the next transfer if there is another waiting */ if (ep_cfg->addr != USB_CONTROL_EP_OUT && udc_buf_peek(ep_cfg) != NULL) { @@ -272,93 +254,33 @@ static void udc_event_xfer_out_done(const struct device *dev, struct udc_ep_conf } } -static int udc_ctrl_feed_dout(const struct device *dev, const size_t length) +static int udc_event_setup(const struct device *dev) { - struct udc_max32_data *priv = udc_get_private(dev); - const struct udc_max32_config *config = dev->config; - MXC_USB_Req_t *ep_request = &priv->ep_request[USB_EP_GET_IDX(USB_CONTROL_EP_OUT)]; - struct req_cb_data *req_cb_data = &priv->req_cb_data[USB_EP_GET_IDX(USB_CONTROL_EP_OUT)]; - struct net_buf *buf; - int ret; - - /* Allocate buffer for data stage OUT */ - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - memset(buf->data, 0, length); - udc_buf_put(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT), buf); - - req_cb_data->dev = dev; - req_cb_data->ep = USB_CONTROL_EP_OUT; - - ep_request->ep = USB_EP_GET_IDX(USB_CONTROL_EP_OUT); - ep_request->data = buf->data; - ep_request->reqlen = length; - ep_request->actlen = 0; - ep_request->error_code = 0; - ep_request->callback = udc_event_xfer_out_callback; - ep_request->cbdata = req_cb_data; - ep_request->type = MAXUSB_TYPE_TRANS; + int ret = 0; + MXC_USB_SetupPkt setup_pkt; - ret = MXC_USB_ReadEndpoint(ep_request); + /* Get setup data from FIFO */ + ret = MXC_USB_GetSetup(&setup_pkt); if (ret != 0) { - LOG_ERR("ep 0x%02x error: %x", USB_CONTROL_EP_OUT, ret); - udc_submit_ep_event(dev, buf, -ECONNREFUSED); + LOG_ERR("Failed to get setup data"); + return ret; } - /* - * Set the SERV_OUTPKTRDY bit to trigger the interrupt after creating a read request. - * Otherwise program miss this interrupt because of race condition. - */ - config->base->csr0 |= MXC_F_USBHS_CSR0_SERV_OUTPKTRDY; - - return ret; -} - -static int udc_event_setup(const struct device *dev) -{ - const struct udc_max32_config *config = dev->config; - struct net_buf *buf; - int ret; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate for setup"); - return -ENOMEM; + /* Hardware requires explicit ACK for SETUP if no follow-on data */ + if (setup_pkt.wLength == 0) { + MXC_USB_Ackstat(0); } - udc_ep_buf_set_setup(buf); - memset(buf->data, 0, sizeof(MXC_USB_SetupPkt)); - if (MXC_USB_GetSetup((MXC_USB_SetupPkt *)buf->data) < 0) { - LOG_ERR("Failed to get setup data"); - return -1; + /* Clear EP0 previous requests */ + ret = MXC_USB_ResetEp(0); + if (ret != 0) { + LOG_ERR("Failed to reset EP0"); + return ret; } - net_buf_add(buf, sizeof(MXC_USB_SetupPkt)); - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); + udc_setup_received(dev, &setup_pkt); - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - ret = udc_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (ret == -ENOMEM) { - ret = udc_submit_ep_event(dev, buf, ret); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - /* - * Moved following line from MSDK driver to here because of the solution of - * ctrl_data_out stage's problem. - */ - config->base->csr0 |= MXC_F_USBHS_CSR0_SERV_OUTPKTRDY; - LOG_INF("Setup: IN"); - ret = udc_ctrl_submit_s_in_status(dev); - } else { - ret = udc_ctrl_submit_s_status(dev); - } - - return ret; + return 0; } static ALWAYS_INLINE void max32_thread_handler(void *const arg) @@ -745,6 +667,7 @@ static int udc_max32_driver_preinit(const struct device *dev) data->caps.rwup = true; data->caps.can_detect_vbus = true; data->caps.out_ack = true; + data->caps.addr_before_status = true; data->caps.mps0 = UDC_MPS0_64; if (config->speed_idx == 2) { data->caps.hs = true; diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index d586435c0fea5..0b97d023b8faa 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -3,6 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ + #define DT_DRV_COMPAT nxp_ehci #include @@ -170,49 +171,9 @@ static int udc_mcux_ep_try_feed(const struct device *dev, return 0; } -/* - * Allocate buffer and initiate a new control OUT transfer. - */ -static int udc_mcux_ctrl_feed_dout(const struct device *dev, - const size_t length) -{ - struct net_buf *buf; - struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - int ret; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - k_fifo_put(&cfg->fifo, buf); - - ret = udc_mcux_ep_feed(dev, cfg, buf); - - if (ret) { - net_buf_unref(buf); - return ret; - } - - return 0; -} - static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_packet *setup) { - int err; - struct net_buf *buf; - LOG_DBG("setup packet"); - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, - sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate for setup"); - return -EIO; - } - - udc_ep_buf_set_setup(buf); - memcpy(buf->data, setup, 8); - net_buf_add(buf, 8); if (setup->RequestType.type == USB_REQTYPE_TYPE_STANDARD && setup->RequestType.direction == USB_REQTYPE_DIR_TO_DEVICE && @@ -222,83 +183,32 @@ static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_pac &setup->wValue); } - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (!buf->len) { - return -EIO; - } - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - err = udc_mcux_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - err = udc_ctrl_submit_s_in_status(dev); - } else { - err = udc_ctrl_submit_s_status(dev); - } + udc_setup_received(dev, setup); - return err; + return 0; } static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) { - int err = 0; uint32_t len; len = MIN(net_buf_tailroom(buf), mcux_len); net_buf_add(buf, len); - if (udc_ctrl_stage_is_status_out(dev)) { - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - /* Status stage finished, notify upper layer */ - err = udc_ctrl_submit_status(dev, buf); - } else { - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - } - - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - } - return err; + return udc_submit_ep_event(dev, buf, 0); } static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) { - int err = 0; uint32_t len; len = MIN(buf->len, mcux_len); buf->data += len; buf->len -= len; - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - err = udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* - * IN transfer finished, release buffer, - * control OUT buffer should be already fed. - */ - net_buf_unref(buf); - err = udc_mcux_ctrl_feed_dout(dev, 0u); - } - - return err; + return udc_submit_ep_event(dev, buf, 0); } static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep, @@ -471,7 +381,6 @@ static void udc_mcux_work_handler(struct k_work *item) 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)) { @@ -593,6 +502,17 @@ static int udc_mcux_ep_enqueue(const struct device *dev, struct udc_ep_config *const cfg, struct net_buf *const buf) { + if (cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + udc_buf_put(cfg, buf); + + /* SETUP can be received without any action */ + return 0; + } + } + udc_buf_put(cfg, buf); if (cfg->stat.halted) { LOG_DBG("ep 0x%02x halted", cfg->addr); @@ -689,11 +609,32 @@ static int udc_mcux_set_address(const struct device *dev, const uint8_t addr) static int udc_mcux_enable(const struct device *dev) { + 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"); + } + + 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 udc_mcux_control(dev, kUSB_DeviceControlRun, NULL); } static int udc_mcux_disable(const struct device *dev) { + struct udc_ep_config *cfg; + + 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); + } + return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL); } diff --git a/drivers/usb/udc/udc_mcux_ip3511.c b/drivers/usb/udc/udc_mcux_ip3511.c index c6aab0d0bb1b1..273a137595cde 100644 --- a/drivers/usb/udc/udc_mcux_ip3511.c +++ b/drivers/usb/udc/udc_mcux_ip3511.c @@ -157,49 +157,9 @@ static int udc_mcux_ep_try_feed(const struct device *dev, return 0; } -/* - * Allocate buffer and initiate a new control OUT transfer. - */ -static int udc_mcux_ctrl_feed_dout(const struct device *dev, - const size_t length) -{ - struct net_buf *buf; - struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - int ret; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - k_fifo_put(&cfg->fifo, buf); - - ret = udc_mcux_ep_feed(dev, cfg, buf); - - if (ret) { - net_buf_unref(buf); - return ret; - } - - return 0; -} - static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_packet *setup) { - int err; - struct net_buf *buf; - LOG_DBG("setup packet"); - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, - sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate for setup"); - return -EIO; - } - - udc_ep_buf_set_setup(buf); - memcpy(buf->data, setup, 8); - net_buf_add(buf, 8); if (setup->RequestType.type == USB_REQTYPE_TYPE_STANDARD && setup->RequestType.direction == USB_REQTYPE_DIR_TO_DEVICE && @@ -209,83 +169,32 @@ static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_pac &setup->wValue); } - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (!buf->len) { - return -EIO; - } - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - err = udc_mcux_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - err = udc_ctrl_submit_s_in_status(dev); - } else { - err = udc_ctrl_submit_s_status(dev); - } + udc_setup_received(dev, setup); - return err; + return 0; } static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) { - int err = 0; uint32_t len; len = MIN(net_buf_tailroom(buf), mcux_len); net_buf_add(buf, len); - if (udc_ctrl_stage_is_status_out(dev)) { - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - /* Status stage finished, notify upper layer */ - err = udc_ctrl_submit_status(dev, buf); - } else { - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - } - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - } - - return err; + return udc_submit_ep_event(dev, buf, 0); } static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) { - int err = 0; uint32_t len; len = MIN(buf->len, mcux_len); buf->data += len; buf->len -= len; - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - err = udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* - * IN transfer finished, release buffer, - * control OUT buffer should be already fed. - */ - net_buf_unref(buf); - err = udc_mcux_ctrl_feed_dout(dev, 0u); - } - - return err; + return udc_submit_ep_event(dev, buf, 0); } static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep, @@ -458,7 +367,6 @@ static void udc_mcux_work_handler(struct k_work *item) 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)) { @@ -580,6 +488,17 @@ static int udc_mcux_ep_enqueue(const struct device *dev, struct udc_ep_config *const cfg, struct net_buf *const buf) { + if (cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + udc_buf_put(cfg, buf); + + /* SETUP can be received without any action */ + return 0; + } + } + udc_buf_put(cfg, buf); if (cfg->stat.halted) { LOG_DBG("ep 0x%02x halted", cfg->addr); @@ -676,11 +595,32 @@ static int udc_mcux_set_address(const struct device *dev, const uint8_t addr) static int udc_mcux_enable(const struct device *dev) { + 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"); + } + + 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 udc_mcux_control(dev, kUSB_DeviceControlRun, NULL); } static int udc_mcux_disable(const struct device *dev) { + struct udc_ep_config *cfg; + + 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); + } + return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL); } diff --git a/drivers/usb/udc/udc_nrf.c b/drivers/usb/udc/udc_nrf.c index 5e86061f46e05..d911106f190fa 100644 --- a/drivers/usb/udc/udc_nrf.c +++ b/drivers/usb/udc/udc_nrf.c @@ -43,8 +43,6 @@ enum udc_nrf_event_type { UDC_NRF_EVT_RESUME, /* Remote Wakeup initiated */ UDC_NRF_EVT_WUREQ, - /* Let controller perform status stage */ - UDC_NRF_EVT_STATUS_IN, }; /* Main events the driver thread waits for */ @@ -67,6 +65,7 @@ static struct k_thread drv_stack_data; static struct udc_ep_config ep_cfg_out[CFG_EPOUT_CNT + CFG_EP_ISOOUT_CNT + 1]; static struct udc_ep_config ep_cfg_in[CFG_EPIN_CNT + CFG_EP_ISOIN_CNT + 1]; +static bool udc_nrf_ctrl_data_in_finished; static bool udc_nrf_setup_set_addr, udc_nrf_fake_setup; static uint8_t udc_nrf_address; const static struct device *udc_nrf_dev; @@ -1178,52 +1177,34 @@ static void udc_event_xfer_in_next(const struct device *dev, const uint8_t ep) } buf = udc_buf_peek(ep_cfg); - if (buf != NULL) { - nrf_usbd_start_transfer(ep); - udc_ep_set_busy(ep_cfg, true); - } -} - -static void udc_event_xfer_ctrl_in(const struct device *dev, - struct net_buf *const buf) -{ - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - if (udc_ctrl_stage_is_data_in(dev)) { - /* - * s-in-[status] finished, release buffer. - * Since the controller supports auto-status we cannot use - * if (udc_ctrl_stage_is_status_out()) after state update. - */ - net_buf_unref(buf); + if (buf == NULL) { + return; } - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); + if (ep == USB_CONTROL_EP_IN) { + const struct udc_buf_info *bi = udc_get_buf_info(buf); - if (!udc_nrf_setup_set_addr) { - /* Allow status stage */ - NRF_USBD->TASKS_EP0STATUS = 1; - } -} + if (bi->data) { + m_ep0_data_dir = USB_CONTROL_EP_IN; + } -static void udc_event_fake_status_in(const struct device *dev) -{ - struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - struct net_buf *buf; + if (bi->status) { + if (!udc_nrf_setup_set_addr) { + /* Allow status stage */ + NRF_USBD->TASKS_EP0STATUS = 1; + } - buf = udc_buf_get(ep_cfg); - if (unlikely(buf == NULL)) { - LOG_DBG("ep 0x%02x queue is empty", USB_CONTROL_EP_IN); - return; + /* Controller automatically performs status IN + * stage and SW cannot know when it is done. + */ + buf = udc_buf_get(ep_cfg); + udc_submit_ep_event(dev, buf, 0); + return; + } } - LOG_DBG("Fake status IN %p", buf); - udc_event_xfer_ctrl_in(dev, buf); + nrf_usbd_start_transfer(ep); + udc_ep_set_busy(ep_cfg, true); } static void udc_event_xfer_in(const struct device *dev, const uint8_t ep) @@ -1241,26 +1222,25 @@ static void udc_event_xfer_in(const struct device *dev, const uint8_t ep) } udc_ep_set_busy(ep_cfg, false); + udc_submit_ep_event(dev, buf, 0); + if (ep == USB_CONTROL_EP_IN) { - udc_event_xfer_ctrl_in(dev, buf); - } else { - udc_submit_ep_event(dev, buf, 0); - } -} + __ASSERT(udc_get_buf_info(buf)->data, "EP0IN buf is not data"); -static void udc_event_xfer_ctrl_out(const struct device *dev, - struct net_buf *const buf) -{ - /* - * In case s-in-status, controller supports auto-status therefore we - * do not have to call udc_ctrl_stage_is_status_out(). - */ + udc_nrf_ctrl_data_in_finished = true; - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); + /* STALL any further IN tokens, allow status stage */ + NRF_USBD->TASKS_EP0STATUS = 1; - if (udc_ctrl_stage_is_status_in(dev)) { - udc_ctrl_submit_s_out_status(dev, buf); + /* Software won't know when status stage finishes, if we have + * status OUT pending, just complete it. + */ + ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + buf = udc_buf_get(ep_cfg); + if (buf != NULL) { + __ASSERT(udc_get_buf_info(buf)->status, "EP0OUT buf is not status"); + udc_submit_ep_event(dev, buf, 0); + } } } @@ -1275,6 +1255,30 @@ static void udc_event_xfer_out_next(const struct device *dev, const uint8_t ep) buf = udc_buf_peek(ep_cfg); if (buf != NULL) { + if (ep == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + /* SETUP can be received without any action */ + return; + } + + if (bi->data) { + m_ep0_data_dir = USB_CONTROL_EP_OUT; + + /* Allow receiving first OUT Data Stage packet */ + NRF_USBD->TASKS_EP0RCVOUT = 1; + } + + if (bi->status) { + if (udc_nrf_ctrl_data_in_finished) { + udc_submit_ep_event(dev, buf, 0); + } + + return; + } + } + nrf_usbd_start_transfer(ep); udc_ep_set_busy(ep_cfg, true); } else { @@ -1295,71 +1299,20 @@ static void udc_event_xfer_out(const struct device *dev, const uint8_t ep) } udc_ep_set_busy(ep_cfg, false); - if (ep == USB_CONTROL_EP_OUT) { - udc_event_xfer_ctrl_out(dev, buf); - } else { - udc_submit_ep_event(dev, buf, 0); - } -} - -static int usbd_ctrl_feed_dout(const struct device *dev, - const size_t length) -{ - struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - udc_buf_put(cfg, buf); - - __ASSERT_NO_MSG(k_current_get() == &drv_stack_data); - udc_event_xfer_out_next(dev, USB_CONTROL_EP_OUT); - - /* Allow receiving first OUT Data Stage packet */ - NRF_USBD->TASKS_EP0RCVOUT = 1; - - return 0; + udc_submit_ep_event(dev, buf, 0); } static int udc_event_xfer_setup(const struct device *dev) { - struct udc_ep_config *cfg_out = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct udc_ep_config *cfg_in = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - struct usb_setup_packet *setup; - struct net_buf *buf; - int err; + struct usb_setup_packet setup; - /* Make sure there isn't any obsolete data stage buffer queued */ - buf = udc_buf_get_all(cfg_out); - if (buf) { - net_buf_unref(buf); - } + udc_nrf_ctrl_data_in_finished = false; - buf = udc_buf_get_all(cfg_in); - if (buf) { - net_buf_unref(buf); - } - - udc_ep_set_busy(cfg_out, false); - udc_ep_set_busy(cfg_in, false); - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, - sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate for setup"); - return -ENOMEM; - } - - udc_ep_buf_set_setup(buf); - setup = (struct usb_setup_packet *)buf->data; - setup->bmRequestType = NRF_USBD->BMREQUESTTYPE; - setup->bRequest = NRF_USBD->BREQUEST; - setup->wValue = NRF_USBD->WVALUEL | (NRF_USBD->WVALUEH << 8); - setup->wIndex = NRF_USBD->WINDEXL | (NRF_USBD->WINDEXH << 8); - setup->wLength = NRF_USBD->WLENGTHL | (NRF_USBD->WLENGTHH << 8); + setup.bmRequestType = NRF_USBD->BMREQUESTTYPE; + setup.bRequest = NRF_USBD->BREQUEST; + setup.wValue = NRF_USBD->WVALUEL | (NRF_USBD->WVALUEH << 8); + setup.wIndex = NRF_USBD->WINDEXL | (NRF_USBD->WINDEXH << 8); + setup.wLength = NRF_USBD->WLENGTHL | (NRF_USBD->WLENGTHH << 8); /* USBD peripheral automatically handles Set Address in slightly * different manner than the USB stack. @@ -1385,10 +1338,10 @@ static int udc_event_xfer_setup(const struct device *dev) * device STALLs status stage and address remains unchanged. */ udc_nrf_setup_set_addr = - setup->bmRequestType == 0 && - setup->bRequest == USB_SREQ_SET_ADDRESS; + setup.bmRequestType == 0 && + setup.bRequest == USB_SREQ_SET_ADDRESS; if (udc_nrf_setup_set_addr) { - if (setup->wLength) { + if (setup.wLength) { /* Currently USB stack only STALLs OUT Data Stage when * buffer allocation fails. To prevent the device from * ACKing the Data Stage, simply ignore the request @@ -1399,7 +1352,6 @@ static int udc_event_xfer_setup(const struct device *dev) * equal to current device address). If host does not * issue IN token then the mismatch will be avoided. */ - net_buf_unref(buf); return 0; } @@ -1409,8 +1361,8 @@ static int udc_event_xfer_setup(const struct device *dev) * Just clear the bits so stack will handle the request in the * same way as USBD peripheral does, avoiding the mismatch. */ - setup->wValue &= 0x7F; - setup->wIndex = 0; + setup.wValue &= 0x7F; + setup.wIndex = 0; } if (!udc_nrf_setup_set_addr && udc_nrf_address != NRF_USBD->USBADDR) { @@ -1420,36 +1372,18 @@ static int udc_event_xfer_setup(const struct device *dev) udc_nrf_fake_setup = true; udc_nrf_setup_set_addr = true; - setup->bmRequestType = 0; - setup->bRequest = USB_SREQ_SET_ADDRESS; - setup->wValue = NRF_USBD->USBADDR; - setup->wIndex = 0; - setup->wLength = 0; + setup.bmRequestType = 0; + setup.bRequest = USB_SREQ_SET_ADDRESS; + setup.wValue = NRF_USBD->USBADDR; + setup.wIndex = 0; + setup.wLength = 0; } else { udc_nrf_fake_setup = false; } - net_buf_add(buf, sizeof(nrf_usbd_common_setup_t)); - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - m_ep0_data_dir = USB_CONTROL_EP_OUT; - err = usbd_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - m_ep0_data_dir = USB_CONTROL_EP_IN; - err = udc_ctrl_submit_s_in_status(dev); - } else { - err = udc_ctrl_submit_s_status(dev); - } + udc_setup_received(dev, &setup); - return err; + return 0; } static void udc_nrf_thread_handler(const struct device *dev) @@ -1517,10 +1451,6 @@ static void udc_nrf_thread_handler(const struct device *dev) } } - if (evt & BIT(UDC_NRF_EVT_STATUS_IN)) { - udc_event_fake_status_in(dev); - } - if (evt & BIT(UDC_NRF_EVT_SETUP)) { udc_event_xfer_setup(dev); } @@ -1564,16 +1494,6 @@ static int udc_nrf_ep_enqueue(const struct device *dev, { udc_buf_put(cfg, buf); - if (cfg->addr == USB_CONTROL_EP_IN && buf->len == 0) { - const struct udc_buf_info *bi = udc_get_buf_info(buf); - - if (bi->status) { - /* Controller automatically performs status IN stage */ - k_event_post(&drv_evt, BIT(UDC_NRF_EVT_STATUS_IN)); - return 0; - } - } - atomic_set_bit(&xfer_new, ep2bit(cfg->addr)); k_event_post(&drv_evt, BIT(UDC_NRF_EVT_XFER)); @@ -1855,7 +1775,6 @@ static int udc_nrf_driver_init(const struct device *dev) } data->caps.rwup = true; - data->caps.out_ack = true; data->caps.mps0 = UDC_NRF_MPS0; data->caps.can_detect_vbus = true; diff --git a/drivers/usb/udc/udc_numaker.c b/drivers/usb/udc/udc_numaker.c index 4092fbcc96959..cde57ac03fa37 100644 --- a/drivers/usb/udc/udc_numaker.c +++ b/drivers/usb/udc/udc_numaker.c @@ -85,6 +85,8 @@ enum numaker_usbd_msg_type { NUMAKER_USBD_MSG_TYPE_OUT, /* IN transaction for specific EP completed */ NUMAKER_USBD_MSG_TYPE_IN, + /* Control transfer status stage completed */ + NUMAKER_USBD_MSG_TYPE_STATUS, /* Re-activate queued transfer for specific EP */ NUMAKER_USBD_MSG_TYPE_XFER, /* S/W reconnect */ @@ -182,8 +184,6 @@ struct numaker_usbd_ep_mgmt { /* Mutable device context */ struct udc_numaker_data { - uint8_t addr; /* Host assigned USB device address */ - struct k_msgq *msgq; struct numaker_usbd_ep_mgmt ep_mgmt; /* EP management */ @@ -204,6 +204,8 @@ struct udc_numaker_data { #if defined(CONFIG_UDC_NUMAKER_DMA) struct k_sem sem_dma_done; #endif + + bool status_out; }; static inline void numaker_usbd_sw_connect(const struct device *dev) @@ -227,8 +229,8 @@ static inline void numaker_usbd_sw_connect(const struct device *dev) COND_CODE_1(CONFIG_UDC_ENABLE_SOF, (HSUSBD_BUSINTEN_SOFIEN_Msk), (0)); /* CPU load concern */ base->CEPINTEN = HSUSBD_CEPINTEN_STSDONEIEN_Msk | HSUSBD_CEPINTEN_ERRIEN_Msk | - HSUSBD_CEPINTEN_STALLIEN_Msk | HSUSBD_CEPINTEN_SETUPPKIEN_Msk | - HSUSBD_CEPINTEN_SETUPTKIEN_Msk; + HSUSBD_CEPINTEN_STALLIEN_Msk | HSUSBD_CEPINTEN_TXPKIEN_Msk | + HSUSBD_CEPINTEN_SETUPPKIEN_Msk; /* Enable USB handshake * @@ -287,7 +289,6 @@ static inline void numaker_usbd_sw_reconnect(const struct device *dev) static inline void numaker_usbd_reset_addr(const struct device *dev) { const struct udc_numaker_config *config = dev->config; - struct udc_numaker_data *priv = udc_get_private(dev); if (config->is_hsusbd) { HSUSBD_T *base = config->base; @@ -298,28 +299,6 @@ static inline void numaker_usbd_reset_addr(const struct device *dev) base->FADDR = 0; } - - priv->addr = 0; -} - -static inline void numaker_usbd_set_addr(const struct device *dev) -{ - const struct udc_numaker_config *config = dev->config; - struct udc_numaker_data *priv = udc_get_private(dev); - - if (config->is_hsusbd) { - HSUSBD_T *base = config->base; - - if (base->FADDR != priv->addr) { - base->FADDR = priv->addr; - } - } else { - USBD_T *base = config->base; - - if (base->FADDR != priv->addr) { - base->FADDR = priv->addr; - } - } } /* USBD/HSUSBD EP base by EP index e.g. EP0/EPA, EP1/EPB, etc. */ @@ -758,10 +737,6 @@ static void numaker_hsusbd_bus_reset_th(const struct device *dev) ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx); if (ep_cur->ep_hw_idx == CEP) { - /* Disable CEP interrupt (exclude Setup) */ - base->CEPINTEN &= ~(HSUSBD_CEPINTEN_TXPKIEN_Msk | - HSUSBD_CEPINTEN_RXPKIEN_Msk); - /* Flush CEP FIFO */ base->CEPCTL = HSUSBD_CEPCTL_FLUSH | HSUSBD_CEPCTL_NAKCLR_Msk; @@ -899,21 +874,26 @@ static void numaker_usbd_ep_th(const struct device *dev, uint32_t ep_hw_idx) ep_idx = (ep_base->CFG & USBD_CFG_EPNUM_Msk) >> USBD_CFG_EPNUM_Pos; ep = USB_EP_GET_ADDR(ep_idx, ep_dir); - /* NOTE: See comment in udc_numaker_set_address()'s implementation - * for safe place to change USB device address - */ - if (ep == USB_EP_GET_ADDR(0, USB_EP_DIR_IN)) { - numaker_usbd_set_addr(dev); - } - /* NOTE: See comment on mxpld_ctrlout for why make one copy of * CTRL OUT's MXPLD */ if (ep == USB_EP_GET_ADDR(0, USB_EP_DIR_OUT)) { + const struct udc_numaker_config *config = dev->config; struct numaker_usbd_ep *ep_ctrlout = priv->ep_pool + 0; + USBD_T *base = config->base; ep_ctrlout->mxpld_ctrlout = (ep_base->MXPLD & USBD_MXPLD_MXPLD_Msk) >> USBD_MXPLD_MXPLD_Pos; + + if (base->INTSTS & USBD_INTSTS_SETUP) { + /* We don't know if MXPLD was read before or after SETUP + * was set. There is no way to know if e.g. status stage + * was successfully completed before SETUP was received. + * Fortunately, it does not matter from USB protocol + * point of view, so we just return here. + */ + return; + } } /* Message for bottom-half processing */ @@ -934,27 +914,13 @@ static void numaker_hsusbd_cep_th(const struct device *dev, uint32_t cepintsts) HSUSBD_T *base = config->base; struct numaker_usbd_msg msg = {0}; - /* Setup packet */ - if (cepintsts & HSUSBD_CEPINTSTS_SETUPPKIF_Msk) { - /* By USB spec, following transactions, regardless of Data/Status stage, - * will always be DATA1. HSUBSD will handle the toggle by itself and needn't - * extra control. - */ - - /* Message for bottom-half processing */ - /* NOTE: In Zephyr USB device stack, Setup packet is passed via - * CTRL OUT EP - */ - msg.type = NUMAKER_USBD_MSG_TYPE_SETUP; - numaker_usbd_setup_copy_to_user(dev, msg.setup.packet); - numaker_usbd_send_msg(dev, &msg); - } - /* Data packet received */ if (cepintsts & HSUSBD_CEPINTSTS_RXPKIF_Msk) { /* Block until next CEP trigger */ base->CEPINTEN &= ~HSUSBD_CEPINTEN_RXPKIEN_Msk; + base->CEPINTSTS = HSUSBD_CEPINTSTS_RXPKIF_Msk; + /* Message for bottom-half processing */ msg.type = NUMAKER_USBD_MSG_TYPE_OUT; msg.out.ep = USB_CONTROL_EP_OUT; @@ -963,8 +929,7 @@ static void numaker_hsusbd_cep_th(const struct device *dev, uint32_t cepintsts) /* Data packet transmitted */ if (cepintsts & HSUSBD_CEPINTSTS_TXPKIF_Msk) { - /* Block until next CEP trigger */ - base->CEPINTEN &= ~HSUSBD_CEPINTEN_TXPKIEN_Msk; + base->CEPINTSTS = HSUSBD_CEPINTSTS_TXPKIF_Msk; /* Message for bottom-half processing */ msg.type = NUMAKER_USBD_MSG_TYPE_IN; @@ -974,23 +939,45 @@ static void numaker_hsusbd_cep_th(const struct device *dev, uint32_t cepintsts) /* Status stage completed */ if (cepintsts & HSUSBD_CEPINTSTS_STSDONEIF_Msk) { - /* NOTE: See comment in udc_numaker_set_address()'s implementation - * for safe place to change USB device address - */ - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - numaker_usbd_set_addr(dev); - } + struct udc_numaker_data *priv = udc_get_private(dev); + + base->CEPINTSTS = HSUSBD_CEPINTSTS_STSDONEIF_Msk; /* Message for bottom-half processing */ - if (udc_ctrl_stage_is_status_out(dev)) { - msg.type = NUMAKER_USBD_MSG_TYPE_OUT; + msg.type = NUMAKER_USBD_MSG_TYPE_STATUS; + if (priv->status_out) { msg.out.ep = USB_CONTROL_EP_OUT; } else { - msg.type = NUMAKER_USBD_MSG_TYPE_IN; msg.in.ep = USB_CONTROL_EP_IN; } numaker_usbd_send_msg(dev, &msg); } + + /* If OUT DATA1 is received after SETUP, then HSUSBD will not set RXPKIF + * bit until SETUPPKIF is cleared. If RXPKIF is set simultaneously with + * SETUPPKIF, then OUT DATAx happened on the bus before SETUP DATA0. + * Therefore, SETUPPKIF must be checked as the last item. + */ + if (cepintsts & HSUSBD_CEPINTSTS_SETUPPKIF_Msk) { + /* Disable RXPKIEN until Data OUT is enqueued */ + base->CEPINTEN &= ~HSUSBD_CEPINTEN_RXPKIEN_Msk; + + /* HSUSBD will not set RXPKIF before SETUPPKIF */ + base->CEPINTSTS = HSUSBD_CEPINTSTS_SETUPPKIF_Msk; + + /* By USB spec, following transactions, regardless of Data/Status stage, + * will always be DATA1. HSUSBD will handle the toggle by itself and needn't + * extra control. + */ + + /* Message for bottom-half processing */ + /* NOTE: In Zephyr USB device stack, Setup packet is passed via + * CTRL OUT EP + */ + msg.type = NUMAKER_USBD_MSG_TYPE_SETUP; + numaker_usbd_setup_copy_to_user(dev, msg.setup.packet); + numaker_usbd_send_msg(dev, &msg); + } } /* Interrupt top half processing for BULK/INT/ISO transfer */ @@ -1549,13 +1536,6 @@ static void numaker_hsusbd_ep_disable(struct numaker_usbd_ep *ep_cur) HSUSBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx); if (ep_cur->ep_hw_idx == CEP) { - /* Disable CEP local interrupt */ - if (USB_EP_DIR_IS_IN(ep_cur->addr)) { - base->CEPINTEN &= ~HSUSBD_CEPINTEN_TXPKIEN_Msk; - } else { - base->CEPINTEN &= ~HSUSBD_CEPINTEN_RXPKIEN_Msk; - } - /* CEP global interrupt shouldn't get disabled for resident. */ } else { /* Disable EP local interrupt */ @@ -1584,26 +1564,9 @@ static void numaker_hsusbd_ep_trigger(struct numaker_usbd_ep *ep_cur, uint32_t l if (ep_cur->ep_hw_idx == CEP) { if (USB_EP_DIR_IS_IN(ep_cur->addr)) { - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - /* Unleash Status stage */ - base->CEPCTL = HSUSBD_CEPCTL_NAKCLR; - } - - if (len == 0) { - base->CEPCTL = HSUSBD_CEPCTL_ZEROLEN | HSUSBD_CEPCTL_NAKCLR_Msk; - } else { - __ASSERT_NO_MSG(len <= ep_cur->mps); - base->CEPTXCNT = len; - } - - /* Enable CEP interrupt */ - base->CEPINTEN |= HSUSBD_CEPINTEN_TXPKIEN_Msk; + __ASSERT_NO_MSG(len <= ep_cur->mps); + base->CEPTXCNT = len; } else { - if (udc_ctrl_stage_is_status_out(dev)) { - /* Unleash Status stage */ - base->CEPCTL = HSUSBD_CEPCTL_NAKCLR; - } - /* Enable CEP interrupt */ base->CEPINTEN |= HSUSBD_CEPINTEN_RXPKIEN_Msk; } @@ -1864,6 +1827,7 @@ static int numaker_usbd_xfer_out(const struct device *dev, uint8_t ep, bool stri struct net_buf *buf; struct numaker_usbd_ep *ep_cur; struct udc_ep_config *ep_cfg; + bool status = false; if (!USB_EP_DIR_IS_OUT(ep)) { LOG_ERR("Invalid EP address 0x%02x for data out", ep); @@ -1890,6 +1854,16 @@ static int numaker_usbd_xfer_out(const struct device *dev, uint8_t ep, bool stri return 0; } + if (ep == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + return 0; + } + + status = bi->status; + } + /* Bind EP H/W context to EP address */ ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep); if (!ep_cur) { @@ -1897,7 +1871,7 @@ static int numaker_usbd_xfer_out(const struct device *dev, uint8_t ep, bool stri return -ENODEV; } - numaker_usbd_ep_trigger(ep_cur, ep_cur->mps); + numaker_usbd_ep_trigger(ep_cur, status ? 0 : ep_cur->mps); return 0; } @@ -1962,30 +1936,6 @@ static int numaker_usbd_xfer_in(const struct device *dev, uint8_t ep, bool stric return 0; } -static int numaker_usbd_ctrl_feed_dout(const struct device *dev, const size_t length) -{ - struct udc_numaker_data *priv = udc_get_private(dev); - struct udc_ep_config *ep_cfg; - struct net_buf *buf; - - ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - if (ep_cfg == NULL) { - LOG_ERR("Bind udc_ep_config: ep=0x%02x", USB_CONTROL_EP_OUT); - return -ENODEV; - } - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - LOG_ERR("Allocate net_buf: ep=0x%02x", USB_CONTROL_EP_OUT); - return -ENOMEM; - } - priv->ctrlout_tailroom = length; - - k_fifo_put(&ep_cfg->fifo, buf); - - return numaker_usbd_xfer_out(dev, ep_cfg->addr, true); -} - /* Message handler for device plug-in */ static int numaker_usbd_msg_handle_attach(const struct device *dev, struct numaker_usbd_msg *msg) { @@ -2063,32 +2013,13 @@ static int numaker_usbd_msg_handle_resume(const struct device *dev, struct numak static int numaker_usbd_msg_handle_setup(const struct device *dev, struct numaker_usbd_msg *msg) { const struct udc_numaker_config *config = dev->config; - int err; + struct usb_setup_packet *setup = (struct usb_setup_packet *)msg->setup.packet; + struct udc_numaker_data *priv = udc_get_private(dev); uint8_t ep; struct numaker_usbd_ep *ep_cur; - struct udc_ep_config *ep_cfg; - struct net_buf *buf; - uint8_t *data_ptr; __ASSERT_NO_MSG(msg->type == NUMAKER_USBD_MSG_TYPE_SETUP); - /* Recover from incomplete Control transfer - * - * Previous Control transfer can be incomplete, and causes not - * only net_buf leak but also logic error. This recycles dangling - * net_buf for new clean Control transfer. - */ - ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - buf = udc_buf_get_all(ep_cfg); - if (buf != NULL) { - net_buf_unref(buf); - } - ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - buf = udc_buf_get_all(ep_cfg); - if (buf != NULL) { - net_buf_unref(buf); - } - ep = USB_CONTROL_EP_OUT; /* Bind EP H/W context to EP address */ @@ -2122,33 +2053,16 @@ static int numaker_usbd_msg_handle_setup(const struct device *dev, struct numake numaker_usbd_ep_sync_udc_halt(ep_cur, false); numaker_usbd_ep_sync_udc_halt(ep_cur + 1, false); - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, 8); - if (buf == NULL) { - LOG_ERR("Failed to allocate for Setup"); - return -ENOMEM; - } - - udc_ep_buf_set_setup(buf); - data_ptr = net_buf_tail(buf); - memcpy(data_ptr, msg->setup.packet, 8); - net_buf_add(buf, 8); - - /* Update to next stage of CTRL transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for DATA OUT stage */ - err = numaker_usbd_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - err = udc_ctrl_submit_s_in_status(dev); + if (USB_REQTYPE_GET_DIR(setup->bmRequestType) == USB_REQTYPE_DIR_TO_DEVICE && + setup->wLength) { + priv->ctrlout_tailroom = setup->wLength; } else { - err = udc_ctrl_submit_s_status(dev); + priv->ctrlout_tailroom = 0; } - return err; + udc_setup_received(dev, msg->setup.packet); + + return 0; } /* Message handler for DATA OUT transaction completed */ @@ -2211,35 +2125,23 @@ static int numaker_usbd_msg_handle_out(const struct device *dev, struct numaker_ } /* CTRL DATA OUT/STATUS OUT stage completed */ - if (ep == USB_CONTROL_EP_OUT && priv->ctrlout_tailroom != 0) { - goto next_xfer; - } - if (ep == USB_CONTROL_EP_OUT) { + if (priv->ctrlout_tailroom != 0) { + goto next_xfer; + } + /* To submit the peeked buffer */ udc_buf_get(ep_cfg); - if (udc_ctrl_stage_is_status_out(dev)) { - /* s-in-status finished */ - err = udc_ctrl_submit_status(dev, buf); - if (err < 0) { - LOG_ERR("udc_ctrl_submit_status failed for s-in-status: %d", err); - return err; - } + err = udc_submit_ep_event(dev, buf, 0); + if (err < 0) { + LOG_ERR("udc_submit_ep_event failed for ep=0x%02x: %d", ep, err); } - /* Update to next stage of CTRL transfer */ - udc_ctrl_update_stage(dev, buf); + return err; + } - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - if (err < 0) { - LOG_ERR("udc_ctrl_submit_s_out_status failed for s-out-status: %d", - err); - return err; - } - } - } else if ((net_buf_tailroom(buf) == 0) || (data_len < ep_cfg->mps) || + if ((net_buf_tailroom(buf) == 0) || (data_len < ep_cfg->mps) || (ep_type == USB_EP_TYPE_ISO)) { /* Fix submit condition for non-control transfer * @@ -2301,38 +2203,10 @@ static int numaker_usbd_msg_handle_in(const struct device *dev, struct numaker_u /* To submit the peeked buffer */ udc_buf_get(ep_cfg); - if (ep == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - /* s-out-status/s-status finished */ - err = udc_ctrl_submit_status(dev, buf); - if (err < 0) { - LOG_ERR("udc_ctrl_submit_status failed for s-out-status/s-status: " - "%d", - err); - return err; - } - } - - /* Update to next stage of CTRL transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* DATA IN stage finished, release buffer */ - net_buf_unref(buf); - - /* Allocate and feed buffer for STATUS OUT stage */ - err = numaker_usbd_ctrl_feed_dout(dev, 0); - if (err < 0) { - LOG_ERR("ctrl_feed_dout failed for status out: %d", err); - return err; - } - } - } else { - err = udc_submit_ep_event(dev, buf, 0); - if (err < 0) { - LOG_ERR("udc_submit_ep_event failed for ep=0x%02x: %d", ep, err); - return err; - } + err = udc_submit_ep_event(dev, buf, 0); + if (err < 0) { + LOG_ERR("udc_submit_ep_event failed for ep=0x%02x: %d", ep, err); + return err; } xfer_next: @@ -2342,15 +2216,88 @@ static int numaker_usbd_msg_handle_in(const struct device *dev, struct numaker_u return 0; } +/* Message handler for HSUSB status stage completed */ +static int numaker_usbd_msg_handle_status(const struct device *dev, struct numaker_usbd_msg *msg) +{ + int err; + uint8_t ep; + struct udc_ep_config *ep_cfg; + struct udc_buf_info *bi; + struct net_buf *buf; + + __ASSERT_NO_MSG(msg->type == NUMAKER_USBD_MSG_TYPE_STATUS); + + ep = msg->in.ep; + ep_cfg = udc_get_ep_cfg(dev, ep); + + udc_ep_set_busy(ep_cfg, false); + + buf = udc_buf_peek(ep_cfg); + if (buf == NULL) { + return 0; + } + + bi = udc_get_buf_info(buf); + if (!bi->status) { + LOG_ERR("enqueued buffer is not status"); + return -EINVAL; + } + + /* To submit the peeked buffer */ + udc_buf_get(ep_cfg); + + err = udc_submit_ep_event(dev, buf, 0); + if (err < 0) { + LOG_ERR("udc_submit_ep_event failed for ep=0x%02x: %d", ep, err); + return err; + } + + return 0; +} + /* Message handler for queued transfer re-activated */ static int numaker_usbd_msg_handle_xfer(const struct device *dev, struct numaker_usbd_msg *msg) { + const struct udc_numaker_config *config = dev->config; uint8_t ep; __ASSERT_NO_MSG(msg->type == NUMAKER_USBD_MSG_TYPE_XFER); ep = msg->xfer.ep; + if (config->is_hsusbd) { + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep); + struct net_buf *buf = udc_buf_peek(ep_cfg); + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + return 0; + } + + if (ep == USB_CONTROL_EP_OUT && bi->data) { + HSUSBD_T *base = config->base; + + /* Endpoint seems to get automatically armed for Data + * OUT stage reception. Now that Data stage buffer is + * queued we can allow processing. + */ + base->CEPINTEN |= HSUSBD_CEPINTEN_RXPKIEN_Msk; + return 0; + } + + if (bi->status) { + struct udc_numaker_data *priv = udc_get_private(dev); + HSUSBD_T *base = config->base; + + priv->status_out = USB_EP_DIR_IS_OUT(ep); + + /* Unleash Status stage */ + base->CEPCTL = HSUSBD_CEPCTL_NAKCLR; + + return 0; + } + } + if (USB_EP_DIR_IS_OUT(ep)) { numaker_usbd_xfer_out(dev, ep, false); } else { @@ -2411,6 +2358,10 @@ static void numaker_usbd_msg_handler(const struct device *dev) err = numaker_usbd_msg_handle_in(dev, &msg); break; + case NUMAKER_USBD_MSG_TYPE_STATUS: + err = numaker_usbd_msg_handle_status(dev, &msg); + break; + case NUMAKER_USBD_MSG_TYPE_XFER: err = numaker_usbd_msg_handle_xfer(dev, &msg); break; @@ -2446,8 +2397,10 @@ __maybe_unused static void numaker_usbd_isr(const struct device *dev) */ usbd_intsts &= base->INTEN | USBD_INTSTS_SETUP; - /* Clear event flag */ - base->INTSTS = usbd_intsts; + /* Clear event flag, but **DO NOT** clear USB Event Interrupt status bit + * because clearing USBIF clears all bits in EPINTSTS. + */ + base->INTSTS = usbd_intsts & ~USBD_INTSTS_USB; /* USB plug-in/unplug */ if (usbd_intsts & USBD_INTSTS_FLDET) { @@ -2488,15 +2441,15 @@ __maybe_unused static void numaker_usbd_isr(const struct device *dev) numaker_usbd_sof_th(dev); } - /* USB Setup/EP */ + /* Setup event */ + if (usbd_intsts & USBD_INTSTS_SETUP) { + numaker_usbd_setup_th(dev); + } + + /* Endpoint events */ if (usbd_intsts & USBD_INTSTS_USB) { uint32_t epintsts; - /* Setup event */ - if (usbd_intsts & USBD_INTSTS_SETUP) { - numaker_usbd_setup_th(dev); - } - /* EP events */ epintsts = base->EPINTSTS; @@ -2509,7 +2462,7 @@ __maybe_unused static void numaker_usbd_isr(const struct device *dev) numaker_usbd_ep_th(dev, ep_hw_idx); /* Have handled this EP and go next */ - epintsts &= ~BIT(ep_hw_idx - EP0); + epintsts &= ~BIT(ep_hw_idx); } } } @@ -2523,11 +2476,9 @@ __maybe_unused static void numaker_hsusbd_isr(const struct device *dev) uint32_t gintsts_ep = gintsts & (BIT_MASK(priv->ep_pool_size - 2) << HSUSBD_GINTSTS_EPAIF_Pos); uint32_t busintsts = base->BUSINTSTS; - uint32_t cepintsts = base->CEPINTSTS; /* Focus on enabled */ busintsts &= base->BUSINTEN; - cepintsts &= base->CEPINTEN; /* Clear event flag */ base->BUSINTSTS = busintsts; @@ -2584,9 +2535,10 @@ __maybe_unused static void numaker_hsusbd_isr(const struct device *dev) #endif /* USB CEP */ - if (cepintsts) { - /* Clear event flag */ - base->CEPINTSTS = cepintsts; + if (gintsts & HSUSBD_GINTSTS_CEPIF_Msk) { + uint32_t cepintsts = base->CEPINTSTS; + + cepintsts &= base->CEPINTEN; numaker_hsusbd_cep_th(dev, cepintsts); } @@ -2826,15 +2778,19 @@ static int udc_numaker_host_wakeup(const struct device *dev) static int udc_numaker_set_address(const struct device *dev, const uint8_t addr) { - struct udc_numaker_data *priv = udc_get_private(dev); + const struct udc_numaker_config *config = dev->config; LOG_DBG("Set new address %u for %p", addr, dev); - /* NOTE: Timing for configuring USB device address into H/W is critical. It must be done - * in-between SET_ADDRESS control transfer and next transfer. For this, it is done in - * IN ACK ISR of SET_ADDRESS control transfer. - */ - priv->addr = addr; + if (config->is_hsusbd) { + HSUSBD_T *base = config->base; + + base->FADDR = addr; + } else { + USBD_T *base = config->base; + + base->FADDR = addr; + } return 0; } @@ -2982,7 +2938,7 @@ static int udc_numaker_driver_preinit(const struct device *dev) /* For USBD, support just full-speed */ } data->caps.rwup = true; - data->caps.addr_before_status = true; + data->caps.addr_before_status = false; data->caps.can_detect_vbus = true; data->caps.mps0 = UDC_MPS0_64; diff --git a/drivers/usb/udc/udc_numaker.h b/drivers/usb/udc/udc_numaker.h index 643a726491c1c..ad15edbb4a3ea 100644 --- a/drivers/usb/udc/udc_numaker.h +++ b/drivers/usb/udc/udc_numaker.h @@ -168,6 +168,7 @@ typedef struct { #define HSUSBD_GINTEN_EPAIEN_Pos 0 #define HSUSBD_GINTEN_USBIEN_Msk 0 #define HSUSBD_GINTSTS_EPAIF_Pos 0 +#define HSUSBD_GINTSTS_CEPIF_Msk 0 #define HSUSBD_BUSINTEN_RESUMEIEN_Msk 0 #define HSUSBD_BUSINTEN_RSTIEN_Msk 0 #define HSUSBD_BUSINTEN_SOFIEN_Msk 0 diff --git a/drivers/usb/udc/udc_renesas_ra.c b/drivers/usb/udc/udc_renesas_ra.c index 1a228f64a1863..24bd88bd3b379 100644 --- a/drivers/usb/udc/udc_renesas_ra.c +++ b/drivers/usb/udc/udc_renesas_ra.c @@ -30,7 +30,7 @@ struct udc_renesas_ra_data { struct k_thread thread_data; struct st_usbd_instance_ctrl udc; struct st_usbd_cfg udc_cfg; - atomic_t set_addr_req; + bool ignore_status_in; }; enum udc_renesas_ra_event_type { @@ -126,78 +126,46 @@ static void udc_event_xfer_next(const struct device *dev, const uint8_t ep) } } -static int usbd_ctrl_feed_dout(const struct device *dev, const size_t length) -{ - struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - struct udc_renesas_ra_data *data = udc_get_private(dev); - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - k_fifo_put(&cfg->fifo, buf); - - if (FSP_SUCCESS != R_USBD_XferStart(&data->udc, cfg->addr, buf->data, buf->size)) { - return -EIO; - } - - return 0; -} - static int udc_event_xfer_setup(const struct device *dev, struct udc_renesas_ra_evt *evt) { struct udc_renesas_ra_data *data = udc_get_private(dev); - struct net_buf *buf; - int err; struct usb_setup_packet *setup_packet = (struct usb_setup_packet *)&evt->hal_evt.setup_received; if (setup_packet->bRequest == USB_SREQ_SET_ADDRESS) { - atomic_set(&data->set_addr_req, 1); - } - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate for setup"); - return -ENOMEM; - } - - udc_ep_buf_set_setup(buf); - net_buf_add_mem(buf, setup_packet, sizeof(struct usb_setup_packet)); - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - err = usbd_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - err = udc_ctrl_submit_s_in_status(dev); + data->ignore_status_in = true; + } else if (USB_REQTYPE_GET_DIR(setup_packet->bmRequestType) == USB_REQTYPE_DIR_TO_DEVICE && + setup_packet->wLength) { + /* Status IN after Data OUT is automatically handled. + * This should not be the case because it does not allow USB + * stack to stall Status stage in case the data is invalid + * (determined by handler). + */ + data->ignore_status_in = true; } else { - err = udc_ctrl_submit_s_status(dev); + data->ignore_status_in = false; } - return err; + udc_setup_received(dev, setup_packet); + + return 0; } static void udc_event_xfer_ctrl_in(const struct device *dev, struct net_buf *const buf) { struct udc_renesas_ra_data *data = udc_get_private(dev); - atomic_val_t is_set_addr = atomic_clear(&data->set_addr_req); + struct udc_buf_info *bi = udc_get_buf_info(buf); fsp_err_t err; - if (udc_ctrl_stage_is_no_data(dev)) { - if (is_set_addr == 0) { - /* Complete s-[status] stage for non-SET_ADDRESS requests */ + if (bi->status) { + if (!data->ignore_status_in) { + /* Complete Status IN stage for control requests without + * Data OUT stage other than SET_ADDRESS. + */ err = R_USBD_XferStart(&data->udc, USB_CONTROL_EP_IN, NULL, 0); if (err != FSP_SUCCESS) { + udc_submit_ep_event(dev, buf, -EIO); return; } } @@ -208,10 +176,7 @@ static void udc_event_xfer_ctrl_in(const struct device *dev, struct net_buf *con * Controller supports auto-status, so we cannot check for status completion after state * update */ - net_buf_unref(buf); - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); + udc_submit_ep_event(dev, buf, 0); } static void udc_event_status_in(const struct device *dev) @@ -227,19 +192,6 @@ static void udc_event_status_in(const struct device *dev) udc_event_xfer_ctrl_in(dev, buf); } -static void udc_event_xfer_ctrl_out(const struct device *dev, struct net_buf *const buf, - uint32_t len) -{ - net_buf_add(buf, len); - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - udc_ctrl_submit_s_out_status(dev, buf); - } -} - static void udc_event_xfer_complete(const struct device *dev, struct udc_renesas_ra_evt *evt) { struct net_buf *buf; @@ -276,8 +228,6 @@ static void udc_event_xfer_complete(const struct device *dev, struct udc_renesas if (ep == USB_CONTROL_EP_IN) { udc_event_xfer_ctrl_in(dev, buf); - } else if (ep == USB_CONTROL_EP_OUT) { - udc_event_xfer_ctrl_out(dev, buf, len); } else { if (USB_EP_DIR_IS_OUT(ep)) { net_buf_add(buf, len); @@ -337,15 +287,28 @@ static int udc_renesas_ra_ep_enqueue(const struct device *dev, struct udc_ep_con LOG_DBG("%p enqueue %p", dev, buf); - udc_buf_put(cfg, buf); - evt.ep = cfg->addr; - if (cfg->addr == USB_CONTROL_EP_IN && buf->len == 0) { - evt.type = UDC_RENESAS_RA_EVT_STATUS; - } else { - evt.type = UDC_RENESAS_RA_EVT_XFER; + evt.type = UDC_RENESAS_RA_EVT_XFER; + + if (cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + udc_buf_put(cfg, buf); + return 0; + } } + if (cfg->addr == USB_CONTROL_EP_IN) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->status) { + evt.type = UDC_RENESAS_RA_EVT_STATUS; + } + } + + udc_buf_put(cfg, buf); + k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); if (cfg->stat.halted) { diff --git a/drivers/usb/udc/udc_rpi_pico.c b/drivers/usb/udc/udc_rpi_pico.c index b0fb2fcf44f10..7324281649d40 100644 --- a/drivers/usb/udc/udc_rpi_pico.c +++ b/drivers/usb/udc/udc_rpi_pico.c @@ -315,83 +315,10 @@ static int rpi_pico_prep_tx(const struct device *dev, return 0; } -static int rpi_pico_ctrl_feed_dout(const struct device *dev, const size_t length) -{ - struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - udc_buf_put(ep_cfg, buf); - - return rpi_pico_prep_rx(dev, buf, ep_cfg); -} - -static void drop_control_transfers(const struct device *dev) -{ - struct udc_ep_config *cfg_out = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct udc_ep_config *cfg_in = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - struct net_buf *buf; - - buf = udc_buf_get_all(cfg_out); - if (buf != NULL) { - net_buf_unref(buf); - } - - buf = udc_buf_get_all(cfg_in); - if (buf != NULL) { - net_buf_unref(buf); - } -} - -static int rpi_pico_handle_evt_setup(const struct device *dev) -{ - struct rpi_pico_data *priv = udc_get_private(dev); - struct net_buf *buf; - int err; - - drop_control_transfers(dev); - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, 8); - if (buf == NULL) { - udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); - return -ENOMEM; - } - - net_buf_add_mem(buf, priv->setup, sizeof(priv->setup)); - udc_ep_buf_set_setup(buf); - LOG_HEXDUMP_DBG(buf->data, buf->len, "setup"); - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - - err = rpi_pico_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err != 0) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - LOG_DBG("s:%p|feed for -in-status", buf); - err = udc_ctrl_submit_s_in_status(dev); - } else { - LOG_DBG("s:%p|no data", buf); - err = udc_ctrl_submit_s_status(dev); - } - - return err; -} - static inline int rpi_pico_handle_evt_dout(const struct device *dev, struct udc_ep_config *const cfg) { struct net_buf *buf; - int err = 0; buf = udc_buf_get(cfg); if (buf == NULL) { @@ -402,32 +329,13 @@ static inline int rpi_pico_handle_evt_dout(const struct device *dev, udc_ep_set_busy(cfg, false); - if (cfg->addr == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - LOG_DBG("dout:%p|status, feed >s", buf); - - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - } - } else { - err = udc_submit_ep_event(dev, buf, 0); - } - - return err; + return udc_submit_ep_event(dev, buf, 0); } static int rpi_pico_handle_evt_din(const struct device *dev, struct udc_ep_config *const cfg) { struct net_buf *buf; - int err; buf = udc_buf_peek(cfg); if (buf == NULL) { @@ -439,29 +347,6 @@ static int rpi_pico_handle_evt_din(const struct device *dev, buf = udc_buf_get(cfg); udc_ep_set_busy(cfg, false); - if (cfg->addr == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* IN transfer finished, submit buffer for status stage */ - net_buf_unref(buf); - - err = rpi_pico_ctrl_feed_dout(dev, 0); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } - - return 0; - } - return udc_submit_ep_event(dev, buf, 0); } @@ -476,6 +361,15 @@ static void rpi_pico_handle_xfer_next(const struct device *dev, return; } + if (cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + /* SETUP data will be received without any action */ + return; + } + } + if (USB_EP_DIR_IS_OUT(cfg->addr)) { if (cfg->stat.halted) { return; @@ -550,7 +444,7 @@ static ALWAYS_INLINE void rpi_pico_thread_handler(void *const arg) if (evt & BIT(RPI_PICO_EVT_SETUP)) { k_event_clear(&priv->events, BIT(RPI_PICO_EVT_SETUP)); LOG_DBG("SETUP event"); - rpi_pico_handle_evt_setup(dev); + udc_setup_received(dev, priv->setup); } udc_unlock_internal(dev); diff --git a/drivers/usb/udc/udc_sam0.c b/drivers/usb/udc/udc_sam0.c index e351c98e284bc..96c0bbf425ed6 100644 --- a/drivers/usb/udc/udc_sam0.c +++ b/drivers/usb/udc/udc_sam0.c @@ -307,78 +307,8 @@ static int sam0_prep_in(const struct device *dev, return 0; } -static int sam0_ctrl_feed_dout(const struct device *dev, const size_t length) -{ - struct udc_ep_config *const ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - udc_buf_put(ep_cfg, buf); - - return sam0_prep_out(dev, buf, ep_cfg); -} - -static void drop_control_transfers(const struct device *dev) -{ - struct net_buf *buf; - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); - if (buf != NULL) { - net_buf_unref(buf); - } - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN)); - if (buf != NULL) { - net_buf_unref(buf); - } -} - -static int sam0_handle_evt_setup(const struct device *dev) -{ - struct udc_sam0_data *const priv = udc_get_private(dev); - struct net_buf *buf; - int err; - - drop_control_transfers(dev); - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, 8); - if (buf == NULL) { - return -ENOMEM; - } - - net_buf_add_mem(buf, priv->setup, sizeof(priv->setup)); - udc_ep_buf_set_setup(buf); - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", (void *)buf); - - err = sam0_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - udc_submit_ep_event(dev, buf, err); - } else { - return err; - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - LOG_DBG("s:%p|feed for -in-status", (void *)buf); - err = udc_ctrl_submit_s_in_status(dev); - } else { - LOG_DBG("s:%p|no data", (void *)buf); - err = udc_ctrl_submit_s_status(dev); - } - - return err; -} - -static int sam0_handle_evt_din(const struct device *dev, - struct udc_ep_config *const ep_cfg) +static int sam0_handle_evt_finished(const struct device *dev, + struct udc_ep_config *const ep_cfg) { struct net_buf *buf; @@ -390,33 +320,6 @@ static int sam0_handle_evt_din(const struct device *dev, udc_ep_set_busy(ep_cfg, false); - if (ep_cfg->addr == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - int err; - - /* IN transfer finished, submit buffer for status stage */ - net_buf_unref(buf); - - err = sam0_ctrl_feed_dout(dev, 0); - if (err == -ENOMEM) { - udc_submit_ep_event(dev, buf, err); - } else { - return err; - } - } - - return 0; - } - return udc_submit_ep_event(dev, buf, 0); } @@ -424,7 +327,6 @@ static inline int sam0_handle_evt_dout(const struct device *dev, struct udc_ep_config *const ep_cfg) { struct net_buf *buf; - int err = 0; buf = udc_buf_get(ep_cfg); if (buf == NULL) { @@ -434,25 +336,7 @@ static inline int sam0_handle_evt_dout(const struct device *dev, udc_ep_set_busy(ep_cfg, false); - if (ep_cfg->addr == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - LOG_DBG("dout:%p|status, feed >s", (void *)buf); - - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - } - } else { - err = udc_submit_ep_event(dev, buf, 0); - } - - return err; + return udc_submit_ep_event(dev, buf, 0); } static void sam0_handle_xfer_next(const struct device *dev, @@ -466,6 +350,15 @@ static void sam0_handle_xfer_next(const struct device *dev, return; } + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + /* SETUP data will be received without any action */ + return; + } + } + if (USB_EP_DIR_IS_OUT(ep_cfg->addr)) { err = sam0_prep_out(dev, buf, ep_cfg); } else { @@ -502,12 +395,7 @@ static ALWAYS_INLINE void sam0_thread_handler(const struct device *const dev) ep_cfg = udc_get_ep_cfg(dev, ep); LOG_DBG("Finished event ep 0x%02x", ep); - if (USB_EP_DIR_IS_IN(ep)) { - err = sam0_handle_evt_din(dev, ep_cfg); - } else { - err = sam0_handle_evt_dout(dev, ep_cfg); - } - + err = sam0_handle_evt_finished(dev, ep_cfg); if (err) { udc_submit_event(dev, UDC_EVT_ERROR, err); } @@ -540,10 +428,12 @@ static ALWAYS_INLINE void sam0_thread_handler(const struct device *const dev) if (evt & BIT(SAM0_EVT_SETUP)) { k_event_clear(&priv->events, BIT(SAM0_EVT_SETUP)); - err = sam0_handle_evt_setup(dev); - if (err) { - udc_submit_event(dev, UDC_EVT_ERROR, err); - } + /* + * BK0RDY bit is set and BK1RDY bit is cleared on receiving the + * setup packet, which cancels ongoing transfer and NAKs any + * OUT/IN data. + */ + udc_setup_received(dev, priv->setup); } udc_unlock_internal(dev); diff --git a/drivers/usb/udc/udc_sam_udp.c b/drivers/usb/udc/udc_sam_udp.c index ae3108a166c3a..48e4d6dc3b033 100644 --- a/drivers/usb/udc/udc_sam_udp.c +++ b/drivers/usb/udc/udc_sam_udp.c @@ -541,6 +541,14 @@ static int udc_sam_udp_ep_enqueue(const struct device *dev, } } } else { + if (cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + return 0; + } + } + /* * Buffer queued for OUT endpoint. If there's pending data * waiting (thread was NAKing due to no buffer), wake up the @@ -1004,40 +1012,6 @@ static void udc_sam_udp_unlock(const struct device *dev) udc_unlock_internal(dev); } -/* - * Control transfer handling - */ -static void udc_sam_udp_drop_ctrl_transfers(const struct device *dev) -{ - struct net_buf *buf; - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); - if (buf != NULL) { - net_buf_unref(buf); - } - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN)); - if (buf != NULL) { - net_buf_unref(buf); - } -} - -static int udc_sam_udp_ctrl_feed_dout(const struct device *dev, - const size_t length) -{ - struct udc_ep_config *const ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - udc_buf_put(ep_cfg, buf); - - return 0; -} - /* * ISR handler for SETUP packets - called from ISR context. * @@ -1053,8 +1027,6 @@ static void udc_sam_udp_isr_handle_setup(const struct device *dev) Udp *base = udc_sam_udp_get_base(dev); struct udc_sam_udp_data *priv = udc_get_private(dev); - udc_sam_udp_drop_ctrl_transfers(dev); - /* Read SETUP packet from FIFO - must be done before clearing RXSETUP */ for (int i = 0; i < 8; i++) { priv->setup[i] = base->UDP_FDR[0]; @@ -1081,44 +1053,6 @@ static void udc_sam_udp_isr_handle_setup(const struct device *dev) k_event_post(&priv->events, BIT(SAM_UDP_EVT_SETUP)); } -/* - * Thread handler for SETUP packets - called from thread context. - */ -static int udc_sam_udp_thread_handle_setup(const struct device *dev) -{ - struct udc_sam_udp_data *priv = udc_get_private(dev); - struct net_buf *buf; - int err; - - udc_sam_udp_drop_ctrl_transfers(dev); - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, 8); - if (buf == NULL) { - return -ENOMEM; - } - - net_buf_add_mem(buf, priv->setup, 8); - udc_ep_buf_set_setup(buf); - - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - LOG_DBG("s:%p|feed for -out-", (void *)buf); - err = udc_sam_udp_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - LOG_DBG("s:%p|feed for -in-status", (void *)buf); - err = udc_ctrl_submit_s_in_status(dev); - } else { - LOG_DBG("s:%p|no data", (void *)buf); - err = udc_ctrl_submit_s_status(dev); - } - - return err; -} - /* * Handle IN (TX) endpoint completion. * @@ -1136,7 +1070,6 @@ static void udc_sam_udp_handle_in(const struct device *dev, const uint8_t ep) uint8_t hw_ep = USB_EP_GET_IDX(ep); struct net_buf *buf; uint8_t max_banks = ep_has_dual_bank(hw_ep) ? 2 : 1; - int err; if (SAM_UDP_LOG_LEVEL == LOG_LEVEL_DBG) { /* Throughput measurement - only when debug logging enabled */ @@ -1195,29 +1128,7 @@ static void udc_sam_udp_handle_in(const struct device *dev, const uint8_t ep) priv->ep_data[hw_ep].tx_banks == 0) { buf = udc_buf_get(cfg); - if (ep == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - LOG_DBG("IN: status stage complete"); - udc_ctrl_submit_status(dev, buf); - } - - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - LOG_DBG("IN: feeding for status OUT"); - net_buf_unref(buf); - err = udc_sam_udp_ctrl_feed_dout(dev, 0); - if (err != 0) { - LOG_ERR("Failed to feed ctrl dout: %d", - err); - } - buf = NULL; - break; - } - } else { - udc_submit_ep_event(dev, buf, 0); - } + udc_submit_ep_event(dev, buf, 0); /* Check for next buffer and pre-fill if dual-bank */ buf = udc_buf_peek(cfg); @@ -1362,7 +1273,6 @@ static int sam_udp_process_pending_out(const struct device *dev, uint32_t csr; uint32_t bank_flag; uint16_t len; - int err; csr = base->UDP_CSR[hw_ep]; bank_flag = sam_udp_get_out_bank(priv, hw_ep, csr); @@ -1405,23 +1315,7 @@ static int sam_udp_process_pending_out(const struct device *dev, if (len < priv->ep_data[hw_ep].mps || net_buf_tailroom(buf) == 0) { buf = udc_buf_get(cfg); - if (ep == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - LOG_DBG("OUT: status stage complete"); - udc_ctrl_submit_status(dev, buf); - } - - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - if (err != 0) { - LOG_ERR("Failed s-out-status: %d", err); - } - } - } else { - udc_submit_ep_event(dev, buf, 0); - } + udc_submit_ep_event(dev, buf, 0); } return 0; @@ -1482,14 +1376,9 @@ static ALWAYS_INLINE void sam_udp_thread_handler(const struct device *const dev) } } - /* Process SETUP after OUT to avoid dropping status stage */ if (evt & BIT(SAM_UDP_EVT_SETUP)) { k_event_clear(&priv->events, BIT(SAM_UDP_EVT_SETUP)); - err = udc_sam_udp_thread_handle_setup(dev); - if (err) { - LOG_ERR("SETUP handling failed: %d", err); - udc_submit_event(dev, UDC_EVT_ERROR, err); - } + udc_setup_received(dev, priv->setup); } udc_unlock_internal(dev); diff --git a/drivers/usb/udc/udc_sam_usbc.c b/drivers/usb/udc/udc_sam_usbc.c index b6c2f3bd70f72..96a26b1958e2d 100644 --- a/drivers/usb/udc/udc_sam_usbc.c +++ b/drivers/usb/udc/udc_sam_usbc.c @@ -408,70 +408,13 @@ static int sam_usbc_prep_in(const struct device *const dev, return 0; } -static int sam_usbc_ctrl_feed_dout(const struct device *const dev, const size_t length) -{ - struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - udc_buf_put(ep_cfg, buf); - - return sam_usbc_prep_out(dev, buf, ep_cfg); -} - -static void drop_control_transfers(const struct device *const dev) -{ - struct net_buf *buf; - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); - if (buf != NULL) { - net_buf_unref(buf); - } - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN)); - if (buf != NULL) { - net_buf_unref(buf); - } -} - static int sam_usbc_handle_evt_setup(const struct device *const dev) { struct udc_sam_usbc_data *priv = udc_get_private(dev); - struct net_buf *buf; - int err; - - drop_control_transfers(dev); - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, EP0_SETUP_SIZE); - if (buf == NULL) { - return -ENOMEM; - } - net_buf_add_mem(buf, priv->setup, EP0_SETUP_SIZE); - udc_ep_buf_set_setup(buf); + udc_setup_received(dev, priv->setup); - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - LOG_DBG("s:%p|feed for -out-", (void *)buf); - err = sam_usbc_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - LOG_DBG("s:%p|feed for -in-status", (void *)buf); - err = udc_ctrl_submit_s_in_status(dev); - } else { - LOG_DBG("s:%p|no data", (void *)buf); - err = udc_ctrl_submit_s_status(dev); - } - - return err; + return 0; } static int sam_usbc_handle_evt_din(const struct device *const dev, @@ -508,36 +451,12 @@ static int sam_usbc_handle_evt_din(const struct device *const dev, udc_ep_set_busy(ep_cfg, false); if (ep_cfg->addr == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - - /* - * CRITICAL: Restore EP0 OUT descriptor for next SETUP. - * In single-bank mode, IN and OUT share the same - * descriptor. sam_usbc_prep_in() set ep_pipe_addr to - * the IN buffer. We must restore it to the setup - * buffer before the next SETUP arrives. - * EP0 always uses physical EP0. - */ - dt->ep_pipe_addr = priv->setup; - dt->sizes = 0; - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* - * IN transfer finished, need to receive OUT ZLP - * for status stage - */ - net_buf_unref(buf); - return sam_usbc_ctrl_feed_dout(dev, 0); - } - - return 0; + /* + * Restore EP0 OUT descriptor for next SETUP. + * Single-bank: IN and OUT share same descriptor. + */ + dt->ep_pipe_addr = priv->setup; + dt->sizes = 0; } return udc_submit_ep_event(dev, buf, 0); @@ -554,7 +473,6 @@ static int sam_usbc_handle_evt_dout(const struct device *const dev, struct net_buf *buf; uint32_t size; uint8_t bank = 0; - int err = 0; /* EP0 control endpoint: single-bank, read from descriptor */ dt = sam_usbc_get_desc(dev, phys_ep, 0); @@ -596,32 +514,12 @@ static int sam_usbc_handle_evt_dout(const struct device *const dev, udc_ep_set_busy(ep_cfg, false); if (ep_cfg->addr == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - LOG_DBG("dout:%p|status, feed >s", (void *)buf); - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - - /* - * CRITICAL: Restore EP0 OUT descriptor for next SETUP. - * After status OUT (ZLP from host), ensure descriptor - * is ready for the next SETUP packet. - * EP0 always uses physical EP0. - */ - dt->ep_pipe_addr = priv->setup; - dt->sizes = 0; - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - } - } else { - err = udc_submit_ep_event(dev, buf, 0); + /* Restore EP0 OUT descriptor for next SETUP */ + dt->ep_pipe_addr = priv->setup; + dt->sizes = 0; } - return err; + return udc_submit_ep_event(dev, buf, 0); } static void sam_usbc_handle_xfer_next(const struct device *const dev, @@ -635,6 +533,14 @@ static void sam_usbc_handle_xfer_next(const struct device *const dev, return; } + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + return; + } + } + if (USB_EP_DIR_IS_OUT(ep_cfg->addr)) { err = sam_usbc_prep_out(dev, buf, ep_cfg); } else { diff --git a/drivers/usb/udc/udc_sam_usbhs.c b/drivers/usb/udc/udc_sam_usbhs.c index 3bba97f4c3ba8..ee18e50f1c802 100644 --- a/drivers/usb/udc/udc_sam_usbhs.c +++ b/drivers/usb/udc/udc_sam_usbhs.c @@ -473,73 +473,14 @@ static int sam_usbhs_prep_in(const struct device *dev, return 0; } -static int sam_usbhs_ctrl_feed_dout(const struct device *dev, - const size_t length) -{ - struct udc_ep_config *const ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - udc_buf_put(ep_cfg, buf); - - return sam_usbhs_prep_out(dev, buf, ep_cfg); -} - -static void drop_control_transfers(const struct device *dev) -{ - struct net_buf *buf; - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); - if (buf != NULL) { - net_buf_unref(buf); - } - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN)); - if (buf != NULL) { - net_buf_unref(buf); - } -} - static int sam_usbhs_handle_evt_setup(const struct device *dev) { - struct udc_sam_usbhs_data *const priv = udc_get_private(dev); - struct net_buf *buf; - int err; - - drop_control_transfers(dev); + struct udc_sam_usbhs_data *const priv = + udc_get_private(dev); - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, 8); - if (buf == NULL) { - return -ENOMEM; - } + udc_setup_received(dev, priv->setup); - net_buf_add_mem(buf, priv->setup, sizeof(priv->setup)); - udc_ep_buf_set_setup(buf); - - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - LOG_DBG("s:%p|feed for -out-", (void *)buf); - - err = sam_usbhs_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - udc_submit_ep_event(dev, buf, err); - } else { - return err; - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - LOG_DBG("s:%p|feed for -in-status", (void *)buf); - err = udc_ctrl_submit_s_in_status(dev); - } else { - LOG_DBG("s:%p|no data", (void *)buf); - err = udc_ctrl_submit_s_status(dev); - } - - return err; + return 0; } static int sam_usbhs_handle_evt_din(const struct device *dev, @@ -555,30 +496,6 @@ static int sam_usbhs_handle_evt_din(const struct device *dev, udc_ep_set_busy(ep_cfg, false); - if (ep_cfg->addr == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - udc_ctrl_submit_status(dev, buf); - } - - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - int err; - - net_buf_unref(buf); - - err = sam_usbhs_ctrl_feed_dout(dev, 0); - if (err == -ENOMEM) { - udc_submit_ep_event(dev, buf, err); - } else { - return err; - } - } - - return 0; - } - return udc_submit_ep_event(dev, buf, 0); } @@ -586,7 +503,6 @@ static int sam_usbhs_handle_evt_dout(const struct device *dev, struct udc_ep_config *const ep_cfg) { struct net_buf *buf; - int err = 0; buf = udc_buf_get(ep_cfg); if (buf == NULL) { @@ -596,22 +512,7 @@ static int sam_usbhs_handle_evt_dout(const struct device *dev, udc_ep_set_busy(ep_cfg, false); - if (ep_cfg->addr == USB_CONTROL_EP_OUT) { - if (udc_ctrl_stage_is_status_out(dev)) { - LOG_DBG("dout:%p|status, feed >s", (void *)buf); - udc_ctrl_submit_status(dev, buf); - } - - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - err = udc_ctrl_submit_s_out_status(dev, buf); - } - } else { - err = udc_submit_ep_event(dev, buf, 0); - } - - return err; + return udc_submit_ep_event(dev, buf, 0); } static void sam_usbhs_handle_xfer_next(const struct device *dev, @@ -625,6 +526,14 @@ static void sam_usbhs_handle_xfer_next(const struct device *dev, return; } + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + return; + } + } + if (USB_EP_DIR_IS_OUT(ep_cfg->addr)) { err = sam_usbhs_prep_out(dev, buf, ep_cfg); } else { diff --git a/drivers/usb/udc/udc_smartbond.c b/drivers/usb/udc/udc_smartbond.c index 718efa3205c54..88805f127ccb8 100644 --- a/drivers/usb/udc/udc_smartbond.c +++ b/drivers/usb/udc/udc_smartbond.c @@ -516,7 +516,7 @@ static void handle_ep0_rx(struct usb_smartbond_data *data) ep0_out_config->stat.data1 ^= 1; net_buf_add(ep0_out_state->buf, ep0_out_state->last_packet_size); if (ep0_out_state->last_packet_size < EP0_FIFO_SIZE || - ep0_out_state->buf->len == 0) { + net_buf_tailroom(ep0_out_state->buf) == 0) { k_work_submit_to_queue(udc_get_work_q(), &data->ep0_rx_work); } else { @@ -615,6 +615,39 @@ static int udc_smartbond_ep_enqueue(const struct device *dev, struct udc_ep_conf LOG_DBG("ep 0x%02x enqueue %p", ep, buf); udc_buf_put(ep_cfg, buf); + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + /* SETUP can be received without any action */ + return 0; + } + + if (bi->status) { + /* Start sending Data IN only when the status buffer is + * enqueued because control endpoint can only be enabled + * in one direction at any given time. Status OUT stage + * will start once OUT endpoint NAKs. + */ + lock_key = irq_lock(); + + ret = udc_smartbond_ep_tx(dev, USB_CONTROL_EP_IN); + + irq_unlock(lock_key); + + return ret; + } + } + + if (ep_cfg->addr == USB_CONTROL_EP_IN) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->data) { + /* Wait for status OUT to be enqueued */ + return 0; + } + } + if (ep_cfg->stat.halted) { /* * It is fine to enqueue a transfer for a halted endpoint, @@ -722,20 +755,12 @@ static int udc_smartbond_ep_disable(const struct device *dev, struct udc_ep_conf static int udc_smartbond_ep_set_halt(const struct device *dev, struct udc_ep_config *const ep_cfg) { struct smartbond_ep_state *ep_state = (struct smartbond_ep_state *)(ep_cfg); - struct net_buf *buf; const uint8_t ep = ep_cfg->addr; LOG_DBG("Set halt ep 0x%02x", ep); ep_cfg->stat.halted = 1; if (ep_cfg->addr == USB_CONTROL_EP_IN) { - /* Stall in DATA IN phase, drop status OUT packet */ - if (udc_ctrl_stage_is_data_in(dev)) { - buf = udc_buf_get(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); - if (buf) { - net_buf_unref(buf); - } - } USB->USB_RXC0_REG = USB_USB_RXC0_REG_USB_FLUSH_Msk; USB->USB_EPC0_REG |= USB_USB_EPC0_REG_USB_STALL_Msk; USB->USB_TXC0_REG |= USB_USB_TXC0_REG_USB_TX_EN_Msk; @@ -1164,22 +1189,6 @@ static void handle_ep0_nak(struct usb_smartbond_data *data) } } -static void empty_ep0_queues(const struct device *dev) -{ - struct udc_ep_config *cfg_out = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct udc_ep_config *cfg_in = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); - struct net_buf *buf; - - buf = udc_buf_get_all(cfg_out); - if (buf) { - net_buf_unref(buf); - } - buf = udc_buf_get_all(cfg_in); - if (buf) { - net_buf_unref(buf); - } -} - static void handle_bus_reset(struct usb_smartbond_data *data) { const struct udc_smartbond_config *config = data->dev->config; @@ -1209,7 +1218,6 @@ static void handle_bus_reset(struct usb_smartbond_data *data) USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESUME_Msk; alt_ev = USB->USB_ALTEV_REG; check_reset_end(data, alt_ev); - empty_ep0_queues(data->dev); } static void usb_clock_on(struct usb_smartbond_data *data) @@ -1416,41 +1424,6 @@ static void usb_dc_smartbond_vbus_isr(struct usb_smartbond_data *data) 0); } -static int usb_dc_smartbond_alloc_status_out(const struct device *dev) -{ - - struct udc_ep_config *const ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, 0); - if (buf == NULL) { - return -ENOMEM; - } - - k_fifo_put(&ep_cfg->fifo, buf); - - return 0; -} - -static int usbd_ctrl_feed_dout(const struct device *dev, const size_t length) -{ - struct usb_smartbond_data *data = udc_get_private(dev); - struct smartbond_ep_state *ep_state = EP0_OUT_STATE(data); - struct udc_ep_config *const ep_cfg = &ep_state->config; - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - k_fifo_put(&ep_cfg->fifo, buf); - ep_state->buf = buf; - start_rx_packet(data, ep_state); - - return 0; -} - static void handle_ep0_rx_work(struct k_work *item) { struct usb_smartbond_data *data = @@ -1475,23 +1448,15 @@ static void handle_ep0_rx_work(struct k_work *item) LOG_ERR("ep 0x%02x queue is empty", ep); return; } - /* Update packet size */ - if (udc_ctrl_stage_is_status_out(dev)) { - udc_ctrl_update_stage(dev, buf); - udc_ctrl_submit_status(dev, buf); - } else { - udc_ctrl_update_stage(dev, buf); - } - if (udc_ctrl_stage_is_status_in(dev)) { - udc_ctrl_submit_s_out_status(dev, buf); - } + udc_submit_ep_event(dev, buf, 0); } static void handle_ep0_tx_work(struct k_work *item) { struct usb_smartbond_data *data = CONTAINER_OF(item, struct usb_smartbond_data, ep0_tx_work); + struct udc_buf_info *bi; struct net_buf *buf; const struct device *dev = data->dev; const uint8_t ep = USB_CONTROL_EP_IN; @@ -1517,18 +1482,8 @@ static void handle_ep0_tx_work(struct k_work *item) __ASSERT(buf == EP0_IN_STATE(data)->buf, "Internal error"); - /* For control endpoint get ready for ACK stage - * from host. - */ - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { + bi = udc_get_buf_info(buf); + if (bi->data) { /* * Flush TX FIFO in case host already send status OUT packet * and is not interested in reading from IN endpoint @@ -1536,56 +1491,23 @@ static void handle_ep0_tx_work(struct k_work *item) USB->USB_TXC0_REG = USB_USB_TXC0_REG_USB_FLUSH_Msk; /* Enable reception of status OUT packet */ REG_SET_BIT(USB_RXC0_REG, USB_RX_EN); - /* - * IN transfer finished, release buffer, - */ - net_buf_unref(buf); } + + udc_submit_ep_event(dev, buf, 0); } static void handle_ep0_setup_work(struct k_work *item) { struct usb_smartbond_data *data = CONTAINER_OF(item, struct usb_smartbond_data, ep0_setup_work); - struct net_buf *buf; - int err; const struct device *dev = data->dev; struct smartbond_ep_state *ep0_out_state; - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate for setup"); - return; - } - - udc_ep_buf_set_setup(buf); - net_buf_add_mem(buf, data->setup_buffer, 8); ep0_out_state = EP0_OUT_STATE(data); ep0_out_state->last_packet_size = 0; ep0_out_state->buf = NULL; - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s:%p|feed for -out-", buf); - err = usbd_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - /* Allocate buffer for Status OUT state */ - err = usb_dc_smartbond_alloc_status_out(dev); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } else { - err = udc_ctrl_submit_s_in_status(dev); - if (err == -ENOMEM) { - err = udc_submit_ep_event(dev, buf, err); - } - } - } else { - err = udc_ctrl_submit_s_status(dev); - } + + udc_setup_received(dev, data->setup_buffer); } static int udc_smartbond_enable(const struct device *dev) diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index ca31f28061594..0ac6c8e28127e 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -278,67 +278,6 @@ void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state) } } -/* - * Prepare OUT EP0 for reception. - * - * @param dev USB controller - * @param length wLength from SETUP packet for s-out-status - * 0 for s-in-status ZLP - */ -static int udc_stm32_prep_out_ep0_rx(const struct device *dev, const size_t length) -{ - struct udc_stm32_data *priv = udc_get_private(dev); - struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - uint32_t buf_size; - - udc_ep_set_busy(ep_cfg, true); - - /* - * Make sure OUT EP0 can receive bMaxPacketSize0 bytes - * from each Data packet by rounding up allocation size - * even if "device behaviour is undefined if the host - * should send more data than specified in wLength" - * according to the USB Specification. - * - * Note that ROUND_UP() will return 0 for ZLP. - */ - buf_size = ROUND_UP(length, UDC_STM32_EP0_MAX_PACKET_SIZE); - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, buf_size); - if (buf == NULL) { - return -ENOMEM; - } - - k_fifo_put(&ep_cfg->fifo, buf); - - /* - * Keep track of how much data we're expecting from - * host so we know when the transfer is complete. - * Unlike other endpoints, this bookkeeping isn't - * done by the HAL for OUT EP0. - */ - priv->ep0_out_wlength = length; - - /* Don't try to receive more than bMaxPacketSize0 */ - if (HAL_PCD_EP_Receive(&priv->pcd, ep_cfg->addr, net_buf_tail(buf), - UDC_STM32_EP0_MAX_PACKET_SIZE) != HAL_OK) { - return -EIO; - } - - return 0; -} - -static void udc_stm32_flush_tx_fifo(const struct device *dev) -{ - struct udc_stm32_data *priv = udc_get_private(dev); - struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - HAL_StatusTypeDef __maybe_unused status; - - status = HAL_PCD_EP_Receive(&priv->pcd, ep_cfg->addr, NULL, 0); - __ASSERT_NO_MSG(status == HAL_OK); -} - static int udc_stm32_tx(const struct device *dev, struct udc_ep_config *ep_cfg, struct net_buf *buf) { @@ -371,41 +310,66 @@ static int udc_stm32_tx(const struct device *dev, struct udc_ep_config *ep_cfg, udc_ep_set_busy(ep_cfg, true); - if (ep_cfg->addr == USB_CONTROL_EP_IN && len > 0U) { - /* Wait for an empty package from the host. - * This also flushes the TX FIFO to the host. - */ - if (DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usb)) { - udc_stm32_flush_tx_fifo(dev); - } else { - udc_stm32_prep_out_ep0_rx(dev, 0); - } - } - return 0; } -static int udc_stm32_rx(const struct device *dev, struct udc_ep_config *ep_cfg, - struct net_buf *buf) +/* Start accepting packets (OUT transfers) */ +static int udc_stm32_initiate_ep_rx(const struct device *dev, + struct udc_ep_config *const ep_cfg, + struct net_buf *const buf) { struct udc_stm32_data *priv = udc_get_private(dev); HAL_StatusTypeDef status; + uint32_t rx_size; - /* OUT EP0 requires special logic! */ - __ASSERT_NO_MSG(ep_cfg->addr != USB_CONTROL_EP_OUT); + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + struct udc_buf_info *bi = udc_get_buf_info(buf); + + if (bi->setup) { + /* SETUP data will be received without any action */ + return 0; + } + } + + /* + * NOTE: we don't check udc_ep_is_busy(ep_cfg) here because + * this may be called while the endpoint is still *marked* + * as busy even though a transaction just ended. In such + * situation, the call to `udc_ep_set_busy(true)` at the + * end of this function is a no-op. + */ LOG_DBG("RX ep 0x%02x len %u", ep_cfg->addr, buf->size); - if (udc_ep_is_busy(ep_cfg)) { - return 0; + if (ep_cfg->addr == USB_CONTROL_EP_OUT) { + /* + * Keep track of how much data we're expecting from + * host so we know when the transfer is complete. + * Unlike other endpoints, this bookkeeping isn't + * done by the HAL for OUT EP0. + */ + priv->ep0_out_wlength = buf->size; + + /* We can receive only up to bMaxPacketSize0 per + * call to HAL_PCD_EP_Receive(), regardless of + * how much data the host wants to send in total. + */ + rx_size = MIN(buf->size, UDC_STM32_EP0_MAX_PACKET_SIZE); + } else { + /* One call to HAL_PCD_EP_Receive() is enough, + * the HAL will split in multiple receive transfers + * and perform all bookkeeping internally. + */ + rx_size = buf->size; } - status = HAL_PCD_EP_Receive(&priv->pcd, ep_cfg->addr, buf->data, buf->size); + status = HAL_PCD_EP_Receive(&priv->pcd, ep_cfg->addr, buf->data, rx_size); if (status != HAL_OK) { LOG_ERR("HAL_PCD_EP_Receive failed(0x%02x), %d", ep_cfg->addr, (int)status); return -EIO; } + /* RX started - mark endpoint as busy */ udc_ep_set_busy(ep_cfg, true); return 0; @@ -528,71 +492,35 @@ static void handle_msg_data_out(struct udc_stm32_data *priv, uint8_t epnum, uint net_buf_add(buf, rx_count); if (ep == USB_CONTROL_EP_OUT) { - /* - * OUT EP0 is used for two purposes: - * - receive 'out' Data packets during s-(out)-status - * - receive Status OUT ZLP during s-in-(status) - */ - if (udc_ctrl_stage_is_status_out(dev)) { - /* - * s-in-status completed (ZLP) - * - * Dequeue packet and submit it to stack. - */ - __ASSERT_NO_MSG(rx_count == 0); - buf = udc_buf_get(ep_cfg); - udc_ctrl_update_stage(dev, buf); - udc_ctrl_submit_status(dev, buf); - } else { - /* Verify that host did not send more data than it promised */ - __ASSERT(buf->len <= priv->ep0_out_wlength, - "Received more data from Host than expected!"); - - /* Check if the data stage is complete */ - if (buf->len < priv->ep0_out_wlength) { - HAL_StatusTypeDef __maybe_unused status; - - /* Not yet - prepare to receive more data and wait */ - status = HAL_PCD_EP_Receive(&priv->pcd, ep_cfg->addr, - net_buf_tail(buf), - UDC_STM32_EP0_MAX_PACKET_SIZE); - __ASSERT_NO_MSG(status == HAL_OK); - return; - } /* else: buf->len == priv->ep0_out_wlength */ - - /* - * Data stage is complete: update to next step - * which should be Status IN, then submit the - * Setup+Data phase buffers to UDC stack and - * let it handle the next stage. Don't forget - * to dequeue packet before submitting it. - */ - buf = udc_buf_get(ep_cfg); - udc_ctrl_update_stage(dev, buf); - __ASSERT_NO_MSG(udc_ctrl_stage_is_status_in(dev)); - udc_ctrl_submit_s_out_status(dev, buf); - } - } else { - /* All data received - dequeue packet and submit it to stack */ - buf = udc_buf_get(ep_cfg); - udc_submit_ep_event(dev, buf, 0); + /* Verify that host did not send more data than it promised */ + __ASSERT(buf->len <= priv->ep0_out_wlength, + "Received more data from Host than expected!"); + + /* Check if the data stage is complete */ + if (rx_count == UDC_STM32_EP0_MAX_PACKET_SIZE && + buf->len < priv->ep0_out_wlength) { + HAL_StatusTypeDef __maybe_unused status; + uint32_t rx_size = MIN(net_buf_tailroom(buf), + UDC_STM32_EP0_MAX_PACKET_SIZE); + + /* Not yet - prepare to receive more data and wait */ + status = HAL_PCD_EP_Receive(&priv->pcd, ep_cfg->addr, + net_buf_tail(buf), rx_size); + __ASSERT_NO_MSG(status == HAL_OK); + return; + } /* else: buf->len == priv->ep0_out_wlength */ } - /* Endpoint is no longer busy */ - udc_ep_set_busy(ep_cfg, false); + /* All data received - dequeue packet and submit it to stack */ + buf = udc_buf_get(ep_cfg); + udc_submit_ep_event(dev, buf, 0); /* Prepare next transfer for EP if its queue is not empty */ buf = udc_buf_peek(ep_cfg); if (buf != NULL) { - /* - * Only the driver is allowed to queue transfers on OUT EP0, - * and it should only be doing so once per Control transfer. - * If it has a queued transfer, something must be wrong. - */ - __ASSERT(ep_cfg->addr != USB_CONTROL_EP_OUT, - "OUT EP0 should never have pending transfers!"); - - udc_stm32_rx(dev, ep_cfg, buf); + udc_stm32_initiate_ep_rx(dev, ep_cfg, buf); + } else { /* Nothing left to receive: EP is no longer busy */ + udc_ep_set_busy(ep_cfg, false); } } @@ -656,31 +584,11 @@ static void handle_msg_data_in(struct udc_stm32_data *priv, uint8_t epnum) return; } - udc_buf_get(ep_cfg); - - if (ep == USB_CONTROL_EP_IN) { - if (udc_ctrl_stage_is_status_in(dev) || - udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* - * IN transfer finished, release buffer, - * control OUT buffer should be already fed. - */ - net_buf_unref(buf); - } - - return; - } - + /* drop reference to packet and submit to stack */ + buf = udc_buf_get(ep_cfg); udc_submit_ep_event(dev, buf, 0); + /* enqueue */ buf = udc_buf_peek(ep_cfg); if (buf != NULL) { udc_stm32_tx(dev, ep_cfg, buf); @@ -700,48 +608,6 @@ void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) } } -static void handle_msg_setup(struct udc_stm32_data *priv) -{ - struct usb_setup_packet *setup = (void *)priv->pcd.Setup; - const struct device *dev = priv->dev; - struct net_buf *buf; - int err; - - /* Drop all transfers in control endpoints queue upon new SETUP */ - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); - if (buf != NULL) { - net_buf_unref(buf); - } - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN)); - if (buf != NULL) { - net_buf_unref(buf); - } - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet)); - if (buf == NULL) { - LOG_ERR("Failed to allocate for setup"); - return; - } - - udc_ep_buf_set_setup(buf); - net_buf_add_mem(buf, setup, sizeof(struct usb_setup_packet)); - - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - err = udc_stm32_prep_out_ep0_rx(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - udc_ctrl_submit_s_in_status(dev); - } else { - udc_ctrl_submit_s_status(dev); - } -} - static void udc_stm32_thread_handler(void *arg1, void *arg2, void *arg3) { const struct device *dev = arg1; @@ -752,7 +618,8 @@ static void udc_stm32_thread_handler(void *arg1, void *arg2, void *arg3) k_msgq_get(&priv->msgq_data, &msg, K_FOREVER); switch (msg.type) { case UDC_STM32_MSG_SETUP: - handle_msg_setup(priv); + /* HAL copies SETUP packet contents to pcd.Setup */ + udc_setup_received(dev, priv->pcd.Setup); break; case UDC_STM32_MSG_DATA_IN: handle_msg_data_in(priv, msg.ep); @@ -1176,6 +1043,7 @@ static int udc_stm32_ep_clear_halt(const struct device *dev, struct udc_stm32_data *priv = udc_get_private(dev); HAL_StatusTypeDef status; struct net_buf *buf; + int ret = 0; LOG_DBG("Clear halt for ep 0x%02x", ep_cfg->addr); @@ -1201,11 +1069,12 @@ static int udc_stm32_ep_clear_halt(const struct device *dev, if (USB_EP_DIR_IS_IN(ep_cfg->addr) && !busy) { udc_stm32_tx(dev, ep_cfg, buf); - } else if (USB_EP_DIR_IS_OUT(ep_cfg->addr) && busy) { - udc_stm32_rx(dev, ep_cfg, buf); + } else if (USB_EP_DIR_IS_OUT(ep_cfg->addr) && !busy) { + ret = udc_stm32_initiate_ep_rx(dev, ep_cfg, buf); } } - return 0; + + return ret; } static int udc_stm32_ep_enqueue(const struct device *dev, @@ -1215,10 +1084,10 @@ static int udc_stm32_ep_enqueue(const struct device *dev, unsigned int lock_key; int ret = 0; - udc_buf_put(ep_cfg, buf); - lock_key = irq_lock(); + udc_buf_put(ep_cfg, buf); + if (USB_EP_DIR_IS_IN(ep_cfg->addr)) { if (ep_cfg->stat.halted) { LOG_DBG("skip enqueue for halted ep 0x%02x", ep_cfg->addr); @@ -1226,7 +1095,9 @@ static int udc_stm32_ep_enqueue(const struct device *dev, ret = udc_stm32_tx(dev, ep_cfg, buf); } } else { - ret = udc_stm32_rx(dev, ep_cfg, buf); + if (!udc_ep_is_busy(ep_cfg)) { + ret = udc_stm32_initiate_ep_rx(dev, ep_cfg, buf); + } } irq_unlock(lock_key); @@ -1345,7 +1216,6 @@ static int udc_stm32_driver_preinit(const struct device *dev) } data->caps.rwup = true; - data->caps.out_ack = false; data->caps.addr_before_status = true; data->caps.mps0 = UDC_MPS0_64; if (cfg->selected_speed == PCD_SPEED_HIGH) { diff --git a/drivers/usb/udc/udc_virtual.c b/drivers/usb/udc/udc_virtual.c index db4f56b11b06b..0d5e1340b9b6f 100644 --- a/drivers/usb/udc/udc_virtual.c +++ b/drivers/usb/udc/udc_virtual.c @@ -73,104 +73,18 @@ static void ctrl_ep_clear_halt(const struct device *dev) cfg->stat.halted = false; } -static int vrt_ctrl_feed_dout(const struct device *dev, - const size_t length) -{ - struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); - struct net_buf *buf; - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); - if (buf == NULL) { - return -ENOMEM; - } - - udc_buf_put(ep_cfg, buf); - - return 0; -} - -static void drop_control_transfers(const struct device *dev) -{ - struct net_buf *buf; - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT)); - if (buf != NULL) { - net_buf_unref(buf); - } - - buf = udc_buf_get_all(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN)); - if (buf != NULL) { - net_buf_unref(buf); - } -} - static int vrt_handle_setup(const struct device *dev, struct uvb_packet *const pkt) { - struct net_buf *buf; - int err, ret; - - drop_control_transfers(dev); - - buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, 8); - if (buf == NULL) { - return -ENOMEM; + if (pkt->length != sizeof(struct usb_setup_packet)) { + /* Incorrect SETUP DATA0, device should timeout */ + return vrt_request_reply(dev, pkt, UVB_REPLY_TIMEOUT); } - net_buf_add_mem(buf, pkt->data, pkt->length); - udc_ep_buf_set_setup(buf); + udc_setup_received(dev, pkt->data); ctrl_ep_clear_halt(dev); - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_data_out(dev)) { - /* Allocate and feed buffer for data OUT stage */ - LOG_DBG("s: %p | feed for -out-", buf); - err = vrt_ctrl_feed_dout(dev, udc_data_stage_length(buf)); - if (err == -ENOMEM) { - /* - * Pass it on to the higher level which will - * halt control OUT endpoint. - */ - err = udc_submit_ep_event(dev, buf, err); - } - } else if (udc_ctrl_stage_is_data_in(dev)) { - LOG_DBG("s: %p | submit for -in-", buf); - /* Allocate buffer for data IN and submit to upper layer */ - err = udc_ctrl_submit_s_in_status(dev); - } else { - LOG_DBG("s:%p | submit for -status", buf); - /* - * For all other cases we feed with a buffer - * large enough for setup packet. - */ - err = udc_ctrl_submit_s_status(dev); - } - - ret = vrt_request_reply(dev, pkt, UVB_REPLY_ACK); - - return ret ? ret : err; -} - -static int vrt_handle_ctrl_out(const struct device *dev, - struct net_buf *const buf) -{ - int err = 0; - - if (udc_ctrl_stage_is_status_out(dev)) { - /* Status stage finished, notify upper layer */ - err = udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_in(dev)) { - return udc_ctrl_submit_s_out_status(dev, buf); - } - - return err; + return vrt_request_reply(dev, pkt, UVB_REPLY_ACK); } static int vrt_handle_out(const struct device *dev, @@ -203,11 +117,7 @@ static int vrt_handle_out(const struct device *dev, if (net_buf_tailroom(buf) == 0 || pkt->length < udc_mps_ep_size(ep_cfg)) { buf = udc_buf_get(ep_cfg); - if (ep == USB_CONTROL_EP_OUT) { - err = vrt_handle_ctrl_out(dev, buf); - } else { - err = udc_submit_ep_event(dev, buf, 0); - } + err = udc_submit_ep_event(dev, buf, 0); } ret = vrt_request_reply(dev, pkt, UVB_REPLY_ACK); @@ -215,31 +125,6 @@ static int vrt_handle_out(const struct device *dev, return ret ? ret : err; } -static int isr_handle_ctrl_in(const struct device *dev, - struct net_buf *const buf) -{ - int err = 0; - - if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { - /* Status stage finished, notify upper layer */ - err = udc_ctrl_submit_status(dev, buf); - } - - /* Update to next stage of control transfer */ - udc_ctrl_update_stage(dev, buf); - - if (udc_ctrl_stage_is_status_out(dev)) { - /* - * IN transfer finished, release buffer, - * Feed control OUT buffer for status stage. - */ - net_buf_unref(buf); - return vrt_ctrl_feed_dout(dev, 0); - } - - return err; -} - static int vrt_handle_in(const struct device *dev, struct uvb_packet *const pkt) { @@ -278,11 +163,7 @@ static int vrt_handle_in(const struct device *dev, LOG_DBG("Finish data IN %zu | %u", pkt->length, buf->len); buf = udc_buf_get(ep_cfg); - if (ep == USB_CONTROL_EP_IN) { - err = isr_handle_ctrl_in(dev, buf); - } else { - err = udc_submit_ep_event(dev, buf, 0); - } + err = udc_submit_ep_event(dev, buf, 0); } continue_in: @@ -410,6 +291,7 @@ static int udc_vrt_ep_enqueue(const struct device *dev, struct net_buf *buf) { LOG_DBG("%p enqueue %p", dev, buf); + udc_buf_put(cfg, buf); if (cfg->stat.halted) { diff --git a/include/zephyr/drivers/usb/udc.h b/include/zephyr/drivers/usb/udc.h index 8d9c3d732ac2b..d18ab591076d7 100644 --- a/include/zephyr/drivers/usb/udc.h +++ b/include/zephyr/drivers/usb/udc.h @@ -38,7 +38,11 @@ struct udc_device_caps { uint32_t hs : 1; /** Controller supports USB remote wakeup */ uint32_t rwup : 1; - /** Controller performs status OUT stage automatically */ + /** + * Controller performs Status OUT stage automatically after Data IN. + * + * When set, USB stack will not enqueue Status OUT buffer. + */ uint32_t out_ack : 1; /** Controller expects device address to be set before status stage */ uint32_t addr_before_status : 1; @@ -285,12 +289,14 @@ struct udc_data { const void *event_ctx; /** USB device controller status */ atomic_t status; - /** Internal used Control Sequence Stage */ - int stage; - /** Pointer to buffer containing setup packet */ - struct net_buf *setup; /** Driver private data */ void *priv; + /** Last cached setup data (not necessarily last received setup data) */ + uint8_t setup[8]; + /** Cached setup data is waiting for USB stack */ + bool setup_pending; + /** Last cached setup data is valid (8 bytes, CRC OK) */ + bool setup_valid; }; /** @@ -298,7 +304,7 @@ struct udc_data { * @defgroup udc_api USB Device Controller * @ingroup usb_interfaces * @since 3.3 - * @version 0.1.0 + * @version 0.1.1 * @{ */ @@ -635,6 +641,26 @@ int udc_ep_clear_halt(const struct device *dev, const uint8_t ep); */ int udc_ep_enqueue(const struct device *dev, struct net_buf *const buf); +/** + * @brief Purges control endpoint queues after controller shutdown + * + * @param[in] dev Pointer to device struct of the driver instance + * + * @return 0 on success, all other values should be treated as error. + * @return -EBUSY controller is not shutdown + */ +int udc_purge_queues(const struct device *dev); + +/** + * @brief Determines if endpoint queue is empty + * + * @param[in] dev Pointer to device struct of the driver instance + * @param[in] ep Endpoint address + * + * @return true if endpoint queue is empty, false otherwise + */ +bool udc_ep_queue_is_empty(const struct device *dev, const uint8_t ep); + /** * @brief Remove all USB device controller requests from endpoint queue * @@ -668,6 +694,45 @@ struct net_buf *udc_ep_buf_alloc(const struct device *dev, const uint8_t ep, const size_t size); +/** + * @brief Allocate UDC control transfer SETUP buffer + * + * Allocate a new buffer from common control transfer buffer pool. + * + * @param[in] dev Pointer to device struct of the driver instance + * + * @return pointer to allocated request or NULL on error. + */ +struct net_buf *udc_ctrl_setup_alloc(const struct device *dev); + +/** + * @brief Allocate UDC control transfer data stage buffer + * + * Allocate a new buffer from common control transfer buffer pool. + * + * @param[in] dev Pointer to device struct of the driver instance + * @param[in] ep Control endpoint address + * @param[in] size Size of the request buffer + * + * @return pointer to allocated request or NULL on error. + */ +struct net_buf *udc_ctrl_data_alloc(const struct device *dev, + const uint8_t ep, + const size_t size); + +/** + * @brief Allocate UDC control transfer status stage buffer + * + * Allocate a new buffer from common control transfer buffer pool. + * + * @param[in] dev Pointer to device struct of the driver instance + * @param[in] ep Control endpoint address + * + * @return pointer to allocated request or NULL on error. + */ +struct net_buf *udc_ctrl_status_alloc(const struct device *dev, + const uint8_t ep); + /** * @brief Free UDC request buffer * diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 352f24a2a2dc7..4527f417483ee 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -210,8 +210,6 @@ enum usbd_ch9_state { struct usbd_ch9_data { /** Setup packet, up-to-date for the respective control request */ struct usb_setup_packet setup; - /** Control type, internally used for stage verification */ - int ctrl_type; /** Protocol state of the USB device stack */ enum usbd_ch9_state state; /** Halted endpoints bitmap */ @@ -307,6 +305,8 @@ struct usbd_context { void *fs_desc; /** Pointer to High-Speed device descriptor */ void *hs_desc; + /** Pre-allocated buffer for control transfer SETUP stage */ + struct net_buf *setup_buf; }; /** @@ -1029,6 +1029,19 @@ bool usbd_ep_is_halted(struct usbd_context *uds_ctx, uint8_t ep); struct net_buf *usbd_ep_buf_alloc(const struct usbd_class_data *const c_data, const uint8_t ep, const size_t size); +/** + * @brief Allocate buffer for USB control transfer data stage + * + * Allocate a new buffer from controller's driver buffer pool. + * + * @param[in] uds_ctx Pointer to USB device support context + * @param[in] size Size of the request buffer + * + * @return pointer to allocated request or NULL on error. + */ +struct net_buf *usbd_ep_ctrl_data_in_alloc(const struct usbd_context *const uds_ctx, + const size_t size); + /** * @brief Queue USB device control request * diff --git a/subsys/usb/device_next/usbd_ch9.c b/subsys/usb/device_next/usbd_ch9.c index c31e406c275c9..666a9ba49d739 100644 --- a/subsys/usb/device_next/usbd_ch9.c +++ b/subsys/usb/device_next/usbd_ch9.c @@ -24,9 +24,6 @@ #include LOG_MODULE_REGISTER(usbd_ch9, CONFIG_USBD_LOG_LEVEL); -#define CTRL_AWAIT_SETUP_DATA 0 -#define CTRL_AWAIT_STATUS_STAGE 1 - #define SF_TEST_MODE_SELECTOR(wIndex) ((uint8_t)((wIndex) >> 8)) #define SF_TEST_LOWER_BYTE(wIndex) ((uint8_t)(wIndex)) @@ -43,17 +40,6 @@ static bool reqtype_is_to_device(const struct usb_setup_packet *const setup) return !reqtype_is_to_host(setup); } -static void ch9_set_ctrl_type(struct usbd_context *const uds_ctx, - const int type) -{ - uds_ctx->ch9_data.ctrl_type = type; -} - -static int ch9_get_ctrl_type(struct usbd_context *const uds_ctx) -{ - return uds_ctx->ch9_data.ctrl_type; -} - static int post_status_stage(struct usbd_context *const uds_ctx) { struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx); @@ -1058,10 +1044,8 @@ static int ctrl_xfer_get_setup(struct usbd_context *const uds_ctx, struct net_buf *const buf) { struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx); - struct net_buf *buf_b; - struct udc_buf_info *bi, *bi_b; - if (buf->len < sizeof(struct usb_setup_packet)) { + if (buf->len != sizeof(struct usb_setup_packet)) { return -EINVAL; } @@ -1071,65 +1055,74 @@ static int ctrl_xfer_get_setup(struct usbd_context *const uds_ctx, setup->wIndex = sys_le16_to_cpu(setup->wIndex); setup->wLength = sys_le16_to_cpu(setup->wLength); - bi = udc_get_buf_info(buf); + return 0; +} + +static int usbd_enqueue_setup(struct usbd_context *const uds_ctx) +{ + struct net_buf *setup; + int ret; + + /* Reset and increase reference counter to facilitate buffer reuse */ + setup = net_buf_ref(uds_ctx->setup_buf); + net_buf_reset(setup); - buf_b = buf->frags; - if (buf_b == NULL) { - LOG_ERR("Buffer for data|status is missing"); - return -ENODATA; + ret = usbd_ep_ctrl_enqueue(uds_ctx, setup); + if (ret) { + LOG_ERR("Failed to enqueue SETUP buffer"); + net_buf_unref(setup); } - bi_b = udc_get_buf_info(buf_b); + return ret; +} - if (reqtype_is_to_device(setup)) { - if (setup->wLength) { - if (!bi_b->data) { - LOG_ERR("%p is not data", buf_b); - return -EINVAL; - } - } else { - if (!bi_b->status) { - LOG_ERR("%p is not status", buf_b); - return -EINVAL; - } - } - } else { - if (!setup->wLength) { - LOG_ERR("device-to-host with wLength zero"); - return -ENOTSUP; - } +static int usbd_enqueue_status_in(struct usbd_context *const uds_ctx) +{ + struct net_buf *status_in; + int ret; - if (!bi_b->data) { - LOG_ERR("%p is not data", buf_b); - return -EINVAL; - } + status_in = udc_ctrl_status_alloc(uds_ctx->dev, USB_CONTROL_EP_IN); + if (status_in == NULL) { + return -ENOMEM; + } + ret = usbd_ep_ctrl_enqueue(uds_ctx, status_in); + if (ret) { + LOG_ERR("Failed to enqueue Status IN buffer"); + net_buf_unref(status_in); } - return 0; + return ret; } -static struct net_buf *spool_data_out(struct net_buf *const buf) +static int usbd_enqueue_status_out(struct usbd_context *const uds_ctx) { - struct net_buf *next_buf = buf; - struct udc_buf_info *bi; + struct udc_device_caps caps = udc_caps(uds_ctx->dev); + struct net_buf *status_out; + int ret; - while (next_buf) { - LOG_DBG("spool %p", next_buf); - next_buf = net_buf_frag_del(NULL, next_buf); - if (next_buf) { - bi = udc_get_buf_info(next_buf); - if (bi->status) { - return next_buf; - } - } + /* Status OUT can only happen after Data IN */ + if (caps.out_ack) { + /* Nothing to do, controller will handle things automatically */ + return 0; } - return NULL; + status_out = udc_ctrl_status_alloc(uds_ctx->dev, USB_CONTROL_EP_OUT); + if (status_out == NULL) { + return -ENOMEM; + } + + ret = usbd_ep_ctrl_enqueue(uds_ctx, status_out); + if (ret) { + LOG_ERR("Failed to enqueue Status OUT buffer"); + net_buf_unref(status_out); + } + + return ret; } int usbd_handle_ctrl_xfer(struct usbd_context *const uds_ctx, - struct net_buf *const buf, const int err) + struct net_buf *const buf, int err) { struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx); struct udc_buf_info *bi; @@ -1141,45 +1134,76 @@ int usbd_handle_ctrl_xfer(struct usbd_context *const uds_ctx, return -EIO; } - if (err && err != -ENOMEM && !bi->setup) { - if (err == -ECONNABORTED) { - LOG_INF("Transfer 0x%02x aborted (bus reset?)", bi->ep); - net_buf_unref(buf); - return 0; - } + LOG_DBG("Handle control %p ep 0x%02x, len %u, s:%u d:%u s:%u, err %d", + buf, bi->ep, buf->len, bi->setup, bi->data, bi->status, err); - LOG_ERR("Control transfer for 0x%02x has error %d, halt", - bi->ep, err); + if (err) { net_buf_unref(buf); - return err; + + if (bi->setup || (bi->data && bi->ep == USB_CONTROL_EP_OUT)) { + return usbd_enqueue_setup(uds_ctx); + } + + return 0; } - LOG_DBG("Handle control %p ep 0x%02x, len %u, s:%u d:%u s:%u", - buf, bi->ep, buf->len, bi->setup, bi->data, bi->status); + if (bi->data && bi->ep == USB_CONTROL_EP_IN) { + net_buf_unref(buf); + return 0; + } - if (bi->setup && bi->ep == USB_CONTROL_EP_OUT) { + if ((bi->setup || bi->data) && bi->ep == USB_CONTROL_EP_OUT) { struct net_buf *next_buf; - if (ctrl_xfer_get_setup(uds_ctx, buf)) { - LOG_ERR("Malformed setup packet"); - net_buf_unref(buf); - goto ctrl_xfer_stall; - } + if (bi->setup) { + if (ctrl_xfer_get_setup(uds_ctx, buf)) { + LOG_ERR("Malformed setup packet"); + net_buf_unref(buf); + goto ctrl_xfer_stall; + } - /* Remove setup packet buffer from the chain */ - next_buf = net_buf_frag_del(NULL, buf); - if (next_buf == NULL) { - LOG_ERR("Buffer for data|status is missing"); - goto ctrl_xfer_stall; + /* Remove setup packet buffer from the chain */ + next_buf = net_buf_frag_del(NULL, buf); + if (next_buf != NULL) { + LOG_ERR("Unexpected buffer linked to setup"); + net_buf_unref(next_buf); + goto ctrl_xfer_stall; + } + + if (reqtype_is_to_device(setup) && setup->wLength) { + next_buf = udc_ctrl_data_alloc(uds_ctx->dev, USB_CONTROL_EP_OUT, + setup->wLength); + if (next_buf == NULL) { + err = -ENOMEM; + goto ctrl_xfer_stall; + } + ret = usbd_ep_ctrl_enqueue(uds_ctx, next_buf); + return ret; + } + + if (setup->wLength) { + /* Stack is supposed to allocate buffer */ + next_buf = usbd_ep_ctrl_data_in_alloc(uds_ctx, setup->wLength); + if (next_buf == NULL) { + err = -ENOMEM; + goto ctrl_xfer_stall; + } + } + } else { + /* Data OUT received */ + next_buf = buf; } /* * Handle request and data stage, next_buf is either - * data+status or status buffers. + * data buffer or is NULL. */ ret = handle_setup_request(uds_ctx, next_buf); - if (ret) { + if ((ret || errno) && next_buf) { net_buf_unref(next_buf); + } + + if (ret) { return ret; } @@ -1188,50 +1212,53 @@ int usbd_handle_ctrl_xfer(struct usbd_context *const uds_ctx, * Halt, only protocol errors are recoverable. * Free data stage and linked status stage buffer. */ - net_buf_unref(next_buf); goto ctrl_xfer_stall; } - ch9_set_ctrl_type(uds_ctx, CTRL_AWAIT_STATUS_STAGE); - if (reqtype_is_to_device(setup) && setup->wLength) { - /* Enqueue STATUS (IN) buffer */ - next_buf = spool_data_out(next_buf); - if (next_buf == NULL) { - LOG_ERR("Buffer for status is missing"); - goto ctrl_xfer_stall; - } + if (setup->wLength == 0) { + ret = usbd_enqueue_status_in(uds_ctx); + } else if (reqtype_is_to_device(setup)) { + /* Data OUT buffer is no longer needed */ + net_buf_unref(next_buf); - ret = usbd_ep_ctrl_enqueue(uds_ctx, next_buf); + ret = usbd_enqueue_status_in(uds_ctx); } else { - /* Enqueue DATA (IN) or STATUS (OUT) buffer */ + /* Enqueue Data IN */ ret = usbd_ep_ctrl_enqueue(uds_ctx, next_buf); + if (ret) { + net_buf_unref(next_buf); + goto ctrl_xfer_stall; + } + + /* 8.5.3.3 Error Handling on the Last Data Transaction + * effectively requires us to enqueue status OUT before + * device knows that data IN finishes. + */ + ret = usbd_enqueue_status_out(uds_ctx); + } + + if (ret) { + goto ctrl_xfer_stall; } + ret = usbd_enqueue_setup(uds_ctx); + return ret; } if (bi->status && bi->ep == USB_CONTROL_EP_OUT) { - if (ch9_get_ctrl_type(uds_ctx) == CTRL_AWAIT_STATUS_STAGE) { - LOG_DBG("s-in-status finished"); - } else { - LOG_WRN("Awaited s-in-status not finished"); - } - + LOG_DBG("Status OUT finished"); net_buf_unref(buf); - return 0; + return ret; } if (bi->status && bi->ep == USB_CONTROL_EP_IN) { net_buf_unref(buf); - if (ch9_get_ctrl_type(uds_ctx) == CTRL_AWAIT_STATUS_STAGE) { - LOG_DBG("s-(out)-status finished"); - if (unlikely(uds_ctx->ch9_data.post_status)) { - ret = post_status_stage(uds_ctx); - } - } else { - LOG_WRN("Awaited s-(out)-status not finished"); + LOG_DBG("Status IN finished"); + if (unlikely(uds_ctx->ch9_data.post_status)) { + ret = post_status_stage(uds_ctx); } return ret; @@ -1255,15 +1282,25 @@ int usbd_handle_ctrl_xfer(struct usbd_context *const uds_ctx, ret = udc_ep_set_halt(uds_ctx->dev, USB_CONTROL_EP_IN); } - ch9_set_ctrl_type(uds_ctx, CTRL_AWAIT_SETUP_DATA); + ret = usbd_enqueue_setup(uds_ctx); return ret; } -int usbd_init_control_pipe(struct usbd_context *const uds_ctx) +int usbd_init_control_pipe(struct usbd_context *const uds_ctx, bool enqueue_setup) { + int ret = 0; + + if (enqueue_setup) { + /* Signal to UDC that stack is ready to process SETUP data */ + ret = usbd_enqueue_setup(uds_ctx); + + if (ret != 0) { + return ret; + } + } + uds_ctx->ch9_data.state = USBD_STATE_DEFAULT; - ch9_set_ctrl_type(uds_ctx, CTRL_AWAIT_SETUP_DATA); return 0; } diff --git a/subsys/usb/device_next/usbd_ch9.h b/subsys/usb/device_next/usbd_ch9.h index 168baa024d7e2..14d97ecf8a13f 100644 --- a/subsys/usb/device_next/usbd_ch9.h +++ b/subsys/usb/device_next/usbd_ch9.h @@ -141,11 +141,12 @@ int usbd_handle_ctrl_xfer(struct usbd_context *uds_ctx, /** * @brief Initialize control pipe to default values * - * @param[in] uds_ctx Pointer to a device context + * @param[in] uds_ctx Pointer to a device context + * @param[in] enqueue_setup True to enqueue SETUP buffer * * @return 0 on success, other values on fail. */ -int usbd_init_control_pipe(struct usbd_context *uds_ctx); +int usbd_init_control_pipe(struct usbd_context *uds_ctx, bool enqueue_setup); #endif /* ZEPHYR_INCLUDE_USBD_CH9_H */ diff --git a/subsys/usb/device_next/usbd_core.c b/subsys/usb/device_next/usbd_core.c index a802d74028a29..93b630801149e 100644 --- a/subsys/usb/device_next/usbd_core.c +++ b/subsys/usb/device_next/usbd_core.c @@ -134,11 +134,6 @@ static int event_handler_bus_reset(struct usbd_context *const uds_ctx) return ret; } - /* There might be pending data stage transfer */ - if (usbd_ep_dequeue(uds_ctx, USB_CONTROL_EP_IN)) { - LOG_ERR("Failed to dequeue control IN"); - } - LOG_INF("Actual device speed %u", udc_device_speed(uds_ctx->dev)); udc_speed = udc_device_speed(uds_ctx->dev); switch (udc_speed) { diff --git a/subsys/usb/device_next/usbd_device.c b/subsys/usb/device_next/usbd_device.c index fcb689a5c567e..ec75f0bc2c8a1 100644 --- a/subsys/usb/device_next/usbd_device.c +++ b/subsys/usb/device_next/usbd_device.c @@ -268,8 +268,40 @@ int usbd_init(struct usbd_context *const uds_ctx) return ret; } +static int usbd_preallocate(struct usbd_context *const uds_ctx) +{ + /* SETUP buffer allocation must not fail. Ensure SETUP buffer is always + * available by pre-allocating it. USB stack effectively resubmits the + * same buffer after each control transfer. This ensures that SETUP + * stage can always be received regardless of UDC memory usage. + * + * Preallocation happens in usbd_enable() because bMaxPacketSize0 must + * be known at allocation time. The buffer is kept until usbd_shutdown() + * to not reallocate it on udc_disable()/udc_enable() cycles. + */ + if (uds_ctx->setup_buf == NULL) { + uds_ctx->setup_buf = udc_ctrl_setup_alloc(uds_ctx->dev); + } + + if (uds_ctx->setup_buf == NULL) { + return -ENOMEM; + } + + return 0; +} + +static void usbd_free_preallocated(struct usbd_context *const uds_ctx) +{ + /* Release reference to pre-allocated setup buffer */ + if (uds_ctx->setup_buf != NULL) { + net_buf_unref(uds_ctx->setup_buf); + uds_ctx->setup_buf = NULL; + } +} + int usbd_enable(struct usbd_context *const uds_ctx) { + bool ep0_empty; int ret; k_sched_lock(); @@ -287,13 +319,27 @@ int usbd_enable(struct usbd_context *const uds_ctx) goto enable_exit; } + /* UDC drivers can keep enqueued control buffers across disable/enable + * cycle. Enqueue SETUP buffer only if there are no queued buffers. + * This check has to be done before udc_enable(), because only before + * enable the driver won't complete any queued buffer. + */ + ep0_empty = udc_ep_queue_is_empty(uds_ctx->dev, USB_CONTROL_EP_OUT); + ret = udc_enable(uds_ctx->dev); if (ret != 0) { LOG_ERR("Failed to enable controller"); goto enable_exit; } - ret = usbd_init_control_pipe(uds_ctx); + ret = usbd_preallocate(uds_ctx); + if (ret != 0) { + LOG_ERR("Buffer preallocation failed"); + udc_disable(uds_ctx->dev); + goto enable_exit; + } + + ret = usbd_init_control_pipe(uds_ctx, ep0_empty); if (ret != 0) { udc_disable(uds_ctx->dev); goto enable_exit; @@ -342,12 +388,18 @@ int usbd_shutdown(struct usbd_context *const uds_ctx) usbd_device_lock(uds_ctx); - /* TODO: control request dequeue ? */ ret = usbd_device_shutdown_core(uds_ctx); if (ret) { LOG_ERR("Failed to shutdown USB device"); } + ret = udc_purge_queues(uds_ctx->dev); + if (ret) { + LOG_ERR("Failed to purge endpoint queues"); + } + + usbd_free_preallocated(uds_ctx); + uds_ctx->status.initialized = false; usbd_device_unlock(uds_ctx); diff --git a/subsys/usb/device_next/usbd_endpoint.c b/subsys/usb/device_next/usbd_endpoint.c index 1e0ef658d35ca..0023604847b52 100644 --- a/subsys/usb/device_next/usbd_endpoint.c +++ b/subsys/usb/device_next/usbd_endpoint.c @@ -99,6 +99,12 @@ static void usbd_ep_ctrl_set_zlp(struct usbd_context *const uds_ctx, * All the functions below are part of public USB device support API. */ +struct net_buf *usbd_ep_ctrl_data_in_alloc(const struct usbd_context *const uds_ctx, + const size_t size) +{ + return udc_ctrl_data_alloc(uds_ctx->dev, USB_CONTROL_EP_IN, size); +} + int usbd_ep_ctrl_enqueue(struct usbd_context *const uds_ctx, struct net_buf *const buf) {