diff -u ng_ubt2.c ng_ubt2.c --- ng_ubt2.c Wed Jan 21 17:51:04 2009 +++ ng_ubt2.c Fri Jan 23 20:17:44 2009 @@ -28,7 +28,7 @@ * SUCH DAMAGE. * * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $ - * $FreeBSD$ + * $FreeBSD: src/sys/dev/usb2/bluetooth/ng_ubt2.c,v 1.6 2009/01/20 23:25:27 emax Exp $ */ /* @@ -37,22 +37,12 @@ * driver will *NOT* create traditional /dev/ enties, only Netgraph * node. * - * NOTE ON LOCKS USED: ng_ubt2 drives uses 3 locks (mutexes) + * NOTE ON LOCKS USED: ng_ubt2 drives uses 1 lock * - * 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. + * The "sc_mtx" lock protects both USB and Netgraph data. The "sc_mtx" + * lock should not be grabbed for a long time. * - * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts. + * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 2 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 @@ -67,26 +57,6 @@ * 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 @@ -111,7 +81,6 @@ #include #include -#include #include #include @@ -128,10 +97,6 @@ static device_attach_t ubt_attach; static device_detach_t ubt_detach; -static int ubt_task_schedule(ubt_softc_p, int); -static task_fn_t ubt_task; -static void ubt_xfer_start(ubt_softc_p, int); - /* Netgraph methods */ static ng_constructor_t ng_ubt_constructor; static ng_shutdown_t ng_ubt_shutdown; @@ -279,6 +244,7 @@ .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, + .if_index = 0, .mh.bufsize = UBT_BULK_WRITE_BUFFER_SIZE, .mh.flags = { .pipe_bof = 1, }, .mh.callback = &ubt_bulk_write_callback, @@ -288,6 +254,7 @@ .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, + .if_index = 0, .mh.bufsize = UBT_BULK_READ_BUFFER_SIZE, .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, .mh.callback = &ubt_bulk_read_callback, @@ -297,6 +264,7 @@ .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, + .if_index = 0, .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, .mh.bufsize = UBT_INTR_BUFFER_SIZE, .mh.callback = &ubt_intr_read_callback, @@ -306,6 +274,7 @@ .type = UE_CONTROL, .endpoint = 0x00, /* control pipe */ .direction = UE_DIR_ANY, + .if_index = 0, .mh.bufsize = UBT_CTRL_BUFFER_SIZE, .mh.callback = &ubt_ctrl_write_callback, .mh.timeout = 5000, /* 5 seconds */ @@ -315,6 +284,7 @@ .type = UE_CONTROL, .endpoint = 0x00, /* control pipe */ .direction = UE_DIR_ANY, + .if_index = 0, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ubt_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ @@ -325,6 +295,7 @@ .type = UE_CONTROL, .endpoint = 0x00, /* control pipe */ .direction = UE_DIR_ANY, + .if_index = 0, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ubt_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ @@ -338,6 +309,7 @@ .type = UE_CONTROL, .endpoint = 0x00, /* control pipe */ .direction = UE_DIR_ANY, + .if_index = 0, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ubt_intr_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ @@ -353,6 +325,7 @@ .type = UE_ISOCHRONOUS, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, + .if_index = 1, .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ .mh.frames = UBT_ISOC_NFRAMES, .mh.flags = { .short_xfer_ok = 1, }, @@ -363,6 +336,7 @@ .type = UE_ISOCHRONOUS, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, + .if_index = 1, .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ .mh.frames = UBT_ISOC_NFRAMES, .mh.flags = { .short_xfer_ok = 1, }, @@ -373,6 +347,7 @@ .type = UE_ISOCHRONOUS, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, + .if_index = 1, .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ .mh.frames = UBT_ISOC_NFRAMES, .mh.flags = { .short_xfer_ok = 1, }, @@ -383,6 +358,7 @@ .type = UE_ISOCHRONOUS, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, + .if_index = 1, .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ .mh.frames = UBT_ISOC_NFRAMES, .mh.flags = { .short_xfer_ok = 1, }, @@ -450,7 +426,8 @@ struct ubt_softc *sc = device_get_softc(dev); struct usb2_endpoint_descriptor *ed; uint16_t wMaxPacketSize; - uint8_t alt_index, iface_index, i, j; + uint8_t alt_index, i, j; + uint8_t iface_index[2]; device_set_usb2_desc(dev); @@ -483,28 +460,22 @@ /* state */ sc->sc_debug = NG_UBT_WARN_LEVEL; - sc->sc_flags = 0; + UBT_STAT_RESET(sc); /* 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); + mtx_init(&sc->sc_mtx, "UBT", NULL, MTX_DEF | MTX_RECURSE); /* initialize packet queues */ NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); - /* initialize glue task */ - sc->sc_task_flags = 0; - TASK_INIT(&sc->sc_task, 0, ubt_task, sc->sc_node); - /* * Configure Bluetooth USB device. Discover all required USB * interfaces and endpoints. * - * Device is expected to be a high-speed device. + * Device is expected to be a full-speed device. * * USB device must present two interfaces: * 1) Interface 0 that has 3 endpoints @@ -520,20 +491,11 @@ * configurations with different packet size. */ - bzero(&sc->sc_xfer, sizeof(sc->sc_xfer)); - /* * Interface 0 */ - iface_index = 0; - 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; - } + iface_index[0] = 0; /* * Interface 1 @@ -580,13 +542,11 @@ goto detach; } - 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"); + iface_index[1] = 1; + if (usb2_transfer_setup(uaa->device, iface_index, + sc->sc_xfer, ubt_config, UBT_N_TRANSFER, + sc->sc_node, &sc->sc_mtx)) { + device_printf(dev, "could not allocate transfers\n"); goto detach; } @@ -594,6 +554,10 @@ for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++) usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); + UBT_LOCK(sc); + sc->sc_flags |= UBT_FLAG_READY; + UBT_UNLOCK(sc); + return (0); /* success */ detach: @@ -612,17 +576,33 @@ { struct ubt_softc *sc = device_get_softc(dev); node_p node = sc->sc_node; + uint8_t i; + + UBT_LOCK(sc); + sc->sc_flags &= ~UBT_FLAG_READY; + UBT_UNLOCK(sc); + + /* make sure that all USB transfers are stopped! */ + for (i = 0; i != UBT_N_TRANSFER; i++) + usb2_transfer_drain(sc->sc_xfer[i]); /* Destroy Netgraph node */ if (node != NULL) { sc->sc_node = NULL; - NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_REALLY_DIE(node); - NG_NODE_REF(node); ng_rmnode_self(node); - } + /* Need to wait until Netgraph has shutdown the node! */ + UBT_LOCK(sc); + while (!(sc->sc_flags & UBT_FLAG_SHUTDOWN)) + usb2_pause_mtx(&sc->sc_mtx, 100); + UBT_UNLOCK(sc); + + /* Check if there is a leftover hook */ + if (sc->sc_hook != NULL) + NG_NODE_UNREF(node); + } /* Free USB transfers, if any */ usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); @@ -630,15 +610,13 @@ NG_NODE_UNREF(node); /* Destroy queues */ - UBT_MBUFQ_LOCK(sc); + UBT_LOCK(sc); NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); - UBT_MBUFQ_UNLOCK(sc); + UBT_UNLOCK(sc); - mtx_destroy(&sc->sc_if_mtx[0]); - mtx_destroy(&sc->sc_if_mtx[1]); - mtx_destroy(&sc->sc_mbufq_mtx); + mtx_destroy(&sc->sc_mtx); return (0); } /* ubt_detach */ @@ -657,37 +635,25 @@ 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: - if (xfer->error != 0) - UBT_STAT_OERROR(sc); - else { - UBT_INFO(sc, "sent %d bytes to control pipe\n", - xfer->actlen); + UBT_INFO(sc, "sent %d bytes to control pipe\n", + xfer->actlen); + + UBT_STAT_BYTES_SENT(sc, xfer->actlen); + UBT_STAT_PCKTS_SENT(sc); - UBT_STAT_BYTES_SENT(sc, xfer->actlen); - UBT_STAT_PCKTS_SENT(sc); - } /* FALLTHROUGH */ case USB_ST_SETUP: 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) { UBT_INFO(sc, "HCI command queue is empty\n"); - NG_NODE_UNREF(node); - return; + break; } /* Initialize a USB control request and then schedule it */ @@ -719,8 +685,6 @@ UBT_STAT_OERROR(sc); goto send_next; } - - NG_NODE_UNREF(node); /* cancelled */ break; } } /* ubt_ctrl_write_callback */ @@ -740,19 +704,8 @@ 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)) { @@ -836,8 +789,7 @@ /* Try to clear stall first */ sc->sc_flags |= UBT_FLAG_INTR_STALL; usb2_transfer_start(sc->sc_xfer[UBT_IF_0_INTR_CS_RD]); - } else - NG_NODE_UNREF(node); /* cancelled */ + } break; } } /* ubt_intr_read_callback */ @@ -855,11 +807,6 @@ 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]; @@ -867,8 +814,7 @@ 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 */ /* @@ -887,19 +833,7 @@ uint16_t len; 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)) { @@ -983,8 +917,7 @@ /* 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 */ @@ -1002,11 +935,6 @@ 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_RD]; @@ -1014,8 +942,7 @@ 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 */ /* @@ -1031,36 +958,24 @@ 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: - if (xfer->error != 0) - UBT_STAT_OERROR(sc); - else { - UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", - xfer->actlen); + UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", + xfer->actlen); + + UBT_STAT_BYTES_SENT(sc, xfer->actlen); + UBT_STAT_PCKTS_SENT(sc); - UBT_STAT_BYTES_SENT(sc, xfer->actlen); - UBT_STAT_PCKTS_SENT(sc); - } /* 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) { UBT_INFO(sc, "ACL data queue is empty\n"); - NG_NODE_UNREF(node); - return; /* transfer completed */ + break; } /* @@ -1089,8 +1004,7 @@ /* try to clear stall first */ sc->sc_flags |= UBT_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_CS_WR]); - } else - NG_NODE_UNREF(node); /* cancelled */ + } break; } } /* ubt_bulk_write_callback */ @@ -1108,11 +1022,6 @@ 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]; @@ -1120,8 +1029,7 @@ 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 */ /* @@ -1137,19 +1045,8 @@ 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: for (n = 0; n < xfer->nframes; n ++) @@ -1171,8 +1068,6 @@ goto read_next; /* NOT REACHED */ } - - NG_NODE_UNREF(node); /* cancelled */ break; } } /* ubt_isoc_read_callback */ @@ -1277,24 +1172,16 @@ 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: - if (xfer->error) - UBT_STAT_OERROR(sc); - else { - UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", - xfer->actlen); + 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); - UBT_STAT_BYTES_SENT(sc, xfer->actlen); - UBT_STAT_PCKTS_SENT(sc); - } /* FALLTHROUGH */ case USB_ST_SETUP: @@ -1305,10 +1192,7 @@ 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; } @@ -1328,9 +1212,7 @@ /* 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); } /* @@ -1355,174 +1237,12 @@ goto send_next; /* NOT REACHED */ } - - NG_NODE_UNREF(node); /* 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, int 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_task(void *context, int pending) -{ - node_p node = context; - ubt_softc_p sc; - int task_flags; - - if (NG_NODE_NOT_VALID(node)) { - NG_NODE_UNREF(node); - return; /* netgraph node is gone */ - } - - 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]); - } - - /* 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]); - } - - /* 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]); - } - - /* 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]); - } - - NG_NODE_UNREF(node); -} /* ubt_task */ - -/* - * Start USB transfer. - * Helper function called from ubt_task. Must be called with appropriate - * interface lock held. - * Taskqueue context. - */ - -static void -ubt_xfer_start(ubt_softc_p sc, int transfer) -{ - if (!usb2_transfer_pending(sc->sc_xfer[transfer])) { - NG_NODE_REF(sc->sc_node); - usb2_transfer_start(sc->sc_xfer[transfer]); - } -} /* ubt_xfer_start */ - -/**************************************************************************** - **************************************************************************** ** Netgraph specific **************************************************************************** ****************************************************************************/ @@ -1546,13 +1266,18 @@ static int ng_ubt_shutdown(node_p node) { + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + if (node->nd_flags & NGF_REALLY_DIE) { /* * We came here because the USB device is being * detached, so stop being persistant. */ + UBT_LOCK(sc); + sc->sc_flags |= UBT_FLAG_SHUTDOWN; + UBT_UNLOCK(sc); + NG_NODE_SET_PRIVATE(node, NULL); - NG_NODE_UNREF(node); } else NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ @@ -1592,9 +1317,21 @@ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); - UBT_MBUFQ_LOCK(sc); - ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); - UBT_MBUFQ_UNLOCK(sc); + if (!(sc->sc_flags & UBT_FLAG_READY)) { + /* called too early */ + return (EINVAL); + } + + NG_NODE_REF(sc->sc_node); + + UBT_LOCK(sc); + usb2_transfer_start(sc->sc_xfer[UBT_IF_0_INTR_DT_RD]); + usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_DT_RD]); + usb2_transfer_start(sc->sc_xfer[UBT_IF_1_ISOC_DT_RD1]); + usb2_transfer_start(sc->sc_xfer[UBT_IF_1_ISOC_DT_RD2]); + usb2_transfer_start(sc->sc_xfer[UBT_IF_1_ISOC_DT_WR1]); + usb2_transfer_start(sc->sc_xfer[UBT_IF_1_ISOC_DT_WR2]); + UBT_UNLOCK(sc); return (0); } /* ng_ubt_connect */ @@ -1609,6 +1346,7 @@ { node_p node = NG_HOOK_NODE(hook); struct ubt_softc *sc; + uint8_t i; if (NG_NODE_NOT_VALID(node)) return (0); @@ -1618,19 +1356,25 @@ if (hook != sc->sc_hook) return (EINVAL); + /* + * Synchronously drain all USB transfers: + * Can take some milliseconds! + */ + for (i = 0; i != UBT_N_TRANSFER; i++) + usb2_transfer_drain(sc->sc_xfer[i]); + sc->sc_hook = NULL; - UBT_MBUFQ_LOCK(sc); + UBT_LOCK(sc); /* Drain queues */ NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); - /* Kick off task to stop all USB xfers */ - ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); + UBT_UNLOCK(sc); - UBT_MBUFQ_UNLOCK(sc); + NG_NODE_UNREF(node); return (0); } /* ng_ubt_disconnect */ @@ -1664,7 +1408,6 @@ "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" \ @@ -1672,7 +1415,6 @@ 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, @@ -1823,7 +1565,8 @@ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); struct mbuf *m; struct ng_bt_mbufq *q; - int action, error = 0; + int error = 0; + uint8_t xfer_action; if (hook != sc->sc_hook) { error = EINVAL; @@ -1853,7 +1596,7 @@ UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); q = &sc->sc_cmdq; - action = UBT_FLAG_T_START_CTRL; + xfer_action = UBT_IF_0_CTRL_DT_WR; break; case NG_HCI_ACL_DATA_PKT: @@ -1863,12 +1606,12 @@ UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); q = &sc->sc_aclq; - action = UBT_FLAG_T_START_BULK; + xfer_action = UBT_IF_0_BULK_DT_WR; break; case NG_HCI_SCO_DATA_PKT: q = &sc->sc_scoq; - action = 0; + xfer_action = 255; break; default: @@ -1881,10 +1624,10 @@ /* NOT REACHED */ } - UBT_MBUFQ_LOCK(sc); + UBT_LOCK(sc); if (NG_BT_MBUFQ_FULL(q)) { NG_BT_MBUFQ_DROP(q); - UBT_MBUFQ_UNLOCK(sc); + UBT_UNLOCK(sc); UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", *mtod(m, uint8_t *), m->m_pkthdr.len); @@ -1894,9 +1637,9 @@ /* 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); - - UBT_MBUFQ_UNLOCK(sc); + if (xfer_action != 255) + usb2_transfer_start(sc->sc_xfer[xfer_action]); + UBT_UNLOCK(sc); } done: NG_FREE_ITEM(item); @@ -1962,4 +1705,3 @@ 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); - diff -u ng_ubt2_var.h ng_ubt2_var.h --- ng_ubt2_var.h Wed Jan 21 17:51:04 2009 +++ ng_ubt2_var.h Fri Jan 23 20:08:04 2009 @@ -28,7 +28,7 @@ * SUCH DAMAGE. * * $Id: ng_ubt_var.h,v 1.2 2003/03/22 23:44:36 max Exp $ - * $FreeBSD$ + * $FreeBSD: src/sys/dev/usb2/bluetooth/ng_ubt2_var.h,v 1.2 2009/01/20 22:17:05 emax Exp $ */ #ifndef _NG_UBT_VAR_H_ @@ -47,8 +47,8 @@ #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) +#define UBT_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define UBT_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) /* Bluetooth USB control request type */ #define UBT_HCI_REQUEST 0x20 @@ -65,17 +65,14 @@ 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_RD1, UBT_IF_1_ISOC_DT_RD2, UBT_IF_1_ISOC_DT_WR1, UBT_IF_1_ISOC_DT_WR2, UBT_N_TRANSFER, /* total number of transfers */ - - UBT_IF_1_N_TRANSFER = UBT_N_TRANSFER - UBT_IF_1_ISOC_DT_RD1, }; /* USB device softc structure */ @@ -89,6 +86,8 @@ #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 */ +#define UBT_FLAG_READY (1 << 4) /* set when we are ready */ +#define UBT_FLAG_SHUTDOWN (1 << 5) /* set when we are shutdown */ ng_ubt_node_stat_ep sc_stat; /* statistic */ #define UBT_STAT_PCKTS_SENT(sc) (sc)->sc_stat.pckts_sent ++ @@ -100,11 +99,9 @@ #define UBT_STAT_RESET(sc) bzero(&(sc)->sc_stat, sizeof((sc)->sc_stat)) /* USB device specific */ - struct mtx sc_if_mtx[2]; /* interface locks */ + struct mtx sc_mtx; struct usb2_xfer *sc_xfer[UBT_N_TRANSFER]; - struct mtx sc_mbufq_mtx; /* lock for all queues */ - /* HCI commands */ struct ng_bt_mbufq sc_cmdq; /* HCI command queue */ #define UBT_CTRL_BUFFER_SIZE (sizeof(struct usb2_device_request) + \ @@ -123,17 +120,6 @@ /* 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;