nullplan wrote:
So here's a question: Isn't GET_DESCRIPTOR a standard request? Even if you are asking for a class specific descriptor?
I've read a bit more in the QEMU source code. It appears that GET_DESCRIPTOR is always a standard device request. So you must set the request type to USB_DIR_IN | USB_RECIP_DEVICE | USB_TYPE_STANDARD. I suspect that is already done in USB_DEV_GET_DESCRIPTOR_REQ_TYPE. So the USB_REQ_TYPE_CLASS is definitely wrong. It appears to be the case that you must get the entire configuration descriptor, and then parse that one bytewise, rather than ask for descriptor type 0x24 directly. So
Code:
req.RequestType = USB_DEV_GET_DESCRIPTOR_REQ_TYPE;
req.Request = USB_REQ_GET_DESCRIPTOR;
req.Value = USB_DT_CONFIG;
req.Index = 0; /* I have never seen a USB device with more than one configuration. */
req.Length = 9; /* At least get the full config descriptor */
status = UsbIo->UsbControlTransfer(UsbIo, &req, EfiUsbDataIn, PcdGet32 (PcdUsbTransferTimeoutValue), &Header, req.Length, &UsbStatus);
/* handle error */
req.Length = Header[2] | Header[3] << 8;
status = UsbIo->UsbControlTransfer(UsbIo, &req, EfiUsbDataIn, PcdGet32 (PcdUsbTransferTimeoutValue), &Header, req.Length, &UsbStatus);
That ought to get you the whole config descriptor, including all interface descriptors and their special descriptors.
Nope, its not working. Here's what I did:
Code:
EFI_USB_DEVICE_REQUEST req = {0};
req.RequestType = USB_DEV_GET_DESCRIPTOR_REQ_TYPE;
req.Request = USB_REQ_GET_DESCRIPTOR;
req.Value = USB_DESC_TYPE_CONFIG;
req.Index = 0;
req.Length = 9;
status = UsbIo->UsbControlTransfer(UsbIo, &req, EfiUsbDataIn, PcdGet32 (PcdUsbTransferTimeoutValue), &Header, req.Length, &UsbStatus);
if (EFI_ERROR(status)) {
st->BootServices->FreePool(Header);
goto failed;
}
req.Length = Header[2] | Header[3] << 8;
status = UsbIo->UsbControlTransfer(UsbIo, &req, EfiUsbDataIn, PcdGet32 (PcdUsbTransferTimeoutValue), &Header, req.Length, &UsbStatus);
if (EFI_ERROR(status)) {
st->BootServices->FreePool(Header);
goto failed;
}
UINTN DescriptorPosition = 0;
for (UINTN i = 0; i < 0xFFFF; ++i) {
if (Header[i] == 0x24 && Header[i + 1] == 0x01) {
DescriptorPosition = i - 1;
break;
}
}
if (DescriptorPosition == 0) {
status = EFI_NOT_FOUND;
goto failed;
}
Print(L"Length is %d, total length is %d, total length of interface descriptor is %d, USB ADC version is %04x, %d interfaces in collection\n", Header[DescriptorPosition], req.Length, Header[DescriptorPosition + 3] | Header[DescriptorPosition + 4] << 8, Header[DescriptorPosition + 5] | Header[DescriptorPosition + 6] << 8, Header[DescriptorPosition + 7]);
I get (in the EFI shell) the error:
Quote:
usb_desc_get_descriptor: 1 unknown type 0 (len 9)
There's a function for retrieving the configuration descriptor (UsbIo->UsbGetConfigurationDescriptor) but it doesn't do this.
Edit: Okay, so I've been really, really stupid. EDK II comes with lots of extra libraries on top of the protocols that it exposes. One of them is UefiUsbLib. UefiUsbLib has a function called UsbGetDescriptor():
Code:
/**
Get the descriptor of the specified USB device.
Submit a USB get descriptor request for the USB device specified by UsbIo, Value,
and Index, and return the descriptor in the buffer specified by Descriptor.
The status of the transfer is returned in Status.
If UsbIo is NULL, then ASSERT().
If Descriptor is NULL, then ASSERT().
If Status is NULL, then ASSERT().
@param UsbIo A pointer to the USB I/O Protocol instance for the specific USB target.
@param Value The device request value.
@param Index The device request index.
@param DescriptorLength The size, in bytes, of Descriptor.
@param Descriptor A pointer to the descriptor buffer to get.
@param Status A pointer to the status of the transfer.
@retval EFI_SUCCESS The request executed successfully.
@retval EFI_OUT_OF_RESOURCES The request could not be completed because the
buffer specified by DescriptorLength and Descriptor
is not large enough to hold the result of the request.
@retval EFI_TIMEOUT A timeout occurred executing the request.
@retval EFI_DEVICE_ERROR The request failed due to a device error. The transfer
status is returned in Status.
**/
EFI_STATUS
EFIAPI
UsbGetDescriptor (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN UINT16 Value,
IN UINT16 Index,
IN UINT16 DescriptorLength,
OUT VOID *Descriptor,
OUT UINT32 *Status
);
I automatically dismissed it because I didn't know how to use it but now that I know more about USB it all makes sense. Will try it now and update you guys on the status.