Email or username:

Password:

Forgot your password?
Hector Martin

Let's talk about one reason why Asahi Linux doesn't have DisplayPort Alt Mode support yet.

I just spent the past 2 days trying to get the Apple Type-C PHY to work properly in the various alternate modes, including DisplayPort and USB3 host and device.

It's a painful mess. If you touch one part of the code, another part of the code breaks.

Here's the thing: Linux likes to abstract out hardware interfaces. Sometimes this works well, with stuff like I²C and SPI controllers.

Sometimes this doesn't work well, like the Linux mailbox subsystem, which is a poor excuse for abstracting out the concept of a hardware mailbox. Hardware mailboxes are not standards like I²C and SPI, it's a vague concept of similarly-designed hardware. There is no "cross-compatibility" between mailbox "controllers" and consumers. The subsystem exists only because someone decided to abstract out some common code (like queuing and polling/waiting for completion), but got the approach all wrong. When you want to abstract out common code, you want to do it as pluggable functions. The DRM subsystem does this well with its multiple components. What mailbox did is try to define a common interface around the hardware, abstracting out all possible variations through one common interface. That doesn't work.

We tried using mailbox for Asahi Linux, and after a bunch of time wasted in attempts to hack on the code and mailing list reviews, we gave up. I made the executive decision to drop mailbox. We now have an internal interface for Apple mailboxes, which only have Apple-specific consumer drivers anyway. It works much better, is overall less code than trying to work around the impedance mismatches of the mailbox subsystem, is more maintainable, and won't break when someone else touches the common code.

The story now repeats, but it's much, much worse. The players are the Linux Type-C subsystem, the PHY subsystem, DRM, USB, and more. This time there are actually semi-common drivers like dwc3 (the driver for the DesignWare USB3 host/device controller that Apple licensed) and tipd (the driver for the Texas Instruments Type-C port controller that Apple modified and commissioned a variant of), plus our own display/DCP drivers and Apple Type-C PHY drivers.

And the challenge is getting all these drivers to work together and drive the hardware in the right sequence to work, including complex/sudden hotplug cycles, mode switches, data role changes, live changes between USB and USB3+DP mode, the list goes on.

Here's how I spent the past hour:

- USB3 host + DP mode works on boot
- Hotplug doesn't work because my USB3 hub transiently starts up with the data role backwards (as if it were a host), and device/gadget mode is broken.
- Let's fix gadget mode. I need to change the USB3 PIPEHANDLER initialization, but right now it happens in usb3phy->power_on(), which is too early to know if we're device or host mode
- Let's move it to usb3phy->set_mode()
- After fixing some other stuff related to USB2, now SuperSpeed gadget mode works
- Try the hub coldplug again. It's completely broken in host mode.
- Turns out this broke because the dwc driver calls usb3phy->set_mode() twice with different but equivalent arguments for some bizarre reason
- Okay, add a flag so we only listen to the first call
- Now USB3 host mode works, but there's a 5-second delay in enumeration, so the PHY starts up in a bad state
- Diff register traces to see what happened
- Turns out usb3phy->set_mode() happens too late, after host controller soft-reset, and that's too late to work well.
- But usb3phy->power_on() is too early to know the mode, we're screwed
- ...but usb2phy->set_mode() is early enough for some reason

So now I have PIPEHANDLER setup (a USB3-relevant configuration) happening in the "USB2" PHY callback (which goes to the same shared Apple Type-C PHY driver anyway), just to make it work.

This is just the last debugging mess. It's all like this. Yesterday I found out a previous version of the code was only working due to a race between "mux configuration" and dwc3 initialization in a workqueue, synchronized by an accidentally load-bearing msleep(20) in dwc3. Yes, really. The fix for that is changing the tipd driver to do the mux set before setting up the Type-C data role (which is what triggers dwc3 init). tipd is its own hacky mess, since the hardware is too high-level for the Linux Type-C subsystem's design.

What makes this worse is that this setup is all reverse-engineered, so we have no documentation on what is supposed to work and what isn't. All we have is register traces from how macOS does it. But it's actually completely impractical to replicate them 1:1 in Linux, because the Linux cross-driver sync points are nowhere near enough for that. So we're left guessing and trying different interleaving orders and hoping that if something works, it's reliable, even though it's different to what macOS does.

(continued)

