how to parse USB Device Instance ID (DIID)? - windows

I want to know how "Device Instance ID" is made or how to parse it.
Take the following string as an example:
USB\VID_093A&PID_2700&MI_00\6&2703A67B&0&0000
As per my knowledge:
USB tells that this is a USB device
VID_093A indicates its vendor ID
similarly, PID_2700 is the product ID
However, I don't know about the rest.

MI is for multiple interfaces. A composite device has several interfaces.
6&2703A67B&0&0000 is the Instance ID.
The 6 at the beginning corresponds to the device node depth.
0 are root devices (e.g. volmgr, acpi_hal, they have Root\ prefix)
1 are acpi_hal devices (e.g. acpi (uses PNP0C08\0 instead of vid/pid/1), they have ACPI_HAL\ prefix)
2 are acpi devices (e.g. root pci bus controller (uses PNP0A08\0 instead of vid/pid/2), they have ACPI\ prefix)
3 are pci devices (e.g. xhci controller, they have PCI\ prefix)
4 are xhci devices (e.g. root hub, they have IUSB3\ prefix)
5+ are hub devices (e.g. usbccgp FDOs for composite devices, ubstor for storage etc., they have USB\ prefix)
6+ are usbstor devices (disk FDOs, USBSTOR\ prefix) or usbccgp composite interfaces (e.g. hidusb FDOs for input devices, USB\ prefix and have an MI)
7+ are endpoints (kbdhid FDOs for keyboards, mouhid FDOs for mice, HID\ prefix and have an MI)
The ID 2703A67B is an ID assigned by the parent. The xhci controller assigns the same one to all its root hubs; the separate hubs are identified by the value after the final ampersand, which is a simple incremental value. The root hub gives its child devices an ID that is the same across all devices and reboots; the separate devices are identified by the value after the final ampersand, again an incremental value. A composite device gives its children an ID that changes based on what hub/port it is plugged into; the separate interfaces are identified by the value after the final ampersand. An interface gives its children endpoints an ID that changes based on what hub/port it is plugged into; the separate endpoints are identified by the value after the final ampersand.
I can confirm that the ID that a hub assigns to its children changes if the hub is plugged into a different port on the root hub or a different hub altogether. I wrote '+' to indicate the presence of possible nested hubs. The PCI bus could also be nested and have a child bus (via PCIe to PCI bridge or PCIe root port aka. a PCIe to PCIe bridge), but I decided to leave that out. A root port is just a PCI device, basically a bridge/controller to a bus with only one slot (or more slots if the lanes are configured on the port to be split up), with a PCI\ prefix, and the child device will also have a PCI\ prefix.
By that token, the ID that the xHCI controller assigns probably depends on the PCI slot, and the ID the PCI bus controller assigns is probably dependent on what port the subordinate bus controller is connected to or whether it is the root controller.
This is why it installs drivers again when you insert a device into another port (unless it has a serial number). The drivers are considered installed when there is a registry entry for the DIID. The USB device will have a separate entry for each port and root hub combination they're plugged into. The composite device interfaces will have a separate entry for each port and root hub as well, as will the endpoints.
If the device has a serial number, which is uncommon, then the instance ID will be the serial number, and this will be the same regardless of where it is plugged into the system. If a composite device has a serial number instance ID then its interfaces probably have the same instance ID as that because the MI in the Device ID tells them apart. The endpoints are unlikely to have their own serial numbers and probably use the regular instance ID scheme.

Well, you really cant. This is the response from Microsoft regarding the same..
the device instance ID should be treated as opaque. if you want the VID/PID, query for the hardware and/or compa IDs and parse those. the hardware IDs are not considered opaque (but you still have to assume new hardware IDs will show up, so you need to detect the pattern you want to parse and have proactive code that handles other types of formats).
source: http://social.msdn.microsoft.com/Forums/en-US/4ff692bc-97c9-4943-b1ee-ec4f098e3b14/how-to-detect-sim-card-change-or-imsi-change-programatically-in-windows-phone-8?forum=wpdevelop

