Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I obtain USB_DEVICE_DESCRIPTOR given a device path

I have been able to enumerate USB devices using the SetupAPI, and I've looked at the usbview application from the WDK, but I still can't figure out how to get the USB_DEVICE_DESCRIPTOR.

  • I would rather avoid using WMI.
  • DeviceIoControl is what the sample app usbview uses, but that really only works if you're enumerating devices on a Hub. I suppose if I can get to the parent hub (and port) given a device path (or Id), this method may work but I haven't been able to determine how to do this either.
  • I have a mix of devices for which I'd like to get the descriptor. Some of these are HIDs, and it's possible some are WinUsb.sys devices. If they are WinUsb devices I can use WinUsb_GetDescriptor, but that won't work for HIDs (and I don't know how to tell the difference between them from the Id or Path...Interface class I guess?).
  • I could use SetupDiGetDeviceRegistryProperty but in the list of available properties, I can see the Manufacturer string, but not the vendor Id.
  • I could possibly parse this value from the device path or the device Id, but that seems sort of...hack-ish. Is that just what people do though? Also it still leaves me going to other methods if I want other fields like the Manufacturer, where if I could just get the whole USB_DEVICE_DESCRIPTOR I think I'd have about everything I need.
  • LibUsb.Net only supports WinUsb devices apparently. That's how it seems to get the descriptor.
  • Apparently WinRT has some new APIs and therefore Windows Store apps have a nice way of getting the descriptor. But this is definitely not a Windows Store app, and I don't know that there is another way to use the newer APIs.

Can anyone point me in the right direction? Is it just not possible to get this information from the WinAPI in a nice way without starting at the Hub?

like image 856
Malaise Avatar asked Nov 09 '22 20:11

Malaise


1 Answers

Your best bet would be to extract the info from the device path and use the SetupDi functions to get the other bits and pieces. As far as I know, the device path always follows the same convention. i.e.:

"\\?\usb#vid_0000&pid_1111#SERIAL#{GUID}" where 0000 is the VID and 1111 is the PID as hex strings. SERIAL is either the serial provided by the hardware or the OS-assigned serial value.

I personally found an instance where I absolutely wanted to get the device descriptor in order to pull the serial that way. In some instances, the OS was not recognizing the serial number provided by my hardware. I fixed that on the hardware side, but I still wanted to accommodate old hardware on the PC side. Below is my approach. There may be something better, but this is the best that I have come up with so far. You may still consider it to be "hack-ish" though.

  1. Call SetupDiGetClassDevs() to setup your desired DeviceInfoSet
  2. Obtain your device info data using SetupDiEnumDeviceInfo()
  3. Call SetupDiGetDeviceRegistryProperty() with SPDRP_LOCATION_INFORMATION to get the location information. This string should look like "Port_#0001.Hub_#0001". Parse this string to get the port number where your device is located. (I assume that this value is base 10, but I haven't verified this yet)
  4. Call CM_Get_Parent() to get the device node pointer value of the parent (hub)
  5. Call SetupDiGetClassDevs() with the GUID of {0xf18a0e88, 0xc30c, 0x11d0, {0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8}} to get all of the hubs on your system. That GUID should be defined as GUID_DEVINTERFACE_USB_HUB in usbiodef.h.
  6. Iterate through the list of devices using SetupDiEnumDeviceInfo(). Stop once DevInst equals the value obtained in step 4.
  7. Call SetupDiGetDeviceInterfaceDetail() on the index found in step 6.
  8. Call CreateFile() on the DevicePath obtained in step 7.
  9. Call DeviceIoControl() using the file created in step 8 and the port number obtained in step 3 as your connection index.

-EDIT-

As Ben pointed out in the comments, you can skip steps 5, 6, and 7 by using CM_Get_Device_ID on the parent's dev node obtained in step 4. Change the slashes (\) in this string to pounds (#). Prepend "\\?\" and then append "#{f18a0e88-c30c-11d0-8815-00a0c906bed8}". Use that as your device path in step 8. This avoids iterating through all of the hub devices on your system :)

like image 191
Tails86 Avatar answered Nov 15 '22 04:11

Tails86