37 comments
Hector Martin

The Linux modular subsystem approach falls flat on its face here. The Type-C, USB, PHY, etc. drivers are too loosely coupled. Even if we get this to work, it's liable to break any time anyone changes common code and subtly changes the order of operations. It is not practically possible to build an overarching, top-down, abstracted environment for tying drivers together like this when the underlying hardware has much more tightly coupled requirements for how it is driven. And don't get me started on adding in PCIe into the mix once we need to get Thunderbolt to work...

The "PHY" concept is the most egregious offender. PHY hardware is as diverse as mailbox hardware, yet Linux thinks it's reasonable to abstract it behind a bunch of rigid operations like "init", "power_on", "set_mode", "set_media", "set_speed", "validate", "calibrate". Then you end up with unions like PHY configuration operations for MIPI DPHY, DP, LVDS, .... And all this starts becoming an intractable mess when you have stuff like a Type-C phy that talks USB2, USB3, DisplayPort, and Thunderbolt on one side, and has interfaces for a USB2+USB3 host/device controller (talking to the USB2 side and either nothing, the USB3 side, or a USB4 tunnel on its SuperSpeed side), a PCIe controller, and a bridge to a DisplayPort mux that then connects to an array of display controllers, and which has fun dependencies like having the USB3 controller hard-reset register as part of the PHY, and init sequences that are tightly coupled to how the other peripherals are driven, and if you get it wrong everything just breaks.

When there is no universal hardware spec for what interface a given type of PHY (never mind all types of PHYs) should have, in terms of operations and state machines, trying to abstract it out under such an interface in software is a path doomed to failure. It might work for simple PHYs for simple protocols of a decade or two ago, but vendors are shipping "do everything USB Type C can do including multiple protocols in parallel and tunneling all in one PHY" now and good luck with that.

What we really need is a bottom-up approach where a vendor-specific driver ties everything together. But Linux hates that approach with a passion, because it's what hacky vendor kernel forks do all the time. And yes, when done poorly, as they do, it sucks. But sometimes it's the only way to get anything sane. Linux does support this concept in at least some places: the ALSA ASoC subsystem supports machine drivers into which different codec/DMA/PCM drivers plug in, and that's how we tie together all the audio hardware for Asahi Linux.

But there's nothing like a "Type C top-level port management driver", nor are the underlying driver interfaces structured to allow this, nor fine-grained enough to compose properly. It would be a major new undertaking.

And then we come to the final problem: We don't have time to fight for a huge refactoring of kernel subsystems. If the kernel community were in general friendlier, discussion and patch submission weren't saddled with a ton of friction, etc., we could entertain the notion. But given past experiences with LKML threads... sorry, I'm not going there.

End result, this is highly annoying and demotivating to work on. That's why the person who started working on it mostly lost interest. Even the USB3 support we've shipped so far partially worked by accident (it has the 5-second delay bug and other issues), we just didn't dare touch it for fear of breaking it. And now we need to get all the other features to work without regressing anything.

Best I can hope right now is I make this barely work with the existing approach and a bunch of hacks, that upstream won't block any of it, and that nobody will touch the code and break it. Wish me luck.

And if you think you can help and are willing to fight the bureaucracy to make this less insane in Linux, please get in touch.

The Linux modular subsystem approach falls flat on its face here. The Type-C, USB, PHY, etc. drivers are too loosely coupled. Even if we get this to work, it's liable to break any time anyone changes common code and subtly changes the order of operations. It is not practically possible to build an overarching, top-down, abstracted environment for tying drivers together like this when the underlying hardware has much more tightly coupled requirements for how it is driven. And don't get me started...

gaytabase

@marcan friendship ended with everything is a file, only everything is a PHY matters now

Paul Barker

@marcan Would a multi-function driver (drivers/mfd) help?

For a certain SoC our team are working on at Renesas there are muxes, common configuration registers and ordering dependencies between different Ethernet subsystems. This can be handled by an MFD driver which has functions that can be called by the subsystem-specific drivers.

Hector Martin

@pbarker As far as I know mfd is mostly just a convenient way to have subdevices. We actually already had a fight with that maintainer because the SMC driver we have is a good fit for that subsystem, but he kept saying it wasn't because SMC doesn't "look" like most register-based drivers and he didn't understand why it made sense to use it. Still haven't had a chance to refactor and resubmit all that into what we finally agreed on (after a long mailing list discussion and a further IRC discussion).

