13671d9d8SAndrew Thompson /* 23671d9d8SAndrew Thompson * ng_ubt.c 33671d9d8SAndrew Thompson */ 43671d9d8SAndrew Thompson 53671d9d8SAndrew Thompson /*- 63671d9d8SAndrew Thompson * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> 73671d9d8SAndrew Thompson * All rights reserved. 83671d9d8SAndrew Thompson * 93671d9d8SAndrew Thompson * Redistribution and use in source and binary forms, with or without 103671d9d8SAndrew Thompson * modification, are permitted provided that the following conditions 113671d9d8SAndrew Thompson * are met: 123671d9d8SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 133671d9d8SAndrew Thompson * notice, this list of conditions and the following disclaimer. 143671d9d8SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 153671d9d8SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 163671d9d8SAndrew Thompson * documentation and/or other materials provided with the distribution. 173671d9d8SAndrew Thompson * 183671d9d8SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 193671d9d8SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 203671d9d8SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 213671d9d8SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 223671d9d8SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 233671d9d8SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 243671d9d8SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 253671d9d8SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 263671d9d8SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 273671d9d8SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 283671d9d8SAndrew Thompson * SUCH DAMAGE. 293671d9d8SAndrew Thompson * 303671d9d8SAndrew Thompson * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $ 313671d9d8SAndrew Thompson * $FreeBSD$ 323671d9d8SAndrew Thompson */ 333671d9d8SAndrew Thompson 343671d9d8SAndrew Thompson /* 353671d9d8SAndrew Thompson * NOTE: ng_ubt2 driver has a split personality. On one side it is 363671d9d8SAndrew Thompson * a USB device driver and on the other it is a Netgraph node. This 373671d9d8SAndrew Thompson * driver will *NOT* create traditional /dev/ enties, only Netgraph 383671d9d8SAndrew Thompson * node. 393671d9d8SAndrew Thompson * 403671d9d8SAndrew Thompson * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes) 413671d9d8SAndrew Thompson * 423671d9d8SAndrew Thompson * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used 433671d9d8SAndrew Thompson * by USB for any USB request going over device's interface #0 and #1, 443671d9d8SAndrew Thompson * i.e. interrupt, control, bulk and isoc. transfers. 453671d9d8SAndrew Thompson * 463671d9d8SAndrew Thompson * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph 473671d9d8SAndrew Thompson * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook 483671d9d8SAndrew Thompson * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact, 493671d9d8SAndrew Thompson * think of it as a spin lock. 503671d9d8SAndrew Thompson * 513671d9d8SAndrew Thompson * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts. 523671d9d8SAndrew Thompson * 533671d9d8SAndrew Thompson * 1) USB context. This is where all the USB related stuff happens. All 543671d9d8SAndrew Thompson * callbacks run in this context. All callbacks are called (by USB) with 553671d9d8SAndrew Thompson * appropriate interface lock held. It is (generally) allowed to grab 563671d9d8SAndrew Thompson * any additional locks. 573671d9d8SAndrew Thompson * 583671d9d8SAndrew Thompson * 2) Netgraph context. This is where all the Netgraph related stuff happens. 593671d9d8SAndrew Thompson * Since we mark node as WRITER, the Netgraph node will be "locked" (from 603671d9d8SAndrew Thompson * Netgraph point of view). Any variable that is only modified from the 613671d9d8SAndrew Thompson * Netgraph context does not require any additonal locking. It is generally 623671d9d8SAndrew Thompson * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT* 633671d9d8SAndrew Thompson * grab any lock in the Netgraph context that could cause de-scheduling of 643671d9d8SAndrew Thompson * the Netgraph thread for significant amount of time. In fact, the only 653671d9d8SAndrew Thompson * lock that is allowed in the Netgraph context is the sc_ng_mtx lock. 663671d9d8SAndrew Thompson * Also make sure that any code that is called from the Netgraph context 673671d9d8SAndrew Thompson * follows the rule above. 683671d9d8SAndrew Thompson * 693671d9d8SAndrew Thompson * 3) Taskqueue context. This is where ubt_task runs. Since we are generally 703671d9d8SAndrew Thompson * NOT allowed to grab any lock that could cause de-scheduling in the 713671d9d8SAndrew Thompson * Netgraph context, and, USB requires us to grab interface lock before 723671d9d8SAndrew Thompson * doing things with transfers, it is safer to transition from the Netgraph 733671d9d8SAndrew Thompson * context to the Taskqueue context before we can call into USB subsystem. 743671d9d8SAndrew Thompson * 753671d9d8SAndrew Thompson * So, to put everything together, the rules are as follows. 763671d9d8SAndrew Thompson * It is OK to call from the USB context or the Taskqueue context into 773671d9d8SAndrew Thompson * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words 783671d9d8SAndrew Thompson * it is allowed to call into the Netgraph context with locks held. 793671d9d8SAndrew Thompson * Is it *NOT* OK to call from the Netgraph context into the USB context, 803671d9d8SAndrew Thompson * because USB requires us to grab interface locks, and, it is safer to 813671d9d8SAndrew Thompson * avoid it. So, to make things safer we set task flags to indicate which 823671d9d8SAndrew Thompson * actions we want to perform and schedule ubt_task which would run in the 833671d9d8SAndrew Thompson * Taskqueue context. 843671d9d8SAndrew Thompson * Is is OK to call from the Taskqueue context into the USB context, 853671d9d8SAndrew Thompson * and, ubt_task does just that (i.e. grabs appropriate interface locks 863671d9d8SAndrew Thompson * before calling into USB). 873671d9d8SAndrew Thompson * Access to the outgoing queues, task flags and hook pointer is 883671d9d8SAndrew Thompson * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again, 893671d9d8SAndrew Thompson * sc_ng_mtx should really be a spin lock (and it is very likely to an 903671d9d8SAndrew Thompson * equivalent of spin lock due to adaptive nature of FreeBSD mutexes). 913671d9d8SAndrew Thompson * All USB callbacks accept softc pointer as a private data. USB ensures 923671d9d8SAndrew Thompson * that this pointer is valid. 933671d9d8SAndrew Thompson */ 943671d9d8SAndrew Thompson 95ed6d949aSAndrew Thompson #include <sys/stdint.h> 96ed6d949aSAndrew Thompson #include <sys/stddef.h> 97ed6d949aSAndrew Thompson #include <sys/param.h> 98ed6d949aSAndrew Thompson #include <sys/queue.h> 99ed6d949aSAndrew Thompson #include <sys/types.h> 100ed6d949aSAndrew Thompson #include <sys/systm.h> 101ed6d949aSAndrew Thompson #include <sys/kernel.h> 102ed6d949aSAndrew Thompson #include <sys/bus.h> 103ed6d949aSAndrew Thompson #include <sys/linker_set.h> 104ed6d949aSAndrew Thompson #include <sys/module.h> 105ed6d949aSAndrew Thompson #include <sys/lock.h> 106ed6d949aSAndrew Thompson #include <sys/mutex.h> 107ed6d949aSAndrew Thompson #include <sys/condvar.h> 108ed6d949aSAndrew Thompson #include <sys/sysctl.h> 109ed6d949aSAndrew Thompson #include <sys/sx.h> 110ed6d949aSAndrew Thompson #include <sys/unistd.h> 111ed6d949aSAndrew Thompson #include <sys/callout.h> 112ed6d949aSAndrew Thompson #include <sys/malloc.h> 113ed6d949aSAndrew Thompson #include <sys/priv.h> 114ed6d949aSAndrew Thompson 1153671d9d8SAndrew Thompson #include "usbdevs.h" 1163671d9d8SAndrew Thompson #include <dev/usb/usb.h> 117ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 118ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 1193671d9d8SAndrew Thompson 120a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 1213671d9d8SAndrew Thompson #include <dev/usb/usb_debug.h> 1223671d9d8SAndrew Thompson #include <dev/usb/usb_busdma.h> 1233671d9d8SAndrew Thompson 1243671d9d8SAndrew Thompson #include <sys/mbuf.h> 1253671d9d8SAndrew Thompson #include <sys/taskqueue.h> 1263671d9d8SAndrew Thompson 1273671d9d8SAndrew Thompson #include <netgraph/ng_message.h> 1283671d9d8SAndrew Thompson #include <netgraph/netgraph.h> 1293671d9d8SAndrew Thompson #include <netgraph/ng_parse.h> 1303671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h> 1313671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_hci.h> 1323671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_ubt.h> 13306079f14SAndrew Thompson #include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h> 1343671d9d8SAndrew Thompson 1353671d9d8SAndrew Thompson static int ubt_modevent(module_t, int, void *); 1363671d9d8SAndrew Thompson static device_probe_t ubt_probe; 1373671d9d8SAndrew Thompson static device_attach_t ubt_attach; 1383671d9d8SAndrew Thompson static device_detach_t ubt_detach; 1393671d9d8SAndrew Thompson 1403671d9d8SAndrew Thompson static void ubt_task_schedule(ubt_softc_p, int); 1413671d9d8SAndrew Thompson static task_fn_t ubt_task; 1423671d9d8SAndrew Thompson 143a593f6b8SAndrew Thompson #define ubt_xfer_start(sc, i) usbd_transfer_start((sc)->sc_xfer[(i)]) 1443671d9d8SAndrew Thompson 1453671d9d8SAndrew Thompson /* Netgraph methods */ 1463671d9d8SAndrew Thompson static ng_constructor_t ng_ubt_constructor; 1473671d9d8SAndrew Thompson static ng_shutdown_t ng_ubt_shutdown; 1483671d9d8SAndrew Thompson static ng_newhook_t ng_ubt_newhook; 1493671d9d8SAndrew Thompson static ng_connect_t ng_ubt_connect; 1503671d9d8SAndrew Thompson static ng_disconnect_t ng_ubt_disconnect; 1513671d9d8SAndrew Thompson static ng_rcvmsg_t ng_ubt_rcvmsg; 1523671d9d8SAndrew Thompson static ng_rcvdata_t ng_ubt_rcvdata; 1533671d9d8SAndrew Thompson 1543671d9d8SAndrew Thompson /* Queue length */ 1553671d9d8SAndrew Thompson static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = 1563671d9d8SAndrew Thompson { 1573671d9d8SAndrew Thompson { "queue", &ng_parse_int32_type, }, 1583671d9d8SAndrew Thompson { "qlen", &ng_parse_int32_type, }, 1593671d9d8SAndrew Thompson { NULL, } 1603671d9d8SAndrew Thompson }; 1613671d9d8SAndrew Thompson static const struct ng_parse_type ng_ubt_node_qlen_type = 1623671d9d8SAndrew Thompson { 1633671d9d8SAndrew Thompson &ng_parse_struct_type, 1643671d9d8SAndrew Thompson &ng_ubt_node_qlen_type_fields 1653671d9d8SAndrew Thompson }; 1663671d9d8SAndrew Thompson 1673671d9d8SAndrew Thompson /* Stat info */ 1683671d9d8SAndrew Thompson static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = 1693671d9d8SAndrew Thompson { 1703671d9d8SAndrew Thompson { "pckts_recv", &ng_parse_uint32_type, }, 1713671d9d8SAndrew Thompson { "bytes_recv", &ng_parse_uint32_type, }, 1723671d9d8SAndrew Thompson { "pckts_sent", &ng_parse_uint32_type, }, 1733671d9d8SAndrew Thompson { "bytes_sent", &ng_parse_uint32_type, }, 1743671d9d8SAndrew Thompson { "oerrors", &ng_parse_uint32_type, }, 1753671d9d8SAndrew Thompson { "ierrors", &ng_parse_uint32_type, }, 1763671d9d8SAndrew Thompson { NULL, } 1773671d9d8SAndrew Thompson }; 1783671d9d8SAndrew Thompson static const struct ng_parse_type ng_ubt_node_stat_type = 1793671d9d8SAndrew Thompson { 1803671d9d8SAndrew Thompson &ng_parse_struct_type, 1813671d9d8SAndrew Thompson &ng_ubt_node_stat_type_fields 1823671d9d8SAndrew Thompson }; 1833671d9d8SAndrew Thompson 1843671d9d8SAndrew Thompson /* Netgraph node command list */ 1853671d9d8SAndrew Thompson static const struct ng_cmdlist ng_ubt_cmdlist[] = 1863671d9d8SAndrew Thompson { 1873671d9d8SAndrew Thompson { 1883671d9d8SAndrew Thompson NGM_UBT_COOKIE, 1893671d9d8SAndrew Thompson NGM_UBT_NODE_SET_DEBUG, 1903671d9d8SAndrew Thompson "set_debug", 1913671d9d8SAndrew Thompson &ng_parse_uint16_type, 1923671d9d8SAndrew Thompson NULL 1933671d9d8SAndrew Thompson }, 1943671d9d8SAndrew Thompson { 1953671d9d8SAndrew Thompson NGM_UBT_COOKIE, 1963671d9d8SAndrew Thompson NGM_UBT_NODE_GET_DEBUG, 1973671d9d8SAndrew Thompson "get_debug", 1983671d9d8SAndrew Thompson NULL, 1993671d9d8SAndrew Thompson &ng_parse_uint16_type 2003671d9d8SAndrew Thompson }, 2013671d9d8SAndrew Thompson { 2023671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2033671d9d8SAndrew Thompson NGM_UBT_NODE_SET_QLEN, 2043671d9d8SAndrew Thompson "set_qlen", 2053671d9d8SAndrew Thompson &ng_ubt_node_qlen_type, 2063671d9d8SAndrew Thompson NULL 2073671d9d8SAndrew Thompson }, 2083671d9d8SAndrew Thompson { 2093671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2103671d9d8SAndrew Thompson NGM_UBT_NODE_GET_QLEN, 2113671d9d8SAndrew Thompson "get_qlen", 2123671d9d8SAndrew Thompson &ng_ubt_node_qlen_type, 2133671d9d8SAndrew Thompson &ng_ubt_node_qlen_type 2143671d9d8SAndrew Thompson }, 2153671d9d8SAndrew Thompson { 2163671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2173671d9d8SAndrew Thompson NGM_UBT_NODE_GET_STAT, 2183671d9d8SAndrew Thompson "get_stat", 2193671d9d8SAndrew Thompson NULL, 2203671d9d8SAndrew Thompson &ng_ubt_node_stat_type 2213671d9d8SAndrew Thompson }, 2223671d9d8SAndrew Thompson { 2233671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2243671d9d8SAndrew Thompson NGM_UBT_NODE_RESET_STAT, 2253671d9d8SAndrew Thompson "reset_stat", 2263671d9d8SAndrew Thompson NULL, 2273671d9d8SAndrew Thompson NULL 2283671d9d8SAndrew Thompson }, 2293671d9d8SAndrew Thompson { 0, } 2303671d9d8SAndrew Thompson }; 2313671d9d8SAndrew Thompson 2323671d9d8SAndrew Thompson /* Netgraph node type */ 2333671d9d8SAndrew Thompson static struct ng_type typestruct = 2343671d9d8SAndrew Thompson { 2353671d9d8SAndrew Thompson .version = NG_ABI_VERSION, 2363671d9d8SAndrew Thompson .name = NG_UBT_NODE_TYPE, 2373671d9d8SAndrew Thompson .constructor = ng_ubt_constructor, 2383671d9d8SAndrew Thompson .rcvmsg = ng_ubt_rcvmsg, 2393671d9d8SAndrew Thompson .shutdown = ng_ubt_shutdown, 2403671d9d8SAndrew Thompson .newhook = ng_ubt_newhook, 2413671d9d8SAndrew Thompson .connect = ng_ubt_connect, 2423671d9d8SAndrew Thompson .rcvdata = ng_ubt_rcvdata, 2433671d9d8SAndrew Thompson .disconnect = ng_ubt_disconnect, 2443671d9d8SAndrew Thompson .cmdlist = ng_ubt_cmdlist 2453671d9d8SAndrew Thompson }; 2463671d9d8SAndrew Thompson 2473671d9d8SAndrew Thompson /**************************************************************************** 2483671d9d8SAndrew Thompson **************************************************************************** 2493671d9d8SAndrew Thompson ** USB specific 2503671d9d8SAndrew Thompson **************************************************************************** 2513671d9d8SAndrew Thompson ****************************************************************************/ 2523671d9d8SAndrew Thompson 2533671d9d8SAndrew Thompson /* USB methods */ 254e0a69b51SAndrew Thompson static usb_callback_t ubt_ctrl_write_callback; 255e0a69b51SAndrew Thompson static usb_callback_t ubt_intr_read_callback; 256e0a69b51SAndrew Thompson static usb_callback_t ubt_bulk_read_callback; 257e0a69b51SAndrew Thompson static usb_callback_t ubt_bulk_write_callback; 258e0a69b51SAndrew Thompson static usb_callback_t ubt_isoc_read_callback; 259e0a69b51SAndrew Thompson static usb_callback_t ubt_isoc_write_callback; 2603671d9d8SAndrew Thompson 2613671d9d8SAndrew Thompson static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **); 262760bc48eSAndrew Thompson static int ubt_isoc_read_one_frame(struct usb_xfer *, int); 2633671d9d8SAndrew Thompson 2643671d9d8SAndrew Thompson /* 2653671d9d8SAndrew Thompson * USB config 2663671d9d8SAndrew Thompson * 2673671d9d8SAndrew Thompson * The following desribes usb transfers that could be submitted on USB device. 2683671d9d8SAndrew Thompson * 2693671d9d8SAndrew Thompson * Interface 0 on the USB device must present the following endpoints 2703671d9d8SAndrew Thompson * 1) Interrupt endpoint to receive HCI events 2713671d9d8SAndrew Thompson * 2) Bulk IN endpoint to receive ACL data 2723671d9d8SAndrew Thompson * 3) Bulk OUT endpoint to send ACL data 2733671d9d8SAndrew Thompson * 2743671d9d8SAndrew Thompson * Interface 1 on the USB device must present the following endpoints 2753671d9d8SAndrew Thompson * 1) Isochronous IN endpoint to receive SCO data 2763671d9d8SAndrew Thompson * 2) Isochronous OUT endpoint to send SCO data 2773671d9d8SAndrew Thompson */ 2783671d9d8SAndrew Thompson 279760bc48eSAndrew Thompson static const struct usb_config ubt_config[UBT_N_TRANSFER] = 2803671d9d8SAndrew Thompson { 2813671d9d8SAndrew Thompson /* 2823671d9d8SAndrew Thompson * Interface #0 2833671d9d8SAndrew Thompson */ 2843671d9d8SAndrew Thompson 2853671d9d8SAndrew Thompson /* Outgoing bulk transfer - ACL packets */ 2863671d9d8SAndrew Thompson [UBT_IF_0_BULK_DT_WR] = { 2873671d9d8SAndrew Thompson .type = UE_BULK, 2883671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 2893671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 2903671d9d8SAndrew Thompson .if_index = 0, 2913671d9d8SAndrew Thompson .bufsize = UBT_BULK_WRITE_BUFFER_SIZE, 2923671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .force_short_xfer = 1, }, 2933671d9d8SAndrew Thompson .callback = &ubt_bulk_write_callback, 2943671d9d8SAndrew Thompson }, 2953671d9d8SAndrew Thompson /* Incoming bulk transfer - ACL packets */ 2963671d9d8SAndrew Thompson [UBT_IF_0_BULK_DT_RD] = { 2973671d9d8SAndrew Thompson .type = UE_BULK, 2983671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 2993671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3003671d9d8SAndrew Thompson .if_index = 0, 3013671d9d8SAndrew Thompson .bufsize = UBT_BULK_READ_BUFFER_SIZE, 3023671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 3033671d9d8SAndrew Thompson .callback = &ubt_bulk_read_callback, 3043671d9d8SAndrew Thompson }, 3053671d9d8SAndrew Thompson /* Incoming interrupt transfer - HCI events */ 3063671d9d8SAndrew Thompson [UBT_IF_0_INTR_DT_RD] = { 3073671d9d8SAndrew Thompson .type = UE_INTERRUPT, 3083671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3093671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3103671d9d8SAndrew Thompson .if_index = 0, 3113671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 3123671d9d8SAndrew Thompson .bufsize = UBT_INTR_BUFFER_SIZE, 3133671d9d8SAndrew Thompson .callback = &ubt_intr_read_callback, 3143671d9d8SAndrew Thompson }, 3153671d9d8SAndrew Thompson /* Outgoing control transfer - HCI commands */ 3163671d9d8SAndrew Thompson [UBT_IF_0_CTRL_DT_WR] = { 3173671d9d8SAndrew Thompson .type = UE_CONTROL, 3183671d9d8SAndrew Thompson .endpoint = 0x00, /* control pipe */ 3193671d9d8SAndrew Thompson .direction = UE_DIR_ANY, 3203671d9d8SAndrew Thompson .if_index = 0, 3213671d9d8SAndrew Thompson .bufsize = UBT_CTRL_BUFFER_SIZE, 3223671d9d8SAndrew Thompson .callback = &ubt_ctrl_write_callback, 3233671d9d8SAndrew Thompson .timeout = 5000, /* 5 seconds */ 3243671d9d8SAndrew Thompson }, 3253671d9d8SAndrew Thompson 3263671d9d8SAndrew Thompson /* 3273671d9d8SAndrew Thompson * Interface #1 3283671d9d8SAndrew Thompson */ 3293671d9d8SAndrew Thompson 3303671d9d8SAndrew Thompson /* Incoming isochronous transfer #1 - SCO packets */ 3313671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_RD1] = { 3323671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3333671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3343671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3353671d9d8SAndrew Thompson .if_index = 1, 3363671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3373671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3383671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3393671d9d8SAndrew Thompson .callback = &ubt_isoc_read_callback, 3403671d9d8SAndrew Thompson }, 3413671d9d8SAndrew Thompson /* Incoming isochronous transfer #2 - SCO packets */ 3423671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_RD2] = { 3433671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3443671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3453671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3463671d9d8SAndrew Thompson .if_index = 1, 3473671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3483671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3493671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3503671d9d8SAndrew Thompson .callback = &ubt_isoc_read_callback, 3513671d9d8SAndrew Thompson }, 3523671d9d8SAndrew Thompson /* Outgoing isochronous transfer #1 - SCO packets */ 3533671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_WR1] = { 3543671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3553671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3563671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 3573671d9d8SAndrew Thompson .if_index = 1, 3583671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3593671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3603671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3613671d9d8SAndrew Thompson .callback = &ubt_isoc_write_callback, 3623671d9d8SAndrew Thompson }, 3633671d9d8SAndrew Thompson /* Outgoing isochronous transfer #2 - SCO packets */ 3643671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_WR2] = { 3653671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3663671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3673671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 3683671d9d8SAndrew Thompson .if_index = 1, 3693671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3703671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3713671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3723671d9d8SAndrew Thompson .callback = &ubt_isoc_write_callback, 3733671d9d8SAndrew Thompson }, 3743671d9d8SAndrew Thompson }; 3753671d9d8SAndrew Thompson 3763671d9d8SAndrew Thompson /* 3773671d9d8SAndrew Thompson * If for some reason device should not be attached then put 3783671d9d8SAndrew Thompson * VendorID/ProductID pair into the list below. The format is 3793671d9d8SAndrew Thompson * as follows: 3803671d9d8SAndrew Thompson * 3813671d9d8SAndrew Thompson * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, 3823671d9d8SAndrew Thompson * 3833671d9d8SAndrew Thompson * where VENDOR_ID and PRODUCT_ID are hex numbers. 3843671d9d8SAndrew Thompson */ 3853671d9d8SAndrew Thompson 386760bc48eSAndrew Thompson static const struct usb_device_id ubt_ignore_devs[] = 3873671d9d8SAndrew Thompson { 3883671d9d8SAndrew Thompson /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */ 3893671d9d8SAndrew Thompson { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) }, 3903671d9d8SAndrew Thompson }; 3913671d9d8SAndrew Thompson 3923671d9d8SAndrew Thompson /* List of supported bluetooth devices */ 393760bc48eSAndrew Thompson static const struct usb_device_id ubt_devs[] = 3943671d9d8SAndrew Thompson { 3953671d9d8SAndrew Thompson /* Generic Bluetooth class devices */ 3963671d9d8SAndrew Thompson { USB_IFACE_CLASS(UDCLASS_WIRELESS), 3973671d9d8SAndrew Thompson USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 3983671d9d8SAndrew Thompson USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 3993671d9d8SAndrew Thompson 4003671d9d8SAndrew Thompson /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ 4013671d9d8SAndrew Thompson { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) }, 4023671d9d8SAndrew Thompson }; 4033671d9d8SAndrew Thompson 4043671d9d8SAndrew Thompson /* 4053671d9d8SAndrew Thompson * Probe for a USB Bluetooth device. 4063671d9d8SAndrew Thompson * USB context. 4073671d9d8SAndrew Thompson */ 4083671d9d8SAndrew Thompson 4093671d9d8SAndrew Thompson static int 4103671d9d8SAndrew Thompson ubt_probe(device_t dev) 4113671d9d8SAndrew Thompson { 412760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 4133671d9d8SAndrew Thompson 4143671d9d8SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) 4153671d9d8SAndrew Thompson return (ENXIO); 4163671d9d8SAndrew Thompson 4173671d9d8SAndrew Thompson if (uaa->info.bIfaceIndex != 0) 4183671d9d8SAndrew Thompson return (ENXIO); 4193671d9d8SAndrew Thompson 4203671d9d8SAndrew Thompson if (uaa->use_generic == 0) 4213671d9d8SAndrew Thompson return (ENXIO); 4223671d9d8SAndrew Thompson 423a593f6b8SAndrew Thompson if (usbd_lookup_id_by_uaa(ubt_ignore_devs, 4243671d9d8SAndrew Thompson sizeof(ubt_ignore_devs), uaa) == 0) 4253671d9d8SAndrew Thompson return (ENXIO); 4263671d9d8SAndrew Thompson 427a593f6b8SAndrew Thompson return (usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa)); 4283671d9d8SAndrew Thompson } /* ubt_probe */ 4293671d9d8SAndrew Thompson 4303671d9d8SAndrew Thompson /* 4313671d9d8SAndrew Thompson * Attach the device. 4323671d9d8SAndrew Thompson * USB context. 4333671d9d8SAndrew Thompson */ 4343671d9d8SAndrew Thompson 4353671d9d8SAndrew Thompson static int 4363671d9d8SAndrew Thompson ubt_attach(device_t dev) 4373671d9d8SAndrew Thompson { 438760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 4393671d9d8SAndrew Thompson struct ubt_softc *sc = device_get_softc(dev); 440760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 441760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 4423671d9d8SAndrew Thompson uint16_t wMaxPacketSize; 4433671d9d8SAndrew Thompson uint8_t alt_index, i, j; 4443671d9d8SAndrew Thompson uint8_t iface_index[2] = { 0, 1 }; 4453671d9d8SAndrew Thompson 446a593f6b8SAndrew Thompson device_set_usb_desc(dev); 4473671d9d8SAndrew Thompson 4483671d9d8SAndrew Thompson sc->sc_dev = dev; 4493671d9d8SAndrew Thompson sc->sc_debug = NG_UBT_WARN_LEVEL; 4503671d9d8SAndrew Thompson 4513671d9d8SAndrew Thompson /* 4523671d9d8SAndrew Thompson * Create Netgraph node 4533671d9d8SAndrew Thompson */ 4543671d9d8SAndrew Thompson 4553671d9d8SAndrew Thompson if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { 4563671d9d8SAndrew Thompson UBT_ALERT(sc, "could not create Netgraph node\n"); 4573671d9d8SAndrew Thompson return (ENXIO); 4583671d9d8SAndrew Thompson } 4593671d9d8SAndrew Thompson 4603671d9d8SAndrew Thompson /* Name Netgraph node */ 4613671d9d8SAndrew Thompson if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { 4623671d9d8SAndrew Thompson UBT_ALERT(sc, "could not name Netgraph node\n"); 4633671d9d8SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 4643671d9d8SAndrew Thompson return (ENXIO); 4653671d9d8SAndrew Thompson } 4663671d9d8SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 4673671d9d8SAndrew Thompson NG_NODE_FORCE_WRITER(sc->sc_node); 4683671d9d8SAndrew Thompson 4693671d9d8SAndrew Thompson /* 4703671d9d8SAndrew Thompson * Initialize device softc structure 4713671d9d8SAndrew Thompson */ 4723671d9d8SAndrew Thompson 4733671d9d8SAndrew Thompson /* initialize locks */ 4743671d9d8SAndrew Thompson mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); 4753671d9d8SAndrew Thompson mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); 4763671d9d8SAndrew Thompson 4773671d9d8SAndrew Thompson /* initialize packet queues */ 4783671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); 4793671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); 4803671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); 4813671d9d8SAndrew Thompson 4823671d9d8SAndrew Thompson /* initialize glue task */ 4833671d9d8SAndrew Thompson TASK_INIT(&sc->sc_task, 0, ubt_task, sc); 4843671d9d8SAndrew Thompson 4853671d9d8SAndrew Thompson /* 4863671d9d8SAndrew Thompson * Configure Bluetooth USB device. Discover all required USB 4873671d9d8SAndrew Thompson * interfaces and endpoints. 4883671d9d8SAndrew Thompson * 4893671d9d8SAndrew Thompson * USB device must present two interfaces: 4903671d9d8SAndrew Thompson * 1) Interface 0 that has 3 endpoints 4913671d9d8SAndrew Thompson * 1) Interrupt endpoint to receive HCI events 4923671d9d8SAndrew Thompson * 2) Bulk IN endpoint to receive ACL data 4933671d9d8SAndrew Thompson * 3) Bulk OUT endpoint to send ACL data 4943671d9d8SAndrew Thompson * 4953671d9d8SAndrew Thompson * 2) Interface 1 then has 2 endpoints 4963671d9d8SAndrew Thompson * 1) Isochronous IN endpoint to receive SCO data 4973671d9d8SAndrew Thompson * 2) Isochronous OUT endpoint to send SCO data 4983671d9d8SAndrew Thompson * 4993671d9d8SAndrew Thompson * Interface 1 (with isochronous endpoints) has several alternate 5003671d9d8SAndrew Thompson * configurations with different packet size. 5013671d9d8SAndrew Thompson */ 5023671d9d8SAndrew Thompson 5033671d9d8SAndrew Thompson /* 5043671d9d8SAndrew Thompson * For interface #1 search alternate settings, and find 5053671d9d8SAndrew Thompson * the descriptor with the largest wMaxPacketSize 5063671d9d8SAndrew Thompson */ 5073671d9d8SAndrew Thompson 5083671d9d8SAndrew Thompson wMaxPacketSize = 0; 5093671d9d8SAndrew Thompson alt_index = 0; 5103671d9d8SAndrew Thompson i = 0; 5113671d9d8SAndrew Thompson j = 0; 5123671d9d8SAndrew Thompson ed = NULL; 5133671d9d8SAndrew Thompson 5143671d9d8SAndrew Thompson /* 5153671d9d8SAndrew Thompson * Search through all the descriptors looking for the largest 5163671d9d8SAndrew Thompson * packet size: 5173671d9d8SAndrew Thompson */ 518a593f6b8SAndrew Thompson while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach( 519a593f6b8SAndrew Thompson usbd_get_config_descriptor(uaa->device), 520760bc48eSAndrew Thompson (struct usb_descriptor *)ed))) { 5213671d9d8SAndrew Thompson 5223671d9d8SAndrew Thompson if ((ed->bDescriptorType == UDESC_INTERFACE) && 5233671d9d8SAndrew Thompson (ed->bLength >= sizeof(*id))) { 524760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)ed; 5253671d9d8SAndrew Thompson i = id->bInterfaceNumber; 5263671d9d8SAndrew Thompson j = id->bAlternateSetting; 5273671d9d8SAndrew Thompson } 5283671d9d8SAndrew Thompson 5293671d9d8SAndrew Thompson if ((ed->bDescriptorType == UDESC_ENDPOINT) && 5303671d9d8SAndrew Thompson (ed->bLength >= sizeof(*ed)) && 5313671d9d8SAndrew Thompson (i == 1)) { 5323671d9d8SAndrew Thompson uint16_t temp; 5333671d9d8SAndrew Thompson 5343671d9d8SAndrew Thompson temp = UGETW(ed->wMaxPacketSize); 5353671d9d8SAndrew Thompson if (temp > wMaxPacketSize) { 5363671d9d8SAndrew Thompson wMaxPacketSize = temp; 5373671d9d8SAndrew Thompson alt_index = j; 5383671d9d8SAndrew Thompson } 5393671d9d8SAndrew Thompson } 5403671d9d8SAndrew Thompson } 5413671d9d8SAndrew Thompson 5423671d9d8SAndrew Thompson /* Set alt configuration on interface #1 only if we found it */ 5433671d9d8SAndrew Thompson if (wMaxPacketSize > 0 && 544a593f6b8SAndrew Thompson usbd_set_alt_interface_index(uaa->device, 1, alt_index)) { 5453671d9d8SAndrew Thompson UBT_ALERT(sc, "could not set alternate setting %d " \ 5463671d9d8SAndrew Thompson "for interface 1!\n", alt_index); 5473671d9d8SAndrew Thompson goto detach; 5483671d9d8SAndrew Thompson } 5493671d9d8SAndrew Thompson 5503671d9d8SAndrew Thompson /* Setup transfers for both interfaces */ 551a593f6b8SAndrew Thompson if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, 5523671d9d8SAndrew Thompson ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { 5533671d9d8SAndrew Thompson UBT_ALERT(sc, "could not allocate transfers\n"); 5543671d9d8SAndrew Thompson goto detach; 5553671d9d8SAndrew Thompson } 5563671d9d8SAndrew Thompson 5573671d9d8SAndrew Thompson /* Claim all interfaces on the device */ 558a593f6b8SAndrew Thompson for (i = 1; usbd_get_iface(uaa->device, i) != NULL; i ++) 559a593f6b8SAndrew Thompson usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 5603671d9d8SAndrew Thompson 5613671d9d8SAndrew Thompson return (0); /* success */ 5623671d9d8SAndrew Thompson 5633671d9d8SAndrew Thompson detach: 5643671d9d8SAndrew Thompson ubt_detach(dev); 5653671d9d8SAndrew Thompson 5663671d9d8SAndrew Thompson return (ENXIO); 5673671d9d8SAndrew Thompson } /* ubt_attach */ 5683671d9d8SAndrew Thompson 5693671d9d8SAndrew Thompson /* 5703671d9d8SAndrew Thompson * Detach the device. 5713671d9d8SAndrew Thompson * USB context. 5723671d9d8SAndrew Thompson */ 5733671d9d8SAndrew Thompson 5743671d9d8SAndrew Thompson int 5753671d9d8SAndrew Thompson ubt_detach(device_t dev) 5763671d9d8SAndrew Thompson { 5773671d9d8SAndrew Thompson struct ubt_softc *sc = device_get_softc(dev); 5783671d9d8SAndrew Thompson node_p node = sc->sc_node; 5793671d9d8SAndrew Thompson 5803671d9d8SAndrew Thompson /* Destroy Netgraph node */ 5813671d9d8SAndrew Thompson if (node != NULL) { 5823671d9d8SAndrew Thompson sc->sc_node = NULL; 5833671d9d8SAndrew Thompson NG_NODE_REALLY_DIE(node); 5843671d9d8SAndrew Thompson ng_rmnode_self(node); 5853671d9d8SAndrew Thompson } 5863671d9d8SAndrew Thompson 5873671d9d8SAndrew Thompson /* Make sure ubt_task in gone */ 5883671d9d8SAndrew Thompson taskqueue_drain(taskqueue_swi, &sc->sc_task); 5893671d9d8SAndrew Thompson 5903671d9d8SAndrew Thompson /* Free USB transfers, if any */ 591a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); 5923671d9d8SAndrew Thompson 5933671d9d8SAndrew Thompson /* Destroy queues */ 5943671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 5953671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); 5963671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); 5973671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); 5983671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 5993671d9d8SAndrew Thompson 6003671d9d8SAndrew Thompson mtx_destroy(&sc->sc_if_mtx); 6013671d9d8SAndrew Thompson mtx_destroy(&sc->sc_ng_mtx); 6023671d9d8SAndrew Thompson 6033671d9d8SAndrew Thompson return (0); 6043671d9d8SAndrew Thompson } /* ubt_detach */ 6053671d9d8SAndrew Thompson 6063671d9d8SAndrew Thompson /* 6073671d9d8SAndrew Thompson * Called when outgoing control request (HCI command) has completed, i.e. 6083671d9d8SAndrew Thompson * HCI command was sent to the device. 6093671d9d8SAndrew Thompson * USB context. 6103671d9d8SAndrew Thompson */ 6113671d9d8SAndrew Thompson 6123671d9d8SAndrew Thompson static void 613ed6d949aSAndrew Thompson ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 6143671d9d8SAndrew Thompson { 615ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 616760bc48eSAndrew Thompson struct usb_device_request req; 6173671d9d8SAndrew Thompson struct mbuf *m; 618ed6d949aSAndrew Thompson struct usb_page_cache *pc; 619ed6d949aSAndrew Thompson int actlen; 620ed6d949aSAndrew Thompson 621ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 6223671d9d8SAndrew Thompson 6233671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 6243671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 625ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen); 626ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 6273671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 6283671d9d8SAndrew Thompson /* FALLTHROUGH */ 6293671d9d8SAndrew Thompson 6303671d9d8SAndrew Thompson case USB_ST_SETUP: 6313671d9d8SAndrew Thompson send_next: 6323671d9d8SAndrew Thompson /* Get next command mbuf, if any */ 6333671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 6343671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 6353671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 6363671d9d8SAndrew Thompson 6373671d9d8SAndrew Thompson if (m == NULL) { 6383671d9d8SAndrew Thompson UBT_INFO(sc, "HCI command queue is empty\n"); 6393671d9d8SAndrew Thompson break; /* transfer complete */ 6403671d9d8SAndrew Thompson } 6413671d9d8SAndrew Thompson 6423671d9d8SAndrew Thompson /* Initialize a USB control request and then schedule it */ 6433671d9d8SAndrew Thompson bzero(&req, sizeof(req)); 6443671d9d8SAndrew Thompson req.bmRequestType = UBT_HCI_REQUEST; 6453671d9d8SAndrew Thompson USETW(req.wLength, m->m_pkthdr.len); 6463671d9d8SAndrew Thompson 6473671d9d8SAndrew Thompson UBT_INFO(sc, "Sending control request, " \ 6483671d9d8SAndrew Thompson "bmRequestType=0x%02x, wLength=%d\n", 6493671d9d8SAndrew Thompson req.bmRequestType, UGETW(req.wLength)); 6503671d9d8SAndrew Thompson 651ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 652ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, &req, sizeof(req)); 653ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 1); 654ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 6553671d9d8SAndrew Thompson 656ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 657ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len); 658ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, 2); 6593671d9d8SAndrew Thompson 6603671d9d8SAndrew Thompson NG_FREE_M(m); 6613671d9d8SAndrew Thompson 662a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 6633671d9d8SAndrew Thompson break; 6643671d9d8SAndrew Thompson 6653671d9d8SAndrew Thompson default: /* Error */ 666ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 6673671d9d8SAndrew Thompson UBT_WARN(sc, "control transfer failed: %s\n", 668ed6d949aSAndrew Thompson usbd_errstr(error)); 6693671d9d8SAndrew Thompson 6703671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 6713671d9d8SAndrew Thompson goto send_next; 6723671d9d8SAndrew Thompson } 6733671d9d8SAndrew Thompson 6743671d9d8SAndrew Thompson /* transfer cancelled */ 6753671d9d8SAndrew Thompson break; 6763671d9d8SAndrew Thompson } 6773671d9d8SAndrew Thompson } /* ubt_ctrl_write_callback */ 6783671d9d8SAndrew Thompson 6793671d9d8SAndrew Thompson /* 6803671d9d8SAndrew Thompson * Called when incoming interrupt transfer (HCI event) has completed, i.e. 6813671d9d8SAndrew Thompson * HCI event was received from the device. 6823671d9d8SAndrew Thompson * USB context. 6833671d9d8SAndrew Thompson */ 6843671d9d8SAndrew Thompson 6853671d9d8SAndrew Thompson static void 686ed6d949aSAndrew Thompson ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 6873671d9d8SAndrew Thompson { 688ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 6893671d9d8SAndrew Thompson struct mbuf *m; 6903671d9d8SAndrew Thompson ng_hci_event_pkt_t *hdr; 691ed6d949aSAndrew Thompson struct usb_page_cache *pc; 692ed6d949aSAndrew Thompson int actlen; 693ed6d949aSAndrew Thompson 694ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 6953671d9d8SAndrew Thompson 6963671d9d8SAndrew Thompson m = NULL; 6973671d9d8SAndrew Thompson 6983671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 6993671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 7003671d9d8SAndrew Thompson /* Allocate a new mbuf */ 7013671d9d8SAndrew Thompson MGETHDR(m, M_DONTWAIT, MT_DATA); 7023671d9d8SAndrew Thompson if (m == NULL) { 7033671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 7043671d9d8SAndrew Thompson goto submit_next; 7053671d9d8SAndrew Thompson } 7063671d9d8SAndrew Thompson 7073671d9d8SAndrew Thompson MCLGET(m, M_DONTWAIT); 7083671d9d8SAndrew Thompson if (!(m->m_flags & M_EXT)) { 7093671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 7103671d9d8SAndrew Thompson goto submit_next; 7113671d9d8SAndrew Thompson } 7123671d9d8SAndrew Thompson 7133671d9d8SAndrew Thompson /* Add HCI packet type */ 7143671d9d8SAndrew Thompson *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; 7153671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = 1; 7163671d9d8SAndrew Thompson 717ed6d949aSAndrew Thompson if (actlen > MCLBYTES - 1) 718ed6d949aSAndrew Thompson actlen = MCLBYTES - 1; 7193671d9d8SAndrew Thompson 720ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 721ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 722ed6d949aSAndrew Thompson m->m_pkthdr.len += actlen; 723ed6d949aSAndrew Thompson m->m_len += actlen; 7243671d9d8SAndrew Thompson 7253671d9d8SAndrew Thompson UBT_INFO(sc, "got %d bytes from interrupt pipe\n", 726ed6d949aSAndrew Thompson actlen); 7273671d9d8SAndrew Thompson 7283671d9d8SAndrew Thompson /* Validate packet and send it up the stack */ 7293671d9d8SAndrew Thompson if (m->m_pkthdr.len < sizeof(*hdr)) { 7303671d9d8SAndrew Thompson UBT_INFO(sc, "HCI event packet is too short\n"); 7313671d9d8SAndrew Thompson 7323671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 7333671d9d8SAndrew Thompson goto submit_next; 7343671d9d8SAndrew Thompson } 7353671d9d8SAndrew Thompson 7363671d9d8SAndrew Thompson hdr = mtod(m, ng_hci_event_pkt_t *); 7373671d9d8SAndrew Thompson if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { 7383671d9d8SAndrew Thompson UBT_ERR(sc, "Invalid HCI event packet size, " \ 7393671d9d8SAndrew Thompson "length=%d, pktlen=%d\n", 7403671d9d8SAndrew Thompson hdr->length, m->m_pkthdr.len); 7413671d9d8SAndrew Thompson 7423671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 7433671d9d8SAndrew Thompson goto submit_next; 7443671d9d8SAndrew Thompson } 7453671d9d8SAndrew Thompson 7463671d9d8SAndrew Thompson UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ 7473671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, hdr->length); 7483671d9d8SAndrew Thompson 7493671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 7503671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 7513671d9d8SAndrew Thompson 7523671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 7533671d9d8SAndrew Thompson /* m == NULL at this point */ 7543671d9d8SAndrew Thompson /* FALLTHROUGH */ 7553671d9d8SAndrew Thompson 7563671d9d8SAndrew Thompson case USB_ST_SETUP: 7573671d9d8SAndrew Thompson submit_next: 7583671d9d8SAndrew Thompson NG_FREE_M(m); /* checks for m != NULL */ 7593671d9d8SAndrew Thompson 760ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 761a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 7623671d9d8SAndrew Thompson break; 7633671d9d8SAndrew Thompson 7643671d9d8SAndrew Thompson default: /* Error */ 765ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 7663671d9d8SAndrew Thompson UBT_WARN(sc, "interrupt transfer failed: %s\n", 767ed6d949aSAndrew Thompson usbd_errstr(error)); 7683671d9d8SAndrew Thompson 7693671d9d8SAndrew Thompson /* Try to clear stall first */ 770ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 7713671d9d8SAndrew Thompson goto submit_next; 7723671d9d8SAndrew Thompson } 7733671d9d8SAndrew Thompson /* transfer cancelled */ 7743671d9d8SAndrew Thompson break; 7753671d9d8SAndrew Thompson } 7763671d9d8SAndrew Thompson } /* ubt_intr_read_callback */ 7773671d9d8SAndrew Thompson 7783671d9d8SAndrew Thompson /* 7793671d9d8SAndrew Thompson * Called when incoming bulk transfer (ACL packet) has completed, i.e. 7803671d9d8SAndrew Thompson * ACL packet was received from the device. 7813671d9d8SAndrew Thompson * USB context. 7823671d9d8SAndrew Thompson */ 7833671d9d8SAndrew Thompson 7843671d9d8SAndrew Thompson static void 785ed6d949aSAndrew Thompson ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 7863671d9d8SAndrew Thompson { 787ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 7883671d9d8SAndrew Thompson struct mbuf *m; 7893671d9d8SAndrew Thompson ng_hci_acldata_pkt_t *hdr; 790ed6d949aSAndrew Thompson struct usb_page_cache *pc; 7913671d9d8SAndrew Thompson uint16_t len; 792ed6d949aSAndrew Thompson int actlen; 793ed6d949aSAndrew Thompson 794ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 7953671d9d8SAndrew Thompson 7963671d9d8SAndrew Thompson m = NULL; 7973671d9d8SAndrew Thompson 7983671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 7993671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 8003671d9d8SAndrew Thompson /* Allocate new mbuf */ 8013671d9d8SAndrew Thompson MGETHDR(m, M_DONTWAIT, MT_DATA); 8023671d9d8SAndrew Thompson if (m == NULL) { 8033671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 8043671d9d8SAndrew Thompson goto submit_next; 8053671d9d8SAndrew Thompson } 8063671d9d8SAndrew Thompson 8073671d9d8SAndrew Thompson MCLGET(m, M_DONTWAIT); 8083671d9d8SAndrew Thompson if (!(m->m_flags & M_EXT)) { 8093671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 8103671d9d8SAndrew Thompson goto submit_next; 8113671d9d8SAndrew Thompson } 8123671d9d8SAndrew Thompson 8133671d9d8SAndrew Thompson /* Add HCI packet type */ 8143671d9d8SAndrew Thompson *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; 8153671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = 1; 8163671d9d8SAndrew Thompson 817ed6d949aSAndrew Thompson if (actlen > MCLBYTES - 1) 818ed6d949aSAndrew Thompson actlen = MCLBYTES - 1; 8193671d9d8SAndrew Thompson 820ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 821ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 822ed6d949aSAndrew Thompson m->m_pkthdr.len += actlen; 823ed6d949aSAndrew Thompson m->m_len += actlen; 8243671d9d8SAndrew Thompson 8253671d9d8SAndrew Thompson UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", 826ed6d949aSAndrew Thompson actlen); 8273671d9d8SAndrew Thompson 8283671d9d8SAndrew Thompson /* Validate packet and send it up the stack */ 8293671d9d8SAndrew Thompson if (m->m_pkthdr.len < sizeof(*hdr)) { 8303671d9d8SAndrew Thompson UBT_INFO(sc, "HCI ACL packet is too short\n"); 8313671d9d8SAndrew Thompson 8323671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 8333671d9d8SAndrew Thompson goto submit_next; 8343671d9d8SAndrew Thompson } 8353671d9d8SAndrew Thompson 8363671d9d8SAndrew Thompson hdr = mtod(m, ng_hci_acldata_pkt_t *); 8373671d9d8SAndrew Thompson len = le16toh(hdr->length); 8383671d9d8SAndrew Thompson if (len != (m->m_pkthdr.len - sizeof(*hdr))) { 8393671d9d8SAndrew Thompson UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ 8403671d9d8SAndrew Thompson "pktlen=%d\n", len, m->m_pkthdr.len); 8413671d9d8SAndrew Thompson 8423671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 8433671d9d8SAndrew Thompson goto submit_next; 8443671d9d8SAndrew Thompson } 8453671d9d8SAndrew Thompson 8463671d9d8SAndrew Thompson UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ 8473671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, len); 8483671d9d8SAndrew Thompson 8493671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 8503671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 8513671d9d8SAndrew Thompson 8523671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 8533671d9d8SAndrew Thompson /* m == NULL at this point */ 8543671d9d8SAndrew Thompson /* FALLTHOUGH */ 8553671d9d8SAndrew Thompson 8563671d9d8SAndrew Thompson case USB_ST_SETUP: 8573671d9d8SAndrew Thompson submit_next: 8583671d9d8SAndrew Thompson NG_FREE_M(m); /* checks for m != NULL */ 8593671d9d8SAndrew Thompson 860ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 861a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 8623671d9d8SAndrew Thompson break; 8633671d9d8SAndrew Thompson 8643671d9d8SAndrew Thompson default: /* Error */ 865ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 8663671d9d8SAndrew Thompson UBT_WARN(sc, "bulk-in transfer failed: %s\n", 867ed6d949aSAndrew Thompson usbd_errstr(error)); 8683671d9d8SAndrew Thompson 8693671d9d8SAndrew Thompson /* Try to clear stall first */ 870ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 8713671d9d8SAndrew Thompson goto submit_next; 8723671d9d8SAndrew Thompson } 8733671d9d8SAndrew Thompson /* transfer cancelled */ 8743671d9d8SAndrew Thompson break; 8753671d9d8SAndrew Thompson } 8763671d9d8SAndrew Thompson } /* ubt_bulk_read_callback */ 8773671d9d8SAndrew Thompson 8783671d9d8SAndrew Thompson /* 8793671d9d8SAndrew Thompson * Called when outgoing bulk transfer (ACL packet) has completed, i.e. 8803671d9d8SAndrew Thompson * ACL packet was sent to the device. 8813671d9d8SAndrew Thompson * USB context. 8823671d9d8SAndrew Thompson */ 8833671d9d8SAndrew Thompson 8843671d9d8SAndrew Thompson static void 885ed6d949aSAndrew Thompson ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 8863671d9d8SAndrew Thompson { 887ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 8883671d9d8SAndrew Thompson struct mbuf *m; 889ed6d949aSAndrew Thompson struct usb_page_cache *pc; 890ed6d949aSAndrew Thompson int actlen; 891ed6d949aSAndrew Thompson 892ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 8933671d9d8SAndrew Thompson 8943671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 8953671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 896ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen); 897ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 8983671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 8993671d9d8SAndrew Thompson /* FALLTHROUGH */ 9003671d9d8SAndrew Thompson 9013671d9d8SAndrew Thompson case USB_ST_SETUP: 9023671d9d8SAndrew Thompson send_next: 9033671d9d8SAndrew Thompson /* Get next mbuf, if any */ 9043671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 9053671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); 9063671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 9073671d9d8SAndrew Thompson 9083671d9d8SAndrew Thompson if (m == NULL) { 9093671d9d8SAndrew Thompson UBT_INFO(sc, "ACL data queue is empty\n"); 9103671d9d8SAndrew Thompson break; /* transfer completed */ 9113671d9d8SAndrew Thompson } 9123671d9d8SAndrew Thompson 9133671d9d8SAndrew Thompson /* 9143671d9d8SAndrew Thompson * Copy ACL data frame back to a linear USB transfer buffer 9153671d9d8SAndrew Thompson * and schedule transfer 9163671d9d8SAndrew Thompson */ 9173671d9d8SAndrew Thompson 918ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 919ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 920ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 9213671d9d8SAndrew Thompson 9223671d9d8SAndrew Thompson UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", 9233671d9d8SAndrew Thompson m->m_pkthdr.len); 9243671d9d8SAndrew Thompson 9253671d9d8SAndrew Thompson NG_FREE_M(m); 9263671d9d8SAndrew Thompson 927a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 9283671d9d8SAndrew Thompson break; 9293671d9d8SAndrew Thompson 9303671d9d8SAndrew Thompson default: /* Error */ 931ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 9323671d9d8SAndrew Thompson UBT_WARN(sc, "bulk-out transfer failed: %s\n", 933ed6d949aSAndrew Thompson usbd_errstr(error)); 9343671d9d8SAndrew Thompson 9353671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 9363671d9d8SAndrew Thompson 9373671d9d8SAndrew Thompson /* try to clear stall first */ 938ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 9393671d9d8SAndrew Thompson goto send_next; 9403671d9d8SAndrew Thompson } 9413671d9d8SAndrew Thompson /* transfer cancelled */ 9423671d9d8SAndrew Thompson break; 9433671d9d8SAndrew Thompson } 9443671d9d8SAndrew Thompson } /* ubt_bulk_write_callback */ 9453671d9d8SAndrew Thompson 9463671d9d8SAndrew Thompson /* 9473671d9d8SAndrew Thompson * Called when incoming isoc transfer (SCO packet) has completed, i.e. 9483671d9d8SAndrew Thompson * SCO packet was received from the device. 9493671d9d8SAndrew Thompson * USB context. 9503671d9d8SAndrew Thompson */ 9513671d9d8SAndrew Thompson 9523671d9d8SAndrew Thompson static void 953ed6d949aSAndrew Thompson ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 9543671d9d8SAndrew Thompson { 955ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 9563671d9d8SAndrew Thompson int n; 957ed6d949aSAndrew Thompson int actlen, nframes; 958ed6d949aSAndrew Thompson 959ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 9603671d9d8SAndrew Thompson 9613671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 9623671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 963ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) 9643671d9d8SAndrew Thompson if (ubt_isoc_read_one_frame(xfer, n) < 0) 9653671d9d8SAndrew Thompson break; 9663671d9d8SAndrew Thompson /* FALLTHROUGH */ 9673671d9d8SAndrew Thompson 9683671d9d8SAndrew Thompson case USB_ST_SETUP: 9693671d9d8SAndrew Thompson read_next: 970ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) 971ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, 972ed6d949aSAndrew Thompson usbd_xfer_max_framelen(xfer)); 9733671d9d8SAndrew Thompson 974a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 9753671d9d8SAndrew Thompson break; 9763671d9d8SAndrew Thompson 9773671d9d8SAndrew Thompson default: /* Error */ 978ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 9793671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 9803671d9d8SAndrew Thompson goto read_next; 9813671d9d8SAndrew Thompson } 9823671d9d8SAndrew Thompson 9833671d9d8SAndrew Thompson /* transfer cancelled */ 9843671d9d8SAndrew Thompson break; 9853671d9d8SAndrew Thompson } 9863671d9d8SAndrew Thompson } /* ubt_isoc_read_callback */ 9873671d9d8SAndrew Thompson 9883671d9d8SAndrew Thompson /* 9893671d9d8SAndrew Thompson * Helper function. Called from ubt_isoc_read_callback() to read 9903671d9d8SAndrew Thompson * SCO data from one frame. 9913671d9d8SAndrew Thompson * USB context. 9923671d9d8SAndrew Thompson */ 9933671d9d8SAndrew Thompson 9943671d9d8SAndrew Thompson static int 995760bc48eSAndrew Thompson ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no) 9963671d9d8SAndrew Thompson { 997ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 998ed6d949aSAndrew Thompson struct usb_page_cache *pc; 9993671d9d8SAndrew Thompson struct mbuf *m; 1000ed6d949aSAndrew Thompson int len, want, got, total; 10013671d9d8SAndrew Thompson 10023671d9d8SAndrew Thompson /* Get existing SCO reassembly buffer */ 1003ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 10043671d9d8SAndrew Thompson m = sc->sc_isoc_in_buffer; 10058f9e0ef9SAndrew Thompson total = usbd_xfer_frame_len(xfer, frame_no); 10063671d9d8SAndrew Thompson 10073671d9d8SAndrew Thompson /* While we have data in the frame */ 1008ed6d949aSAndrew Thompson while (total > 0) { 10093671d9d8SAndrew Thompson if (m == NULL) { 10103671d9d8SAndrew Thompson /* Start new reassembly buffer */ 10113671d9d8SAndrew Thompson MGETHDR(m, M_DONTWAIT, MT_DATA); 10123671d9d8SAndrew Thompson if (m == NULL) { 10133671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 10143671d9d8SAndrew Thompson return (-1); /* XXX out of sync! */ 10153671d9d8SAndrew Thompson } 10163671d9d8SAndrew Thompson 10173671d9d8SAndrew Thompson MCLGET(m, M_DONTWAIT); 10183671d9d8SAndrew Thompson if (!(m->m_flags & M_EXT)) { 10193671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 10203671d9d8SAndrew Thompson NG_FREE_M(m); 10213671d9d8SAndrew Thompson return (-1); /* XXX out of sync! */ 10223671d9d8SAndrew Thompson } 10233671d9d8SAndrew Thompson 10243671d9d8SAndrew Thompson /* Expect SCO header */ 10253671d9d8SAndrew Thompson *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; 10263671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = got = 1; 10273671d9d8SAndrew Thompson want = sizeof(ng_hci_scodata_pkt_t); 10283671d9d8SAndrew Thompson } else { 10293671d9d8SAndrew Thompson /* 10303671d9d8SAndrew Thompson * Check if we have SCO header and if so 10313671d9d8SAndrew Thompson * adjust amount of data we want 10323671d9d8SAndrew Thompson */ 10333671d9d8SAndrew Thompson got = m->m_pkthdr.len; 10343671d9d8SAndrew Thompson want = sizeof(ng_hci_scodata_pkt_t); 10353671d9d8SAndrew Thompson 10363671d9d8SAndrew Thompson if (got >= want) 10373671d9d8SAndrew Thompson want += mtod(m, ng_hci_scodata_pkt_t *)->length; 10383671d9d8SAndrew Thompson } 10393671d9d8SAndrew Thompson 10403671d9d8SAndrew Thompson /* Append frame data to the SCO reassembly buffer */ 1041ed6d949aSAndrew Thompson len = total; 10423671d9d8SAndrew Thompson if (got + len > want) 10433671d9d8SAndrew Thompson len = want - got; 10443671d9d8SAndrew Thompson 1045ed6d949aSAndrew Thompson usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer), 10463671d9d8SAndrew Thompson mtod(m, uint8_t *) + m->m_pkthdr.len, len); 10473671d9d8SAndrew Thompson 10483671d9d8SAndrew Thompson m->m_pkthdr.len += len; 10493671d9d8SAndrew Thompson m->m_len += len; 1050ed6d949aSAndrew Thompson total -= len; 10513671d9d8SAndrew Thompson 10523671d9d8SAndrew Thompson /* Check if we got everything we wanted, if not - continue */ 10533671d9d8SAndrew Thompson if (got != want) 10543671d9d8SAndrew Thompson continue; 10553671d9d8SAndrew Thompson 10563671d9d8SAndrew Thompson /* If we got here then we got complete SCO frame */ 10573671d9d8SAndrew Thompson UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ 10583671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, 10593671d9d8SAndrew Thompson mtod(m, ng_hci_scodata_pkt_t *)->length); 10603671d9d8SAndrew Thompson 10613671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 10623671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 10633671d9d8SAndrew Thompson 10643671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 10653671d9d8SAndrew Thompson /* m == NULL at this point */ 10663671d9d8SAndrew Thompson } 10673671d9d8SAndrew Thompson 10683671d9d8SAndrew Thompson /* Put SCO reassembly buffer back */ 10693671d9d8SAndrew Thompson sc->sc_isoc_in_buffer = m; 10703671d9d8SAndrew Thompson 10713671d9d8SAndrew Thompson return (0); 10723671d9d8SAndrew Thompson } /* ubt_isoc_read_one_frame */ 10733671d9d8SAndrew Thompson 10743671d9d8SAndrew Thompson /* 10753671d9d8SAndrew Thompson * Called when outgoing isoc transfer (SCO packet) has completed, i.e. 10763671d9d8SAndrew Thompson * SCO packet was sent to the device. 10773671d9d8SAndrew Thompson * USB context. 10783671d9d8SAndrew Thompson */ 10793671d9d8SAndrew Thompson 10803671d9d8SAndrew Thompson static void 1081ed6d949aSAndrew Thompson ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 10823671d9d8SAndrew Thompson { 1083ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 1084ed6d949aSAndrew Thompson struct usb_page_cache *pc; 10853671d9d8SAndrew Thompson struct mbuf *m; 10863671d9d8SAndrew Thompson int n, space, offset; 1087ed6d949aSAndrew Thompson int actlen, nframes; 1088ed6d949aSAndrew Thompson 1089ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1090ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 10913671d9d8SAndrew Thompson 10923671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 10933671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 1094ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen); 1095ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 10963671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 10973671d9d8SAndrew Thompson /* FALLTHROUGH */ 10983671d9d8SAndrew Thompson 10993671d9d8SAndrew Thompson case USB_ST_SETUP: 11003671d9d8SAndrew Thompson send_next: 11013671d9d8SAndrew Thompson offset = 0; 1102ed6d949aSAndrew Thompson space = usbd_xfer_max_framelen(xfer) * nframes; 11033671d9d8SAndrew Thompson m = NULL; 11043671d9d8SAndrew Thompson 11053671d9d8SAndrew Thompson while (space > 0) { 11063671d9d8SAndrew Thompson if (m == NULL) { 11073671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 11083671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); 11093671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 11103671d9d8SAndrew Thompson 11113671d9d8SAndrew Thompson if (m == NULL) 11123671d9d8SAndrew Thompson break; 11133671d9d8SAndrew Thompson } 11143671d9d8SAndrew Thompson 11153671d9d8SAndrew Thompson n = min(space, m->m_pkthdr.len); 11163671d9d8SAndrew Thompson if (n > 0) { 1117ed6d949aSAndrew Thompson usbd_m_copy_in(pc, offset, m,0, n); 11183671d9d8SAndrew Thompson m_adj(m, n); 11193671d9d8SAndrew Thompson 11203671d9d8SAndrew Thompson offset += n; 11213671d9d8SAndrew Thompson space -= n; 11223671d9d8SAndrew Thompson } 11233671d9d8SAndrew Thompson 11243671d9d8SAndrew Thompson if (m->m_pkthdr.len == 0) 11253671d9d8SAndrew Thompson NG_FREE_M(m); /* sets m = NULL */ 11263671d9d8SAndrew Thompson } 11273671d9d8SAndrew Thompson 11283671d9d8SAndrew Thompson /* Put whatever is left from mbuf back on queue */ 11293671d9d8SAndrew Thompson if (m != NULL) { 11303671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 11313671d9d8SAndrew Thompson NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); 11323671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 11333671d9d8SAndrew Thompson } 11343671d9d8SAndrew Thompson 11353671d9d8SAndrew Thompson /* 11363671d9d8SAndrew Thompson * Calculate sizes for isoc frames. 11373671d9d8SAndrew Thompson * Note that offset could be 0 at this point (i.e. we have 11383671d9d8SAndrew Thompson * nothing to send). That is fine, as we have isoc. transfers 11393671d9d8SAndrew Thompson * going in both directions all the time. In this case it 11403671d9d8SAndrew Thompson * would be just empty isoc. transfer. 11413671d9d8SAndrew Thompson */ 11423671d9d8SAndrew Thompson 1143ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) { 1144ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, 1145ed6d949aSAndrew Thompson min(offset, usbd_xfer_max_framelen(xfer))); 11468f9e0ef9SAndrew Thompson offset -= usbd_xfer_frame_len(xfer, n); 11473671d9d8SAndrew Thompson } 11483671d9d8SAndrew Thompson 1149a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 11503671d9d8SAndrew Thompson break; 11513671d9d8SAndrew Thompson 11523671d9d8SAndrew Thompson default: /* Error */ 1153ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 11543671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 11553671d9d8SAndrew Thompson goto send_next; 11563671d9d8SAndrew Thompson } 11573671d9d8SAndrew Thompson 11583671d9d8SAndrew Thompson /* transfer cancelled */ 11593671d9d8SAndrew Thompson break; 11603671d9d8SAndrew Thompson } 11613671d9d8SAndrew Thompson } 11623671d9d8SAndrew Thompson 11633671d9d8SAndrew Thompson /* 11643671d9d8SAndrew Thompson * Utility function to forward provided mbuf upstream (i.e. up the stack). 11653671d9d8SAndrew Thompson * Modifies value of the mbuf pointer (sets it to NULL). 11663671d9d8SAndrew Thompson * Save to call from any context. 11673671d9d8SAndrew Thompson */ 11683671d9d8SAndrew Thompson 11693671d9d8SAndrew Thompson static int 11703671d9d8SAndrew Thompson ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) 11713671d9d8SAndrew Thompson { 11723671d9d8SAndrew Thompson hook_p hook; 11733671d9d8SAndrew Thompson int error; 11743671d9d8SAndrew Thompson 11753671d9d8SAndrew Thompson /* 11763671d9d8SAndrew Thompson * Close the race with Netgraph hook newhook/disconnect methods. 11773671d9d8SAndrew Thompson * Save the hook pointer atomically. Two cases are possible: 11783671d9d8SAndrew Thompson * 11793671d9d8SAndrew Thompson * 1) The hook pointer is NULL. It means disconnect method got 11803671d9d8SAndrew Thompson * there first. In this case we are done. 11813671d9d8SAndrew Thompson * 11823671d9d8SAndrew Thompson * 2) The hook pointer is not NULL. It means that hook pointer 11833671d9d8SAndrew Thompson * could be either in valid or invalid (i.e. in the process 11843671d9d8SAndrew Thompson * of disconnect) state. In any case grab an extra reference 11853671d9d8SAndrew Thompson * to protect the hook pointer. 11863671d9d8SAndrew Thompson * 11873671d9d8SAndrew Thompson * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as 11883671d9d8SAndrew Thompson * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). 11893671d9d8SAndrew Thompson */ 11903671d9d8SAndrew Thompson 11913671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 11923671d9d8SAndrew Thompson if ((hook = sc->sc_hook) != NULL) 11933671d9d8SAndrew Thompson NG_HOOK_REF(hook); 11943671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 11953671d9d8SAndrew Thompson 11963671d9d8SAndrew Thompson if (hook == NULL) { 11973671d9d8SAndrew Thompson NG_FREE_M(*m); 11983671d9d8SAndrew Thompson return (ENETDOWN); 11993671d9d8SAndrew Thompson } 12003671d9d8SAndrew Thompson 12013671d9d8SAndrew Thompson NG_SEND_DATA_ONLY(error, hook, *m); 12023671d9d8SAndrew Thompson NG_HOOK_UNREF(hook); 12033671d9d8SAndrew Thompson 12043671d9d8SAndrew Thompson if (error != 0) 12053671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 12063671d9d8SAndrew Thompson 12073671d9d8SAndrew Thompson return (error); 12083671d9d8SAndrew Thompson } /* ubt_fwd_mbuf_up */ 12093671d9d8SAndrew Thompson 12103671d9d8SAndrew Thompson /**************************************************************************** 12113671d9d8SAndrew Thompson **************************************************************************** 12123671d9d8SAndrew Thompson ** Glue 12133671d9d8SAndrew Thompson **************************************************************************** 12143671d9d8SAndrew Thompson ****************************************************************************/ 12153671d9d8SAndrew Thompson 12163671d9d8SAndrew Thompson /* 12173671d9d8SAndrew Thompson * Schedule glue task. Should be called with sc_ng_mtx held. 12183671d9d8SAndrew Thompson * Netgraph context. 12193671d9d8SAndrew Thompson */ 12203671d9d8SAndrew Thompson 12213671d9d8SAndrew Thompson static void 12223671d9d8SAndrew Thompson ubt_task_schedule(ubt_softc_p sc, int action) 12233671d9d8SAndrew Thompson { 12243671d9d8SAndrew Thompson mtx_assert(&sc->sc_ng_mtx, MA_OWNED); 12253671d9d8SAndrew Thompson 12263671d9d8SAndrew Thompson /* 12273671d9d8SAndrew Thompson * Try to handle corner case when "start all" and "stop all" 12283671d9d8SAndrew Thompson * actions can both be set before task is executed. 12293671d9d8SAndrew Thompson * 12303671d9d8SAndrew Thompson * The rules are 12313671d9d8SAndrew Thompson * 12323671d9d8SAndrew Thompson * sc_task_flags action new sc_task_flags 12333671d9d8SAndrew Thompson * ------------------------------------------------------ 12343671d9d8SAndrew Thompson * 0 start start 12353671d9d8SAndrew Thompson * 0 stop stop 12363671d9d8SAndrew Thompson * start start start 12373671d9d8SAndrew Thompson * start stop stop 12383671d9d8SAndrew Thompson * stop start stop|start 12393671d9d8SAndrew Thompson * stop stop stop 12403671d9d8SAndrew Thompson * stop|start start stop|start 12413671d9d8SAndrew Thompson * stop|start stop stop 12423671d9d8SAndrew Thompson */ 12433671d9d8SAndrew Thompson 12443671d9d8SAndrew Thompson if (action != 0) { 12453671d9d8SAndrew Thompson if ((action & UBT_FLAG_T_STOP_ALL) != 0) 12463671d9d8SAndrew Thompson sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; 12473671d9d8SAndrew Thompson 12483671d9d8SAndrew Thompson sc->sc_task_flags |= action; 12493671d9d8SAndrew Thompson } 12503671d9d8SAndrew Thompson 12513671d9d8SAndrew Thompson if (sc->sc_task_flags & UBT_FLAG_T_PENDING) 12523671d9d8SAndrew Thompson return; 12533671d9d8SAndrew Thompson 12543671d9d8SAndrew Thompson if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { 12553671d9d8SAndrew Thompson sc->sc_task_flags |= UBT_FLAG_T_PENDING; 12563671d9d8SAndrew Thompson return; 12573671d9d8SAndrew Thompson } 12583671d9d8SAndrew Thompson 12593671d9d8SAndrew Thompson /* XXX: i think this should never happen */ 12603671d9d8SAndrew Thompson } /* ubt_task_schedule */ 12613671d9d8SAndrew Thompson 12623671d9d8SAndrew Thompson /* 12633671d9d8SAndrew Thompson * Glue task. Examines sc_task_flags and does things depending on it. 12643671d9d8SAndrew Thompson * Taskqueue context. 12653671d9d8SAndrew Thompson */ 12663671d9d8SAndrew Thompson 12673671d9d8SAndrew Thompson static void 12683671d9d8SAndrew Thompson ubt_task(void *context, int pending) 12693671d9d8SAndrew Thompson { 12703671d9d8SAndrew Thompson ubt_softc_p sc = context; 12713671d9d8SAndrew Thompson int task_flags, i; 12723671d9d8SAndrew Thompson 12733671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 12743671d9d8SAndrew Thompson task_flags = sc->sc_task_flags; 12753671d9d8SAndrew Thompson sc->sc_task_flags = 0; 12763671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 12773671d9d8SAndrew Thompson 12783671d9d8SAndrew Thompson /* 12793671d9d8SAndrew Thompson * Stop all USB transfers synchronously. 12803671d9d8SAndrew Thompson * Stop interface #0 and #1 transfers at the same time and in the 1281a593f6b8SAndrew Thompson * same loop. usbd_transfer_drain() will do appropriate locking. 12823671d9d8SAndrew Thompson */ 12833671d9d8SAndrew Thompson 12843671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_STOP_ALL) 12853671d9d8SAndrew Thompson for (i = 0; i < UBT_N_TRANSFER; i ++) 1286a593f6b8SAndrew Thompson usbd_transfer_drain(sc->sc_xfer[i]); 12873671d9d8SAndrew Thompson 12883671d9d8SAndrew Thompson /* Start incoming interrupt and bulk, and all isoc. USB transfers */ 12893671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_ALL) { 12903671d9d8SAndrew Thompson /* 12913671d9d8SAndrew Thompson * Interface #0 12923671d9d8SAndrew Thompson */ 12933671d9d8SAndrew Thompson 12943671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 12953671d9d8SAndrew Thompson 12963671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); 12973671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); 12983671d9d8SAndrew Thompson 12993671d9d8SAndrew Thompson /* 13003671d9d8SAndrew Thompson * Interface #1 13013671d9d8SAndrew Thompson * Start both read and write isoc. transfers by default. 13023671d9d8SAndrew Thompson * Get them going all the time even if we have nothing 13033671d9d8SAndrew Thompson * to send to avoid any delays. 13043671d9d8SAndrew Thompson */ 13053671d9d8SAndrew Thompson 13063671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); 13073671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); 13083671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); 13093671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); 13103671d9d8SAndrew Thompson 13113671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 13123671d9d8SAndrew Thompson } 13133671d9d8SAndrew Thompson 13143671d9d8SAndrew Thompson /* Start outgoing control transfer */ 13153671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_CTRL) { 13163671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 13173671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); 13183671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 13193671d9d8SAndrew Thompson } 13203671d9d8SAndrew Thompson 13213671d9d8SAndrew Thompson /* Start outgoing bulk transfer */ 13223671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_BULK) { 13233671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 13243671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); 13253671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 13263671d9d8SAndrew Thompson } 13273671d9d8SAndrew Thompson } /* ubt_task */ 13283671d9d8SAndrew Thompson 13293671d9d8SAndrew Thompson /**************************************************************************** 13303671d9d8SAndrew Thompson **************************************************************************** 13313671d9d8SAndrew Thompson ** Netgraph specific 13323671d9d8SAndrew Thompson **************************************************************************** 13333671d9d8SAndrew Thompson ****************************************************************************/ 13343671d9d8SAndrew Thompson 13353671d9d8SAndrew Thompson /* 13363671d9d8SAndrew Thompson * Netgraph node constructor. Do not allow to create node of this type. 13373671d9d8SAndrew Thompson * Netgraph context. 13383671d9d8SAndrew Thompson */ 13393671d9d8SAndrew Thompson 13403671d9d8SAndrew Thompson static int 13413671d9d8SAndrew Thompson ng_ubt_constructor(node_p node) 13423671d9d8SAndrew Thompson { 13433671d9d8SAndrew Thompson return (EINVAL); 13443671d9d8SAndrew Thompson } /* ng_ubt_constructor */ 13453671d9d8SAndrew Thompson 13463671d9d8SAndrew Thompson /* 13473671d9d8SAndrew Thompson * Netgraph node destructor. Destroy node only when device has been detached. 13483671d9d8SAndrew Thompson * Netgraph context. 13493671d9d8SAndrew Thompson */ 13503671d9d8SAndrew Thompson 13513671d9d8SAndrew Thompson static int 13523671d9d8SAndrew Thompson ng_ubt_shutdown(node_p node) 13533671d9d8SAndrew Thompson { 13543671d9d8SAndrew Thompson if (node->nd_flags & NGF_REALLY_DIE) { 13553671d9d8SAndrew Thompson /* 13563671d9d8SAndrew Thompson * We came here because the USB device is being 13573671d9d8SAndrew Thompson * detached, so stop being persistant. 13583671d9d8SAndrew Thompson */ 13593671d9d8SAndrew Thompson NG_NODE_SET_PRIVATE(node, NULL); 13603671d9d8SAndrew Thompson NG_NODE_UNREF(node); 13613671d9d8SAndrew Thompson } else 13623671d9d8SAndrew Thompson NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ 13633671d9d8SAndrew Thompson 13643671d9d8SAndrew Thompson return (0); 13653671d9d8SAndrew Thompson } /* ng_ubt_shutdown */ 13663671d9d8SAndrew Thompson 13673671d9d8SAndrew Thompson /* 13683671d9d8SAndrew Thompson * Create new hook. There can only be one. 13693671d9d8SAndrew Thompson * Netgraph context. 13703671d9d8SAndrew Thompson */ 13713671d9d8SAndrew Thompson 13723671d9d8SAndrew Thompson static int 13733671d9d8SAndrew Thompson ng_ubt_newhook(node_p node, hook_p hook, char const *name) 13743671d9d8SAndrew Thompson { 13753671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(node); 13763671d9d8SAndrew Thompson 13773671d9d8SAndrew Thompson if (strcmp(name, NG_UBT_HOOK) != 0) 13783671d9d8SAndrew Thompson return (EINVAL); 13793671d9d8SAndrew Thompson 13803671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 13813671d9d8SAndrew Thompson if (sc->sc_hook != NULL) { 13823671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 13833671d9d8SAndrew Thompson 13843671d9d8SAndrew Thompson return (EISCONN); 13853671d9d8SAndrew Thompson } 13863671d9d8SAndrew Thompson 13873671d9d8SAndrew Thompson sc->sc_hook = hook; 13883671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 13893671d9d8SAndrew Thompson 13903671d9d8SAndrew Thompson return (0); 13913671d9d8SAndrew Thompson } /* ng_ubt_newhook */ 13923671d9d8SAndrew Thompson 13933671d9d8SAndrew Thompson /* 13943671d9d8SAndrew Thompson * Connect hook. Start incoming USB transfers. 13953671d9d8SAndrew Thompson * Netgraph context. 13963671d9d8SAndrew Thompson */ 13973671d9d8SAndrew Thompson 13983671d9d8SAndrew Thompson static int 13993671d9d8SAndrew Thompson ng_ubt_connect(hook_p hook) 14003671d9d8SAndrew Thompson { 14013671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 14023671d9d8SAndrew Thompson 14033671d9d8SAndrew Thompson NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 14043671d9d8SAndrew Thompson 14053671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 14063671d9d8SAndrew Thompson ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); 14073671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14083671d9d8SAndrew Thompson 14093671d9d8SAndrew Thompson return (0); 14103671d9d8SAndrew Thompson } /* ng_ubt_connect */ 14113671d9d8SAndrew Thompson 14123671d9d8SAndrew Thompson /* 14133671d9d8SAndrew Thompson * Disconnect hook. 14143671d9d8SAndrew Thompson * Netgraph context. 14153671d9d8SAndrew Thompson */ 14163671d9d8SAndrew Thompson 14173671d9d8SAndrew Thompson static int 14183671d9d8SAndrew Thompson ng_ubt_disconnect(hook_p hook) 14193671d9d8SAndrew Thompson { 14203671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 14213671d9d8SAndrew Thompson 14223671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 14233671d9d8SAndrew Thompson 14243671d9d8SAndrew Thompson if (hook != sc->sc_hook) { 14253671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14263671d9d8SAndrew Thompson 14273671d9d8SAndrew Thompson return (EINVAL); 14283671d9d8SAndrew Thompson } 14293671d9d8SAndrew Thompson 14303671d9d8SAndrew Thompson sc->sc_hook = NULL; 14313671d9d8SAndrew Thompson 14323671d9d8SAndrew Thompson /* Kick off task to stop all USB xfers */ 14333671d9d8SAndrew Thompson ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); 14343671d9d8SAndrew Thompson 14353671d9d8SAndrew Thompson /* Drain queues */ 14363671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); 14373671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); 14383671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); 14393671d9d8SAndrew Thompson 14403671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14413671d9d8SAndrew Thompson 14423671d9d8SAndrew Thompson return (0); 14433671d9d8SAndrew Thompson } /* ng_ubt_disconnect */ 14443671d9d8SAndrew Thompson 14453671d9d8SAndrew Thompson /* 14463671d9d8SAndrew Thompson * Process control message. 14473671d9d8SAndrew Thompson * Netgraph context. 14483671d9d8SAndrew Thompson */ 14493671d9d8SAndrew Thompson 14503671d9d8SAndrew Thompson static int 14513671d9d8SAndrew Thompson ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) 14523671d9d8SAndrew Thompson { 14533671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(node); 14543671d9d8SAndrew Thompson struct ng_mesg *msg, *rsp = NULL; 14553671d9d8SAndrew Thompson struct ng_bt_mbufq *q; 14563671d9d8SAndrew Thompson int error = 0, queue, qlen; 14573671d9d8SAndrew Thompson 14583671d9d8SAndrew Thompson NGI_GET_MSG(item, msg); 14593671d9d8SAndrew Thompson 14603671d9d8SAndrew Thompson switch (msg->header.typecookie) { 14613671d9d8SAndrew Thompson case NGM_GENERIC_COOKIE: 14623671d9d8SAndrew Thompson switch (msg->header.cmd) { 14633671d9d8SAndrew Thompson case NGM_TEXT_STATUS: 14643671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 14653671d9d8SAndrew Thompson if (rsp == NULL) { 14663671d9d8SAndrew Thompson error = ENOMEM; 14673671d9d8SAndrew Thompson break; 14683671d9d8SAndrew Thompson } 14693671d9d8SAndrew Thompson 14703671d9d8SAndrew Thompson snprintf(rsp->data, NG_TEXTRESPONSE, 14713671d9d8SAndrew Thompson "Hook: %s\n" \ 14723671d9d8SAndrew Thompson "Task flags: %#x\n" \ 14733671d9d8SAndrew Thompson "Debug: %d\n" \ 14743671d9d8SAndrew Thompson "CMD queue: [have:%d,max:%d]\n" \ 14753671d9d8SAndrew Thompson "ACL queue: [have:%d,max:%d]\n" \ 14763671d9d8SAndrew Thompson "SCO queue: [have:%d,max:%d]", 14773671d9d8SAndrew Thompson (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", 14783671d9d8SAndrew Thompson sc->sc_task_flags, 14793671d9d8SAndrew Thompson sc->sc_debug, 14803671d9d8SAndrew Thompson sc->sc_cmdq.len, 14813671d9d8SAndrew Thompson sc->sc_cmdq.maxlen, 14823671d9d8SAndrew Thompson sc->sc_aclq.len, 14833671d9d8SAndrew Thompson sc->sc_aclq.maxlen, 14843671d9d8SAndrew Thompson sc->sc_scoq.len, 14853671d9d8SAndrew Thompson sc->sc_scoq.maxlen); 14863671d9d8SAndrew Thompson break; 14873671d9d8SAndrew Thompson 14883671d9d8SAndrew Thompson default: 14893671d9d8SAndrew Thompson error = EINVAL; 14903671d9d8SAndrew Thompson break; 14913671d9d8SAndrew Thompson } 14923671d9d8SAndrew Thompson break; 14933671d9d8SAndrew Thompson 14943671d9d8SAndrew Thompson case NGM_UBT_COOKIE: 14953671d9d8SAndrew Thompson switch (msg->header.cmd) { 14963671d9d8SAndrew Thompson case NGM_UBT_NODE_SET_DEBUG: 14973671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ 14983671d9d8SAndrew Thompson error = EMSGSIZE; 14993671d9d8SAndrew Thompson break; 15003671d9d8SAndrew Thompson } 15013671d9d8SAndrew Thompson 15023671d9d8SAndrew Thompson sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); 15033671d9d8SAndrew Thompson break; 15043671d9d8SAndrew Thompson 15053671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_DEBUG: 15063671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), 15073671d9d8SAndrew Thompson M_NOWAIT); 15083671d9d8SAndrew Thompson if (rsp == NULL) { 15093671d9d8SAndrew Thompson error = ENOMEM; 15103671d9d8SAndrew Thompson break; 15113671d9d8SAndrew Thompson } 15123671d9d8SAndrew Thompson 15133671d9d8SAndrew Thompson *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; 15143671d9d8SAndrew Thompson break; 15153671d9d8SAndrew Thompson 15163671d9d8SAndrew Thompson case NGM_UBT_NODE_SET_QLEN: 15173671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 15183671d9d8SAndrew Thompson error = EMSGSIZE; 15193671d9d8SAndrew Thompson break; 15203671d9d8SAndrew Thompson } 15213671d9d8SAndrew Thompson 15223671d9d8SAndrew Thompson queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 15233671d9d8SAndrew Thompson qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; 15243671d9d8SAndrew Thompson 15253671d9d8SAndrew Thompson switch (queue) { 15263671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_CMD: 15273671d9d8SAndrew Thompson q = &sc->sc_cmdq; 15283671d9d8SAndrew Thompson break; 15293671d9d8SAndrew Thompson 15303671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_ACL: 15313671d9d8SAndrew Thompson q = &sc->sc_aclq; 15323671d9d8SAndrew Thompson break; 15333671d9d8SAndrew Thompson 15343671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_SCO: 15353671d9d8SAndrew Thompson q = &sc->sc_scoq; 15363671d9d8SAndrew Thompson break; 15373671d9d8SAndrew Thompson 15383671d9d8SAndrew Thompson default: 15393671d9d8SAndrew Thompson error = EINVAL; 15403671d9d8SAndrew Thompson goto done; 15413671d9d8SAndrew Thompson /* NOT REACHED */ 15423671d9d8SAndrew Thompson } 15433671d9d8SAndrew Thompson 15443671d9d8SAndrew Thompson q->maxlen = qlen; 15453671d9d8SAndrew Thompson break; 15463671d9d8SAndrew Thompson 15473671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_QLEN: 15483671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 15493671d9d8SAndrew Thompson error = EMSGSIZE; 15503671d9d8SAndrew Thompson break; 15513671d9d8SAndrew Thompson } 15523671d9d8SAndrew Thompson 15533671d9d8SAndrew Thompson queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 15543671d9d8SAndrew Thompson 15553671d9d8SAndrew Thompson switch (queue) { 15563671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_CMD: 15573671d9d8SAndrew Thompson q = &sc->sc_cmdq; 15583671d9d8SAndrew Thompson break; 15593671d9d8SAndrew Thompson 15603671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_ACL: 15613671d9d8SAndrew Thompson q = &sc->sc_aclq; 15623671d9d8SAndrew Thompson break; 15633671d9d8SAndrew Thompson 15643671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_SCO: 15653671d9d8SAndrew Thompson q = &sc->sc_scoq; 15663671d9d8SAndrew Thompson break; 15673671d9d8SAndrew Thompson 15683671d9d8SAndrew Thompson default: 15693671d9d8SAndrew Thompson error = EINVAL; 15703671d9d8SAndrew Thompson goto done; 15713671d9d8SAndrew Thompson /* NOT REACHED */ 15723671d9d8SAndrew Thompson } 15733671d9d8SAndrew Thompson 15743671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), 15753671d9d8SAndrew Thompson M_NOWAIT); 15763671d9d8SAndrew Thompson if (rsp == NULL) { 15773671d9d8SAndrew Thompson error = ENOMEM; 15783671d9d8SAndrew Thompson break; 15793671d9d8SAndrew Thompson } 15803671d9d8SAndrew Thompson 15813671d9d8SAndrew Thompson ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; 15823671d9d8SAndrew Thompson ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; 15833671d9d8SAndrew Thompson break; 15843671d9d8SAndrew Thompson 15853671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_STAT: 15863671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), 15873671d9d8SAndrew Thompson M_NOWAIT); 15883671d9d8SAndrew Thompson if (rsp == NULL) { 15893671d9d8SAndrew Thompson error = ENOMEM; 15903671d9d8SAndrew Thompson break; 15913671d9d8SAndrew Thompson } 15923671d9d8SAndrew Thompson 15933671d9d8SAndrew Thompson bcopy(&sc->sc_stat, rsp->data, 15943671d9d8SAndrew Thompson sizeof(ng_ubt_node_stat_ep)); 15953671d9d8SAndrew Thompson break; 15963671d9d8SAndrew Thompson 15973671d9d8SAndrew Thompson case NGM_UBT_NODE_RESET_STAT: 15983671d9d8SAndrew Thompson UBT_STAT_RESET(sc); 15993671d9d8SAndrew Thompson break; 16003671d9d8SAndrew Thompson 16013671d9d8SAndrew Thompson default: 16023671d9d8SAndrew Thompson error = EINVAL; 16033671d9d8SAndrew Thompson break; 16043671d9d8SAndrew Thompson } 16053671d9d8SAndrew Thompson break; 16063671d9d8SAndrew Thompson 16073671d9d8SAndrew Thompson default: 16083671d9d8SAndrew Thompson error = EINVAL; 16093671d9d8SAndrew Thompson break; 16103671d9d8SAndrew Thompson } 16113671d9d8SAndrew Thompson done: 16123671d9d8SAndrew Thompson NG_RESPOND_MSG(error, node, item, rsp); 16133671d9d8SAndrew Thompson NG_FREE_MSG(msg); 16143671d9d8SAndrew Thompson 16153671d9d8SAndrew Thompson return (error); 16163671d9d8SAndrew Thompson } /* ng_ubt_rcvmsg */ 16173671d9d8SAndrew Thompson 16183671d9d8SAndrew Thompson /* 16193671d9d8SAndrew Thompson * Process data. 16203671d9d8SAndrew Thompson * Netgraph context. 16213671d9d8SAndrew Thompson */ 16223671d9d8SAndrew Thompson 16233671d9d8SAndrew Thompson static int 16243671d9d8SAndrew Thompson ng_ubt_rcvdata(hook_p hook, item_p item) 16253671d9d8SAndrew Thompson { 16263671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 16273671d9d8SAndrew Thompson struct mbuf *m; 16283671d9d8SAndrew Thompson struct ng_bt_mbufq *q; 16293671d9d8SAndrew Thompson int action, error = 0; 16303671d9d8SAndrew Thompson 16313671d9d8SAndrew Thompson if (hook != sc->sc_hook) { 16323671d9d8SAndrew Thompson error = EINVAL; 16333671d9d8SAndrew Thompson goto done; 16343671d9d8SAndrew Thompson } 16353671d9d8SAndrew Thompson 16363671d9d8SAndrew Thompson /* Deatch mbuf and get HCI frame type */ 16373671d9d8SAndrew Thompson NGI_GET_M(item, m); 16383671d9d8SAndrew Thompson 16393671d9d8SAndrew Thompson /* 16403671d9d8SAndrew Thompson * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, 16413671d9d8SAndrew Thompson * 2 bytes connection handle and at least 1 byte of length. 16423671d9d8SAndrew Thompson * Panic on data frame that has size smaller than 4 bytes (it 16433671d9d8SAndrew Thompson * should not happen) 16443671d9d8SAndrew Thompson */ 16453671d9d8SAndrew Thompson 16463671d9d8SAndrew Thompson if (m->m_pkthdr.len < 4) 16473671d9d8SAndrew Thompson panic("HCI frame size is too small! pktlen=%d\n", 16483671d9d8SAndrew Thompson m->m_pkthdr.len); 16493671d9d8SAndrew Thompson 16503671d9d8SAndrew Thompson /* Process HCI frame */ 16513671d9d8SAndrew Thompson switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ 16523671d9d8SAndrew Thompson case NG_HCI_CMD_PKT: 16533671d9d8SAndrew Thompson if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE) 16543671d9d8SAndrew Thompson panic("HCI command frame size is too big! " \ 16553671d9d8SAndrew Thompson "buffer size=%zd, packet len=%d\n", 16563671d9d8SAndrew Thompson UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); 16573671d9d8SAndrew Thompson 16583671d9d8SAndrew Thompson q = &sc->sc_cmdq; 16593671d9d8SAndrew Thompson action = UBT_FLAG_T_START_CTRL; 16603671d9d8SAndrew Thompson break; 16613671d9d8SAndrew Thompson 16623671d9d8SAndrew Thompson case NG_HCI_ACL_DATA_PKT: 16633671d9d8SAndrew Thompson if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) 16643671d9d8SAndrew Thompson panic("ACL data frame size is too big! " \ 16653671d9d8SAndrew Thompson "buffer size=%d, packet len=%d\n", 16663671d9d8SAndrew Thompson UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); 16673671d9d8SAndrew Thompson 16683671d9d8SAndrew Thompson q = &sc->sc_aclq; 16693671d9d8SAndrew Thompson action = UBT_FLAG_T_START_BULK; 16703671d9d8SAndrew Thompson break; 16713671d9d8SAndrew Thompson 16723671d9d8SAndrew Thompson case NG_HCI_SCO_DATA_PKT: 16733671d9d8SAndrew Thompson q = &sc->sc_scoq; 16743671d9d8SAndrew Thompson action = 0; 16753671d9d8SAndrew Thompson break; 16763671d9d8SAndrew Thompson 16773671d9d8SAndrew Thompson default: 16783671d9d8SAndrew Thompson UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ 16793671d9d8SAndrew Thompson "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len); 16803671d9d8SAndrew Thompson 16813671d9d8SAndrew Thompson NG_FREE_M(m); 16823671d9d8SAndrew Thompson error = EINVAL; 16833671d9d8SAndrew Thompson goto done; 16843671d9d8SAndrew Thompson /* NOT REACHED */ 16853671d9d8SAndrew Thompson } 16863671d9d8SAndrew Thompson 16873671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 16883671d9d8SAndrew Thompson if (NG_BT_MBUFQ_FULL(q)) { 16893671d9d8SAndrew Thompson NG_BT_MBUFQ_DROP(q); 16903671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 16913671d9d8SAndrew Thompson 16923671d9d8SAndrew Thompson UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", 16933671d9d8SAndrew Thompson *mtod(m, uint8_t *), m->m_pkthdr.len); 16943671d9d8SAndrew Thompson 16953671d9d8SAndrew Thompson NG_FREE_M(m); 16963671d9d8SAndrew Thompson } else { 16973671d9d8SAndrew Thompson /* Loose HCI packet type, enqueue mbuf and kick off task */ 16983671d9d8SAndrew Thompson m_adj(m, sizeof(uint8_t)); 16993671d9d8SAndrew Thompson NG_BT_MBUFQ_ENQUEUE(q, m); 17003671d9d8SAndrew Thompson ubt_task_schedule(sc, action); 17013671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 17023671d9d8SAndrew Thompson } 17033671d9d8SAndrew Thompson done: 17043671d9d8SAndrew Thompson NG_FREE_ITEM(item); 17053671d9d8SAndrew Thompson 17063671d9d8SAndrew Thompson return (error); 17073671d9d8SAndrew Thompson } /* ng_ubt_rcvdata */ 17083671d9d8SAndrew Thompson 17093671d9d8SAndrew Thompson /**************************************************************************** 17103671d9d8SAndrew Thompson **************************************************************************** 17113671d9d8SAndrew Thompson ** Module 17123671d9d8SAndrew Thompson **************************************************************************** 17133671d9d8SAndrew Thompson ****************************************************************************/ 17143671d9d8SAndrew Thompson 17153671d9d8SAndrew Thompson /* 17163671d9d8SAndrew Thompson * Load/Unload the driver module 17173671d9d8SAndrew Thompson */ 17183671d9d8SAndrew Thompson 17193671d9d8SAndrew Thompson static int 17203671d9d8SAndrew Thompson ubt_modevent(module_t mod, int event, void *data) 17213671d9d8SAndrew Thompson { 17223671d9d8SAndrew Thompson int error; 17233671d9d8SAndrew Thompson 17243671d9d8SAndrew Thompson switch (event) { 17253671d9d8SAndrew Thompson case MOD_LOAD: 17263671d9d8SAndrew Thompson error = ng_newtype(&typestruct); 17273671d9d8SAndrew Thompson if (error != 0) 17283671d9d8SAndrew Thompson printf("%s: Could not register Netgraph node type, " \ 17293671d9d8SAndrew Thompson "error=%d\n", NG_UBT_NODE_TYPE, error); 17303671d9d8SAndrew Thompson break; 17313671d9d8SAndrew Thompson 17323671d9d8SAndrew Thompson case MOD_UNLOAD: 17333671d9d8SAndrew Thompson error = ng_rmtype(&typestruct); 17343671d9d8SAndrew Thompson break; 17353671d9d8SAndrew Thompson 17363671d9d8SAndrew Thompson default: 17373671d9d8SAndrew Thompson error = EOPNOTSUPP; 17383671d9d8SAndrew Thompson break; 17393671d9d8SAndrew Thompson } 17403671d9d8SAndrew Thompson 17413671d9d8SAndrew Thompson return (error); 17423671d9d8SAndrew Thompson } /* ubt_modevent */ 17433671d9d8SAndrew Thompson 17443671d9d8SAndrew Thompson static devclass_t ubt_devclass; 17453671d9d8SAndrew Thompson 17463671d9d8SAndrew Thompson static device_method_t ubt_methods[] = 17473671d9d8SAndrew Thompson { 17483671d9d8SAndrew Thompson DEVMETHOD(device_probe, ubt_probe), 17493671d9d8SAndrew Thompson DEVMETHOD(device_attach, ubt_attach), 17503671d9d8SAndrew Thompson DEVMETHOD(device_detach, ubt_detach), 17513671d9d8SAndrew Thompson { 0, 0 } 17523671d9d8SAndrew Thompson }; 17533671d9d8SAndrew Thompson 17543671d9d8SAndrew Thompson static driver_t ubt_driver = 17553671d9d8SAndrew Thompson { 17563671d9d8SAndrew Thompson .name = "ubt", 17573671d9d8SAndrew Thompson .methods = ubt_methods, 17583671d9d8SAndrew Thompson .size = sizeof(struct ubt_softc), 17593671d9d8SAndrew Thompson }; 17603671d9d8SAndrew Thompson 17613671d9d8SAndrew Thompson DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0); 17623671d9d8SAndrew Thompson MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); 17633671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 17643671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 17653671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, usb, 1, 1, 1); 17663671d9d8SAndrew Thompson 1767