kernel/netgraph7: Sync ubt and ubtbcmfw with current FreeBSD.
authorSascha Wildner <saw@online.de>
Sun, 13 Jul 2014 18:47:23 +0000 (20:47 +0200)
committerSascha Wildner <saw@online.de>
Sun, 13 Jul 2014 18:47:23 +0000 (20:47 +0200)
Also make them compile.

We need those drivers as of FreeBSD >=8 here since that's when
FreeBSD switched to usb4bsd.

sys/netgraph7/bluetooth/drivers/ubt/Makefile [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/ubt/ng_ubt.c
sys/netgraph7/bluetooth/drivers/ubt/ng_ubt_var.h
sys/netgraph7/bluetooth/drivers/ubtbcmfw/Makefile [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c

diff --git a/sys/netgraph7/bluetooth/drivers/ubt/Makefile b/sys/netgraph7/bluetooth/drivers/ubt/Makefile
new file mode 100644 (file)
index 0000000..ae63532
--- /dev/null
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.2 2003/03/22 23:44:34 max Exp $
+# $FreeBSD: head/sys/modules/netgraph/bluetooth/ubt/Makefile 192909 2009-05-27 16:43:40Z thompsa $
+
+CFLAGS+=       -I${.CURDIR}/../../include
+
+KMOD=          ng_ubt
+SRCS=          ng_ubt.c opt_bus.h opt_usb.h device_if.h bus_if.h \
+               usb_if.h usbdevs.h
+
+.include <bsd.kmod.mk>
index 3dbbea3..0b6abfb 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*-
- * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * SUCH DAMAGE.
  *
  * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
- * $FreeBSD: src/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c,v 1.33 2007/06/23 04:34:38 imp Exp $
+ * $FreeBSD: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c 260482 2014-01-09 15:31:44Z adrian $
  */
 
+/*
+ * NOTE: ng_ubt2 driver has a split personality. On one side it is
+ * a USB device driver and on the other it is a Netgraph node. This
+ * driver will *NOT* create traditional /dev/ enties, only Netgraph 
+ * node.
+ *
+ * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
+ *
+ * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
+ *    by USB for any USB request going over device's interface #0 and #1,
+ *    i.e. interrupt, control, bulk and isoc. transfers.
+ * 
+ * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
+ *    and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
+ *    pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
+ *    think of it as a spin lock.
+ *
+ * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
+ *
+ * 1) USB context. This is where all the USB related stuff happens. All
+ *    callbacks run in this context. All callbacks are called (by USB) with
+ *    appropriate interface lock held. It is (generally) allowed to grab
+ *    any additional locks.
+ *
+ * 2) Netgraph context. This is where all the Netgraph related stuff happens.
+ *    Since we mark node as WRITER, the Netgraph node will be "locked" (from
+ *    Netgraph point of view). Any variable that is only modified from the
+ *    Netgraph context does not require any additonal locking. It is generally
+ *    *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
+ *    grab any lock in the Netgraph context that could cause de-scheduling of
+ *    the Netgraph thread for significant amount of time. In fact, the only
+ *    lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
+ *    Also make sure that any code that is called from the Netgraph context
+ *    follows the rule above.
+ *
+ * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
+ *    NOT allowed to grab any lock that could cause de-scheduling in the
+ *    Netgraph context, and, USB requires us to grab interface lock before
+ *    doing things with transfers, it is safer to transition from the Netgraph
+ *    context to the Taskqueue context before we can call into USB subsystem.
+ *
+ * So, to put everything together, the rules are as follows.
+ *     It is OK to call from the USB context or the Taskqueue context into
+ * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
+ * it is allowed to call into the Netgraph context with locks held.
+ *     Is it *NOT* OK to call from the Netgraph context into the USB context,
+ * because USB requires us to grab interface locks, and, it is safer to
+ * avoid it. So, to make things safer we set task flags to indicate which
+ * actions we want to perform and schedule ubt_task which would run in the
+ * Taskqueue context.
+ *     Is is OK to call from the Taskqueue context into the USB context,
+ * and, ubt_task does just that (i.e. grabs appropriate interface locks
+ * before calling into USB).
+ *     Access to the outgoing queues, task flags and hook pointer is
+ * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
+ * sc_ng_mtx should really be a spin lock (and it is very likely to an
+ * equivalent of spin lock due to adaptive nature of FreeBSD mutexes).
+ *     All USB callbacks accept softc pointer as a private data. USB ensures
+ * that this pointer is valid.
+ */
+
+#include <sys/stdint.h>
 #include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
 #include <sys/systm.h>
-#include <sys/bus.h>
-#include <sys/conf.h>
-#include <sys/endian.h>
-#include <sys/filio.h>
-#include <sys/fcntl.h>
-#include <sys/mbuf.h>
-#include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/bus.h>
 #include <sys/module.h>
-#include <sys/poll.h>
-#include <sys/uio.h>
-#include <machine/bus.h>
-
-#include <dev/usb/usb.h>
-#include <dev/usb/usbdi.h>
-#include <dev/usb/usbdi_util.h>
-#include <dev/usb/usbdivar.h>
-
-#include "ng_message.h"
-#include "netgraph.h"
-#include "ng_parse.h"
-#include "bluetooth/include/ng_bluetooth.h"
-#include "bluetooth/include/ng_hci.h"
-#include "bluetooth/include/ng_ubt.h"
-#include "bluetooth/drivers/ubt/ng_ubt_var.h"
+#include <sys/lock.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
 
 #include "usbdevs.h"
+#include <bus/u4b/usb.h>
+#include <bus/u4b/usbdi.h>
+#include <bus/u4b/usbdi_util.h>
 
-/*
- * USB methods
- */
-
-static device_probe_t ubt_match;
-static device_attach_t ubt_attach;
-static device_detach_t ubt_detach;
-
-static device_method_t ubt_methods[] = {
-       /* Device interface */
-       DEVMETHOD(device_probe,         ubt_match),
-       DEVMETHOD(device_attach,        ubt_attach),
-       DEVMETHOD(device_detach,        ubt_detach),
-
-       DEVMETHOD_END
-};
-
-static driver_t ubt_driver = {
-       "ubt",
-       ubt_methods,
-       sizeof(struct ubt_softc)
-};
-
-static devclass_t ubt_devclass;
-
-static int         ubt_modevent                  (module_t, int, void *);
-
-static usbd_status ubt_request_start      (ubt_softc_p);
-static void        ubt_request_complete   (usbd_xfer_handle, 
-                                          usbd_private_handle, usbd_status);
-static void        ubt_request_complete2  (node_p, hook_p, void *, int);
+#define        USB_DEBUG_VAR usb_debug
+#include <bus/u4b/usb_debug.h>
+#include <bus/u4b/usb_busdma.h>
 
-static usbd_status ubt_intr_start        (ubt_softc_p);
-static void        ubt_intr_complete      (usbd_xfer_handle, 
-                                          usbd_private_handle, usbd_status);
-static void        ubt_intr_complete2     (node_p, hook_p, void *, int); 
-
-static usbd_status ubt_bulk_in_start     (ubt_softc_p);
-static void        ubt_bulk_in_complete   (usbd_xfer_handle, 
-                                          usbd_private_handle, usbd_status);
-static void        ubt_bulk_in_complete2  (node_p, hook_p, void *, int);
+#include <sys/mbuf.h>
+#include <sys/taskqueue.h>
 
-static usbd_status ubt_bulk_out_start     (ubt_softc_p);
-static void        ubt_bulk_out_complete  (usbd_xfer_handle, 
-                                          usbd_private_handle, usbd_status);
-static void        ubt_bulk_out_complete2 (node_p, hook_p, void *, int); 
+#include <netgraph7/ng_message.h>
+#include <netgraph7/netgraph.h>
+#include <netgraph7/ng_parse.h>
+#include <netgraph7/bluetooth/include/ng_bluetooth.h>
+#include <netgraph7/bluetooth/include/ng_hci.h>
+#include <netgraph7/bluetooth/include/ng_ubt.h>
+#include <netgraph7/bluetooth/drivers/ubt/ng_ubt_var.h>
 
-static usbd_status ubt_isoc_in_start      (ubt_softc_p);
-static void        ubt_isoc_in_complete   (usbd_xfer_handle, 
-                                          usbd_private_handle, usbd_status);
-static void        ubt_isoc_in_complete2  (node_p, hook_p, void *, int);
+static int             ubt_modevent(module_t, int, void *);
+static device_probe_t  ubt_probe;
+static device_attach_t ubt_attach;
+static device_detach_t ubt_detach;
 
-static usbd_status ubt_isoc_out_start     (ubt_softc_p);
-static void        ubt_isoc_out_complete  (usbd_xfer_handle, 
-                                          usbd_private_handle, usbd_status);
-static void        ubt_isoc_out_complete2 (node_p, hook_p, void *, int);
+static void            ubt_task_schedule(ubt_softc_p, int);
+static task_fn_t       ubt_task;
 
-static void        ubt_reset              (ubt_softc_p);
-
-/*
- * Netgraph methods
- */
+#define        ubt_xfer_start(sc, i)   usbd_transfer_start((sc)->sc_xfer[(i)])
 
+/* Netgraph methods */
 static ng_constructor_t        ng_ubt_constructor;
 static ng_shutdown_t   ng_ubt_shutdown;
 static ng_newhook_t    ng_ubt_newhook;
@@ -139,7 +154,8 @@ static const struct ng_parse_struct_field   ng_ubt_node_qlen_type_fields[] =
        { "qlen",  &ng_parse_int32_type, },
        { NULL, }
 };
-static const struct ng_parse_type              ng_ubt_node_qlen_type = {
+static const struct ng_parse_type              ng_ubt_node_qlen_type =
+{
        &ng_parse_struct_type,
        &ng_ubt_node_qlen_type_fields
 };
@@ -155,61 +171,64 @@ static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] =
        { "ierrors",    &ng_parse_uint32_type, },
        { NULL, }
 };
