After another user related question on this forum I try to get my USB HID Gadget Keyboard running in BIOS. My gadget is the RPi-Z using libcomposite on Raspbian Buster.
When I plug my keyboard into my another RaspberryPi where Raspbian is also installed, I can see the Boot Protocol used when I'm on my desktop and when I open a terminal and type lsusb -v -d VIP:PID.
The lsusb return an """UNAVAILABLE DESCRIPTOR""", so I think it is because the Boot Protocol is used (subclass indicate Boot protocol) and then the Report Descriptor is not used because when Boot Descriptor is used Report Descriptor is not used. But if I use usbhid-dump tool , I can also get the HID keyboard descriptor, so I don't really understand yet.
This is my 'boot' test :
I plug my gadget on another computer and I press F12 for enter in BIOS settings on the PC default keyboard. (The raspberry is correctly power-on when I start the PC.)
My first menu is to select a boot drive or advanced settings. So I don't move from here and I wait for my Raspbian boot and 'mount' the keyboard for sending [down] or [up] keys with Wi-Fi and ssh. (I use an ssh script for that where the script work perfectly)
But when I send a key DOWN or UP (arrows) keys nothing is appended in this BIOS menu.
My tool show the 8 Bytes datas send (key & modifier),and send the other 8 Bytes datas (release all keys) and the script is always opened 'same as the hosts not responding'
If I exit from the BIOS settings, I go to Cryptsetup for entrer disk encryption key and then my script is closed and the keys was sent correctly (I send [enter] key for cypher key validation and it's works).
What can it mean :
Does the BIOS not support the USB HID keyboard ?
That once in the BIOS the hardware can no longer be detected 'dynamically' ?
EDIT 1:
I use another usb keyboard,try the same test and it's work, so it's a problem with my gadget conf and not the BIOS. And because the first 8 Bytes are successfully sent from my script and my gadget but nothing happens on the menu. I think it's a problem with my keys
LSUSB
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 186
bNumInterfaces 5
bConfigurationValue 1
iConfiguration 4 Config 1: RNDIS network
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 250mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 1 Keyboard
iInterface 9
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.01
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 65
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 4
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 4
REPORT_DESCRIPTOR (copied from my working usb keyboard)
05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
75 01 95 08 81 02 95 01 75 08 81 01 95 05 75 01
05 08 19 01 29 05 91 02 95 01 75 03 91 01 95 06
75 08 15 00 26 A4 00 05 07 19 00 2A A4 00 81 00
C0
I have also try this descriptor BootKeyboard.cpp but not work.
If I read this document on page 60 I can see the require descriptor for boot keyboard:
Usage Page (Generic Desktop),
Usage (Keyboard),
Collection (Application),
Report Size (1),
Report Count (8),
Usage Page (Key Codes),
...
My descriptor seems to be correct. Always on document on page 60 I can read :
The following table represents the keyboard input report (8 bytes).
I send :
echo -en '\0\0\x52\0\0\0\0\0' > /dev/hidg0
This command seems to be executed succesfully (but nothing is done on the BIOS menu), follow by :
echo -en '\0\0\0\0\0\0\0\0' > /dev/hidg0
The shell is 'blocked'. (wait for usb response ?)
I use this paper page 53 for reference
EDIT 2:
On the gadget I can see some errors on gadget boot :
[...] configfs-gadget gadget: End Point Request ERROR: -108
[...] dwc2 20980000.usb: new device is high-speed
[...] dwc2 20980000.usb: new device is high-speed
[...] dwc2 20980000.usb: new address 2
[...] configfs-gadget gadget: high-speed config #1: c
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep a792ad4a ep0, 0)
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt: can't clear halt on ep0
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep a792ad4a ep0, 0)
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt: can't clear halt on ep0
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep a792ad4a ep0, 0)
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt: can't clear halt on ep0
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep a792ad4a ep0, 0)
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt: can't clear halt on ep0
EDIT 3:
Other test (same result if I plug my gadget into another USB port) :
sudo bash -c "echo > UDC"
sudo bash -c "echo 20980000.usb > UDC"
[...] dwc2 20980000.usb: bound driver configfs-gadget
[...] dwc2 20980000.usb: new device is high-speed
[...] dwc2 20980000.usb: new address 2
[...] configfs-gadget gadget: high-speed config #1: c
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep a792ad4a ep0, 0)
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt: can't clear halt on ep0
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep a792ad4a ep0, 0)
[...] dwc2 20980000.usb: dwc2_hsotg_ep_sethalt: can't clear halt on ep0
EDIT 4:
When the gadget work :
sudo cat /sys/kernel/debug/20980000.usb/ep0
Endpoint index 0, named ep0, dir out:
DIEPCTL=0x00008000, DOEPCTL=0x80028000
DIEPDMA=0x525bd044, DOEPDMA=0x56b35ae0
DIEPINT=0x000020d0, DOEPINT=0x00002010
DIEPTSIZ=0x00000000, DOEPTSIZ=0x00080008
mps 64
total_data=0
request list (63f56bc4,63f56bc4):
* req 83dd708b: 8 bytes @bfdef28f, 0 done, res -115
sudo cat /sys/kernel/debug/20980000.usb/ep3in
Endpoint index 3, named ep3in, dir in:
DIEPCTL=0x004c8008, DOEPCTL=0x80088200
DIEPDMA=0xb03e91cc, DOEPDMA=0x519dd800
DIEPINT=0x00002090, DOEPINT=0x00000000
DIEPTSIZ=0x00000000, DOEPTSIZ=0x001805fe
mps 8
total_data=0
request list (1ece2335,1ece2335):
sudo cat /sys/kernel/debug/20980000.usb/ep2out
Endpoint index 2, named ep2out, dir out:
DIEPCTL=0x818c8008, DOEPCTL=0x000e8008
DIEPDMA=0x5404ade8, DOEPDMA=0x519352c4
DIEPINT=0x00000000, DOEPINT=0x00000000
DIEPTSIZ=0x20080000, DOEPTSIZ=0x00000007
mps 8
total_data=0
request list (3dc15386,3dc15386):
When the gadget not work :
sudo cat /sys/kernel/debug/20980000.usb/ep0
Endpoint index 0, named ep0, dir out:
DIEPCTL=0x00008000, DOEPCTL=0x80028000
DIEPDMA=0x52e440bc, DOEPDMA=0x569bfb60
DIEPINT=0x000020d0, DOEPINT=0x00002010
DIEPTSIZ=0x00000000, DOEPTSIZ=0x00080008
mps 64
total_data=0
request list (0d6f4638,0d6f4638):
* req 1378b877: 8 bytes @044102c4, 0 done, res -115
sudo cat /sys/kernel/debug/20980000.usb/ep3in
Endpoint index 3, named ep3in, dir in:
DIEPCTL=0x004c8008, DOEPCTL=0x00000400
DIEPDMA=0xb01e91cc, DOEPDMA=0xb0d3eb0b
DIEPINT=0x00000080, DOEPINT=0x00000000
DIEPTSIZ=0x00000000, DOEPTSIZ=0x00000000
mps 8
total_data=0
request list (3f381df3,3f381df3):
sudo cat /sys/kernel/debug/20980000.usb/ep2out
Endpoint index 2, named ep2out, dir out:
DIEPCTL=0x018c8008, DOEPCTL=0x800c8008
DIEPDMA=0x7532451b, DOEPDMA=0x5152c6e0
DIEPINT=0x00000000, DOEPINT=0x00000000
DIEPTSIZ=0x00000000, DOEPTSIZ=0x00080008
mps 8
total_data=0
request list (831d3428,abbeefd9):
* req 7372364d: 8 bytes @d0414864, 0 done, res -115
req a85c223f: 8 bytes @851c7555, 0 done, res -115
req 21dba8da: 8 bytes @7fc50fee, 0 done, res -115
req 621b9773: 8 bytes @dd7307ac, 0 done, res -115
The request seems to use EP2 OUT.
I think the output must be on the EP 3 IN but the gadget use the OUT endpoint ?
EDIT 5:
From developer.electricimp.com
Halt Conditions
The device has found itself in a state that requires intervention from the host; the host has to perform some form of recovery action before communication to that endpoint can begin again. An example of an event that can trigger a STALL is an attempt to transfer more data to the device than was specified.
On working device : bMaxPacketSize0: 32 (on the gadget : bMaxPacketSize0: 64, unable to change this value, trying hexa value, dec value ...) ?
From docs.microsoft.com
Interrupt IN transfers
Conversely, a USB device can support interrupt IN endpoints as a way to inform the host about hardware interrupts generated by the device. Typically USB Human Interface Devices (HID) such as keyboards and pointing devices support interrupt OUT endpoints. When an interrupt occurs, the endpoint stores interrupt data but that data does not reach the host immediately. The endpoint must wait for the host controller to poll the device. Because there must be minimal delay between the time data is generated and reaches the host, it polls the device at regular intervals.
Interrupt OUT transfers
A USB device can support interrupt OUT endpoints that receive data from the host at regular intervals. Each time the host polls the device, the host sends data.
On working device : bInterval : 10 (on the gadget : bInterval : 4) ?
EDIT 6:
void setFeatureReport(void* report, int length){
if(length > 0){
featureReport = (uint8_t*)report;
featureLength = length;
// Disable feature report by default
disableFeatureReport();
}
}
// Feature (set feature report)
if(setup.wValueH == HID_REPORT_TYPE_FEATURE){
// No need to check for negative featureLength values,
// except the host tries to send more then 32k bytes.
// We dont have that much ram anyways.
if (length == featureLength) {
USB_RecvControl(featureReport, featureLength);
// Block until data is read (make length negative)
disableFeatureReport();
return true;
}
// TODO fake clear data?
}
How to do this with libcomposite ? stall parameter doesn't seem to be available same as UMS configuration.
My other working device have only one endpoint :
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 10
The differences with my working device USB keyboard are :
bMaxPacketSize0: 32(on the gadget this value is 64, unable to change this value)One Interrupt endpoint(on the gadget I have two endpoints)bInterval :10(on the gadget the value is 4)
If I understand OTG then my raspberry is powered by its USB OTG port.
Does gadget mode need the OTG, is it possible to power the raspberry via its default power input and use libcomposite ?
