Skip to contentSkip to conversation

usb: gadget: hid: allow dynamic interval configuration via configfs

PATCH Superseded [email protected] 3 participants 4 messages Apr 29, 2025 USB

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 usb: gadget: hid: allow dynamic interval configuration via configfs

From: Ben Hoff <[email protected]>

This patch adds support for dynamically configuring the polling interval (bInterval) for HID function drivers using configfs. This enables custom HID gadgets with user-specified poll rates without recompilation.

---
 drivers/usb/gadget/function/f_hid.c | 54 ++++++++++++++---------------
 drivers/usb/gadget/function/u_hid.h |  1 +
 2 files changed, 27 insertions(+), 28 deletions(-)
drivers/usb/gadget/function/f_hid.c +26 -28
index 740311c4fa24..8aae22f9d609 100644
@@ -62,6 +62,7 @@ struct f_hidg {
 	unsigned short			report_desc_length;
 	char				*report_desc;
 	unsigned short			report_length;
+	unsigned char			interval;
 	/*
 	 * use_out_ep - if true, the OUT Endpoint (interrupt out method)
 	 *              will be used to receive reports from the host
@@ -156,10 +157,7 @@ static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = {
 	.bEndpointAddress	= USB_DIR_IN,
 	.bmAttributes		= USB_ENDPOINT_XFER_INT,
 	/*.wMaxPacketSize	= DYNAMIC */
-	.bInterval		= 4, /* FIXME: Add this field in the
-				      * HID gadget configuration?
-				      * (struct hidg_func_descriptor)
-				      */
+	/*.bInterval		= DYNAMIC */
 };
 
 static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = {
@@ -177,10 +175,7 @@ static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = {
 	.bEndpointAddress	= USB_DIR_OUT,
 	.bmAttributes		= USB_ENDPOINT_XFER_INT,
 	/*.wMaxPacketSize	= DYNAMIC */
-	.bInterval		= 4, /* FIXME: Add this field in the
-				      * HID gadget configuration?
-				      * (struct hidg_func_descriptor)
-				      */
+	/*.bInterval		= DYNAMIC */
 };
 
 static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = {
@@ -218,10 +213,7 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
 	.bEndpointAddress	= USB_DIR_IN,
 	.bmAttributes		= USB_ENDPOINT_XFER_INT,
 	/*.wMaxPacketSize	= DYNAMIC */
-	.bInterval		= 4, /* FIXME: Add this field in the
-				      * HID gadget configuration?
-				      * (struct hidg_func_descriptor)
-				      */
+	/* .bInterval		= DYNAMIC */
 };
 
 static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
@@ -230,10 +222,7 @@ static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
 	.bEndpointAddress	= USB_DIR_OUT,
 	.bmAttributes		= USB_ENDPOINT_XFER_INT,
 	/*.wMaxPacketSize	= DYNAMIC */
-	.bInterval		= 4, /* FIXME: Add this field in the
-				      * HID gadget configuration?
-				      * (struct hidg_func_descriptor)
-				      */
+	/*.bInterval		= DYNAMIC */
 };
 
 static struct usb_descriptor_header *hidg_hs_descriptors_intout[] = {
@@ -259,10 +248,7 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
 	.bEndpointAddress	= USB_DIR_IN,
 	.bmAttributes		= USB_ENDPOINT_XFER_INT,
 	/*.wMaxPacketSize	= DYNAMIC */
-	.bInterval		= 10, /* FIXME: Add this field in the
-				       * HID gadget configuration?
-				       * (struct hidg_func_descriptor)
-				       */
+	/*.bInterval		= DYNAMIC */
 };
 
 static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
@@ -1202,6 +1188,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
 	hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
 	hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
 	hidg_ss_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+
+	hidg_fs_in_ep_desc.bInterval = hidg->interval;
+	hidg_hs_in_ep_desc.bInterval = hidg->interval;
+
 	hidg_ss_out_comp_desc.wBytesPerInterval =
 				cpu_to_le16(hidg->report_length);
 	hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
@@ -1224,19 +1214,21 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
 	hidg_ss_out_ep_desc.bEndpointAddress =
 		hidg_fs_out_ep_desc.bEndpointAddress;
 
