Terrydaktal wrote:
Yes, I know how it usually works but the issue is the sources saying that the hub driver calls IoInvalidateDeviceRelations. How can it when it’s the xHCI driver that set up an interrupt for event ring 0 and knows the address of the event ring that it created in memory so it can read from it.
It’s not really that hard to understand, is it? Just because the XHCI
driver talks to the wire does not mean that it is responsible for all
the devices that happen to appear on the wire. I don’t know that this
is the exact sequence, but here’s one potential architecture. The HCD
(the xHCI driver) gets a signal from a physical hub saying “a new device
has arrived”. At that point, the HCD doesn’t know anything about it,
and it doesn’t even know the hub protocols. It has child drivers that
are hub drivers (one for each hub) to handle those details. It sends a
signal to the appropriate hub driver saying “hey, wake up, a device
arrived”. The hub driver then sends packets to the hub it controls
telling it to enumerate the device and assign an ID. The HCD happily
sends those packets and returns the results to the hub driver. At that
point, the hub driver knows the VID and PID for the new device, and
creates a child PDO.
If the interrupt is actually registered by the hub driver then it would still have to know the address of the event ring to handle it, how would the drivers exchange this information?
Interrupt ownership is totally irrelevant. This is a protocol driver
stack with multiple layers. The HCD doesn’t understand hub protocol.
It understands packet queues and packet scheduling. In a normal device
stack, the hub driver would submit an IRP to the HCD that remains
pending until some event happens. When a device appears, the HCD will
complete that request, telling the hub driver to do investigate.
If the xHCI driver is the one that registers the interrupt and reads the event on ring 0, what’s the point in cramming this into an IRP that causes the hub driver to invoke IoInvalidateDeviceRelations when it could just call it on the PDO of the hub driver itself, job done.
Encapsulation, division of labor, ease of future expansion. Each HCD
has many hubs. Each hub has many devices (including more hubs). Each
device has many interfaces. The best architecture lets each of those
things be handled by one driver that understands its one function, and
lets other drivers handle the other protocols. You can replace the HCD
with your own (for example, for USB-over-Ethernet) without having to
rewrite the entire device stack. As long as you know how to tickle the
USB hub driver, it can continue to do the same job.
The issue here is, the hub driver will now be the one thst responds to the event but now it needs to know which ports have had an insertion / removal on and to do that it needs to read a host controller register but only the xHCI driver knows where this is in memory.
It’s not a host controller register. It’s a packet from the hub. The
HCD treats it the same as every other packet, handing it off to the
driver that knows how to handle it. IRPs are cheap
Instead of the hub driver sending an IRP to perform this read action, the xHCI controller could do all the initialisation on the port and then send an IRP to the hub driver with the slot ID that has been added and removed and it will call IoInvalidateDeviceRelations and add and remove PDOs in the IRP it responds to that correspond to the slot IDs.
Why should the HCD have to know anything about hubs? A hub is just
another device, with its own request protocols. Let a hub driver handle
that. And remember that a lower device cannot send an IRP to an upper
device. Requests always flow down, towards the hardware.
Now there is the issue of usbstor. If the prefix + VID +PID + instanceID combo leads to the PnP manager loading usbstor.sys for the PDO it queries then how does usbstor.sys know that it needs to load a child and call IoInvalidateDeviceRelations itself? My only guess is that the prefix + VID + PID + InstanceID returned by the usb hub driver is a dummy one that will get usbstor.sys loaded by the PnP manager if the hub driver notices the PDO is a mass storage type. Then, usbstor.sys has to load the correct device, how does it know it has a child?
This is simply not how it works. The hub driver doesn’t know anything
about audio or video or mass storage or mice or keyboards. The hub
driver sees that a new device with VID=1234 and PID=5678 and DID=0100
and class 08 in its descriptors. The hub driver creates a new PDO and
gives it the following hardware IDs:
USB\VID_1234&PID_5678&DID_0100
USB\VID_1234&PID_5678
USB\Class_08&SubClass=02&Prot=50
USB\Class_08&SubClass=02
USB\Class_08
At that point, the hub driver calls IoInvalidateDeviceRelations to tell
PnP that there is a new PDO to deal with. At that point, the hub
driver’s job is through. It has done everything it needs to do.
Here, PnP takes over. It starts at the top of that list of IDs and
searches for INF files that match it. If there is no specific driver
for this device, the third ID, the one that identifies it as a generic
mass storage device, is found in usbstor.inf, so PnP loads usbstor.sys
and hands it the unique device identifier.
Usbstor.sys now creates URBs to go identify the device and start to
configure it. It sends requests to the hub driver, which (possibly)
reformats them and sends them to the HCD driver, which turns them into
scheduled packets. At some point, usbstor.sys will probably find a
volume on that device. It will create a child PDO with an identifier
like SCSI\My_Device_Name. That will trigger the loading of a driver
that understands SCSI protocol but doesn’t know anything about
communicating with hardware.
Every device knows its piece of the puzzle. As long as they follow the
interface rules, it becomes easy to add new types of devices, or to use
existing drivers with new buses.
And somehow, it needs to be able to correctly respond to IRPs that query the ID of the device. Do you reckon there is a mechanism in which it can send a get_descriptor request in an IRP to the xHCI driver and attach the slotID to perform the action on, it passes it down to the hub driver, the hub doesn’t implement and the xHCI driver responds.
Each layer has a well defined protocol. Usbstor accepts disk requests
at the top. It translates them into URBs for its device, which it hands
off to the hub driver. The hub driver might make some adjustments to
the request, and hands it off to the HCD.
It does seem like a bit of a tangle to me.
You just aren’t seeing the bigger picture.