The Microsoft Device Instance ID page explains well how a Device Instance ID string is composed.
This page explains how a USB Identifier string (which composes the Device ID part of a Device Instance ID) is created.
Specifically, for your example:
USB\VID_093A&PID_2700&MI_00 is the Device ID, and
6&2703A67B&0&0000 is the Instance ID
Moreover, the Device ID tells you that:
USB\: this is a device under the USB enumerator
VID_093A: Pixart, Inc. manufactured this USB Device (you can find the USB Vendor ID list here)
PID_2700: this is the Product ID
MI_00: This is a Multiple Interface USB Device

Related

How to identify a HID after it's moved to a different port on Windows

I'm on Windows and am trying to store calibration data for game controllers connected via USB and am trying to find a value which uniquely identifies them in a port independent way.
There is the HidD_GetSerialNumberString function but i've read here that it's uncommon for devices to have serial numbers and indeed when i try to read one f.e. from a PS4 controller HidD_GetSerialNumberString returns FALSE and GetLastError returns ERROR_INVALID_PARAMETER.
Is there any other data available which can be accessed to achieve this?
You can try to use instance ID for your HID device (call CM_Get_Device_Interface_Property with device interface path and DEVPKEY_Device_InstanceId property and use string after last & char). It should be unique and persistent per system restarts. But it is not guaranteed if serial number is not provided (in this case instance ID will be different if device is plugged into different usb port)...
More info on this here: https://stackoverflow.com/a/56885175/1795050

What happens when we press a key on Windows?

