| | |
||||||||||||||||||
The new USB APIThe 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 | ||||||||||||||||||||
This document was last updated on Wed Oct 15 08:55:57 CEST 2008.
|