usbx86-64osdevhci

Why do I get 2 interface descriptors before the endpoint descriptor after a GET_DESCRIPTOR USB request on QEMU?


I am writing a small x86-64 hobby OS I boot with UEFI. I am currently writing a driver for the Intel's xHC. I am at a point where I can address USB devices and have a Transfer Ring allocated for Endpoint 0 of each device. I then use a GET_DESCRIPTOR request to get the configuration descriptor of each device. I ask QEMU to emulate a USB keyboard and a USB mouse. I thus get 2 different descriptors which are the following:

user@user-System-Product-Name:~$ hexdump -C result.bin
00000000  09 02 22 00 01 01 06 a0  32 09 04 00 00 01 03 01  |..".....2.......|
00000010  02 00 09 21 01 00 00 01  22 34 00 07 05 81 03 04  |...!...."4......|
00000020  00 07 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001000  09 02 22 00 01 01 08 a0  32 09 04 00 00 01 03 01  |..".....2.......|
00001010  01 00 09 21 11 01 00 01  22 3f 00 07 05 81 03 08  |...!...."?......|
00001020  00 07 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00001030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00002000

Basically, I ask GDB to output the content of RAM where I placed the descriptors in the file result.bin. Then I hexdump the content of result.bin in the console. Here you can see the configuration of the USB mouse first. Then, 1 page later, the configuration of the USB keyboard.

The configuration descriptor of the mouse is 09 02 22 00 01 01 06 a0 32. It is followed with 2 interface descriptors: 09 04 00 00 01 03 01 02 00 and 09 21 01 00 00 01 22 34 00. Those are followed by one endpoint descriptor: 07 05 81 03 08 00 07.

In the first interface descriptor for both the mouse and the keyboard, it is indicated that there is one endpoint descriptor (indicated by the bNumEndpoints field of the descriptor which is byte number 4 indexed from 0). I would expect that the following descriptor is the endpoint descriptor. Instead, I get a second interface descriptor (indicated by the fact that it has a length of 9 bytes instead of 7 and by the values of the different fields).

As stated on https://wiki.osdev.org/Universal_Serial_Bus:

Each CONFIGURATION descriptor has at least one INTERFACE descriptor, and each INTERFACE descriptor may have up to 15 ENDPOINT descriptors. When the host requests a certain CONFIGURATION descriptor, the device returns the CONFIGURATION descriptor followed immediately by the first INTERFACE descriptor, followed immediately by all of the ENDPOINT descriptors for endpoints that the interface defines (which may be none). This is followed immediately by the next INTERFACE descriptor if one exists, and then by its ENDPOINT descriptors if applicable. This pattern continues until all the information within the scope of the specific configuration is transfered.

Why do I get 2 interface descriptors followed by the endpoint descriptor in my case? Is it a QEMU bug or is it something I should expect?


Solution

  • You're not accurately describing the binary data that I see in your shell output.

    The dump starts with 9-byte descriptor of type 2 so that is your device descriptor:

    09 02 22 00 01 01 06 a0 32
    

    Then there is a 9-byte descriptor of type 4, so that is an interface, and it has bNumEndpoints set to 1:

    09 04 00 00 01 03 01 02 00
    

    Then there is another 9-byte descriptor of type 0x21. I don't recognize that code off the top of my head, but it probably is something standard:

    09 21 01 00 00 01 22 34 00
    

    Then we have a 7-byte descriptor of type 5, so that is an endpoint descriptor:

    07 05 81 03 04 00 07