-	if (hidg->use_out_ep)
-		status = usb_assign_descriptors(f,
-			hidg_fs_descriptors_intout,
-			hidg_hs_descriptors_intout,
-			hidg_ss_descriptors_intout,
-			hidg_ss_descriptors_intout);
-	else
+	if (hidg->use_out_ep) {
+		hidg_fs_out_ep_desc.bInterval = hidg->interval;
+		hidg_hs_out_ep_desc.bInterval = hidg->interval;
+		status = usb_assign_descriptors(f,
+			    hidg_fs_descriptors_intout,
+			    hidg_hs_descriptors_intout,
+			    hidg_ss_descriptors_intout,
+			    hidg_ss_descriptors_intout);
+	} else {
 		status = usb_assign_descriptors(f,
 			hidg_fs_descriptors_ssreport,
 			hidg_hs_descriptors_ssreport,
 			hidg_ss_descriptors_ssreport,
 			hidg_ss_descriptors_ssreport);
-
+	}
 	if (status)
 		goto fail;
 
@@ -1362,6 +1354,7 @@ F_HID_OPT(subclass, 8, 255);
 F_HID_OPT(protocol, 8, 255);
 F_HID_OPT(no_out_endpoint, 8, 1);
 F_HID_OPT(report_length, 16, 65535);
