|| ||Alan Stern <firstname.lastname@example.org>|
|| ||USB development list <email@example.com>|
|| ||[linux-usb-devel] Upcoming changes and USB driver design patterns|
|| ||Wed, 14 Jan 2004 17:10:05 -0500 (EST)|
It looks like certain changes are looming for the USB system, and I wanted
to present a brief summary to make sure everyone knows about them and
agrees they ought to be made. Simple ones first...
The halted member of struct usb_device is going to change
to refuse. I posted another message about this a couple of
weeks ago and nobody has responded, so I guess that's okay.
Not surprising, since this feature is pretty much unused.
The children and maxchild members will go away. They are
meaningful only for hubs, and most of the information is
already present in the driver model's children lists.
The information missing in the driver model is the mapping from
port number to child device. That mapping will be turned around;
each struct usb_device will get a new member called portnum.
The state member of struct usb_device will be explicitly
_not_ under the protection of the serialize semaphore. Instead
there will be a single global spinlock to protect all the
states. Events like disconnection will be cause state to
change as soon as they are detected, without having to wait for
a driver to release usbdev->serialize.
Now a more interesting item, not yet entirely certain:
struct usb_device->serialize will become a read-write semaphore.
The serialize semaphore protects against major changes, things that would
involve binding/unbinding drivers or altering the device's physical state.
In particular, the following routines will require the caller to hold the
semaphore (or, in the new version, to have a write-lock):
Also, a driver is guaranteed that the semaphore will be held when its
probe() and disconnect() routines are called.
In general, a driver can acquire the serialize semaphore whenever it wants
to block one of the functions above temporarily. A good example would be
when calling usb_ifnum_to_if(); that function scans the interfaces in the
current configuration and the results would be meaningless if the
configuration unexpectedly changed.
There are some good reasons for making serialize into a read-write
semaphore. Duncan Sands reports that the usbfs implementation would be
greatly simplified. Also, if a device has multiple interfaces each with
its own driver, then the separate drivers might individually want to
prevent configuration changes without interfering with each other
activities; getting a read-lock is the natural way to do this.
Some longer-range changes:
Add a new usb_device_state: USB_STATE_RESET. Although it
doesn't correspond exactly to any state given in the USB spec
it could be very useful. A device is in this state while it
or a hub upstream from it is undergoing a reset.
The somewhat obscure reason for having this is that it allows us to
recover the devices attached to a hub when that hub is reset. Resetting a
hub disables all its ports; after the hub revives, each of its children
will have undergone the equivalent of a reset. Knowing that this has
happened, we will be able to retain the child device structures and their
driver bindings. They will resume activity rather than going through the
process of logical disconnect followed by logical connect -- which would
have the effect of replacing each device with a new incarnation, breaking
things like filesystem mounts.
This may turn out to be excessive design. I don't know that hubs need to
be reset at all often, and it might not be worth the trouble to do all
this. Furthermore, it would greatly complicate the set of possible state
transitions in the hub driver.
Add new callbacks to struct usb_driver to notify drivers that
their device is about to be reset or has just finished a reset.
I don't like the name "reset" for a callback as it implies that
the driver should reset the device. Maybe better names would
be "notify_reset" and "reinit" (or "notify_reinit").
As things stand now, we don't handle resets well. A device can have
multiple drivers bound to multiple interfaces; when any one of them
requests a reset it will affect all the others but they have no way to
know what's going on. Likewise, if a hub is reset it automatically has
the effect of resetting all the downstream devices, and the drivers need
to know about it.
The core will call notify_reset() for each driver bound to the device
before performing the reset and will call reinit() afterwards, assuming
the device survived the process intact. (If the device did not survive
the core will call disconnect() instead.) Drivers will be able to suspend
their activities, then reinitialize the device and resume where they left
Although I described this as a long-range change, there's no reason it
couldn't be implemented fairly soon. If a driver doesn't define a
notify_reset or reinit callback pointer, the core simply won't notify it.
That's exactly the same behavior we have now.
Design patterns for USB drivers
Many USB device drivers follow a design pattern that is typified by
usb-skeleton. The driver has a per-device private data structure which
includes an in-use semaphore and a refcount (or open_count). The
semaphore serves to prevent the disconnect() routine from running while
the driver is actively using the device. The refcount serves the usual
purpose of preventing deallocation of the private structure until nothing
is using it any more.
With the new changes, another pattern might work better. In particular,
there's no need for the private data structure to include an in-use
semaphore since usbdev->serialize (read-locked) would do just as well.
But in fact, the whole idea of using a semaphore to block disconnect() may
There has been a requirement that after a driver's disconnect() routine
returns, the driver will not try to access the device at all -- all URBs
must be unlinked and the struct usb_device may be deallocated at any time.
The core is now (or soon will be) sufficiently well protected that this
requirement is no longer needed. When disconnect() is called,
usbdev->state will already have been set to USB_STATE_NOTATTACHED so no
new URBs will be accepted, and all URBs in flight will have been unlinked.
All the driver really needs to do is make sure that the struct usb_device
isn't deallocated until it has finished cleaning up, and that can be
handled with usb_get_dev()/usb_put_dev().
This approach does have the disadvantage of complicating the driver's exit
routine. It would no longer be able to rely on the usb_deregister() call for
synchronization; instead it would have to wait until all the private data
structures had been freed. So perhaps it's not such a good idea. But
it's worth considering, and it would speed up disconnect processing.
This SF.net email is sponsored by: Perforce Software.
Perforce is the Fast Software Configuration Management System offering
advanced branching capabilities and atomic changes on 50+ platforms.
Free Eval! http://www.perforce.com/perforce/loadprog.html
To unsubscribe, use the last form field at: