Skip to content
Merged
Show file tree
Hide file tree
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
12 changes: 5 additions & 7 deletions drivers/usb/udc/Kconfig.dwc2
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,22 @@ config UDC_DWC2
help
DWC2 USB device controller driver.

if UDC_DWC2

config UDC_DWC2_DMA
bool "DWC2 USB DMA support"
default y
depends on UDC_DWC2
help
Enable Buffer DMA if DWC2 USB controller supports Internal DMA.

config UDC_DWC2_HIBERNATION
bool "DWC2 USB Hibernation support"
default y
depends on UDC_DWC2
help
Enable Hibernation if DWC2 USB controller supports hibernation.

config UDC_DWC2_PTI
bool "DWC2 USB Periodic Transfer Interrupt"
depends on UDC_DWC2
help
Ignore frame number when scheduling isochronous endpoints. Use this
when isochronous transfers should finish only after receiving token
Expand All @@ -38,24 +37,23 @@ config UDC_DWC2_PTI

config UDC_DWC2_STACK_SIZE
int "UDC DWC2 driver internal thread stack size"
depends on UDC_DWC2
default 512
help
DWC2 driver internal thread stack size.

config UDC_DWC2_THREAD_PRIORITY
int "UDC DWC2 driver thread priority"
depends on UDC_DWC2
default 8
help
DWC2 driver thread priority.

config UDC_DWC2_USBHS_VBUS_READY_TIMEOUT
int "UDC DWC2 USBHS VBUS ready event timeout in ms"
depends on UDC_DWC2
depends on NRFS_HAS_VBUS_DETECTOR_SERVICE
depends on SOC_SERIES_NRF54HX || SOC_SERIES_NRF54LX || SOC_SERIES_NRF92X
default 0
help
UDC DWC2 USBHS VBUS ready event timeout. If the VBUS is not ready
and the Nordic USBHS controller is used, the udc_enable() is
blocked for this amount of time. Set it to zero to wait forever.

endif # UDC_DWC2
27 changes: 23 additions & 4 deletions drivers/usb/udc/udc_dwc2.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,11 @@
} 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.
*/
Expand Down Expand Up @@ -1581,10 +1586,12 @@
*diepctl &= ~USB_DWC2_DEPCTL_TXFNUM_MASK;

