USB2: ng_ubt2 patch

View: New views
14 Messages — Rating Filter:   Alert me  

USB2: ng_ubt2 patch

by Maksim Yevmenkin-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

dear bluetooth users,

please find attached patch for ng_ubt2 (well it is almost a complete rewrite).

this is an early preview and likely to have bugs. i briefly kicked the
tires to make sure it compiles and did some very quick testing, i.e.
run few hci commands, sdp transfers etc.

this is my first attempt to write something under usb2, so any
review/comments/suggestions/etc. are greatly appreciated.

thanks,
max

Index: ng_ubt2.c
===================================================================
--- ng_ubt2.c (revision 186800)
+++ ng_ubt2.c (working copy)
@@ -3,7 +3,7 @@
  */
 
 /*-
- * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@...>
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@...>
  * 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_0_mtx - 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_1_mtx - 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 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 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 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 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 outgoing queues and task flags is controlled by sc_mbufq_mtx
+ * lock. It is 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 point 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.
+ */
+
 #include <dev/usb2/include/usb2_devid.h>
 #include <dev/usb2/include/usb2_standard.h>
 #include <dev/usb2/include/usb2_mfunc.h>
@@ -46,6 +109,7 @@
 #include <dev/usb2/core/usb2_busdma.h>
 
 #include <sys/mbuf.h>
+#include <sys/taskqueue.h>
 
 #include <netgraph/ng_message.h>
 #include <netgraph/netgraph.h>
@@ -57,71 +121,56 @@
 #include <dev/usb2/bluetooth/usb2_bluetooth.h>
 #include <dev/usb2/bluetooth/ng_ubt2_var.h>
 
-/*
- * USB methods
- */
+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 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, uint32_t);
+static task_fn_t ubt_task;
 
-static devclass_t ubt_devclass;
+/* 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 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;
-
 /* 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,316 +213,280 @@
  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
+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 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 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 *, uint32_t);
 
-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
+ *
+ * The following desribes usb transfers that could be submitted
+ * on USB device's interface 0
+ *
+ * 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
+ */
 
-/* USB config */
-static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] = {
-
+static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] =
+{
+ /* Outgoing bulk transfer - ACL packets */
  [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,
+ .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 */
  [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,
+ .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 */
  [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,
+ .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 */
  [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 */
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) +
+ UBT_CTRL_BUFFER_SIZE), /* bytes */
+ .mh.callback = &ubt_ctrl_write_callback,
+ .mh.timeout = 5000, /* 5 seconds */
  },
-
+ /* Outgoing control transfer to clear stall on outgoing bulk transfer */
  [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 */
+ .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 */
  [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 */
+ .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
+ */
  [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 */
+ .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] = {
+/*
+ * USB config
+ *
+ * The following desribes usb transfers that could be submitted
+ * on USB device's interface 1
+ *
+ * Interface 1 on the UBS device must present must 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.
+ *
+ * Device is expected to be a high speed device.
+ */
 
+static const struct usb2_config ubt_config_if_1[UBT_IF_1_N_TRANSFER] =
+{
+ /* Incoming isochronous transfer #1 - SCO packets */
  [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,
+ .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 */
  [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,
+ .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 */
  [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,
+ .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 */
  [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,
+ .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,
- },
-};
-
 /*
- * 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
- ****************************************************************************
- ****************************************************************************/
-
-/*
- * 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 */
-
-/*
  * 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));
 
+ /* Expect high-speed device */
+ if (usb2_get_speed(uaa->device) != USB_SPEED_HIGH)
+ device_printf(dev, "device is not a high-speed device!\n");
+
+ /*
+ * 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 +494,28 @@
  /* 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 */
+ /* initialize locks */
+ mtx_init(&sc->sc_mbufq_mtx, "ubt mbufq", NULL, MTX_DEF);
+ mtx_init(&sc->sc_if_0_mtx, "ubt if0", NULL, MTX_DEF | MTX_RECURSE);
+ mtx_init(&sc->sc_if_1_mtx, "ubt if1", NULL, MTX_DEF | MTX_RECURSE);
+
+ /* initialize packet queues */
  NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
-
- /* bulk-out pipe */
  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));
+ /* initialize glue task */
+ sc->sc_task_flags = 0;
+ TASK_INIT(&sc->sc_task, 0, ubt_task, sc->sc_node);
 
- /* 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;
-
  /*
  * 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
@@ -527,791 +534,995 @@
  * 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_if_0,
+ ubt_config_if_0, UBT_IF_0_N_TRANSFER,
+ sc->sc_node, &sc->sc_if_0_mtx)) {
+ 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;
+ j ++;
  }
- 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;
- }
- /* 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_if_1,
+ ubt_config_if_1, UBT_IF_1_N_TRANSFER,
+ sc->sc_node, &sc->sc_if_1_mtx)) {
+ 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);
 
- /* destroy Netgraph node */
-
+ /* Destroy Netgraph node */
  if (sc->sc_node != NULL) {
  NG_NODE_SET_PRIVATE(sc->sc_node, NULL);
  ng_rmnode_self(sc->sc_node);
  sc->sc_node = NULL;
  }
- /* free USB transfers, if any */
 
+ /* 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);
 
- mtx_destroy(&sc->sc_mtx);
-
- /* destroy queues */
-
+ /* 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_0_mtx);
+ mtx_destroy(&sc->sc_if_1_mtx);
+ 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);
 
- if (xfer->error) {
- NG_UBT_STAT_OERROR(sc->sc_stat);
- } else {
- NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen);
- NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
  }
+ /* FALLTHROUGH */
 
  case USB_ST_SETUP:
-
- /* get next mbuf, if any */
-
+send_next:
+ /* Get next command mbuf, if any */
+ UBT_MBUFQ_LOCK(sc);
  NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
+ UBT_MBUFQ_UNLOCK(sc);
 
  if (m == NULL) {
- NG_UBT_INFO(sc, "HCI command queue is empty\n");
+ UBT_INFO(sc, "HCI command queue is empty\n");
+ NG_NODE_UNREF(node); /* transfer completed */
  return;
  }
- /*
- * check HCI command frame size and
- * copy it to USB transfer buffer:
- */
 
- if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE) {
- panic("HCI command frame too big, size=%zd, len=%d\n",
-    UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
- }
- /* initialize a USB control request and then schedule it */
-
+ /* 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(sc, "Sending control request, bmRequestType=0x%02x, "
-    "wLength=%d\n", req.bmRequestType, UGETW(req.wLength));
+ UBT_INFO(sc, "Sending control request, " \
+ "bmRequestType=0x%02x, wLength=%d\n",
+ req.bmRequestType, UGETW(req.wLength));
 
  usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
  usb2_m_copy_in(xfer->frbuffers + 1, 0, m, 0, m->m_pkthdr.len);
 
  xfer->frlengths[0] = sizeof(req);
  xfer->frlengths[1] = m->m_pkthdr.len;
- xfer->nframes = xfer->frlengths[1] ? 2 : 1;
+ xfer->nframes = 2;
 
  NG_FREE_M(m);
 
  usb2_start_hardware(xfer);
- return;
+ break;
 
- default: /* Error */
- if (xfer->error == USB_ERR_CANCELLED) {
- /* ignore */
- return;
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "control transfer failed: %s\n",
+ usb2_errstr(xfer->error));
+
+ UBT_STAT_OERROR(sc);
+ goto send_next;
  }
- goto tr_transferred;
+
+ NG_NODE_UNREF(node); /* transfer cancelled */
+ break;
  }
-}
+} /* ubt_ctrl_write_callback */
 
+/*
+ * Called when incoming interrupt transfer (HCI event) has completed, i.e.
+ * HCI event was received from the device.
+ * USB context.
+ */
+
 static void
 ubt_intr_read_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct mbuf *m;
- uint32_t max_len;
- uint8_t *ptr;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct mbuf *m;
+ ng_hci_event_pkt_t *hdr;
+ int error;
 
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
+
+ sc = NG_NODE_PRIVATE(node);
+
+ if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) {
+ UBT_INFO(sc, "no upstream hook\n");
+ NG_NODE_UNREF(node);
+ return; /* upstream hook is gone */
+ }
+
+ m = NULL;
+
  switch (USB_GET_STATE(xfer)) {
  case USB_ST_TRANSFERRED:
-
- /* allocate a new mbuf */
-
+ /* Allocate a new mbuf */
  MGETHDR(m, M_DONTWAIT, MT_DATA);
-
  if (m == NULL) {
- goto tr_setup;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
+
  MCLGET(m, M_DONTWAIT);
-
  if (!(m->m_flags & M_EXT)) {
- NG_FREE_M(m);
- goto tr_setup;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
- *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
- m->m_pkthdr.len = m->m_len = 1;
- } else {
- m->m_pkthdr.len = m->m_len = 0;
+
+ /* Add HCI packet type */
+ *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
+
+ if (xfer->actlen > MCLBYTES - 1)
+ xfer->actlen = MCLBYTES - 1;
+
+ usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
+ xfer->actlen);
+ m->m_pkthdr.len += xfer->actlen;
+ m->m_len += xfer->actlen;
+
+ UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
+ xfer->actlen);
+
+ /* Validate packet and send it up the stack */
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ UBT_INFO(sc, "HCI event packet is too short\n");
+
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
 
- max_len = (MCLBYTES - m->m_len);
+ 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);
 
- if (xfer->actlen > max_len) {
- xfer->actlen = max_len;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- ptr = ((uint8_t *)(m->m_data)) + m->m_len;
 
- usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen);
+ UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
+ "length=%d\n", m->m_pkthdr.len, hdr->length);
 
- m->m_pkthdr.len += xfer->actlen;
- m->m_len += xfer->actlen;
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
 
- NG_UBT_INFO(sc, "got %d bytes from interrupt "
-    "pipe\n", xfer->actlen);
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (error != 0)
+ UBT_STAT_IERROR(sc);
 
- sc->sc_intr_buffer = m;
+ /* m == NULL at this point */
+ /* FALLTHROUGH */
 
  case USB_ST_SETUP:
-tr_setup:
+submit_next:
+ NG_FREE_M(m); /* checks for m != NULL */
 
- if (sc->sc_intr_buffer) {
- ng_send_fn(sc->sc_node, NULL, ubt_intr_read_complete, NULL, 0);
- return;
- }
  if (sc->sc_flags & UBT_FLAG_INTR_STALL) {
  usb2_transfer_start(sc->sc_xfer_if_0[6]);
- return;
+ } else {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
  }
- xfer->frlengths[0] = xfer->max_data_length;
+ break;
 
- usb2_start_hardware(xfer);
- return;
-
- default: /* Error */
+ default: /* Error */
  if (xfer->error != USB_ERR_CANCELLED) {
- /* try to clear stall first */
+ /* Try to clear stall first */
  sc->sc_flags |= UBT_FLAG_INTR_STALL;
  usb2_transfer_start(sc->sc_xfer_if_0[6]);
- }
- return;
-
+ } else
+ NG_NODE_UNREF(node); /* transfer cancelled */
+ break;
  }
-}
+} /* ubt_intr_read_callback */
 
+/*
+ * Called when outgoing control transfer initiated to clear stall on
+ * interrupt pipe has completed.
+ * USB context.
+ */
+
 static void
 ubt_intr_read_clear_stall_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[2];
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct usb2_xfer *xfer_other;
 
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
+
+ sc = NG_NODE_PRIVATE(node);
+ xfer_other = sc->sc_xfer_if_0[2];
+
  if (usb2_clear_stall_callback(xfer, xfer_other)) {
  DPRINTF("stall cleared\n");
  sc->sc_flags &= ~UBT_FLAG_INTR_STALL;
  usb2_transfer_start(xfer_other);
- }
-}
+ } else
+ NG_NODE_UNREF(node); /* XXX cant clear stall */
+} /* ubt_intr_read_clear_stall_callback */
 
+/*
+ * Called when incoming bulk transfer (ACL packet) has completed, i.e.
+ * ACL packet was received from the device.
+ * USB context.
+ */
+
 static void
-ubt_intr_read_complete(node_p node, hook_p hook, void *arg1, int arg2)
+ubt_bulk_read_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
- struct mbuf *m;
- ng_hci_event_pkt_t *hdr;
- int error;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct mbuf *m;
+ ng_hci_acldata_pkt_t *hdr;
+ uint16_t len;
+ int error;
 
- if (sc == NULL) {
- return;
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
  }
- mtx_lock(&sc->sc_mtx);
 
- m = sc->sc_intr_buffer;
+ sc = NG_NODE_PRIVATE(node);
 
- if (m) {
+ if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) {
+ UBT_INFO(sc, "no upstream hook\n");
+ NG_NODE_UNREF(node);
+ return; /* upstream hook is gone */
+ }
 
- sc->sc_intr_buffer = NULL;
+ m = NULL;
 
- hdr = mtod(m, ng_hci_event_pkt_t *);
-
- if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) {
- NG_UBT_INFO(sc, "No upstream hook\n");
- goto done;
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ /* Allocate new mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len);
 
- if (m->m_pkthdr.len < sizeof(*hdr)) {
- NG_UBT_INFO(sc, "Packet too short\n");
- goto done;
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- if (hdr->length == (m->m_pkthdr.len - sizeof(*hdr))) {
- NG_UBT_INFO(sc, "Got complete HCI event frame, "
-    "pktlen=%d, length=%d\n",
-    m->m_pkthdr.len, hdr->length);
 
- NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+ /* Add HCI packet type */
+ *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
 
- NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (xfer->actlen > MCLBYTES - 1)
+ xfer->actlen = MCLBYTES - 1;
 
- m = NULL;
+ usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
+ xfer->actlen);
+ m->m_pkthdr.len += xfer->actlen;
+ m->m_len += xfer->actlen;
 
- if (error != 0) {
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
- } else {
- NG_UBT_ERR(sc, "Invalid HCI event frame size, "
-    "length=%d, pktlen=%d\n",
-    hdr->length, m->m_pkthdr.len);
+ UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
+ xfer->actlen);
 
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
- }
-done:
- if (m) {
- NG_FREE_M(m);
- }
- /* start USB transfer if not already started */
+ /* Validate packet and send it up the stack */
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ UBT_INFO(sc, "HCI ACL packet is too short\n");
 
- usb2_transfer_start(sc->sc_xfer_if_0[2]);
-
- mtx_unlock(&sc->sc_mtx);
-}
-
-static void
-ubt_bulk_read_callback(struct usb2_xfer *xfer)
-{
- struct ubt_softc *sc = xfer->priv_sc;
- struct mbuf *m;
- uint32_t max_len;
- uint8_t *ptr;
-
- switch (USB_GET_STATE(xfer)) {
- case USB_ST_TRANSFERRED:
-
- /* allocate new mbuf */
-
- MGETHDR(m, M_DONTWAIT, MT_DATA);
-
- if (m == NULL) {
- goto tr_setup;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- MCLGET(m, M_DONTWAIT);
 
- if (!(m->m_flags & M_EXT)) {
- NG_FREE_M(m);
- goto tr_setup;
- }
- if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
- *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
- m->m_pkthdr.len = m->m_len = 1;
- } else {
- m->m_pkthdr.len = m->m_len = 0;
- }
+ hdr = mtod(m, ng_hci_acldata_pkt_t *);
+ len = le16toh(hdr->length);
+ if (len != (m->m_pkthdr.len - sizeof(*hdr))) {
+ UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
+ "pktlen=%d\n", len, m->m_pkthdr.len);
 
- max_len = (MCLBYTES - m->m_len);
-
- if (xfer->actlen > max_len) {
- xfer->actlen = max_len;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- ptr = ((uint8_t *)(m->m_data)) + m->m_len;
 
- usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen);
+ UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
+ "length=%d\n", m->m_pkthdr.len, len);
 
- m->m_pkthdr.len += xfer->actlen;
- m->m_len += xfer->actlen;
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
 
- NG_UBT_INFO(sc, "got %d bytes from bulk-in "
-    "pipe\n", xfer->actlen);
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (error != 0)
+ UBT_STAT_IERROR(sc);
 
- sc->sc_bulk_in_buffer = m;
+ /* m == NULL at this point */
+ /* FALLTHOUGH */
 
  case USB_ST_SETUP:
-tr_setup:
- if (sc->sc_bulk_in_buffer) {
- ng_send_fn(sc->sc_node, NULL, ubt_bulk_read_complete, NULL, 0);
- return;
- }
- if (sc->sc_flags & UBT_FLAG_READ_STALL) {
+submit_next:
+ NG_FREE_M(m); /* checks for m != NULL */
+
+ if (sc->sc_flags & UBT_FLAG_READ_STALL)
  usb2_transfer_start(sc->sc_xfer_if_0[5]);
- return;
+ else {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
  }
- xfer->frlengths[0] = xfer->max_data_length;
+ break;
 
- usb2_start_hardware(xfer);
- return;
-
- default: /* Error */
+ default: /* Error */
  if (xfer->error != USB_ERR_CANCELLED) {
- /* try to clear stall first */
+ /* Try to clear stall first */
  sc->sc_flags |= UBT_FLAG_READ_STALL;
  usb2_transfer_start(sc->sc_xfer_if_0[5]);
- }
- return;
-
+ } else
+ NG_NODE_UNREF(node); /* transfer cancelled */
+ break;
  }
-}
+} /* ubt_bulk_read_callback */
 
+/*
+ * Called when outgoing control transfer initiated to clear stall on
+ * incoming bulk pipe has completed.
+ * USB context.
+ */
+
 static void
 ubt_bulk_read_clear_stall_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[1];
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct usb2_xfer *xfer_other;
 
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
+
+ sc = NG_NODE_PRIVATE(node);
+ xfer_other = sc->sc_xfer_if_0[1];
+
  if (usb2_clear_stall_callback(xfer, xfer_other)) {
  DPRINTF("stall cleared\n");
  sc->sc_flags &= ~UBT_FLAG_READ_STALL;
  usb2_transfer_start(xfer_other);
- }
-}
+ } else
+ NG_NODE_UNREF(node); /* XXX cant clear stall */
+} /* ubt_bulk_read_clear_stall_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_read_complete(node_p node, hook_p hook, void *arg1, int arg2)
+ubt_bulk_write_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
- struct mbuf *m;
- ng_hci_acldata_pkt_t *hdr;
- uint16_t len;
- int error;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct mbuf *m;
 
- if (sc == NULL) {
- return;
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
  }
- mtx_lock(&sc->sc_mtx);
 
- m = sc->sc_bulk_in_buffer;
+ sc = NG_NODE_PRIVATE(node);
 
- if (m) {
-
- 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(sc, "No upstream hook\n");
- goto done;
- }
- NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len);
-
- if (m->m_pkthdr.len < sizeof(*hdr)) {
- NG_UBT_INFO(sc, "Packet too short\n");
- goto done;
- }
- len = le16toh(hdr->length);
-
- if (len == (m->m_pkthdr.len - sizeof(*hdr))) {
- NG_UBT_INFO(sc, "Got complete ACL data frame, "
-    "pktlen=%d, length=%d\n",
-    m->m_pkthdr.len, len);
-
- NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
-
- NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
-
- m = NULL;
-
- if (error != 0) {
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
- } else {
- NG_UBT_ERR(sc, "Invalid ACL frame size, "
-    "length=%d, pktlen=%d\n",
-    len, m->m_pkthdr.len);
-
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
- }
-done:
- if (m) {
- NG_FREE_M(m);
- }
- /* start USB transfer if not already started */
-
- usb2_transfer_start(sc->sc_xfer_if_0[1]);
-
- mtx_unlock(&sc->sc_mtx);
-}
-
-static void
-ubt_bulk_write_callback(struct usb2_xfer *xfer)
-{
- struct ubt_softc *sc = xfer->priv_sc;
- struct mbuf *m;
-
  switch (USB_GET_STATE(xfer)) {
  case USB_ST_TRANSFERRED:
- NG_UBT_INFO(sc, "sent %d bytes to bulk-out "
-    "pipe\n", xfer->actlen);
- NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen);
- NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ if (xfer->error != 0)
+ UBT_STAT_OERROR(sc);
+ else {
+ UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n",
+ xfer->actlen);
 
- case USB_ST_SETUP:
- if (sc->sc_flags & UBT_FLAG_WRITE_STALL) {
- usb2_transfer_start(sc->sc_xfer_if_0[4]);
- return;
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
  }
- /* get next mbuf, if any */
+ /* FALLTHROUGH */
 
+ case USB_ST_SETUP:
+ /* Get next mbuf, if any */
+ UBT_MBUFQ_LOCK(sc);
  NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
+ UBT_MBUFQ_UNLOCK(sc);
 
  if (m == NULL) {
- NG_UBT_INFO(sc, "ACL data queue is empty\n");
- return;
+ UBT_INFO(sc, "ACL data queue is empty\n");
+ NG_NODE_UNREF(node);
+ return; /* transfer completed */
  }
+
  /*
- * check ACL data frame size and
- * copy it back to a linear USB
- * transfer buffer:
+ * Copy ACL data frame back to a linear USB transfer buffer
+ * and schedule transfer
  */
 
- if (m->m_pkthdr.len > UBT_BULK_WRITE_BUFFER_SIZE) {
- panic("ACL data frame too big, size=%d, len=%d\n",
-    UBT_BULK_WRITE_BUFFER_SIZE,
-    m->m_pkthdr.len);
- }
  usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len);
+ xfer->frlengths[0] = m->m_pkthdr.len;
 
- NG_UBT_INFO(sc, "bulk-out transfer has been started, "
-    "len=%d\n", m->m_pkthdr.len);
+ UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
+ m->m_pkthdr.len);
 
- xfer->frlengths[0] = m->m_pkthdr.len;
-
  NG_FREE_M(m);
 
  usb2_start_hardware(xfer);
- return;
+ break;
 
- default: /* Error */
+ default: /* Error */
  if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "bulk-out transfer failed: %s\n",
+ usb2_errstr(xfer->error));
 
- NG_UBT_WARN(sc, "bulk-out transfer failed: %s\n",
-    usb2_errstr(xfer->error));
+ UBT_STAT_OERROR(sc);
 
- NG_UBT_STAT_OERROR(sc->sc_stat);
-
  /* try to clear stall first */
  sc->sc_flags |= UBT_FLAG_WRITE_STALL;
  usb2_transfer_start(sc->sc_xfer_if_0[4]);
- }
- return;
-
+ } else
+ NG_NODE_UNREF(node); /* transfer cancelled */
+ break;
  }
-}
+} /* ubt_bulk_write_callback */
 
+/*
+ * Called when outgoing control transfer initiated to clear stall on
+ * outgoing bulk pipe has completed.
+ * USB context.
+ */
+
 static void
 ubt_bulk_write_clear_stall_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[0];
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct usb2_xfer *xfer_other;
 
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
+
+ sc = NG_NODE_PRIVATE(node);
+ xfer_other = sc->sc_xfer_if_0[0];
+
  if (usb2_clear_stall_callback(xfer, xfer_other)) {
  DPRINTF("stall cleared\n");
  sc->sc_flags &= ~UBT_FLAG_WRITE_STALL;
  usb2_transfer_start(xfer_other);
- }
-}
+ } else
+ NG_NODE_UNREF(node); /* XXX cant clear stall */
+} /* ubt_bulk_write_clear_stall_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_read_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- ng_hci_scodata_pkt_t hdr;
- struct mbuf *m;
- uint8_t *ptr;
- uint32_t max_len;
- uint32_t n;
- uint32_t offset;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ int n;
 
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
+
+ sc = NG_NODE_PRIVATE(node);
+
+ if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) {
+ UBT_INFO(sc, "no upstream hook\n");
+ NG_NODE_UNREF(node);
+ return; /* upstream hook is gone */
+ }
+
  switch (USB_GET_STATE(xfer)) {
  case USB_ST_TRANSFERRED:
-tr_transferred:
- offset = 0;
+ for (n = 0; n < xfer->nframes; n ++)
+ if (ubt_isoc_read_one_frame(xfer, n) < 0)
+ break;
+ /* FALLTHROUGH */
 
- for (n = 0; n < xfer->nframes; n++) {
+ case USB_ST_SETUP:
+read_next:
+ for (n = 0; n < xfer->nframes; n ++)
+ xfer->frlengths[n] = xfer->max_frame_size;
 
- if (xfer->frlengths[n] >= sizeof(hdr)) {
+ usb2_start_hardware(xfer);
+ break;
 
- usb2_copy_out(xfer->frbuffers, offset,
-    &hdr, sizeof(hdr));
+ default: /* Error */
+                if (xfer->error != USB_ERR_CANCELLED) {
+                        UBT_STAT_IERROR(sc);
+                        goto read_next;
+ /* NOT REACHED */
+                }
 
- if (hdr.length == (xfer->frlengths[n] - sizeof(hdr))) {
+ NG_NODE_UNREF(node); /* transfer is cancelled */
+ break;
+ }
+} /* ubt_isoc_read_callback */
 
- NG_UBT_INFO(sc, "got complete SCO data "
-    "frame, length=%d\n", hdr.length);
+/*
+ * Helper function. Called from ubt_isoc_read_callback() to read
+ * SCO data from one frame.
+ * USB context.
+ */
 
- if (!NG_BT_MBUFQ_FULL(&sc->sc_sciq)) {
+static int
+ubt_isoc_read_one_frame(struct usb2_xfer *xfer, uint32_t frame_no)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+ int len, want, got, error;
 
- /* allocate a new mbuf */
+ /* Get existing SCO reassembly buffer */
+ m = sc->sc_isoc_in_buffer;
+ sc->sc_isoc_in_buffer = NULL;
 
- MGETHDR(m, M_DONTWAIT, MT_DATA);
+ /* While we have data in the frame */
+ while ((len = xfer->frlengths[frame_no]) > 0) {
+ if (m == NULL) {
+ /* Start new reassembly buffer */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ UBT_STAT_IERROR(sc);
+ return (-1); /* XXX out of sync! */
+ }
 
- if (m == NULL) {
- goto tr_setup;
- }
- MCLGET(m, M_DONTWAIT);
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ UBT_STAT_IERROR(sc);
+ NG_FREE_M(m);
+ return (-1); /* XXX out of sync! */
+ }
 
- if (!(m->m_flags & M_EXT)) {
- NG_FREE_M(m);
- goto tr_setup;
- }
- /*
- * fix SCO data frame header
- * if required
- */
+ /* 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 (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
- *mtod(m, uint8_t *)= NG_HCI_SCO_DATA_PKT;
- m->m_pkthdr.len = m->m_len = 1;
- } else {
- m->m_pkthdr.len = m->m_len = 0;
- }
+ if (got >= want)
+ want += mtod(m, ng_hci_scodata_pkt_t *)->length;
+ }
 
- max_len = (MCLBYTES - m->m_len);
+ /* Append frame data to the SCO reassembly buffer */
+ if (got + len > want)
+ len = want - got;
 
- if (xfer->frlengths[n] > max_len) {
- xfer->frlengths[n] = max_len;
- }
- ptr = ((uint8_t *)(m->m_data)) + m->m_len;
+ usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size,
+ mtod(m, uint8_t *) + m->m_pkthdr.len, len);
 
- usb2_copy_out
-    (xfer->frbuffers, offset,
-    ptr, xfer->frlengths[n]);
+ m->m_pkthdr.len += len;
+ m->m_len += len;
+ xfer->frlengths[frame_no] -= len;
 
- m->m_pkthdr.len += xfer->frlengths[n];
- m->m_len += xfer->frlengths[n];
+ /* Check if we got everything we wanted, if not - continue */
+ if (got != want)
+ continue;
 
- NG_BT_MBUFQ_ENQUEUE(&sc->sc_sciq, m);
- }
- }
- }
- offset += xfer->max_frame_size;
+ /* 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);
+
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
+
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (error != 0)
+ UBT_STAT_IERROR(sc);
+
+ /* m == NULL at this point */
+ }
+
+ /* Put SCO reassembly buffer back */
+ sc->sc_isoc_in_buffer = m;
+
+ 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.
+ */
+
+static void
+ubt_isoc_write_callback(struct usb2_xfer *xfer)
+{
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct mbuf *m;
+ uint32_t n, space, offset;
+
+ 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:
+ if (xfer->error)
+ UBT_STAT_OERROR(sc);
+ else {
+ UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n",
+ xfer->actlen);
+
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
  }
+ /* FALLTHROUGH */
 
  case USB_ST_SETUP:
-tr_setup:
+send_next:
+ offset = 0;
+ space = xfer->max_frame_size * xfer->nframes;
+ m = NULL;
 
- if (NG_BT_MBUFQ_LEN(&sc->sc_sciq) > 0) {
- ng_send_fn(sc->sc_node, NULL, ubt_isoc_read_complete, NULL, 0);
+ while (space > 0) {
+ if (m == NULL) {
+ UBT_MBUFQ_LOCK(sc);
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
+ UBT_MBUFQ_UNLOCK(sc);
+
+ if (m == NULL)
+ break;
+ }
+
+ n = min(space, m->m_pkthdr.len);
+ if (n > 0) {
+ usb2_m_copy_in(xfer->frbuffers,offset, m,0, n);
+ m_adj(m, n);
+
+ offset += n;
+ space -= n;
+ }
+
+ if (m->m_pkthdr.len == 0)
+ NG_FREE_M(m);
  }
- for (n = 0; n < xfer->nframes; n++) {
- xfer->frlengths[n] = xfer->max_frame_size;
+
+ if (offset == 0) {
+ NG_NODE_UNREF(node);
+ return; /* nothing to send */
  }
 
+ /* Put whatever is left from mbuf back on queue */
+ if (m != NULL) {
+ UBT_MBUFQ_LOCK(sc);
+ NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
+ UBT_MBUFQ_UNLOCK(sc);
+ }
+
+ /* Calculate sized for isoc frames */
+ for (n = 0; offset > 0; n ++) {
+ xfer->frlengths[n] = min(offset, xfer->max_frame_size);
+ offset -= xfer->max_frame_size;
+ }
+
  usb2_start_hardware(xfer);
- return;
+ break;
 
- default: /* Error */
- if (xfer->error == USB_ERR_CANCELLED) {
- /* ignore */
- return;
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_STAT_OERROR(sc);
+ goto send_next;
+ /* NOT REACHED */
  }
- goto tr_transferred;
+
+ NG_NODE_UNREF(node); /* transfer cancelled */
+ break;
  }
 }
 
