Skip to content
113 changes: 92 additions & 21 deletions drivers/usb/udc/udc_dwc2.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
/* Isochronous endpoint enabled (IN on bits 0-15, OUT on bits 16-31) */
uint32_t iso_enabled;
uint16_t iso_in_rearm;
uint16_t iso_out_rearm;
uint16_t ep_out_disable;
uint16_t ep_out_stall;
uint16_t txf_set;
Expand All @@ -140,7 +141,8 @@
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;

Check notice on line 145 in drivers/usb/udc/udc_dwc2.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/usb/udc/udc_dwc2.c:145 - unsigned int ignore_ep0_nakeff : 1; + unsigned int ignore_ep0_nakeff: 1;
/* Number of endpoints including control endpoint */
uint8_t numdeveps;
/* Number of IN endpoints including control endpoint */
Expand Down Expand Up @@ -484,7 +486,6 @@
mem_addr_t dieptsiz_reg = (mem_addr_t)&base->in_ep[ep_idx].dieptsiz;
/* TODO: use dwc2_get_dxepctl_reg() */
mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl;
mem_addr_t diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint;

uint32_t diepctl;
uint32_t max_xfersize, max_pktcnt, pktcnt;
Expand Down Expand Up @@ -616,9 +617,6 @@
}
sys_write32(diepctl, diepctl_reg);

/* Clear IN Endpoint NAK Effective interrupt in case it was set */
sys_write32(USB_DWC2_DIEPINT_INEPNAKEFF, diepint_reg);