-static const struct ng_parse_type      ng_ubt_node_stat_type = {
+static const struct ng_parse_type              ng_ubt_node_stat_type =
+{
        &ng_parse_struct_type,
        &ng_ubt_node_stat_type_fields
 };
 
 /* Netgraph node command list */
-static const struct ng_cmdlist ng_ubt_cmdlist[] = {
+static const struct ng_cmdlist                 ng_ubt_cmdlist[] =
 {
-       NGM_UBT_COOKIE,
-       NGM_UBT_NODE_SET_DEBUG,
-       "set_debug",
-       &ng_parse_uint16_type,
-       NULL
-},
-{
-       NGM_UBT_COOKIE,
-       NGM_UBT_NODE_GET_DEBUG,
-       "get_debug",
-       NULL,
-       &ng_parse_uint16_type
-},
-{
-       NGM_UBT_COOKIE,
-       NGM_UBT_NODE_SET_QLEN,
-       "set_qlen",
-       &ng_ubt_node_qlen_type,
-       NULL
-},
-{
-       NGM_UBT_COOKIE,
-       NGM_UBT_NODE_GET_QLEN,
-       "get_qlen",
-       &ng_ubt_node_qlen_type,
-       &ng_ubt_node_qlen_type
-},
-{
-       NGM_UBT_COOKIE,
-       NGM_UBT_NODE_GET_STAT,
-       "get_stat",
-       NULL,
-       &ng_ubt_node_stat_type
-},
-{
-       NGM_UBT_COOKIE,
-       NGM_UBT_NODE_RESET_STAT,
-       "reset_stat",
-        NULL,
-       NULL
-},
-{ 0, }
+       {
+               NGM_UBT_COOKIE,
+               NGM_UBT_NODE_SET_DEBUG,
+               "set_debug",
+               &ng_parse_uint16_type,
+               NULL
+       },
+       {
+               NGM_UBT_COOKIE,
+               NGM_UBT_NODE_GET_DEBUG,
+               "get_debug",
+               NULL,
+               &ng_parse_uint16_type
+       },
+       {
+               NGM_UBT_COOKIE,
+               NGM_UBT_NODE_SET_QLEN,
+               "set_qlen",
+               &ng_ubt_node_qlen_type,
+               NULL
+       },
+       {
+               NGM_UBT_COOKIE,
+               NGM_UBT_NODE_GET_QLEN,
+               "get_qlen",
+               &ng_ubt_node_qlen_type,
+               &ng_ubt_node_qlen_type
+       },
+       {
+               NGM_UBT_COOKIE,
+               NGM_UBT_NODE_GET_STAT,
+               "get_stat",
+               NULL,
+               &ng_ubt_node_stat_type
+       },
+       {
+               NGM_UBT_COOKIE,
+               NGM_UBT_NODE_RESET_STAT,
+               "reset_stat",
+               NULL,
+               NULL
+       },
+       { 0, }
 };
 
 /* Netgraph node type */
-static struct ng_type  typestruct = {
-       .version =      NG_ABI_VERSION,
+static struct ng_type  typestruct =
+{
+       .version =      NG_ABI_VERSION,
        .name =         NG_UBT_NODE_TYPE,
        .constructor =  ng_ubt_constructor,
        .rcvmsg =       ng_ubt_rcvmsg,
@@ -218,1607 +237,1203 @@ static struct ng_type typestruct = {
        .connect =      ng_ubt_connect,
        .rcvdata =      ng_ubt_rcvdata,
        .disconnect =   ng_ubt_disconnect,
-       .cmdlist =      ng_ubt_cmdlist  
+       .cmdlist =      ng_ubt_cmdlist
 };
 
-/*
- * Module
- */
-
-DRIVER_MODULE(ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, NULL);
-MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
-MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
-MODULE_DEPEND(ubt, usb, 1, 1, 1);
-
-
 /****************************************************************************
  ****************************************************************************
  **                              USB specific
  ****************************************************************************
  ****************************************************************************/
 
-/*
- * Load/Unload the driver module
- */
+/* USB methods */
+static usb_callback_t  ubt_ctrl_write_callback;
+static usb_callback_t  ubt_intr_read_callback;
+static usb_callback_t  ubt_bulk_read_callback;
+static usb_callback_t  ubt_bulk_write_callback;
+static usb_callback_t  ubt_isoc_read_callback;
+static usb_callback_t  ubt_isoc_write_callback;
 
-static int
-ubt_modevent(module_t mod, int event, void *data)
-{
-       int     error;
-
-       switch (event) {
-       case MOD_LOAD:
-               error = ng_newtype(&typestruct);
-               if (error != 0)
-                       printf(
-"%s: Could not register Netgraph node type, error=%d\n",
-                               NG_UBT_NODE_TYPE, error);
-               else
-                       error = usbd_driver_load(mod, event, data);
-               break;
-
-       case MOD_UNLOAD:
-               error = ng_rmtype(&typestruct);
-               if (error == 0)
-                       error = usbd_driver_load(mod, event, data);
-               break;
-
-       default:
-               error = EOPNOTSUPP;
-               break;
-       }
-
-       return (error);
-} /* ubt_modevent */
+static int             ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
+static int             ubt_isoc_read_one_frame(struct usb_xfer *, int);
 
 /*
- * Probe for a USB Bluetooth device
+ * USB config
+ * 
+ * The following desribes usb transfers that could be submitted on USB device.
+ *
+ * Interface 0 on the USB device must present the following endpoints
+ *     1) Interrupt endpoint to receive HCI events
+ *     2) Bulk IN endpoint to receive ACL data
+ *     3) Bulk OUT endpoint to send ACL data
+ *
+ * Interface 1 on the USB device must present the following endpoints
+ *     1) Isochronous IN endpoint to receive SCO data
+ *     2) Isochronous OUT endpoint to send SCO data
  */
 
-static int
-ubt_match(device_t self)
+static const struct usb_config         ubt_config[UBT_N_TRANSFER] =
 {
        /*
-        * If for some reason device should not be attached then put
-        * VendorID/ProductID pair into the list below. The format is
-        * as follows:
-        *
-        *      { VENDOR_ID, PRODUCT_ID },
-        *
-        * where VENDOR_ID and PRODUCT_ID are hex numbers.
-        */
-
-       static struct usb_devno const   ubt_ignored_devices[] = {
-               { USB_VENDOR_AVM, 0x2200 }, /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
-               { 0, 0 } /* This should be the last item in the list */
-       };
+        * Interface #0
+        */
+
+       /* Outgoing bulk transfer - ACL packets */
+       [UBT_IF_0_BULK_DT_WR] = {
+               .type =         UE_BULK,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_OUT,
+               .if_index =     0,
+               .bufsize =      UBT_BULK_WRITE_BUFFER_SIZE,
+               .flags =        { .pipe_bof = 1, .force_short_xfer = 1, },
+               .callback =     &ubt_bulk_write_callback,
+       },
+       /* Incoming bulk transfer - ACL packets */
+       [UBT_IF_0_BULK_DT_RD] = {
+               .type =         UE_BULK,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_IN,
+               .if_index =     0,
+               .bufsize =      UBT_BULK_READ_BUFFER_SIZE,
+               .flags =        { .pipe_bof = 1, .short_xfer_ok = 1, },
+               .callback =     &ubt_bulk_read_callback,
+       },
+       /* Incoming interrupt transfer - HCI events */
+       [UBT_IF_0_INTR_DT_RD] = {
+               .type =         UE_INTERRUPT,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_IN,
+               .if_index =     0,
+               .flags =        { .pipe_bof = 1, .short_xfer_ok = 1, },
+               .bufsize =      UBT_INTR_BUFFER_SIZE,
+               .callback =     &ubt_intr_read_callback,
+       },
+       /* Outgoing control transfer - HCI commands */
+       [UBT_IF_0_CTRL_DT_WR] = {
+               .type =         UE_CONTROL,
+               .endpoint =     0x00,   /* control pipe */
+               .direction =    UE_DIR_ANY,
+               .if_index =     0,
+               .bufsize =      UBT_CTRL_BUFFER_SIZE,
+               .callback =     &ubt_ctrl_write_callback,
+               .timeout =      5000,   /* 5 seconds */
+       },
 
        /*
-        * If device violates Bluetooth specification and has bDeviceClass,
-        * bDeviceSubClass and bDeviceProtocol set to wrong values then you
-        * could try to put VendorID/ProductID pair into the list below.
-        * Adding VendorID/ProductID pair into this list forces ng_ubt(4)
-        * to attach to the broken device.
-        */
-
-       static struct usb_devno const   ubt_broken_devices[] = {
-               { USB_VENDOR_AVM, 0x3800 }, /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
-               { 0, 0 } /* This should be the last item in the list */
-       };
-
-       struct usb_attach_arg *uaa = device_get_ivars(self);
-       usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
-
-       if (uaa->iface == NULL ||
-           usb_lookup(ubt_ignored_devices, uaa->vendor, uaa->product))
-               return (UMATCH_NONE);
-       
-       if (dd->bDeviceClass == UDCLASS_WIRELESS &&
-           dd->bDeviceSubClass == UDSUBCLASS_RF &&
-           dd->bDeviceProtocol == UDPROTO_BLUETOOTH)
-               return (UMATCH_DEVCLASS_DEVSUBCLASS);
-
-       if (usb_lookup(ubt_broken_devices, uaa->vendor, uaa->product))
-               return (UMATCH_VENDOR_PRODUCT);
-
-       return (UMATCH_NONE);
-} /* ubt_match */
+        * Interface #1
+        */
+
+       /* Incoming isochronous transfer #1 - SCO packets */
+       [UBT_IF_1_ISOC_DT_RD1] = {
+               .type =         UE_ISOCHRONOUS,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_IN,
+               .if_index =     1,
+               .bufsize =      0,      /* use "wMaxPacketSize * frames" */
+               .frames =       UBT_ISOC_NFRAMES,
+               .flags =        { .short_xfer_ok = 1, },
+               .callback =     &ubt_isoc_read_callback,
+       },
+       /* Incoming isochronous transfer #2 - SCO packets */
+       [UBT_IF_1_ISOC_DT_RD2] = {
+               .type =         UE_ISOCHRONOUS,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_IN,
+               .if_index =     1,
+               .bufsize =      0,      /* use "wMaxPacketSize * frames" */
+               .frames =       UBT_ISOC_NFRAMES,
+               .flags =        { .short_xfer_ok = 1, },
+               .callback =     &ubt_isoc_read_callback,
+       },
+       /* Outgoing isochronous transfer #1 - SCO packets */
+       [UBT_IF_1_ISOC_DT_WR1] = {
+               .type =         UE_ISOCHRONOUS,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_OUT,
+               .if_index =     1,
+               .bufsize =      0,      /* use "wMaxPacketSize * frames" */
+               .frames =       UBT_ISOC_NFRAMES,
+               .flags =        { .short_xfer_ok = 1, },
+               .callback =     &ubt_isoc_write_callback,
+       },
+       /* Outgoing isochronous transfer #2 - SCO packets */
+       [UBT_IF_1_ISOC_DT_WR2] = {
+               .type =         UE_ISOCHRONOUS,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_OUT,
+               .if_index =     1,
+               .bufsize =      0,      /* use "wMaxPacketSize * frames" */
+               .frames =       UBT_ISOC_NFRAMES,
+               .flags =        { .short_xfer_ok = 1, },
+               .callback =     &ubt_isoc_write_callback,
+       },
+};
 
 /*
- * Attach the device
+ * If for some reason device should not be attached then put
+ * VendorID/ProductID pair into the list below. The format is
+ * as follows:
+ *
+ *     { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
+ *
+ * where VENDOR_ID and PRODUCT_ID are hex numbers.
  */
 
-static int
-ubt_attach(device_t self)
+static const STRUCT_USB_HOST_ID ubt_ignore_devs[] = 
 {
-       struct ubt_softc *sc = device_get_softc(self);
-       struct usb_attach_arg *uaa = device_get_ivars(self);
-       usb_config_descriptor_t         *cd = NULL;
-       usb_interface_descriptor_t      *id = NULL;
-       usb_endpoint_descriptor_t       *ed = NULL;
-       usbd_status                      error;
-       int                              i, ai, alt_no, isoc_in, isoc_out,
-                                        isoc_isize, isoc_osize;
-
-       /* Get USB device info */
-       sc->sc_dev = self;
-       sc->sc_udev = uaa->device;
-
-       /* 
-        * Initialize device softc structure
-        */
-
-       /* State */
-       sc->sc_debug = NG_UBT_WARN_LEVEL;
-       sc->sc_flags = 0;
-       NG_UBT_STAT_RESET(sc->sc_stat);
-
-       /* Interfaces */
-       sc->sc_iface0 = sc->sc_iface1 = NULL;
-
-       /* Interrupt pipe */
-       sc->sc_intr_ep = -1;
-       sc->sc_intr_pipe = NULL;
-       sc->sc_intr_xfer = NULL;
-       sc->sc_intr_buffer = NULL;
-
-       /* Control pipe */
-       sc->sc_ctrl_xfer = NULL;
-       sc->sc_ctrl_buffer = NULL;
-       NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
+       /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
+       { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
+
+       /* Atheros 3011 with sflash firmware */
+       { USB_VPI(0x0cf3, 0x3002, 0) },
+       { USB_VPI(0x0cf3, 0xe019, 0) },
+       { USB_VPI(0x13d3, 0x3304, 0) },
+       { USB_VPI(0x0930, 0x0215, 0) },
+       { USB_VPI(0x0489, 0xe03d, 0) },
+       { USB_VPI(0x0489, 0xe027, 0) },
+
+       /* Atheros AR9285 Malbec with sflash firmware */
+       { USB_VPI(0x03f0, 0x311d, 0) },
+
+       /* Atheros 3012 with sflash firmware */
+       { USB_VPI(0x0cf3, 0x3004, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x0cf3, 0x311d, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x13d3, 0x3375, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x04ca, 0x3005, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x04ca, 0x3006, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x04ca, 0x3008, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x13d3, 0x3362, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x0cf3, 0xe004, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x0930, 0x0219, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x0489, 0xe057, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x13d3, 0x3393, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x0489, 0xe04e, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x0489, 0xe056, 0), USB_DEV_BCD_LTEQ(1) },
+
+       /* Atheros AR5BBU12 with sflash firmware */
+       { USB_VPI(0x0489, 0xe02c, 0), USB_DEV_BCD_LTEQ(1) },
+
+       /* Atheros AR5BBU12 with sflash firmware */
+       { USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) },
+       { USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) },
+};
 
-       /* Bulk-in pipe */
-       sc->sc_bulk_in_ep = -1;
-       sc->sc_bulk_in_pipe = NULL;
-       sc->sc_bulk_in_xfer = NULL;
-       sc->sc_bulk_in_buffer = NULL;
-
-       /* Bulk-out pipe */
-       sc->sc_bulk_out_ep = -1;
-       sc->sc_bulk_out_pipe = NULL;
-       sc->sc_bulk_out_xfer = NULL;
-       sc->sc_bulk_out_buffer = NULL;
-       NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
+/* List of supported bluetooth devices */
+static const STRUCT_USB_HOST_ID ubt_devs[] =
+{
+       /* Generic Bluetooth class devices */
+       { USB_IFACE_CLASS(UDCLASS_WIRELESS),
+         USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
+         USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
 
-       /* Isoc-in pipe */
-       sc->sc_isoc_in_ep = -1;
-       sc->sc_isoc_in_pipe = NULL;
-       sc->sc_isoc_in_xfer = NULL;
+       /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
+       { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
 
-       /* Isoc-out pipe */
-       sc->sc_isoc_out_ep = -1;
-       sc->sc_isoc_out_pipe = NULL;
-       sc->sc_isoc_out_xfer = NULL;
-       sc->sc_isoc_size = -1;
-       NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
+       /* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
+       { USB_VENDOR(USB_VENDOR_BROADCOM),
+         USB_IFACE_CLASS(UICLASS_VENDOR),
+         USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
+         USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
 
-       /* Netgraph part */
-       sc->sc_node = NULL;
-       sc->sc_hook = NULL;
+       /* Apple-specific (Broadcom) devices */
+       { USB_VENDOR(USB_VENDOR_APPLE),
+         USB_IFACE_CLASS(UICLASS_VENDOR),
+         USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
+         USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
 
-       /*
-        * XXX set configuration?
-        *
-        * Configure Bluetooth USB device. Discover all required USB interfaces
-        * and endpoints.
-        *
-        * USB device must present two interfaces:
-        * 1) Interface 0 that has 3 endpoints
-        *      1) Interrupt endpoint to receive HCI events
-        *      2) Bulk IN endpoint to receive ACL data
-        *      3) Bulk OUT endpoint to send ACL data
-        *
-        * 2) Interface 1 then has 2 endpoints
-        *      1) Isochronous IN endpoint to receive SCO data
-        *      2) Isochronous OUT endpoint to send SCO data
-        *
-        * Interface 1 (with isochronous endpoints) has several alternate 
-        * configurations with different packet size.
-        */
+       /* Foxconn - Hon Hai */
+       { USB_VENDOR(USB_VENDOR_FOXCONN),
+         USB_IFACE_CLASS(UICLASS_VENDOR),
+         USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
+         USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
 
-       /*
-        * Interface 0
-        */
+       /* MediaTek MT76x0E */
+       { USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },
 
-       error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface0);
-       if (error || sc->sc_iface0 == NULL) {
-               printf("%s: Could not get interface 0 handle. %s (%d), " \
-                       "handle=%p\n", device_get_nameunit(sc->sc_dev),
-                       usbd_errstr(error), error, sc->sc_iface0);
-               goto bad;
-       }
+       /* Broadcom SoftSailing reporting vendor specific */
+       { USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },
 
-       id = usbd_get_interface_descriptor(sc->sc_iface0);
-       if (id == NULL) {
-               printf("%s: Could not get interface 0 descriptor\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
+       /* Apple MacBookPro 7,1 */
+       { USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },
 
-       for (i = 0; i < id->bNumEndpoints; i ++) {
-               ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i);
-               if (ed == NULL) {
-                       printf("%s: Could not read endpoint descriptor for " \
-                               "interface 0, i=%d\n", device_get_nameunit(sc->sc_dev),
-                               i);
-                       goto bad;
-               }
+       /* Apple iMac11,1 */
+       { USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },
 
-               switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
-               case UE_BULK:
-                       if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
-                               sc->sc_bulk_in_ep = ed->bEndpointAddress;
-                       else
-                               sc->sc_bulk_out_ep = ed->bEndpointAddress;
-                       break;
+       /* Apple MacBookPro6,2 */
+       { USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },
 
-               case UE_INTERRUPT:
-                       sc->sc_intr_ep = ed->bEndpointAddress;
-                       break;
-               }
-       }
+       /* Apple MacBookAir3,1, MacBookAir3,2 */
+       { USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },
 
-       /* Check if we got everything we wanted on Interface 0 */
-       if (sc->sc_intr_ep == -1) {
-               printf("%s: Could not detect interrupt endpoint\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       if (sc->sc_bulk_in_ep == -1) {
-               printf("%s: Could not detect bulk-in endpoint\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       if (sc->sc_bulk_out_ep == -1) {
-               printf("%s: Could not detect bulk-out endpoint\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
+       /* Apple MacBookAir4,1 */
+       { USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },
 
-       printf("%s: Interface 0 endpoints: interrupt=%#x, bulk-in=%#x, " \
-               "bulk-out=%#x\n", device_get_nameunit(sc->sc_dev), 
-               sc->sc_intr_ep, sc->sc_bulk_in_ep, sc->sc_bulk_out_ep);
+       /* MacBookAir6,1 */
+       { USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },
 
-       /*
-        * Interface 1
-        */
+       /* Apple MacBookPro8,2 */
+       { USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },
 
-       cd = usbd_get_config_descriptor(sc->sc_udev);
-       if (cd == NULL) {
-               printf("%s: Could not get device configuration descriptor\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
+       /* Apple MacMini5,1 */
+       { USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },
 
-       error = usbd_device2interface_handle(sc->sc_udev, 1, &sc->sc_iface1);
-       if (error || sc->sc_iface1 == NULL) {
-               printf("%s: Could not get interface 1 handle. %s (%d), " \
-                       "handle=%p\n", device_get_nameunit(sc->sc_dev), 
-                       usbd_errstr(error), error, sc->sc_iface1);
-               goto bad;
-       }
+       /* Bluetooth Ultraport Module from IBM */
+       { USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },
 
-       id = usbd_get_interface_descriptor(sc->sc_iface1);
-       if (id == NULL) {
-               printf("%s: Could not get interface 1 descriptor\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
+       /* ALPS Modules with non-standard ID */
+       { USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },
+       { USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },
 
-       /*
-        * Scan all alternate configurations for interface 1
-        */
+       { USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },
 
-       alt_no = -1;
+       /* Canyon CN-BTU1 with HID interfaces */
+       { USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },
 
-       for (ai = 0; ai < usbd_get_no_alts(cd, 1); ai++)  {
-               error = usbd_set_interface(sc->sc_iface1, ai);
-               if (error) {
-                       printf("%s: [SCAN] Could not set alternate " \
-                               "configuration %d for interface 1. %s (%d)\n",
-                               device_get_nameunit(sc->sc_dev),  ai, usbd_errstr(error),
-                               error);
-                       goto bad;
-               }
-               id = usbd_get_interface_descriptor(sc->sc_iface1);
-               if (id == NULL) {
-                       printf("%s: Could not get interface 1 descriptor for " \
-                               "alternate configuration %d\n",
-                               device_get_nameunit(sc->sc_dev), ai);
-                       goto bad;
-               }
+       /* Broadcom BCM20702A0 */
+       { USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },
+       { USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },
+       { USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },
+       { USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },
+       { USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
+};
 
-               isoc_in = isoc_out = -1;
-               isoc_isize = isoc_osize = 0;
-
-               for (i = 0; i < id->bNumEndpoints; i ++) {
-                       ed = usbd_interface2endpoint_descriptor(sc->sc_iface1, i);
-                       if (ed == NULL) {
-                               printf("%s: Could not read endpoint " \
-                                       "descriptor for interface 1, " \
-                                       "alternate configuration %d, i=%d\n",
-                                       device_get_nameunit(sc->sc_dev), ai, i);
-                               goto bad;
-                       }
+/*
+ * Probe for a USB Bluetooth device.
+ * USB context.
+ */
 
-                       if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
-                               continue;
+static int
+ubt_probe(device_t dev)
+{
+       struct usb_attach_arg   *uaa = device_get_ivars(dev);
+       int error;
 
-                       if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
-                               isoc_in = ed->bEndpointAddress;
-                               isoc_isize = UGETW(ed->wMaxPacketSize);
-                       } else {
-                               isoc_out = ed->bEndpointAddress;
-                               isoc_osize = UGETW(ed->wMaxPacketSize);
-                       }
-               }
+       if (uaa->usb_mode != USB_MODE_HOST)
+               return (ENXIO);
 
-               /*
-                * Make sure that configuration looks sane and if so
-                * update current settings
-                */
+       if (uaa->info.bIfaceIndex != 0)
+               return (ENXIO);
 
-               if (isoc_in != -1 && isoc_out != -1 &&
-                   isoc_isize > 0  && isoc_osize > 0 &&
-                   isoc_isize == isoc_osize && isoc_isize > sc->sc_isoc_size) {
-                       sc->sc_isoc_in_ep = isoc_in;
-                       sc->sc_isoc_out_ep = isoc_out;
-                       sc->sc_isoc_size = isoc_isize;
-                       alt_no = ai;
-               }
-       }
+       if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
+                       sizeof(ubt_ignore_devs), uaa) == 0)
+               return (ENXIO);
 
-       /* Check if we got everything we wanted on Interface 0 */
-       if (sc->sc_isoc_in_ep == -1) {
-               printf("%s: Could not detect isoc-in endpoint\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       if (sc->sc_isoc_out_ep == -1) {
-               printf("%s: Could not detect isoc-out endpoint\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       if (sc->sc_isoc_size <= 0) {
-               printf("%s: Invalid isoc. packet size=%d\n",
-                       device_get_nameunit(sc->sc_dev), sc->sc_isoc_size);
-               goto bad;
-       }
+       error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
+       if (error == 0)
+               return (BUS_PROBE_GENERIC);
+       return (error);
+} /* ubt_probe */
 
-       error = usbd_set_interface(sc->sc_iface1, alt_no);
-       if (error) {
-               printf("%s: Could not set alternate configuration " \
-                       "%d for interface 1. %s (%d)\n", device_get_nameunit(sc->sc_dev),
-                       alt_no, usbd_errstr(error), error);
-               goto bad;
-       }
+/*
+ * Attach the device.
+ * USB context.
+ */
 
-       /* Allocate USB transfer handles and buffers */
-       sc->sc_ctrl_xfer = usbd_alloc_xfer(sc->sc_udev);
-       if (sc->sc_ctrl_xfer == NULL) {
-               printf("%s: Could not allocate control xfer handle\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       sc->sc_ctrl_buffer = usbd_alloc_buffer(sc->sc_ctrl_xfer, 
-                                               UBT_CTRL_BUFFER_SIZE);
-       if (sc->sc_ctrl_buffer == NULL) {
-               printf("%s: Could not allocate control buffer\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
+static int
+ubt_attach(device_t dev)
+{
+       struct usb_attach_arg           *uaa = device_get_ivars(dev);
+       struct ubt_softc                *sc = device_get_softc(dev);
+       struct usb_endpoint_descriptor  *ed;
+       struct usb_interface_descriptor *id;
+       struct usb_interface            *iface;
+       uint16_t                        wMaxPacketSize;
+       uint8_t                         alt_index, i, j;
+       uint8_t                         iface_index[2] = { 0, 1 };
+
+       device_set_usb_desc(dev);
+
+       sc->sc_dev = dev;
+       sc->sc_debug = NG_UBT_WARN_LEVEL;
 
-       sc->sc_intr_xfer = usbd_alloc_xfer(sc->sc_udev);
-       if (sc->sc_intr_xfer == NULL) {
-               printf("%s: Could not allocate interrupt xfer handle\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
+       /* 
+        * Create Netgraph node
+        */
 
-       sc->sc_bulk_in_xfer = usbd_alloc_xfer(sc->sc_udev);
-       if (sc->sc_bulk_in_xfer == NULL) {
-               printf("%s: Could not allocate bulk-in xfer handle\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
+       if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
+               UBT_ALERT(sc, "could not create Netgraph node\n");
+               return (ENXIO);
        }
 
-       sc->sc_bulk_out_xfer = usbd_alloc_xfer(sc->sc_udev);
-       if (sc->sc_bulk_out_xfer == NULL) {
-               printf("%s: Could not allocate bulk-out xfer handle\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       sc->sc_bulk_out_buffer = usbd_alloc_buffer(sc->sc_bulk_out_xfer,
-                                               UBT_BULK_BUFFER_SIZE);
-       if (sc->sc_bulk_out_buffer == NULL) {
-               printf("%s: Could not allocate bulk-out buffer\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
+       /* Name Netgraph node */
+       if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
+               UBT_ALERT(sc, "could not name Netgraph node\n");
+               NG_NODE_UNREF(sc->sc_node);
+               return (ENXIO);
        }
+       NG_NODE_SET_PRIVATE(sc->sc_node, sc);
+       NG_NODE_FORCE_WRITER(sc->sc_node);
 
        /*
-        * Allocate buffers for isoc. transfers
+        * Initialize device softc structure
         */
 
-       sc->sc_isoc_nframes = (UBT_ISOC_BUFFER_SIZE / sc->sc_isoc_size) + 1;
+       /* initialize locks */
+       lockinit(&sc->sc_ng_lock, "ubt ng", 0, 0);
+       lockinit(&sc->sc_if_lock, "ubt if", 0, LK_CANRECURSE);
 
-       sc->sc_isoc_in_xfer = usbd_alloc_xfer(sc->sc_udev);
-       if (sc->sc_isoc_in_xfer == NULL) {
-               printf("%s: Could not allocate isoc-in xfer handle\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       sc->sc_isoc_in_buffer = usbd_alloc_buffer(sc->sc_isoc_in_xfer,
-                                       sc->sc_isoc_nframes * sc->sc_isoc_size);
-       if (sc->sc_isoc_in_buffer == NULL) {
-               printf("%s: Could not allocate isoc-in buffer\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       sc->sc_isoc_in_frlen = kmalloc(sizeof(u_int16_t) * sc->sc_isoc_nframes, 
-                                               M_USBDEV, M_WAITOK | M_NULLOK);
-       if (sc->sc_isoc_in_frlen == NULL) {
-               printf("%s: Could not allocate isoc-in frame sizes buffer\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
+       /* initialize packet queues */
+       NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
+       NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
+       NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
 
-       sc->sc_isoc_out_xfer = usbd_alloc_xfer(sc->sc_udev);
-       if (sc->sc_isoc_out_xfer == NULL) {
-               printf("%s: Could not allocate isoc-out xfer handle\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       sc->sc_isoc_out_buffer = usbd_alloc_buffer(sc->sc_isoc_out_xfer,
-                                       sc->sc_isoc_nframes * sc->sc_isoc_size);
-       if (sc->sc_isoc_out_buffer == NULL) {
-               printf("%s: Could not allocate isoc-out buffer\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
-       sc->sc_isoc_out_frlen = kmalloc(sizeof(u_int16_t) * sc->sc_isoc_nframes, 
-                                               M_USBDEV, M_WAITOK | M_NULLOK);
-       if (sc->sc_isoc_out_frlen == NULL) {
-               printf("%s: Could not allocate isoc-out frame sizes buffer\n",
-                       device_get_nameunit(sc->sc_dev));
-               goto bad;
-       }
+       /* initialize glue task */
+       TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
 
-       printf("%s: Interface 1 (alt.config %d) endpoints: isoc-in=%#x, " \
-               "isoc-out=%#x; wMaxPacketSize=%d; nframes=%d, buffer size=%d\n",
-               device_get_nameunit(sc->sc_dev), alt_no, sc->sc_isoc_in_ep,
-               sc->sc_isoc_out_ep, sc->sc_isoc_size, sc->sc_isoc_nframes, 
-               (sc->sc_isoc_nframes * sc->sc_isoc_size));
+       /*
+        * Configure Bluetooth USB device. Discover all required USB
+        * interfaces and endpoints.
+        *
+        * USB device must present two interfaces:
+        * 1) Interface 0 that has 3 endpoints
+        *      1) Interrupt endpoint to receive HCI events
+        *      2) Bulk IN endpoint to receive ACL data
+        *      3) Bulk OUT endpoint to send ACL data
+        *
+        * 2) Interface 1 then has 2 endpoints
+        *      1) Isochronous IN endpoint to receive SCO data
+        *      2) Isochronous OUT endpoint to send SCO data
+        *
+        * Interface 1 (with isochronous endpoints) has several alternate
+        * configurations with different packet size.
+        */
 
        /*
-        * Open pipes
+        * For interface #1 search alternate settings, and find
+        * the descriptor with the largest wMaxPacketSize
         */
 
-       /* Interrupt */ 
-       error = usbd_open_pipe(sc->sc_iface0, sc->sc_intr_ep,
-                       USBD_EXCLUSIVE_USE, &sc->sc_intr_pipe);
-       if (error != USBD_NORMAL_COMPLETION) {
-               printf("%s: %s - Could not open interrupt pipe. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(error),
-                       error);
-               goto bad;
-       }
+       wMaxPacketSize = 0;
+       alt_index = 0;
+       i = 0;
+       j = 0;
+       ed = NULL;
 
-       /* Bulk-in */
-       error = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_in_ep,
-                       USBD_EXCLUSIVE_USE, &sc->sc_bulk_in_pipe);
-       if (error != USBD_NORMAL_COMPLETION) {
-               printf("%s: %s - Could not open bulk-in pipe. %s (%d)\n",
-                       __func__,  device_get_nameunit(sc->sc_dev), usbd_errstr(error),
-                       error);
-               goto bad;
-       }
+       /* 
+        * Search through all the descriptors looking for the largest
+        * packet size:
+        */
+       while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
+           usbd_get_config_descriptor(uaa->device), 
+           (struct usb_descriptor *)ed))) {
+
+               if ((ed->bDescriptorType == UDESC_INTERFACE) &&
+                   (ed->bLength >= sizeof(*id))) {
+                       id = (struct usb_interface_descriptor *)ed;
+                       i = id->bInterfaceNumber;
+                       j = id->bAlternateSetting;
+               }
 
-       /* Bulk-out */
-       error = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_out_ep,
-                       USBD_EXCLUSIVE_USE, &sc->sc_bulk_out_pipe);
-       if (error != USBD_NORMAL_COMPLETION) {
-               printf("%s: %s - Could not open bulk-out pipe. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(error),
-                       error);
-               goto bad;
-       }
+               if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
+                   (ed->bLength >= sizeof(*ed)) &&
+                   (i == 1)) {
+                       uint16_t temp;
 
-#if 0 /* XXX FIXME */
-       /* Isoc-in */
-       error = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_in_ep,
-                       USBD_EXCLUSIVE_USE, &sc->sc_isoc_in_pipe);
-       if (error != USBD_NORMAL_COMPLETION) {
-               printf("%s: %s - Could not open isoc-in pipe. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(error),
-                       error);
-               goto bad;
+                       temp = UGETW(ed->wMaxPacketSize);
+                       if (temp > wMaxPacketSize) {
+                               wMaxPacketSize = temp;
+                               alt_index = j;
+                       }
+               }
        }
 
-       /* Isoc-out */
-       error = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_out_ep, 
-                       USBD_EXCLUSIVE_USE, &sc->sc_isoc_out_pipe);
-       if (error != USBD_NORMAL_COMPLETION) {
-               printf("%s: %s - Could not open isoc-out pipe. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(error),
-                       error);
-               goto bad;
+       /* Set alt configuration on interface #1 only if we found it */
+       if (wMaxPacketSize > 0 &&
+           usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
+               UBT_ALERT(sc, "could not set alternate setting %d " \
+                       "for interface 1!\n", alt_index);
+               goto detach;
        }
-#endif
 
-       /* Create Netgraph node */
-       if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
-               printf("%s: Could not create Netgraph node\n",
-                       device_get_nameunit(sc->sc_dev));
-               sc->sc_node = NULL;
-               goto bad;
+       /* Setup transfers for both interfaces */
+       if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
+                       ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_lock)) {
+               UBT_ALERT(sc, "could not allocate transfers\n");
+               goto detach;
        }
 
-       /* Name node */
-       if (ng_name_node(sc->sc_node, device_get_nameunit(sc->sc_dev)) != 0) {
-               printf("%s: Could not name Netgraph node\n",
-                       device_get_nameunit(sc->sc_dev));
-               NG_NODE_UNREF(sc->sc_node);
-               sc->sc_node = NULL;
-               goto bad;
+       /* Claim all interfaces belonging to the Bluetooth part */
+       for (i = 1;; i++) {
+               iface = usbd_get_iface(uaa->device, i);
+               if (iface == NULL)
+                       break;
+               id = usbd_get_interface_descriptor(iface);
+
+               if ((id != NULL) &&
+                   (id->bInterfaceClass == UICLASS_WIRELESS) &&
+                   (id->bInterfaceSubClass == UISUBCLASS_RF) &&
+                   (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) {
+                       usbd_set_parent_iface(uaa->device, i,
+                           uaa->info.bIfaceIndex);
+               }
        }
+       return (0); /* success */
 
-       NG_NODE_SET_PRIVATE(sc->sc_node, sc);
-       NG_NODE_FORCE_WRITER(sc->sc_node);
-
-       /* Claim all interfaces on the device */
-       for (i = 0; i < uaa->nifaces; i++)
-               uaa->ifaces[i] = NULL;
+detach:
+       ubt_detach(dev);
 
-       usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
-               sc->sc_dev);
-
-       return 0;
-bad:
-       ubt_detach(self);
-
-       return ENXIO;
+       return (ENXIO);
 } /* ubt_attach */
 
 /*
- * Detach the device
+ * Detach the device.
+ * USB context.
  */
 
-static int
-ubt_detach(device_t self)
+int
+ubt_detach(device_t dev)
 {
-       struct ubt_softc *sc = device_get_softc(self);
+       struct ubt_softc        *sc = device_get_softc(dev);
+       node_p                  node = sc->sc_node;
 
        /* Destroy Netgraph node */
-       if (sc->sc_node != NULL) {
-               NG_NODE_SET_PRIVATE(sc->sc_node, NULL);
-               ng_rmnode_self(sc->sc_node);
+       if (node != NULL) {
                sc->sc_node = NULL;
+               NG_NODE_REALLY_DIE(node);
+               ng_rmnode_self(node);
        }
 
-       /* Close pipes */
-       if (sc->sc_intr_pipe != NULL) {
-               usbd_close_pipe(sc->sc_intr_pipe);
-               sc->sc_intr_pipe = NULL;
-       }
-
-       if (sc->sc_bulk_in_pipe != NULL) {
-               usbd_close_pipe(sc->sc_bulk_in_pipe);
-               sc->sc_bulk_in_pipe = NULL;
-       }
-       if (sc->sc_bulk_out_pipe != NULL) {
-               usbd_close_pipe(sc->sc_bulk_out_pipe);
-               sc->sc_bulk_out_pipe = NULL;
-       }
-
-       if (sc->sc_isoc_in_pipe != NULL) {
-               usbd_close_pipe(sc->sc_isoc_in_pipe);
-               sc->sc_isoc_in_pipe = NULL;
-       }
-       if (sc->sc_isoc_out_pipe != NULL) {
-               usbd_close_pipe(sc->sc_isoc_out_pipe);
-               sc->sc_isoc_out_pipe = NULL;
-       }
+       /* Make sure ubt_task in gone */
+       taskqueue_drain(taskqueue_swi, &sc->sc_task);
 
-       /* Destroy USB transfer handles */
-       if (sc->sc_ctrl_xfer != NULL) {
-               usbd_free_xfer(sc->sc_ctrl_xfer);
-               sc->sc_ctrl_xfer = NULL;
-       }
-
-       if (sc->sc_intr_xfer != NULL) {
-               usbd_free_xfer(sc->sc_intr_xfer);
-               sc->sc_intr_xfer = NULL;
-       }
-
-       if (sc->sc_bulk_in_xfer != NULL) {
-               usbd_free_xfer(sc->sc_bulk_in_xfer);
-               sc->sc_bulk_in_xfer = NULL;
-       }
-       if (sc->sc_bulk_out_xfer != NULL) {
-               usbd_free_xfer(sc->sc_bulk_out_xfer);
-               sc->sc_bulk_out_xfer = NULL;
-       }
-
-       if (sc->sc_isoc_in_xfer != NULL) {
-               usbd_free_xfer(sc->sc_isoc_in_xfer);
-               sc->sc_isoc_in_xfer = NULL;
-       }
-       if (sc->sc_isoc_out_xfer != NULL) {
-               usbd_free_xfer(sc->sc_isoc_out_xfer);
-               sc->sc_isoc_out_xfer = NULL;
-       }
-
-       /* Destroy isoc. frame size buffers */
-       if (sc->sc_isoc_in_frlen != NULL) {
-               kfree(sc->sc_isoc_in_frlen, M_USBDEV);
-               sc->sc_isoc_in_frlen = NULL;
-       }
-       if (sc->sc_isoc_out_frlen != NULL) {
-               kfree(sc->sc_isoc_out_frlen, M_USBDEV);
-               sc->sc_isoc_out_frlen = NULL;
-       }
+       /* Free USB transfers, if any */
+       usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
 
        /* Destroy queues */
-       NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
-       NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
-       NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
+       UBT_NG_LOCK(sc);
+       NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
+       NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
+       NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
+       UBT_NG_UNLOCK(sc);
 
-       usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
-                       sc->sc_dev);
+       lockuninit(&sc->sc_if_lock);
+       lockuninit(&sc->sc_ng_lock);
 
        return (0);
 } /* ubt_detach */
 
-/*
- * Start USB control request (HCI command). Must be called with node locked
- */
-
-static usbd_status
-ubt_request_start(ubt_softc_p sc)
-{
-       usb_device_request_t     req;
-       struct mbuf             *m = NULL;
-       usbd_status              status;
-
-       KASSERT(!(sc->sc_flags & UBT_CMD_XMIT), (
-"%s: %s - Another control request is pending\n",
-               __func__, device_get_nameunit(sc->sc_dev)));
-
-       NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
-       if (m == NULL) {
-               NG_UBT_INFO(
-"%s: %s - HCI command queue is empty\n", __func__, device_get_nameunit(sc->sc_dev));
-
-               return (USBD_NORMAL_COMPLETION);
-       }
-
-       /*
-        * Check HCI command frame size and copy it back to 
-        * linear USB transfer buffer.
-        */ 
-
-       if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE)
-               panic(
-"%s: %s - HCI command frame too big, size=%zd, len=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), UBT_CTRL_BUFFER_SIZE,
-                       m->m_pkthdr.len);
-
-       m_copydata(m, 0, m->m_pkthdr.len, sc->sc_ctrl_buffer);
-
-       /* Initialize a USB control request and then schedule it */
-       bzero(&req, sizeof(req));
-       req.bmRequestType = UBT_HCI_REQUEST;
-       USETW(req.wLength, m->m_pkthdr.len);
-
-       NG_UBT_INFO(
-"%s: %s - Sending control request, bmRequestType=%#x, wLength=%d\n",
-               __func__, device_get_nameunit(sc->sc_dev), req.bmRequestType,
-               UGETW(req.wLength));
-
-       usbd_setup_default_xfer(
-               sc->sc_ctrl_xfer,
-               sc->sc_udev,
-               (usbd_private_handle) sc->sc_node,
-               USBD_DEFAULT_TIMEOUT, /* XXX */
-               &req,
-               sc->sc_ctrl_buffer,
-               m->m_pkthdr.len,
-               USBD_NO_COPY,
-               ubt_request_complete);
-
-       NG_NODE_REF(sc->sc_node);
-
-       status = usbd_transfer(sc->sc_ctrl_xfer);
-       if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
-               NG_UBT_ERR(
-"%s: %s - Could not start control request. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev),
-                       usbd_errstr(status), status);
-
-               NG_NODE_UNREF(sc->sc_node);
-
-               NG_BT_MBUFQ_DROP(&sc->sc_cmdq);
-               NG_UBT_STAT_OERROR(sc->sc_stat);
-
-               /* XXX FIXME should we try to resubmit another request? */
-       } else {
-               NG_UBT_INFO(
-"%s: %s - Control request has been started\n",
-                       __func__, device_get_nameunit(sc->sc_dev));
-
-               sc->sc_flags |= UBT_CMD_XMIT;
-               status = USBD_NORMAL_COMPLETION;
-       }
-
-       NG_FREE_M(m);
-
-       return (status);
-} /* ubt_request_start */
-
-/*
- * USB control request callback
+/* 
+ * Called when outgoing control request (HCI command) has completed, i.e.
+ * HCI command was sent to the device.
+ * USB context.
  */
 
 static void
-ubt_request_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
 {
-       ng_send_fn((node_p) p, NULL, ubt_request_complete2, (void *) h, s);
-       NG_NODE_UNREF((node_p) p);
-} /* ubt_request_complete */
-
-static void
-ubt_request_complete2(node_p node, hook_p hook, void *arg1, int arg2)
-{ 
-       ubt_softc_p             sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
-       usbd_xfer_handle        h = (usbd_xfer_handle) arg1;
-       usbd_status             s = (usbd_status) arg2;
-
-       if (sc == NULL)
-               return;
-
-       KASSERT((sc->sc_flags & UBT_CMD_XMIT), (
-"%s: %s - No control request is pending\n", __func__, device_get_nameunit(sc->sc_dev)));
-
-       sc->sc_flags &= ~UBT_CMD_XMIT;
-
-       if (s == USBD_CANCELLED) {
-               NG_UBT_INFO(
-"%s: %s - Control request cancelled\n", __func__, device_get_nameunit(sc->sc_dev));
-
-               return;
-       }
-
-       if (s != USBD_NORMAL_COMPLETION) {    
-               NG_UBT_ERR(
-"%s: %s - Control request failed. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(s), s);
-
-               if (s == USBD_STALLED)
-                       usbd_clear_endpoint_stall_async(h->pipe);
+       struct ubt_softc                *sc = usbd_xfer_softc(xfer);
+       struct usb_device_request       req;
+       struct mbuf                     *m;
+       struct usb_page_cache           *pc;
+       int                             actlen;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
+               UBT_STAT_BYTES_SENT(sc, actlen);
+               UBT_STAT_PCKTS_SENT(sc);
+               /* FALLTHROUGH */
+
+       case USB_ST_SETUP:
+send_next:
+               /* Get next command mbuf, if any */
+               UBT_NG_LOCK(sc);
+               NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
+               UBT_NG_UNLOCK(sc);
+
+               if (m == NULL) {
+                       UBT_INFO(sc, "HCI command queue is empty\n");
+                       break;  /* transfer complete */
+               }
 
-               NG_UBT_STAT_OERROR(sc->sc_stat);
-       } else {
-               NG_UBT_INFO(
-"%s: %s - Sent %d bytes to control pipe\n",
-                       __func__, device_get_nameunit(sc->sc_dev), h->actlen);
+               /* Initialize a USB control request and then schedule it */
+               bzero(&req, sizeof(req));
+               req.bmRequestType = UBT_HCI_REQUEST;
+               USETW(req.wLength, m->m_pkthdr.len);
 
-               NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
-               NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
-       }
+               UBT_INFO(sc, "Sending control request, " \
+                       "bmRequestType=0x%02x, wLength=%d\n",
+                       req.bmRequestType, UGETW(req.wLength));
 
-       if (NG_BT_MBUFQ_LEN(&sc->sc_cmdq) > 0)
-               ubt_request_start(sc);
-} /* ubt_request_complete2 */
+               pc = usbd_xfer_get_frame(xfer, 0);
+               usbd_copy_in(pc, 0, &req, sizeof(req));
+               pc = usbd_xfer_get_frame(xfer, 1);
+               usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
 
-/*
- * Start interrupt transfer. Must be called when node is locked
- */
-
-static usbd_status
-ubt_intr_start(ubt_softc_p sc)
-{
-       struct mbuf     *m = NULL;
-       usbd_status      status;
+               usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+               usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
+               usbd_xfer_set_frames(xfer, 2);
 
-       KASSERT(!(sc->sc_flags & UBT_EVT_RECV), (
-"%s: %s - Another interrupt request is pending\n",
-               __func__, device_get_nameunit(sc->sc_dev)));
-
-       /* Allocate new mbuf cluster */
-       MGETHDR(m, MB_DONTWAIT, MT_DATA);
-       if (m == NULL)
-               return (USBD_NOMEM);
-
-       MCLGET(m, MB_DONTWAIT);
-       if (!(m->m_flags & M_EXT)) {
                NG_FREE_M(m);
-               return (USBD_NOMEM);
-       }
 
-       if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
-               *mtod(m, u_int8_t *) = NG_HCI_EVENT_PKT;
-               m->m_pkthdr.len = m->m_len = 1;
-       } else
-               m->m_pkthdr.len = m->m_len = 0;
-       
-       /* Initialize a USB transfer and then schedule it */
-       usbd_setup_xfer(
-                       sc->sc_intr_xfer,
-                       sc->sc_intr_pipe,
-                       (usbd_private_handle) sc->sc_node,
-                       (void *)(mtod(m, u_int8_t *) + m->m_len),
-                       MCLBYTES - m->m_len,
-                       USBD_SHORT_XFER_OK,
-                       USBD_NO_TIMEOUT,
-                       ubt_intr_complete);
-
-       NG_NODE_REF(sc->sc_node);
-
-       status = usbd_transfer(sc->sc_intr_xfer);
-       if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
-               NG_UBT_ERR(
-"%s: %s - Failed to start intrerrupt transfer. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(status),
-                       status);
+               usbd_transfer_submit(xfer);
+               break;
 
-               NG_NODE_UNREF(sc->sc_node);
+       default: /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       UBT_WARN(sc, "control transfer failed: %s\n",
+                               usbd_errstr(error));
 
-               NG_FREE_M(m);
+                       UBT_STAT_OERROR(sc);
+                       goto send_next;
+               }
 
-               return (status);
+               /* transfer cancelled */
+               break;
        }
+} /* ubt_ctrl_write_callback */
 
-       sc->sc_flags |= UBT_EVT_RECV;
-       sc->sc_intr_buffer = m;
-
-       return (USBD_NORMAL_COMPLETION);
-} /* ubt_intr_start */
-
-/*
- * Process interrupt from USB device (We got data from interrupt pipe)
+/* 
+ * Called when incoming interrupt transfer (HCI event) has completed, i.e.
+ * HCI event was received from the device.
+ * USB context.
  */
 
 static void
-ubt_intr_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
-{
-       ng_send_fn((node_p) p, NULL, ubt_intr_complete2, (void *) h, s);
-       NG_NODE_UNREF((node_p) p);
-} /* ubt_intr_complete */
-
-static void
-ubt_intr_complete2(node_p node, hook_p hook, void *arg1, int arg2)
+ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
 {
-       ubt_softc_p              sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
-       usbd_xfer_handle         h = (usbd_xfer_handle) arg1;
-       usbd_status              s = (usbd_status) arg2;
-       struct mbuf             *m = NULL;
-       ng_hci_event_pkt_t      *hdr = NULL;
-       int                      error;
-
-       if (sc == NULL)
-               return;
-
-       KASSERT((sc->sc_flags & UBT_EVT_RECV), (
-"%s: %s - No interrupt request is pending\n",
-               __func__, device_get_nameunit(sc->sc_dev)));
-
-       sc->sc_flags &= ~UBT_EVT_RECV;
-
-       m = sc->sc_intr_buffer;
-       sc->sc_intr_buffer = NULL;
-
-       hdr = mtod(m, ng_hci_event_pkt_t *);
-
-       if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) {
-               NG_UBT_INFO(
-"%s: %s - No upstream hook\n", __func__, device_get_nameunit(sc->sc_dev));
-
-               NG_FREE_M(m);
-               return;
-       }
-
-       if (s == USBD_CANCELLED) {
-               NG_UBT_INFO(
-"%s: %s - Interrupt xfer cancelled\n", __func__, device_get_nameunit(sc->sc_dev));
-
-               NG_FREE_M(m);
-               return;
-       }
-               
-       if (s != USBD_NORMAL_COMPLETION) {    
-               NG_UBT_WARN(
-"%s: %s - Interrupt xfer failed, %s (%d). No new xfer will be submitted!\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(s), s);
-
-               if (s == USBD_STALLED)
-                       usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
-
-               NG_UBT_STAT_IERROR(sc->sc_stat);
-               NG_FREE_M(m);
-
-               return; /* XXX FIXME we should restart after some delay */
-       }
+       struct ubt_softc        *sc = usbd_xfer_softc(xfer);
+       struct mbuf             *m;
+       ng_hci_event_pkt_t      *hdr;
+       struct usb_page_cache   *pc;
+       int                     actlen;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+       m = NULL;
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               /* Allocate a new mbuf */
+               MGETHDR(m, MB_DONTWAIT, MT_DATA);
+               if (m == NULL) {
+                       UBT_STAT_IERROR(sc);
+                       goto submit_next;
+               }
 
-       NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
-       m->m_pkthdr.len += h->actlen;
-       m->m_len += h->actlen;
+               MCLGET(m, MB_DONTWAIT);
+               if (!(m->m_flags & M_EXT)) {
+                       UBT_STAT_IERROR(sc);
+                       goto submit_next;
+               }
 
-       NG_UBT_INFO(
-"%s: %s - Got %d bytes from interrupt pipe\n",
-               __func__, device_get_nameunit(sc->sc_dev), h->actlen);
+               /* Add HCI packet type */
+               *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
+               m->m_pkthdr.len = m->m_len = 1;
 
-       if (m->m_pkthdr.len < sizeof(*hdr)) {
-               NG_FREE_M(m);
-               goto done;
-       }
+               if (actlen > MCLBYTES - 1)
+                       actlen = MCLBYTES - 1;
 
-       if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) {
-               NG_UBT_INFO(
-"%s: %s - Got complete HCI event frame, pktlen=%d, length=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), m->m_pkthdr.len,
-                       hdr->length);
+               pc = usbd_xfer_get_frame(xfer, 0);
+               usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
+               m->m_pkthdr.len += actlen;
+               m->m_len += actlen;
 
-               NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+               UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
+                       actlen);
 
-               NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
-               if (error != 0)
-                       NG_UBT_STAT_IERROR(sc->sc_stat);
-       } else {
-               NG_UBT_ERR(
-"%s: %s - Invalid HCI event frame size, length=%d, pktlen=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), hdr->length, 
-                       m->m_pkthdr.len);
+               /* Validate packet and send it up the stack */
+               if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
+                       UBT_INFO(sc, "HCI event packet is too short\n");
 
-               NG_UBT_STAT_IERROR(sc->sc_stat);
-               NG_FREE_M(m);
-       }
-done:
-       ubt_intr_start(sc);
-} /* ubt_intr_complete2 */
+                       UBT_STAT_IERROR(sc);
+                       goto submit_next;
+               }
 
-/*
- * Start bulk-in USB transfer (ACL data). Must be called when node is locked
- */
+               hdr = mtod(m, ng_hci_event_pkt_t *);
+               if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
+                       UBT_ERR(sc, "Invalid HCI event packet size, " \
+                               "length=%d, pktlen=%d\n",
+                               hdr->length, m->m_pkthdr.len);
 
-static usbd_status
-ubt_bulk_in_start(ubt_softc_p sc)
-{
-       struct mbuf     *m = NULL;
-       usbd_status      status;
+                       UBT_STAT_IERROR(sc);
+                       goto submit_next;
+               }
 
-       KASSERT(!(sc->sc_flags & UBT_ACL_RECV), (
-"%s: %s - Another bulk-in request is pending\n",
-               __func__, device_get_nameunit(sc->sc_dev)));
+               UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
+                       "length=%d\n", m->m_pkthdr.len, hdr->length);
 
-       /* Allocate new mbuf cluster */
-       MGETHDR(m, MB_DONTWAIT, MT_DATA);
-       if (m == NULL)
-               return (USBD_NOMEM);
+               UBT_STAT_PCKTS_RECV(sc);
+               UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
 
-       MCLGET(m, MB_DONTWAIT);
-       if (!(m->m_flags & M_EXT)) {
-               NG_FREE_M(m);
-               return (USBD_NOMEM);
-       }
+               ubt_fwd_mbuf_up(sc, &m);
+               /* m == NULL at this point */
+               /* FALLTHROUGH */
 
-       if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
-               *mtod(m, u_int8_t *) = NG_HCI_ACL_DATA_PKT;
-               m->m_pkthdr.len = m->m_len = 1;
-       } else
-               m->m_pkthdr.len = m->m_len = 0;
-       
-       /* Initialize a bulk-in USB transfer and then schedule it */
-       usbd_setup_xfer(
-                       sc->sc_bulk_in_xfer,
-                       sc->sc_bulk_in_pipe,
-                       (usbd_private_handle) sc->sc_node,
-                       (void *)(mtod(m, u_int8_t *) + m->m_len),
-                       MCLBYTES - m->m_len,
-                       USBD_SHORT_XFER_OK,
-                       USBD_NO_TIMEOUT,
-                       ubt_bulk_in_complete);
-
-       NG_NODE_REF(sc->sc_node);
-
-       status = usbd_transfer(sc->sc_bulk_in_xfer);
-       if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
-               NG_UBT_ERR(
-"%s: %s - Failed to start bulk-in transfer. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(status),
-                       status);
+       case USB_ST_SETUP:
+submit_next:
+               NG_FREE_M(m); /* checks for m != NULL */
 
-               NG_NODE_UNREF(sc->sc_node);
+               usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+               usbd_transfer_submit(xfer);
+               break;
 
-               NG_FREE_M(m);
+       default: /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       UBT_WARN(sc, "interrupt transfer failed: %s\n",
+                               usbd_errstr(error));
 
-               return (status);
+                       /* Try to clear stall first */
+                       usbd_xfer_set_stall(xfer);
+                       goto submit_next;
+               }
+                       /* transfer cancelled */
+               break;
        }
-
-       sc->sc_flags |= UBT_ACL_RECV;
-       sc->sc_bulk_in_buffer = m;
-
-       return (USBD_NORMAL_COMPLETION);
-} /* ubt_bulk_in_start */
+} /* ubt_intr_read_callback */
 
 /*
- * USB bulk-in transfer callback
+ * Called when incoming bulk transfer (ACL packet) has completed, i.e.
+ * ACL packet was received from the device.
+ * USB context.
  */
 
 static void
-ubt_bulk_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
-{
-       ng_send_fn((node_p) p, NULL, ubt_bulk_in_complete2, (void *) h, s);
-       NG_NODE_UNREF((node_p) p);
-} /* ubt_bulk_in_complete */
-
-static void
-ubt_bulk_in_complete2(node_p node, hook_p hook, void *arg1, int arg2)
+ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
 {
-       ubt_softc_p              sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
-       usbd_xfer_handle         h = (usbd_xfer_handle) arg1;
-       usbd_status              s = (usbd_status) arg2;
-       struct mbuf             *m = NULL;
-       ng_hci_acldata_pkt_t    *hdr = NULL;
-       int                      len;
-
-       if (sc == NULL)
-               return;
-
-       KASSERT((sc->sc_flags & UBT_ACL_RECV), (
-"%s: %s - No bulk-in request is pending\n", __func__, device_get_nameunit(sc->sc_dev)));
-
-       sc->sc_flags &= ~UBT_ACL_RECV;
-
-       m = sc->sc_bulk_in_buffer;
-       sc->sc_bulk_in_buffer = NULL;
-
-       hdr = mtod(m, ng_hci_acldata_pkt_t *);
-
-       if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) {
-               NG_UBT_INFO(
-"%s: %s - No upstream hook\n", __func__, device_get_nameunit(sc->sc_dev));
-
-               NG_FREE_M(m);
-               return;
-       }
-
-       if (s == USBD_CANCELLED) {
-               NG_UBT_INFO(
-"%s: %s - Bulk-in xfer cancelled, pipe=%p\n",
-                       __func__, device_get_nameunit(sc->sc_dev), sc->sc_bulk_in_pipe);
-
-               NG_FREE_M(m);
-               return;
-       }
-
-       if (s != USBD_NORMAL_COMPLETION) {    
-               NG_UBT_WARN(
-"%s: %s - Bulk-in xfer failed, %s (%d). No new xfer will be submitted!\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(s), s);
-
-               if (s == USBD_STALLED)
-                       usbd_clear_endpoint_stall_async(sc->sc_bulk_in_pipe);
-
-               NG_UBT_STAT_IERROR(sc->sc_stat);
-               NG_FREE_M(m);
-
-               return; /* XXX FIXME we should restart after some delay */
-       }
-
-       NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
-       m->m_pkthdr.len += h->actlen;
-       m->m_len += h->actlen;
-
-       NG_UBT_INFO(
-"%s: %s - Got %d bytes from bulk-in pipe\n",
-               __func__, device_get_nameunit(sc->sc_dev), h->actlen);
+       struct ubt_softc        *sc = usbd_xfer_softc(xfer);
+       struct mbuf             *m;
+       ng_hci_acldata_pkt_t    *hdr;
+       struct usb_page_cache   *pc;
+       int len;
+       int actlen;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+       m = NULL;
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               /* Allocate new mbuf */
+               MGETHDR(m, MB_DONTWAIT, MT_DATA);
+               if (m == NULL) {
+                       UBT_STAT_IERROR(sc);
+                       goto submit_next;
+               }
 
-       if (m->m_pkthdr.len < sizeof(*hdr)) {
-               NG_FREE_M(m);
-               goto done;
-       }
+               MCLGET(m, MB_DONTWAIT);
+               if (!(m->m_flags & M_EXT)) {
+                       UBT_STAT_IERROR(sc);
+                       goto submit_next;
+               }
 
-       len = le16toh(hdr->length);
-       if (len == m->m_pkthdr.len - sizeof(*hdr)) {
-               NG_UBT_INFO(
-"%s: %s - Got complete ACL data frame, pktlen=%d, length=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), m->m_pkthdr.len, len);
+               /* Add HCI packet type */
+               *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
+               m->m_pkthdr.len = m->m_len = 1;
 
-               NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+               if (actlen > MCLBYTES - 1)
+                       actlen = MCLBYTES - 1;
 
-               NG_SEND_DATA_ONLY(len, sc->sc_hook, m);
-               if (len != 0)
-                       NG_UBT_STAT_IERROR(sc->sc_stat);
-       } else {
-               NG_UBT_ERR(
-"%s: %s - Invalid ACL frame size, length=%d, pktlen=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), len,
-                       m->m_pkthdr.len);
+               pc = usbd_xfer_get_frame(xfer, 0);
+               usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
+               m->m_pkthdr.len += actlen;
+               m->m_len += actlen;
 
-               NG_UBT_STAT_IERROR(sc->sc_stat);
-               NG_FREE_M(m);
-       }
-done:
-       ubt_bulk_in_start(sc);
-} /* ubt_bulk_in_complete2 */
+               UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
+                       actlen);
 
-/*
- * Start bulk-out USB transfer. Must be called with node locked
- */
+               /* Validate packet and send it up the stack */
+               if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
+                       UBT_INFO(sc, "HCI ACL packet is too short\n");
 
-static usbd_status
-ubt_bulk_out_start(ubt_softc_p sc)
-{
-       struct mbuf     *m = NULL;
-       usbd_status     status;
-
-       KASSERT(!(sc->sc_flags & UBT_ACL_XMIT), (
-"%s: %s - Another bulk-out request is pending\n",
-               __func__, device_get_nameunit(sc->sc_dev)));
-
-       NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
-       if (m == NULL) {
-               NG_UBT_INFO(
-"%s: %s - ACL data queue is empty\n", __func__, device_get_nameunit(sc->sc_dev));
-
-               return (USBD_NORMAL_COMPLETION);
-       }
+                       UBT_STAT_IERROR(sc);
+                       goto submit_next;
+               }
 
-       /*
-        * Check ACL data frame size and copy it back to linear USB 
-        * transfer buffer.
-        */ 
-
-       if (m->m_pkthdr.len > UBT_BULK_BUFFER_SIZE)
-               panic(
-"%s: %s - ACL data frame too big, size=%d, len=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), UBT_BULK_BUFFER_SIZE,
-                       m->m_pkthdr.len);
+               hdr = mtod(m, ng_hci_acldata_pkt_t *);
+               len = le16toh(hdr->length);
+               if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
+                       UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
+                               "pktlen=%d\n", len, m->m_pkthdr.len);
 
-       m_copydata(m, 0, m->m_pkthdr.len, sc->sc_bulk_out_buffer);
+                       UBT_STAT_IERROR(sc);
+                       goto submit_next;
+               }
 
-       /* Initialize a bulk-out USB transfer and then schedule it */
-       usbd_setup_xfer(
-                       sc->sc_bulk_out_xfer,
-                       sc->sc_bulk_out_pipe,
-                       (usbd_private_handle) sc->sc_node,
-                       sc->sc_bulk_out_buffer,
-                       m->m_pkthdr.len,
-                       USBD_NO_COPY,
-                       USBD_DEFAULT_TIMEOUT, /* XXX */
-                       ubt_bulk_out_complete);
+               UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
+                       "length=%d\n", m->m_pkthdr.len, len);
 
-       NG_NODE_REF(sc->sc_node);
+               UBT_STAT_PCKTS_RECV(sc);
+               UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
 
-       status = usbd_transfer(sc->sc_bulk_out_xfer);
-       if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
-               NG_UBT_ERR(
-"%s: %s - Could not start bulk-out transfer. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(status),
-                       status);
+               ubt_fwd_mbuf_up(sc, &m);
+               /* m == NULL at this point */
+               /* FALLTHOUGH */
 
-               NG_NODE_UNREF(sc->sc_node);
+       case USB_ST_SETUP:
+submit_next:
+               NG_FREE_M(m); /* checks for m != NULL */
 
-               NG_BT_MBUFQ_DROP(&sc->sc_aclq);
-               NG_UBT_STAT_OERROR(sc->sc_stat);
+               usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+               usbd_transfer_submit(xfer);
+               break;
 
-               /* XXX FIXME should we try to start another transfer? */
-       } else {
-               NG_UBT_INFO(
-"%s: %s - Bulk-out transfer has been started, len=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), m->m_pkthdr.len);
+       default: /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       UBT_WARN(sc, "bulk-in transfer failed: %s\n",
+                               usbd_errstr(error));
 
-               sc->sc_flags |= UBT_ACL_XMIT;
-               status = USBD_NORMAL_COMPLETION;
+                       /* Try to clear stall first */
+                       usbd_xfer_set_stall(xfer);
+                       goto submit_next;
+               }
+                       /* transfer cancelled */
+               break;
        }
-
-       NG_FREE_M(m);
-
-       return (status);
-} /* ubt_bulk_out_start */
+} /* ubt_bulk_read_callback */
 
 /*
- * USB bulk-out transfer callback
+ * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
+ * ACL packet was sent to the device.
+ * USB context.
  */
 
 static void
