On Fri, 16 Sep 2005, David Brownell wrote:

> Remember that root hubs, unlike "normal" hubs, expose _three_ faces (not two)
> through the driver model:  the controller (e.g. a PCI or platform device); the
> root hub usb_device; and the root hub usb_interfacee.  Plus, no upstream USB
> link exists; USB gets morphed to some other bus in silicon ...
> 
> So if the HC (PCI/platform device) is the upstream link, then it'll be
> be power managed as a non-USB device.  As one would expect.
> 
> But for the downstream link ... I think the choice between the two is
> almost arbitrary.  And it's less confusing to have hub->suspend call
> hcd->hub_suspend than to special-case hub support into device suspend.  :)

I think I see where our thinking diverges...  Mostly it comes down to what 
"suspend the root hub" really means.

> > which 
> > implies that the bus->op->hub_suspend method call in __usb_suspend_device
> > should stay where it is.  Moving the method call to the hub_suspend
> > function is wrong, because it would mean that suspending a hub's interface
> > has the unexpected side-effect of suspending the whole hub device (for
> > root hubs)!  (And thereby suspending that entire bus!) 
> 
> That's not "wrong" ... it's 100% consistent with suspending any other
> hub interface!  When it finishes suspending, the down-stream links are
> all suspended; not the upstream one.  (True for both faces of the
> downstream link... though the usb_interface is always suspended first.)

Consider what will happen when we remove _all_ the recursion from the USB
suspend/resume routines.  Suspending an external hub's interface won't
have any effect on the downstream ports; they will be affected only when
you call usb_suspend_device for the children.  The fact that your proposed
root-hub behavior is consistent with the current state of affairs is a
coincidence, an artifact of the way you originally wrote the suspend code 
to include the recursion.

What happens when an external hub's interface is suspended but the
downstream ports are still active?  Answer: There's no status URB queued,
so usbcore won't be aware of port status changes, but the hub will
continue to relay packets.  Root hubs should be capable of the same sort 
of behavior.  (Until very recently, unbinding the hub driver from a hub 
via sysfs would leave it in pretty much this state.)


Part of the conceptual difficulty here is that our implementation of
virtual root hubs isn't as accurate as it could be.  When an external hub
has been suspended, you can't communicate with it at all (apart from
wakeup signals).  It won't respond to Get-Descriptor or Get-Port-Status
requests, for example.  (Assuming usbcore would allow you to send such
requests in the first place, rather than just returning -EHOSTUNREACH.)  
But root hubs _do_ respond to such requests when they are suspended.  The
code in hcd.c that handles Get-Descriptor for root hubs doesn't even know
when the hub has been suspended, and the port-status code in HCDs (like in
uhci-hub.c) doesn't bother to check.

In practice this flaw in the emulation doesn't matter, since usbcore
doesn't let you send URBs to suspended devices.  But it does confuse
our thinking.  So for the sake of argument, let's pretend that we have
changed the emulation and removed the -EHOSTUNREACH check.  From now on,
URBs sent to suspended root hubs return -EPROTO errors, as do URBs sent to 
suspended external hubs.

Now look at how your proposal results in different behavior for root hubs 
and external hubs.  If you suspend an external hub's interface, you can 
still send Get-Descriptor to the hub itself and get a reply.  But if you 
suspend a root hub's interface, a Get-Descriptor request will return 
-EPROTO because hcd->hub_suspend was called.


At this point, you'll probably want to say that my analysis is wrong, that
virtual root hubs are effectively split into two halves.  The upstream
half is the HC device, and what we've been calling the root hub is really
only the downstream usb_device half.  To fully suspend the root hub, it's
necessary to suspend both halves.

I don't like this explanation.  I think the HC device should embody the
parent bus (e.g., the PCI) aspects of a host controller and the root-hub
device should embody the USB aspects -- i.e., its virtual hub.  
Suspending the usb_device should cause the virtual hub to behave like
a physically-suspended external hub.  Suspending the HC device should
cause it to behave like any other suspended PCI device.  It shouldn't be
necessary to put the controller in D3 (for example) just in order to fully
suspend the usb_device.  Nor should it be necessary to make two distinct 
suspend calls to suspend a root hub, seeing as how one call suffices for 
external hubs.

Or put it another way.  You can fully suspend an external hub by calling
usb_suspend_device(hdev); that single call should suffice even when hdev
is a root hub.


Okay, that was a long speech.  But these are subtle and tricky concepts, 
and it's important to state them clearly and agree on them.


> This is easier to fix than the hub/device parenting stuff...

You lost me there.  What hub/device parenting stuff are you talking about?


> > Likewise, resuming 
> > the root-hub device wouldn't work so long as the interface remained
> > suspended -- whereas it would work with external hubs.
> 
> But we had previously discussed the need to change that behavior so
> that resuming a hub need **NOT** resume all the downstream links...
> again (testing is the only hard part of that patch).

True.  But resuming a hub and calling usb_resume_device for its children
_should_ cause those downstream links to become active, even if the hub's 
interface remains suspended.  Your proposal would violate this for root 
hubs.  Whereas my proposal is consistent with both behaviors.


> > However I like your clean definition of CONIFG_USB_SUSPEND, if you add the
> > proviso that (a) applies only to physical, non-root, devices.
> 
> That is, we expect all HCDs to support "global(-to-this-bus) suspend"?

Just call it "bus-wide suspend"; I won't mind the non-standard 
terminology.  :-)

I'm not sure what you're asking.  An HCD doesn't need anything special to
support bus-wide suspend.  All it has to do is stop the controller from
transmitting packets.  That's an inevitable side-effect of suspending the
root hub.  (In fact, although the USB spec doesn't spell this out fully,
what it calls "global suspend" is really nothing more than a root-hub
suspend.)

> Yes; usbcore will always use hcd->hub_suspend().  Today it doesn't.

It doesn't if USB_SUSPEND isn't set, right.  But it should, so that the 
HCDs don't need to take special actions.  Yes.


> Another way to describe the difference would then be:  (a) it's no longer
> just root hubs that will suspend downstream devices, all hubs will do that.

Yes.  Sort of.

With USB_SUSPEND set, any hub will be able to suspend any child.  Without
it only root hubs can suspend anything, and they won't support suspending
individual children -- only all their children together.

It's clearer to say that with USB_SUSPEND set, usb_suspend_device will
work on any device.  Otherwise it will work only on root hubs.

Alan Stern



-------------------------------------------------------
SF.Net email is sponsored by:
Tame your development challenges with Apache's Geronimo App Server. Download
it for free - -and be entered to win a 42" plasma tv or your very own
Sony(tm)PSP.  Click here to play: http://sourceforge.net/geronimo.php
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to