The new USB API
The new USB 2.0 API consists of 4 functions. All
transfer types are managed using these functions. There is no longer
need for separate functions to setup INTERRUPT- and ISOCHRONOUS- transfers.
+--------------------------------------------------------------+
| |
| "usbd_transfer_setup" - This function will allocate all |
| necessary DMA memory and might |
| sleep! |
| |
| "usbd_transfer_unsetup" - This function will stop the USB |
| transfer, if it is currently |
| active and release all DMA |
| memory. |
| |
| "usbd_transfer_start" - This function will start a USB |
| transfer, if not already started.|
| This function is always non- |
| blocking, except when the USB |
| transfer is SYNCHRONOUS. ** |
| |
| "usbd_transfer_stop" - This function will stop a USB |
| transfer, if not already stopped.|
| The callback function will be |
| called before this function |
| returns. This function is always |
| non-blocking. ** |
| |
| ** These functions must be called with the private driver's |
| lock locked, e.g. Giant for drivers using splxxx. |
| |
+--------------------------------------------------------------+
Reference: ../isdn4bsd/sources/src/sys/dev/usb2/_usb_transfer.c
The rule is that one must setup the USB transfer from the callback,
which is required for non-blocking operation, in the end. This
behaviour is also very practical when writing USB device drivers,
because it is easy to make a loop, starting the next transfer from the
previous. Simply reorder the labels! The callback's lock is locked by
the caller. This solves synchronization problems related to stopping
USB transfers!
/*
* A simple USB callback state-machine:
*
* +->-----------------------+
* | |
* +-<-+-------[tr_setup]--------+-<-+-<-[start/restart]
* | |
* | |
* | |
* +------>-[tr_transferred]---------+
* | |
* +--------->-[tr_error]------------+
*/
void
usbd_default_callback(struct usbd_xfer *xfer)
{
USBD_CHECK_STATUS(xfer);
tr_setup:
/* setup xfer->length, xfer->frlengths, xfer->nframes
* and write data to xfer->buffer if any
*/
/**/
usbd_start_hardware(xfer);
return;
tr_transferred:
/* read data from xfer->buffer if any */
tr_error:
/* print error message */
return;
}
NOTE: all of the control transfer now resides in the buffer pointed to
by "xfer->buffer", including the request. This is better and saves memory.
1) Something that one should be aware of is that, all USB callbacks
support recursation. That means one can start/stop whatever transfer
from the callback of another transfer one desires. Also the transfer
that is currently called back. Recursion is handled like this, that
when the callback that wants to recurse returns, it is called one more
time.
2) After that the "tr_setup" label has been jumped in the callback,
one can always depend on that "tr_error" or "tr_transferred" will get
jumped afterwards. Always!
3) sleeping functions can only be called from the attach routine of
the driver. Else one should not use sleeping functions unless one has
to. It is very difficult with sleep, because one has to think that
the device might have detached when it returns from sleep.
4) I see that many USB drivers use synchronous configuration. That is
wrong. All of this must be made asynchronous. In that regard one
should write a small helper structure/library, that will make this
easy to implement for the drivers. I have a ring-buffer system for
handling device configuration for the ISDN drivers. I think we will
move it over to using mbufs, and then add some more fields, and
options, to make it more generic. Like some "wakeup" this pointer
after finished this and this command.
USB device driver examples:
../isdn4bsd/sources/src/sys/i4b/layer1/ihfc2/i4b_regdata.h
and
../isdn4bsd/sources/src/sys/i4b/layer1/ihfc2/i4b_wibusb.h
--HPS