diff --git a/include/zephyr/usb/class/usbd_hid.h b/include/zephyr/usb/class/usbd_hid.h index 5539369d5b057..79b52dd8fd9d3 100644 --- a/include/zephyr/usb/class/usbd_hid.h +++ b/include/zephyr/usb/class/usbd_hid.h @@ -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); + /** * @} */ diff --git a/samples/subsys/usb/hid-keyboard/sample.yaml b/samples/subsys/usb/hid-keyboard/sample.yaml index f53ae514f97c6..2998f84807d2e 100644 --- a/samples/subsys/usb/hid-keyboard/sample.yaml +++ b/samples/subsys/usb/hid-keyboard/sample.yaml @@ -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 diff --git a/samples/subsys/usb/hid-keyboard/src/main.c b/samples/subsys/usb/hid-keyboard/src/main.c index 45580c95a845f..39ce8836ff6a9 100644 --- a/samples/subsys/usb/hid-keyboard/src/main.c +++ b/samples/subsys/usb/hid-keyboard/src/main.c @@ -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"); diff --git a/subsys/usb/device_next/class/Kconfig.hid b/subsys/usb/device_next/class/Kconfig.hid index 8e3133a1dde89..a6f2980ec3abb 100644 --- a/subsys/usb/device_next/class/Kconfig.hid +++ b/subsys/usb/device_next/class/Kconfig.hid @@ -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" diff --git a/subsys/usb/device_next/class/usbd_hid.c b/subsys/usb/device_next/class/usbd_hid.c index d978aea0d27cd..2ea3a08db763a 100644 --- a/subsys/usb/device_next/class/usbd_hid.c +++ b/subsys/usb/device_next/class/usbd_hid.c @@ -57,6 +57,7 @@ struct usbd_hid_descriptor { }; enum { + HID_DEV_CLASS_INITIALIZED, HID_DEV_CLASS_ENABLED, }; @@ -503,7 +504,14 @@ static int usbd_hid_init(struct usbd_class_data *const c_data) 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"); + 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) { @@ -519,6 +527,10 @@ static int usbd_hid_init(struct usbd_class_data *const c_data) 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); } @@ -620,6 +632,56 @@ static int hid_dev_submit_report(const struct device *dev, return 0; } +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) +{ + 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) @@ -628,7 +690,7 @@ static int hid_dev_register(const struct device *dev, 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; } @@ -694,6 +756,10 @@ struct usbd_class_api usbd_hid_api = { 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" diff --git a/subsys/usb/device_next/class/usbd_hid_api.c b/subsys/usb/device_next/class/usbd_hid_api.c index f2efa4e3cb1e9..fd9f27a23ba99 100644 --- a/subsys/usb/device_next/class/usbd_hid_api.c +++ b/subsys/usb/device_next/class/usbd_hid_api.c @@ -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 { diff --git a/subsys/usb/device_next/class/usbd_hid_internal.h b/subsys/usb/device_next/class/usbd_hid_internal.h index d049b0c22a357..dcb747486279f 100644 --- a/subsys/usb/device_next/class/usbd_hid_internal.h +++ b/subsys/usb/device_next/class/usbd_hid_internal.h @@ -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); };