Author: emax
Date: Tue Jan 20 22:17:05 2009
New Revision: 187494
URL: http://svn.freebsd.org/changeset/base/187494

Log:
  Update (well, actually rewrite mostly) ng_ubt2 driver for USB2.
  
  Reviewed by:  HPS, alfred
  Blessed by:   HPS

Modified:
  head/sys/dev/usb2/bluetooth/ng_ubt2.c
  head/sys/dev/usb2/bluetooth/ng_ubt2_var.h

Modified: head/sys/dev/usb2/bluetooth/ng_ubt2.c
==============================================================================
--- head/sys/dev/usb2/bluetooth/ng_ubt2.c       Tue Jan 20 22:06:07 2009        
(r187493)
+++ head/sys/dev/usb2/bluetooth/ng_ubt2.c       Tue Jan 20 22:17:05 2009        
(r187494)
@@ -3,7 +3,7 @@
  */
 
 /*-
- * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmen...@yahoo.com>
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmen...@yahoo.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,6 +31,69 @@
  * $FreeBSD$
  */
 
+/*
+ * NOTE: ng_ubt2 driver has a split personality. On one side it is
+ * a USB2 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 3 locks (mutexes)
+ *
+ * 1) sc_if_mtx[0] - lock for device's interface #0. This lock is used
+ *    by USB2 for any USB request going over device's interface #0, i.e.
+ *    interrupt, control and bulk transfers.
+ * 
+ * 2) sc_if_mtx[1] - lock for device's interface #1. This lock is used
+ *    by USB2 for any USB request going over device's interface #1, i.e
+ *    isoc. transfers.
+ * 
+ * 3) sc_mbufq_mtx - lock for mbufq and task flags. This lock is used
+ *    to protect device's outgoing mbuf queues and task flags. 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 USB2) 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 lock. Whatever you do, *DO NOT*
+ *    not grab any long-sleep lock in the Netgraph context. In fact, the only
+ *    lock that is allowed in the Netgraph context is the sc_mbufq_mtx lock.
+ *
+ * 3) Taskqueue context. This is where ubt_task runs. Since we are NOT allowed
+ *    to grab any locks in the Netgraph context, and, USB2 requires us to
+ *    grab interface lock before doing things with transfers, we need to
+ *    transition from the Netgraph context to the Taskqueue context before
+ *    we can call into USB2 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 USB2 requires us to grab interface locks and we can not do that.
+ * To avoid this, 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 USB2).
+ *     Access to the outgoing queues and task flags is controlled by the
+ * sc_mbufq_mtx lock. It is an unavoidable evil. Again, sc_mbufq_mtx should
+ * really be a spin lock.
+ *     All USB callbacks accept Netgraph node pointer as private data. To
+ * ensure that Netgraph node pointer remains valid for the duration of the
+ * transfer, we grab a referrence to the node. In other words, if transfer is
+ * pending, then we should have a referrence on the node. NG_NODE_[NOT_]VALID
+ * macro is used to check if node is still present and pointer is valid.
+ */
+
 #include <dev/usb2/include/usb2_devid.h>
 #include <dev/usb2/include/usb2_standard.h>
 #include <dev/usb2/include/usb2_mfunc.h>
@@ -44,8 +107,11 @@
 #include <dev/usb2/core/usb2_lookup.h>
 #include <dev/usb2/core/usb2_util.h>
 #include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_transfer.h>
 
 #include <sys/mbuf.h>
+#include <sys/taskqueue.h>
 
 #include <netgraph/ng_message.h>
 #include <netgraph/netgraph.h>
@@ -57,71 +123,57 @@
 #include <dev/usb2/bluetooth/usb2_bluetooth.h>
 #include <dev/usb2/bluetooth/ng_ubt2_var.h>
 
