Upcoming changes

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):

        usb_driver_claim_interface,
        usb_driver_release_interface,
        usb_disconnect,
        usb_new_device,
        usb_disable_device,
        usb_reset_configuration,
        usb_set_configuration,
        usb_reset_device

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
off.

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
be unnecessary.

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.

Alan Stern



-------------------------------------------------------
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
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to