diff --git a/cpu/nrf52/periph/usbdev.c b/cpu/nrf52/periph/usbdev.c index c639e2f50f..5e90af67f2 100644 --- a/cpu/nrf52/periph/usbdev.c +++ b/cpu/nrf52/periph/usbdev.c @@ -45,7 +45,9 @@ static int _get(usbdev_t *usbdev, usbopt_t opt, void *value, size_t max_len); static int _set(usbdev_t *usbdev, usbopt_t opt, const void *value, size_t value_len); static usbdev_ep_t *_new_ep(usbdev_t *dev, usb_ep_type_t type, usb_ep_dir_t dir, size_t len); static void _esr(usbdev_t *usbdev); +static void _ep0_stall(usbdev_t *usbdev); static void _ep_init(usbdev_ep_t *ep); +static void _ep_stall(usbdev_ep_t *ep, bool enable); static int _ep_get(usbdev_ep_t *ep, usbopt_ep_t opt, void *value, size_t max_len); static int _ep_set(usbdev_ep_t *ep, usbopt_ep_t opt, const void *value, size_t value_len); static int _ep_xmit(usbdev_ep_t *ep, uint8_t *buf, size_t len); @@ -57,7 +59,9 @@ static const usbdev_driver_t _driver = { .get = _get, .set = _set, .esr = _esr, + .ep0_stall = _ep0_stall, .ep_init = _ep_init, + .ep_stall = _ep_stall, .ep_get = _ep_get, .ep_set = _ep_set, .ep_esr = _ep_esr, @@ -177,6 +181,8 @@ static void _ep_disable(usbdev_ep_t *ep) static void _ep_set_stall(usbdev_ep_t *ep, usbopt_enable_t enable) { + assert(ep->num != 0); + /* TODO: validate size */ nrfusb_t *usbdev = (nrfusb_t *)ep->dev; uint32_t val = (ep->num & USBD_EPSTALL_EP_Msk) | @@ -186,6 +192,12 @@ static void _ep_set_stall(usbdev_ep_t *ep, usbopt_enable_t enable) usbdev->device->EPSTALL = val; } +static void _ep_stall(usbdev_ep_t *ep, bool enable) +{ + /* quick wrapper */ + _ep_set_stall(ep, (usbopt_enable_t)enable); +} + static usbopt_enable_t _ep_get_stall(usbdev_ep_t *ep) { /* TODO: validate size */ @@ -390,6 +402,13 @@ static void _ep_disable_irq(usbdev_ep_t *ep) } } +static void _ep0_stall(usbdev_t *dev) +{ + nrfusb_t *usbdev = (nrfusb_t*)dev; + /* Stalls both OUT and IN */ + usbdev->device->TASKS_EP0STALL = 1; +} + static void _ep_init(usbdev_ep_t *ep) { nrfusb_t *usbdev = (nrfusb_t*)ep->dev; diff --git a/cpu/sam0_common/periph/usbdev.c b/cpu/sam0_common/periph/usbdev.c index 1e814b90b8..aa21ad4986 100644 --- a/cpu/sam0_common/periph/usbdev.c +++ b/cpu/sam0_common/periph/usbdev.c @@ -584,12 +584,23 @@ static inline void _enable_ep_stall_in(UsbDeviceEndpoint *ep_reg) static inline void _disable_ep_stall_out(UsbDeviceEndpoint *ep_reg) { - ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0; + ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0 | + USB_DEVICE_EPSTATUSCLR_DTGLOUT; } static inline void _disable_ep_stall_in(UsbDeviceEndpoint *ep_reg) { - ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1; + ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1 | + USB_DEVICE_EPSTATUSCLR_DTGLIN; +} + +static void _usbdev_ep0_stall(usbdev_t *usbdev) +{ + sam0_common_usb_t *sam_usbdev = (sam0_common_usb_t *)usbdev; + UsbDeviceEndpoint *ep0_reg = &sam_usbdev->config->device->DeviceEndpoint[0]; + + ep0_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1 | + USB_DEVICE_EPSTATUSSET_STALLRQ0; } static void _ep_set_stall(usbdev_ep_t *ep, usbopt_enable_t enable) @@ -625,6 +636,11 @@ usbopt_enable_t _ep_get_stall(usbdev_ep_t *ep) } +static void _usbdev_ep_stall(usbdev_ep_t *ep, bool enable) +{ + _ep_set_stall(ep, enable); +} + static void _usbdev_ep_init(usbdev_ep_t *ep) { _enable_ep_irq(ep); @@ -773,7 +789,9 @@ const usbdev_driver_t driver = { .get = _usbdev_get, .set = _usbdev_set, .esr = _usbdev_esr, + .ep0_stall = _usbdev_ep0_stall, .ep_init = _usbdev_ep_init, + .ep_stall = _usbdev_ep_stall, .ep_get = _usbdev_ep_get, .ep_set = _usbdev_ep_set, .ep_esr = _usbdev_ep_esr, diff --git a/cpu/stm32/periph/usbdev_fs.c b/cpu/stm32/periph/usbdev_fs.c index 9d6478d4d3..5f94d520a1 100644 --- a/cpu/stm32/periph/usbdev_fs.c +++ b/cpu/stm32/periph/usbdev_fs.c @@ -665,6 +665,18 @@ static void _ep_set_stall(usbdev_ep_t *ep, usbopt_enable_t enable) EP_REG(ep->num) = reg; } +static void _usbdev_ep0_stall(usbdev_t *usbdev) +{ + (void)usbdev; + _ep_set_stall(&_ep_in[0], true); + _ep_set_stall(&_ep_out[0], true); +} + +static void _usbdev_ep_stall(usbdev_ep_t *ep, bool enable) +{ + _ep_set_stall(ep, enable); +} + static int _usbdev_ep_set(usbdev_ep_t *ep, usbopt_ep_t opt, const void *value, size_t value_len) { @@ -773,7 +785,9 @@ const usbdev_driver_t driver = { .get = _usbdev_get, .set = _usbdev_set, .esr = _usbdev_esr, + .ep0_stall = _usbdev_ep0_stall, .ep_init = _usbdev_ep_init, + .ep_stall = _usbdev_ep_stall, .ep_get = _usbdev_ep_get, .ep_set = _usbdev_ep_set, .ep_esr = _usbdev_ep_esr, diff --git a/drivers/include/periph/usbdev.h b/drivers/include/periph/usbdev.h index 0d947ea9f2..b10addf20b 100644 --- a/drivers/include/periph/usbdev.h +++ b/drivers/include/periph/usbdev.h @@ -75,6 +75,7 @@ #ifndef PERIPH_USBDEV_H #define PERIPH_USBDEV_H +#include #include #include @@ -330,6 +331,15 @@ typedef struct usbdev_driver { */ void (*esr)(usbdev_t *dev); + /** + * @brief Stall both OUT and IN packets on endpoint 0 until a setup packet + * is received. + * + * @note The stall condition should be cleared automatically either by + * hardware or by the usbdev implementation after receiving a setup packet. + */ + void (*ep0_stall)(usbdev_t *usbdev); + /** * @brief Initialize the USB endpoint * @@ -340,6 +350,22 @@ typedef struct usbdev_driver { */ void (*ep_init)(usbdev_ep_t *ep); + /** + * @brief Enable or disable the stall condition on the USB endpoint + * + * After clearing the stall condition on the endpoint, the usb peripheral + * must reinitialize the data toggle to DATA0. + * + * @note For enabling stall on endpoint 0 @ref usbdev_driver_t::ep0_stall + * must be used. + * + * @pre (ep->num != 0) + * + * @param[in] ep USB endpoint descriptor + * @param[in] enable True to set stall, false to disable stall + */ + void (*ep_stall)(usbdev_ep_t *ep, bool enable); + /** * @brief Get an option value from a given usb device endpoint * @@ -509,6 +535,25 @@ static inline void usbdev_esr(usbdev_t *dev) dev->driver->esr(dev); } +/** + * @brief Stall both OUT and IN packets on endpoint 0 until a setup packet + * is received. + * + * @see @ref usbdev_driver_t::ep0_stall + * + * @note The stall condition is automatically cleared after receiving a + * setup packet. + * + * @pre `(dev != NULL)` + * + * @param[in] dev USB device descriptor + */ +static inline void usbdev_ep0_stall(usbdev_t *dev) +{ + assert(dev); + dev->driver->ep0_stall(dev); +} + /** * @brief Initialize the USB endpoint * @@ -526,6 +571,26 @@ static inline void usbdev_ep_init(usbdev_ep_t *ep) ep->dev->driver->ep_init(ep); } +/** + * @brief Enable or disable the stall condition on the USB endpoint + * + * @note For enabling stall on endpoint 0 @ref usbdev_driver_t::ep0_stall + * must be used. + * + * @see @ref usbdev_driver_t::ep_stall + * + * @pre (ep->num != 0) + * + * @param[in] ep USB endpoint descriptor + * @param[in] enable True to set stall, false to disable stall + */ +static inline void usbdev_ep_stall(usbdev_ep_t *ep, bool enable) +{ + assert(ep); + assert(ep->dev); + ep->dev->driver->ep_stall(ep, enable); +} + /** * @brief Get an option value from a given usb device endpoint * diff --git a/drivers/usbdev_synopsys_dwc2/usbdev_synopsys_dwc2.c b/drivers/usbdev_synopsys_dwc2/usbdev_synopsys_dwc2.c index 0d1140f3ee..faa53e8b48 100644 --- a/drivers/usbdev_synopsys_dwc2/usbdev_synopsys_dwc2.c +++ b/drivers/usbdev_synopsys_dwc2/usbdev_synopsys_dwc2.c @@ -330,9 +330,10 @@ static uint32_t _ep0_size(size_t size) } /** - * @brief Disables an IN type endpoint + * @brief Disables transfers on an IN type endpoint. * - * Endpoint is only deactivated if it was activated + * Endpoint is only deactivated if it was activated. + * The endpoint will still respond to traffic, but any transfers will be aborted */ static void _ep_in_disable(const dwc2_usb_otg_fshs_config_t *conf, size_t num) { @@ -352,9 +353,10 @@ static void _ep_in_disable(const dwc2_usb_otg_fshs_config_t *conf, size_t num) } /** - * @brief Disables an OUT type endpoint + * @brief Disables transfers on an OUT type endpoint. * * Endpoint is only deactivated if it was activated + * The endpoint will still respond to traffic, but any transfers will be aborted */ static void _ep_out_disable(const dwc2_usb_otg_fshs_config_t *conf, size_t num) { @@ -1148,25 +1150,58 @@ static int _usbdev_ep_get(usbdev_ep_t *ep, usbopt_ep_t opt, return res; } +static void _usbdev_ep0_stall(usbdev_t *usbdev) +{ + dwc2_usb_otg_fshs_t *st_usbdev = (dwc2_usb_otg_fshs_t *)usbdev; + const dwc2_usb_otg_fshs_config_t *conf = st_usbdev->config; + /* Stall both directions, cleared automatically on SETUP received */ + _in_regs(conf, 0)->DIEPCTL |= USB_OTG_DIEPCTL_STALL; + _out_regs(conf, 0)->DOEPCTL |= USB_OTG_DOEPCTL_STALL; +} + static void _ep_set_stall(usbdev_ep_t *ep, bool enable) { + (void)enable; + + assert(ep->num != 0); dwc2_usb_otg_fshs_t *usbdev = (dwc2_usb_otg_fshs_t *)ep->dev; const dwc2_usb_otg_fshs_config_t *conf = usbdev->config; - (void)enable; - - if (ep->dir == USB_EP_DIR_IN) { - /* Disable first */ - _ep_in_disable(conf, ep->num); - _in_regs(conf, ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_STALL; + if (enable) { + if (ep->dir == USB_EP_DIR_IN) { + /* Disable first */ + _ep_in_disable(conf, ep->num); + _in_regs(conf, ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_STALL; + } + else { + /* Disable first */ + _ep_out_disable(conf, ep->num); + _out_regs(conf, ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_STALL; + } } else { - /* Disable first */ - _ep_out_disable(conf, ep->num); - _out_regs(conf, ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_STALL; + if (ep->dir == USB_EP_DIR_IN) { + /* Clear stall and set to DATA0 */ + uint32_t diepctl = _in_regs(conf, ep->num)->DIEPCTL; + diepctl &= ~(USB_OTG_DIEPCTL_STALL); + diepctl |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM; + _in_regs(conf, ep->num)->DIEPCTL = diepctl; + } + else { + /* Clear stall and set to DATA0 */ + uint32_t doepctl = _out_regs(conf, ep->num)->DOEPCTL; + doepctl &= ~(USB_OTG_DIEPCTL_STALL); + doepctl |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM; + _out_regs(conf, ep->num)->DOEPCTL = doepctl; + } } } +static void _usbdev_ep_stall(usbdev_ep_t *ep, bool enable) +{ + _ep_set_stall(ep, enable); +} + static int _usbdev_ep_set(usbdev_ep_t *ep, usbopt_ep_t opt, const void *value, size_t value_len) { @@ -1492,7 +1527,9 @@ const usbdev_driver_t driver = { .get = _usbdev_get, .set = _usbdev_set, .esr = _usbdev_esr, + .ep0_stall = _usbdev_ep0_stall, .ep_init = _usbdev_ep_init, + .ep_stall = _usbdev_ep_stall, .ep_get = _usbdev_ep_get, .ep_set = _usbdev_ep_set, .ep_esr = _usbdev_ep_esr, diff --git a/sys/usb/usbus/usbus_control.c b/sys/usb/usbus/usbus_control.c index d86f9fae44..96290f12d1 100644 --- a/sys/usb/usbus/usbus_control.c +++ b/sys/usb/usbus/usbus_control.c @@ -277,10 +277,8 @@ static void _recv_setup(usbus_t *usbus, usbus_control_handler_t *handler) } } if (res < 0) { - /* Signal stall to indicate unsupported (USB 2.0 spec 9.6.2 */ - static const usbopt_enable_t enable = USBOPT_ENABLE; - usbdev_ep_set(handler->in, USBOPT_EP_STALL, &enable, - sizeof(usbopt_enable_t)); + /* Signal stall to indicate unsupported (USB 2.0 spec 9.6.2) */ + usbdev_ep0_stall(usbus->dev); handler->control_request_state = USBUS_CONTROL_REQUEST_STATE_READY; } else if (res) {