+/****************************************************************************
+ ****************************************************************************
+ **                                 Glue
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Schedule glue task. Should be called with sc_mbufq_mtx held.
+ * Netgraph context.
+ */
+
+static int
+ubt_task_schedule(ubt_softc_p sc, uint32_t action)
+{
+ mtx_assert(&sc->sc_mbufq_mtx, MA_OWNED);
+
+ if ((sc->sc_task_flags & action) == 0) {
+ /*
+ * Try to handle corner case when "start all" and "stop all"
+ * actions can both be set before task is executed.
+ *
+ * Assume the following:
+ * 1) "stop all" after "start all" cancels "start all", and,
+ *    keeps "stop all"
+ *
+ * 2) "start all" after "stop all" is fine because task is
+ * executing "stop all" first
+ */
+
+ if (action == UBT_FLAG_T_STOP_ALL &&
+    (sc->sc_task_flags & UBT_FLAG_T_START_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 (1);
+
+ if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
+ NG_NODE_REF(sc->sc_node);
+ sc->sc_task_flags |= UBT_FLAG_T_PENDING;
+ return (1);
+ }
+
+ /* XXX: i think this should never happen */
+
+ return (0);
+} /* ubt_task_schedule */
+
+/*
+ * Glue task. Examines sc_task_flags and does things depending on it.
+ * Taskqueue context.
+ */
+
 static void
-ubt_isoc_read_complete(node_p node, hook_p hook, void *arg1, int arg2)
+ubt_task(void *context, int pending)
 {
- ubt_softc_p sc = NG_NODE_PRIVATE(node);
- struct mbuf *m;
- int error;
+ node_p node = context;
+ ubt_softc_p sc;
+ uint32_t task_flags;
 
- if (sc == NULL) {
- return;
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
  }
- mtx_lock(&sc->sc_mtx);
 
- while (1) {
+ sc = NG_NODE_PRIVATE(node);
 
- NG_BT_MBUFQ_DEQUEUE(&sc->sc_sciq, m);
+ UBT_MBUFQ_LOCK(sc);
+ task_flags = sc->sc_task_flags;
+ sc->sc_task_flags = 0;
+ UBT_MBUFQ_UNLOCK(sc);
 
- if (m == NULL) {
- break;
- }
- if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) {
- NG_UBT_INFO(sc, "No upstream hook\n");
- goto done;
- }
- NG_UBT_INFO(sc, "Got complete SCO data frame, "
-    "pktlen=%d bytes\n", m->m_pkthdr.len);
+ /* Stop all USB transfers */
+ if (task_flags & UBT_FLAG_T_STOP_ALL) {
+ /*
+ * Interface #0
+ */
 
- NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
- NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len);
+ mtx_lock(&sc->sc_if_0_mtx);
 
- NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ usb2_transfer_stop(sc->sc_xfer_if_0[0]); /* bulk out */
+ usb2_transfer_stop(sc->sc_xfer_if_0[1]); /* bulk in */
+ usb2_transfer_stop(sc->sc_xfer_if_0[2]); /* intr in */
+ usb2_transfer_stop(sc->sc_xfer_if_0[3]); /* ctrl out */
+ usb2_transfer_stop(sc->sc_xfer_if_0[4]);
+ usb2_transfer_stop(sc->sc_xfer_if_0[5]);
+ usb2_transfer_stop(sc->sc_xfer_if_0[6]);
 
- m = NULL;
+ mtx_unlock(&sc->sc_if_0_mtx);
 
- if (error) {
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
-done:
- if (m) {
- NG_FREE_M(m);
- }
+ /*
+ * Interface #1
+ */
+
+ mtx_lock(&sc->sc_if_1_mtx);
+
+ usb2_transfer_stop(sc->sc_xfer_if_1[0]); /* isoc in #1 */
+ usb2_transfer_stop(sc->sc_xfer_if_1[1]); /* isoc in #2 */
+ usb2_transfer_stop(sc->sc_xfer_if_1[2]); /* isoc out #1 */
+ usb2_transfer_stop(sc->sc_xfer_if_1[3]); /* isoc out #2 */
+
+ mtx_unlock(&sc->sc_if_1_mtx);
  }
 
- mtx_unlock(&sc->sc_mtx);
-}
+ /* Start all incoming USB transfers */
+ if (task_flags & UBT_FLAG_T_START_ALL) {
+ /*
+ * Interface #0
+ */
 
-static void
-ubt_isoc_write_callback(struct usb2_xfer *xfer)
-{
- struct ubt_softc *sc = xfer->priv_sc;
- struct mbuf *m;
- uint32_t n;
- uint32_t len;
- uint32_t offset;
+ mtx_lock(&sc->sc_if_0_mtx);
 
- switch (USB_GET_STATE(xfer)) {
- case USB_ST_TRANSFERRED:
-tr_transferred:
- if (xfer->error) {
- NG_UBT_STAT_OERROR(sc->sc_stat);
- } else {
- NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen);
- NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
- }
+ NG_NODE_REF(node);
+ usb2_transfer_start(sc->sc_xfer_if_0[1]); /* bulk in */
 
- case USB_ST_SETUP:
- offset = 0;
+ NG_NODE_REF(node);
+ usb2_transfer_start(sc->sc_xfer_if_0[2]); /* intr in */
 
- for (n = 0; n < xfer->nframes; n++) {
+ mtx_unlock(&sc->sc_if_0_mtx);
 
- NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
+ /*
+ * Interface #1
+ */
 
- if (m) {
- len = min(xfer->max_frame_size, m->m_pkthdr.len);
+ mtx_lock(&sc->sc_if_1_mtx);
 
- usb2_m_copy_in(xfer->frbuffers, offset, m, 0, len);
+ NG_NODE_REF(node);
+ usb2_transfer_start(sc->sc_xfer_if_1[0]); /* isoc in #1 */
 
- NG_FREE_M(m);
+ NG_NODE_REF(node);
+ usb2_transfer_start(sc->sc_xfer_if_1[1]); /* isoc in #2 */
 
- xfer->frlengths[n] = len;
- offset += len;
- } else {
- xfer->frlengths[n] = 0;
- }
- }
+ mtx_unlock(&sc->sc_if_1_mtx);
+ }
 
- usb2_start_hardware(xfer);
- return;
+ /* Start outgoing control transfer */
+ if (task_flags & UBT_FLAG_T_START_CTRL) {
+ mtx_lock(&sc->sc_if_0_mtx);
+ NG_NODE_REF(node);
+ usb2_transfer_start(sc->sc_xfer_if_0[3]);
+ mtx_unlock(&sc->sc_if_0_mtx);
+ }
 
- default: /* Error */
- if (xfer->error == USB_ERR_CANCELLED) {
- /* ignore */
- return;
- }
- goto tr_transferred;
+ /* Start outgoing bulk transfer */
+ if (task_flags & UBT_FLAG_T_START_BULK) {
+ mtx_lock(&sc->sc_if_0_mtx);
+ NG_NODE_REF(node);
+ usb2_transfer_start(sc->sc_xfer_if_0[0]);
+ mtx_unlock(&sc->sc_if_0_mtx);
  }
-}
 
+ /* Start outgoing isoc transfers */
+ if (task_flags & UBT_FLAG_T_START_ISOC) {
+                mtx_lock(&sc->sc_if_1_mtx);
+
+                NG_NODE_REF(node);
+                usb2_transfer_start(sc->sc_xfer_if_1[2]); /* isoc out #1 */
+
+                NG_NODE_REF(node);
+                usb2_transfer_start(sc->sc_xfer_if_1[3]); /* isoc out #2 */
+
+                mtx_unlock(&sc->sc_if_1_mtx);
+ }
+
+ NG_NODE_UNREF(node);
+} /* ubt_task */
+
 /****************************************************************************
  ****************************************************************************
  **                        Netgraph specific
@@ -1319,206 +1530,148 @@
  ****************************************************************************/
 
 /*
- * Netgraph node constructor.
- * Do not allow to create node of this type:
+ * Netgraph node constructor. Do not allow to create node of this type.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_constructor(node_p node)
 {
  return (EINVAL);
-}
+} /* 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)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
+ struct ubt_softc *sc = NG_NODE_PRIVATE(node);
 
  /* Let old node go */
  NG_NODE_SET_PRIVATE(node, NULL);
  NG_NODE_UNREF(node);
 
- if (sc == NULL) {
+ if (sc == NULL)
  goto done;
- }
- mtx_lock(&sc->sc_mtx);
-
+
  /* Create Netgraph node */
  if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
- printf("%s: Could not create Netgraph node\n",
-    sc->sc_name);
+ printf("%s: Could not create Netgraph node\n", sc->sc_name);
  sc->sc_node = NULL;
  goto done;
  }
+
  /* Name node */
  if (ng_name_node(sc->sc_node, sc->sc_name) != 0) {
- printf("%s: Could not name Netgraph node\n",
-    sc->sc_name);
+ printf("%s: Could not name Netgraph node\n", sc->sc_name);
  NG_NODE_UNREF(sc->sc_node);
  sc->sc_node = NULL;
  goto done;
  }
+
  NG_NODE_SET_PRIVATE(sc->sc_node, sc);
  NG_NODE_FORCE_WRITER(sc->sc_node);
 
+ UBT_MBUFQ_LOCK(sc);
+ sc->sc_task_flags = 0;
+ UBT_MBUFQ_UNLOCK(sc);
 done:
- if (sc) {
- mtx_unlock(&sc->sc_mtx);
- }
  return (0);
-}
+} /* ng_ubt_shutdown */
 
 /*
- * Create new hook.
- * There can only be one.
+ * Create new hook. There can only be one.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_newhook(node_p node, hook_p hook, char const *name)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
- int error = 0;
+ struct ubt_softc *sc = NG_NODE_PRIVATE(node);
 
- if (strcmp(name, NG_UBT_HOOK) != 0) {
+ if (strcmp(name, NG_UBT_HOOK) != 0)
  return (EINVAL);
- }
- mtx_lock(&sc->sc_mtx);
 
- if (sc->sc_hook != NULL) {
- error = EISCONN;
- } else {
- sc->sc_hook = hook;
- }
+ if (sc->sc_hook != NULL)
+ return (EISCONN);
 
- mtx_unlock(&sc->sc_mtx);
+ sc->sc_hook = hook;
 
- return (error);
-}
+ 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)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
 
  NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
 
- mtx_lock(&sc->sc_mtx);
+ UBT_MBUFQ_LOCK(sc);
+ ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
+ UBT_MBUFQ_UNLOCK(sc);
 
- sc->sc_flags |= (UBT_FLAG_READ_STALL |
-    UBT_FLAG_WRITE_STALL |
-    UBT_FLAG_INTR_STALL);
-
- /* start intr transfer */
- usb2_transfer_start(sc->sc_xfer_if_0[2]);
-
- /* start bulk-in transfer */
- usb2_transfer_start(sc->sc_xfer_if_0[1]);
-
- /* start bulk-out transfer */
- usb2_transfer_start(sc->sc_xfer_if_0[0]);
-
- /* start control-out transfer */
- usb2_transfer_start(sc->sc_xfer_if_0[3]);
-#if 0
- XXX can enable this XXX
-
- /* start isoc-in transfer */
-     usb2_transfer_start(sc->sc_xfer_if_1[0]);
-
- usb2_transfer_start(sc->sc_xfer_if_1[1]);
-
- /* start isoc-out transfer */
- usb2_transfer_start(sc->sc_xfer_if_1[2]);
- usb2_transfer_start(sc->sc_xfer_if_1[3]);
-#endif
-
- mtx_unlock(&sc->sc_mtx);
-
  return (0);
-}
+} /* ng_ubt_connect */
 
 /*
- * Disconnect hook
+ * Disconnect hook.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_disconnect(hook_p hook)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
- int error = 0;
+ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
 
- if (sc != NULL) {
+ if (sc == NULL)
+ return (0);
 
- mtx_lock(&sc->sc_mtx);
+ if (hook != sc->sc_hook)
+ return (EINVAL);
 
- if (hook != sc->sc_hook) {
- error = EINVAL;
- } else {
+ sc->sc_hook = NULL;
 
- /* stop intr transfer */
- usb2_transfer_stop(sc->sc_xfer_if_0[2]);
- usb2_transfer_stop(sc->sc_xfer_if_0[6]);
+ UBT_MBUFQ_LOCK(sc);
 
- /* stop bulk-in transfer */
- usb2_transfer_stop(sc->sc_xfer_if_0[1]);
- usb2_transfer_stop(sc->sc_xfer_if_0[5]);
+ /* Drain queues */
+ NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
 
- /* stop bulk-out transfer */
- usb2_transfer_stop(sc->sc_xfer_if_0[0]);
- usb2_transfer_stop(sc->sc_xfer_if_0[4]);
+ /* Kick off task to stop all USB xfers */
+ ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
 
- /* stop control transfer */
- usb2_transfer_stop(sc->sc_xfer_if_0[3]);
+ UBT_MBUFQ_UNLOCK(sc);
 
- /* stop isoc-in transfer */
- usb2_transfer_stop(sc->sc_xfer_if_1[0]);
- usb2_transfer_stop(sc->sc_xfer_if_1[1]);
-
- /* stop isoc-out transfer */
- usb2_transfer_stop(sc->sc_xfer_if_1[2]);
- usb2_transfer_stop(sc->sc_xfer_if_1[3]);
-
- /* cleanup queues */
- NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
- NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
- NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
- NG_BT_MBUFQ_DRAIN(&sc->sc_sciq);
-
- sc->sc_hook = NULL;
- }
-
- mtx_unlock(&sc->sc_mtx);
- }
- return (error);
-}
-
+ 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)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
- struct ng_mesg *msg = NULL, *rsp = NULL;
- struct ng_bt_mbufq *q = NULL;
- int error = 0, queue, qlen;
+ struct ubt_softc *sc = NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg, *rsp = NULL;
+ struct ng_bt_mbufq *q;
+ int error = 0, queue, qlen;
 
  if (sc == NULL) {
  NG_FREE_ITEM(item);
  return (EHOSTDOWN);
  }
- mtx_lock(&sc->sc_mtx);
 
  NGI_GET_MSG(item, msg);
 
@@ -1527,25 +1680,29 @@
  switch (msg->header.cmd) {
  case NGM_TEXT_STATUS:
  NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
- 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" \
+ "Flags: %#x\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_flags,
+ 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:
@@ -1557,59 +1714,54 @@
  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_NOWAIT);
- if (rsp == NULL)
+ if (rsp == NULL) {
  error = ENOMEM;
- else
- *((ng_ubt_node_debug_ep *) (rsp->data)) =
-    sc->sc_debug;
+ 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;
+ break;
+ }
 
- if (qlen <= 0) {
- error = EINVAL;
- 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:
@@ -1617,7 +1769,9 @@
  error = EMSGSIZE;
  break;
  }
+
  queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
+
  switch (queue) {
  case NGM_UBT_NODE_QUEUE_CMD:
  q = &sc->sc_cmdq;
@@ -1632,39 +1786,36 @@
  break;
 
  default:
- q = NULL;
  error = EINVAL;
+ goto done;
+ /* NOT REACHED */
+ }
+
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
  break;
  }
 
- if (q != NULL) {
- NG_MKRESPONSE(rsp, msg,
-    sizeof(ng_ubt_node_qlen_ep), M_NOWAIT);
- 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_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_NOWAIT);
- if (rsp == NULL)
+ 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:
@@ -1677,90 +1828,172 @@
  error = EINVAL;
  break;
  }
-
+done:
  NG_RESPOND_MSG(error, node, item, rsp);
  NG_FREE_MSG(msg);
 
- mtx_unlock(&sc->sc_mtx);
-
  return (error);
-}
+} /* ng_ubt_rcvmsg */
 
 /*
- * Process data
+ * Process data.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_rcvdata(hook_p hook, item_p item)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
- struct mbuf *m;
- struct ng_bt_mbufq *q;
- struct usb2_xfer *xfer;
- int error = 0;
+ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m;
+ struct ng_bt_mbufq *q;
+ uint32_t action;
+ int error = 0;
 
  if (sc == NULL) {
  error = EHOSTDOWN;
  goto done;
  }
- mtx_lock(&sc->sc_mtx);
 
  if (hook != sc->sc_hook) {
  error = EINVAL;
  goto done;
  }
- /* deatch mbuf and get HCI frame type */
+
+ /* Deatch mbuf and get HCI frame type */
  NGI_GET_M(item, m);
 
- /* process HCI frame */
+ /*
+ * 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, uint8_t *)) { /* XXX call m_pullup ? */
  case NG_HCI_CMD_PKT:
- xfer = sc->sc_xfer_if_0[3];
+ if (m->m_pkthdr.len - 1 > 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;
+ action = UBT_FLAG_T_START_CTRL;
  break;
 
  case NG_HCI_ACL_DATA_PKT:
- xfer = sc->sc_xfer_if_0[0];
+ 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;
+ action = UBT_FLAG_T_START_BULK;
  break;
 
  case NG_HCI_SCO_DATA_PKT:
- xfer = NULL;
+#if 0 /* XXX FIXME - not yet */
  q = &sc->sc_scoq;
+ action = UBT_FLAG_T_START_ISOC;
  break;
+#else
+ NG_FREE_M(m);
+ goto done;
+#endif
 
  default:
- NG_UBT_ERR(sc, "Dropping unsupported HCI frame, "
-    "type=0x%02x, pktlen=%d\n",
-    *mtod(m, uint8_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(uint8_t));
- }
+ UBT_MBUFQ_LOCK(sc);
  if (NG_BT_MBUFQ_FULL(q)) {
- NG_UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. "
-    "Queue full\n", *mtod(m, uint8_t *),
-    m->m_pkthdr.len);
+ NG_BT_MBUFQ_DROP(q);
+ UBT_MBUFQ_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 {
+ /* Loose HCI packet type, enqueue mbuf and kick off task */
+ m_adj(m, sizeof(uint8_t));
  NG_BT_MBUFQ_ENQUEUE(q, m);
- }
+ ubt_task_schedule(sc, action);
 
- if (xfer) {
- usb2_transfer_start(xfer);
+ UBT_MBUFQ_UNLOCK(sc);
  }
 done:
  NG_FREE_ITEM(item);
 
- if (sc) {
- mtx_unlock(&sc->sc_mtx);
+ 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),
+ { 0, 0 }
+};
+
+static driver_t ubt_driver =
+{
+ .name =   "ubt",
+ .methods = ubt_methods,
+ .size =   sizeof(struct ubt_softc),
+};
+
+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);
+
Index: ng_ubt2_var.h
===================================================================
--- ng_ubt2_var.h (revision 186800)
+++ ng_ubt2_var.h (working copy)
@@ -3,7 +3,7 @@
  */
 
 /*-
- * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@...>
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@...>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,95 +32,94 @@
  */
 
 #ifndef _NG_UBT_VAR_H_
-#define _NG_UBT_VAR_H_
+#define _NG_UBT_VAR_H_ 1
 
-/* 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__, sc->sc_name, (s)); \
- } \
- } while (0)
-
 /* Debug printf's */
-#define NG_UBT_DEBUG(level, sc, fmt, ...) do { \
-    if ((sc)->sc_debug >= (level)) { \
-        printf("%s:%s:%d: " fmt, (sc)->sc_name, \
-       __FUNCTION__, __LINE__,## __VA_ARGS__); \
-    } \
+#define UBT_DEBUG(level, sc, fmt, ...) \
+do { \
+ if ((sc)->sc_debug >= (level)) \
+ printf("%s:%s:%d: " fmt, (sc)->sc_name, \
+ __FUNCTION__, __LINE__,## __VA_ARGS__); \
 } while (0)
 
-#define NG_UBT_ALERT(...) NG_UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__)
-#define NG_UBT_ERR(...)   NG_UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__)
-#define NG_UBT_WARN(...)  NG_UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__)
-#define NG_UBT_INFO(...)  NG_UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__)
+#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_MBUFQ_LOCK(sc) mtx_lock(&(sc)->sc_mbufq_mtx)
+#define UBT_MBUFQ_UNLOCK(sc) mtx_unlock(&(sc)->sc_mbufq_mtx)
+
 /* Bluetooth USB control request type */
 #define UBT_HCI_REQUEST 0x20
 #define UBT_DEFAULT_QLEN 12
 
 /* Bluetooth USB defines */
-#define UBT_IF_0_N_TRANSFER  7 /* units */
-#define UBT_IF_1_N_TRANSFER  4 /* units */
-#define UBT_ISOC_NFRAMES    25 /* units */
+#define UBT_IF_0_N_TRANSFER 7 /* units */
+#define UBT_IF_1_N_TRANSFER 4 /* units */
+#define UBT_ISOC_NFRAMES 25 /* units */
 
 /* USB device softc structure */
 struct ubt_softc {
+ uint8_t sc_name[16];
+
  /* State */
- ng_ubt_node_debug_ep sc_debug; /* debug level */
- uint32_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_FLAG_READ_STALL     (1 << 1)/* read transfer has stalled */
-#define UBT_FLAG_WRITE_STALL    (1 << 2)/* write transfer has stalled */
-#define UBT_FLAG_INTR_STALL     (1 << 3)/* interrupt transfer has stalled */
+ ng_ubt_node_debug_ep sc_debug; /* debug level */
 
- 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)))
+ uint32_t sc_flags; /* device flags */
+#define UBT_FLAG_READ_STALL (1 << 0) /* read transfer has stalled */
+#define UBT_FLAG_WRITE_STALL (1 << 1) /* write transfer has stalled */
+#define UBT_FLAG_INTR_STALL (1 << 2) /* inter transfer has stalled */
 
- uint8_t sc_name[16];
+ 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))
 
- struct mtx sc_mtx;
-
  /* USB device specific */
- struct usb2_xfer *sc_xfer_if_0[UBT_IF_0_N_TRANSFER];
- struct usb2_xfer *sc_xfer_if_1[UBT_IF_1_N_TRANSFER];
+ struct mtx sc_if_0_mtx; /* if #0 lock */
+ struct usb2_xfer *sc_xfer_if_0[UBT_IF_0_N_TRANSFER];
 
- /* Interrupt pipe (HCI events) */
- struct mbuf *sc_intr_buffer; /* interrupt buffer */
+ struct mtx sc_if_1_mtx; /* if #1 lock */
+ struct usb2_xfer *sc_xfer_if_1[UBT_IF_1_N_TRANSFER];
 
- /* Control pipe (HCI commands) */
- 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)
+ struct mtx sc_mbufq_mtx; /* lock for all queues */
 
- /* Bulk in pipe (ACL data) */
- struct mbuf *sc_bulk_in_buffer; /* bulk-in buffer */
+ /* HCI commands */
+ 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)
+#define UBT_INTR_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */
 
- /* Bulk out pipe (ACL data) */
- struct ng_bt_mbufq sc_aclq; /* ACL data queue */
-#define UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1) /* reserve one 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)
 
- /* Isoc. out pipe (ACL data) */
- struct ng_bt_mbufq sc_scoq; /* SCO data queue */
+ /* SCO data */
+ struct ng_bt_mbufq sc_scoq; /* SCO data queue */
+ struct mbuf *sc_isoc_in_buffer; /* SCO reassembly buffer */
 
- /* Isoc. in pipe (ACL data) */
- struct ng_bt_mbufq sc_sciq; /* SCO data queue */
-
  /* 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 */
+ uint32_t 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 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) */
+#define UBT_FLAG_T_START_ISOC (1 << 5) /* start isoc xfer (write) */
+
+ struct task sc_task;
 };
 typedef struct ubt_softc ubt_softc_t;
 typedef struct ubt_softc *ubt_softc_p;
 
-#endif /* ndef _NG_UBT_VAR_H_ */
+#endif /* ndef _NG_UBT_VAR_H_ */
+

_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: USB2: ng_ubt2 patch

by Hans Petter Selasky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thursday 15 January 2009, Maksim Yevmenkin wrote:

> dear bluetooth users,
>
> please find attached patch for ng_ubt2 (well it is almost a complete
> rewrite).
>
> this is an early preview and likely to have bugs. i briefly kicked the
> tires to make sure it compiles and did some very quick testing, i.e.
> run few hci commands, sdp transfers etc.
>
> this is my first attempt to write something under usb2, so any
> review/comments/suggestions/etc. are greatly appreciated.
>
> thanks,
> max

Hi,

This looks very interesting.

1) Maybe you just want to merge together all the USB config structures into
one?

2) There are some isochronous details for non-Full speed USB. UBT_ISOC_NFRAMES
must be a factor of 8 if it should work.

How much bluetooth functionality have you tested?

BTW: I think your patch is pretty Ok. Is what you attached the final version
of your patch?

--HPS
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: USB2: ng_ubt2 patch

by Maksim Yevmenkin-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, Jan 15, 2009 at 12:54 AM, Hans Petter Selasky <hselasky@...> wrote:

> On Thursday 15 January 2009, Maksim Yevmenkin wrote:
>> dear bluetooth users,
>>
>> please find attached patch for ng_ubt2 (well it is almost a complete
>> rewrite).
>>
>> this is an early preview and likely to have bugs. i briefly kicked the
>> tires to make sure it compiles and did some very quick testing, i.e.
>> run few hci commands, sdp transfers etc.
>>
>> this is my first attempt to write something under usb2, so any
>> review/comments/suggestions/etc. are greatly appreciated.
>
> This looks very interesting.

thanks for taking time and looking at this.

> 1) Maybe you just want to merge together all the USB config structures into
> one?

i guess, i could. since isoc transfers are going over different (from
control, interrupt and bulk transfers) interface, i kept original code
that had two separate usb2_config structures (i.e. one per each
interface). for the same reason, i added separate lock for interface 1
(where isoc transfers are). i figured, due to the nature of isoc
transfers, this lock will be grabbed a lot more often. it seemed like
a good idea to use different lock so there would be less contention
with interface 0 lock. but then again, may be i just full of it :) and
it really does not matter.

could you please tell me what is the reason for merging usb2_config
structures? is it better for usb2?

> 2) There are some isochronous details for non-Full speed USB. UBT_ISOC_NFRAMES
> must be a factor of 8 if it should work.

ok. the bluetooth spec says device should be a high speed device,
however, i have a bluetooth usb dongle (made by 3com) that is not a
high speed device. so, if i just bump UBT_ISOC_NFRAMES to 32, this
should work for both full- and high-speed devices, right?

another question is how to submit isoc. writes. recall that ubt driver
uses multiple isoc transfers in both directions. older (usb1) code
kept track of isoc write transfers and knew which were not active. so
when ubt needed to write isoc frame, it simply picked inactive isoc
transfer. so, should i simply start all isoc write transfers and let
usb2 code to figure it out or what? also, i'm a bit confused about
api. what is the difference between usb2_transfer_start() and
usb2_start_hardware() and when one should use former or later?

> How much bluetooth functionality have you tested?

like i said, i kicked the tires :) hci commands/events appear to work.
acl/l2cap appears to work as well. i have not tried isoc (read my
questions above). of course, i will need to run torture tests etc.

> BTW: I think your patch is pretty Ok. Is what you attached the final version
> of your patch?

probably not. i'm sure there will be some tweaks to it. hopefully
nothing too big. i'm just not sure how should i proceed at this point,
i.e. do i

1) start committing this to -head and let you pick it up from there

or

2) let you commit this into p4 (or whatever) and then let you merge it
into -head

thanks,
max
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: USB2: ng_ubt2 patch

by Hans Petter Selasky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thursday 15 January 2009, Maksim Yevmenkin wrote:
> On Thu, Jan 15, 2009 at 12:54 AM, Hans Petter Selasky <hselasky@...>
wrote:

> > On Thursday 15 January 2009, Maksim Yevmenkin wrote:
> >> dear bluetooth users,
> >>
> >> please find attached patch for ng_ubt2 (well it is almost a complete
> >> rewrite).
> >>
> >> this is an early preview and likely to have bugs. i briefly kicked the
> >> tires to make sure it compiles and did some very quick testing, i.e.
> >> run few hci commands, sdp transfers etc.
> >>
> >> this is my first attempt to write something under usb2, so any
> >> review/comments/suggestions/etc. are greatly appreciated.
> >
> > This looks very interesting.
>
> thanks for taking time and looking at this.
>
> > 1) Maybe you just want to merge together all the USB config structures
> > into one?
>
> i guess, i could. since isoc transfers are going over different (from
> control, interrupt and bulk transfers) interface, i kept original code
> that had two separate usb2_config structures (i.e. one per each
> interface). for the same reason, i added separate lock for interface 1
> (where isoc transfers are). i figured, due to the nature of isoc
> transfers, this lock will be grabbed a lot more often. it seemed like
> a good idea to use different lock so there would be less contention
> with interface 0 lock. but then again, may be i just full of it :) and
> it really does not matter.
>
> could you please tell me what is the reason for merging usb2_config
> structures? is it better for usb2?

It will save some memory allocations.

>
> > 2) There are some isochronous details for non-Full speed USB.
> > UBT_ISOC_NFRAMES must be a factor of 8 if it should work.
>
> ok. the bluetooth spec says device should be a high speed device,
> however, i have a bluetooth usb dongle (made by 3com) that is not a
> high speed device. so, if i just bump UBT_ISOC_NFRAMES to 32, this
> should work for both full- and high-speed devices, right?

Yes, just note that High-Speed USB is 8000 fps while Full-Speed USB is 1000
fps.

>
> another question is how to submit isoc. writes. recall that ubt driver
> uses multiple isoc transfers in both directions. older (usb1) code
> kept track of isoc write transfers and knew which were not active. so
> when ubt needed to write isoc frame, it simply picked inactive isoc
> transfer. so, should i simply start all isoc write transfers and let
> usb2 code to figure it out or what? also, i'm a bit confused about
> api. what is the difference between usb2_transfer_start() and
> usb2_start_hardware() and when one should use former or later?

Two ISOC transfers are enough for double buffering.

>
> > How much bluetooth functionality have you tested?
>
> like i said, i kicked the tires :) hci commands/events appear to work.
> acl/l2cap appears to work as well. i have not tried isoc (read my
> questions above). of course, i will need to run torture tests etc.

Great!

>
> > BTW: I think your patch is pretty Ok. Is what you attached the final
> > version of your patch?
>
> probably not. i'm sure there will be some tweaks to it. hopefully
> nothing too big. i'm just not sure how should i proceed at this point,
> i.e. do i
>
> 1) start committing this to -head and let you pick it up from there
>
> or
>
> 2) let you commit this into p4 (or whatever) and then let you merge it
> into -head

Just re-send me the latest patch you've got and I'll get it in P4 and my
private SVN first.

--HPS
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: USB2: ng_ubt2 patch

by Maksim Yevmenkin-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, Jan 15, 2009 at 1:25 PM, Hans Petter Selasky <hselasky@...> wrote:
> On Thursday 15 January 2009, Maksim Yevmenkin wrote:
>> On Thu, Jan 15, 2009 at 12:54 AM, Hans Petter Selasky <hselasky@...>
> wrote:
>> > On Thursday 15 January 2009, Maksim Yevmenkin wrote:
[...]

>> thanks for taking time and looking at this.
>>
>> > 1) Maybe you just want to merge together all the USB config structures
>> > into one?
>>
>> i guess, i could. since isoc transfers are going over different (from
>> control, interrupt and bulk transfers) interface, i kept original code
>> that had two separate usb2_config structures (i.e. one per each
>> interface). for the same reason, i added separate lock for interface 1
>> (where isoc transfers are). i figured, due to the nature of isoc
>> transfers, this lock will be grabbed a lot more often. it seemed like
>> a good idea to use different lock so there would be less contention
>> with interface 0 lock. but then again, may be i just full of it :) and
>> it really does not matter.
>>
>> could you please tell me what is the reason for merging usb2_config
>> structures? is it better for usb2?
>
> It will save some memory allocations.

i will see what i can do.

[...]

>> another question is how to submit isoc. writes. recall that ubt driver
>> uses multiple isoc transfers in both directions. older (usb1) code
>> kept track of isoc write transfers and knew which were not active. so
>> when ubt needed to write isoc frame, it simply picked inactive isoc
>> transfer. so, should i simply start all isoc write transfers and let
>> usb2 code to figure it out or what? also, i'm a bit confused about
>> api. what is the difference between usb2_transfer_start() and
>> usb2_start_hardware() and when one should use former or later?
>
> Two ISOC transfers are enough for double buffering.

actually, could you please answer the question: is there an api that
would tell me if a given transfer is in progress? i kinda hate to poke
into usb2 guts and look at flags_int structure  inside usb2_xfer.

[...]