-/*
- * USB methods
- */
-
-static device_probe_t ubt_probe;
-static device_attach_t ubt_attach;
-static device_detach_t ubt_detach;
-
-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),
-       {0, 0}
-};
-
-static driver_t ubt_driver = {
-       .name = "ubt",
-       .methods = ubt_methods,
-       .size = sizeof(struct ubt_softc),
-};
-
-/*
- * Netgraph methods
- */
-
-static ng_constructor_t ng_ubt_constructor;
-static ng_shutdown_t ng_ubt_shutdown;
-static ng_newhook_t ng_ubt_newhook;
-static ng_connect_t ng_ubt_connect;
-static ng_disconnect_t ng_ubt_disconnect;
-static ng_rcvmsg_t ng_ubt_rcvmsg;
-static ng_rcvdata_t ng_ubt_rcvdata;
+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 int             ubt_task_schedule(ubt_softc_p, int);
+static task_fn_t       ubt_task;
+static void            ubt_xfer_start(ubt_softc_p, int);
+
+/* Netgraph methods */
+static ng_constructor_t        ng_ubt_constructor;
+static ng_shutdown_t   ng_ubt_shutdown;
+static ng_newhook_t    ng_ubt_newhook;
+static ng_connect_t    ng_ubt_connect;
+static ng_disconnect_t ng_ubt_disconnect;
+static ng_rcvmsg_t     ng_ubt_rcvmsg;
+static ng_rcvdata_t    ng_ubt_rcvdata;
 
 /* Queue length */
-static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] =
+static const struct ng_parse_struct_field      ng_ubt_node_qlen_type_fields[] =
 {
-       {"queue", &ng_parse_int32_type,},
-       {"qlen", &ng_parse_int32_type,},
-       {NULL,}
+       { "queue", &ng_parse_int32_type, },
+       { "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
 };
 
 /* Stat info */
-static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] =
+static const struct ng_parse_struct_field      ng_ubt_node_stat_type_fields[] =
 {
-       {"pckts_recv", &ng_parse_uint32_type,},
-       {"bytes_recv", &ng_parse_uint32_type,},
-       {"pckts_sent", &ng_parse_uint32_type,},
-       {"bytes_sent", &ng_parse_uint32_type,},
-       {"oerrors", &ng_parse_uint32_type,},
-       {"ierrors", &ng_parse_uint32_type,},
-       {NULL,}
+       { "pckts_recv", &ng_parse_uint32_type, },
+       { "bytes_recv", &ng_parse_uint32_type, },
+       { "pckts_sent", &ng_parse_uint32_type, },
+       { "bytes_sent", &ng_parse_uint32_type, },
+       { "oerrors",    &ng_parse_uint32_type, },
+       { "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,
@@ -164,315 +216,266 @@ static const struct ng_cmdlist ng_ubt_cm
                NULL,
                NULL
        },
-       {0,}
+       { 0, }
 };
 
 /* Netgraph node type */
-static struct ng_type typestruct = {
-       .version = NG_ABI_VERSION,
-       .name = NG_UBT_NODE_TYPE,
-       .constructor = ng_ubt_constructor,
-       .rcvmsg = ng_ubt_rcvmsg,
-       .shutdown = ng_ubt_shutdown,
-       .newhook = ng_ubt_newhook,
-       .connect = ng_ubt_connect,
-       .rcvdata = ng_ubt_rcvdata,
-       .disconnect = ng_ubt_disconnect,
-       .cmdlist = ng_ubt_cmdlist
-};
-
-/* USB methods */
-
-static usb2_callback_t ubt_ctrl_write_callback;
-static usb2_callback_t ubt_intr_read_callback;
-static usb2_callback_t ubt_intr_read_clear_stall_callback;
-static usb2_callback_t ubt_bulk_read_callback;
-static usb2_callback_t ubt_bulk_read_clear_stall_callback;
-static usb2_callback_t ubt_bulk_write_callback;
-static usb2_callback_t ubt_bulk_write_clear_stall_callback;
-static usb2_callback_t ubt_isoc_read_callback;
-static usb2_callback_t ubt_isoc_write_callback;
-
-static int     ubt_modevent(module_t, int, void *);
-static void    ubt_intr_read_complete(node_p, hook_p, void *, int);
-static void    ubt_bulk_read_complete(node_p, hook_p, void *, int);
-static void    ubt_isoc_read_complete(node_p, hook_p, void *, int);
-
-/* USB config */
-static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] = {
-
-       [0] = {
-               .type = UE_BULK,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_OUT,
-               .mh.bufsize = UBT_BULK_WRITE_BUFFER_SIZE,
-               .mh.flags = {.pipe_bof = 1,},
-               .mh.callback = &ubt_bulk_write_callback,
-       },
-
-       [1] = {
-               .type = UE_BULK,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_IN,
-               .mh.bufsize = UBT_BULK_READ_BUFFER_SIZE,
-               .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
-               .mh.callback = &ubt_bulk_read_callback,
-       },
-
-       [2] = {
-               .type = UE_INTERRUPT,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_IN,
-               .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
-               .mh.bufsize = 0x110,    /* bytes */
-               .mh.callback = &ubt_intr_read_callback,
-       },
-
-       [3] = {
-               .type = UE_CONTROL,
-               .endpoint = 0x00,       /* Control pipe */
-               .direction = UE_DIR_ANY,
-               .mh.bufsize = (sizeof(struct usb2_device_request) + 
UBT_CTRL_BUFFER_SIZE),
-               .mh.callback = &ubt_ctrl_write_callback,
-               .mh.timeout = 5000,     /* 5 seconds */
-       },
-
-       [4] = {
-               .type = UE_CONTROL,
-               .endpoint = 0x00,       /* Control pipe */
-               .direction = UE_DIR_ANY,
-               .mh.bufsize = sizeof(struct usb2_device_request),
-               .mh.callback = &ubt_bulk_write_clear_stall_callback,
-               .mh.timeout = 1000,     /* 1 second */
-               .mh.interval = 50,      /* 50ms */
-       },
-
-       [5] = {
-               .type = UE_CONTROL,
-               .endpoint = 0x00,       /* Control pipe */
-               .direction = UE_DIR_ANY,
-               .mh.bufsize = sizeof(struct usb2_device_request),
-               .mh.callback = &ubt_bulk_read_clear_stall_callback,
-               .mh.timeout = 1000,     /* 1 second */
-               .mh.interval = 50,      /* 50ms */
-       },
-
-       [6] = {
-               .type = UE_CONTROL,
-               .endpoint = 0x00,       /* Control pipe */
-               .direction = UE_DIR_ANY,
-               .mh.bufsize = sizeof(struct usb2_device_request),
-               .mh.callback = &ubt_intr_read_clear_stall_callback,
-               .mh.timeout = 1000,     /* 1 second */
-               .mh.interval = 50,      /* 50ms */
-       },
-};
-
-/* USB config */
-static const struct usb2_config
-       ubt_config_if_1_full_speed[UBT_IF_1_N_TRANSFER] = {
-
-       [0] = {
-               .type = UE_ISOCHRONOUS,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_IN,
-               .mh.bufsize = 0,        /* use "wMaxPacketSize * frames" */
-               .mh.frames = UBT_ISOC_NFRAMES,
-               .mh.flags = {.short_xfer_ok = 1,},
-               .mh.callback = &ubt_isoc_read_callback,
-       },
-
-       [1] = {
-               .type = UE_ISOCHRONOUS,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_IN,
-               .mh.bufsize = 0,        /* use "wMaxPacketSize * frames" */
-               .mh.frames = UBT_ISOC_NFRAMES,
-               .mh.flags = {.short_xfer_ok = 1,},
-               .mh.callback = &ubt_isoc_read_callback,
-       },
-
-       [2] = {
-               .type = UE_ISOCHRONOUS,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_OUT,
-               .mh.bufsize = 0,        /* use "wMaxPacketSize * frames" */
-               .mh.frames = UBT_ISOC_NFRAMES,
-               .mh.flags = {.short_xfer_ok = 1,},
-               .mh.callback = &ubt_isoc_write_callback,
-       },
-
-       [3] = {
-               .type = UE_ISOCHRONOUS,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_OUT,
-               .mh.bufsize = 0,        /* use "wMaxPacketSize * frames" */
-               .mh.frames = UBT_ISOC_NFRAMES,
-               .mh.flags = {.short_xfer_ok = 1,},
-               .mh.callback = &ubt_isoc_write_callback,
-       },
-};
-
-/* USB config */
-static const struct usb2_config
-       ubt_config_if_1_high_speed[UBT_IF_1_N_TRANSFER] = {
-
-       [0] = {
-               .type = UE_ISOCHRONOUS,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_IN,
-               .mh.bufsize = 0,        /* use "wMaxPacketSize * frames" */
-               .mh.frames = UBT_ISOC_NFRAMES * 8,
-               .mh.flags = {.short_xfer_ok = 1,},
-               .mh.callback = &ubt_isoc_read_callback,
-       },
-
-       [1] = {
-               .type = UE_ISOCHRONOUS,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_IN,
-               .mh.bufsize = 0,        /* use "wMaxPacketSize * frames" */
-               .mh.frames = UBT_ISOC_NFRAMES * 8,
-               .mh.flags = {.short_xfer_ok = 1,},
-               .mh.callback = &ubt_isoc_read_callback,
-       },
-
-       [2] = {
-               .type = UE_ISOCHRONOUS,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_OUT,
-               .mh.bufsize = 0,        /* use "wMaxPacketSize * frames" */
-               .mh.frames = UBT_ISOC_NFRAMES * 8,
-               .mh.flags = {.short_xfer_ok = 1,},
-               .mh.callback = &ubt_isoc_write_callback,
-       },
-
-       [3] = {
-               .type = UE_ISOCHRONOUS,
-               .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_OUT,
-               .mh.bufsize = 0,        /* use "wMaxPacketSize * frames" */
-               .mh.frames = UBT_ISOC_NFRAMES * 8,
-               .mh.flags = {.short_xfer_ok = 1,},
-               .mh.callback = &ubt_isoc_write_callback,
-       },
+static struct ng_type  typestruct =
+{
+       .version =      NG_ABI_VERSION,
+       .name =         NG_UBT_NODE_TYPE,
+       .constructor =  ng_ubt_constructor,
+       .rcvmsg =       ng_ubt_rcvmsg,
+       .shutdown =     ng_ubt_shutdown,
+       .newhook =      ng_ubt_newhook,
+       .connect =      ng_ubt_connect,
+       .rcvdata =      ng_ubt_rcvdata,
+       .disconnect =   ng_ubt_disconnect,
+       .cmdlist =      ng_ubt_cmdlist
 };
 
-/*
- * Module
- */
-
-DRIVER_MODULE(ng_ubt, ushub, ubt_driver, ubt_devclass, ubt_modevent, 0);
-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, usb2_bluetooth, 1, 1, 1);
-MODULE_DEPEND(ng_ubt, usb2_core, 1, 1, 1);
-
 /****************************************************************************
  ****************************************************************************
  **                              USB specific
  ****************************************************************************
  ****************************************************************************/
 
+/* USB methods */
+static usb2_callback_t ubt_ctrl_write_callback;
+static usb2_callback_t ubt_intr_read_callback;
+static usb2_callback_t ubt_intr_read_clear_stall_callback;
+static usb2_callback_t ubt_bulk_read_callback;
+static usb2_callback_t ubt_bulk_read_clear_stall_callback;
+static usb2_callback_t ubt_bulk_write_callback;
+static usb2_callback_t ubt_bulk_write_clear_stall_callback;
+static usb2_callback_t ubt_isoc_read_callback;
+static usb2_callback_t ubt_isoc_write_callback;
+
+static int     ubt_isoc_read_one_frame(struct usb2_xfer *, int);
+
 /*
- * Load/Unload the driver module
+ * 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_modevent(module_t mod, int event, void *data)
+static const struct usb2_config                ubt_config[UBT_N_TRANSFER] =
 {
-       int error;
+       /*
+        * Interface #0
+        */
 
-       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;
+       /* Outgoing bulk transfer - ACL packets */
+       [UBT_IF_0_BULK_DT_WR] = {
+               .type =         UE_BULK,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_OUT,
+               .mh.bufsize =   UBT_BULK_WRITE_BUFFER_SIZE,
+               .mh.flags =     { .pipe_bof = 1, },
+               .mh.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,
+               .mh.bufsize =   UBT_BULK_READ_BUFFER_SIZE,
+               .mh.flags =     { .pipe_bof = 1, .short_xfer_ok = 1, },
+               .mh.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,
+               .mh.flags =     { .pipe_bof = 1, .short_xfer_ok = 1, },
+               .mh.bufsize =   UBT_INTR_BUFFER_SIZE,
+               .mh.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,
+               .mh.bufsize =   UBT_CTRL_BUFFER_SIZE,
+               .mh.callback =  &ubt_ctrl_write_callback,
+               .mh.timeout =   5000,   /* 5 seconds */
+       },
+       /* Outgoing control transfer to clear stall on outgoing bulk transfer */
+       [UBT_IF_0_BULK_CS_WR] = {
+               .type =         UE_CONTROL,
+               .endpoint =     0x00,   /* control pipe */
+               .direction =    UE_DIR_ANY,
+               .mh.bufsize =   sizeof(struct usb2_device_request),
+               .mh.callback =  &ubt_bulk_write_clear_stall_callback,
+               .mh.timeout =   1000,   /* 1 second */
+               .mh.interval =  50,     /* 50ms */
+       },
+       /* Outgoing control transfer to clear stall on incoming bulk transfer */
+       [UBT_IF_0_BULK_CS_RD] = {
+               .type =         UE_CONTROL,
+               .endpoint =     0x00,   /* control pipe */
+               .direction =    UE_DIR_ANY,
+               .mh.bufsize =   sizeof(struct usb2_device_request),
+               .mh.callback =  &ubt_bulk_read_clear_stall_callback,
+               .mh.timeout =   1000,   /* 1 second */
+               .mh.interval =  50,     /* 50ms */
+       },
+       /*
+        * Outgoing control transfer to clear stall on incoming
+        * interrupt transfer
+        */
+       [UBT_IF_0_INTR_CS_RD] = {
+               .type =         UE_CONTROL,
+               .endpoint =     0x00,   /* control pipe */
+               .direction =    UE_DIR_ANY,
+               .mh.bufsize =   sizeof(struct usb2_device_request),
+               .mh.callback =  &ubt_intr_read_clear_stall_callback,
+               .mh.timeout =   1000,   /* 1 second */
+               .mh.interval =  50,     /* 50ms */
+       },
 
-       case MOD_UNLOAD:
-               error = ng_rmtype(&typestruct);
-               break;
+       /*
+        * Interface #1
+        */
 
-       default:
-               error = EOPNOTSUPP;
-               break;
-       }
-       return (error);
-}                                      /* ubt_modevent */
+       /* Incoming isochronous transfer #1 - SCO packets */
+       [UBT_IF_1_ISOC_DT_RD1] = {
+               .type =         UE_ISOCHRONOUS,
+               .endpoint =     UE_ADDR_ANY,
+               .direction =    UE_DIR_IN,
+               .mh.bufsize =   0,      /* use "wMaxPacketSize * frames" */
+               .mh.frames =    UBT_ISOC_NFRAMES,
+               .mh.flags =     { .short_xfer_ok = 1, },
+               .mh.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,
+               .mh.bufsize =   0,      /* use "wMaxPacketSize * frames" */
+               .mh.frames =    UBT_ISOC_NFRAMES,
+               .mh.flags =     { .short_xfer_ok = 1, },
+               .mh.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,
+               .mh.bufsize =   0,      /* use "wMaxPacketSize * frames" */
+               .mh.frames =    UBT_ISOC_NFRAMES,
+               .mh.flags =     { .short_xfer_ok = 1, },
+               .mh.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,
+               .mh.bufsize =   0,      /* use "wMaxPacketSize * frames" */
+               .mh.frames =    UBT_ISOC_NFRAMES,
+               .mh.flags =     { .short_xfer_ok = 1, },
+               .mh.callback =  &ubt_isoc_write_callback,
+       },
+};
 
 /*
  * 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 },
+ *     { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
  *
  * where VENDOR_ID and PRODUCT_ID are hex numbers.
  */
 static const struct usb2_device_id ubt_ignore_devs[] = {
        /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
-       {USB_VPI(USB_VENDOR_AVM, 0x2200, 0)},
+       { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
 };
 
 /* List of supported bluetooth devices */
 static const struct usb2_device_id ubt_devs[] = {
-       /* Generic Bluetooth class devices. */
-       {USB_IFACE_CLASS(UDCLASS_WIRELESS),
-               USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
-       USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH)},
+       /* Generic Bluetooth class devices */
+       { USB_IFACE_CLASS(UDCLASS_WIRELESS),
+         USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
+         USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
 
        /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
-       {USB_VPI(USB_VENDOR_AVM, 0x3800, 0)},
+       { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
 };
 
 /*
- * Probe for a USB Bluetooth device
+ * Probe for a USB Bluetooth device.
+ * USB context.
  */
 
 static int
 ubt_probe(device_t dev)
 {
-       struct usb2_attach_arg *uaa = device_get_ivars(dev);
+       struct usb2_attach_arg  *uaa = device_get_ivars(dev);
 
-       if (uaa->usb2_mode != USB_MODE_HOST) {
+       if (uaa->usb2_mode != USB_MODE_HOST)
                return (ENXIO);
-       }
-       if (uaa->info.bIfaceIndex != 0) {
+
+       if (uaa->info.bIfaceIndex != 0)
                return (ENXIO);
-       }
+
        if (usb2_lookup_id_by_uaa(ubt_ignore_devs,
-           sizeof(ubt_ignore_devs), uaa) == 0) {
+                       sizeof(ubt_ignore_devs), uaa) == 0)
                return (ENXIO);
-       }
+
        return (usb2_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa));
-}
+} /* ubt_probe */
 
 /*
- * Attach the device
+ * Attach the device.
+ * USB context.
  */
 
 static int
 ubt_attach(device_t dev)
 {
-       struct usb2_attach_arg *uaa = device_get_ivars(dev);
-       struct ubt_softc *sc = device_get_softc(dev);
-       const struct usb2_config *isoc_setup;
-       struct usb2_endpoint_descriptor *ed;
-       uint16_t wMaxPacketSize;
-       uint8_t alt_index;
-       uint8_t iface_index;
-       uint8_t i;
-       uint8_t j;
+       struct usb2_attach_arg          *uaa = device_get_ivars(dev);
+       struct ubt_softc                *sc = device_get_softc(dev);
+       struct usb2_endpoint_descriptor *ed;
+       uint16_t                        wMaxPacketSize;
+       uint8_t                         alt_index, iface_index, i, j;
 
        device_set_usb2_desc(dev);
 
        snprintf(sc->sc_name, sizeof(sc->sc_name),
-           "%s", device_get_nameunit(dev));
+               "%s", device_get_nameunit(dev));
+
+       /* 
+        * Create Netgraph node
+        */
+
+       sc->sc_hook = NULL;
+
+       if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
+               device_printf(dev, "could not create Netgraph node\n");
+               return (ENXIO);
+       }
+
+       /* Name Netgraph node */
+       if (ng_name_node(sc->sc_node, sc->sc_name) != 0) {
+               device_printf(dev, "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);
 
        /*
         * Initialize device softc structure
@@ -481,34 +484,28 @@ ubt_attach(device_t dev)
        /* state */
        sc->sc_debug = NG_UBT_WARN_LEVEL;
        sc->sc_flags = 0;
-       NG_UBT_STAT_RESET(sc->sc_stat);
+       UBT_STAT_RESET(sc);
 
-       /* control pipe */
-       NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
+       /* initialize locks */
+       mtx_init(&sc->sc_mbufq_mtx, "ubt mbufq", NULL, MTX_DEF);
+       mtx_init(&sc->sc_if_mtx[0], "ubt if0", NULL, MTX_DEF | MTX_RECURSE);
+       mtx_init(&sc->sc_if_mtx[1], "ubt if1", NULL, MTX_DEF | MTX_RECURSE);
 
-       /* bulk-out pipe */
+       /* 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);
 
-       /* isoc-out pipe */
-       NG_BT_MBUFQ_INIT(&sc->sc_scoq,
-           (usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ?
-           (2 * UBT_ISOC_NFRAMES * 8) :
-           (2 * UBT_ISOC_NFRAMES));
-
-       /* isoc-in pipe */
-       NG_BT_MBUFQ_INIT(&sc->sc_sciq,
-           (usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ?
-           (2 * UBT_ISOC_NFRAMES * 8) :
-           (2 * UBT_ISOC_NFRAMES));
-
-       /* netgraph part */
-       sc->sc_node = NULL;
-       sc->sc_hook = NULL;
+       /* initialize glue task */
+       sc->sc_task_flags = 0;
+       TASK_INIT(&sc->sc_task, 0, ubt_task, sc->sc_node);
 
        /*
         * Configure Bluetooth USB device. Discover all required USB
         * interfaces and endpoints.
         *
+        * Device is expected to be a high-speed device.
+        *
         * USB device must present two interfaces:
         * 1) Interface 0 that has 3 endpoints
         *      1) Interrupt endpoint to receive HCI events
@@ -523,794 +520,1006 @@ ubt_attach(device_t dev)
         * configurations with different packet size.
         */
 
+       bzero(&sc->sc_xfer, sizeof(sc->sc_xfer));
+
        /*
         * Interface 0
         */
 
-       mtx_init(&sc->sc_mtx, "ubt lock", NULL, MTX_DEF | MTX_RECURSE);
-
        iface_index = 0;
-       if (usb2_transfer_setup
-           (uaa->device, &iface_index, sc->sc_xfer_if_0, ubt_config_if_0,
-           UBT_IF_0_N_TRANSFER, sc, &sc->sc_mtx)) {
-               device_printf(dev, "Could not allocate transfers "
-                   "for interface 0!\n");
+       if (usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+                       ubt_config, UBT_IF_0_N_TRANSFER,
+                       sc->sc_node, &sc->sc_if_mtx[0])) {
+               device_printf(dev, "could not allocate transfers for " \
+                       "interface 0!\n");
                goto detach;
        }
+
        /*
         * Interface 1
-        * (search alternate settings, and find
-        *  the descriptor with the largest
+        * (search alternate settings, and find the descriptor with the largest
         *  wMaxPacketSize)
         */
-       isoc_setup =
-           ((usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ?
-           ubt_config_if_1_high_speed :
-           ubt_config_if_1_full_speed);
 
        wMaxPacketSize = 0;
-
-       /* search through all the descriptors looking for bidir mode */
-
-       alt_index = 0 - 1;
+       alt_index = 0;
        i = 0;
        j = 0;
+
+       /* Search through all the descriptors looking for bidir mode */
        while (1) {
                uint16_t temp;
 
-               ed = usb2_find_edesc(
-                   usb2_get_config_descriptor(uaa->device), 1, i, j);
+               ed = usb2_find_edesc(usb2_get_config_descriptor(uaa->device),
+                               1, i, j);
                if (ed == NULL) {
-                       if (j == 0) {
-                               /* end of interfaces */
-                               break;
-                       } else {
+                       if (j != 0) {
                                /* next interface */
                                j = 0;
-                               i++;
+                               i ++;
                                continue;
                        }
+
+                       break;  /* end of interfaces */
                }
+
                temp = UGETW(ed->wMaxPacketSize);
                if (temp > wMaxPacketSize) {
                        wMaxPacketSize = temp;
                        alt_index = i;
                }
-               j++;
-       }
 
-       if (usb2_set_alt_interface_index(uaa->device, 1, alt_index)) {
-               device_printf(dev, "Could not set alternate "
-                   "setting %d for interface 1!\n", alt_index);
-               goto detach;
-       }
-       iface_index = 1;
-       if (usb2_transfer_setup
-           (uaa->device, &iface_index, sc->sc_xfer_if_1,
-           isoc_setup, UBT_IF_1_N_TRANSFER, sc, &sc->sc_mtx)) {
-               device_printf(dev, "Could not allocate transfers "
-                   "for interface 1!\n");
-               goto detach;
+               j ++;
        }
-       /* create Netgraph node */
 
-       if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
-               printf("%s: Could not create Netgraph node\n",
-                   sc->sc_name);
-               sc->sc_node = NULL;
+       /* Set alt configuration only if we found it */
+       if (wMaxPacketSize > 0 &&
+           usb2_set_alt_interface_index(uaa->device, 1, alt_index)) {
+               device_printf(dev, "could not set alternate setting %d " \
+                       "for interface 1!\n", alt_index);
                goto detach;
        }
-       /* name node */
 
-       if (ng_name_node(sc->sc_node, sc->sc_name) != 0) {
-               printf("%s: Could not name Netgraph node\n",
-                   sc->sc_name);
-               NG_NODE_UNREF(sc->sc_node);
-               sc->sc_node = NULL;
+       iface_index = 1;
+       if (usb2_transfer_setup(uaa->device, &iface_index,
+                       &sc->sc_xfer[UBT_IF_0_N_TRANSFER],
+                       &ubt_config[UBT_IF_0_N_TRANSFER], UBT_IF_1_N_TRANSFER,
+                       sc->sc_node, &sc->sc_if_mtx[1])) {
+               device_printf(dev, "could not allocate transfers for " \
+                       "interface 1!\n");
                goto detach;
        }
-       NG_NODE_SET_PRIVATE(sc->sc_node, sc);
-       NG_NODE_FORCE_WRITER(sc->sc_node);
 
-       /* claim all interfaces on the device */
-
-       for (i = 1;; i++) {
-
-               if (usb2_get_iface(uaa->device, i) == NULL) {
-                       break;
-               }
+       /* Claim all interfaces on the device */
+       for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++)
                usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
-       }
 
-       return (0);                     /* success */
+       return (0); /* success */
 
 detach:
        ubt_detach(dev);
 
        return (ENXIO);
-}
+} /* ubt_attach */
 
 /*
- * Detach the device
+ * Detach the device.
+ * USB context.
  */
 
 int
 ubt_detach(device_t dev)
 {
-       struct ubt_softc *sc = device_get_softc(dev);
+       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);
+       /* Destroy Netgraph node */
+       if (node != NULL) {
                sc->sc_node = NULL;
-       }
-       /* free USB transfers, if any */
 
-       usb2_transfer_unsetup(sc->sc_xfer_if_0, UBT_IF_0_N_TRANSFER);
-
-       usb2_transfer_unsetup(sc->sc_xfer_if_1, UBT_IF_1_N_TRANSFER);
+               NG_NODE_SET_PRIVATE(node, NULL);
+               NG_NODE_REALLY_DIE(node);
+               NG_NODE_REF(node);
+               ng_rmnode_self(node);
+       }
 
-       mtx_destroy(&sc->sc_mtx);
+       /* Free USB transfers, if any */
+       usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
 
-       /* destroy queues */
+       if (node != NULL)
+               NG_NODE_UNREF(node);
 
+       /* Destroy queues */
+       UBT_MBUFQ_LOCK(sc);
        NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
        NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
        NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
-       NG_BT_MBUFQ_DESTROY(&sc->sc_sciq);
+       UBT_MBUFQ_UNLOCK(sc);
+
+       mtx_destroy(&sc->sc_if_mtx[0]);
+       mtx_destroy(&sc->sc_if_mtx[1]);
+       mtx_destroy(&sc->sc_mbufq_mtx);
 
        return (0);
-}
+} /* ubt_detach */
+
+/* 
+ * Called when outgoing control request (HCI command) has completed, i.e.
+ * HCI command was sent to the device.
+ * USB context.
+ */
 
 static void
 ubt_ctrl_write_callback(struct usb2_xfer *xfer)
 {
-       struct ubt_softc *sc = xfer->priv_sc;
-       struct usb2_device_request req;
-       struct mbuf *m;
+       node_p                          node = xfer->priv_sc;
+       struct ubt_softc                *sc;
+       struct usb2_device_request      req;
+       struct mbuf                     *m;
+
+       if (NG_NODE_NOT_VALID(node)) {
+               NG_NODE_UNREF(node);
+               return; /* netgraph node is gone */
+       }
+
+       sc = NG_NODE_PRIVATE(node);
 
        switch (USB_GET_STATE(xfer)) {
        case USB_ST_TRANSFERRED:
-tr_transferred:
+               if (xfer->error != 0)
+                       UBT_STAT_OERROR(sc);
+               else {
+                       UBT_INFO(sc, "sent %d bytes to control pipe\n",
+                               xfer->actlen);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to