Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 19 additions & 131 deletions drivers/usb/udc/udc_sam0.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

#error This UDC driver has to be adapted to new control transfer handling

#include "udc_common.h"

#include <string.h>
Expand Down Expand Up @@ -309,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;

Expand All @@ -392,41 +320,13 @@ 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);
}

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) {
Expand All @@ -436,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,
Expand All @@ -468,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 {
Expand Down Expand Up @@ -504,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);
}
Expand Down Expand Up @@ -542,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, true);
}

udc_unlock_internal(dev);
Expand Down
Loading