First of all, I would say to you that I write this question from nothing because I have attempt to find good documentation but nothing stand out...
What happens when we squeeze a key?
I think this is complex but I hope you can help me.
What I search to know : all (but especially the program start on the host machine and how the key electric signal is encoded and send...)
The eXtensible Host Controller (xHC) has a Periodic Transfer Ring. Windows programs this ring to trigger a transfer every time an interval in milliseconds has passed. The right interval is specified in the USB descriptor returned by the USB device. When the transfer occurs, the xHC puts a Transfer Event TRB on the event ring and triggers an MSI-X interrupt which bypasses the IOAPIC as some kind of inter-processor interrupt. If Windows detects some change in the keys pressed, it will send a message to the application which currently has focus (calling the window's procedure) with the key pressed in one of the argument.
I don't know about electrical signals but I know the eXtensible Host Controller is the USB controller responsible to interact with USB on modern Windows systems. Since Windows nowadays requires an x64 processor, the xHC must be present on your motherboard. The xHC is a PCI-Express device which is compliant with the PCI-Express specification.
To find an xHC, you:
Find the RSDP ACPI table in RAM;
This table will be found by the UEFI firmware which acts as some kind of small operating-system (OS) during boot of the computer. Then, the OS developers will write a small UEFI application named bootx64.efi that they will place on a FAT32 partition on the hard-disk. They will place this app in the /boot/efi directory. The UEFI firmware will directly launch that application on boot of the computer which allows to have an OS which doesn't require user input to be launched (similarly to how it used to work with the legacy BIOS fetching the first sector of the hard-disk and executing the instructions found there).
The UEFI application is compiled in practice with either EDK2 or gnu-efi. These compilers are aware of the UEFI environment and specification. They thus compile the code to system calls that are present during boot and available for the UEFI application written by the OS developers. The System Tables (often the ACPI tables) are given as an argument to the "main" function (often called UefiMain) called by the UEFI firmware in the UEFI application. The code of the application can thus simply use these arguments to find the RSDP table and pass it to the OS.
Find the MCFG ACPI table using the RSDP;
The chain of table is RSDP -> XSDT -> MCFG. Once the OS found the MCFG, this table specifies the base address of the PCI configuration space. To interact with PCI devices you use memory mapped IO (MMIO). You write to some position in RAM and it will instead write to the registers of the PCI devices. The MCFG thus specifies the base address at which you will start finding MMIO registers for the different PCI devices that are plugged into the computer.
Iterate on the PCI devices and look at their IDs until you find an xHC.
To iterate on the PCI devices, the PCI convention specifies a formula which is the following:
UINT64 physical_address = base_address + ((bus - first_bus) << 20 | device << 15 | function << 12);
The base_address is for a specific segment group. Each segment group can have 256 buses (suitable for large servers or large computers with lots of components). There can be up to 65536 segment groups and each can have up to 256 PCI buses. Each PCI bus can have up to 32 devices plugged onto it and each device can have up to 8 functions. Each function can also be a PCI bridge. This is quite straightforward to understand because the terminology is clear. The bus here is an actual serial bus that the PCI devices (like a network card, a graphics card, an xHC, an AHCI, etc.) use to communicate with RAM. The function is a functionality of the PCI device like controlling USB devices, hard-disks, HDMI screens (for graphics cards), etc. The PCI bridge bridges a PCI bus to another PCI bus. It means you can have almost an infinite amount of devices with the PCI specification because the bridges allow to extend the tree of devices by adding other PCI host controllers.
Meanwhile, the bus is simply a number between 0 and 255. The first bus is specified in the MCFG ACPI table for a specific segment group. The device is a number between 0 and 31 and the function is a number between 0 and 7. This formula returns a physical address which points to a conventional configuration space (it is the same for all functions) which has specific registers. These registers are used to determine what is the type of device and to load a proper driver for it. Each function of each device thus gets a configuration space.
For the xHC, there will be only one function and the IDs returned by its configuration space will be 0x0C for the class ID and 0x03 for the subclass ID (https://wiki.osdev.org/EXtensible_Host_Controller_Interface).
Once you found an xHC, it gets rather complex. You need to initialize it and get the USB devices which are plugged in the computer at the current moment. You need to take several steps to get the xHC operational. For this part, I'll leave you to read the xHCI specification which (on chapter 4) specifies exactly the steps which need to be taken (https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf).
For the keyboard portion I'll leave you to read one of my answer on the stackexchange for computer science: https://cs.stackexchange.com/questions/141870/when-are-a-controllers-registers-loaded-and-ready-to-inform-an-i-o-operation/141918#141918.
Some good links:
https://wiki.osdev.org/Universal_Serial_Bus
https://wiki.osdev.org/PCI

A driver has enumerated two child PDO's that returned identical Device ID's

I have a kmdf bus driver PCI\VEN_XXXX&DEV_XXXX that creates two statically enumerated PDOs with serial numbers: 217 and 218; one for each Ethernet port. The PDO hardware id is ROOT\MY_NIC_PORT so I can install a NDIS Miniport driver on them.
The bus driver passes SDV and Verifier; but, on reboot two more PDOs get enumerated. On the next reboot I get a duplicate pdo crash.
The toaster example used the device class guid as part of the hardware id. When I tried that my NIC ports no longer showed up in device manager.
Any debug suggestion or work around idea would be appreciated?
pnpCaps.LockSupported = WdfFalse;
pnpCaps.EjectSupported = WdfTrue;
pnpCaps.Removable = WdfTrue;
pnpCaps.DockDevice = WdfFalse;
pnpCaps.UniqueID = WdfTrue;
pnpCaps.SilentInstall = WdfTrue;
pnpCaps.SurpriseRemovalOK = WdfTrue;
pnpCaps.HardwareDisabled = WdfFalse;
pnpCaps.NoDisplayInUI = WdfFalse;
pnpCaps.Address = SerialNo;
pnpCaps.UINumber = SerialNo;
************************************************************
Driver Verifier detected violation:
A driver has enumerated two child PDO's that returned identical Device
ID's.
CulpritAddress = FFFFF8025ED309C4, DeviceObject1 = FFFFE3882FB2F300,
DeviceObject2 = FFFFE3882EBF88D0.
************************************************************
There are a few versions of the toaster bus sample -- assuming you started with this one, then note that it saves its list of child PDOs in the registry. My guess is that your driver is both loading PDOs from the registry, and trying to dynamically create some too.
Set a breakpoint on your driver's version of Bus_PlugInDevice, and see how often it's getting called. Make sure it's never getting called 2x with the same Instance ID.
To clear up a bit of the naming thing: a device setup class is a GUID that is totally unrelated to its hardware ID. For NICs that want to interoperate with the OS's networking stack, you must use the NET setup class, {4d36e972-e325-11ce-bfc1-08002be10318}. You can put anything you want into your hardware ID. I don't really encourage you to put "ROOT\" in there, since that could be confused with a root-enumerated device (which your devices are not). Instead, you can use "yourcompany_yourdevice\port1" as a hardware ID.
While you're thinking about naming things, there are a few things to note about hardware IDs:
Once you assign a HWID, it's rather difficult to change it in a future driver update, without breaking customers who had already installed your device. So get it right the first time.
Once you assign an Instance ID, don't change or reuse it for the lifetime of the device. Otherwise you'll cause this bugcheck, or cause IP addresses to bounce around / get reset. The OS ultimately uses the Instance ID to figure out which NIC port to bind an IP address to.
Think about what happens if someone plugs 2 of your device into a system. Make sure your Instance ID is unique across all ports. You can do this by encoding into the Instance ID the PCI device serial number (if it has one) or by falling back to the PCI bus:device:function.
Don't lump together different types of hardware under the same hardware ID. For example, if the deluxe version of your device supports checksum offload, but the regular version does not -- you should use 2 different hardware IDs for these two different devices. Otherwise it gets difficult to write a single INF that has keywords for both.

Get property of driver's parent (or ancestor)

I have a driver for a USB-attached custom SCSI device which implements some vendor-specific commands. Each USB device hosts more than one SCSI LUN. This is working quite happily, but there is a problem with grouping SCSI LUNs according to device, as from the IOSCSILogicalUnitNub level down (which my driver inherits from), there are no identifying features which link the LUNs together (there are SCSI Logical Unit Numbers, but these collide for every new device; they all have a unit 0x0).
I'd like to do something like link them all according to the USB locationID field (or any other unique key), but I don't know how to get at the parent USB devices given only the io_service_t of the matching SCSI drivers. This would also be better to be done on the application side, rather than in the driver, as the application needs to keep track of which service has which LUNs and was opened by which instance of the handler.
The hierarchy in the IO Registry is:
MyUSB-SCSI Device#fd130000
|-IOUSBCompositeDriver
|-Mass Storage Class Interface#0 (has location ID 0xfd130000)
|-IOUSBMassStorageClass
|-IOSCSILogicalUnitNub#0 (has SCSI LUN number, unique in this device, but not globally)
| |-com_Company_driver_MyDriver (my driver)
| |-IOBlockStorageServices
| |-Manuf Device Media (these are strings from the device firmware)
| |-IOMediaBSDClient
|
|-IOSCSILogicalUnitNub#0
|-com_Company_driver_MyDriver
|-IOBlockStorageServices
|-Manuf Device Media
|-IOMediaBSDClient
If your device's io_service_t is in the variable _device, then the code would look like:
CFTypeRef _thing_you_want = IORegistryEntrySearchCFProperty(_device, kIOServicePlane, CFSTR("locationID"), NULL, kIORegistryIterateParents);
Go here for more info:
http://developer.apple.com/library/mac/#documentation/devicedrivers/conceptual/AccessingHardware/AH_IOKitLib_API/AH_IOKitLib_API.html

Is the PNPDeviceID unique

I wonder if the PNPDeviceID of an USB drive is unique (at least for a charge of identical devices) and if the ID does not change.
As far as I know -- No. For example, "USBSTOR\DISK&VEN_LG&PROD_USB_DRIVE&REV_1100\AA04012700014149&0" - is a device id of a usb stick. For all such given usb sticks device id will be the same. But Windows adds an instance id of device ("&0"), so it can distinguish them. If you plug two same usb sticks the device id of each of them will be different, for example: USBSTOR\DISK&VEN_LG&PROD_USB_DRIVE&REV_1100\AA04012700014149&0 USBSTOR\DISK&VEN_LG&PROD_USB_DRIVE&REV_1100\AA04012700014149&1
P.S. Sometimes Windows doesn't add instance of a device, if another same device isn't plugged. (I don't know why)

Resources