>> > BTW: I think your patch is pretty Ok. Is what you attached the final
>> > version of your patch?
>>
>> probably not. i'm sure there will be some tweaks to it. hopefully
>> nothing too big. i'm just not sure how should i proceed at this point,
>> i.e. do i
>>
>> 1) start committing this to -head and let you pick it up from there
>>
>> or
>>
>> 2) let you commit this into p4 (or whatever) and then let you merge it
>> into -head
>
> Just re-send me the latest patch you've got and I'll get it in P4 and my
> private SVN first.

all right, i will do that. but first i need to iron out a couple of wrinkles :)

thanks
max
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: USB2: ng_ubt2 patch

by Hans Petter Selasky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Friday 16 January 2009, Maksim Yevmenkin wrote:
> On Thu, Jan 15, 2009 at 1:25 PM, Hans Petter Selasky <hselasky@...>
wrote:

> > On Thursday 15 January 2009, Maksim Yevmenkin wrote:
> >> On Thu, Jan 15, 2009 at 12:54 AM, Hans Petter Selasky <hselasky@...>
> >
> > wrote:
> >> > On Thursday 15 January 2009, Maksim Yevmenkin wrote:
>
> [...]
>
> >> thanks for taking time and looking at this.
> >>
> >> > 1) Maybe you just want to merge together all the USB config structures
> >> > into one?
> >>
> >> i guess, i could. since isoc transfers are going over different (from
> >> control, interrupt and bulk transfers) interface, i kept original code
> >> that had two separate usb2_config structures (i.e. one per each
> >> interface). for the same reason, i added separate lock for interface 1
> >> (where isoc transfers are). i figured, due to the nature of isoc
> >> transfers, this lock will be grabbed a lot more often. it seemed like
> >> a good idea to use different lock so there would be less contention
> >> with interface 0 lock. but then again, may be i just full of it :) and
> >> it really does not matter.
> >>
> >> could you please tell me what is the reason for merging usb2_config
> >> structures? is it better for usb2?
> >
> > It will save some memory allocations.
>
> i will see what i can do.
>
> [...]
>
> >> another question is how to submit isoc. writes. recall that ubt driver
> >> uses multiple isoc transfers in both directions. older (usb1) code
> >> kept track of isoc write transfers and knew which were not active. so
> >> when ubt needed to write isoc frame, it simply picked inactive isoc
> >> transfer. so, should i simply start all isoc write transfers and let
> >> usb2 code to figure it out or what? also, i'm a bit confused about
> >> api. what is the difference between

If isochronous is being used, then I recommend you have two transfers running
at any time in both directions. If there is nothing to send you send zero
length frames. Else you end up with glitches in the isochronous stream when
you change from one transfer to the other! Both isochronous transfers should
be queued all the time!

> >> usb2_transfer_start() and
> >> usb2_start_hardware() and when one should use former or later?

usb2_transfer_start() kicks the callback.
usb2_start_hardware() kicks the hardware.

I don't recomment calling usb2_start_hardware() outside the callback, but if
you check with "usb2_transfer_pending()" then it is possible.

> >
> > Two ISOC transfers are enough for double buffering.
>
> actually, could you please answer the question: is there an api that
> would tell me if a given transfer is in progress? i kinda hate to poke
> into usb2 guts and look at flags_int structure  inside usb2_xfer.

Yes, see "usb2_transfer_pending()".

--HPS
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: USB2: ng_ubt2 patch

by Maksim Yevmenkin-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

hello,

[...]

>> >> > 1) Maybe you just want to merge together all the USB config structures
>> >> > into one?
>> >>
>> >> i guess, i could. since isoc transfers are going over different (from
>> >> control, interrupt and bulk transfers) interface, i kept original code
>> >> that had two separate usb2_config structures (i.e. one per each
>> >> interface). for the same reason, i added separate lock for interface 1
>> >> (where isoc transfers are). i figured, due to the nature of isoc
>> >> transfers, this lock will be grabbed a lot more often. it seemed like
>> >> a good idea to use different lock so there would be less contention
>> >> with interface 0 lock. but then again, may be i just full of it :) and
>> >> it really does not matter.
>> >>
>> >> could you please tell me what is the reason for merging usb2_config
>> >> structures? is it better for usb2?
>> >
>> > It will save some memory allocations.
>>
>> i will see what i can do.
ok, as per your request, i have merged two usb2_config structures into
one  (see attached patch)

[...]

> If isochronous is being used, then I recommend you have two transfers running
> at any time in both directions. If there is nothing to send you send zero
> length frames. Else you end up with glitches in the isochronous stream when
> you change from one transfer to the other! Both isochronous transfers should
> be queued all the time!

ok, i've made this change as well. newer code queues and keeps both
read and both write isoc. transfers running all the time.

the attached patch seems to work (there was a reference counting
problem in the previous version of the patch). i also tortured it a
bit, i.e. stop the stack and/or yank the device while stack it active
and sending lots of data (i used l2ping -f to create lots of
activity).

i think, it can be committed to -head now. it should be stable enough
for early adopters of usb2 and bluetooth :) so, please let me know how
should i proceed at this point?

do i

1) commit it to head myself

or

2) wait for you commit it into your cvs/scv/p4/etc. branch and then
merge it into -head

thanks,
max

Index: ng_ubt2.c
===================================================================
--- ng_ubt2.c (revision 187313)
+++ ng_ubt2.c (working copy)
@@ -3,7 +3,7 @@
  */
 
 /*-
- * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@...>
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@...>
  * 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 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 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);
 
-static devclass_t ubt_devclass;
+/* 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 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;
-
 /* 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,316 +216,267 @@
  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
+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 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 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);
 
-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
+ *
+ * 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
+ */
 
-/* USB config */
-static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] = {
+static const struct usb2_config ubt_config[UBT_N_TRANSFER] =
+{
+ /*
+ * Interface #0
+ */
 
- [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,
+ /* 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,
  },
-
- [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,
+ /* 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,
  },
-
- [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,
+ /* 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,
  },
-
- [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 */
+ /* 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 */
  },
-
- [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 */
+ /* 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 */
  },
-
- [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 */
+ /* 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 */
  },
-
- [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 */
+ /*
+ * 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 */
  },
-};
 
-/* USB config */
-static const struct usb2_config
- ubt_config_if_1_full_speed[UBT_IF_1_N_TRANSFER] = {
+ /*
+ * Interface #1
+ */
 
- [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,
+ /* 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,
  },
-
- [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,
+ /* 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,
  },
-
- [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,
+ /* 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,
  },
-
- [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,
+ /* 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,
  },
 };
 
-/* 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,
- },
-};
-
 /*
- * 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
- ****************************************************************************
- ****************************************************************************/
-
-/*
- * 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 */
-
-/*
  * 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 @@
  /* 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 */
+ /* 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);
+
+ /* initialize packet queues */
  NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
-
- /* bulk-out pipe */
  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));
+ /* initialize glue task */
+ sc->sc_task_flags = 0;
+ TASK_INIT(&sc->sc_task, 0, ubt_task, sc->sc_node);
 
- /* 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;
-
  /*
  * 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,1003 +520,1134 @@
  * 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;
+ j ++;
  }
- 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;
- }
- /* 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 */
+ /* Destroy Netgraph node */
+ if (node != NULL) {
+ sc->sc_node = NULL;
 
- if (sc->sc_node != NULL) {
- NG_NODE_SET_PRIVATE(sc->sc_node, NULL);
- ng_rmnode_self(sc->sc_node);
- sc->sc_node = NULL;
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_REALLY_DIE(node);
+ NG_NODE_REF(node);
+ ng_rmnode_self(node);
  }
- /* free USB transfers, if any */
 
- usb2_transfer_unsetup(sc->sc_xfer_if_0, UBT_IF_0_N_TRANSFER);
+ /* Free USB transfers, if any */
+ usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
 
- usb2_transfer_unsetup(sc->sc_xfer_if_1, UBT_IF_1_N_TRANSFER);
+ if (node != NULL)
+ NG_NODE_UNREF(node);
 
- mtx_destroy(&sc->sc_mtx);
-
- /* destroy queues */
-
+ /* 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);
 
- if (xfer->error) {
- NG_UBT_STAT_OERROR(sc->sc_stat);
- } else {
- NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen);
- NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
  }
+ /* FALLTHROUGH */
 
  case USB_ST_SETUP:
-
- /* get next mbuf, if any */
-
+send_next:
+ /* Get next command mbuf, if any */
+ UBT_MBUFQ_LOCK(sc);
  NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
+ UBT_MBUFQ_UNLOCK(sc);
 
  if (m == NULL) {
- NG_UBT_INFO(sc, "HCI command queue is empty\n");
+ UBT_INFO(sc, "HCI command queue is empty\n");
+ NG_NODE_UNREF(node);
  return;
  }
- /*
- * check HCI command frame size and
- * copy it to USB transfer buffer:
- */
 
- if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE) {
- panic("HCI command frame too big, size=%zd, len=%d\n",
-    UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
- }
- /* initialize a USB control request and then schedule it */
-
+ /* 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(sc, "Sending control request, bmRequestType=0x%02x, "
-    "wLength=%d\n", req.bmRequestType, UGETW(req.wLength));
+ UBT_INFO(sc, "Sending control request, " \
+ "bmRequestType=0x%02x, wLength=%d\n",
+ req.bmRequestType, UGETW(req.wLength));
 
  usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
  usb2_m_copy_in(xfer->frbuffers + 1, 0, m, 0, m->m_pkthdr.len);
 
  xfer->frlengths[0] = sizeof(req);
  xfer->frlengths[1] = m->m_pkthdr.len;
- xfer->nframes = xfer->frlengths[1] ? 2 : 1;
+ xfer->nframes = 2;
 
  NG_FREE_M(m);
 
  usb2_start_hardware(xfer);
- return;
+ break;
 
- default: /* Error */
- if (xfer->error == USB_ERR_CANCELLED) {
- /* ignore */
- return;
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "control transfer failed: %s\n",
+ usb2_errstr(xfer->error));
+
+ UBT_STAT_OERROR(sc);
+ goto send_next;
  }
- goto tr_transferred;
+
+ NG_NODE_UNREF(node); /* cancelled */
+ break;
  }
-}
+} /* ubt_ctrl_write_callback */
 
+/*
+ * Called when incoming interrupt transfer (HCI event) has completed, i.e.
+ * HCI event was received from the device.
+ * USB context.
+ */
+
 static void
 ubt_intr_read_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct mbuf *m;
- uint32_t max_len;
- uint8_t *ptr;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct mbuf *m;
+ ng_hci_event_pkt_t *hdr;
+ int error;
 
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
+
+ sc = NG_NODE_PRIVATE(node);
+
+ if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) {
+ UBT_INFO(sc, "no upstream hook\n");
+ NG_NODE_UNREF(node);
+ return; /* upstream hook is gone */
+ }
+
+ m = NULL;
+
  switch (USB_GET_STATE(xfer)) {
  case USB_ST_TRANSFERRED:
-
- /* allocate a new mbuf */
-
+ /* Allocate a new mbuf */
  MGETHDR(m, M_DONTWAIT, MT_DATA);
-
  if (m == NULL) {
- goto tr_setup;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
+
  MCLGET(m, M_DONTWAIT);
-
  if (!(m->m_flags & M_EXT)) {
- NG_FREE_M(m);
- goto tr_setup;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
- *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
- m->m_pkthdr.len = m->m_len = 1;
- } else {
- m->m_pkthdr.len = m->m_len = 0;
+
+ /* Add HCI packet type */
+ *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
+
+ if (xfer->actlen > MCLBYTES - 1)
+ xfer->actlen = MCLBYTES - 1;
+
+ usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
+ xfer->actlen);
+ m->m_pkthdr.len += xfer->actlen;
+ m->m_len += xfer->actlen;
+
+ UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
+ xfer->actlen);
+
+ /* Validate packet and send it up the stack */
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ UBT_INFO(sc, "HCI event packet is too short\n");
+
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
 
- max_len = (MCLBYTES - m->m_len);
+ 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);
 
- if (xfer->actlen > max_len) {
- xfer->actlen = max_len;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- ptr = ((uint8_t *)(m->m_data)) + m->m_len;
 
- usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen);
+ UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
+ "length=%d\n", m->m_pkthdr.len, hdr->length);
 
- m->m_pkthdr.len += xfer->actlen;
- m->m_len += xfer->actlen;
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
 
- NG_UBT_INFO(sc, "got %d bytes from interrupt "
-    "pipe\n", xfer->actlen);
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (error != 0)
+ UBT_STAT_IERROR(sc);
 
- sc->sc_intr_buffer = m;
+ /* m == NULL at this point */
+ /* FALLTHROUGH */
 
  case USB_ST_SETUP:
-tr_setup:
+submit_next:
+ NG_FREE_M(m); /* checks for m != NULL */
 
- if (sc->sc_intr_buffer) {
- ng_send_fn(sc->sc_node, NULL, ubt_intr_read_complete, NULL, 0);
- return;
+ if (sc->sc_flags & UBT_FLAG_INTR_STALL)
+ usb2_transfer_start(sc->sc_xfer[UBT_IF_0_INTR_CS_RD]);
+ else {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
  }
- if (sc->sc_flags & UBT_FLAG_INTR_STALL) {
- usb2_transfer_start(sc->sc_xfer_if_0[6]);
- return;
- }
- xfer->frlengths[0] = xfer->max_data_length;
+ break;
 
- usb2_start_hardware(xfer);
- return;
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "interrupt transfer failed: %s\n",
+ usb2_errstr(xfer->error));
 
- default: /* Error */
- if (xfer->error != USB_ERR_CANCELLED) {
- /* try to clear stall first */
+ /* Try to clear stall first */
  sc->sc_flags |= UBT_FLAG_INTR_STALL;
- usb2_transfer_start(sc->sc_xfer_if_0[6]);
- }
- return;
-
+ usb2_transfer_start(sc->sc_xfer[UBT_IF_0_INTR_CS_RD]);
+ } else
+ NG_NODE_UNREF(node); /* cancelled */
+ break;
  }
-}
+} /* ubt_intr_read_callback */
 
+/*
+ * Called when outgoing control transfer initiated to clear stall on
+ * interrupt pipe has completed.
+ * USB context.
+ */
+
 static void
 ubt_intr_read_clear_stall_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[2];
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct usb2_xfer *xfer_other;
 
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
+
+ sc = NG_NODE_PRIVATE(node);
+ xfer_other = sc->sc_xfer[UBT_IF_0_INTR_DT_RD];
+
  if (usb2_clear_stall_callback(xfer, xfer_other)) {
  DPRINTF("stall cleared\n");
  sc->sc_flags &= ~UBT_FLAG_INTR_STALL;
  usb2_transfer_start(xfer_other);
- }
-}
+ } else
+ NG_NODE_UNREF(node); /* cant clear stall */
+} /* ubt_intr_read_clear_stall_callback */
 
+/*
+ * Called when incoming bulk transfer (ACL packet) has completed, i.e.
+ * ACL packet was received from the device.
+ * USB context.
+ */
+
 static void
-ubt_intr_read_complete(node_p node, hook_p hook, void *arg1, int arg2)
+ubt_bulk_read_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
- struct mbuf *m;
- ng_hci_event_pkt_t *hdr;
- int error;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct mbuf *m;
+ ng_hci_acldata_pkt_t *hdr;
+ uint16_t len;
+ int error;
 
- if (sc == NULL) {
- return;
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
  }
- mtx_lock(&sc->sc_mtx);
 
- m = sc->sc_intr_buffer;
+ sc = NG_NODE_PRIVATE(node);
 