-ubt_bulk_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
 {
-       ng_send_fn((node_p) p,  NULL, ubt_bulk_out_complete2, (void *) h, s);
-       NG_NODE_UNREF((node_p) p);
-} /* ubt_bulk_out_complete */
-
-static void
-ubt_bulk_out_complete2(node_p node, hook_p hook, void *arg1, int arg2)
-{
-       ubt_softc_p             sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
-       usbd_xfer_handle        h = (usbd_xfer_handle) arg1;
-       usbd_status             s = (usbd_status) arg2;
-
-       if (sc == NULL)
-               return;
-
-       KASSERT((sc->sc_flags & UBT_ACL_XMIT), (
-"%s: %s - No bulk-out request is pending\n", __func__, device_get_nameunit(sc->sc_dev)));
-
-       sc->sc_flags &= ~UBT_ACL_XMIT;
-
-       if (s == USBD_CANCELLED) {
-               NG_UBT_INFO(
-"%s: %s - Bulk-out xfer cancelled, pipe=%p\n",
-                       __func__, device_get_nameunit(sc->sc_dev), sc->sc_bulk_out_pipe);
-
-               return;
-       }
-
-       if (s != USBD_NORMAL_COMPLETION) {    
-               NG_UBT_WARN(
-"%s: %s - Bulk-out xfer failed. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(s), s);
-
-               if (s == USBD_STALLED)
-                       usbd_clear_endpoint_stall_async(sc->sc_bulk_out_pipe);
-
-               NG_UBT_STAT_OERROR(sc->sc_stat);
-       } else {
-               NG_UBT_INFO(
-"%s: %s - Sent %d bytes to bulk-out pipe\n",
-                       __func__, device_get_nameunit(sc->sc_dev), h->actlen);
+       struct ubt_softc        *sc = usbd_xfer_softc(xfer);
+       struct mbuf             *m;
+       struct usb_page_cache   *pc;
+       int                     actlen;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
+               UBT_STAT_BYTES_SENT(sc, actlen);
+               UBT_STAT_PCKTS_SENT(sc);
+               /* FALLTHROUGH */
+
+       case USB_ST_SETUP:
+send_next:
+               /* Get next mbuf, if any */
+               UBT_NG_LOCK(sc);
+               NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
+               UBT_NG_UNLOCK(sc);
+
+               if (m == NULL) {
+                       UBT_INFO(sc, "ACL data queue is empty\n");
+                       break; /* transfer completed */
+               }
 
-               NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
-               NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); 
-       }
+               /*
+                * Copy ACL data frame back to a linear USB transfer buffer
+                * and schedule transfer
+                */
 
-       if (NG_BT_MBUFQ_LEN(&sc->sc_aclq) > 0)
-               ubt_bulk_out_start(sc);
-} /* ubt_bulk_out_complete2 */
+               pc = usbd_xfer_get_frame(xfer, 0);
+               usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
+               usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
 