But I don't think it fits here since in this case it's not even register-based devices involved only (e.g. tipd is on an I²C bus). We can't realistically nest all the dependencies under a parent driver. In fact for stuff like DCP which is behind a mux, that doesn't work at all since the mapping isn't 1:1.

And then the issue is getting things like the existing dwc3 driver to talk to us the way we need. No matter how you organize the device tree, that's still going to be a point of contention because it's a shared driver used by many other systems with different requirements.

@pbarker As far as I know mfd is mostly just a convenient way to have subdevices. We actually already had a fight with that maintainer because the SMC driver we have is a good fit for that subsystem, but he kept saying it wasn't because SMC doesn't "look" like most register-based drivers and he didn't understand why it made sense to use it. Still haven't had a chance to refactor and resubmit all that into what we finally agreed on (after a long mailing list discussion and a further IRC discussion).

Janne Grunau

@marcan Even the USB3 support we ship broke already at least twice due to upstream changes and we had to fix it.

Dave Polaschek

@marcan I have nothing to offer but the hope you can sort it out.

Dirkjan Ochtman

@marcan sounds unfortunate. Would it be feasible to get buy-in from a high-level maintainer (maybe Torvalds or one level below that) before you embark on the refactoring, so that you have a mandate you can use to (somewhat) shout down the naysayers during actual review?

doragasu

@marcan Sorry to read all that. Wish I could help, unfortunately kernel development is out of my reach. It must be really demotivating when the biggest problem is not technical and has no sane solution in sight. Hope the situation improves somehow.

Wouter van Heyst