- if (m) {
-
- 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(sc, "No upstream hook\n");
- goto done;
- }
- NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len);
-
- if (m->m_pkthdr.len < sizeof(*hdr)) {
- NG_UBT_INFO(sc, "Packet too short\n");
- goto done;
- }
- if (hdr->length == (m->m_pkthdr.len - sizeof(*hdr))) {
- NG_UBT_INFO(sc, "Got complete HCI event frame, "
-    "pktlen=%d, length=%d\n",
-    m->m_pkthdr.len, hdr->length);
-
- NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
-
- NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
-
- m = NULL;
-
- if (error != 0) {
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
- } else {
- NG_UBT_ERR(sc, "Invalid HCI event frame size, "
-    "length=%d, pktlen=%d\n",
-    hdr->length, m->m_pkthdr.len);
-
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
+ if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) {
+ UBT_INFO(sc, "no upstream hook\n");
+ NG_NODE_UNREF(node);
+ return; /* upstream hook is gone */
  }
-done:
- if (m) {
- NG_FREE_M(m);
- }
- /* start USB transfer if not already started */
 
- usb2_transfer_start(sc->sc_xfer_if_0[2]);
+ m = NULL;
 
- mtx_unlock(&sc->sc_mtx);
-}
-
-static void
-ubt_bulk_read_callback(struct usb2_xfer *xfer)
-{
- struct ubt_softc *sc = xfer->priv_sc;
- struct mbuf *m;
- uint32_t max_len;
- uint8_t *ptr;
-
  switch (USB_GET_STATE(xfer)) {
  case USB_ST_TRANSFERRED:
-
- /* allocate new mbuf */
-
+ /* Allocate new mbuf */
  MGETHDR(m, M_DONTWAIT, MT_DATA);
-
  if (m == NULL) {
- goto tr_setup;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
+
  MCLGET(m, M_DONTWAIT);
-
  if (!(m->m_flags & M_EXT)) {
- NG_FREE_M(m);
- goto tr_setup;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
- *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
- m->m_pkthdr.len = m->m_len = 1;
- } else {
- m->m_pkthdr.len = m->m_len = 0;
- }
 
- max_len = (MCLBYTES - m->m_len);
+ /* Add HCI packet type */
+ *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
 
- if (xfer->actlen > max_len) {
- xfer->actlen = max_len;
- }
- ptr = ((uint8_t *)(m->m_data)) + m->m_len;
+ if (xfer->actlen > MCLBYTES - 1)
+ xfer->actlen = MCLBYTES - 1;
 
- usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen);
-
+ usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
+ xfer->actlen);
  m->m_pkthdr.len += xfer->actlen;
  m->m_len += xfer->actlen;
 
- NG_UBT_INFO(sc, "got %d bytes from bulk-in "
-    "pipe\n", xfer->actlen);
+ UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
+ xfer->actlen);
 
- sc->sc_bulk_in_buffer = m;
+ /* Validate packet and send it up the stack */
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ UBT_INFO(sc, "HCI ACL packet is too short\n");
 
- case USB_ST_SETUP:
-tr_setup:
- if (sc->sc_bulk_in_buffer) {
- ng_send_fn(sc->sc_node, NULL, ubt_bulk_read_complete, NULL, 0);
- return;
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- if (sc->sc_flags & UBT_FLAG_READ_STALL) {
- usb2_transfer_start(sc->sc_xfer_if_0[5]);
- return;
- }
- xfer->frlengths[0] = xfer->max_data_length;
 
- usb2_start_hardware(xfer);
- return;
+ hdr = mtod(m, ng_hci_acldata_pkt_t *);
+ len = le16toh(hdr->length);
+ if (len != (m->m_pkthdr.len - sizeof(*hdr))) {
+ UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
+ "pktlen=%d\n", len, m->m_pkthdr.len);
 
- default: /* Error */
- if (xfer->error != USB_ERR_CANCELLED) {
- /* try to clear stall first */
- sc->sc_flags |= UBT_FLAG_READ_STALL;
- usb2_transfer_start(sc->sc_xfer_if_0[5]);
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
  }
- return;
 
- }
-}
+ UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
+ "length=%d\n", m->m_pkthdr.len, len);
 
-static void
-ubt_bulk_read_clear_stall_callback(struct usb2_xfer *xfer)
-{
- struct ubt_softc *sc = xfer->priv_sc;
- struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[1];
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
 
- if (usb2_clear_stall_callback(xfer, xfer_other)) {
- DPRINTF("stall cleared\n");
- sc->sc_flags &= ~UBT_FLAG_READ_STALL;
- usb2_transfer_start(xfer_other);
- }
-}
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (error != 0)
+ UBT_STAT_IERROR(sc);
 
-static void
-ubt_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2)
-{
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
- struct mbuf *m;
- ng_hci_acldata_pkt_t *hdr;
- uint16_t len;
- int error;
+ /* m == NULL at this point */
+ /* FALLTHOUGH */
 
- if (sc == NULL) {
- return;
- }
- mtx_lock(&sc->sc_mtx);
+ case USB_ST_SETUP:
+submit_next:
+ NG_FREE_M(m); /* checks for m != NULL */
 
- m = sc->sc_bulk_in_buffer;
-
- if (m) {
-
- 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(sc, "No upstream hook\n");
- goto done;
+ if (sc->sc_flags & UBT_FLAG_READ_STALL)
+ usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_CS_RD]);
+ else {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
  }
- NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len);
+ break;
 
- if (m->m_pkthdr.len < sizeof(*hdr)) {
- NG_UBT_INFO(sc, "Packet too short\n");
- goto done;
- }
- len = le16toh(hdr->length);
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "bulk-in transfer failed: %s\n",
+ usb2_errstr(xfer->error));
 
- if (len == (m->m_pkthdr.len - sizeof(*hdr))) {
- NG_UBT_INFO(sc, "Got complete ACL data frame, "
-    "pktlen=%d, length=%d\n",
-    m->m_pkthdr.len, len);
+ /* Try to clear stall first */
+ sc->sc_flags |= UBT_FLAG_READ_STALL;
+ usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_CS_RD]);
+ } else
+ NG_NODE_UNREF(node); /* cancelled */
+ break;
+ }
+} /* ubt_bulk_read_callback */
 
- NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+/*
+ * Called when outgoing control transfer initiated to clear stall on
+ * incoming bulk pipe has completed.
+ * USB context.
+ */
 
- NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+static void
+ubt_bulk_read_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct usb2_xfer *xfer_other;
 
- m = NULL;
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
 
- if (error != 0) {
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
- } else {
- NG_UBT_ERR(sc, "Invalid ACL frame size, "
-    "length=%d, pktlen=%d\n",
-    len, m->m_pkthdr.len);
+ sc = NG_NODE_PRIVATE(node);
+ xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_RD];
 
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
- }
-done:
- if (m) {
- NG_FREE_M(m);
- }
- /* start USB transfer if not already started */
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~UBT_FLAG_READ_STALL;
+ usb2_transfer_start(xfer_other);
+ } else
+ NG_NODE_UNREF(node); /* cant clear stall */
+} /* ubt_bulk_read_clear_stall_callback */
 
- usb2_transfer_start(sc->sc_xfer_if_0[1]);
+/*
+ * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
+ * ACL packet was sent to the device.
+ * USB context.
+ */
 
- mtx_unlock(&sc->sc_mtx);
-}
-
 static void
 ubt_bulk_write_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct mbuf *m;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ 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:
- NG_UBT_INFO(sc, "sent %d bytes to bulk-out "
-    "pipe\n", xfer->actlen);
- NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen);
- NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ if (xfer->error != 0)
+ UBT_STAT_OERROR(sc);
+ else {
+ UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n",
+ xfer->actlen);
 
- case USB_ST_SETUP:
- if (sc->sc_flags & UBT_FLAG_WRITE_STALL) {
- usb2_transfer_start(sc->sc_xfer_if_0[4]);
- return;
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
  }
- /* get next mbuf, if any */
+ /* FALLTHROUGH */
 
+ case USB_ST_SETUP:
+ /* Get next mbuf, if any */
+ UBT_MBUFQ_LOCK(sc);
  NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
+ UBT_MBUFQ_UNLOCK(sc);
 
  if (m == NULL) {
- NG_UBT_INFO(sc, "ACL data queue is empty\n");
- return;
+ UBT_INFO(sc, "ACL data queue is empty\n");
+ NG_NODE_UNREF(node);
+ return; /* transfer completed */
  }
+
  /*
- * check ACL data frame size and
- * copy it back to a linear USB
- * transfer buffer:
+ * Copy ACL data frame back to a linear USB transfer buffer
+ * and schedule transfer
  */
 
- if (m->m_pkthdr.len > UBT_BULK_WRITE_BUFFER_SIZE) {
- panic("ACL data frame too big, size=%d, len=%d\n",
-    UBT_BULK_WRITE_BUFFER_SIZE,
-    m->m_pkthdr.len);
- }
  usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len);
+ xfer->frlengths[0] = m->m_pkthdr.len;
 
- NG_UBT_INFO(sc, "bulk-out transfer has been started, "
-    "len=%d\n", m->m_pkthdr.len);
+ UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
+ m->m_pkthdr.len);
 
- xfer->frlengths[0] = m->m_pkthdr.len;
-
  NG_FREE_M(m);
 
  usb2_start_hardware(xfer);
- return;
+ break;
 
- default: /* Error */
+ default: /* Error */
  if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "bulk-out transfer failed: %s\n",
+ usb2_errstr(xfer->error));
 
- NG_UBT_WARN(sc, "bulk-out transfer failed: %s\n",
-    usb2_errstr(xfer->error));
+ UBT_STAT_OERROR(sc);
 
- NG_UBT_STAT_OERROR(sc->sc_stat);
-
  /* try to clear stall first */
  sc->sc_flags |= UBT_FLAG_WRITE_STALL;
- usb2_transfer_start(sc->sc_xfer_if_0[4]);
- }
- return;
-
+ usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_CS_WR]);
+ } else
+ NG_NODE_UNREF(node); /* cancelled */
+ break;
  }
-}
+} /* ubt_bulk_write_callback */
 
+/*
+ * Called when outgoing control transfer initiated to clear stall on
+ * outgoing bulk pipe has completed.
+ * USB context.
+ */
+
 static void
 ubt_bulk_write_clear_stall_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[0];
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct usb2_xfer *xfer_other;
 
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
+
+ sc = NG_NODE_PRIVATE(node);
+ xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_WR];
+
  if (usb2_clear_stall_callback(xfer, xfer_other)) {
  DPRINTF("stall cleared\n");
  sc->sc_flags &= ~UBT_FLAG_WRITE_STALL;
  usb2_transfer_start(xfer_other);
- }
-}
+ } else
+ NG_NODE_UNREF(node); /* cant clear stall */
+} /* ubt_bulk_write_clear_stall_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_read_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- ng_hci_scodata_pkt_t hdr;
- struct mbuf *m;
- uint8_t *ptr;
- uint32_t max_len;
- uint32_t n;
- uint32_t offset;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ int n;
 
- switch (USB_GET_STATE(xfer)) {
- case USB_ST_TRANSFERRED:
-tr_transferred:
- offset = 0;
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
 
- for (n = 0; n < xfer->nframes; n++) {
+ sc = NG_NODE_PRIVATE(node);
 
- if (xfer->frlengths[n] >= sizeof(hdr)) {
+ if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) {
+ UBT_INFO(sc, "no upstream hook\n");
+ NG_NODE_UNREF(node);
+ return; /* upstream hook is gone */
+ }
 
- usb2_copy_out(xfer->frbuffers, offset,
-    &hdr, sizeof(hdr));
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ for (n = 0; n < xfer->nframes; n ++)
+ if (ubt_isoc_read_one_frame(xfer, n) < 0)
+ break;
+ /* FALLTHROUGH */
 
- if (hdr.length == (xfer->frlengths[n] - sizeof(hdr))) {
+ case USB_ST_SETUP:
+read_next:
+ for (n = 0; n < xfer->nframes; n ++)
+ xfer->frlengths[n] = xfer->max_frame_size;
 
- NG_UBT_INFO(sc, "got complete SCO data "
-    "frame, length=%d\n", hdr.length);
+ usb2_start_hardware(xfer);
+ break;
 
- if (!NG_BT_MBUFQ_FULL(&sc->sc_sciq)) {
+ default: /* Error */
+                if (xfer->error != USB_ERR_CANCELLED) {
+                        UBT_STAT_IERROR(sc);
+                        goto read_next;
+ /* NOT REACHED */
+                }
 
- /* allocate a new mbuf */
+ NG_NODE_UNREF(node); /* cancelled */
+ break;
+ }
+} /* ubt_isoc_read_callback */
 
- MGETHDR(m, M_DONTWAIT, MT_DATA);
+/*
+ * Helper function. Called from ubt_isoc_read_callback() to read
+ * SCO data from one frame.
+ * USB context.
+ */
 
- if (m == NULL) {
- goto tr_setup;
- }
- MCLGET(m, M_DONTWAIT);
+static int
+ubt_isoc_read_one_frame(struct usb2_xfer *xfer, int frame_no)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+ int len, want, got, error;
 
- if (!(m->m_flags & M_EXT)) {
- NG_FREE_M(m);
- goto tr_setup;
- }
- /*
- * fix SCO data frame header
- * if required
- */
+ /* Get existing SCO reassembly buffer */
+ m = sc->sc_isoc_in_buffer;
+ sc->sc_isoc_in_buffer = NULL;
 
- if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
- *mtod(m, uint8_t *)= NG_HCI_SCO_DATA_PKT;
- m->m_pkthdr.len = m->m_len = 1;
- } else {
- m->m_pkthdr.len = m->m_len = 0;
- }
+ /* While we have data in the frame */
+ while ((len = xfer->frlengths[frame_no]) > 0) {
+ if (m == NULL) {
+ /* Start new reassembly buffer */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ UBT_STAT_IERROR(sc);
+ return (-1); /* XXX out of sync! */
+ }
 
- max_len = (MCLBYTES - m->m_len);
-
- if (xfer->frlengths[n] > max_len) {
- xfer->frlengths[n] = max_len;
- }
- ptr = ((uint8_t *)(m->m_data)) + m->m_len;
-
- usb2_copy_out
-    (xfer->frbuffers, offset,
-    ptr, xfer->frlengths[n]);
-
- m->m_pkthdr.len += xfer->frlengths[n];
- m->m_len += xfer->frlengths[n];
-
- NG_BT_MBUFQ_ENQUEUE(&sc->sc_sciq, m);
- }
- }
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ UBT_STAT_IERROR(sc);
+ NG_FREE_M(m);
+ return (-1); /* XXX out of sync! */
  }
- offset += xfer->max_frame_size;
- }
 
- case USB_ST_SETUP:
-tr_setup:
+ /* 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 (NG_BT_MBUFQ_LEN(&sc->sc_sciq) > 0) {
- ng_send_fn(sc->sc_node, NULL, ubt_isoc_read_complete, NULL, 0);
+ if (got >= want)
+ want += mtod(m, ng_hci_scodata_pkt_t *)->length;
  }
- for (n = 0; n < xfer->nframes; n++) {
- xfer->frlengths[n] = xfer->max_frame_size;
- }
 
- usb2_start_hardware(xfer);
- return;
+ /* Append frame data to the SCO reassembly buffer */
+ if (got + len > want)
+ len = want - got;
 
- default: /* Error */
- if (xfer->error == USB_ERR_CANCELLED) {
- /* ignore */
- return;
- }
- goto tr_transferred;
- }
-}
+ usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size,
+ mtod(m, uint8_t *) + m->m_pkthdr.len, len);
 