-/*
- * Start Isochronous-in USB transfer. Must be called with node locked
- */
+               UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
+                       m->m_pkthdr.len);
 
-static usbd_status
-ubt_isoc_in_start(ubt_softc_p sc)
-{
-       usbd_status     status;
-       int             i;
-
-       KASSERT(!(sc->sc_flags & UBT_SCO_RECV), (
-"%s: %s - Another isoc-in request is pending\n",
-                __func__, device_get_nameunit(sc->sc_dev)));
-
-       /* Initialize a isoc-in USB transfer and then schedule it */
-       for (i = 0; i < sc->sc_isoc_nframes; i++)
-               sc->sc_isoc_in_frlen[i] = sc->sc_isoc_size;
-
-       usbd_setup_isoc_xfer(
-                       sc->sc_isoc_in_xfer,
-                       sc->sc_isoc_in_pipe,
-                       (usbd_private_handle) sc->sc_node,
-                       sc->sc_isoc_in_frlen,
-                       sc->sc_isoc_nframes,
-                       USBD_NO_COPY, /* XXX flags */
-                       ubt_isoc_in_complete);
-
-       NG_NODE_REF(sc->sc_node);
-
-       status = usbd_transfer(sc->sc_isoc_in_xfer);
-       if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
-               NG_UBT_ERR(
-"%s: %s - Failed to start isoc-in transfer. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev),
-                       usbd_errstr(status), status);
+               NG_FREE_M(m);
 
-               NG_NODE_UNREF(sc->sc_node);
+               usbd_transfer_submit(xfer);
+               break;
 
-               return (status);
-       }
+       default: /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       UBT_WARN(sc, "bulk-out transfer failed: %s\n",
+                               usbd_errstr(error));
 
-       sc->sc_flags |= UBT_SCO_RECV;
+                       UBT_STAT_OERROR(sc);
 
-       return (USBD_NORMAL_COMPLETION);
-} /* ubt_isoc_in_start */
+                       /* try to clear stall first */
+                       usbd_xfer_set_stall(xfer);
+                       goto send_next;
+               }
+                       /* transfer cancelled */
+               break;
+       }
+} /* ubt_bulk_write_callback */
 
 /*
- * USB isochronous transfer callback
+ * Called when incoming isoc transfer (SCO packet) has completed, i.e.
+ * SCO packet was received from the device.
+ * USB context.
  */
 
 static void
-ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
 {
-       ng_send_fn((node_p) p, NULL, ubt_isoc_in_complete2, (void *) h, s);
-       NG_NODE_UNREF((node_p) p);
-} /* ubt_isoc_in_complete */
+       struct ubt_softc        *sc = usbd_xfer_softc(xfer);
+       int                     n;
+       int actlen, nframes;
 
-static void
-ubt_isoc_in_complete2(node_p node, hook_p hook, void *arg1, int arg2)
-{
-       ubt_softc_p              sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
-       usbd_xfer_handle         h = (usbd_xfer_handle) arg1;
-       usbd_status              s = (usbd_status) arg2;
-       struct mbuf             *m = NULL;
-       ng_hci_scodata_pkt_t    *hdr = NULL;
-       u_int8_t                *b = NULL;
-       int                      i;
-
-       if (sc == NULL)
-               return;
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
 
-       KASSERT((sc->sc_flags & UBT_SCO_RECV), (
-"%s: %s - No isoc-in request is pending\n", __func__, device_get_nameunit(sc->sc_dev)));
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               for (n = 0; n < nframes; n ++)
+                       if (ubt_isoc_read_one_frame(xfer, n) < 0)
+                               break;
+               /* FALLTHROUGH */
 
-       sc->sc_flags &= ~UBT_SCO_RECV;
+       case USB_ST_SETUP:
+read_next:
+               for (n = 0; n < nframes; n ++)
+                       usbd_xfer_set_frame_len(xfer, n,
+                           usbd_xfer_max_framelen(xfer));
 
-       if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) {
-               NG_UBT_INFO(
-"%s: %s - No upstream hook\n", __func__, device_get_nameunit(sc->sc_dev));
+               usbd_transfer_submit(xfer);
+               break;
 
-               return;
+       default: /* Error */
+                if (error != USB_ERR_CANCELLED) {
+                        UBT_STAT_IERROR(sc);
+                        goto read_next;
+                }
+
+               /* transfer cancelled */
+               break;
        }
+} /* ubt_isoc_read_callback */
+
+/*
+ * Helper function. Called from ubt_isoc_read_callback() to read
+ * SCO data from one frame.
+ * USB context.
+ */
 
-       if (s == USBD_CANCELLED) {
-               NG_UBT_INFO(
-"%s: %s - Isoc-in xfer cancelled, pipe=%p\n",
-                       __func__, device_get_nameunit(sc->sc_dev), sc->sc_isoc_in_pipe);
+static int
+ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
+{
+       struct ubt_softc        *sc = usbd_xfer_softc(xfer);
+       struct usb_page_cache   *pc;
+       struct mbuf             *m;
+       int                     len, want, got, total;
+
+       /* Get existing SCO reassembly buffer */
+       pc = usbd_xfer_get_frame(xfer, 0);
+       m = sc->sc_isoc_in_buffer;
+       total = usbd_xfer_frame_len(xfer, frame_no);
+
+       /* While we have data in the frame */
+       while (total > 0) {
+               if (m == NULL) {
+                       /* Start new reassembly buffer */
+                       MGETHDR(m, MB_DONTWAIT, MT_DATA);
+                       if (m == NULL) {
+                               UBT_STAT_IERROR(sc);
+                               return (-1);    /* XXX out of sync! */
+                       }
 
-               return;
-       }
+                       MCLGET(m, MB_DONTWAIT);
+                       if (!(m->m_flags & M_EXT)) {
+                               UBT_STAT_IERROR(sc);
+                               NG_FREE_M(m);
+                               return (-1);    /* XXX out of sync! */
+                       }
 
-       if (s != USBD_NORMAL_COMPLETION) {    
-               NG_UBT_WARN(
-"%s: %s - Isoc-in xfer failed, %s (%d). No new xfer will be submitted!\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(s), s);
+                       /* Expect SCO header */
+                       *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
+                       m->m_pkthdr.len = m->m_len = got = 1;
+                       want = sizeof(ng_hci_scodata_pkt_t);
+               } else {
+                       /*
+                        * Check if we have SCO header and if so 
+                        * adjust amount of data we want
+                        */
+                       got = m->m_pkthdr.len;
+                       want = sizeof(ng_hci_scodata_pkt_t);
+
+                       if (got >= want)
+                               want += mtod(m, ng_hci_scodata_pkt_t *)->length;
+               }
 
-               if (s == USBD_STALLED)
-                       usbd_clear_endpoint_stall_async(sc->sc_isoc_in_pipe);
+               /* Append frame data to the SCO reassembly buffer */
+               len = total;
+               if (got + len > want)
+                       len = want - got;
 
-               NG_UBT_STAT_IERROR(sc->sc_stat);
+               usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
+                       mtod(m, uint8_t *) + m->m_pkthdr.len, len);
 
-               return; /* XXX FIXME we should restart after some delay */
-       }
+               m->m_pkthdr.len += len;
+               m->m_len += len;
+               total -= len;
 
-       NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+               /* Check if we got everything we wanted, if not - continue */
+               if (got != want)
+                       continue;
 
-       NG_UBT_INFO(
-"%s: %s - Got %d bytes from isoc-in pipe\n",
-               __func__, device_get_nameunit(sc->sc_dev), h->actlen);
+               /* If we got here then we got complete SCO frame */
+               UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
+                       "length=%d\n", m->m_pkthdr.len,
+                       mtod(m, ng_hci_scodata_pkt_t *)->length);
 
-       /* Copy SCO data frame to mbuf */
-       MGETHDR(m, MB_DONTWAIT, MT_DATA);
-       if (m == NULL) {
-               NG_UBT_ALERT(
-"%s: %s - Could not allocate mbuf\n",
-                       __func__, device_get_nameunit(sc->sc_dev));
+               UBT_STAT_PCKTS_RECV(sc);
+               UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
 
-               NG_UBT_STAT_IERROR(sc->sc_stat);
-               goto done;
+               ubt_fwd_mbuf_up(sc, &m);
+               /* m == NULL at this point */
        }
 
-       /* Fix SCO data frame header if required */
-       if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
-               *mtod(m, u_int8_t *) = NG_HCI_SCO_DATA_PKT;
-               m->m_pkthdr.len = 1;
-               m->m_len = min(MHLEN, h->actlen + 1); /* XXX m_copyback */
-       } else {
-               m->m_pkthdr.len = 0;
-               m->m_len = min(MHLEN, h->actlen); /* XXX m_copyback */
-       }
+       /* Put SCO reassembly buffer back */
+       sc->sc_isoc_in_buffer = m;
 
-       /*
-        * XXX FIXME how do we know how many frames we have received?
-        * XXX use frlen for now. is that correct?
-        */
+       return (0);
+} /* ubt_isoc_read_one_frame */
+
+/*
+ * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
+ * SCO packet was sent to the device.
+ * USB context.
+ */
 
-       b = (u_int8_t *) sc->sc_isoc_in_buffer;
+static void
+ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+       struct ubt_softc        *sc = usbd_xfer_softc(xfer);
+       struct usb_page_cache   *pc;
+       struct mbuf             *m;
+       int                     n, space, offset;
+       int                     actlen, nframes;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
+       pc = usbd_xfer_get_frame(xfer, 0);
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
+               UBT_STAT_BYTES_SENT(sc, actlen);
+               UBT_STAT_PCKTS_SENT(sc);
+               /* FALLTHROUGH */
+
+       case USB_ST_SETUP:
+send_next:
+               offset = 0;
+               space = usbd_xfer_max_framelen(xfer) * nframes;
+               m = NULL;
+
+               while (space > 0) {
+                       if (m == NULL) {
+                               UBT_NG_LOCK(sc);
+                               NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
+                               UBT_NG_UNLOCK(sc);
+
+                               if (m == NULL)
+                                       break;
+                       }
 
-       for (i = 0; i < sc->sc_isoc_nframes; i++) {
-               b += (i * sc->sc_isoc_size);
+                       n = min(space, m->m_pkthdr.len);
+                       if (n > 0) {
+                               usbd_m_copy_in(pc, offset, m,0, n);
+                               m_adj(m, n);
 
-               if (sc->sc_isoc_in_frlen[i] > 0)
-                       m_copyback(m, m->m_pkthdr.len,
-                               sc->sc_isoc_in_frlen[i], b);
-       }
+                               offset += n;
+                               space -= n;
+                       }
 
-       if (m->m_pkthdr.len < sizeof(*hdr))
-               goto done;
+                       if (m->m_pkthdr.len == 0)
+                               NG_FREE_M(m); /* sets m = NULL */
+               }
 
-       hdr = mtod(m, ng_hci_scodata_pkt_t *);
+               /* Put whatever is left from mbuf back on queue */
+               if (m != NULL) {
+                       UBT_NG_LOCK(sc);
+                       NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
+                       UBT_NG_UNLOCK(sc);
+               }
 
-       if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) {
-               NG_UBT_INFO(
-"%s: %s - Got complete SCO data frame, pktlen=%d, length=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), m->m_pkthdr.len,
-                       hdr->length);
+               /*
+                * Calculate sizes for isoc frames.
+                * Note that offset could be 0 at this point (i.e. we have
+                * nothing to send). That is fine, as we have isoc. transfers
+                * going in both directions all the time. In this case it
+                * would be just empty isoc. transfer.
+                */
 
-               NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+               for (n = 0; n < nframes; n ++) {
+                       usbd_xfer_set_frame_len(xfer, n,
+                           min(offset, usbd_xfer_max_framelen(xfer)));
+                       offset -= usbd_xfer_frame_len(xfer, n);
+               }
 
-               NG_SEND_DATA_ONLY(i, sc->sc_hook, m);
-               if (i != 0)
-                       NG_UBT_STAT_IERROR(sc->sc_stat);
-       } else {
-               NG_UBT_ERR(
-"%s: %s - Invalid SCO frame size, length=%d, pktlen=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), hdr->length, 
-                       m->m_pkthdr.len);
+               usbd_transfer_submit(xfer);
+               break;
 
-               NG_UBT_STAT_IERROR(sc->sc_stat);
-               NG_FREE_M(m);
+       default: /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       UBT_STAT_OERROR(sc);
+                       goto send_next;
+               }
+
+               /* transfer cancelled */
+               break;
        }
-done:
-       ubt_isoc_in_start(sc);
-} /* ubt_isoc_in_complete2 */
+}
 
 /*
- * Start isochronous-out USB transfer. Must be called with node locked
+ * Utility function to forward provided mbuf upstream (i.e. up the stack).
+ * Modifies value of the mbuf pointer (sets it to NULL).
+ * Save to call from any context.
  */
 
-static usbd_status
-ubt_isoc_out_start(ubt_softc_p sc)
+static int
+ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
 {
-       struct mbuf     *m = NULL;
-       u_int8_t        *b = NULL;
-       int              i, len, nframes;
-       usbd_status      status;
+       hook_p  hook;
+       int     error;
 
-       KASSERT(!(sc->sc_flags & UBT_SCO_XMIT), (
-"%s: %s - Another isoc-out request is pending\n",
-               __func__, device_get_nameunit(sc->sc_dev)));
+       /*
+        * Close the race with Netgraph hook newhook/disconnect methods.
+        * Save the hook pointer atomically. Two cases are possible:
+        *
+        * 1) The hook pointer is NULL. It means disconnect method got
+        *    there first. In this case we are done.
+        *
+        * 2) The hook pointer is not NULL. It means that hook pointer
+        *    could be either in valid or invalid (i.e. in the process
+        *    of disconnect) state. In any case grab an extra reference
+        *    to protect the hook pointer.
+        *
+        * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
+        * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
+        */
 
-       NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
-       if (m == NULL) {
-               NG_UBT_INFO(
-"%s: %s - SCO data queue is empty\n", __func__, device_get_nameunit(sc->sc_dev));
+       UBT_NG_LOCK(sc);
+       if ((hook = sc->sc_hook) != NULL)
+               NG_HOOK_REF(hook);
+       UBT_NG_UNLOCK(sc);
 
-               return (USBD_NORMAL_COMPLETION);
+       if (hook == NULL) {
+               NG_FREE_M(*m);
+               return (ENETDOWN);
        }
 
-       /* Copy entire SCO frame into USB transfer buffer and start transfer */ 
-       b = (u_int8_t *) sc->sc_isoc_out_buffer;
-       nframes = 0;
+       NG_SEND_DATA_ONLY(error, hook, *m);
+       NG_HOOK_UNREF(hook);
 
-       for (i = 0; i < sc->sc_isoc_nframes; i++) {
-               b += (i * sc->sc_isoc_size);
+       if (error != 0)
+               UBT_STAT_IERROR(sc);
 
-               len = min(m->m_pkthdr.len, sc->sc_isoc_size);
-               if (len > 0) {
-                       m_copydata(m, 0, len, b);
-                       m_adj(m, len);
-                       nframes ++;
-               }
+       return (error);
+} /* ubt_fwd_mbuf_up */
 
-               sc->sc_isoc_out_frlen[i] = len;
-       }
+/****************************************************************************
+ ****************************************************************************
+ **                                 Glue 
+ ****************************************************************************
+ ****************************************************************************/
 
-       if (m->m_pkthdr.len > 0)
-               panic(
-"%s: %s - SCO data frame is too big, nframes=%d, size=%d, len=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), sc->sc_isoc_nframes,
-                       sc->sc_isoc_size, m->m_pkthdr.len);
-
-       NG_FREE_M(m);
-
-       /* Initialize a isoc-out USB transfer and then schedule it */
-       usbd_setup_isoc_xfer(
-                       sc->sc_isoc_out_xfer,
-                       sc->sc_isoc_out_pipe,
-                       (usbd_private_handle) sc->sc_node,
-                       sc->sc_isoc_out_frlen,
-                       nframes,
-                       USBD_NO_COPY,
-                       ubt_isoc_out_complete);
-
-       NG_NODE_REF(sc->sc_node);
-
-       status = usbd_transfer(sc->sc_isoc_out_xfer);
-       if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
-               NG_UBT_ERR(
-"%s: %s - Could not start isoc-out transfer. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(status),
-                       status);
+/*
+ * Schedule glue task. Should be called with sc_ng_mtx held. 
+ * Netgraph context.
+ */
 
-               NG_NODE_UNREF(sc->sc_node);
+static void
+ubt_task_schedule(ubt_softc_p sc, int action)
+{
+       KKASSERT(lockowned(&sc->sc_ng_lock) != 0);
 
-               NG_BT_MBUFQ_DROP(&sc->sc_scoq);
-               NG_UBT_STAT_OERROR(sc->sc_stat);
-       } else {
-               NG_UBT_INFO(
-"%s: %s - Isoc-out transfer has been started, nframes=%d, size=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), nframes,
-                       sc->sc_isoc_size);
+       /*
+        * Try to handle corner case when "start all" and "stop all"
+        * actions can both be set before task is executed.
+        *
+        * The rules are
+        *
+        * sc_task_flags        action          new sc_task_flags
+        * ------------------------------------------------------
+        * 0                    start           start
+        * 0                    stop            stop
+        * start                start           start
+        * start                stop            stop
+        * stop                 start           stop|start
+        * stop                 stop            stop
+        * stop|start           start           stop|start
+        * stop|start           stop            stop
+        */
+
+       if (action != 0) {
+               if ((action & UBT_FLAG_T_STOP_ALL) != 0)
+                       sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
+
+               sc->sc_task_flags |= action;
+       }
+
+       if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
+               return;
 
-               sc->sc_flags |= UBT_SCO_XMIT;
-               status = USBD_NORMAL_COMPLETION;
+       if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
+               sc->sc_task_flags |= UBT_FLAG_T_PENDING;
+               return;
        }
 
-       return (status);
-} /* ubt_isoc_out_start */
+       /* XXX: i think this should never happen */
+} /* ubt_task_schedule */
 
 /*
- * USB isoc-out. transfer callback
+ * Glue task. Examines sc_task_flags and does things depending on it.
+ * Taskqueue context.
  */
 
 static void
-ubt_isoc_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+ubt_task(void *context, int pending)
 {
-       ng_send_fn((node_p) p, NULL, ubt_isoc_out_complete2, (void *) h, s);
-       NG_NODE_UNREF((node_p) p);
-} /* ubt_isoc_out_complete */
+       ubt_softc_p     sc = context;
+       int             task_flags, i;
 
-static void
-ubt_isoc_out_complete2(node_p node, hook_p hook, void *arg1, int arg2)
-{
-       ubt_softc_p             sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
-       usbd_xfer_handle        h = (usbd_xfer_handle) arg1;
-       usbd_status             s = (usbd_status) arg2;
-
-       if (sc == NULL)
-               return;
+       UBT_NG_LOCK(sc);
+       task_flags = sc->sc_task_flags;
+       sc->sc_task_flags = 0;
+       UBT_NG_UNLOCK(sc);
 
-       KASSERT((sc->sc_flags & UBT_SCO_XMIT), (
-"%s: %s - No isoc-out request is pending\n", __func__, device_get_nameunit(sc->sc_dev)));
+       /*
+        * Stop all USB transfers synchronously.
+        * Stop interface #0 and #1 transfers at the same time and in the
+        * same loop. usbd_transfer_drain() will do appropriate locking.
+        */
 
-       sc->sc_flags &= ~UBT_SCO_XMIT;
+       if (task_flags & UBT_FLAG_T_STOP_ALL)
+               for (i = 0; i < UBT_N_TRANSFER; i ++)
+                       usbd_transfer_drain(sc->sc_xfer[i]);
 
-       if (s == USBD_CANCELLED) {
-               NG_UBT_INFO(
-"%s: %s - Isoc-out xfer cancelled, pipe=%p\n",
-                       __func__, device_get_nameunit(sc->sc_dev),
-                       sc->sc_isoc_out_pipe);
+       /* Start incoming interrupt and bulk, and all isoc. USB transfers */
+       if (task_flags & UBT_FLAG_T_START_ALL) {
+               /*
+                * Interface #0
+                */
 
-               return;
-       }
+               lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
 
-       if (s != USBD_NORMAL_COMPLETION) {    
-               NG_UBT_WARN(
-"%s: %s - Isoc-out xfer failed. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(s), s);
+               ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
+               ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
 
-               if (s == USBD_STALLED)
-                       usbd_clear_endpoint_stall_async(sc->sc_isoc_out_pipe);
+               /*
+                * Interface #1
+                * Start both read and write isoc. transfers by default.
+                * Get them going all the time even if we have nothing
+                * to send to avoid any delays.
+                */
 
-               NG_UBT_STAT_OERROR(sc->sc_stat);
-       } else {
-               NG_UBT_INFO(
-"%s: %s - Sent %d bytes to isoc-out pipe\n",
-                       __func__, device_get_nameunit(sc->sc_dev), h->actlen);
+               ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
+               ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
+               ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
+               ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
 
-               NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
-               NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+               lockmgr(&sc->sc_if_lock, LK_RELEASE);
        }
 
-       if (NG_BT_MBUFQ_LEN(&sc->sc_scoq) > 0)
-               ubt_isoc_out_start(sc);
-} /* ubt_isoc_out_complete2 */
-
-/*
- * Abort transfers on all USB pipes
- */
+       /* Start outgoing control transfer */
+       if (task_flags & UBT_FLAG_T_START_CTRL) {
+               lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
+               ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
+               lockmgr(&sc->sc_if_lock, LK_RELEASE);
+       }
 
-static void
-ubt_reset(ubt_softc_p sc)
-{
-       /* Interrupt */
-       if (sc->sc_intr_pipe != NULL)
-               usbd_abort_pipe(sc->sc_intr_pipe);
-
-       /* Bulk-in/out */
-       if (sc->sc_bulk_in_pipe != NULL)
-               usbd_abort_pipe(sc->sc_bulk_in_pipe);
-       if (sc->sc_bulk_out_pipe != NULL)
-               usbd_abort_pipe(sc->sc_bulk_out_pipe);
-
-       /* Isoc-in/out */
-       if (sc->sc_isoc_in_pipe != NULL)
-               usbd_abort_pipe(sc->sc_isoc_in_pipe);
-       if (sc->sc_isoc_out_pipe != NULL)
-               usbd_abort_pipe(sc->sc_isoc_out_pipe);
-
-       /* Cleanup queues */
-       NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
-       NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
-       NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
-} /* ubt_reset */
+       /* Start outgoing bulk transfer */
+       if (task_flags & UBT_FLAG_T_START_BULK) {
+               lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
+               ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
+               lockmgr(&sc->sc_if_lock, LK_RELEASE);
+       }
+} /* ubt_task */
 
 /****************************************************************************
  ****************************************************************************
@@ -1828,6 +1443,7 @@ ubt_reset(ubt_softc_p sc)
 
 /*
  * Netgraph node constructor. Do not allow to create node of this type.
+ * Netgraph context.
  */
 
 static int
@@ -1837,152 +1453,116 @@ ng_ubt_constructor(node_p node)
 } /* ng_ubt_constructor */
 
 /*
- * Netgraph node destructor. Destroy node only when device has been detached
+ * Netgraph node destructor. Destroy node only when device has been detached.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_shutdown(node_p node)
 {
-       ubt_softc_p     sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
-
-       /* Let old node go */
-       NG_NODE_SET_PRIVATE(node, NULL);
-       NG_NODE_UNREF(node);
-       
-       if (sc == NULL)
-               goto done;
-
-       /* Create Netgraph node */
-       if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
-               printf("%s: Could not create Netgraph node\n",
-                       device_get_nameunit(sc->sc_dev));
-               sc->sc_node = NULL;
-               goto done;
-       }
-       
-       /* Name node */ 
-       if (ng_name_node(sc->sc_node, device_get_nameunit(sc->sc_dev)) != 0) {
-               printf("%s: Could not name Netgraph node\n",
-                       device_get_nameunit(sc->sc_dev));
-               NG_NODE_UNREF(sc->sc_node);
-               sc->sc_node = NULL;
-               goto done;
-       }
+       if (node->nd_flags & NGF_REALLY_DIE) {
+               /*
+                 * We came here because the USB device is being
+                * detached, so stop being persistant.
+                 */
+               NG_NODE_SET_PRIVATE(node, NULL);
+               NG_NODE_UNREF(node);
+       } else
+               NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
 