+F_HID_OPT(interval, 8, 255);
 
 static ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page)
 {
@@ -1422,6 +1415,7 @@ static struct configfs_attribute *hid_attrs[] = {
 	&f_hid_opts_attr_protocol,
 	&f_hid_opts_attr_no_out_endpoint,
 	&f_hid_opts_attr_report_length,
+	&f_hid_opts_attr_interval,
 	&f_hid_opts_attr_report_desc,
 	&f_hid_opts_attr_dev,
 	NULL,
@@ -1468,6 +1462,9 @@ static struct usb_function_instance *hidg_alloc_inst(void)
 	if (!opts)
 		return ERR_PTR(-ENOMEM);
 	mutex_init(&opts->lock);
+
+	opts->interval = 10;
+
 	opts->func_inst.free_func_inst = hidg_free_inst;
 	ret = &opts->func_inst;
 
@@ -1546,6 +1543,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
 	hidg->bInterfaceProtocol = opts->protocol;
 	hidg->report_length = opts->report_length;
 	hidg->report_desc_length = opts->report_desc_length;
+	hidg->interval = opts->interval;
 	if (opts->report_desc) {
 		hidg->report_desc = kmemdup(opts->report_desc,
 					    opts->report_desc_length,
drivers/usb/gadget/function/u_hid.h +1 -0
index 84bb70292855..a29dcb14f738 100644
@@ -25,6 +25,7 @@ struct f_hid_opts {
 	unsigned short			report_desc_length;
 	unsigned char			*report_desc;
 	bool				report_desc_alloc;
+	unsigned char			interval;
 
 	/*
 	 * Protect the data form concurrent access by read/write
Signature · 1 line
2.49.0
maintainer
#1 lore raw
Quoted 5 lines · On Tue, Apr 29, 2025 at 09:33:10AM -0400, [email protected] wrote:

From: Ben Hoff <[email protected]>

This patch adds support for dynamically configuring the polling interval (bInterval) for HID function drivers using configfs. This enables custom HID gadgets with user-specified poll rates without recompilation.

But you changed the default from:

drivers/usb/gadget/function/f_hid.c static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = { · line 162

Quoted 4 lines
drivers/usb/gadget/function/f_hid.c +0 -4
-	.bInterval		= 4, /* FIXME: Add this field in the
-				      * HID gadget configuration?
-				      * (struct hidg_func_descriptor)
-				      */

to:

drivers/usb/gadget/function/f_hid.c static struct usb_function_instance *hidg_alloc_inst(void) · line 1466

Quoted 1 line
drivers/usb/gadget/function/f_hid.c +1 -0
+	opts->interval = 10;

do you mean to do that without documenting it?

thanks,

greg k-h

#2 lore raw
In reply to [email protected]
Quoted 12 lines · On 4/29/2025 7:03 PM, [email protected] wrote:

From: Ben Hoff <[email protected]>

This patch adds support for dynamically configuring the polling interval (bInterval) for HID function drivers using configfs. This enables custom HID gadgets with user-specified poll rates without recompilation.

---
  drivers/usb/gadget/function/f_hid.c | 54 ++++++++++++++---------------
  drivers/usb/gadget/function/u_hid.h |  1 +
  2 files changed, 27 insertions(+), 28 deletions(-)

[...]

drivers/usb/gadget/function/f_hid.c static struct usb_function_instance *hidg_alloc_inst(void) · line 1467

Quoted 7 lines
drivers/usb/gadget/function/f_hid.c +3 -0
@@ -1468,6 +1462,9 @@ static struct usb_function_instance *hidg_alloc_inst(void)
  	if (!opts)
  		return ERR_PTR(-ENOMEM);
  	mutex_init(&opts->lock);
+
+	opts->interval = 10;
+

The default value was 10 only for hidg_hs_descriptors_intout. Aren't we now making it 10 for all other ep descriptors as well ?

Regards,
Krishna,

Quoted 23 lines
drivers/usb/gadget/function/f_hid.c +1 -0
  	opts->func_inst.free_func_inst = hidg_free_inst;
  	ret = &opts->func_inst;
 
@@ -1546,6 +1543,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
  	hidg->bInterfaceProtocol = opts->protocol;
  	hidg->report_length = opts->report_length;
  	hidg->report_desc_length = opts->report_desc_length;
+	hidg->interval = opts->interval;
  	if (opts->report_desc) {
  		hidg->report_desc = kmemdup(opts->report_desc,
  					    opts->report_desc_length,
drivers/usb/gadget/function/u_hid.h +1 -0
index 84bb70292855..a29dcb14f738 100644
@@ -25,6 +25,7 @@ struct f_hid_opts {
  	unsigned short			report_desc_length;
  	unsigned char			*report_desc;
  	bool				report_desc_alloc;
+	unsigned char			interval;
 
  	/*
  	 * Protect the data form concurrent access by read/write
author
#3 lore raw

Hi Greg, Krishna,

Thanks for the feedback!

You're right — I should not have changed the default bInterval from 4 to 10 for all endpoints.
To preserve the original behavior, I'll resend a v2 patch that:

- Sets the global default bInterval to 4 frames, matching the existing implementation.
- Leaves the High-Speed interrupt-out endpoints (hidg_hs_descriptors_intout) at their historic 10-frame default unless explicitly overridden through configfs.

Thanks again for reviewing this — I'll send an updated patch shortly.

Ben

Quoted 56 lines · Krishna Kurapati
Quoted 12 lines · On 4/29/2025 7:03 PM, [email protected] wrote:

From: Ben Hoff <[email protected]>

This patch adds support for dynamically configuring the polling interval (bInterval) for HID function drivers using configfs. This enables custom HID gadgets with user-specified poll rates without recompilation.

---
  drivers/usb/gadget/function/f_hid.c | 54 ++++++++++++++---------------
  drivers/usb/gadget/function/u_hid.h |  1 +
  2 files changed, 27 insertions(+), 28 deletions(-)

[...]

drivers/usb/gadget/function/f_hid.c static struct usb_function_instance *hidg_alloc_inst(void) · line 1467

Quoted 7 lines
drivers/usb/gadget/function/f_hid.c +3 -0
@@ -1468,6 +1462,9 @@ static struct usb_function_instance *hidg_alloc_inst(void)
      if (!opts)
              return ERR_PTR(-ENOMEM);
      mutex_init(&opts->lock);
+
+     opts->interval = 10;
+

The default value was 10 only for hidg_hs_descriptors_intout. Aren't we now making it 10 for all other ep descriptors as well ?

Regards,
Krishna,

Quoted 23 lines
drivers/usb/gadget/function/f_hid.c +1 -0
      opts->func_inst.free_func_inst = hidg_free_inst;
      ret = &opts->func_inst;
 
@@ -1546,6 +1543,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
      hidg->bInterfaceProtocol = opts->protocol;
      hidg->report_length = opts->report_length;
      hidg->report_desc_length = opts->report_desc_length;
+     hidg->interval = opts->interval;
      if (opts->report_desc) {
              hidg->report_desc = kmemdup(opts->report_desc,
                                          opts->report_desc_length,
drivers/usb/gadget/function/u_hid.h +1 -0
index 84bb70292855..a29dcb14f738 100644
@@ -25,6 +25,7 @@ struct f_hid_opts {
      unsigned short                  report_desc_length;
      unsigned char                   *report_desc;
      bool                            report_desc_alloc;
+     unsigned char                   interval;
 
      /*
       * Protect the data form concurrent access by read/write