if (dwc2_in_completer_mode(dev)) {
const uint8_t *src = buf->data;

Expand Down Expand Up @@ -810,7 +808,20 @@
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
dwc2_prep_rx(dev, buf, cfg);
} else {
int err = dwc2_tx_fifo_write(dev, cfg, buf);
int err;

if (cfg->addr == USB_CONTROL_EP_IN &&
udc_ctrl_stage_is_status_in(dev)) {
/* It was observed that EPENA results in INEPNAKEFF

Check notice on line 815 in drivers/usb/udc/udc_dwc2.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/usb/udc/udc_dwc2.c:815 - if (cfg->addr == USB_CONTROL_EP_IN && - udc_ctrl_stage_is_status_in(dev)) { + if (cfg->addr == USB_CONTROL_EP_IN && udc_ctrl_stage_is_status_in(dev)) {
* 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;
}

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
Expand Down Expand Up @@ -861,6 +872,7 @@
* transfer beforehand. In Buffer DMA the SETUP can be copied to any EP0
* OUT buffer. If there is any buffer queued, it is obsolete now.
*/
priv->ignore_ep0_nakeff = 0;
udc_dwc2_ep_disable(dev, cfg_in, false, true);
atomic_and(&priv->xfer_finished, ~(BIT(0) | BIT(16)));

Expand Down Expand Up @@ -2657,7 +2669,21 @@
}

if (status & USB_DWC2_DIEPINT_INEPNAKEFF) {
sys_set_bits(diepctl_reg, USB_DWC2_DEPCTL_EPDIS);
uint32_t diepctl = sys_read32(diepctl_reg);

if (!(diepctl & USB_DWC2_DEPCTL_NAKSTS)) {
/* Ignore stale NAK effective interrupt */
} else if (n == 0 && priv->ignore_ep0_nakeff) {
/* Status stage enabled endpoint. NAK will be
* cleared in STSPHSERCVD interrupt.
*/
} else if (diepctl & USB_DWC2_DEPCTL_EPENA) {
diepctl &= ~USB_DWC2_DEPCTL_EPENA;
diepctl |= USB_DWC2_DEPCTL_EPDIS;
sys_write32(diepctl, diepctl_reg);
} else if (priv->iso_in_rearm & (BIT(n))) {
priv->iso_in_rearm &= ~BIT(n);
}
}

if (status & USB_DWC2_DIEPINT_EPDISBLD) {
Expand All @@ -2675,6 +2701,13 @@
if ((usb_dwc2_get_depctl_eptype(diepctl) == USB_DWC2_DEPCTL_EPTYPE_ISO) &&
(priv->iso_in_rearm & BIT(n))) {
struct udc_ep_config *cfg = udc_get_ep_cfg(dev, n | USB_EP_DIR_IN);
struct net_buf *buf;

/* Data is no longer relevant, discard it */
buf = udc_buf_get(cfg);
if (buf) {
udc_submit_ep_event(dev, buf, 0);
}

/* Try to queue next packet before SOF */
dwc2_handle_xfer_next(dev, cfg);
Expand Down Expand Up @@ -2776,6 +2809,7 @@
while (epint) {
uint8_t n = find_lsb_set(epint) - 1;
mem_addr_t doepint_reg = (mem_addr_t)&base->out_ep[n].doepint;
mem_addr_t doepctl_reg = (mem_addr_t)&base->out_ep[n].doepctl;
uint32_t doepint;
uint32_t status;

Expand Down Expand Up @@ -2828,7 +2862,28 @@
}

if (status & USB_DWC2_DOEPINT_EPDISBLD) {
uint32_t doepctl = sys_read32(doepctl_reg);

k_event_post(&priv->ep_disabled, BIT(16 + n));

if ((usb_dwc2_get_depctl_eptype(doepctl) == USB_DWC2_DEPCTL_EPTYPE_ISO) &&
(priv->iso_out_rearm & BIT(n))) {
struct udc_ep_config *cfg;
struct net_buf *buf;

cfg = udc_get_ep_cfg(dev, n | USB_EP_DIR_OUT);

/* Discard disabled transfer buffer */
buf = udc_buf_get(cfg);
if (buf) {
udc_submit_ep_event(dev, buf, 0);
}

/* Try to queue next packet before SOF */
dwc2_handle_xfer_next(dev, cfg);

priv->iso_out_rearm &= ~BIT(n);
}
}

epint &= ~BIT(n);
Expand Down Expand Up @@ -2868,21 +2923,14 @@
/* Check if endpoint didn't receive ISO IN data */
if ((diepctl & mask) == val) {
struct udc_ep_config *cfg;
struct net_buf *buf;

cfg = udc_get_ep_cfg(dev, i | USB_EP_DIR_IN);
__ASSERT_NO_MSG(cfg && cfg->stat.enabled &&
dwc2_ep_is_iso(cfg));

udc_dwc2_ep_disable(dev, cfg, false, false);

buf = udc_buf_get(cfg);
if (buf) {
/* Data is no longer relevant */
udc_submit_ep_event(dev, buf, 0);

rearm |= BIT(i);
}
rearm |= BIT(i);
}

eps &= ~BIT(i);
Expand Down Expand Up @@ -2913,6 +2961,7 @@
((priv->sof_num & 1) ? USB_DWC2_DEPCTL_DPID : 0) |
USB_DWC2_DEPCTL_USBACTEP;
uint32_t eps = (priv->iso_enabled & 0xFFFF0000UL) >> 16;
uint16_t rearm = 0;

while (eps) {
uint8_t i = find_lsb_set(eps) - 1;
Expand All @@ -2924,23 +2973,22 @@
/* Check if endpoint didn't receive ISO OUT data */
if ((doepctl & mask) == val) {
struct udc_ep_config *cfg;
struct net_buf *buf;

cfg = udc_get_ep_cfg(dev, i);
__ASSERT_NO_MSG(cfg && cfg->stat.enabled &&
dwc2_ep_is_iso(cfg));

udc_dwc2_ep_disable(dev, cfg, false, false);

buf = udc_buf_get(cfg);
if (buf) {
udc_submit_ep_event(dev, buf, 0);
}
rearm |= BIT(i);
}

eps &= ~BIT(i);
}

/* Mark endpoints to re-arm in EPDISBLD handler */
priv->iso_out_rearm = rearm;

sys_write32(USB_DWC2_GINTSTS_INCOMPISOOUT, gintsts_reg);
}

Expand All @@ -2963,8 +3011,31 @@
doepctl_reg = (mem_addr_t)&base->out_ep[ep_idx].doepctl;
doepctl = sys_read32(doepctl_reg);

/* The application cannot disable control OUT endpoint 0. */
if (ep_idx != 0) {
if (!(doepctl & USB_DWC2_DEPCTL_EPENA)) {
/* While endpoint was enabled when application requested
* to disable it, there is a race window between setting
* DCTL SGOUTNAK bit and it becoming effective. During
* the window, it is possible for host to actually send
* OUT DATA packet ending the transfer which disables
* the endpoint.
*
* If we arrived here due to INCOMPISOOUT, clearing
* the rearm flag is enough.
*/
if (priv->iso_out_rearm & BIT(ep_idx)) {
priv->iso_out_rearm &= ~BIT(ep_idx);
}

/* If application requested STALL then set it here, but
* otherwise there's nothing to do.
*/
if (!(priv->ep_out_stall & BIT(ep_idx))) {
continue;
}
} else if (ep_idx != 0) {
/* Only set EPDIS if EPENA is set, but do not set it for
* endpoint 0 which cannot be disabled by application.
*/
doepctl |= USB_DWC2_DEPCTL_EPDIS;
}

Expand Down
2 changes: 2 additions & 0 deletions samples/subsys/usb/uac2_implicit_feedback/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ static void uac2_terminal_update_cb(const struct device *dev, uint8_t terminal,
!ctx->microphone_enabled) {
i2s_trigger(ctx->i2s_dev, I2S_DIR_BOTH, I2S_TRIGGER_DROP);
ctx->i2s_started = false;
ctx->rx_started = false;
ctx->i2s_counter = 0;
ctx->plus_ones = ctx->minus_ones = 0;
if (ctx->pending_mic_samples) {
Expand Down Expand Up @@ -172,6 +173,7 @@ static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal,
ret = i2s_write(ctx->i2s_dev, buf, size);
if (ret < 0) {
ctx->i2s_started = false;
ctx->rx_started = false;
ctx->i2s_counter = 0;
ctx->plus_ones = ctx->minus_ones = 0;
if (ctx->pending_mic_samples) {
Expand Down
10 changes: 10 additions & 0 deletions subsys/usb/device_next/class/usbd_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,29 @@ struct hid_device_data {

static inline uint8_t hid_get_in_ep(struct usbd_class_data *const c_data)
{
struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
const struct device *dev = usbd_class_get_private(c_data);
const struct hid_device_config *dcfg = dev->config;
struct usbd_hid_descriptor *desc = dcfg->desc;

if (USBD_SUPPORTS_HIGH_SPEED && usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
return desc->hs_in_ep.bEndpointAddress;
}

return desc->in_ep.bEndpointAddress;
}

static inline uint8_t hid_get_out_ep(struct usbd_class_data *const c_data)
{
struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
const struct device *dev = usbd_class_get_private(c_data);
const struct hid_device_config *dcfg = dev->config;
struct usbd_hid_descriptor *desc = dcfg->desc;

if (USBD_SUPPORTS_HIGH_SPEED && usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
return desc->hs_out_ep.bEndpointAddress;
}

return desc->out_ep.bEndpointAddress;
}

Expand Down
Loading
Loading