-       NG_NODE_SET_PRIVATE(sc->sc_node, sc);
-       NG_NODE_FORCE_WRITER(sc->sc_node);
-done:
        return (0);
 } /* ng_ubt_shutdown */
 
 /*
  * Create new hook. There can only be one.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_newhook(node_p node, hook_p hook, char const *name)
 {
-       ubt_softc_p     sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
+       struct ubt_softc        *sc = NG_NODE_PRIVATE(node);
 
        if (strcmp(name, NG_UBT_HOOK) != 0)
                return (EINVAL);
 
-       if (sc->sc_hook != NULL)
+       UBT_NG_LOCK(sc);
+       if (sc->sc_hook != NULL) {
+               UBT_NG_UNLOCK(sc);
+
                return (EISCONN);
+       }
 
        sc->sc_hook = hook;
+       UBT_NG_UNLOCK(sc);
 
        return (0);
 } /* ng_ubt_newhook */
 
 /*
- * Connect hook. Start incoming USB transfers
+ * Connect hook. Start incoming USB transfers.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_connect(hook_p hook)
 {
-       ubt_softc_p     sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
-       usbd_status     status;
+       struct ubt_softc        *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
 
        NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
 
-       /* Start intr transfer */
-       status = ubt_intr_start(sc);
-       if (status != USBD_NORMAL_COMPLETION) {
-               NG_UBT_ALERT(
-"%s: %s - Could not start interrupt transfer. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(status),
-                       status);
-               goto fail;
-       }
-
-       /* Start bulk-in transfer */
-       status = ubt_bulk_in_start(sc);
-       if (status != USBD_NORMAL_COMPLETION) {
-               NG_UBT_ALERT(
-"%s: %s - Could not start bulk-in transfer. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(status),
-                       status);
-               goto fail;
-       }
-
-#if 0 /* XXX FIXME */
-       /* Start isoc-in transfer */
-       status = ubt_isoc_in_start(sc);
-       if (status != USBD_NORMAL_COMPLETION) {
-               NG_UBT_ALERT(
-"%s: %s - Could not start isoc-in transfer. %s (%d)\n",
-                       __func__, device_get_nameunit(sc->sc_dev), usbd_errstr(status),
-                       status);
-               goto fail;
-       }
-#endif
+       UBT_NG_LOCK(sc);
+       ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
+       UBT_NG_UNLOCK(sc);
 
        return (0);
-fail:
-       ubt_reset(sc);
-       sc->sc_hook = NULL;
-
-       return (ENXIO);
 } /* ng_ubt_connect */
 
 /*
- * Disconnect hook
+ * Disconnect hook.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_disconnect(hook_p hook)
 {
-       ubt_softc_p     sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+       struct ubt_softc        *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+       UBT_NG_LOCK(sc);
 
-       if (sc != NULL) {
-               if (hook != sc->sc_hook)
-                       return (EINVAL);
+       if (hook != sc->sc_hook) {
+               UBT_NG_UNLOCK(sc);
 
-               ubt_reset(sc);
-               sc->sc_hook = NULL;
+               return (EINVAL);
        }
 
+       sc->sc_hook = NULL;
+
+       /* Kick off task to stop all USB xfers */
+       ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
+
+       /* Drain queues */
+       NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
+       NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
+       NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
+
+       UBT_NG_UNLOCK(sc);
+
        return (0);
 } /* ng_ubt_disconnect */
-
+       
 /*
- * Process control message
+ * Process control message.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
 {
-       ubt_softc_p              sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
-       struct ng_mesg          *msg = NULL, *rsp = NULL;
-       struct ng_bt_mbufq      *q = NULL;
-       int                      error = 0, queue, qlen;
-
-       if (sc == NULL) {
-               NG_FREE_ITEM(item);
-               return (EHOSTDOWN);
-       }
+       struct ubt_softc        *sc = NG_NODE_PRIVATE(node);
+       struct ng_mesg          *msg, *rsp = NULL;
+       struct ng_bt_mbufq      *q;
+       int                     error = 0, queue, qlen;
 
        NGI_GET_MSG(item, msg);
 
@@ -1991,25 +1571,27 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
                switch (msg->header.cmd) {
                case NGM_TEXT_STATUS:
                        NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_WAITOK | M_NULLOK);
-                       if (rsp == NULL)
+                       if (rsp == NULL) {
                                error = ENOMEM;
-                       else
-                               snprintf(rsp->data, NG_TEXTRESPONSE,
-                                       "Hook: %s\n"   \
-                                       "Flags: %#x\n" \
-                                       "Debug: %d\n"  \
-                                       "CMD queue: [have:%d,max:%d]\n" \
-                                       "ACL queue: [have:%d,max:%d]\n" \
-                                       "SCO queue: [have:%d,max:%d]",
-                                       (sc->sc_hook != NULL)? NG_UBT_HOOK : "",
-                                       sc->sc_flags,
-                                       sc->sc_debug,
-                                       NG_BT_MBUFQ_LEN(&sc->sc_cmdq),
-                                       sc->sc_cmdq.maxlen,
-                                       NG_BT_MBUFQ_LEN(&sc->sc_aclq),
-                                       sc->sc_aclq.maxlen,
-                                       NG_BT_MBUFQ_LEN(&sc->sc_scoq),
-                                       sc->sc_scoq.maxlen);
+                               break;
+                       }
+
+                       snprintf(rsp->data, NG_TEXTRESPONSE,
+                               "Hook: %s\n" \
+                               "Task flags: %#x\n" \
+                               "Debug: %d\n" \
+                               "CMD queue: [have:%d,max:%d]\n" \
+                               "ACL queue: [have:%d,max:%d]\n" \
+                               "SCO queue: [have:%d,max:%d]",
+                               (sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
+                               sc->sc_task_flags,
+                               sc->sc_debug,
+                               sc->sc_cmdq.len,
+                               sc->sc_cmdq.maxlen,
+                               sc->sc_aclq.len,
+                               sc->sc_aclq.maxlen,
+                               sc->sc_scoq.len,
+                               sc->sc_scoq.maxlen);
                        break;
 
                default:
@@ -2021,59 +1603,54 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
        case NGM_UBT_COOKIE:
                switch (msg->header.cmd) {
                case NGM_UBT_NODE_SET_DEBUG:
-                       if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep))
+                       if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
                                error = EMSGSIZE;
-                       else
-                               sc->sc_debug =
-                                       *((ng_ubt_node_debug_ep *)(msg->data));
+                               break;
+                       }
+
+                       sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
                        break;
 
                case NGM_UBT_NODE_GET_DEBUG:
                        NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
-                               M_WAITOK | M_NULLOK);
-                       if (rsp == NULL)
+                           M_WAITOK | M_NULLOK);
+                       if (rsp == NULL) {
                                error = ENOMEM;
-                       else
-                               *((ng_ubt_node_debug_ep *)(rsp->data)) = 
-                                       sc->sc_debug;
-                        break;
+                               break;
+                       }
+
+                       *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
+                       break;
 
                case NGM_UBT_NODE_SET_QLEN:
-                       if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep))
+                       if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
                                error = EMSGSIZE;
-                       else {
-                               queue = ((ng_ubt_node_qlen_ep *)
-                                               (msg->data))->queue;
-                               qlen = ((ng_ubt_node_qlen_ep *)
-                                               (msg->data))->qlen;
-
-                               if (qlen <= 0) {
-                                       error = EINVAL;
-                                       break;
-                               }
+                               break;
+                       }
 
-                               switch (queue) {
-                               case NGM_UBT_NODE_QUEUE_CMD:
-                                       q = &sc->sc_cmdq;
-                                       break;
+                       queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
+                       qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
 
-                               case NGM_UBT_NODE_QUEUE_ACL:
-                                       q = &sc->sc_aclq;
-                                       break;
+                       switch (queue) {
+                       case NGM_UBT_NODE_QUEUE_CMD:
+                               q = &sc->sc_cmdq;
+                               break;
 
-                               case NGM_UBT_NODE_QUEUE_SCO:
-                                       q = &sc->sc_scoq;
-                                       break;
+                       case NGM_UBT_NODE_QUEUE_ACL:
+                               q = &sc->sc_aclq;
+                               break;
 
-                               default:
-                                       q = NULL;
-                                       error = EINVAL;
-                                       break;
-                               }
+                       case NGM_UBT_NODE_QUEUE_SCO:
+                               q = &sc->sc_scoq;
+                               break;
 
-                               if (q != NULL)
-                                       q->maxlen = qlen;
+                       default:
+                               error = EINVAL;
+                               goto done;
+                               /* NOT REACHED */
                        }
+
+                       q->maxlen = qlen;
                        break;
 
                case NGM_UBT_NODE_GET_QLEN:
@@ -2082,7 +1659,8 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
                                break;
                        }
 
-                       queue = ((ng_ubt_node_qlen_ep *)(msg->data))->queue;
+                       queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
+
                        switch (queue) {
                        case NGM_UBT_NODE_QUEUE_CMD:
                                q = &sc->sc_cmdq;
@@ -2097,38 +1675,36 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
                                break;
 
                        default:
-                               q = NULL;
                                error = EINVAL;
-                               break;
+                               goto done;
+                               /* NOT REACHED */
                        }
 
-                       if (q != NULL) {
-                               NG_MKRESPONSE(rsp, msg, 
-                                       sizeof(ng_ubt_node_qlen_ep), M_WAITOK | M_NULLOK);
-                               if (rsp == NULL) {
-                                       error = ENOMEM;
-                                       break;
-                               }
-
-                               ((ng_ubt_node_qlen_ep *)(rsp->data))->queue =
-                                       queue;
-                               ((ng_ubt_node_qlen_ep *)(rsp->data))->qlen =
-                                       q->maxlen;
+                       NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
+                               M_WAITOK | M_NULLOK);
+                       if (rsp == NULL) {
+                               error = ENOMEM;
+                               break;
                        }
+
+                       ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
+                       ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
                        break;
 
                case NGM_UBT_NODE_GET_STAT:
                        NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
-                               M_WAITOK | M_NULLOK);
-                       if (rsp == NULL)
+                           M_WAITOK | M_NULLOK);
+                       if (rsp == NULL) {
                                error = ENOMEM;
-                       else
-                               bcopy(&sc->sc_stat, rsp->data,
-                                       sizeof(ng_ubt_node_stat_ep));
+                               break;
+                       }
+
+                       bcopy(&sc->sc_stat, rsp->data,
+                               sizeof(ng_ubt_node_stat_ep));
                        break;
 
                case NGM_UBT_NODE_RESET_STAT:
-                       NG_UBT_STAT_RESET(sc->sc_stat);
+                       UBT_STAT_RESET(sc);
                        break;
 
                default:
@@ -2141,7 +1717,7 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
                error = EINVAL;
                break;
        }
-
+done:
        NG_RESPOND_MSG(error, node, item, rsp);
        NG_FREE_MSG(msg);
 
@@ -2149,22 +1725,17 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
 } /* ng_ubt_rcvmsg */
 
 /*
- * Process data
+ * Process data.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_rcvdata(hook_p hook, item_p item)
 {
-       ubt_softc_p              sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
-       struct mbuf             *m = NULL;
-       usbd_status             (*f)(ubt_softc_p) = NULL;
-       struct ng_bt_mbufq      *q = NULL;
-       int                      b, error = 0;
-
-       if (sc == NULL) {
-               error = EHOSTDOWN;
-               goto done;
-       }
+       struct ubt_softc        *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+       struct mbuf             *m;
+       struct ng_bt_mbufq      *q;
+       int                     action, error = 0;
 
        if (hook != sc->sc_hook) {
                error = EINVAL;
@@ -2174,61 +1745,132 @@ ng_ubt_rcvdata(hook_p hook, item_p item)
        /* Deatch mbuf and get HCI frame type */
        NGI_GET_M(item, m);
 
+       /*
+        * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
+        * 2 bytes connection handle and at least 1 byte of length.
+        * Panic on data frame that has size smaller than 4 bytes (it
+        * should not happen)
+        */
+
+       if (m->m_pkthdr.len < 4)
+               panic("HCI frame size is too small! pktlen=%d\n",
+                       m->m_pkthdr.len);
+
        /* Process HCI frame */
-       switch (*mtod(m, u_int8_t *)) { /* XXX call m_pullup ? */
+       switch (*mtod(m, uint8_t *)) {  /* XXX call m_pullup ? */
        case NG_HCI_CMD_PKT:
-               f = ubt_request_start;
+               if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
+                       panic("HCI command frame size is too big! " \
+                               "buffer size=%zd, packet len=%d\n",
+                               UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
+
                q = &sc->sc_cmdq;
-               b = UBT_CMD_XMIT;
+               action = UBT_FLAG_T_START_CTRL;
                break;
 
        case NG_HCI_ACL_DATA_PKT:
-               f = ubt_bulk_out_start;
+               if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
+                       panic("ACL data frame size is too big! " \
+                               "buffer size=%d, packet len=%d\n",
+                               UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
+
                q = &sc->sc_aclq;
-               b = UBT_ACL_XMIT;
+               action = UBT_FLAG_T_START_BULK;
                break;
 
-#if 0 /* XXX FIXME */
        case NG_HCI_SCO_DATA_PKT:
-               f = ubt_isoc_out_start;
                q = &sc->sc_scoq;
-               b = UBT_SCO_XMIT;
+               action = 0;
                break;
-#endif
 
        default:
-               NG_UBT_ERR(
-"%s: %s - Dropping unknown/unsupported HCI frame, type=%d, pktlen=%d\n",
-                       __func__, device_get_nameunit(sc->sc_dev), *mtod(m, u_int8_t *),
-                       m->m_pkthdr.len);
+               UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
+                       "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
 
                NG_FREE_M(m);
                error = EINVAL;
-
                goto done;
                /* NOT REACHED */
        }
 
-       /* Loose frame type, if required */
-       if (!(sc->sc_flags & UBT_NEED_FRAME_TYPE))
-               m_adj(m, sizeof(u_int8_t)); 
-
+       UBT_NG_LOCK(sc);
        if (NG_BT_MBUFQ_FULL(q)) {
-               NG_UBT_ERR(
-"%s: %s - Dropping HCI frame %#x, len=%d. Queue full\n",
-                       __func__, device_get_nameunit(sc->sc_dev),
-                       *mtod(m, u_int8_t *), m->m_pkthdr.len);
+               NG_BT_MBUFQ_DROP(q);
+               UBT_NG_UNLOCK(sc);
+               
+               UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
+                       *mtod(m, uint8_t *), m->m_pkthdr.len);
 
                NG_FREE_M(m);
-       } else
+       } else {
+               /* Loose HCI packet type, enqueue mbuf and kick off task */
+               m_adj(m, sizeof(uint8_t));
                NG_BT_MBUFQ_ENQUEUE(q, m);
-
-       if (!(sc->sc_flags & b))
-               if ((*f)(sc) != USBD_NORMAL_COMPLETION)
-                       error = EIO;
+               ubt_task_schedule(sc, action);
+               UBT_NG_UNLOCK(sc);
+       }
 done:
        NG_FREE_ITEM(item);
 
        return (error);
 } /* ng_ubt_rcvdata */
 
+/****************************************************************************
+ ****************************************************************************
+ **                              Module
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Load/Unload the driver module
+ */
+
+static int
+ubt_modevent(module_t mod, int event, void *data)
+{
+       int     error;
+
+       switch (event) {
+       case MOD_LOAD:
+               error = ng_newtype(&typestruct);
+               if (error != 0)
+                       printf("%s: Could not register Netgraph node type, " \
+                               "error=%d\n", NG_UBT_NODE_TYPE, error);
+               break;
+
+       case MOD_UNLOAD:
+               error = ng_rmtype(&typestruct);
+               break;
+
+       default:
+               error = EOPNOTSUPP;
+               break;
+       }
+
+       return (error);
+} /* ubt_modevent */
+
+static devclass_t      ubt_devclass;
+
+static device_method_t ubt_methods[] =
+{
+       DEVMETHOD(device_probe, ubt_probe),
+       DEVMETHOD(device_attach, ubt_attach),
+       DEVMETHOD(device_detach, ubt_detach),
+
+       DEVMETHOD_END
+};
+
+static driver_t                ubt_driver =
+{
+       .name =    "ubt",
+       .methods = ubt_methods,
+       .size =    sizeof(struct ubt_softc),
+};
+
+DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, NULL);
+MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
+MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
+MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
+MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
+
index 413d774..4a471b4 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*-
- * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * SUCH DAMAGE.
  *
  * $Id: ng_ubt_var.h,v 1.2 2003/03/22 23:44:36 max Exp $
- * $FreeBSD: src/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h,v 1.7 2006/09/07 23:38:09 emax Exp $
- * $DragonFly: src/sys/netgraph7/bluetooth/drivers/ubt/ng_ubt_var.h,v 1.2 2008/06/26 23:05:40 dillon Exp $
+ * $FreeBSD: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h 196219 2009-08-14 20:03:53Z jhb $
  */
 
 #ifndef _NG_UBT_VAR_H_
-#define _NG_UBT_VAR_H_
-
-/* pullup wrapper */
-#define NG_UBT_M_PULLUP(m, s) \
-       do { \
-               if ((m)->m_len < (s)) \
-                       (m) = m_pullup((m), (s)); \
-               if ((m) == NULL) \
-                       NG_UBT_ALERT("%s: %s - m_pullup(%d) failed\n", \
-                               __func__, device_get_nameunit(sc->sc_dev), (s)); \
-       } while (0)
+#define        _NG_UBT_VAR_H_  1
 
 /* Debug printf's */
