]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
usb: gadget: f_hid: added GET_IDLE and SET_IDLE handlers
authorMaxim Devaev <mdevaev@gmail.com>
Wed, 21 Jul 2021 18:03:51 +0000 (21:03 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 Aug 2021 11:19:41 +0000 (13:19 +0200)
commit afcff6dc690e24d636a41fd4bee6057e7c70eebd upstream.

The USB HID standard declares mandatory support for GET_IDLE and SET_IDLE
requests for Boot Keyboard. Most hosts can handle their absence, but others
like some old/strange UEFIs and BIOSes consider this a critical error
and refuse to work with f_hid.

This primitive implementation of saving and returning idle is sufficient
to meet the requirements of the standard and these devices.

Acked-by: Felipe Balbi <balbi@kernel.org>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Maxim Devaev <mdevaev@gmail.com>
Link: https://lore.kernel.org/r/20210721180351.129450-1-mdevaev@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/f_hid.c

index fa8a8e04008ae0b13052c453e35f03a3a0073156..db32091140d457221bd19a67fa549527cf79e9c3 100644 (file)
@@ -41,6 +41,7 @@ struct f_hidg {
        unsigned char                   bInterfaceSubClass;
        unsigned char                   bInterfaceProtocol;
        unsigned char                   protocol;
+       unsigned char                   idle;
        unsigned short                  report_desc_length;
        char                            *report_desc;
        unsigned short                  report_length;
@@ -529,6 +530,14 @@ static int hidg_setup(struct usb_function *f,
                goto respond;
                break;
 
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+                 | HID_REQ_GET_IDLE):
+               VDBG(cdev, "get_idle\n");
+               length = min_t(unsigned int, length, 1);
+               ((u8 *) req->buf)[0] = hidg->idle;
+               goto respond;
+               break;
+
        case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
                  | HID_REQ_SET_REPORT):
                VDBG(cdev, "set_report | wLength=%d\n", ctrl->wLength);
@@ -552,6 +561,14 @@ static int hidg_setup(struct usb_function *f,
                goto stall;
                break;
 
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+                 | HID_REQ_SET_IDLE):
+               VDBG(cdev, "set_idle\n");
+               length = 0;
+               hidg->idle = value;
+               goto respond;
+               break;
+
        case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
                  | USB_REQ_GET_DESCRIPTOR):
                switch (value >> 8) {
@@ -779,6 +796,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
        hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
        hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
        hidg->protocol = HID_REPORT_PROTOCOL;
+       hidg->idle = 1;
        hidg_ss_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
        hidg_ss_in_comp_desc.wBytesPerInterval =
                                cpu_to_le16(hidg->report_length);