-static void
-ubt_isoc_read_complete(node_p node, hook_p hook, void *arg1, int arg2)
-{
- ubt_softc_p sc = NG_NODE_PRIVATE(node);
- struct mbuf *m;
- int error;
+ m->m_pkthdr.len += len;
+ m->m_len += len;
+ xfer->frlengths[frame_no] -= len;
 
- if (sc == NULL) {
- return;
- }
- mtx_lock(&sc->sc_mtx);
+ /* Check if we got everything we wanted, if not - continue */
+ if (got != want)
+ continue;
 
- while (1) {
+ /* 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);
 
- NG_BT_MBUFQ_DEQUEUE(&sc->sc_sciq, m);
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
 
- if (m == NULL) {
- break;
- }
- if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) {
- NG_UBT_INFO(sc, "No upstream hook\n");
- goto done;
- }
- NG_UBT_INFO(sc, "Got complete SCO data frame, "
-    "pktlen=%d bytes\n", m->m_pkthdr.len);
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (error != 0)
+ UBT_STAT_IERROR(sc);
 
- NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
- NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len);
+ /* m == NULL at this point */
+ }
 
- NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ /* Put SCO reassembly buffer back */
+ sc->sc_isoc_in_buffer = m;
 
- m = NULL;
+ return (0);
+} /* ubt_isoc_read_one_frame */
 
- if (error) {
- NG_UBT_STAT_IERROR(sc->sc_stat);
- }
-done:
- if (m) {
- NG_FREE_M(m);
- }
- }
+/*
+ * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
+ * SCO packet was sent to the device.
+ * USB context.
+ */
 
- mtx_unlock(&sc->sc_mtx);
-}
-
 static void
 ubt_isoc_write_callback(struct usb2_xfer *xfer)
 {
- struct ubt_softc *sc = xfer->priv_sc;
- struct mbuf *m;
- uint32_t n;
- uint32_t len;
- uint32_t offset;
+ node_p node = xfer->priv_sc;
+ struct ubt_softc *sc;
+ struct mbuf *m;
+ int n, space, offset;
 
+ 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) {
- NG_UBT_STAT_OERROR(sc->sc_stat);
- } else {
- NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen);
- NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ if (xfer->error)
+ UBT_STAT_OERROR(sc);
+ else {
+ UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n",
+ xfer->actlen);
+
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
  }
+ /* FALLTHROUGH */
 
  case USB_ST_SETUP:
+send_next:
  offset = 0;
+ space = xfer->max_frame_size * xfer->nframes;
+ m = NULL;
 
- for (n = 0; n < xfer->nframes; n++) {
+ while (space > 0) {
+ if (m == NULL) {
+ UBT_MBUFQ_LOCK(sc);
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
+ UBT_MBUFQ_UNLOCK(sc);
 
- NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
+ if (m == NULL)
+ break;
+ }
 
- if (m) {
- len = min(xfer->max_frame_size, m->m_pkthdr.len);
+ n = min(space, m->m_pkthdr.len);
+ if (n > 0) {
+ usb2_m_copy_in(xfer->frbuffers, offset, m,0, n);
+ m_adj(m, n);
 
- usb2_m_copy_in(xfer->frbuffers, offset, m, 0, len);
+ offset += n;
+ space -= n;
+ }
 
- NG_FREE_M(m);
+ if (m->m_pkthdr.len == 0)
+ NG_FREE_M(m); /* sets m = NULL */
+ }
 
- xfer->frlengths[n] = len;
- offset += len;
- } else {
- xfer->frlengths[n] = 0;
- }
+ /* Put whatever is left from mbuf back on queue */
+ if (m != NULL) {
+ UBT_MBUFQ_LOCK(sc);
+ NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
+ UBT_MBUFQ_UNLOCK(sc);
  }
 
+ /*
+ * 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.
+ */
+
+ for (n = 0; n < xfer->nframes; n ++) {
+ xfer->frlengths[n] = min(offset, xfer->max_frame_size);
+ offset -= xfer->frlengths[n];
+ }
+
  usb2_start_hardware(xfer);
- return;
+ break;
 
- default: /* Error */
- if (xfer->error == USB_ERR_CANCELLED) {
- /* ignore */
- return;
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_STAT_OERROR(sc);
+ goto send_next;
+ /* NOT REACHED */
  }
- goto tr_transferred;
+
+ NG_NODE_UNREF(node); /* cancelled */
+ break;
  }
 }
 
 /****************************************************************************
  ****************************************************************************
- **                        Netgraph specific
+ **                                 Glue
  ****************************************************************************
  ****************************************************************************/
 
 /*
- * Netgraph node constructor.
- * Do not allow to create node of this type:
+ * Schedule glue task. Should be called with sc_mbufq_mtx held.
+ * Netgraph context.
  */
 
 static int
-ng_ubt_constructor(node_p node)
+ubt_task_schedule(ubt_softc_p sc, int action)
 {
- return (EINVAL);
-}
+ mtx_assert(&sc->sc_mbufq_mtx, MA_OWNED);
 
+ if ((sc->sc_task_flags & action) == 0) {
+ /*
+ * Try to handle corner case when "start all" and "stop all"
+ * actions can both be set before task is executed.
+ *
+ * Assume the following:
+ * 1) "stop all" after "start all" cancels "start all", and,
+ *    keeps "stop all"
+ *
+ * 2) "start all" after "stop all" is fine because task is
+ *    executing "stop all" first
+ */
+
+ if (action == UBT_FLAG_T_STOP_ALL &&
+    (sc->sc_task_flags & UBT_FLAG_T_START_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 (1);
+
+ if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
+ NG_NODE_REF(sc->sc_node);
+ sc->sc_task_flags |= UBT_FLAG_T_PENDING;
+ return (1);
+ }
+
+ /* XXX: i think this should never happen */
+
+ return (0);
+} /* ubt_task_schedule */
+
 /*
- * Netgraph node destructor.
- * Destroy node only when device has been detached:
+ * Glue task. Examines sc_task_flags and does things depending on it.
+ * Taskqueue context.
  */
 
-static int
-ng_ubt_shutdown(node_p node)
+static void
+ubt_task(void *context, int pending)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
+ node_p node = context;
+ ubt_softc_p sc;
+ int task_flags;
 
- /* Let old node go */
- NG_NODE_SET_PRIVATE(node, NULL);
- NG_NODE_UNREF(node);
+ if (NG_NODE_NOT_VALID(node)) {
+ NG_NODE_UNREF(node);
+ return; /* netgraph node is gone */
+ }
 
- if (sc == NULL) {
- goto done;
+ sc = NG_NODE_PRIVATE(node);
+
+ UBT_MBUFQ_LOCK(sc);
+ task_flags = sc->sc_task_flags;
+ sc->sc_task_flags = 0;
+ UBT_MBUFQ_UNLOCK(sc);
+
+ /* Stop all USB transfers */
+ if (task_flags & UBT_FLAG_T_STOP_ALL) {
+ int i;
+
+ /*
+ * Interface #0
+ */
+
+ mtx_lock(&sc->sc_if_mtx[0]);
+
+ for (i = UBT_IF_0_BULK_DT_WR; i < UBT_IF_0_N_TRANSFER; i ++)
+ usb2_transfer_stop(sc->sc_xfer[i]);
+
+ mtx_unlock(&sc->sc_if_mtx[0]);
+
+ /*
+ * Interface #1
+ */
+
+ mtx_lock(&sc->sc_if_mtx[1]);
+
+ for (i = UBT_IF_1_ISOC_DT_RD1; i < UBT_N_TRANSFER; i ++)
+ usb2_transfer_stop(sc->sc_xfer[i]);
+
+ mtx_unlock(&sc->sc_if_mtx[1]);
  }
- mtx_lock(&sc->sc_mtx);
 
- /* 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;
- goto done;
+ /* Start all incoming USB transfers */
+ if (task_flags & UBT_FLAG_T_START_ALL) {
+ /*
+ * Interface #0
+ */
+
+ mtx_lock(&sc->sc_if_mtx[0]);
+ ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
+ ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
+ mtx_unlock(&sc->sc_if_mtx[0]);
+
+ /*
+ * 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.
+ */
+
+ mtx_lock(&sc->sc_if_mtx[1]);
+ 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);
+ mtx_unlock(&sc->sc_if_mtx[1]);
  }
- /* 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;
- goto done;
+
+ /* Start outgoing control transfer */
+ if (task_flags & UBT_FLAG_T_START_CTRL) {
+ mtx_lock(&sc->sc_if_mtx[0]);
+ ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
+ mtx_unlock(&sc->sc_if_mtx[0]);
  }
- NG_NODE_SET_PRIVATE(sc->sc_node, sc);
- NG_NODE_FORCE_WRITER(sc->sc_node);
 
-done:
- if (sc) {
- mtx_unlock(&sc->sc_mtx);
+ /* Start outgoing bulk transfer */
+ if (task_flags & UBT_FLAG_T_START_BULK) {
+ mtx_lock(&sc->sc_if_mtx[0]);
+ ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
+ mtx_unlock(&sc->sc_if_mtx[0]);
  }
- return (0);
-}
 
+ NG_NODE_UNREF(node);
+} /* ubt_task */
+
 /*
- * Create new hook.
- * There can only be one.
+ * Start USB transfer.
+ * Helper function called from ubt_task. Must be called with appropriate
+ * interface lock held.
+ * Taskqueue context.
  */
 
-static int
-ng_ubt_newhook(node_p node, hook_p hook, char const *name)
+static void
+ubt_xfer_start(ubt_softc_p sc, int transfer)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
- int error = 0;
-
- if (strcmp(name, NG_UBT_HOOK) != 0) {
- return (EINVAL);
+ if (!usb2_transfer_pending(sc->sc_xfer[transfer])) {
+ NG_NODE_REF(sc->sc_node);
+ usb2_transfer_start(sc->sc_xfer[transfer]);
  }
- mtx_lock(&sc->sc_mtx);
+} /* ubt_xfer_start */
 
- if (sc->sc_hook != NULL) {
- error = EISCONN;
- } else {
- sc->sc_hook = hook;
- }
+/****************************************************************************
+ ****************************************************************************
+ **                        Netgraph specific
+ ****************************************************************************
+ ****************************************************************************/
 
- mtx_unlock(&sc->sc_mtx);
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ * Netgraph context.
+ */
 
- return (error);
-}
+static int
+ng_ubt_constructor(node_p node)
+{
+ return (EINVAL);
+} /* ng_ubt_constructor */
 
 /*
- * Connect hook.
- * Start incoming USB transfers
+ * Netgraph node destructor. Destroy node only when device has been detached.
+ * Netgraph context.
  */
 
 static int
-ng_ubt_connect(hook_p hook)
+ng_ubt_shutdown(node_p node)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ 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_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+ return (0);
+} /* ng_ubt_shutdown */
 
- mtx_lock(&sc->sc_mtx);
+/*
+ * Create new hook. There can only be one.
+ * Netgraph context.
+ */
 
- sc->sc_flags |= (UBT_FLAG_READ_STALL |
-    UBT_FLAG_WRITE_STALL |
-    UBT_FLAG_INTR_STALL);
+static int
+ng_ubt_newhook(node_p node, hook_p hook, char const *name)
+{
+ struct ubt_softc *sc = NG_NODE_PRIVATE(node);
 
- /* start intr transfer */
- usb2_transfer_start(sc->sc_xfer_if_0[2]);
+ if (strcmp(name, NG_UBT_HOOK) != 0)
+ return (EINVAL);
 
- /* start bulk-in transfer */
- usb2_transfer_start(sc->sc_xfer_if_0[1]);
+ if (sc->sc_hook != NULL)
+ return (EISCONN);
 
- /* start bulk-out transfer */
- usb2_transfer_start(sc->sc_xfer_if_0[0]);
+ sc->sc_hook = hook;
 
- /* start control-out transfer */
- usb2_transfer_start(sc->sc_xfer_if_0[3]);
-#if 0
- XXX can enable this XXX
+ return (0);
+} /* ng_ubt_newhook */
 
- /* start isoc-in transfer */
-     usb2_transfer_start(sc->sc_xfer_if_1[0]);
+/*
+ * Connect hook. Start incoming USB transfers.
+ * Netgraph context.
+ */
 
- usb2_transfer_start(sc->sc_xfer_if_1[1]);
+static int
+ng_ubt_connect(hook_p hook)
+{
+ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
 
- /* start isoc-out transfer */
- usb2_transfer_start(sc->sc_xfer_if_1[2]);
- usb2_transfer_start(sc->sc_xfer_if_1[3]);
-#endif
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
 
- mtx_unlock(&sc->sc_mtx);
+ UBT_MBUFQ_LOCK(sc);
+ ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
+ UBT_MBUFQ_UNLOCK(sc);
 
  return (0);
-}
+} /* ng_ubt_connect */
 
 /*
- * Disconnect hook
+ * Disconnect hook.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_disconnect(hook_p hook)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
- int error = 0;
+ node_p node = NG_HOOK_NODE(hook);
+ struct ubt_softc *sc;
 
- if (sc != NULL) {
+ if (NG_NODE_NOT_VALID(node))
+ return (0);
 
- mtx_lock(&sc->sc_mtx);
+ sc = NG_NODE_PRIVATE(node);
 
- if (hook != sc->sc_hook) {
- error = EINVAL;
- } else {
+ if (hook != sc->sc_hook)
+ return (EINVAL);
 
- /* stop intr transfer */
- usb2_transfer_stop(sc->sc_xfer_if_0[2]);
- usb2_transfer_stop(sc->sc_xfer_if_0[6]);
+ sc->sc_hook = NULL;
 
- /* stop bulk-in transfer */
- usb2_transfer_stop(sc->sc_xfer_if_0[1]);
- usb2_transfer_stop(sc->sc_xfer_if_0[5]);
+ UBT_MBUFQ_LOCK(sc);
 
- /* stop bulk-out transfer */
- usb2_transfer_stop(sc->sc_xfer_if_0[0]);
- usb2_transfer_stop(sc->sc_xfer_if_0[4]);
+ /* Drain queues */
+ NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
 
- /* stop control transfer */
- usb2_transfer_stop(sc->sc_xfer_if_0[3]);
+ /* Kick off task to stop all USB xfers */
+ ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
 
- /* stop isoc-in transfer */
- usb2_transfer_stop(sc->sc_xfer_if_1[0]);
- usb2_transfer_stop(sc->sc_xfer_if_1[1]);
+ UBT_MBUFQ_UNLOCK(sc);
 
- /* stop isoc-out transfer */
- usb2_transfer_stop(sc->sc_xfer_if_1[2]);
- usb2_transfer_stop(sc->sc_xfer_if_1[3]);
-
- /* cleanup queues */
- NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
- NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
- NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
- NG_BT_MBUFQ_DRAIN(&sc->sc_sciq);
-
- sc->sc_hook = NULL;
- }
-
- mtx_unlock(&sc->sc_mtx);
- }
- return (error);
-}
-
+ 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)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(node);
- struct ng_mesg *msg = NULL, *rsp = NULL;
- struct ng_bt_mbufq *q = NULL;
- int error = 0, queue, qlen;
+ struct ubt_softc *sc = NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg, *rsp = NULL;
+ struct ng_bt_mbufq *q;
+ int error = 0, queue, qlen;
 
- if (sc == NULL) {
- NG_FREE_ITEM(item);
- return (EHOSTDOWN);
- }
- mtx_lock(&sc->sc_mtx);
-
  NGI_GET_MSG(item, msg);
 
  switch (msg->header.typecookie) {
@@ -1527,25 +1655,31 @@
  switch (msg->header.cmd) {
  case NGM_TEXT_STATUS:
  NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
- 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,
+ "Refs: %d\n" \
+ "Hook: %s\n" \
+ "Flags: %#x\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]",
+ node->nd_refs,
+ (sc->sc_hook != NULL) ? NG_UBT_HOOK:"",
+ sc->sc_flags,
+ 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:
@@ -1557,59 +1691,54 @@
  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_NOWAIT);
- if (rsp == NULL)
+ if (rsp == NULL) {
  error = ENOMEM;
- else
- *((ng_ubt_node_debug_ep *) (rsp->data)) =
-    sc->sc_debug;
+ 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;
+ break;
+ }
 
- if (qlen <= 0) {
- error = EINVAL;
- 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:
@@ -1617,7 +1746,9 @@
  error = EMSGSIZE;
  break;
  }
+
  queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
+
  switch (queue) {
  case NGM_UBT_NODE_QUEUE_CMD:
  q = &sc->sc_cmdq;
@@ -1632,39 +1763,36 @@
  break;
 
  default:
- q = NULL;
  error = EINVAL;
+ goto done;
+ /* NOT REACHED */
+ }
+
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
  break;
  }
 