-#define NG_UBT_ALERT   if (sc->sc_debug >= NG_UBT_ALERT_LEVEL) printf
-#define NG_UBT_ERR     if (sc->sc_debug >= NG_UBT_ERR_LEVEL)   printf
-#define NG_UBT_WARN    if (sc->sc_debug >= NG_UBT_WARN_LEVEL)  printf
-#define NG_UBT_INFO    if (sc->sc_debug >= NG_UBT_INFO_LEVEL)  printf
+#define        UBT_DEBUG(level, sc, fmt, ...)                          \
+do {                                                           \
+       if ((sc)->sc_debug >= (level))                          \
+               device_printf((sc)->sc_dev, "%s:%d: " fmt,      \
+                       __FUNCTION__, __LINE__,## __VA_ARGS__); \
+} while (0)
+
+#define        UBT_ALERT(...)          UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__)
+#define        UBT_ERR(...)            UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__)
+#define        UBT_WARN(...)           UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__)
+#define        UBT_INFO(...)           UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__)
+
+#define UBT_NG_LOCK(sc)                lockmgr(&(sc)->sc_ng_lock, LK_EXCLUSIVE)
+#define UBT_NG_UNLOCK(sc)      lockmgr(&(sc)->sc_ng_lock, LK_RELEASE)
 
 /* Bluetooth USB control request type */
-#define UBT_HCI_REQUEST                0x20
-#define UBT_DEFAULT_QLEN       12
+#define        UBT_HCI_REQUEST         0x20
+#define        UBT_DEFAULT_QLEN        64
+#define        UBT_ISOC_NFRAMES        32      /* should be factor of 8 */
+
+/* Bluetooth USB defines */
+enum {
+       /* Interface #0 transfers */
+       UBT_IF_0_BULK_DT_WR = 0,
+       UBT_IF_0_BULK_DT_RD,
+       UBT_IF_0_INTR_DT_RD,
+       UBT_IF_0_CTRL_DT_WR,
+       
+       /* Interface #1 transfers */
+       UBT_IF_1_ISOC_DT_RD1,
+       UBT_IF_1_ISOC_DT_RD2,
+       UBT_IF_1_ISOC_DT_WR1,
+       UBT_IF_1_ISOC_DT_WR2,
+
+       UBT_N_TRANSFER,         /* total number of transfers */
+};
 
 /* USB device softc structure */
 struct ubt_softc {
+       device_t                sc_dev;         /* for debug printf */
+
        /* State */
-       ng_ubt_node_debug_ep     sc_debug;      /* debug level */
-       u_int32_t                sc_flags;      /* device flags */
-#define UBT_NEED_FRAME_TYPE    (1 << 0)        /* device required frame type */
-#define UBT_HAVE_FRAME_TYPE UBT_NEED_FRAME_TYPE
-#define UBT_CMD_XMIT           (1 << 1)        /* CMD xmit in progress */
-#define UBT_ACL_XMIT           (1 << 2)        /* ACL xmit in progress */
-#define UBT_SCO_XMIT           (1 << 3)        /* SCO xmit in progress */
-#define UBT_EVT_RECV           (1 << 4)        /* EVN recv in progress */
-#define UBT_ACL_RECV           (1 << 5)        /* ACL recv in progress */
-#define UBT_SCO_RECV           (1 << 6)        /* SCO recv in progress */
-#define UBT_CTRL_DEV           (1 << 7)        /* ctrl device is open */
-#define UBT_INTR_DEV           (1 << 8)        /* intr device is open */
-#define UBT_BULK_DEV           (1 << 9)        /* bulk device is open */
-#define UBT_ANY_DEV            (UBT_CTRL_DEV|UBT_INTR_DEV|UBT_BULK_DEV)
-
-       ng_ubt_node_stat_ep      sc_stat;       /* statistic */
-#define NG_UBT_STAT_PCKTS_SENT(s)      (s).pckts_sent ++
-#define NG_UBT_STAT_BYTES_SENT(s, n)   (s).bytes_sent += (n)
-#define NG_UBT_STAT_PCKTS_RECV(s)      (s).pckts_recv ++
-#define NG_UBT_STAT_BYTES_RECV(s, n)   (s).bytes_recv += (n)
-#define NG_UBT_STAT_OERROR(s)          (s).oerrors ++
-#define NG_UBT_STAT_IERROR(s)          (s).ierrors ++
-#define NG_UBT_STAT_RESET(s)           bzero(&(s), sizeof((s)))
+       ng_ubt_node_debug_ep    sc_debug;       /* debug level */
+
+       ng_ubt_node_stat_ep     sc_stat;        /* statistic */
+#define        UBT_STAT_PCKTS_SENT(sc)         (sc)->sc_stat.pckts_sent ++
+#define        UBT_STAT_BYTES_SENT(sc, n)      (sc)->sc_stat.bytes_sent += (n)
+#define        UBT_STAT_PCKTS_RECV(sc)         (sc)->sc_stat.pckts_recv ++
+#define        UBT_STAT_BYTES_RECV(sc, n)      (sc)->sc_stat.bytes_recv += (n)
+#define        UBT_STAT_OERROR(sc)             (sc)->sc_stat.oerrors ++
+#define        UBT_STAT_IERROR(sc)             (sc)->sc_stat.ierrors ++
+#define        UBT_STAT_RESET(sc)      bzero(&(sc)->sc_stat, sizeof((sc)->sc_stat))
 
        /* USB device specific */
-       device_t                 sc_dev;        /* pointer back to USB device */
-       usbd_device_handle       sc_udev;       /* USB device handle */
-
-       usbd_interface_handle    sc_iface0;     /* USB interface 0 */
-       usbd_interface_handle    sc_iface1;     /* USB interface 1 */
-
-       /* Interrupt pipe (HCI events) */
-       int                      sc_intr_ep;    /* interrupt endpoint */
-       usbd_pipe_handle         sc_intr_pipe;  /* interrupt pipe handle */
-       usbd_xfer_handle         sc_intr_xfer;  /* intr xfer */
-       struct mbuf             *sc_intr_buffer; /* interrupt buffer */
-
-       /* Control pipe (HCI commands) */
-       usbd_xfer_handle         sc_ctrl_xfer;  /* control xfer handle */
-       void                    *sc_ctrl_buffer; /* control buffer */
-       struct ng_bt_mbufq       sc_cmdq;       /* HCI command queue */
-#define UBT_CTRL_BUFFER_SIZE \
-               (sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE)
-
-       /* Bulk in pipe (ACL data) */
-       int                      sc_bulk_in_ep; /* bulk-in enpoint */
-       usbd_pipe_handle         sc_bulk_in_pipe; /* bulk-in pipe */
-       usbd_xfer_handle         sc_bulk_in_xfer; /* bulk-in xfer */
-       struct mbuf             *sc_bulk_in_buffer; /* bulk-in buffer */
-
-       /* Bulk out pipe (ACL data) */
-       int                      sc_bulk_out_ep; /* bulk-out endpoint */
-       usbd_pipe_handle         sc_bulk_out_pipe; /* bulk-out pipe */
-       usbd_xfer_handle         sc_bulk_out_xfer; /* bulk-out xfer */
-       void                    *sc_bulk_out_buffer; /* bulk-out buffer */
-       struct ng_bt_mbufq       sc_aclq;       /* ACL data queue */
-#define UBT_BULK_BUFFER_SIZE \
-               MCLBYTES /* XXX should be big enough to hold one frame */
-
-       /* Isoc. in pipe (SCO data) */
-       int                      sc_isoc_in_ep; /* isoc-in endpoint */
-       usbd_pipe_handle         sc_isoc_in_pipe; /* isoc-in pipe */
-       usbd_xfer_handle         sc_isoc_in_xfer; /* isoc-in xfer */
-       void                    *sc_isoc_in_buffer; /* isoc-in buffer */
-       u_int16_t               *sc_isoc_in_frlen; /* isoc-in. frame length */
-
-       /* Isoc. out pipe (ACL data) */
-       int                      sc_isoc_out_ep; /* isoc-out endpoint */
-       usbd_pipe_handle         sc_isoc_out_pipe; /* isoc-out pipe */
-       usbd_xfer_handle         sc_isoc_out_xfer; /* isoc-out xfer */
-       void                    *sc_isoc_out_buffer; /* isoc-in buffer */
-       u_int16_t               *sc_isoc_out_frlen; /* isoc-out. frame length */
-       struct ng_bt_mbufq       sc_scoq;       /* SCO data queue */
-
-       int                      sc_isoc_size; /* max. size of isoc. packet */
-       u_int32_t                sc_isoc_nframes; /* num. isoc. frames */
-#define UBT_ISOC_BUFFER_SIZE \
-               (sizeof(ng_hci_scodata_pkt_t) + NG_HCI_SCO_PKT_SIZE)
-       
+       struct lock             sc_if_lock;     /* interfaces lock */
+       struct usb_xfer *sc_xfer[UBT_N_TRANSFER];
+
+       struct lock             sc_ng_lock;     /* lock for shared NG data */
+
+       /* HCI commands */
+       struct ng_bt_mbufq      sc_cmdq;        /* HCI command queue */
+#define        UBT_CTRL_BUFFER_SIZE    (sizeof(struct usb_device_request) +    \
+                                sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE)
+#define        UBT_INTR_BUFFER_SIZE    (MCLBYTES-1)    /* reserve 1 byte for ID-tag */
+
+       /* ACL data */
+       struct ng_bt_mbufq      sc_aclq;        /* ACL data queue */
+#define        UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1)  /* reserve 1 byte for ID-tag */
+#define        UBT_BULK_WRITE_BUFFER_SIZE (MCLBYTES)
+
+       /* SCO data */
+       struct ng_bt_mbufq      sc_scoq;        /* SCO data queue */
+       struct mbuf             *sc_isoc_in_buffer; /* SCO reassembly buffer */
+
        /* Netgraph specific */
-       node_p                   sc_node;       /* pointer back to node */
-       hook_p                   sc_hook;       /* upstream hook */
+       node_p                  sc_node;        /* pointer back to node */
+       hook_p                  sc_hook;        /* upstream hook */
+
+       /* Glue */
+       int                     sc_task_flags;  /* task flags */
+#define UBT_FLAG_T_PENDING     (1 << 0)        /* task pending */
+#define UBT_FLAG_T_STOP_ALL    (1 << 1)        /* stop all xfers */
+#define UBT_FLAG_T_START_ALL   (1 << 2)        /* start all read and isoc
+                                                  write xfers */
+#define UBT_FLAG_T_START_CTRL  (1 << 3)        /* start control xfer (write) */
+#define UBT_FLAG_T_START_BULK  (1 << 4)        /* start bulk xfer (write) */
+
+       struct task             sc_task;
 };
 typedef struct ubt_softc       ubt_softc_t;
 typedef struct ubt_softc *     ubt_softc_p;
diff --git a/sys/netgraph7/bluetooth/drivers/ubtbcmfw/Makefile b/sys/netgraph7/bluetooth/drivers/ubtbcmfw/Makefile
new file mode 100644 (file)
index 0000000..e547485
--- /dev/null
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.1 2003/04/27 00:22:12 max Exp $
+# $FreeBSD: head/sys/modules/netgraph/bluetooth/ubtbcmfw/Makefile 192909 2009-05-27 16:43:40Z thompsa $
+
+CFLAGS+=       -I${.CURDIR}/../../include
+
+KMOD=          ubtbcmfw
+SRCS=          ubtbcmfw.c opt_bus.h opt_usb.h device_if.h bus_if.h \
+               usb_if.h usbdevs.h
+
+.include <bsd.kmod.mk>
index 43fcb9b..642a9b6 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*-
- * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * Copyright (c) 2003-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * SUCH DAMAGE.
  *
  * $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $
- * $FreeBSD: src/sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c,v 1.18 2007/06/23 04:34:38 imp Exp $
+ * $FreeBSD: head/sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c 223486 2011-06-24 02:30:02Z hselasky $
  */
 
+#include <sys/stdint.h>
 #include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
 #include <sys/systm.h>
-#include <sys/bus.h>
-#include <sys/conf.h>
-#include <sys/filio.h>
-#include <sys/fcntl.h>
 #include <sys/kernel.h>
+#include <sys/bus.h>
 #include <sys/module.h>
-#include <sys/poll.h>
-#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
 #include <sys/sysctl.h>
-#include <sys/uio.h>
-
-#include <dev/usb/usb.h>
-#include <dev/usb/usbdi.h>
-#include <dev/usb/usbdi_util.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
 
 #include "usbdevs.h"
+#include <bus/u4b/usb.h>
+#include <bus/u4b/usbdi.h>
+#include <bus/u4b/usb_ioctl.h>
+
+#define        USB_DEBUG_VAR usb_debug
+#include <bus/u4b/usb_debug.h>
+#include <bus/u4b/usb_dev.h>
 
 /*
  * Download firmware to BCM2033.
  */
 
-#define UBTBCMFW_CONFIG_NO     1       /* Config number */
-#define UBTBCMFW_IFACE_IDX     0       /* Control interface */
-#define UBTBCMFW_INTR_IN_EP    0x81    /* Fixed endpoint */
-#define UBTBCMFW_BULK_OUT_EP   0x02    /* Fixed endpoint */
-#define UBTBCMFW_INTR_IN       UE_GET_ADDR(UBTBCMFW_INTR_IN_EP)
-#define UBTBCMFW_BULK_OUT      UE_GET_ADDR(UBTBCMFW_BULK_OUT_EP)
+#define        UBTBCMFW_CONFIG_NO      1       /* Config number */
+#define        UBTBCMFW_IFACE_IDX      0       /* Control interface */
+
+#define        UBTBCMFW_BSIZE          1024
+#define        UBTBCMFW_IFQ_MAXLEN     2
+
+enum {
+       UBTBCMFW_BULK_DT_WR = 0,
+       UBTBCMFW_INTR_DT_RD,
+       UBTBCMFW_N_TRANSFER,
+};
 
 struct ubtbcmfw_softc {
-       device_t                sc_dev;                 /* base device */
-       usbd_device_handle      sc_udev;                /* USB device handle */
-       struct cdev *sc_ctrl_dev;               /* control device */
-       struct cdev *sc_intr_in_dev;            /* interrupt device */
-       struct cdev *sc_bulk_out_dev;   /* bulk device */
-       usbd_pipe_handle        sc_intr_in_pipe;        /* interrupt pipe */
-       usbd_pipe_handle        sc_bulk_out_pipe;       /* bulk out pipe */
-       int                     sc_flags;
-#define UBTBCMFW_CTRL_DEV      (1 << 0)
-#define UBTBCMFW_INTR_IN_DEV   (1 << 1)
-#define UBTBCMFW_BULK_OUT_DEV  (1 << 2)
-       int                     sc_refcnt;
-       int                     sc_dying;
+       struct usb_device       *sc_udev;
+       struct lock             sc_lock;
+       struct usb_xfer *sc_xfer[UBTBCMFW_N_TRANSFER];
+       struct usb_fifo_sc      sc_fifo;
 };
 
-typedef struct ubtbcmfw_softc  *ubtbcmfw_softc_p;
+/*
+ * Prototypes
+ */
+
+static device_probe_t          ubtbcmfw_probe;
+static device_attach_t         ubtbcmfw_attach;
+static device_detach_t         ubtbcmfw_detach;
+
+static usb_callback_t          ubtbcmfw_write_callback;
+static usb_callback_t          ubtbcmfw_read_callback;
+
+static usb_fifo_close_t        ubtbcmfw_close;
+static usb_fifo_cmd_t          ubtbcmfw_start_read;
+static usb_fifo_cmd_t          ubtbcmfw_start_write;
+static usb_fifo_cmd_t          ubtbcmfw_stop_read;
+static usb_fifo_cmd_t          ubtbcmfw_stop_write;
+static usb_fifo_ioctl_t        ubtbcmfw_ioctl;
+static usb_fifo_open_t         ubtbcmfw_open;
+
+static struct usb_fifo_methods ubtbcmfw_fifo_methods = 
+{
+       .f_close =              &ubtbcmfw_close,
+       .f_ioctl =              &ubtbcmfw_ioctl,
+       .f_open =               &ubtbcmfw_open,
+       .f_start_read =         &ubtbcmfw_start_read,
+       .f_start_write =        &ubtbcmfw_start_write,
+       .f_stop_read =          &ubtbcmfw_stop_read,
+       .f_stop_write =         &ubtbcmfw_stop_write,
+       .basename[0] =          "ubtbcmfw",
+       .basename[1] =          "ubtbcmfw",
+       .basename[2] =          "ubtbcmfw",
+       .postfix[0] =           "",
+       .postfix[1] =           ".1",
+       .postfix[2] =           ".2",
+};
 
 /*
- * Device methods
+ * Device's config structure
  */
 
-#define UBTBCMFW_UNIT(n)       ((minor(n) >> 4) & 0xf)
-#define UBTBCMFW_ENDPOINT(n)   (minor(n) & 0xf)
-#define UBTBCMFW_MINOR(u, e)   (((u) << 4) | (e))
-#define UBTBCMFW_BSIZE         1024
-
-static d_open_t                ubtbcmfw_open;
-static d_close_t       ubtbcmfw_close;
-static d_read_t                ubtbcmfw_read;
-static d_write_t       ubtbcmfw_write;
-static d_ioctl_t       ubtbcmfw_ioctl;
-static d_poll_t                ubtbcmfw_poll;
-
-static struct cdevsw   ubtbcmfw_cdevsw = {
-       .d_version =    D_VERSION,
-       .d_flags =      D_NEEDGIANT,
-       .d_open =       ubtbcmfw_open,
-       .d_close =      ubtbcmfw_close,
-       .d_read =       ubtbcmfw_read,
-       .d_write =      ubtbcmfw_write,
-       .d_ioctl =      ubtbcmfw_ioctl,
-       .d_poll =       ubtbcmfw_poll,
-       .d_name =       "ubtbcmfw",
+static const struct usb_config ubtbcmfw_config[UBTBCMFW_N_TRANSFER] =
+{
+       [UBTBCMFW_BULK_DT_WR] = {
+               .type =         UE_BULK,
+               .endpoint =     0x02,   /* fixed */
+               .direction =    UE_DIR_OUT,
+               .if_index =     UBTBCMFW_IFACE_IDX,
+               .bufsize =      UBTBCMFW_BSIZE,
+               .flags =        { .pipe_bof = 1, .force_short_xfer = 1,
+                                 .proxy_buffer = 1, },
+               .callback =     &ubtbcmfw_write_callback,
+       },
+
+       [UBTBCMFW_INTR_DT_RD] = {
+               .type =         UE_INTERRUPT,
+               .endpoint =     0x01,   /* fixed */
+               .direction =    UE_DIR_IN,
+               .if_index =     UBTBCMFW_IFACE_IDX,
+               .bufsize =      UBTBCMFW_BSIZE,
+               .flags =        { .pipe_bof = 1, .short_xfer_ok = 1,
+                                 .proxy_buffer = 1, },
+               .callback =     &ubtbcmfw_read_callback,
+       },
 };
 
 /*
  * Module
  */
 
-static device_probe_t ubtbcmfw_match;
-static device_attach_t ubtbcmfw_attach;
-static device_detach_t ubtbcmfw_detach;
+static devclass_t      ubtbcmfw_devclass;
 
-static device_method_t ubtbcmfw_methods[] = {
-       /* Device interface */
-       DEVMETHOD(device_probe,         ubtbcmfw_match),
-       DEVMETHOD(device_attach,        ubtbcmfw_attach),
-       DEVMETHOD(device_detach,        ubtbcmfw_detach),
+static device_method_t ubtbcmfw_methods[] =
+{
+       DEVMETHOD(device_probe, ubtbcmfw_probe),
+       DEVMETHOD(device_attach, ubtbcmfw_attach),
+       DEVMETHOD(device_detach, ubtbcmfw_detach),
 
        DEVMETHOD_END
 };
 
-static driver_t ubtbcmfw_driver = {
-       "ubtbcmfw",
-       ubtbcmfw_methods,
-       sizeof(struct ubtbcmfw_softc)
+static driver_t                ubtbcmfw_driver =
+{
+       .name =         "ubtbcmfw",
+       .methods =      ubtbcmfw_methods,
+       .size =         sizeof(struct ubtbcmfw_softc),
 };
 
-static devclass_t ubtbcmfw_devclass;
-
+DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0);
 MODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
-DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass,
-             usbd_driver_load, 0);
 
 /*
  * Probe for a USB Bluetooth device
  */
 
 static int
-ubtbcmfw_match(device_t self)
+ubtbcmfw_probe(device_t dev)
 {
-#define        USB_PRODUCT_BROADCOM_BCM2033NF  0x2033
-       struct usb_attach_arg *uaa = device_get_ivars(self);
+       static const STRUCT_USB_HOST_ID devs[] = {
+       /* Broadcom BCM2033 devices only */
+       { USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) },
+       };
 
-       if (uaa->iface != NULL)
-               return (UMATCH_NONE);
+       struct usb_attach_arg   *uaa = device_get_ivars(dev);
 
-       /* Match the boot device. */
-       if (uaa->vendor == USB_VENDOR_BROADCOM &&
-           uaa->product == USB_PRODUCT_BROADCOM_BCM2033NF)
-               return (UMATCH_VENDOR_PRODUCT);
+       if (uaa->usb_mode != USB_MODE_HOST)
+               return (ENXIO);
 
-       return (UMATCH_NONE);
-}
+       if (uaa->info.bIfaceIndex != 0)
+               return (ENXIO);
+
+       return (usbd_lookup_id_by_uaa(devs, sizeof(devs), uaa));
+} /* ubtbcmfw_probe */
 
 /*
  * Attach the device
  */
 
 static int
