Latest driver info, v.1.6.1, June 2006; Press here to show the document (HTML)
Old driver info, v.1.6.0, April 2006; Press here to show the document (HTML)
Old driver info, v.1.5.9.r3, February 2006; Press here to show the document (HTML)
Old driver info, v.1.5.9, February 2006; Press here to show the document (HTML)
Old driver info, v.1.5.4, September 2005; Press here to show the document (HTML)
Code reference for the new USB stack and device drivers; Press here to show the document (PDF)
Overview over the new USB API; Press here to show the document (HTML)
Release directory; Press here to show the directory

This directory also contains driver snapshots.

 


Press here to show the print version.

 

Hi,



How to get the latest sources:
svn --username anonsvn --password anonsvn \
      checkout svn://svn.turbocat.net/i4b
#
# The following commands will
# install the driver on FreeBSD:
#
cd i4b/trunk/i4b/FreeBSD.usb
make S=../src package
make install

More about the driver:
On this page one can find tarballs of my new USB driver for FreeBSD 5/6/7. This USB driver was forked off the FreeBSD USB driver in 2004 and built up from scratch. The main motivation for this was to get the USB part of my ISDN driver working properly. Back then I was moving my ISDN driver out of the Giant lock, and realized that it was very difficult to get my ISDN driver out of Giant, as long as the USB driver did only support the Giant lock.

+-----------------------------------------------+
|                                               |
|  Locking order alternatives:                  |
|                                               |
|  1) Lock Giant, then lock private lock        |
|                                               |
|  or                                           |
|                                               |
|  2) Lock private lock, then lock Giant        |
|                                               |
|  but not both!                                |
|                                               |
+-----------------------------------------------+

For my implementation I have chosen alternative 2. That means that the Giant lock or the USB controller lock is always locked after that the private lock is locked. In my driver each USB controller has each its lock.
+--------------------+--------------------+
|  USB controller 0  |  USB controller 1  |
|      Lock 0        |      Lock 1        |
+--------------------+--------------------+

When a USB controller calls back the USB device driver it must release its lock before locking the private lock of the USB device driver. In other words the system will be unlocked for a short period of time. During this short period of time, anything can happen. The three most important things to consider is what happens if the callback is stopped, restarted or freed. Here is how I have solved this:
/*
 * this function is called when a USB 
 * transfer does not complete in time
 */
static void
uhci_timeout(struct usbd_xfer *xfer)
{
        struct usbd_callback_info info[1];
        uhci_softc_t *sc = xfer->usb_sc;

        DPRINTF(("xfer=%p\n", xfer));

        /* 
         * the following lock should be locked
         * by the caller of "uhci_timeout()" to
         * stay clear of synchronization 
         * problems:
         */
        mtx_lock(&sc->sc_bus.mtx);

        /* 
         * the following function will unlink
         * all hardware structures from memory
         * and wait a little
         */
        uhci_device_done(xfer, USBD_TIMEOUT);

        /* 
         * queue callback 
         */
        info[0].xfer = xfer;
        info[0].refcount = xfer->usb_refcount;

        /*
         * USB transfers can share the same 
         * piece of memory, which is protected
         * by a single "memory_refcount".
         * When the memory refount reaches zero
         * the memory is freed, but not before!
         */
        xfer->usb_root->memory_refcount++;

        /*
         * one must unlock the USB controller lock
         * before calling any callbacks:
         */
        mtx_unlock(&sc->sc_bus.mtx);

        /*
         * call the callbacks
         */
        usbd_do_callback(&info[0],&info[1]);

        return;
}

Reference: "src/sys/dev/usb2/_uhci.c"

/*---------------------------------------------------------------------------*
 *      usbd_do_callback
 *
 * This function is used to call back a list of USB callbacks. 
 *---------------------------------------------------------------------------*/
void
usbd_do_callback(struct usbd_callback_info *ptr, 
                 struct usbd_callback_info *limit)
{
    struct usbd_xfer *xfer;

    if(limit < ptr)
    {
        /* parameter order switched */
        register void *temp = ptr;
        ptr = limit;
        limit = temp;
    }

    while(ptr < limit)
    {
        xfer = ptr->xfer;

        /*
         * During the unlocked period, the
         * transfer can be restarted by 
         * another thread, which must be
         * checked here:
         */
        mtx_lock(xfer->priv_mtx);

        /* if the "usb_refcount" changed the transfer 
         * was stopped/restarted 
         */
        if(xfer->usb_refcount == ptr->refcount)
        {
            /* call callback */
            __usbd_callback(xfer);
        }
        /* 
         * else already called back !
         */
        mtx_unlock(xfer->priv_mtx);

        usbd_drop_refcount(xfer->usb_root);

        ptr++;
    }
    return;
}

Reference: "src/sys/dev/usb2/usb_transfer.c"

/*---------------------------------------------------------------------------*
 *      usbd_drop_refcount 
 *
 * This function is called from various places, and its job is to
 * free the memory holding a set of "transfers", when it 
 * is safe to do so.
 *---------------------------------------------------------------------------*/
static void
usbd_drop_refcount(struct usbd_memory_info *info)
{
    u_int8_t free_memory;

    mtx_lock(info->usb_mtx);

    KASSERT(info->memory_refcount != 0, ("Invalid memory reference count!\n"));

    free_memory = ((--(info->memory_refcount)) == 0);

    mtx_unlock(info->usb_mtx);

    if(free_memory)
    {
        /*
         * the USB device driver can optionally be
         * called back when the memory is finally freed:
         */
        if(info->priv_func)
        {
            (info->priv_func)(info);
        }
        usb_free_mem(info->memory_base, info->memory_size);
    }
    return;
}

Reference: "src/sys/dev/usb2/usb_transfer.c"

One might question wether it is really neccesary to redesign a whole driver just to get it out of Giant, but the answer is yes.

Resources


The FreeBSD USB mailing list: freebsd-usb@freebsd.org.

END

This document was last updated on Thu Jul 10 11:27:12 CEST 2008.