if (priv->dynfifosizing) {
if (priv->txf_set & ~BIT_MASK(ep_idx)) {
LOG_WRN("Some of the FIFOs higher than %u are set, %lx",
ep_idx, priv->txf_set & ~BIT_MASK(ep_idx));
uint16_t higher_mask = ~BIT_MASK(ep_idx + 1);

if (priv->txf_set & higher_mask) {
LOG_WRN("Some of the FIFOs higher than %u are set, %x",
ep_idx, priv->txf_set & higher_mask);
return 0;

Check notice on line 1594 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:1594 - LOG_WRN("Some of the FIFOs higher than %u are set, %x", - ep_idx, priv->txf_set & higher_mask); + LOG_WRN("Some of the FIFOs higher than %u are set, %x", ep_idx, + priv->txf_set & higher_mask);
}

dwc2_set_txf(dev, ep_idx - 1, 0, 0);
Expand Down Expand Up @@ -1723,7 +1730,19 @@
mem_addr_t dxepctl_reg;
uint32_t dxepctl;

dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
if (priv->hibernated) {
/* If usbd_disable() is called when core is hibernated, modify
* backup registers instead of real ones.
*/
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
dxepctl_reg = (mem_addr_t)&priv->backup.doepctl[ep_idx];
} else {
dxepctl_reg = (mem_addr_t)&priv->backup.diepctl[ep_idx];
}
} else {
dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
}

dxepctl = sys_read32(dxepctl_reg);

if (dxepctl & USB_DWC2_DEPCTL_USBACTEP) {
Expand Down
4 changes: 4 additions & 0 deletions drivers/usb/udc/udc_dwc2_vendor_quirks.h
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ static inline int usbhs_enable_core(const struct device *dev)
k_timeout_t timeout = K_FOREVER;
int err;

if (CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT) {
timeout = K_MSEC(CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT);
}

if (!k_event_wait(&usbhs_events, USBHS_VBUS_READY, false, K_NO_WAIT)) {
LOG_WRN("VBUS is not ready, block udc_enable()");
if (!k_event_wait(&usbhs_events, USBHS_VBUS_READY, false, timeout)) {
Expand Down
32 changes: 32 additions & 0 deletions include/zephyr/usb/class/usbd_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,38 @@ int hid_device_register(const struct device *dev,
int hid_device_submit_report(const struct device *dev,
const uint16_t size, const uint8_t *const report);

/**
* @brief Set input report polling period
*
* Similar to devicetree property in-polling-period-us, but it allows setting
* different polling periods at runtime.
*
* @kconfig_dep{CONFIG_USBD_HID_SET_POLLING_PERIOD}
*
* @param[in] dev Pointer to HID device
* @param[in] period_us Polling period in microseconds
*
* @return 0 on success, negative errno code on failure.
* @retval -ENOTSUP If API is not enabled.
*/
int hid_device_set_in_polling(const struct device *dev, const unsigned int period_us);

/**
* @brief Set output report polling period
*
* Similar to devicetree property out-polling-period-us, but it allows setting
* different polling periods at runtime.
*
* @kconfig_dep{CONFIG_USBD_HID_SET_POLLING_PERIOD}
*
* @param[in] dev Pointer to HID device
* @param[in] period_us Polling period in microseconds
*
* @return 0 on success, negative errno code on failure.
* @retval -ENOTSUP If API is not enabled.
*/
int hid_device_set_out_polling(const struct device *dev, const unsigned int period_us);

/**
* @}
*/
Expand Down
2 changes: 2 additions & 0 deletions samples/subsys/usb/hid-keyboard/sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ tests:
sample.usbd.hid-keyboard.large-out-report:
extra_args:
- EXTRA_DTC_OVERLAY_FILE="large_out_report.overlay"
extra_configs:
- CONFIG_USBD_HID_SET_POLLING_PERIOD=y
12 changes: 12 additions & 0 deletions samples/subsys/usb/hid-keyboard/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,18 @@ int main(void)
return ret;
}

if (IS_ENABLED(CONFIG_USBD_HID_SET_POLLING_PERIOD)) {
ret = hid_device_set_in_polling(hid_dev, 1000);
if (ret) {
LOG_WRN("Failed to set IN report polling period, %d", ret);
}

ret = hid_device_set_out_polling(hid_dev, 1000);
if (ret != 0 && ret != -ENOTSUP) {
LOG_WRN("Failed to set OUT report polling period, %d", ret);
}
}

sample_usbd = sample_usbd_init_device(msg_cb);
if (sample_usbd == NULL) {
LOG_ERR("Failed to initialize USB device");
Expand Down
5 changes: 5 additions & 0 deletions subsys/usb/device_next/class/Kconfig.hid
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ config USBD_HID_INIT_PRIORITY
help
HID device initialization priority

config USBD_HID_SET_POLLING_PERIOD
bool "Allow to set polling period at runtime"
help
Allow to set input or output report polling period at runtime.

module = USBD_HID
module-str = usbd hid
source "subsys/logging/Kconfig.template.log_config"
Expand Down
68 changes: 67 additions & 1 deletion subsys/usb/device_next/class/usbd_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
};

enum {
HID_DEV_CLASS_INITIALIZED,
HID_DEV_CLASS_ENABLED,
};

Expand Down Expand Up @@ -503,7 +504,14 @@
const struct device *dev = usbd_class_get_private(c_data);
const struct hid_device_config *dcfg = dev->config;
struct usbd_hid_descriptor *const desc = dcfg->desc;
struct hid_device_data *const ddata = dev->data;

if (ddata->ops == NULL || ddata->rdesc == NULL || !ddata->rsize) {
LOG_ERR("HID device does not seem to be registered");

Check notice on line 510 in subsys/usb/device_next/class/usbd_hid.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

subsys/usb/device_next/class/usbd_hid.c:510 - if (ddata->ops == NULL || ddata->rdesc == NULL || !ddata->rsize) { + if (ddata->ops == NULL || ddata->rdesc == NULL || !ddata->rsize) {
return -EINVAL;
}

atomic_set_bit(&ddata->state, HID_DEV_CLASS_INITIALIZED);
LOG_DBG("HID class %s init", c_data->name);

if (dcfg->if_desc_data != NULL && desc->if0.iInterface == 0) {
Expand All @@ -519,6 +527,10 @@

static void usbd_hid_shutdown(struct usbd_class_data *const c_data)
{
const struct device *dev = usbd_class_get_private(c_data);
struct hid_device_data *const ddata = dev->data;

atomic_clear_bit(&ddata->state, HID_DEV_CLASS_INITIALIZED);
LOG_DBG("HID class %s shutdown", c_data->name);
}

Expand Down Expand Up @@ -620,6 +632,56 @@
return 0;
}

static inline int hid_dev_set_out_polling(const struct device *dev,
const unsigned int period_us)
{

Check notice on line 637 in subsys/usb/device_next/class/usbd_hid.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

subsys/usb/device_next/class/usbd_hid.c:637 -static inline int hid_dev_set_out_polling(const struct device *dev, - const unsigned int period_us) +static inline int hid_dev_set_out_polling(const struct device *dev, const unsigned int period_us)
const struct hid_device_config *const dcfg = dev->config;
struct hid_device_data *const ddata = dev->data;
struct usbd_hid_descriptor *const desc = dcfg->desc;

if (atomic_test_bit(&ddata->state, HID_DEV_CLASS_INITIALIZED)) {
return -EBUSY;
}

if (USBD_SUPPORTS_HIGH_SPEED) {
if (desc->hs_out_ep.bLength == 0) {
/* This device does not have output reports. */
return -ENOTSUP;
}

desc->hs_out_ep.bInterval = USB_HS_INT_EP_INTERVAL(period_us);
}

if (desc->out_ep.bLength == 0) {
/* This device does not have output reports. */
return -ENOTSUP;
}

desc->out_ep.bInterval = USB_FS_INT_EP_INTERVAL(period_us);

return 0;
}

static inline int hid_dev_set_in_polling(const struct device *dev,
const unsigned int period_us)
{

Check notice on line 667 in subsys/usb/device_next/class/usbd_hid.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

subsys/usb/device_next/class/usbd_hid.c:667 -static inline int hid_dev_set_in_polling(const struct device *dev, - const unsigned int period_us) +static inline int hid_dev_set_in_polling(const struct device *dev, const unsigned int period_us)
const struct hid_device_config *const dcfg = dev->config;
struct hid_device_data *const ddata = dev->data;
struct usbd_hid_descriptor *const desc = dcfg->desc;

if (atomic_test_bit(&ddata->state, HID_DEV_CLASS_INITIALIZED)) {
return -EBUSY;
}

if (USBD_SUPPORTS_HIGH_SPEED) {
desc->hs_in_ep.bInterval = USB_HS_INT_EP_INTERVAL(period_us);
}

desc->in_ep.bInterval = USB_FS_INT_EP_INTERVAL(period_us);

return 0;
}

static int hid_dev_register(const struct device *dev,
const uint8_t *const rdesc, const uint16_t rsize,
const struct hid_device_ops *const ops)
Expand All @@ -628,7 +690,7 @@
struct hid_device_data *const ddata = dev->data;
struct usbd_hid_descriptor *const desc = dcfg->desc;

if (atomic_test_bit(&ddata->state, HID_DEV_CLASS_ENABLED)) {
if (atomic_test_bit(&ddata->state, HID_DEV_CLASS_INITIALIZED)) {
return -EALREADY;
}

Expand Down Expand Up @@ -694,6 +756,10 @@
static const struct hid_device_driver_api hid_device_api = {
.submit_report = hid_dev_submit_report,
.dev_register = hid_dev_register,
#if CONFIG_USBD_HID_SET_POLLING_PERIOD
.set_out_polling = hid_dev_set_out_polling,
.set_in_polling = hid_dev_set_in_polling,
#endif
};

#include "usbd_hid_macros.h"
Expand Down
22 changes: 22 additions & 0 deletions subsys/usb/device_next/class/usbd_hid_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ int hid_device_register(const struct device *dev,
return api->dev_register(dev, rdesc, rsize, ops);
}

int hid_device_set_in_polling(const struct device *dev, const unsigned int period_us)
{
const struct hid_device_driver_api *const api = dev->api;

if (IS_ENABLED(CONFIG_USBD_HID_SET_POLLING_PERIOD)) {
return api->set_in_polling(dev, period_us);
}

return -ENOTSUP;
}

int hid_device_set_out_polling(const struct device *dev, const unsigned int period_us)
{
const struct hid_device_driver_api *const api = dev->api;

if (IS_ENABLED(CONFIG_USBD_HID_SET_POLLING_PERIOD)) {
return api->set_out_polling(dev, period_us);
}

return -ENOTSUP;
}

/* Legacy HID API wrapper below */

struct legacy_wrapper {
Expand Down
2 changes: 2 additions & 0 deletions subsys/usb/device_next/class/usbd_hid_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ struct hid_device_driver_api {
int (*dev_register)(const struct device *dev,
const uint8_t *const rdesc, const uint16_t rsize,
const struct hid_device_ops *const ops);
int (*set_in_polling)(const struct device *dev, const unsigned int period_us);
int (*set_out_polling)(const struct device *dev, const unsigned int period_us);
};
Loading