diff --git a/drivers/usb/common/usb_dwc2_hw.h b/drivers/usb/common/usb_dwc2_hw.h index fcd0d2a0060..c0bba3504a8 100644 --- a/drivers/usb/common/usb_dwc2_hw.h +++ b/drivers/usb/common/usb_dwc2_hw.h @@ -763,6 +763,7 @@ USB_DWC2_SET_FIELD_DEFINE(dctl_tstctl, DCTL_TSTCTL) #define USB_DWC2_DSTS_DEVLNSTS_MASK (0x3UL << USB_DWC2_DSTS_DEVLNSTS_POS) #define USB_DWC2_DSTS_SOFFN_POS 8UL #define USB_DWC2_DSTS_SOFFN_MASK (0x3FFFUL << USB_DWC2_DSTS_SOFFN_POS) +#define USB_DWC2_DSTS_SOFFN_LIMIT 0x3FFFUL #define USB_DWC2_DSTS_ERRTICERR_POS 3UL #define USB_DWC2_DSTS_ERRTICERR BIT(USB_DWC2_DSTS_ERRTICERR_POS) #define USB_DWC2_DSTS_ENUMSPD_POS 1UL diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 818bd691d58..3c538aae89d 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -62,6 +62,8 @@ enum dwc2_drv_event_type { */ #define UDC_DWC2_FIFO0_DEPTH (2 * 16U) +#define UDC_DWC2_TARGET_FRAME_INITIAL UINT16_MAX + /* Get Data FIFO access register */ #define UDC_DWC2_EP_FIFO(base, idx) ((mem_addr_t)base + 0x1000 * (idx + 1)) @@ -138,6 +140,9 @@ struct udc_dwc2_data { /* Number of OUT endpoints including control endpoint */ uint8_t outeps; uint8_t setup[8]; + /* Target frame numbers for isochronous endpoints */ + uint16_t target_frames[32]; + uint32_t target_frame_overrun_mask; }; #if defined(CONFIG_PINCTRL) @@ -220,6 +225,14 @@ static inline bool dwc2_in_buffer_dma_mode(const struct device *dev) return IS_ENABLED(CONFIG_UDC_DWC2_DMA) && priv->bufferdma; } +static inline uint16_t dwc2_get_frame_number_limit(const struct device *dev) { + struct udc_dwc2_data *const priv = udc_get_private(dev); + + return (priv->enumspd == USB_DWC2_DSTS_ENUMSPD_HS3060) ? + USB_DWC2_DSTS_SOFFN_LIMIT : + USB_DWC2_DSTS_SOFFN_LIMIT >> 3; +} + /* Get DOEPCTLn or DIEPCTLn register address */ static mem_addr_t dwc2_get_dxepctl_reg(const struct device *dev, const uint8_t ep) { @@ -381,6 +394,53 @@ static bool dwc2_ep_is_periodic(struct udc_ep_config *const cfg) } } +static uint16_t dwc2_get_ep_interval(const struct device *dev, struct udc_ep_config *const cfg) +{ + struct udc_dwc2_data *const priv = udc_get_private(dev); + + return (priv->enumspd == USB_DWC2_DSTS_ENUMSPD_HS3060) ? + (1U << (cfg->interval - 1)) : + cfg->interval; +} + +static void dwc2_ep_next_target_frame(const struct device *dev, struct udc_ep_config *const cfg) { + struct udc_dwc2_data *const priv = udc_get_private(dev); + const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr); + const uint16_t limit = dwc2_get_frame_number_limit(dev); + if (priv->target_frames[ep_bitmap_idx] == UDC_DWC2_TARGET_FRAME_INITIAL) { + return; + } + + priv->target_frames[ep_bitmap_idx] += dwc2_get_ep_interval(dev, cfg); + if (priv->target_frames[ep_bitmap_idx] > limit) { + priv->target_frames[ep_bitmap_idx] &= limit; + priv->target_frame_overrun_mask |= BIT(ep_bitmap_idx); + } else { + priv->target_frame_overrun_mask &= ~BIT(ep_bitmap_idx); + } +} + +static bool dwc2_target_frame_elapsed(const struct device *dev, struct udc_ep_config *const cfg) +{ + struct udc_dwc2_data *const priv = udc_get_private(dev); + const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr); + const uint16_t target_frame = priv->target_frames[ep_bitmap_idx]; + const uint16_t current_frame = priv->sof_num; + const bool frame_overrun = priv->target_frame_overrun_mask & BIT(ep_bitmap_idx); + const uint16_t limit = dwc2_get_frame_number_limit(dev); + + if (!frame_overrun && current_frame >= target_frame) { + return true; + } + + if (frame_overrun && current_frame >= target_frame && + ((current_frame - target_frame) < limit / 2)) { + return true; + } + + return false; +} + static bool dwc2_ep_is_iso(struct udc_ep_config *const cfg) { return (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) == USB_EP_TYPE_ISO; @@ -566,13 +626,14 @@ static int dwc2_tx_fifo_write(const struct device *dev, } if (is_iso) { - /* Queue transfer on next SOF. TODO: allow stack to explicitly - * specify on which (micro-)frame the data should be sent. - */ - if (priv->sof_num & 1) { - diepctl |= USB_DWC2_DEPCTL_SETEVENFR; - } else { - diepctl |= USB_DWC2_DEPCTL_SETODDFR; + if (dwc2_get_ep_interval(dev, cfg) == 1) { + const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr); + + if (priv->target_frames[ep_bitmap_idx] & 1) { + diepctl |= USB_DWC2_DEPCTL_SETODDFR; + } else { + diepctl |= USB_DWC2_DEPCTL_SETEVENFR; + } } } @@ -706,10 +767,14 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, } /* Set the Even/Odd (micro-)frame appropriately */ - if (priv->sof_num & 1) { - doepctl |= USB_DWC2_DEPCTL_SETEVENFR; - } else { - doepctl |= USB_DWC2_DEPCTL_SETODDFR; + if (dwc2_get_ep_interval(dev, cfg) == 1) { + const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr); + + if (priv->target_frames[ep_bitmap_idx] & 1) { + doepctl |= USB_DWC2_DEPCTL_SETODDFR; + } else { + doepctl |= USB_DWC2_DEPCTL_SETEVENFR; + } } } else { xfersize = net_buf_tailroom(buf); @@ -1363,6 +1428,7 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, static int dwc2_ep_control_enable(const struct device *dev, struct udc_ep_config *const cfg) { + struct udc_dwc2_data *const priv = udc_get_private(dev); mem_addr_t dxepctl0_reg; uint32_t dxepctl0; @@ -1416,6 +1482,12 @@ static int udc_dwc2_ep_activate(const struct device *dev, return dwc2_ep_control_enable(dev, cfg); } + if (dwc2_ep_is_iso(cfg)) { + const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr); + + priv->target_frames[ep_bitmap_idx] = UDC_DWC2_TARGET_FRAME_INITIAL; + } + if (USB_EP_DIR_IS_OUT(cfg->addr)) { /* TODO: use dwc2_get_dxepctl_reg() */ dxepctl_reg = (mem_addr_t)&base->out_ep[ep_idx].doepctl; @@ -2196,6 +2268,10 @@ static int udc_dwc2_enable(const struct device *dev) /* Disable soft disconnect */ sys_clear_bits((mem_addr_t)&base->dctl, USB_DWC2_DCTL_SFTDISCON); + + // Disable frame number thing (out of curiousity) + // sys_clear_bits((mem_addr_t)&base->dctl, USB_DWC2_DCTL_IGNRFRMNUM); + LOG_DBG("Enable device %p", base); return 0; @@ -2417,6 +2493,7 @@ static void dwc2_on_bus_reset(const struct device *dev) uint32_t doepmsk; /* Set the NAK bit for all OUT endpoints */ + sys_clear_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_NAKINTRPT); for (uint8_t i = 0U; i < priv->numdeveps; i++) { uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i); mem_addr_t doepctl_reg; @@ -2429,13 +2506,13 @@ static void dwc2_on_bus_reset(const struct device *dev) } } - doepmsk = USB_DWC2_DOEPINT_SETUP | USB_DWC2_DOEPINT_XFERCOMPL; + doepmsk = USB_DWC2_DOEPINT_SETUP | USB_DWC2_DOEPINT_XFERCOMPL /* | USB_DWC2_DOEPINT_OUTTKNEPDIS */; if (dwc2_in_buffer_dma_mode(dev)) { doepmsk |= USB_DWC2_DOEPINT_STSPHSERCVD; } sys_write32(doepmsk, (mem_addr_t)&base->doepmsk); - sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL); + sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL | USB_DWC2_DIEPINT_NAKINTRPT); /* Software has to handle RxFLvl interrupt only in Completer mode */ if (dwc2_in_completer_mode(dev)) { @@ -2558,10 +2635,61 @@ static inline void dwc2_handle_in_xfercompl(const struct device *dev, return; } + if (dwc2_ep_is_iso(ep_cfg)) { + dwc2_ep_next_target_frame(dev, ep_cfg); + } + atomic_set_bit(&priv->xfer_finished, ep_idx); k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED)); } +static inline void dwc2_handle_in_nakintrpt(const struct device *dev, + const uint8_t ep_idx) +{ + struct udc_dwc2_data *const priv = udc_get_private(dev); + struct usb_dwc2_reg *const base = dwc2_get_base(dev); + mem_addr_t diepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx | USB_EP_DIR_IN); + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep_idx | USB_EP_DIR_IN); + const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(ep_cfg->addr); + uint32_t diepctl; + + if (!dwc2_ep_is_iso(ep_cfg)) { + return; + } + + if (priv->target_frames[ep_bitmap_idx] == UDC_DWC2_TARGET_FRAME_INITIAL) { + priv->target_frames[ep_bitmap_idx] = priv->sof_num; + dwc2_ep_next_target_frame(dev, ep_cfg); + if (dwc2_get_ep_interval(dev, ep_cfg) > 1) { + diepctl = sys_read32(diepctl_reg); + if (priv->target_frames[ep_bitmap_idx] & 1) { + diepctl |= USB_DWC2_DEPCTL_SETODDFR; + } else { + diepctl |= USB_DWC2_DEPCTL_SETEVENFR; + } + + sys_write32(diepctl, diepctl_reg); + } + } + + udc_dwc2_ep_disable(dev, ep_cfg, false); + + while (dwc2_target_frame_elapsed(dev, ep_cfg)) { + struct net_buf *buf = udc_buf_get(ep_cfg); + + if (buf) { + if(udc_submit_ep_event(dev, buf, -ENODATA)) { + LOG_ERR("Failed to submit endpoint event"); + } + } + + dwc2_ep_next_target_frame(dev, ep_cfg); + priv->sof_num = usb_dwc2_get_dsts_soffn(sys_read32((mem_addr_t)&base->dsts)); + } + + dwc2_handle_xfer_next(dev, ep_cfg); +} + static inline void dwc2_handle_iepint(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); @@ -2590,6 +2718,9 @@ static inline void dwc2_handle_iepint(const struct device *dev) dwc2_handle_in_xfercompl(dev, n); } + if (status & USB_DWC2_DIEPINT_NAKINTRPT) { + dwc2_handle_in_nakintrpt(dev, n); + } } } @@ -2603,6 +2734,7 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep_idx); struct udc_dwc2_data *const priv = udc_get_private(dev); struct usb_dwc2_reg *const base = dwc2_get_base(dev); + const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(ep_cfg->addr); uint32_t bcnt; struct net_buf *buf; uint32_t doeptsiz; @@ -2610,6 +2742,13 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, doeptsiz = sys_read32((mem_addr_t)&base->out_ep[ep_idx].doeptsiz); + // HACK: since it seems like OUTTKNEPDIS doesn't work for non-control endpoints, + // we use this event instead. This is diff from the linux driver, but its a POC anyways. + if (is_iso && priv->target_frames[ep_bitmap_idx] == UDC_DWC2_TARGET_FRAME_INITIAL) { + priv->target_frames[ep_bitmap_idx] = priv->sof_num; + dwc2_ep_next_target_frame(dev, ep_cfg); + } + buf = udc_buf_peek(ep_cfg); if (!buf) { LOG_ERR("No buffer for ep 0x%02x", ep_cfg->addr); @@ -2660,6 +2799,10 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, net_buf_add(buf, bcnt); } + if (is_iso) { + dwc2_ep_next_target_frame(dev, ep_cfg); + } + if (!is_iso && bcnt && (bcnt % udc_mps_ep_size(ep_cfg)) == 0 && net_buf_tailroom(buf)) { dwc2_prep_rx(dev, buf, ep_cfg); @@ -2669,6 +2812,48 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, } } +static inline void dwc2_handle_out_token_ep_disabled(const struct device *dev, const uint8_t ep_idx) { + struct udc_dwc2_data *const priv = udc_get_private(dev); + struct usb_dwc2_reg *const base = dwc2_get_base(dev); + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep_idx); + const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(ep_cfg->addr); + + if (!USB_EP_DIR_IS_OUT(ep_cfg->addr) || !dwc2_ep_is_iso(ep_cfg)) { + return; + } + + if (priv->target_frames[ep_bitmap_idx] == UDC_DWC2_TARGET_FRAME_INITIAL) { + priv->target_frames[ep_bitmap_idx] = priv->sof_num; + LOG_ERR("RED ALERT STARTING OUT"); + dwc2_ep_next_target_frame(dev, ep_cfg); + if (dwc2_get_ep_interval(dev, ep_cfg) > 1) { + mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx); + uint32_t doepctl = sys_read32(doepctl_reg); + if (priv->target_frames[ep_bitmap_idx] & 1) { + doepctl |= USB_DWC2_DEPCTL_SETODDFR; + } else { + doepctl |= USB_DWC2_DEPCTL_SETEVENFR; + } + sys_write32(doepctl, doepctl_reg); + } + } + + while (dwc2_target_frame_elapsed(dev, ep_cfg)) { + struct net_buf *buf = udc_buf_get(ep_cfg); + + if (buf) { + if(udc_submit_ep_event(dev, buf, -ENODATA)) { + LOG_ERR("Failed to submit endpoint event"); + } + } + + dwc2_ep_next_target_frame(dev, ep_cfg); + priv->sof_num = usb_dwc2_get_dsts_soffn(sys_read32((mem_addr_t)&base->dsts)); + } + + dwc2_handle_xfer_next(dev, ep_cfg); +} + static inline void dwc2_handle_oepint(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); @@ -2734,6 +2919,11 @@ static inline void dwc2_handle_oepint(const struct device *dev) if (status & USB_DWC2_DOEPINT_XFERCOMPL) { dwc2_handle_out_xfercompl(dev, n); } + + // Why does this work for linux gadget when docs suggest its only for control EP + if (status & USB_DWC2_DOEPINT_OUTTKNEPDIS) { + dwc2_handle_out_token_ep_disabled(dev, n); + } } /* Clear OEPINT interrupt */ @@ -2768,25 +2958,36 @@ static void dwc2_handle_incompisoin(const struct device *dev) diepctl = sys_read32(diepctl_reg); - /* Check if endpoint didn't receive ISO OUT data */ + /* 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); + if (!dwc2_target_frame_elapsed(dev, cfg)) { + continue; + } - buf = udc_buf_get(cfg); - if (buf) { - /* Data is no longer relevant */ - udc_submit_ep_event(dev, buf, 0); + LOG_ERR("INCOMPISOIN: EP%02x", cfg->addr); + udc_dwc2_ep_disable(dev, cfg, false); - /* Try to queue next packet before SOF */ - dwc2_handle_xfer_next(dev, cfg); - } + do { + struct net_buf *buf = udc_buf_get(cfg); + + if (buf) { + if(udc_submit_ep_event(dev, buf, -ENODATA)) { + LOG_ERR("Failed to submit endpoint event"); + } + } + + dwc2_ep_next_target_frame(dev, cfg); + priv->sof_num = usb_dwc2_get_dsts_soffn(sys_read32((mem_addr_t)&base->dsts)); + } while (dwc2_target_frame_elapsed(dev, cfg)); + + /* Try to queue next packet before SOF */ + dwc2_handle_xfer_next(dev, cfg); } } } @@ -2832,12 +3033,25 @@ static void dwc2_handle_incompisoout(const struct device *dev) __ASSERT_NO_MSG(cfg && cfg->stat.enabled && dwc2_ep_is_iso(cfg)); + if (!dwc2_target_frame_elapsed(dev, cfg)) { + continue; + } + + LOG_ERR("INCOMPISOOUT: EP%02x", cfg->addr); udc_dwc2_ep_disable(dev, cfg, false); - buf = udc_buf_get(cfg); - if (buf) { - udc_submit_ep_event(dev, buf, 0); - } + do { + struct net_buf *buf = udc_buf_get(cfg); + + if (buf) { + if(udc_submit_ep_event(dev, buf, -ENODATA)) { + LOG_ERR("Failed to submit endpoint event"); + } + } + + dwc2_ep_next_target_frame(dev, cfg); + priv->sof_num = usb_dwc2_get_dsts_soffn(sys_read32((mem_addr_t)&base->dsts)); + } while (dwc2_target_frame_elapsed(dev, cfg)); } } } diff --git a/dts/bindings/usb/uac2/zephyr,uac2-audio-streaming.yaml b/dts/bindings/usb/uac2/zephyr,uac2-audio-streaming.yaml index 6e661d1c73d..45b5f1747e4 100644 --- a/dts/bindings/usb/uac2/zephyr,uac2-audio-streaming.yaml +++ b/dts/bindings/usb/uac2/zephyr,uac2-audio-streaming.yaml @@ -78,3 +78,9 @@ properties: type: int description: | Number of effectively used bits in audio subslot. + + polling-period-us: + type: int + description: | + Input or output type reports polling period in microseconds. For USB full + speed this could be clamped to 1ms or 255ms depending on the value. diff --git a/include/zephyr/usb/usb_ch9.h b/include/zephyr/usb/usb_ch9.h index b78be4397fa..5d69b26252e 100644 --- a/include/zephyr/usb/usb_ch9.h +++ b/include/zephyr/usb/usb_ch9.h @@ -321,6 +321,9 @@ struct usb_association_descriptor { /** Get endpoint address from endpoint index and direction */ #define USB_EP_GET_ADDR(idx, dir) ((idx) | ((dir) & USB_EP_DIR_MASK)) +/** Get endpoint bitmap index mapping */ +#define USB_EP_GET_BITMAP_IDX(ep) (USB_EP_GET_IDX(ep) | (((ep) & USB_EP_DIR_MASK) >> 3U)) + /** True if the endpoint is an IN endpoint */ #define USB_EP_DIR_IS_IN(ep) (USB_EP_GET_DIR(ep) == USB_EP_DIR_IN) diff --git a/subsys/usb/device_next/class/usbd_uac2_macros.h b/subsys/usb/device_next/class/usbd_uac2_macros.h index a31278d9cf8..b07179d7f78 100644 --- a/subsys/usb/device_next/class/usbd_uac2_macros.h +++ b/subsys/usb/device_next/class/usbd_uac2_macros.h @@ -707,13 +707,21 @@ #define AS_BYTES_PER_SAMPLE(node) \ DT_PROP(node, subslot_size) +#define AS_FS_DATA_EP_BINTERVAL(node) \ + USB_FS_ISO_EP_INTERVAL(DT_PROP_OR(node, polling_period_us, 1000)) + +#define AS_HS_DATA_EP_BINTERVAL(node) \ + USB_HS_ISO_EP_INTERVAL(DT_PROP_OR(node, polling_period_us, 125)) + /* Asynchronous endpoints needs space for 1 extra sample */ #define AS_SAMPLES_PER_FRAME(node) \ - ((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 1000) / 1000) + \ + ((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 1000) / 1000) * \ + AS_FS_DATA_EP_BINTERVAL(node) + \ UTIL_NOT(AS_IS_SOF_SYNCHRONIZED(node))) #define AS_SAMPLES_PER_MICROFRAME(node) \ - ((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 8000) / 8000) + \ + (((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 8000) / 8000) << \ + (AS_HS_DATA_EP_BINTERVAL(node) - 1)) + \ UTIL_NOT(AS_IS_SOF_SYNCHRONIZED(node))) #define AS_DATA_EP_SYNC_TYPE(node) \ @@ -745,7 +753,7 @@ AS_DATA_EP_ADDR(node), /* bEndpointAddress */ \ AS_DATA_EP_ATTR(node), /* bmAttributes */ \ U16_LE(AS_FS_DATA_EP_MAX_PACKET_SIZE(node)), /* wMaxPacketSize */ \ - 0x01, /* bInterval */ + AS_FS_DATA_EP_BINTERVAL(node), /* bInterval */ #define AS_ISOCHRONOUS_DATA_ENDPOINT_FS_DESCRIPTORS_ARRAYS(node) \ static uint8_t DESCRIPTOR_NAME(fs_std_data_ep, node)[] = { \ @@ -758,7 +766,7 @@ AS_DATA_EP_ADDR(node), /* bEndpointAddress */ \ AS_DATA_EP_ATTR(node), /* bmAttributes */ \ U16_LE(AS_HS_DATA_EP_MAX_PACKET_SIZE(node)), /* wMaxPacketSize */ \ - 0x01, /* bInterval */ + AS_HS_DATA_EP_BINTERVAL(node), /* bInterval */ #define AS_ISOCHRONOUS_DATA_ENDPOINT_HS_DESCRIPTORS_ARRAYS(node) \ static uint8_t DESCRIPTOR_NAME(hs_std_data_ep, node)[] = { \