- if (q != NULL) {
- NG_MKRESPONSE(rsp, msg,
-    sizeof(ng_ubt_node_qlen_ep), M_NOWAIT);
- 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_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_NOWAIT);
- if (rsp == NULL)
+ 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:
@@ -1677,90 +1805,161 @@
  error = EINVAL;
  break;
  }
-
+done:
  NG_RESPOND_MSG(error, node, item, rsp);
  NG_FREE_MSG(msg);
 
- mtx_unlock(&sc->sc_mtx);
-
  return (error);
-}
+} /* ng_ubt_rcvmsg */
 
 /*
- * Process data
+ * Process data.
+ * Netgraph context.
  */
 
 static int
 ng_ubt_rcvdata(hook_p hook, item_p item)
 {
- struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
- struct mbuf *m;
- struct ng_bt_mbufq *q;
- struct usb2_xfer *xfer;
- int error = 0;
+ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m;
+ struct ng_bt_mbufq *q;
+ int action, error = 0;
 
- if (sc == NULL) {
- error = EHOSTDOWN;
- goto done;
- }
- mtx_lock(&sc->sc_mtx);
-
  if (hook != sc->sc_hook) {
  error = EINVAL;
  goto done;
  }
- /* deatch mbuf and get HCI frame type */
+
+ /* Deatch mbuf and get HCI frame type */
  NGI_GET_M(item, m);
 
- /* process HCI frame */
+ /*
+ * 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, uint8_t *)) { /* XXX call m_pullup ? */
  case NG_HCI_CMD_PKT:
- xfer = sc->sc_xfer_if_0[3];
+ if (m->m_pkthdr.len - 1 > 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;
+ action = UBT_FLAG_T_START_CTRL;
  break;
 
  case NG_HCI_ACL_DATA_PKT:
- xfer = sc->sc_xfer_if_0[0];
+ 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;
+ action = UBT_FLAG_T_START_BULK;
  break;
 
  case NG_HCI_SCO_DATA_PKT:
- xfer = NULL;
  q = &sc->sc_scoq;
+ action = 0;
  break;
 
  default:
- NG_UBT_ERR(sc, "Dropping unsupported HCI frame, "
-    "type=0x%02x, pktlen=%d\n",
-    *mtod(m, uint8_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(uint8_t));
- }
+ UBT_MBUFQ_LOCK(sc);
  if (NG_BT_MBUFQ_FULL(q)) {
- NG_UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. "
-    "Queue full\n", *mtod(m, uint8_t *),
-    m->m_pkthdr.len);
+ NG_BT_MBUFQ_DROP(q);
+ UBT_MBUFQ_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 {
+ /* Loose HCI packet type, enqueue mbuf and kick off task */
+ m_adj(m, sizeof(uint8_t));
  NG_BT_MBUFQ_ENQUEUE(q, m);
- }
+ ubt_task_schedule(sc, action);
 
- if (xfer) {
- usb2_transfer_start(xfer);
+ UBT_MBUFQ_UNLOCK(sc);
  }
 done:
  NG_FREE_ITEM(item);
 
- if (sc) {
- mtx_unlock(&sc->sc_mtx);
+ 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),
+ { 0, 0 }
+};
+
+static driver_t ubt_driver =
+{
+ .name =   "ubt",
+ .methods = ubt_methods,
+ .size =   sizeof(struct ubt_softc),
+};
+
+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);
+
Index: ng_ubt2_var.h
===================================================================
--- ng_ubt2_var.h (revision 187313)
+++ ng_ubt2_var.h (working copy)
@@ -3,7 +3,7 @@
  */
 
 /*-
- * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@...>
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@...>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,95 +32,111 @@
  */
 
 #ifndef _NG_UBT_VAR_H_
-#define _NG_UBT_VAR_H_
+#define _NG_UBT_VAR_H_ 1
 
-/* 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__, sc->sc_name, (s)); \
- } \
- } while (0)
-
 /* Debug printf's */
-#define NG_UBT_DEBUG(level, sc, fmt, ...) do { \
-    if ((sc)->sc_debug >= (level)) { \
-        printf("%s:%s:%d: " fmt, (sc)->sc_name, \
-       __FUNCTION__, __LINE__,## __VA_ARGS__); \
-    } \
+#define UBT_DEBUG(level, sc, fmt, ...) \
+do { \
+ if ((sc)->sc_debug >= (level)) \
+ printf("%s:%s:%d: " fmt, (sc)->sc_name, \
+ __FUNCTION__, __LINE__,## __VA_ARGS__); \
 } while (0)
 
-#define NG_UBT_ALERT(...) NG_UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__)
-#define NG_UBT_ERR(...)   NG_UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__)
-#define NG_UBT_WARN(...)  NG_UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__)
-#define NG_UBT_INFO(...)  NG_UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__)
+#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_MBUFQ_LOCK(sc) mtx_lock(&(sc)->sc_mbufq_mtx)
+#define UBT_MBUFQ_UNLOCK(sc) mtx_unlock(&(sc)->sc_mbufq_mtx)
+
 /* Bluetooth USB control request type */
 #define UBT_HCI_REQUEST 0x20
-#define UBT_DEFAULT_QLEN 12
+#define UBT_DEFAULT_QLEN 64
+#define UBT_ISOC_NFRAMES 32 /* should be factor of 8 */
 
 /* Bluetooth USB defines */
-#define UBT_IF_0_N_TRANSFER  7 /* units */
-#define UBT_IF_1_N_TRANSFER  4 /* units */
-#define UBT_ISOC_NFRAMES    25 /* units */
+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,
+ UBT_IF_0_BULK_CS_WR,
+ UBT_IF_0_BULK_CS_RD,
+ UBT_IF_0_INTR_CS_RD,
+ UBT_IF_0_N_TRANSFER, /* number of interface 0's transfers */
+
+ /* Interface #1 transfers */
+ UBT_IF_1_ISOC_DT_RD1 = UBT_IF_0_N_TRANSFER,
+ 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 */
+
+ UBT_IF_1_N_TRANSFER = UBT_N_TRANSFER - UBT_IF_1_ISOC_DT_RD1,
+};
+
 /* USB device softc structure */
 struct ubt_softc {
+ uint8_t sc_name[16];
+
  /* State */
- ng_ubt_node_debug_ep sc_debug; /* debug level */
- uint32_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_FLAG_READ_STALL     (1 << 1)/* read transfer has stalled */
-#define UBT_FLAG_WRITE_STALL    (1 << 2)/* write transfer has stalled */
-#define UBT_FLAG_INTR_STALL     (1 << 3)/* interrupt transfer has stalled */
+ ng_ubt_node_debug_ep sc_debug; /* debug level */
 
- 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)))
+ int sc_flags; /* device flags */
+#define UBT_FLAG_READ_STALL (1 << 0) /* read transfer has stalled */
+#define UBT_FLAG_WRITE_STALL (1 << 1) /* write transfer has stalled */
+#define UBT_FLAG_INTR_STALL (1 << 2) /* inter transfer has stalled */
 
- uint8_t sc_name[16];
+ 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))
 
- struct mtx sc_mtx;
-
  /* USB device specific */
- struct usb2_xfer *sc_xfer_if_0[UBT_IF_0_N_TRANSFER];
- struct usb2_xfer *sc_xfer_if_1[UBT_IF_1_N_TRANSFER];
+ struct mtx sc_if_mtx[2]; /* interface locks */
+ struct usb2_xfer *sc_xfer[UBT_N_TRANSFER];
 
- /* Interrupt pipe (HCI events) */
- struct mbuf *sc_intr_buffer; /* interrupt buffer */
+ struct mtx sc_mbufq_mtx; /* lock for all queues */
 
- /* Control pipe (HCI commands) */
- 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)
+ /* HCI commands */
+ struct ng_bt_mbufq sc_cmdq; /* HCI command queue */
+#define UBT_CTRL_BUFFER_SIZE (sizeof(struct usb2_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 */
 
- /* Bulk in pipe (ACL data) */
- struct mbuf *sc_bulk_in_buffer; /* bulk-in buffer */
-
- /* Bulk out pipe (ACL data) */
- struct ng_bt_mbufq sc_aclq; /* ACL data queue */
-#define UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1) /* reserve one 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)
 
- /* Isoc. out pipe (ACL data) */
- struct ng_bt_mbufq sc_scoq; /* SCO data queue */
+ /* SCO data */
+ struct ng_bt_mbufq sc_scoq; /* SCO data queue */
+ struct mbuf *sc_isoc_in_buffer; /* SCO reassembly buffer */
 
- /* Isoc. in pipe (ACL data) */
- struct ng_bt_mbufq sc_sciq; /* SCO data queue */
+ /* Netgraph specific */
+ node_p sc_node; /* pointer back to node */
+ hook_p sc_hook; /* upstream hook */
 
- /* Netgraph specific */
- 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;
+typedef struct ubt_softc ubt_softc_t;
+typedef struct ubt_softc * ubt_softc_p;
 
-#endif /* ndef _NG_UBT_VAR_H_ */
+#endif /* ndef _NG_UBT_VAR_H_ */
+

_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: USB2: ng_ubt2 patch

by M. Warner Losh :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

In message: <bb4a86c70901191421p55bb5404u484f9615b00cf30d@...>
            Maksim Yevmenkin <maksim.yevmenkin@...> writes:
: do i
:
: 1) commit it to head myself
: or
: 2) wait for you commit it into your cvs/scv/p4/etc. branch and then
: merge it into -head

The answer really should be #1.  We've got to have the barriers for
entry into usb2 be relatively low.  Since you've talked to Hans-Peter
Selasky about this change, and he's basically signed off on it, you
should be good to go...

Warner
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: USB2: ng_ubt2 patch

by Hans Petter Selasky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

>
> 1) commit it to head myself

You have my blessing to commit it. I will verify the changes next time I
integrate my P4 repository!

--HPS
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Ongoing Bluetooth work; Linux-like APIs?

by Bruce Simpson :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi guys,

I am pleased to see all the hard work going in to maintaining the
FreeBSD Bluetooth support...

I wonder though if anyone has considered implementing the Linux APIs ?

It's kinda difficult to build applications with what we currently have
in FreeBSD -- a whole host of higher level language bindings have popped
up. Whilst they've done work on making sure it works on Windows with all
the stacks they have there, I wonder if FreeBSD's being different from
Linux is doing us any good here.

Just a suggestion... as it is, I've had to side-step FreeBSD for the
time being for what I am cooking at the moment :-(

cheers
BMS
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: Ongoing Bluetooth work; Linux-like APIs?

by Alfred Perlstein-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

* Bruce Simpson <bms@...> [090120 05:21] wrote:

> Hi guys,
>
> I am pleased to see all the hard work going in to maintaining the
> FreeBSD Bluetooth support...
>
> I wonder though if anyone has considered implementing the Linux APIs ?
>
> It's kinda difficult to build applications with what we currently have
> in FreeBSD -- a whole host of higher level language bindings have popped
> up. Whilst they've done work on making sure it works on Windows with all
> the stacks they have there, I wonder if FreeBSD's being different from
> Linux is doing us any good here.
>
> Just a suggestion... as it is, I've had to side-step FreeBSD for the
> time being for what I am cooking at the moment :-(
>
> cheers
> BMS

That would be good.  Please ping about it if you don't see it within
a few weeks.

--
- Alfred Perlstein
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: Ongoing Bluetooth work; Linux-like APIs?

by Bruce Simpson :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Alfred Perlstein wrote:
> ...
>> I wonder though if anyone has considered implementing the Linux APIs ?
>> ...
>>    
>
> That would be good.  Please ping about it if you don't see it within
> a few weeks.
>  

If folk have cycles to look at that, and are interested in following up,
that is excellent news...

Meanwhile, a good set of pointers would be:
 http://org.csail.mit.edu/pybluez/
 http://lightblue.sourceforge.net/
 http://people.csail.mit.edu/albert/bluez-intro/c404.html
 http://code.google.com/p/bluecove/

Broadly there's a surface of interfaces to underlying system services
(e.g. RFCOMM sockets, OBEX libraries, HCI sockets and/or layer access,
and SDP in particular) which needs to be considered. The APIs above
themselves have limitations with things like working with multiple
interfaces, etc.

I had a very brief crack at trying to get BlueCove to work with the
FreeBSD stack, but I couldn't justify the time involved to get it up and
running, given we'd already started building a solution on Linux.

The differences between the Linux and FreeBSD stacks are small but
subtle enough to render generic code incompatible... sigh.

thanks,
BMS
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: Ongoing Bluetooth work; Linux-like APIs?

by Oliver Fromme :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Bruce M Simpson wrote:
 > Alfred Perlstein wrote:
 > > ...
 > > > I wonder though if anyone has considered implementing the Linux APIs ?
 > > > ...
 > >
 > > That would be good.  Please ping about it if you don't see it within
 > > a few weeks.
 >
 > If folk have cycles to look at that, and are interested in following up,
 > that is excellent news...
 >
 > Meanwhile, a good set of pointers would be:
 >  http://org.csail.mit.edu/pybluez/

I'm not exactly sure what the purpose of pybluez is.
Python already supports Bluetooth on FreeBSD out of the box.
I'm controlling several devices via Bluetooth (RFCOMM/SPP)
with Python programs on FreeBSD.  It works great.  The very
same programs work on NetBSD, too.  I haven't tried Linux,
though.

Best regards
   Oliver

--
Oliver Fromme, secnetix GmbH & Co. KG, Marktplatz 29, 85567 Grafing b. M.
Handelsregister: Registergericht Muenchen, HRA 74606,  Geschäftsfuehrung:
secnetix Verwaltungsgesellsch. mbH, Handelsregister: Registergericht Mün-
chen, HRB 125758,  Geschäftsführer: Maik Bachmann, Olaf Erb, Ralf Gebhart

FreeBSD-Dienstleistungen, -Produkte und mehr:  http://www.secnetix.de/bsd

"The ITU has offered the IETF formal alignment with its
corresponding technology, Penguins, but that won't fly."
        -- RFC 2549
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."

Re: Ongoing Bluetooth work; Linux-like APIs?

by Maksim Yevmenkin-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, Jan 21, 2009 at 9:47 AM, Oliver Fromme <olli@...> wrote:

> Bruce M Simpson wrote:
>  > Alfred Perlstein wrote:
>  > > ...
>  > > > I wonder though if anyone has considered implementing the Linux APIs ?
>  > > > ...
>  > >
>  > > That would be good.  Please ping about it if you don't see it within
>  > > a few weeks.
>  >
>  > If folk have cycles to look at that, and are interested in following up,
>  > that is excellent news...
>  >
>  > Meanwhile, a good set of pointers would be:
>  >  http://org.csail.mit.edu/pybluez/
>
> I'm not exactly sure what the purpose of pybluez is.
> Python already supports Bluetooth on FreeBSD out of the box.
> I'm controlling several devices via Bluetooth (RFCOMM/SPP)
> with Python programs on FreeBSD.  It works great.  The very
> same programs work on NetBSD, too.  I haven't tried Linux,
> though.

i think Bruce is talking about more high level (than bluetooth sockets
support) stuff. for example, performing device inquiry, registering
service with sdp server etc.

while bluez libhci stuff  (device inquiry and other general things
such as sending and receiving hci commands and events) can be done
fairly quickly, sdp api is quite different. Iain Hibbert (of netbsd)
and i talking about sdp and how we can make it better.

if anyone is working on libhci for freebsd, please let me know.

thanks,
max
_______________________________________________
freebsd-bluetooth@... mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscribe@..."