-ubtbcmfw_attach(device_t self)
+ubtbcmfw_attach(device_t dev)
 {
-       struct ubtbcmfw_softc *sc = device_get_softc(self);
-       struct usb_attach_arg *uaa = device_get_ivars(self);
-       usbd_interface_handle   iface;
-       usbd_status             err;
+       struct usb_attach_arg   *uaa = device_get_ivars(dev);
+       struct ubtbcmfw_softc   *sc = device_get_softc(dev);
+       uint8_t                 iface_index;
+       int                     error;
 
-       sc->sc_dev = self;
        sc->sc_udev = uaa->device;
 
-       sc->sc_ctrl_dev = sc->sc_intr_in_dev = sc->sc_bulk_out_dev = NULL;
-       sc->sc_intr_in_pipe = sc->sc_bulk_out_pipe = NULL;
-       sc->sc_flags = sc->sc_refcnt = sc->sc_dying = 0;
+       device_set_usb_desc(dev);
 
-       err = usbd_set_config_no(sc->sc_udev, UBTBCMFW_CONFIG_NO, 1);
-       if (err) {
-               printf("%s: setting config no failed. %s\n",
-                       device_get_nameunit(sc->sc_dev), usbd_errstr(err));
-               goto bad;
-       }
+       lockinit(&sc->sc_lock, "ubtbcmfw lock", 0, LK_CANRECURSE);
 
-       err = usbd_device2interface_handle(sc->sc_udev, UBTBCMFW_IFACE_IDX,
-                       &iface);
-       if (err) {
-               printf("%s: getting interface handle failed. %s\n",
-                       device_get_nameunit(sc->sc_dev), usbd_errstr(err));
-               goto bad;
+       iface_index = UBTBCMFW_IFACE_IDX;
+       error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+                               ubtbcmfw_config, UBTBCMFW_N_TRANSFER,
+                               sc, &sc->sc_lock);
+       if (error != 0) {
+               device_printf(dev, "allocating USB transfers failed. %s\n",
+                       usbd_errstr(error));
+               goto detach;
        }
 
-       /* Will be used as a bulk pipe */
-       err = usbd_open_pipe(iface, UBTBCMFW_INTR_IN_EP, 0,
-                       &sc->sc_intr_in_pipe);
-       if (err) {
-               printf("%s: open intr in failed. %s\n",
-                       device_get_nameunit(sc->sc_dev), usbd_errstr(err));
-               goto bad;
+       error = usb_fifo_attach(uaa->device, sc, &sc->sc_lock,
+                       &ubtbcmfw_fifo_methods, &sc->sc_fifo,
+                       device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
+                       UID_ROOT, GID_OPERATOR, 0644);
+       if (error != 0) {
+               device_printf(dev, "could not attach fifo. %s\n",
+                       usbd_errstr(error));
+               goto detach;
        }
 
-       err = usbd_open_pipe(iface, UBTBCMFW_BULK_OUT_EP, 0,
-                       &sc->sc_bulk_out_pipe);
-       if (err) {
-               printf("%s: open bulk out failed. %s\n",
-                       device_get_nameunit(sc->sc_dev), usbd_errstr(err));
-               goto bad;
-       }
+       return (0);     /* success */
 
-       /* Create device nodes */
-       sc->sc_ctrl_dev = make_dev(&ubtbcmfw_cdevsw,
-               UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), 0),
-               UID_ROOT, GID_OPERATOR, 0644,
-               "%s", device_get_nameunit(sc->sc_dev));
+detach:
+       ubtbcmfw_detach(dev);
 
-       sc->sc_intr_in_dev = make_dev(&ubtbcmfw_cdevsw,
-               UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), UBTBCMFW_INTR_IN),
-               UID_ROOT, GID_OPERATOR, 0644,
-               "%s.%d", device_get_nameunit(sc->sc_dev), UBTBCMFW_INTR_IN);
-
-       sc->sc_bulk_out_dev = make_dev(&ubtbcmfw_cdevsw,
-               UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), UBTBCMFW_BULK_OUT),
-               UID_ROOT, GID_OPERATOR, 0644,
-               "%s.%d", device_get_nameunit(sc->sc_dev), UBTBCMFW_BULK_OUT);
-
-       return 0;
-bad:
-       ubtbcmfw_detach(self);  
-       return ENXIO;
-}
+       return (ENXIO); /* failure */
+} /* ubtbcmfw_attach */ 
 
 /*
  * Detach the device
  */
 
 static int
-ubtbcmfw_detach(device_t self)
+ubtbcmfw_detach(device_t dev)
 {
-       struct ubtbcmfw_softc *sc = device_get_softc(self);
+       struct ubtbcmfw_softc   *sc = device_get_softc(dev);
 
-       sc->sc_dying = 1;
-       if (-- sc->sc_refcnt >= 0) {
-               if (sc->sc_intr_in_pipe != NULL) 
-                       usbd_abort_pipe(sc->sc_intr_in_pipe);
+       usb_fifo_detach(&sc->sc_fifo);
 
-               if (sc->sc_bulk_out_pipe != NULL) 
-                       usbd_abort_pipe(sc->sc_bulk_out_pipe);
+       usbd_transfer_unsetup(sc->sc_xfer, UBTBCMFW_N_TRANSFER);
 
-               usb_detach_wait(sc->sc_dev);
-       }
-
-       /* Destroy device nodes */
-       if (sc->sc_bulk_out_dev != NULL) {
-               destroy_dev(sc->sc_bulk_out_dev);
-               sc->sc_bulk_out_dev = NULL;
-       }
-
-       if (sc->sc_intr_in_dev != NULL) {
-               destroy_dev(sc->sc_intr_in_dev);
-               sc->sc_intr_in_dev = NULL;
-       }
-
-       if (sc->sc_ctrl_dev != NULL) {
-               destroy_dev(sc->sc_ctrl_dev);
-               sc->sc_ctrl_dev = NULL;
-       }
-
-       /* Close pipes */
-       if (sc->sc_intr_in_pipe != NULL) {
-               usbd_close_pipe(sc->sc_intr_in_pipe);
-               sc->sc_intr_in_pipe = NULL;
-       }
-
-       if (sc->sc_bulk_out_pipe != NULL) {
-               usbd_close_pipe(sc->sc_bulk_out_pipe);
-               sc->sc_intr_in_pipe = NULL;
-       }
+       lockuninit(&sc->sc_lock);
 
        return (0);
-}
+} /* ubtbcmfw_detach */
 
 /*
- * Open endpoint device
- * XXX FIXME softc locking
+ * USB write callback
  */
 
-static int
-ubtbcmfw_open(struct cdev *dev, int flag, int mode, struct thread *p)
+static void
+ubtbcmfw_write_callback(struct usb_xfer *xfer, usb_error_t error)
 {
-       ubtbcmfw_softc_p        sc = NULL;
-       int                     error = 0;
-
-       /* checks for sc != NULL */
-       sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
-       if (sc == NULL)
-               return (ENXIO);
-       if (sc->sc_dying)
-               return (ENXIO);
-
-       switch (UBTBCMFW_ENDPOINT(dev)) {
-       case USB_CONTROL_ENDPOINT:
-               if (!(sc->sc_flags & UBTBCMFW_CTRL_DEV))
-                       sc->sc_flags |= UBTBCMFW_CTRL_DEV;
-               else
-                       error = EBUSY;
-               break;
-
-       case UBTBCMFW_INTR_IN:
-               if (!(sc->sc_flags & UBTBCMFW_INTR_IN_DEV)) {
-                       if (sc->sc_intr_in_pipe != NULL)
-                               sc->sc_flags |= UBTBCMFW_INTR_IN_DEV;
-                       else
-                               error = ENXIO;
-               } else
-                       error = EBUSY;
-               break;
-
-       case UBTBCMFW_BULK_OUT:
-               if (!(sc->sc_flags & UBTBCMFW_BULK_OUT_DEV)) {
-                       if (sc->sc_bulk_out_pipe != NULL)
-                               sc->sc_flags |= UBTBCMFW_BULK_OUT_DEV;
-                       else
-                               error = ENXIO;
-               } else
-                       error = EBUSY;
+       struct ubtbcmfw_softc   *sc = usbd_xfer_softc(xfer);
+       struct usb_fifo *f = sc->sc_fifo.fp[USB_FIFO_TX];
+       struct usb_page_cache   *pc;
+       uint32_t                actlen;
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_SETUP:
+       case USB_ST_TRANSFERRED:
+setup_next:
+               pc = usbd_xfer_get_frame(xfer, 0);
+               if (usb_fifo_get_data(f, pc, 0, usbd_xfer_max_len(xfer),
+                           &actlen, 0)) {
+                       usbd_xfer_set_frame_len(xfer, 0, actlen);
+                       usbd_transfer_submit(xfer);
+               }
                break;
 
-       default:
-               error = ENXIO;
+       default: /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       /* try to clear stall first */
+                       usbd_xfer_set_stall(xfer);
+                       goto setup_next;
+               }
                break;
        }
-
-       return (error);
-}
+} /* ubtbcmfw_write_callback */
 
 /*
- * Close endpoint device
- * XXX FIXME softc locking
+ * USB read callback
  */
 
-static int
-ubtbcmfw_close(struct cdev *dev, int flag, int mode, struct thread *p)
+static void
+ubtbcmfw_read_callback(struct usb_xfer *xfer, usb_error_t error)
 {
-       ubtbcmfw_softc_p        sc = NULL;
-
-       sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
-       if (sc == NULL)
-               return (ENXIO);
-
-       switch (UBTBCMFW_ENDPOINT(dev)) {
-       case USB_CONTROL_ENDPOINT:
-               sc->sc_flags &= ~UBTBCMFW_CTRL_DEV;
+       struct ubtbcmfw_softc   *sc = usbd_xfer_softc(xfer);
+       struct usb_fifo *fifo = sc->sc_fifo.fp[USB_FIFO_RX];
+       struct usb_page_cache   *pc;
+       int                     actlen;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               pc = usbd_xfer_get_frame(xfer, 0);
+               usb_fifo_put_data(fifo, pc, 0, actlen, 1);
+               /* FALLTHROUGH */
+
+       case USB_ST_SETUP:
+setup_next:
+               if (usb_fifo_put_bytes_max(fifo) > 0) {
+                       usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+                       usbd_transfer_submit(xfer);
+               }
                break;
 
-       case UBTBCMFW_INTR_IN:
-               if (sc->sc_intr_in_pipe != NULL)
-                       usbd_abort_pipe(sc->sc_intr_in_pipe);
-
-               sc->sc_flags &= ~UBTBCMFW_INTR_IN_DEV;
+       default: /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       /* try to clear stall first */
+                       usbd_xfer_set_stall(xfer);
+                       goto setup_next;
+               }
                break;
+       }
+} /* ubtbcmfw_read_callback */
 
-       case UBTBCMFW_BULK_OUT:
-               if (sc->sc_bulk_out_pipe != NULL)
-                       usbd_abort_pipe(sc->sc_bulk_out_pipe);
+/*
+ * Called when we about to start read()ing from the device
+ */
 
-               sc->sc_flags &= ~UBTBCMFW_BULK_OUT_DEV;
-               break;
-       }
+static void
+ubtbcmfw_start_read(struct usb_fifo *fifo)
+{
+       struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
 
-       return (0);
-}
+       usbd_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
+} /* ubtbcmfw_start_read */
 
 /*
- * Read from the endpoint device
- * XXX FIXME softc locking
+ * Called when we about to stop reading (i.e. closing fifo)
  */
 
-static int
-ubtbcmfw_read(struct cdev *dev, struct uio *uio, int flag)
+static void
+ubtbcmfw_stop_read(struct usb_fifo *fifo)
 {
-       ubtbcmfw_softc_p        sc = NULL;
-       u_int8_t                buf[UBTBCMFW_BSIZE];
-       usbd_xfer_handle        xfer;
-       usbd_status             err;
-       int                     n, tn, error = 0;
-
-       sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
-       if (sc == NULL || sc->sc_dying)
-               return (ENXIO);
+       struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
 
-       if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_INTR_IN)
-               return (EOPNOTSUPP);
-       if (sc->sc_intr_in_pipe == NULL)
-               return (ENXIO);
+       usbd_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
+} /* ubtbcmfw_stop_read */
 
-       xfer = usbd_alloc_xfer(sc->sc_udev);
-       if (xfer == NULL)
-               return (ENOMEM);
+/*
+ * Called when we about to start write()ing to the device, poll()ing
+ * for write or flushing fifo
+ */
 
-       sc->sc_refcnt ++;
-
-       while ((n = (int)szmin(sizeof(buf), uio->uio_resid)) != 0) {
-               tn = n;
-               err = usbd_bulk_transfer(xfer, sc->sc_intr_in_pipe,
-                               USBD_SHORT_XFER_OK, USBD_DEFAULT_TIMEOUT,
-                               buf, &tn, "bcmrd");
-               switch (err) {
-               case USBD_NORMAL_COMPLETION:
-                       error = uiomove(buf, (size_t)tn, uio);
-                       break;
-
-               case USBD_INTERRUPTED:
-                       error = EINTR;
-                       break;
-
-               case USBD_TIMEOUT:
-                       error = ETIMEDOUT;
-                       break;
-
-               default:
-                       error = EIO;
-                       break;
-               }
+static void
+ubtbcmfw_start_write(struct usb_fifo *fifo)
+{
+       struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
 
-               if (error != 0 || tn < n)
-                       break;
-       }
+       usbd_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
+} /* ubtbcmfw_start_write */
 
-       usbd_free_xfer(xfer);
+/*
+ * Called when we about to stop writing (i.e. closing fifo)
+ */
 
-       if (-- sc->sc_refcnt < 0)
-               usb_detach_wakeup(sc->sc_dev);
+static void
+ubtbcmfw_stop_write(struct usb_fifo *fifo)
+{
+       struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
 
-       return (error);
-}
+       usbd_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
+} /* ubtbcmfw_stop_write */
 
 /*
- * Write into the endpoint device
- * XXX FIXME softc locking
+ * Called when fifo is open
  */
 
 static int
-ubtbcmfw_write(struct cdev *dev, struct uio *uio, int flag)
+ubtbcmfw_open(struct usb_fifo *fifo, int fflags)
 {
-       ubtbcmfw_softc_p        sc = NULL;
-       u_int8_t                buf[UBTBCMFW_BSIZE];
-       usbd_xfer_handle        xfer;
-       usbd_status             err;
-       int                     n, error = 0;
-
-       sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
-       if (sc == NULL || sc->sc_dying)
-               return (ENXIO);
-
-       if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_BULK_OUT)
-               return (EOPNOTSUPP);
-       if (sc->sc_bulk_out_pipe == NULL)
-               return (ENXIO);
-
-       xfer = usbd_alloc_xfer(sc->sc_udev);
-       if (xfer == NULL)
+       struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
+       struct usb_xfer *xfer;
+
+       /*
+        * f_open fifo method can only be called with either FREAD
+        * or FWRITE flag set at one time.
+        */
+
+       if (fflags & FREAD)
+               xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD];
+       else if (fflags & FWRITE)
+               xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR];
+       else
+               return (EINVAL);        /* should not happen */
+
+       if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(xfer),
+                       UBTBCMFW_IFQ_MAXLEN) != 0)
                return (ENOMEM);
 
-       sc->sc_refcnt ++;
-
-       while ((n = (int)szmin(sizeof(buf), uio->uio_resid)) != 0) {
-               error = uiomove(buf, (size_t)n, uio);
-               if (error != 0)
-                       break;
-
-               err = usbd_bulk_transfer(xfer, sc->sc_bulk_out_pipe,
-                               0, USBD_DEFAULT_TIMEOUT, buf, &n, "bcmwr");
-               switch (err) {
-               case USBD_NORMAL_COMPLETION:
-                       break;
-
-               case USBD_INTERRUPTED:
-                       error = EINTR;
-                       break;
-
-               case USBD_TIMEOUT:
-                       error = ETIMEDOUT;
-                       break;
-
-               default:
-                       error = EIO;
-                       break;
-               }
-
-               if (error != 0)
-                       break;
-       }
-
-       usbd_free_xfer(xfer);
+       return (0);
+} /* ubtbcmfw_open */
 
-       if (-- sc->sc_refcnt < 0)
-               usb_detach_wakeup(sc->sc_dev);
+/* 
+ * Called when fifo is closed
+ */
 
-       return (error);
-}
+static void
+ubtbcmfw_close(struct usb_fifo *fifo, int fflags)
+{
+       if (fflags & (FREAD | FWRITE))
+               usb_fifo_free_buffer(fifo);
+} /* ubtbcmfw_close */
 
 /*
- * Process ioctl on the endpoint device
- * XXX FIXME softc locking
+ * Process ioctl() on USB device
  */
 
 static int
-ubtbcmfw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
-  struct thread *p)
+ubtbcmfw_ioctl(struct usb_fifo *fifo, u_long cmd, void *data,
+    int fflags)
 {
-       ubtbcmfw_softc_p        sc = NULL;
+       struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
        int                     error = 0;
 
-       sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
-       if (sc == NULL || sc->sc_dying)
-               return (ENXIO);
-
-       if (UBTBCMFW_ENDPOINT(dev) != USB_CONTROL_ENDPOINT)
-               return (EOPNOTSUPP);
-
-       sc->sc_refcnt ++;
-
        switch (cmd) {
        case USB_GET_DEVICE_DESC:
-               *(usb_device_descriptor_t *) data =
-                               *usbd_get_device_descriptor(sc->sc_udev);
+               memcpy(data, usbd_get_device_descriptor(sc->sc_udev),
+                       sizeof(struct usb_device_descriptor));
                break;
 
        default:
@@ -532,46 +442,5 @@ ubtbcmfw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
                break;
        }
 
-       if (-- sc->sc_refcnt < 0)
-               usb_detach_wakeup(sc->sc_dev);
-
        return (error);
-}
-
-/*
- * Poll the endpoint device
- * XXX FIXME softc locking
- */
-
-static int
-ubtbcmfw_poll(struct cdev *dev, int events, struct thread *p)
-{
-       ubtbcmfw_softc_p        sc = NULL;
-       int                     revents = 0;
-
-       sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
-       if (sc == NULL)
-               return (ENXIO);
-
-       switch (UBTBCMFW_ENDPOINT(dev)) {
-       case UBTBCMFW_INTR_IN:
-               if (sc->sc_intr_in_pipe != NULL)
-                       revents |= events & (POLLIN | POLLRDNORM);
-               else
-                       revents = ENXIO;
-               break;
-
-       case UBTBCMFW_BULK_OUT:
-               if (sc->sc_bulk_out_pipe != NULL)
-                       revents |= events & (POLLOUT | POLLWRNORM);
-               else
-                       revents = ENXIO;
-               break;
-
-       default:
-               revents = EOPNOTSUPP;
-               break;
-       }
-
-       return (revents);
-}
+} /* ubtbcmfw_ioctl */