@marcan ai that sounds rather painful :( As a happy Asahi user I thank you for your work and wish I could contribute more.

TellowKrinkle

@marcan Kind of curious, how do other laptops' USB-C ports work on Linux?

Hector Martin

@TellowKrinkle I'm not even sure if a full unified PHY for all protocols including USB4 like Apple's exists for any other vendor, and if it does (recent AMD stuff maybe?), it's all managed by firmware/ACPI and built to work with less tightly coupled drivers on the OS side.

Simpler implementations that just do USB3 and nothing else have the PHY managed by the xHCI controller and there is no mode switching or PHY driver. A basic DP/USB3 implementation as might exist in many non-Thunderbolt laptops and some recent embedded platforms would just use a dumb external mux to switch the data lines (which is what the simplistic Linux model is designed to work with, if it isn't just managed by ACPI). Again no PHY drivers, just a trivial mux driver. Intel Thunderbolt stuff is managed by external Thunderbolt controllers with piles of firmware and ACPI glue on the OS side, and again just looks like discrete controllers to the OS.

We are certainly the first platform trying to shove full OS-managed Type-C USB4 PHY support into Linux. Also I'm pretty sure Apple is the only vendor in existence that does full USB4+TBT3+DP+dual-role USB2/3 (including device role / gadget mode). Don't think any other laptop can do that (plug in a Mac into another non-TBT/USB4 host and the Mac enumerates as a SuperSpeed ethernet interface and exposes network services like SMB).

It doesn't help that Apple is using DesignWare USB3 controllers which are one of the worst in terms of quirks and hacks required. The list of special case hack toggles and properties in the devicetree bindings for dwc3 is ridiculous.

@TellowKrinkle I'm not even sure if a full unified PHY for all protocols including USB4 like Apple's exists for any other vendor, and if it does (recent AMD stuff maybe?), it's all managed by firmware/ACPI and built to work with less tightly coupled drivers on the OS side.

Simpler implementations that just do USB3 and nothing else have the PHY managed by the xHCI controller and there is no mode switching or PHY driver. A basic DP/USB3 implementation as might exist in many non-Thunderbolt laptops...

Sawyer Bergeron

@marcan I'm sure this is pure naivety on my part, but is "embrace out of tree until they come to their senses" reasonable at this point? I know that's an easy way to piss off some maintainers, but I can't imagine the people who _would_ take offense haven't already over something else

Hector Martin

@sawyerbergeron Major refactors are not viable out-of-tree. It would increase our rebase workload significantly. The only things viable to carry out-of-tree for longer periods of time are small fixes and whole new drivers, not major subsystem changes.

Sawyer Bergeron

@marcan oh yeah, I don't mean dragging the entire subsystem and all of the leaves along, I meant treating this all as one big driver and pulling in what you need from those subsystems. Guessing still too big a lift?

Hector Martin

@sawyerbergeron I'm leaning towards that option if the current approach ends up completely nonviable, but it would still require at least a pretty horrible patchset to a few other drivers to make them play nicely. E.g. we will probably have to add some kind of ugly hooks to dwc3 so it can call into the PHY driver in more, fine-grained places. And also get rid of its workqueue nonsense for role-switch actions, since running stuff in other threads is a recipe for disaster. The only way this is going to work and be robust is if all the mode changes and init/shutdown logic runs within a single (logical) thread (per Type C port/controller). And then we need to do something about tipd, though I'm starting to lean towards "fork/rewrite it", given how much Apple's variant is already diverging from upstream tipd, and how the non-Apple tipd driver is very barebones and has no altmode support at all anyway.

But then the question remains, is this ever going to be upstreamable. Because if it isn't, we lose long term.

@sawyerbergeron I'm leaning towards that option if the current approach ends up completely nonviable, but it would still require at least a pretty horrible patchset to a few other drivers to make them play nicely. E.g. we will probably have to add some kind of ugly hooks to dwc3 so it can call into the PHY driver in more, fine-grained places. And also get rid of its workqueue nonsense for role-switch actions, since running stuff in other threads is a recipe for disaster. The only way this is going...

Raven667

@marcan @sawyerbergeron I'm no hardware engineer so I'm talking out of my ass here, but the way you describe it it seems like forking the relevant driver(s) and making a hacky prototype would allow you to get something working and tested out the door, then you would know more exactly what the scope of work would be to refactor/rewrite in a production/upstream ready style and what kind of glue would be needed to do all the work that ACPI/firmware provide on other systems, trying to make it production ready from the jump seems like it's a lot harder than you'd like. I suppose the problem for you (and all those other vendors out there) is that if there is no time to rewrite it later, then the time you put in now is the only time you'll have to work on it with this intensity, once it works you'll have other top priorities so if you only end up with a hacky prototype then maintaining it will become burden that takes time away from a production ready version being developed.

@marcan @sawyerbergeron I'm no hardware engineer so I'm talking out of my ass here, but the way you describe it it seems like forking the relevant driver(s) and making a hacky prototype would allow you to get something working and tested out the door, then you would know more exactly what the scope of work would be to refactor/rewrite in a production/upstream ready style and what kind of glue would be needed to do all the work that ACPI/firmware provide on other systems, trying to make it production...

spyke

@marcan High level abstractions starting to leak -- nothing new there. Only few companies actually make drives for Linux (like Intel), so no wonder that you have a lot to improve and implement.

Hector Martin

@spyke Yeah, because ~all the downstream embedded (non-x86) vendors suck and nobody else tries to upstream anything or attempt to engineer anything to be upstreamed...

Sobex

@marcan one question.

Is the issue you are encountering likely also a pain for other USB 4 / Thunderbolt stacks drivers ?

Would there be a way to design a subsystem to deal with USB / Thunderbolt and alt mode independently from that stack, to actually make it possible to deal with those stacks as a whole ?

(That would mean a lot more code, but probably avoid the nonsensical issues you currently have)

knuxify

@marcan reminds me of a fun case that I still haven't 100% figured out on the exynos 4 - how to get USB OTG working reliably.

the microUSB controller is a mostly-standard DWC2 controller... except the host mode bits have been forcibly ripped out. instead, USB host mode is handled by a separate block on the SoC (hsic). in order to switch between the two, a bit needs to be flipped in a system control register (0 means host mode, 1 means device mode, or something like that - would have to check the code). *both controllers cannot be at the same time*; if dwc2 tries to do *anything* while the host mode is enabled, the driver freezes; the hsic driver is a bit nicer about this though.

the current implementation in the kernel does the necessary bit switch on the USB PHY driver; if the host mode PHY is enabled, it switches to host mode, if the device mode PHY is enabled it switches to device mode. this works fine if you only need one of the two, but if you need both, you get this fun interaction:

- USB device mode gets initialized first, PHY and dwc2
- USB host mode gets initialized second, flips the switch to host mode, inits PHY and hsic
- DWC2 attempts wake-up and fails, causing peripheral mode to not work; so only host mode works.

i ended up hacking around this by making the mode switch happen based on the state of an extcon connector (that is a whole mess in and of itself - there's at least 2 or 3 ways to declare such a dependency in DTS...), and it mostly works but still has the caveat that dwc2 is still technically running so if that driver tries to do any funny reset/resume/etc business and we're in host mode, it fill freeze up and break device mode until reboot

@marcan reminds me of a fun case that I still haven't 100% figured out on the exynos 4 - how to get USB OTG working reliably.

the microUSB controller is a mostly-standard DWC2 controller... except the host mode bits have been forcibly ripped out. instead, USB host mode is handled by a separate block on the SoC (hsic). in order to switch between the two, a bit needs to be flipped in a system control register (0 means host mode, 1 means device mode, or something like that - would have to check the...

knuxify

@marcan (if any smart kernel dev reads this genuinely do give advice lol, i wrote down the details in gitlab.com/exynos4-mainline/li)

Hector Martin

Sent an email to lkml & friends about this, but I'm not holding my breath that it's going to lead to a clear solution...

lore.kernel.org/lkml/fda8b831-

Xerz! :blobcathearttrans:

@marcan well, at least they won’t blame you for complaining behind their backs

kloenk

@marcan just write the new subsystem in rust for more shits and giggels

Janne Grunau

@marcan the 5 second USB device enumeration delay exists in the current kernel and was as far as I’m aware always there

Hector Martin

@janne Right, because it's always been broken. The new atcphy code fixes it and I'm trying not to break it again.

Janne Grunau

@marcan ah, I noticed yesterday with $SOME wip phy state that the 5s delay was still there. Good if it is fixed

Thomas A.

@marcan I'm planning to share this on the Linux subreddit, but I'm struggling coming up with a good title. Would "Marcan's Rant on the Current State of Linux Type-C Subsystem" be an okay title?

Hector Martin

@curioustommy It's not really a rant about the Type C subsystem itself. The problem isn't one subsystem, it's getting multiple subsystems to work together in ways they weren't designed to.

Maybe "When simple Linux subsystems collide with complex hardware, nothing works"?

calvin, ","

@marcan just curious, is there a reason why you can't just throw xnu+kexts into the IDA woodchipper? i thought that if someone else did it, made documentation about what it does, and someone else implements, it would maintain clean room?

Hector Martin

@cb This has nothing to do with the RE approach or clean room. You can't reverse engineer documentation for PHY hardware by decompiling drivers. The drivers themselves are just a bunch of magic register pokes.

We actually already have way more info than usual from the Apple drivers because they have surprisingly verbose debug logs, down to register and field names. There's nothing else to be gained by using a decompiler.

The hard part isn't figuring out what macOS does, it's 1) figuring out what the hardware actually requires, and what invariants have to be maintained, and 2) making it all work in the Linux model.

@cb This has nothing to do with the RE approach or clean room. You can't reverse engineer documentation for PHY hardware by decompiling drivers. The drivers themselves are just a bunch of magic register pokes.

We actually already have way more info than usual from the Apple drivers because they have surprisingly verbose debug logs, down to register and field names. There's nothing else to be gained by using a decompiler.

Sean M. Collins

@marcan all I can say is that I am very sympathetic to the frustration of trying to get an open source project to move in a different direction or a very hard architecture change. I can't help you in a technical sense but I have been there and felt similar.

JulianCalaby

@marcan In my unhelpful opinion, this sounds like it needs a "usb-c port" driver of some sort which is (initially) implementation-specific and covers the coordination: modes, timing, coordination, etc. With all the existing drivers being cored out to become "dumb" hardware abstractions which just do their "one" thing only and tell this coordinator when things change. It sounds like the "design" of the current subsystem assumes that the "MUX" thing _is_ the coordinator which works for simple cases and everything else is ACPI or "invisible" firmware.

I've spent a long time thinking about how to build a "tiny" laptop thing with a USB-C port and every time I've tried to come up with a way to do something more complicated than a hardwired host or device port, I can't see any way to communicate which mode to use to the driver for the device-capable USB controller. This assumes I've got a separate microcontroller to manage the USB-C port and are limited to USB2 only.

@marcan In my unhelpful opinion, this sounds like it needs a "usb-c port" driver of some sort which is (initially) implementation-specific and covers the coordination: modes, timing, coordination, etc. With all the existing drivers being cored out to become "dumb" hardware abstractions which just do their "one" thing only and tell this coordinator when things change. It sounds like the "design" of the current subsystem assumes that the "MUX" thing _is_ the coordinator which works for simple cases...

Hector Martin

@juliancalaby Right, the problem is the existing interface isn't even consistent, because it evolved from simpler hardware and is now a mess for more complex hardware.

There's a "USB Role Switch" interface which is part of the USB subsystem, and is called by both drivers in the "extcon" subsystem and the "usb/typec" subsystem. But that is just host/device, it doesn't cover any TypeC shenanigans (this started in the miniUSB/microUSB days). This is what actually triggers the USB controller to switch modes between host/device.

Then there is the TypeC mux interface, which is intended to just change modes for the TypeC pins. But it doesn't know anything about host/device roles, that is assumed to be independent.

Then there is the TypeC USB-PD stuff, which is intended to handle USB-PD interface details for chips which are basically USB-PD PHYs.

Then there's the reset subsystem, which provides out-of-band control over reset lines for peripherals.

And then there is the PHY interface, which just deals, in principle, with a single PHY for a single protocol in front of a single controller.

So this whole thing was designed for a system where you have discrete components:

-

USB/DP/etc controllers, which each interact (potentially) with their own PHY, completely under their control


-

A TypeC USB-PD PHY with the PD protocol managed in software


-

A dumb mux (and/or possibly retimer) chip to switch the data lines between backing PHYs


-

Some unrelated reset control, if necessary

And then the mess we have right now is:

-

The tipd driver handles the USB-PD controller chip, which is not just a PD PHY but actually autonomously does everything the TypeC subsystem wants to do manually, which doesn't work well with the subsystem. It also manages the retimer chips autonomously (but they are just retimers, not muxes). So it skips all those layers and calls into the mux and role switch layers directly.


-

The dwc3 driver handles the role switching and calls into the phy drivers for USB2/USB3, but only to an extent.


-

Our display controller driver delegates its stuff to DCP firmware, but DCP firmware calls back into the display controller driver for PHY management, so it has to forward that to the (DP) PHY subsystem.


-

Our atcphy driver tries to be all of a reset controller, all the PHYs, and USB-C mux, all at once, to tie everything together. But since the mux side doesn't know about host/device roles, and since the PHY side is split up into different logical PHYs, and since the order everything is called is not consistent, and since dwc3 insists on doing role switching in a workqueue asynchronously, and since it's actually tipd doing the calls to both the role switch and mux sides, it's a giant mess.

The ultimate problem here is there is a fixed hierarchy of calls. The design of the subsystems assumes the PD stuff will call into roleswitch and mux. But what we really need is a top-level management driver that just receives status notifications from the PD driver, and then drives the PHY config as needed, coordinating the roleswitch and DisplayPort HPD calls exactly when needed.

@juliancalaby Right, the problem is the existing interface isn't even consistent, because it evolved from simpler hardware and is now a mess for more complex hardware.

There's a "USB Role Switch" interface which is part of the USB subsystem, and is called by both drivers in the "extcon" subsystem and the "usb/typec" subsystem. But that is just host/device, it doesn't cover any TypeC shenanigans (this started in the miniUSB/microUSB days). This is what actually triggers the USB controller to switch...

JulianCalaby

@marcan My perspective on this is mostly USB2, but the same problem exists there too: some drivers do everything in-house, some have external PHY drivers that do ... something maybe involving a MUX thing, etc.

IMHO you should go further than just a "top-level management" driver coordinating things for the PD device and build something more like a "port" driver: status flows up from every device (driver) involved and control flows down to the various components and _nothing_ does anything without the go-ahead of this driver.

The problem then becomes that this would probably require significant refactoring of just about every driver involved, and worse, would probably require coring out most of the "logic" from the dwc3 driver, which is probably just hacked together "port" drivers for other SoCs.

@marcan My perspective on this is mostly USB2, but the same problem exists there too: some drivers do everything in-house, some have external PHY drivers that do ... something maybe involving a MUX thing, etc.

IMHO you should go further than just a "top-level management" driver coordinating things for the PD device and build something more like a "port" driver: status flows up from every device (driver) involved and control flows down to the various components and _nothing_ does anything without...

Go Up