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/module.h> 104ed6d949aSAndrew Thompson #include <sys/lock.h> 105ed6d949aSAndrew Thompson #include <sys/mutex.h> 106ed6d949aSAndrew Thompson #include <sys/condvar.h> 107ed6d949aSAndrew Thompson #include <sys/sysctl.h> 108ed6d949aSAndrew Thompson #include <sys/sx.h> 109ed6d949aSAndrew Thompson #include <sys/unistd.h> 110ed6d949aSAndrew Thompson #include <sys/callout.h> 111ed6d949aSAndrew Thompson #include <sys/malloc.h> 112ed6d949aSAndrew Thompson #include <sys/priv.h> 113ed6d949aSAndrew Thompson 1143671d9d8SAndrew Thompson #include "usbdevs.h" 1153671d9d8SAndrew Thompson #include <dev/usb/usb.h> 116ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 117ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 1183671d9d8SAndrew Thompson 119a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 1203671d9d8SAndrew Thompson #include <dev/usb/usb_debug.h> 1213671d9d8SAndrew Thompson #include <dev/usb/usb_busdma.h> 1223671d9d8SAndrew Thompson 1233671d9d8SAndrew Thompson #include <sys/mbuf.h> 1243671d9d8SAndrew Thompson #include <sys/taskqueue.h> 1253671d9d8SAndrew Thompson 1263671d9d8SAndrew Thompson #include <netgraph/ng_message.h> 1273671d9d8SAndrew Thompson #include <netgraph/netgraph.h> 1283671d9d8SAndrew Thompson #include <netgraph/ng_parse.h> 1293671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h> 1303671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_hci.h> 1313671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_ubt.h> 13206079f14SAndrew Thompson #include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h> 1333671d9d8SAndrew Thompson 1343671d9d8SAndrew Thompson static int ubt_modevent(module_t, int, void *); 1353671d9d8SAndrew Thompson static device_probe_t ubt_probe; 1363671d9d8SAndrew Thompson static device_attach_t ubt_attach; 1373671d9d8SAndrew Thompson static device_detach_t ubt_detach; 1383671d9d8SAndrew Thompson 1393671d9d8SAndrew Thompson static void ubt_task_schedule(ubt_softc_p, int); 1403671d9d8SAndrew Thompson static task_fn_t ubt_task; 1413671d9d8SAndrew Thompson 142a593f6b8SAndrew Thompson #define ubt_xfer_start(sc, i) usbd_transfer_start((sc)->sc_xfer[(i)]) 1433671d9d8SAndrew Thompson 1443671d9d8SAndrew Thompson /* Netgraph methods */ 1453671d9d8SAndrew Thompson static ng_constructor_t ng_ubt_constructor; 1463671d9d8SAndrew Thompson static ng_shutdown_t ng_ubt_shutdown; 1473671d9d8SAndrew Thompson static ng_newhook_t ng_ubt_newhook; 1483671d9d8SAndrew Thompson static ng_connect_t ng_ubt_connect; 1493671d9d8SAndrew Thompson static ng_disconnect_t ng_ubt_disconnect; 1503671d9d8SAndrew Thompson static ng_rcvmsg_t ng_ubt_rcvmsg; 1513671d9d8SAndrew Thompson static ng_rcvdata_t ng_ubt_rcvdata; 1523671d9d8SAndrew Thompson 1533671d9d8SAndrew Thompson /* Queue length */ 1543671d9d8SAndrew Thompson static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = 1553671d9d8SAndrew Thompson { 1563671d9d8SAndrew Thompson { "queue", &ng_parse_int32_type, }, 1573671d9d8SAndrew Thompson { "qlen", &ng_parse_int32_type, }, 1583671d9d8SAndrew Thompson { NULL, } 1593671d9d8SAndrew Thompson }; 1603671d9d8SAndrew Thompson static const struct ng_parse_type ng_ubt_node_qlen_type = 1613671d9d8SAndrew Thompson { 1623671d9d8SAndrew Thompson &ng_parse_struct_type, 1633671d9d8SAndrew Thompson &ng_ubt_node_qlen_type_fields 1643671d9d8SAndrew Thompson }; 1653671d9d8SAndrew Thompson 1663671d9d8SAndrew Thompson /* Stat info */ 1673671d9d8SAndrew Thompson static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = 1683671d9d8SAndrew Thompson { 1693671d9d8SAndrew Thompson { "pckts_recv", &ng_parse_uint32_type, }, 1703671d9d8SAndrew Thompson { "bytes_recv", &ng_parse_uint32_type, }, 1713671d9d8SAndrew Thompson { "pckts_sent", &ng_parse_uint32_type, }, 1723671d9d8SAndrew Thompson { "bytes_sent", &ng_parse_uint32_type, }, 1733671d9d8SAndrew Thompson { "oerrors", &ng_parse_uint32_type, }, 1743671d9d8SAndrew Thompson { "ierrors", &ng_parse_uint32_type, }, 1753671d9d8SAndrew Thompson { NULL, } 1763671d9d8SAndrew Thompson }; 1773671d9d8SAndrew Thompson static const struct ng_parse_type ng_ubt_node_stat_type = 1783671d9d8SAndrew Thompson { 1793671d9d8SAndrew Thompson &ng_parse_struct_type, 1803671d9d8SAndrew Thompson &ng_ubt_node_stat_type_fields 1813671d9d8SAndrew Thompson }; 1823671d9d8SAndrew Thompson 1833671d9d8SAndrew Thompson /* Netgraph node command list */ 1843671d9d8SAndrew Thompson static const struct ng_cmdlist ng_ubt_cmdlist[] = 1853671d9d8SAndrew Thompson { 1863671d9d8SAndrew Thompson { 1873671d9d8SAndrew Thompson NGM_UBT_COOKIE, 1883671d9d8SAndrew Thompson NGM_UBT_NODE_SET_DEBUG, 1893671d9d8SAndrew Thompson "set_debug", 1903671d9d8SAndrew Thompson &ng_parse_uint16_type, 1913671d9d8SAndrew Thompson NULL 1923671d9d8SAndrew Thompson }, 1933671d9d8SAndrew Thompson { 1943671d9d8SAndrew Thompson NGM_UBT_COOKIE, 1953671d9d8SAndrew Thompson NGM_UBT_NODE_GET_DEBUG, 1963671d9d8SAndrew Thompson "get_debug", 1973671d9d8SAndrew Thompson NULL, 1983671d9d8SAndrew Thompson &ng_parse_uint16_type 1993671d9d8SAndrew Thompson }, 2003671d9d8SAndrew Thompson { 2013671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2023671d9d8SAndrew Thompson NGM_UBT_NODE_SET_QLEN, 2033671d9d8SAndrew Thompson "set_qlen", 2043671d9d8SAndrew Thompson &ng_ubt_node_qlen_type, 2053671d9d8SAndrew Thompson NULL 2063671d9d8SAndrew Thompson }, 2073671d9d8SAndrew Thompson { 2083671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2093671d9d8SAndrew Thompson NGM_UBT_NODE_GET_QLEN, 2103671d9d8SAndrew Thompson "get_qlen", 2113671d9d8SAndrew Thompson &ng_ubt_node_qlen_type, 2123671d9d8SAndrew Thompson &ng_ubt_node_qlen_type 2133671d9d8SAndrew Thompson }, 2143671d9d8SAndrew Thompson { 2153671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2163671d9d8SAndrew Thompson NGM_UBT_NODE_GET_STAT, 2173671d9d8SAndrew Thompson "get_stat", 2183671d9d8SAndrew Thompson NULL, 2193671d9d8SAndrew Thompson &ng_ubt_node_stat_type 2203671d9d8SAndrew Thompson }, 2213671d9d8SAndrew Thompson { 2223671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2233671d9d8SAndrew Thompson NGM_UBT_NODE_RESET_STAT, 2243671d9d8SAndrew Thompson "reset_stat", 2253671d9d8SAndrew Thompson NULL, 2263671d9d8SAndrew Thompson NULL 2273671d9d8SAndrew Thompson }, 2283671d9d8SAndrew Thompson { 0, } 2293671d9d8SAndrew Thompson }; 2303671d9d8SAndrew Thompson 2313671d9d8SAndrew Thompson /* Netgraph node type */ 2323671d9d8SAndrew Thompson static struct ng_type typestruct = 2333671d9d8SAndrew Thompson { 2343671d9d8SAndrew Thompson .version = NG_ABI_VERSION, 2353671d9d8SAndrew Thompson .name = NG_UBT_NODE_TYPE, 2363671d9d8SAndrew Thompson .constructor = ng_ubt_constructor, 2373671d9d8SAndrew Thompson .rcvmsg = ng_ubt_rcvmsg, 2383671d9d8SAndrew Thompson .shutdown = ng_ubt_shutdown, 2393671d9d8SAndrew Thompson .newhook = ng_ubt_newhook, 2403671d9d8SAndrew Thompson .connect = ng_ubt_connect, 2413671d9d8SAndrew Thompson .rcvdata = ng_ubt_rcvdata, 2423671d9d8SAndrew Thompson .disconnect = ng_ubt_disconnect, 2433671d9d8SAndrew Thompson .cmdlist = ng_ubt_cmdlist 2443671d9d8SAndrew Thompson }; 2453671d9d8SAndrew Thompson 2463671d9d8SAndrew Thompson /**************************************************************************** 2473671d9d8SAndrew Thompson **************************************************************************** 2483671d9d8SAndrew Thompson ** USB specific 2493671d9d8SAndrew Thompson **************************************************************************** 2503671d9d8SAndrew Thompson ****************************************************************************/ 2513671d9d8SAndrew Thompson 2523671d9d8SAndrew Thompson /* USB methods */ 253e0a69b51SAndrew Thompson static usb_callback_t ubt_ctrl_write_callback; 254e0a69b51SAndrew Thompson static usb_callback_t ubt_intr_read_callback; 255e0a69b51SAndrew Thompson static usb_callback_t ubt_bulk_read_callback; 256e0a69b51SAndrew Thompson static usb_callback_t ubt_bulk_write_callback; 257e0a69b51SAndrew Thompson static usb_callback_t ubt_isoc_read_callback; 258e0a69b51SAndrew Thompson static usb_callback_t ubt_isoc_write_callback; 2593671d9d8SAndrew Thompson 2603671d9d8SAndrew Thompson static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **); 261760bc48eSAndrew Thompson static int ubt_isoc_read_one_frame(struct usb_xfer *, int); 2623671d9d8SAndrew Thompson 2633671d9d8SAndrew Thompson /* 2643671d9d8SAndrew Thompson * USB config 2653671d9d8SAndrew Thompson * 2663671d9d8SAndrew Thompson * The following desribes usb transfers that could be submitted on USB device. 2673671d9d8SAndrew Thompson * 2683671d9d8SAndrew Thompson * Interface 0 on the USB device must present the following endpoints 2693671d9d8SAndrew Thompson * 1) Interrupt endpoint to receive HCI events 2703671d9d8SAndrew Thompson * 2) Bulk IN endpoint to receive ACL data 2713671d9d8SAndrew Thompson * 3) Bulk OUT endpoint to send ACL data 2723671d9d8SAndrew Thompson * 2733671d9d8SAndrew Thompson * Interface 1 on the USB device must present the following endpoints 2743671d9d8SAndrew Thompson * 1) Isochronous IN endpoint to receive SCO data 2753671d9d8SAndrew Thompson * 2) Isochronous OUT endpoint to send SCO data 2763671d9d8SAndrew Thompson */ 2773671d9d8SAndrew Thompson 278760bc48eSAndrew Thompson static const struct usb_config ubt_config[UBT_N_TRANSFER] = 2793671d9d8SAndrew Thompson { 2803671d9d8SAndrew Thompson /* 2813671d9d8SAndrew Thompson * Interface #0 2823671d9d8SAndrew Thompson */ 2833671d9d8SAndrew Thompson 2843671d9d8SAndrew Thompson /* Outgoing bulk transfer - ACL packets */ 2853671d9d8SAndrew Thompson [UBT_IF_0_BULK_DT_WR] = { 2863671d9d8SAndrew Thompson .type = UE_BULK, 2873671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 2883671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 2893671d9d8SAndrew Thompson .if_index = 0, 2903671d9d8SAndrew Thompson .bufsize = UBT_BULK_WRITE_BUFFER_SIZE, 2913671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .force_short_xfer = 1, }, 2923671d9d8SAndrew Thompson .callback = &ubt_bulk_write_callback, 2933671d9d8SAndrew Thompson }, 2943671d9d8SAndrew Thompson /* Incoming bulk transfer - ACL packets */ 2953671d9d8SAndrew Thompson [UBT_IF_0_BULK_DT_RD] = { 2963671d9d8SAndrew Thompson .type = UE_BULK, 2973671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 2983671d9d8SAndrew Thompson .direction = UE_DIR_IN, 2993671d9d8SAndrew Thompson .if_index = 0, 3003671d9d8SAndrew Thompson .bufsize = UBT_BULK_READ_BUFFER_SIZE, 3013671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 3023671d9d8SAndrew Thompson .callback = &ubt_bulk_read_callback, 3033671d9d8SAndrew Thompson }, 3043671d9d8SAndrew Thompson /* Incoming interrupt transfer - HCI events */ 3053671d9d8SAndrew Thompson [UBT_IF_0_INTR_DT_RD] = { 3063671d9d8SAndrew Thompson .type = UE_INTERRUPT, 3073671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3083671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3093671d9d8SAndrew Thompson .if_index = 0, 3103671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 3113671d9d8SAndrew Thompson .bufsize = UBT_INTR_BUFFER_SIZE, 3123671d9d8SAndrew Thompson .callback = &ubt_intr_read_callback, 3133671d9d8SAndrew Thompson }, 3143671d9d8SAndrew Thompson /* Outgoing control transfer - HCI commands */ 3153671d9d8SAndrew Thompson [UBT_IF_0_CTRL_DT_WR] = { 3163671d9d8SAndrew Thompson .type = UE_CONTROL, 3173671d9d8SAndrew Thompson .endpoint = 0x00, /* control pipe */ 3183671d9d8SAndrew Thompson .direction = UE_DIR_ANY, 3193671d9d8SAndrew Thompson .if_index = 0, 3203671d9d8SAndrew Thompson .bufsize = UBT_CTRL_BUFFER_SIZE, 3213671d9d8SAndrew Thompson .callback = &ubt_ctrl_write_callback, 3223671d9d8SAndrew Thompson .timeout = 5000, /* 5 seconds */ 3233671d9d8SAndrew Thompson }, 3243671d9d8SAndrew Thompson 3253671d9d8SAndrew Thompson /* 3263671d9d8SAndrew Thompson * Interface #1 3273671d9d8SAndrew Thompson */ 3283671d9d8SAndrew Thompson 3293671d9d8SAndrew Thompson /* Incoming isochronous transfer #1 - SCO packets */ 3303671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_RD1] = { 3313671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3323671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3333671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3343671d9d8SAndrew Thompson .if_index = 1, 3353671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3363671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3373671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3383671d9d8SAndrew Thompson .callback = &ubt_isoc_read_callback, 3393671d9d8SAndrew Thompson }, 3403671d9d8SAndrew Thompson /* Incoming isochronous transfer #2 - SCO packets */ 3413671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_RD2] = { 3423671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3433671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3443671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3453671d9d8SAndrew Thompson .if_index = 1, 3463671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3473671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3483671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3493671d9d8SAndrew Thompson .callback = &ubt_isoc_read_callback, 3503671d9d8SAndrew Thompson }, 3513671d9d8SAndrew Thompson /* Outgoing isochronous transfer #1 - SCO packets */ 3523671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_WR1] = { 3533671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3543671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3553671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 3563671d9d8SAndrew Thompson .if_index = 1, 3573671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3583671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3593671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3603671d9d8SAndrew Thompson .callback = &ubt_isoc_write_callback, 3613671d9d8SAndrew Thompson }, 3623671d9d8SAndrew Thompson /* Outgoing isochronous transfer #2 - SCO packets */ 3633671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_WR2] = { 3643671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3653671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3663671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 3673671d9d8SAndrew Thompson .if_index = 1, 3683671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3693671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3703671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3713671d9d8SAndrew Thompson .callback = &ubt_isoc_write_callback, 3723671d9d8SAndrew Thompson }, 3733671d9d8SAndrew Thompson }; 3743671d9d8SAndrew Thompson 3753671d9d8SAndrew Thompson /* 3763671d9d8SAndrew Thompson * If for some reason device should not be attached then put 3773671d9d8SAndrew Thompson * VendorID/ProductID pair into the list below. The format is 3783671d9d8SAndrew Thompson * as follows: 3793671d9d8SAndrew Thompson * 3803671d9d8SAndrew Thompson * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, 3813671d9d8SAndrew Thompson * 3823671d9d8SAndrew Thompson * where VENDOR_ID and PRODUCT_ID are hex numbers. 3833671d9d8SAndrew Thompson */ 3843671d9d8SAndrew Thompson 385f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_ignore_devs[] = 3863671d9d8SAndrew Thompson { 3873671d9d8SAndrew Thompson /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */ 3883671d9d8SAndrew Thompson { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) }, 3893671d9d8SAndrew Thompson }; 3903671d9d8SAndrew Thompson 3913671d9d8SAndrew Thompson /* List of supported bluetooth devices */ 392f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_devs[] = 3933671d9d8SAndrew Thompson { 3943671d9d8SAndrew Thompson /* Generic Bluetooth class devices */ 3953671d9d8SAndrew Thompson { USB_IFACE_CLASS(UDCLASS_WIRELESS), 3963671d9d8SAndrew Thompson USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 3973671d9d8SAndrew Thompson USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 3983671d9d8SAndrew Thompson 3993671d9d8SAndrew Thompson /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ 4003671d9d8SAndrew Thompson { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) }, 40170a0e340SGleb Smirnoff 40270a0e340SGleb Smirnoff /* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */ 40370a0e340SGleb Smirnoff { USB_VENDOR(USB_VENDOR_BROADCOM), 40470a0e340SGleb Smirnoff USB_IF_CSI(UICLASS_VENDOR, 0x01, 0x01) }, 4053671d9d8SAndrew Thompson }; 4063671d9d8SAndrew Thompson 4073671d9d8SAndrew Thompson /* 4083671d9d8SAndrew Thompson * Probe for a USB Bluetooth device. 4093671d9d8SAndrew Thompson * USB context. 4103671d9d8SAndrew Thompson */ 4113671d9d8SAndrew Thompson 4123671d9d8SAndrew Thompson static int 4133671d9d8SAndrew Thompson ubt_probe(device_t dev) 4143671d9d8SAndrew Thompson { 415760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 416a5db8fd1SAndriy Gapon int error; 4173671d9d8SAndrew Thompson 4183671d9d8SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) 4193671d9d8SAndrew Thompson return (ENXIO); 4203671d9d8SAndrew Thompson 4213671d9d8SAndrew Thompson if (uaa->info.bIfaceIndex != 0) 4223671d9d8SAndrew Thompson return (ENXIO); 4233671d9d8SAndrew Thompson 424a593f6b8SAndrew Thompson if (usbd_lookup_id_by_uaa(ubt_ignore_devs, 4253671d9d8SAndrew Thompson sizeof(ubt_ignore_devs), uaa) == 0) 4263671d9d8SAndrew Thompson return (ENXIO); 4273671d9d8SAndrew Thompson 428a5db8fd1SAndriy Gapon error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa); 429a5db8fd1SAndriy Gapon if (error == 0) 430a5db8fd1SAndriy Gapon return (BUS_PROBE_GENERIC); 431a5db8fd1SAndriy Gapon return (error); 4323671d9d8SAndrew Thompson } /* ubt_probe */ 4333671d9d8SAndrew Thompson 4343671d9d8SAndrew Thompson /* 4353671d9d8SAndrew Thompson * Attach the device. 4363671d9d8SAndrew Thompson * USB context. 4373671d9d8SAndrew Thompson */ 4383671d9d8SAndrew Thompson 4393671d9d8SAndrew Thompson static int 4403671d9d8SAndrew Thompson ubt_attach(device_t dev) 4413671d9d8SAndrew Thompson { 442760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 4433671d9d8SAndrew Thompson struct ubt_softc *sc = device_get_softc(dev); 444760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 445760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 44612b16d85SHans Petter Selasky struct usb_interface *iface; 4473671d9d8SAndrew Thompson uint16_t wMaxPacketSize; 4483671d9d8SAndrew Thompson uint8_t alt_index, i, j; 4493671d9d8SAndrew Thompson uint8_t iface_index[2] = { 0, 1 }; 4503671d9d8SAndrew Thompson 451a593f6b8SAndrew Thompson device_set_usb_desc(dev); 4523671d9d8SAndrew Thompson 4533671d9d8SAndrew Thompson sc->sc_dev = dev; 4543671d9d8SAndrew Thompson sc->sc_debug = NG_UBT_WARN_LEVEL; 4553671d9d8SAndrew Thompson 4563671d9d8SAndrew Thompson /* 4573671d9d8SAndrew Thompson * Create Netgraph node 4583671d9d8SAndrew Thompson */ 4593671d9d8SAndrew Thompson 4603671d9d8SAndrew Thompson if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { 4613671d9d8SAndrew Thompson UBT_ALERT(sc, "could not create Netgraph node\n"); 4623671d9d8SAndrew Thompson return (ENXIO); 4633671d9d8SAndrew Thompson } 4643671d9d8SAndrew Thompson 4653671d9d8SAndrew Thompson /* Name Netgraph node */ 4663671d9d8SAndrew Thompson if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { 4673671d9d8SAndrew Thompson UBT_ALERT(sc, "could not name Netgraph node\n"); 4683671d9d8SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 4693671d9d8SAndrew Thompson return (ENXIO); 4703671d9d8SAndrew Thompson } 4713671d9d8SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 4723671d9d8SAndrew Thompson NG_NODE_FORCE_WRITER(sc->sc_node); 4733671d9d8SAndrew Thompson 4743671d9d8SAndrew Thompson /* 4753671d9d8SAndrew Thompson * Initialize device softc structure 4763671d9d8SAndrew Thompson */ 4773671d9d8SAndrew Thompson 4783671d9d8SAndrew Thompson /* initialize locks */ 4793671d9d8SAndrew Thompson mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); 4803671d9d8SAndrew Thompson mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); 4813671d9d8SAndrew Thompson 4823671d9d8SAndrew Thompson /* initialize packet queues */ 4833671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); 4843671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); 4853671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); 4863671d9d8SAndrew Thompson 4873671d9d8SAndrew Thompson /* initialize glue task */ 4883671d9d8SAndrew Thompson TASK_INIT(&sc->sc_task, 0, ubt_task, sc); 4893671d9d8SAndrew Thompson 4903671d9d8SAndrew Thompson /* 4913671d9d8SAndrew Thompson * Configure Bluetooth USB device. Discover all required USB 4923671d9d8SAndrew Thompson * interfaces and endpoints. 4933671d9d8SAndrew Thompson * 4943671d9d8SAndrew Thompson * USB device must present two interfaces: 4953671d9d8SAndrew Thompson * 1) Interface 0 that has 3 endpoints 4963671d9d8SAndrew Thompson * 1) Interrupt endpoint to receive HCI events 4973671d9d8SAndrew Thompson * 2) Bulk IN endpoint to receive ACL data 4983671d9d8SAndrew Thompson * 3) Bulk OUT endpoint to send ACL data 4993671d9d8SAndrew Thompson * 5003671d9d8SAndrew Thompson * 2) Interface 1 then has 2 endpoints 5013671d9d8SAndrew Thompson * 1) Isochronous IN endpoint to receive SCO data 5023671d9d8SAndrew Thompson * 2) Isochronous OUT endpoint to send SCO data 5033671d9d8SAndrew Thompson * 5043671d9d8SAndrew Thompson * Interface 1 (with isochronous endpoints) has several alternate 5053671d9d8SAndrew Thompson * configurations with different packet size. 5063671d9d8SAndrew Thompson */ 5073671d9d8SAndrew Thompson 5083671d9d8SAndrew Thompson /* 5093671d9d8SAndrew Thompson * For interface #1 search alternate settings, and find 5103671d9d8SAndrew Thompson * the descriptor with the largest wMaxPacketSize 5113671d9d8SAndrew Thompson */ 5123671d9d8SAndrew Thompson 5133671d9d8SAndrew Thompson wMaxPacketSize = 0; 5143671d9d8SAndrew Thompson alt_index = 0; 5153671d9d8SAndrew Thompson i = 0; 5163671d9d8SAndrew Thompson j = 0; 5173671d9d8SAndrew Thompson ed = NULL; 5183671d9d8SAndrew Thompson 5193671d9d8SAndrew Thompson /* 5203671d9d8SAndrew Thompson * Search through all the descriptors looking for the largest 5213671d9d8SAndrew Thompson * packet size: 5223671d9d8SAndrew Thompson */ 523a593f6b8SAndrew Thompson while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach( 524a593f6b8SAndrew Thompson usbd_get_config_descriptor(uaa->device), 525760bc48eSAndrew Thompson (struct usb_descriptor *)ed))) { 5263671d9d8SAndrew Thompson 5273671d9d8SAndrew Thompson if ((ed->bDescriptorType == UDESC_INTERFACE) && 5283671d9d8SAndrew Thompson (ed->bLength >= sizeof(*id))) { 529760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)ed; 5303671d9d8SAndrew Thompson i = id->bInterfaceNumber; 5313671d9d8SAndrew Thompson j = id->bAlternateSetting; 5323671d9d8SAndrew Thompson } 5333671d9d8SAndrew Thompson 5343671d9d8SAndrew Thompson if ((ed->bDescriptorType == UDESC_ENDPOINT) && 5353671d9d8SAndrew Thompson (ed->bLength >= sizeof(*ed)) && 5363671d9d8SAndrew Thompson (i == 1)) { 5373671d9d8SAndrew Thompson uint16_t temp; 5383671d9d8SAndrew Thompson 5393671d9d8SAndrew Thompson temp = UGETW(ed->wMaxPacketSize); 5403671d9d8SAndrew Thompson if (temp > wMaxPacketSize) { 5413671d9d8SAndrew Thompson wMaxPacketSize = temp; 5423671d9d8SAndrew Thompson alt_index = j; 5433671d9d8SAndrew Thompson } 5443671d9d8SAndrew Thompson } 5453671d9d8SAndrew Thompson } 5463671d9d8SAndrew Thompson 5473671d9d8SAndrew Thompson /* Set alt configuration on interface #1 only if we found it */ 5483671d9d8SAndrew Thompson if (wMaxPacketSize > 0 && 549a593f6b8SAndrew Thompson usbd_set_alt_interface_index(uaa->device, 1, alt_index)) { 5503671d9d8SAndrew Thompson UBT_ALERT(sc, "could not set alternate setting %d " \ 5513671d9d8SAndrew Thompson "for interface 1!\n", alt_index); 5523671d9d8SAndrew Thompson goto detach; 5533671d9d8SAndrew Thompson } 5543671d9d8SAndrew Thompson 5553671d9d8SAndrew Thompson /* Setup transfers for both interfaces */ 556a593f6b8SAndrew Thompson if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, 5573671d9d8SAndrew Thompson ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { 5583671d9d8SAndrew Thompson UBT_ALERT(sc, "could not allocate transfers\n"); 5593671d9d8SAndrew Thompson goto detach; 5603671d9d8SAndrew Thompson } 5613671d9d8SAndrew Thompson 56212b16d85SHans Petter Selasky /* Claim all interfaces belonging to the Bluetooth part */ 56312b16d85SHans Petter Selasky for (i = 1;; i++) { 56412b16d85SHans Petter Selasky iface = usbd_get_iface(uaa->device, i); 56512b16d85SHans Petter Selasky if (iface == NULL) 56612b16d85SHans Petter Selasky break; 56712b16d85SHans Petter Selasky id = usbd_get_interface_descriptor(iface); 5683671d9d8SAndrew Thompson 56912b16d85SHans Petter Selasky if ((id != NULL) && 57012b16d85SHans Petter Selasky (id->bInterfaceClass == UICLASS_WIRELESS) && 57112b16d85SHans Petter Selasky (id->bInterfaceSubClass == UISUBCLASS_RF) && 57212b16d85SHans Petter Selasky (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) { 57312b16d85SHans Petter Selasky usbd_set_parent_iface(uaa->device, i, 57412b16d85SHans Petter Selasky uaa->info.bIfaceIndex); 57512b16d85SHans Petter Selasky } 57612b16d85SHans Petter Selasky } 5773671d9d8SAndrew Thompson return (0); /* success */ 5783671d9d8SAndrew Thompson 5793671d9d8SAndrew Thompson detach: 5803671d9d8SAndrew Thompson ubt_detach(dev); 5813671d9d8SAndrew Thompson 5823671d9d8SAndrew Thompson return (ENXIO); 5833671d9d8SAndrew Thompson } /* ubt_attach */ 5843671d9d8SAndrew Thompson 5853671d9d8SAndrew Thompson /* 5863671d9d8SAndrew Thompson * Detach the device. 5873671d9d8SAndrew Thompson * USB context. 5883671d9d8SAndrew Thompson */ 5893671d9d8SAndrew Thompson 5903671d9d8SAndrew Thompson int 5913671d9d8SAndrew Thompson ubt_detach(device_t dev) 5923671d9d8SAndrew Thompson { 5933671d9d8SAndrew Thompson struct ubt_softc *sc = device_get_softc(dev); 5943671d9d8SAndrew Thompson node_p node = sc->sc_node; 5953671d9d8SAndrew Thompson 5963671d9d8SAndrew Thompson /* Destroy Netgraph node */ 5973671d9d8SAndrew Thompson if (node != NULL) { 5983671d9d8SAndrew Thompson sc->sc_node = NULL; 5993671d9d8SAndrew Thompson NG_NODE_REALLY_DIE(node); 6003671d9d8SAndrew Thompson ng_rmnode_self(node); 6013671d9d8SAndrew Thompson } 6023671d9d8SAndrew Thompson 6033671d9d8SAndrew Thompson /* Make sure ubt_task in gone */ 6043671d9d8SAndrew Thompson taskqueue_drain(taskqueue_swi, &sc->sc_task); 6053671d9d8SAndrew Thompson 6063671d9d8SAndrew Thompson /* Free USB transfers, if any */ 607a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); 6083671d9d8SAndrew Thompson 6093671d9d8SAndrew Thompson /* Destroy queues */ 6103671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 6113671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); 6123671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); 6133671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); 6143671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 6153671d9d8SAndrew Thompson 6163671d9d8SAndrew Thompson mtx_destroy(&sc->sc_if_mtx); 6173671d9d8SAndrew Thompson mtx_destroy(&sc->sc_ng_mtx); 6183671d9d8SAndrew Thompson 6193671d9d8SAndrew Thompson return (0); 6203671d9d8SAndrew Thompson } /* ubt_detach */ 6213671d9d8SAndrew Thompson 6223671d9d8SAndrew Thompson /* 6233671d9d8SAndrew Thompson * Called when outgoing control request (HCI command) has completed, i.e. 6243671d9d8SAndrew Thompson * HCI command was sent to the device. 6253671d9d8SAndrew Thompson * USB context. 6263671d9d8SAndrew Thompson */ 6273671d9d8SAndrew Thompson 6283671d9d8SAndrew Thompson static void 629ed6d949aSAndrew Thompson ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 6303671d9d8SAndrew Thompson { 631ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 632760bc48eSAndrew Thompson struct usb_device_request req; 6333671d9d8SAndrew Thompson struct mbuf *m; 634ed6d949aSAndrew Thompson struct usb_page_cache *pc; 635ed6d949aSAndrew Thompson int actlen; 636ed6d949aSAndrew Thompson 637ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 6383671d9d8SAndrew Thompson 6393671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 6403671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 641ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen); 642ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 6433671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 6443671d9d8SAndrew Thompson /* FALLTHROUGH */ 6453671d9d8SAndrew Thompson 6463671d9d8SAndrew Thompson case USB_ST_SETUP: 6473671d9d8SAndrew Thompson send_next: 6483671d9d8SAndrew Thompson /* Get next command mbuf, if any */ 6493671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 6503671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 6513671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 6523671d9d8SAndrew Thompson 6533671d9d8SAndrew Thompson if (m == NULL) { 6543671d9d8SAndrew Thompson UBT_INFO(sc, "HCI command queue is empty\n"); 6553671d9d8SAndrew Thompson break; /* transfer complete */ 6563671d9d8SAndrew Thompson } 6573671d9d8SAndrew Thompson 6583671d9d8SAndrew Thompson /* Initialize a USB control request and then schedule it */ 6593671d9d8SAndrew Thompson bzero(&req, sizeof(req)); 6603671d9d8SAndrew Thompson req.bmRequestType = UBT_HCI_REQUEST; 6613671d9d8SAndrew Thompson USETW(req.wLength, m->m_pkthdr.len); 6623671d9d8SAndrew Thompson 6633671d9d8SAndrew Thompson UBT_INFO(sc, "Sending control request, " \ 6643671d9d8SAndrew Thompson "bmRequestType=0x%02x, wLength=%d\n", 6653671d9d8SAndrew Thompson req.bmRequestType, UGETW(req.wLength)); 6663671d9d8SAndrew Thompson 667ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 668ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, &req, sizeof(req)); 669ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 1); 670ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 6713671d9d8SAndrew Thompson 672ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 673ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len); 674ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, 2); 6753671d9d8SAndrew Thompson 6763671d9d8SAndrew Thompson NG_FREE_M(m); 6773671d9d8SAndrew Thompson 678a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 6793671d9d8SAndrew Thompson break; 6803671d9d8SAndrew Thompson 6813671d9d8SAndrew Thompson default: /* Error */ 682ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 6833671d9d8SAndrew Thompson UBT_WARN(sc, "control transfer failed: %s\n", 684ed6d949aSAndrew Thompson usbd_errstr(error)); 6853671d9d8SAndrew Thompson 6863671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 6873671d9d8SAndrew Thompson goto send_next; 6883671d9d8SAndrew Thompson } 6893671d9d8SAndrew Thompson 6903671d9d8SAndrew Thompson /* transfer cancelled */ 6913671d9d8SAndrew Thompson break; 6923671d9d8SAndrew Thompson } 6933671d9d8SAndrew Thompson } /* ubt_ctrl_write_callback */ 6943671d9d8SAndrew Thompson 6953671d9d8SAndrew Thompson /* 6963671d9d8SAndrew Thompson * Called when incoming interrupt transfer (HCI event) has completed, i.e. 6973671d9d8SAndrew Thompson * HCI event was received from the device. 6983671d9d8SAndrew Thompson * USB context. 6993671d9d8SAndrew Thompson */ 7003671d9d8SAndrew Thompson 7013671d9d8SAndrew Thompson static void 702ed6d949aSAndrew Thompson ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 7033671d9d8SAndrew Thompson { 704ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 7053671d9d8SAndrew Thompson struct mbuf *m; 7063671d9d8SAndrew Thompson ng_hci_event_pkt_t *hdr; 707ed6d949aSAndrew Thompson struct usb_page_cache *pc; 708ed6d949aSAndrew Thompson int actlen; 709ed6d949aSAndrew Thompson 710ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 7113671d9d8SAndrew Thompson 7123671d9d8SAndrew Thompson m = NULL; 7133671d9d8SAndrew Thompson 7143671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 7153671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 7163671d9d8SAndrew Thompson /* Allocate a new mbuf */ 717eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 7183671d9d8SAndrew Thompson if (m == NULL) { 7193671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 7203671d9d8SAndrew Thompson goto submit_next; 7213671d9d8SAndrew Thompson } 7223671d9d8SAndrew Thompson 723eb1b1807SGleb Smirnoff MCLGET(m, M_NOWAIT); 7243671d9d8SAndrew Thompson if (!(m->m_flags & M_EXT)) { 7253671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 7263671d9d8SAndrew Thompson goto submit_next; 7273671d9d8SAndrew Thompson } 7283671d9d8SAndrew Thompson 7293671d9d8SAndrew Thompson /* Add HCI packet type */ 7303671d9d8SAndrew Thompson *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; 7313671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = 1; 7323671d9d8SAndrew Thompson 733ed6d949aSAndrew Thompson if (actlen > MCLBYTES - 1) 734ed6d949aSAndrew Thompson actlen = MCLBYTES - 1; 7353671d9d8SAndrew Thompson 736ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 737ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 738ed6d949aSAndrew Thompson m->m_pkthdr.len += actlen; 739ed6d949aSAndrew Thompson m->m_len += actlen; 7403671d9d8SAndrew Thompson 7413671d9d8SAndrew Thompson UBT_INFO(sc, "got %d bytes from interrupt pipe\n", 742ed6d949aSAndrew Thompson actlen); 7433671d9d8SAndrew Thompson 7443671d9d8SAndrew Thompson /* Validate packet and send it up the stack */ 7456d917491SHans Petter Selasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 7463671d9d8SAndrew Thompson UBT_INFO(sc, "HCI event packet is too short\n"); 7473671d9d8SAndrew Thompson 7483671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 7493671d9d8SAndrew Thompson goto submit_next; 7503671d9d8SAndrew Thompson } 7513671d9d8SAndrew Thompson 7523671d9d8SAndrew Thompson hdr = mtod(m, ng_hci_event_pkt_t *); 7533671d9d8SAndrew Thompson if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { 7543671d9d8SAndrew Thompson UBT_ERR(sc, "Invalid HCI event packet size, " \ 7553671d9d8SAndrew Thompson "length=%d, pktlen=%d\n", 7563671d9d8SAndrew Thompson hdr->length, m->m_pkthdr.len); 7573671d9d8SAndrew Thompson 7583671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 7593671d9d8SAndrew Thompson goto submit_next; 7603671d9d8SAndrew Thompson } 7613671d9d8SAndrew Thompson 7623671d9d8SAndrew Thompson UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ 7633671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, hdr->length); 7643671d9d8SAndrew Thompson 7653671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 7663671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 7673671d9d8SAndrew Thompson 7683671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 7693671d9d8SAndrew Thompson /* m == NULL at this point */ 7703671d9d8SAndrew Thompson /* FALLTHROUGH */ 7713671d9d8SAndrew Thompson 7723671d9d8SAndrew Thompson case USB_ST_SETUP: 7733671d9d8SAndrew Thompson submit_next: 7743671d9d8SAndrew Thompson NG_FREE_M(m); /* checks for m != NULL */ 7753671d9d8SAndrew Thompson 776ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 777a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 7783671d9d8SAndrew Thompson break; 7793671d9d8SAndrew Thompson 7803671d9d8SAndrew Thompson default: /* Error */ 781ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 7823671d9d8SAndrew Thompson UBT_WARN(sc, "interrupt transfer failed: %s\n", 783ed6d949aSAndrew Thompson usbd_errstr(error)); 7843671d9d8SAndrew Thompson 7853671d9d8SAndrew Thompson /* Try to clear stall first */ 786ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 7873671d9d8SAndrew Thompson goto submit_next; 7883671d9d8SAndrew Thompson } 7893671d9d8SAndrew Thompson /* transfer cancelled */ 7903671d9d8SAndrew Thompson break; 7913671d9d8SAndrew Thompson } 7923671d9d8SAndrew Thompson } /* ubt_intr_read_callback */ 7933671d9d8SAndrew Thompson 7943671d9d8SAndrew Thompson /* 7953671d9d8SAndrew Thompson * Called when incoming bulk transfer (ACL packet) has completed, i.e. 7963671d9d8SAndrew Thompson * ACL packet was received from the device. 7973671d9d8SAndrew Thompson * USB context. 7983671d9d8SAndrew Thompson */ 7993671d9d8SAndrew Thompson 8003671d9d8SAndrew Thompson static void 801ed6d949aSAndrew Thompson ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 8023671d9d8SAndrew Thompson { 803ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 8043671d9d8SAndrew Thompson struct mbuf *m; 8053671d9d8SAndrew Thompson ng_hci_acldata_pkt_t *hdr; 806ed6d949aSAndrew Thompson struct usb_page_cache *pc; 8076d917491SHans Petter Selasky int len; 808ed6d949aSAndrew Thompson int actlen; 809ed6d949aSAndrew Thompson 810ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 8113671d9d8SAndrew Thompson 8123671d9d8SAndrew Thompson m = NULL; 8133671d9d8SAndrew Thompson 8143671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 8153671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 8163671d9d8SAndrew Thompson /* Allocate new mbuf */ 817eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 8183671d9d8SAndrew Thompson if (m == NULL) { 8193671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 8203671d9d8SAndrew Thompson goto submit_next; 8213671d9d8SAndrew Thompson } 8223671d9d8SAndrew Thompson 823eb1b1807SGleb Smirnoff MCLGET(m, M_NOWAIT); 8243671d9d8SAndrew Thompson if (!(m->m_flags & M_EXT)) { 8253671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 8263671d9d8SAndrew Thompson goto submit_next; 8273671d9d8SAndrew Thompson } 8283671d9d8SAndrew Thompson 8293671d9d8SAndrew Thompson /* Add HCI packet type */ 8303671d9d8SAndrew Thompson *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; 8313671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = 1; 8323671d9d8SAndrew Thompson 833ed6d949aSAndrew Thompson if (actlen > MCLBYTES - 1) 834ed6d949aSAndrew Thompson actlen = MCLBYTES - 1; 8353671d9d8SAndrew Thompson 836ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 837ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 838ed6d949aSAndrew Thompson m->m_pkthdr.len += actlen; 839ed6d949aSAndrew Thompson m->m_len += actlen; 8403671d9d8SAndrew Thompson 8413671d9d8SAndrew Thompson UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", 842ed6d949aSAndrew Thompson actlen); 8433671d9d8SAndrew Thompson 8443671d9d8SAndrew Thompson /* Validate packet and send it up the stack */ 8456d917491SHans Petter Selasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 8463671d9d8SAndrew Thompson UBT_INFO(sc, "HCI ACL packet is too short\n"); 8473671d9d8SAndrew Thompson 8483671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 8493671d9d8SAndrew Thompson goto submit_next; 8503671d9d8SAndrew Thompson } 8513671d9d8SAndrew Thompson 8523671d9d8SAndrew Thompson hdr = mtod(m, ng_hci_acldata_pkt_t *); 8533671d9d8SAndrew Thompson len = le16toh(hdr->length); 8546d917491SHans Petter Selasky if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) { 8553671d9d8SAndrew Thompson UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ 8563671d9d8SAndrew Thompson "pktlen=%d\n", len, m->m_pkthdr.len); 8573671d9d8SAndrew Thompson 8583671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 8593671d9d8SAndrew Thompson goto submit_next; 8603671d9d8SAndrew Thompson } 8613671d9d8SAndrew Thompson 8623671d9d8SAndrew Thompson UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ 8633671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, len); 8643671d9d8SAndrew Thompson 8653671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 8663671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 8673671d9d8SAndrew Thompson 8683671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 8693671d9d8SAndrew Thompson /* m == NULL at this point */ 8703671d9d8SAndrew Thompson /* FALLTHOUGH */ 8713671d9d8SAndrew Thompson 8723671d9d8SAndrew Thompson case USB_ST_SETUP: 8733671d9d8SAndrew Thompson submit_next: 8743671d9d8SAndrew Thompson NG_FREE_M(m); /* checks for m != NULL */ 8753671d9d8SAndrew Thompson 876ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 877a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 8783671d9d8SAndrew Thompson break; 8793671d9d8SAndrew Thompson 8803671d9d8SAndrew Thompson default: /* Error */ 881ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 8823671d9d8SAndrew Thompson UBT_WARN(sc, "bulk-in transfer failed: %s\n", 883ed6d949aSAndrew Thompson usbd_errstr(error)); 8843671d9d8SAndrew Thompson 8853671d9d8SAndrew Thompson /* Try to clear stall first */ 886ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 8873671d9d8SAndrew Thompson goto submit_next; 8883671d9d8SAndrew Thompson } 8893671d9d8SAndrew Thompson /* transfer cancelled */ 8903671d9d8SAndrew Thompson break; 8913671d9d8SAndrew Thompson } 8923671d9d8SAndrew Thompson } /* ubt_bulk_read_callback */ 8933671d9d8SAndrew Thompson 8943671d9d8SAndrew Thompson /* 8953671d9d8SAndrew Thompson * Called when outgoing bulk transfer (ACL packet) has completed, i.e. 8963671d9d8SAndrew Thompson * ACL packet was sent to the device. 8973671d9d8SAndrew Thompson * USB context. 8983671d9d8SAndrew Thompson */ 8993671d9d8SAndrew Thompson 9003671d9d8SAndrew Thompson static void 901ed6d949aSAndrew Thompson ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 9023671d9d8SAndrew Thompson { 903ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 9043671d9d8SAndrew Thompson struct mbuf *m; 905ed6d949aSAndrew Thompson struct usb_page_cache *pc; 906ed6d949aSAndrew Thompson int actlen; 907ed6d949aSAndrew Thompson 908ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 9093671d9d8SAndrew Thompson 9103671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 9113671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 912ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen); 913ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 9143671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 9153671d9d8SAndrew Thompson /* FALLTHROUGH */ 9163671d9d8SAndrew Thompson 9173671d9d8SAndrew Thompson case USB_ST_SETUP: 9183671d9d8SAndrew Thompson send_next: 9193671d9d8SAndrew Thompson /* Get next mbuf, if any */ 9203671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 9213671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); 9223671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 9233671d9d8SAndrew Thompson 9243671d9d8SAndrew Thompson if (m == NULL) { 9253671d9d8SAndrew Thompson UBT_INFO(sc, "ACL data queue is empty\n"); 9263671d9d8SAndrew Thompson break; /* transfer completed */ 9273671d9d8SAndrew Thompson } 9283671d9d8SAndrew Thompson 9293671d9d8SAndrew Thompson /* 9303671d9d8SAndrew Thompson * Copy ACL data frame back to a linear USB transfer buffer 9313671d9d8SAndrew Thompson * and schedule transfer 9323671d9d8SAndrew Thompson */ 9333671d9d8SAndrew Thompson 934ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 935ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 936ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 9373671d9d8SAndrew Thompson 9383671d9d8SAndrew Thompson UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", 9393671d9d8SAndrew Thompson m->m_pkthdr.len); 9403671d9d8SAndrew Thompson 9413671d9d8SAndrew Thompson NG_FREE_M(m); 9423671d9d8SAndrew Thompson 943a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 9443671d9d8SAndrew Thompson break; 9453671d9d8SAndrew Thompson 9463671d9d8SAndrew Thompson default: /* Error */ 947ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 9483671d9d8SAndrew Thompson UBT_WARN(sc, "bulk-out transfer failed: %s\n", 949ed6d949aSAndrew Thompson usbd_errstr(error)); 9503671d9d8SAndrew Thompson 9513671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 9523671d9d8SAndrew Thompson 9533671d9d8SAndrew Thompson /* try to clear stall first */ 954ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 9553671d9d8SAndrew Thompson goto send_next; 9563671d9d8SAndrew Thompson } 9573671d9d8SAndrew Thompson /* transfer cancelled */ 9583671d9d8SAndrew Thompson break; 9593671d9d8SAndrew Thompson } 9603671d9d8SAndrew Thompson } /* ubt_bulk_write_callback */ 9613671d9d8SAndrew Thompson 9623671d9d8SAndrew Thompson /* 9633671d9d8SAndrew Thompson * Called when incoming isoc transfer (SCO packet) has completed, i.e. 9643671d9d8SAndrew Thompson * SCO packet was received from the device. 9653671d9d8SAndrew Thompson * USB context. 9663671d9d8SAndrew Thompson */ 9673671d9d8SAndrew Thompson 9683671d9d8SAndrew Thompson static void 969ed6d949aSAndrew Thompson ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 9703671d9d8SAndrew Thompson { 971ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 9723671d9d8SAndrew Thompson int n; 973ed6d949aSAndrew Thompson int actlen, nframes; 974ed6d949aSAndrew Thompson 975ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 9763671d9d8SAndrew Thompson 9773671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 9783671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 979ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) 9803671d9d8SAndrew Thompson if (ubt_isoc_read_one_frame(xfer, n) < 0) 9813671d9d8SAndrew Thompson break; 9823671d9d8SAndrew Thompson /* FALLTHROUGH */ 9833671d9d8SAndrew Thompson 9843671d9d8SAndrew Thompson case USB_ST_SETUP: 9853671d9d8SAndrew Thompson read_next: 986ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) 987ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, 988ed6d949aSAndrew Thompson usbd_xfer_max_framelen(xfer)); 9893671d9d8SAndrew Thompson 990a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 9913671d9d8SAndrew Thompson break; 9923671d9d8SAndrew Thompson 9933671d9d8SAndrew Thompson default: /* Error */ 994ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 9953671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 9963671d9d8SAndrew Thompson goto read_next; 9973671d9d8SAndrew Thompson } 9983671d9d8SAndrew Thompson 9993671d9d8SAndrew Thompson /* transfer cancelled */ 10003671d9d8SAndrew Thompson break; 10013671d9d8SAndrew Thompson } 10023671d9d8SAndrew Thompson } /* ubt_isoc_read_callback */ 10033671d9d8SAndrew Thompson 10043671d9d8SAndrew Thompson /* 10053671d9d8SAndrew Thompson * Helper function. Called from ubt_isoc_read_callback() to read 10063671d9d8SAndrew Thompson * SCO data from one frame. 10073671d9d8SAndrew Thompson * USB context. 10083671d9d8SAndrew Thompson */ 10093671d9d8SAndrew Thompson 10103671d9d8SAndrew Thompson static int 1011760bc48eSAndrew Thompson ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no) 10123671d9d8SAndrew Thompson { 1013ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 1014ed6d949aSAndrew Thompson struct usb_page_cache *pc; 10153671d9d8SAndrew Thompson struct mbuf *m; 1016ed6d949aSAndrew Thompson int len, want, got, total; 10173671d9d8SAndrew Thompson 10183671d9d8SAndrew Thompson /* Get existing SCO reassembly buffer */ 1019ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 10203671d9d8SAndrew Thompson m = sc->sc_isoc_in_buffer; 10218f9e0ef9SAndrew Thompson total = usbd_xfer_frame_len(xfer, frame_no); 10223671d9d8SAndrew Thompson 10233671d9d8SAndrew Thompson /* While we have data in the frame */ 1024ed6d949aSAndrew Thompson while (total > 0) { 10253671d9d8SAndrew Thompson if (m == NULL) { 10263671d9d8SAndrew Thompson /* Start new reassembly buffer */ 1027eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 10283671d9d8SAndrew Thompson if (m == NULL) { 10293671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 10303671d9d8SAndrew Thompson return (-1); /* XXX out of sync! */ 10313671d9d8SAndrew Thompson } 10323671d9d8SAndrew Thompson 1033eb1b1807SGleb Smirnoff MCLGET(m, M_NOWAIT); 10343671d9d8SAndrew Thompson if (!(m->m_flags & M_EXT)) { 10353671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 10363671d9d8SAndrew Thompson NG_FREE_M(m); 10373671d9d8SAndrew Thompson return (-1); /* XXX out of sync! */ 10383671d9d8SAndrew Thompson } 10393671d9d8SAndrew Thompson 10403671d9d8SAndrew Thompson /* Expect SCO header */ 10413671d9d8SAndrew Thompson *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; 10423671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = got = 1; 10433671d9d8SAndrew Thompson want = sizeof(ng_hci_scodata_pkt_t); 10443671d9d8SAndrew Thompson } else { 10453671d9d8SAndrew Thompson /* 10463671d9d8SAndrew Thompson * Check if we have SCO header and if so 10473671d9d8SAndrew Thompson * adjust amount of data we want 10483671d9d8SAndrew Thompson */ 10493671d9d8SAndrew Thompson got = m->m_pkthdr.len; 10503671d9d8SAndrew Thompson want = sizeof(ng_hci_scodata_pkt_t); 10513671d9d8SAndrew Thompson 10523671d9d8SAndrew Thompson if (got >= want) 10533671d9d8SAndrew Thompson want += mtod(m, ng_hci_scodata_pkt_t *)->length; 10543671d9d8SAndrew Thompson } 10553671d9d8SAndrew Thompson 10563671d9d8SAndrew Thompson /* Append frame data to the SCO reassembly buffer */ 1057ed6d949aSAndrew Thompson len = total; 10583671d9d8SAndrew Thompson if (got + len > want) 10593671d9d8SAndrew Thompson len = want - got; 10603671d9d8SAndrew Thompson 1061ed6d949aSAndrew Thompson usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer), 10623671d9d8SAndrew Thompson mtod(m, uint8_t *) + m->m_pkthdr.len, len); 10633671d9d8SAndrew Thompson 10643671d9d8SAndrew Thompson m->m_pkthdr.len += len; 10653671d9d8SAndrew Thompson m->m_len += len; 1066ed6d949aSAndrew Thompson total -= len; 10673671d9d8SAndrew Thompson 10683671d9d8SAndrew Thompson /* Check if we got everything we wanted, if not - continue */ 10693671d9d8SAndrew Thompson if (got != want) 10703671d9d8SAndrew Thompson continue; 10713671d9d8SAndrew Thompson 10723671d9d8SAndrew Thompson /* If we got here then we got complete SCO frame */ 10733671d9d8SAndrew Thompson UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ 10743671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, 10753671d9d8SAndrew Thompson mtod(m, ng_hci_scodata_pkt_t *)->length); 10763671d9d8SAndrew Thompson 10773671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 10783671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 10793671d9d8SAndrew Thompson 10803671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 10813671d9d8SAndrew Thompson /* m == NULL at this point */ 10823671d9d8SAndrew Thompson } 10833671d9d8SAndrew Thompson 10843671d9d8SAndrew Thompson /* Put SCO reassembly buffer back */ 10853671d9d8SAndrew Thompson sc->sc_isoc_in_buffer = m; 10863671d9d8SAndrew Thompson 10873671d9d8SAndrew Thompson return (0); 10883671d9d8SAndrew Thompson } /* ubt_isoc_read_one_frame */ 10893671d9d8SAndrew Thompson 10903671d9d8SAndrew Thompson /* 10913671d9d8SAndrew Thompson * Called when outgoing isoc transfer (SCO packet) has completed, i.e. 10923671d9d8SAndrew Thompson * SCO packet was sent to the device. 10933671d9d8SAndrew Thompson * USB context. 10943671d9d8SAndrew Thompson */ 10953671d9d8SAndrew Thompson 10963671d9d8SAndrew Thompson static void 1097ed6d949aSAndrew Thompson ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 10983671d9d8SAndrew Thompson { 1099ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 1100ed6d949aSAndrew Thompson struct usb_page_cache *pc; 11013671d9d8SAndrew Thompson struct mbuf *m; 11023671d9d8SAndrew Thompson int n, space, offset; 1103ed6d949aSAndrew Thompson int actlen, nframes; 1104ed6d949aSAndrew Thompson 1105ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1106ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 11073671d9d8SAndrew Thompson 11083671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 11093671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 1110ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen); 1111ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 11123671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 11133671d9d8SAndrew Thompson /* FALLTHROUGH */ 11143671d9d8SAndrew Thompson 11153671d9d8SAndrew Thompson case USB_ST_SETUP: 11163671d9d8SAndrew Thompson send_next: 11173671d9d8SAndrew Thompson offset = 0; 1118ed6d949aSAndrew Thompson space = usbd_xfer_max_framelen(xfer) * nframes; 11193671d9d8SAndrew Thompson m = NULL; 11203671d9d8SAndrew Thompson 11213671d9d8SAndrew Thompson while (space > 0) { 11223671d9d8SAndrew Thompson if (m == NULL) { 11233671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 11243671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); 11253671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 11263671d9d8SAndrew Thompson 11273671d9d8SAndrew Thompson if (m == NULL) 11283671d9d8SAndrew Thompson break; 11293671d9d8SAndrew Thompson } 11303671d9d8SAndrew Thompson 11313671d9d8SAndrew Thompson n = min(space, m->m_pkthdr.len); 11323671d9d8SAndrew Thompson if (n > 0) { 1133ed6d949aSAndrew Thompson usbd_m_copy_in(pc, offset, m,0, n); 11343671d9d8SAndrew Thompson m_adj(m, n); 11353671d9d8SAndrew Thompson 11363671d9d8SAndrew Thompson offset += n; 11373671d9d8SAndrew Thompson space -= n; 11383671d9d8SAndrew Thompson } 11393671d9d8SAndrew Thompson 11403671d9d8SAndrew Thompson if (m->m_pkthdr.len == 0) 11413671d9d8SAndrew Thompson NG_FREE_M(m); /* sets m = NULL */ 11423671d9d8SAndrew Thompson } 11433671d9d8SAndrew Thompson 11443671d9d8SAndrew Thompson /* Put whatever is left from mbuf back on queue */ 11453671d9d8SAndrew Thompson if (m != NULL) { 11463671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 11473671d9d8SAndrew Thompson NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); 11483671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 11493671d9d8SAndrew Thompson } 11503671d9d8SAndrew Thompson 11513671d9d8SAndrew Thompson /* 11523671d9d8SAndrew Thompson * Calculate sizes for isoc frames. 11533671d9d8SAndrew Thompson * Note that offset could be 0 at this point (i.e. we have 11543671d9d8SAndrew Thompson * nothing to send). That is fine, as we have isoc. transfers 11553671d9d8SAndrew Thompson * going in both directions all the time. In this case it 11563671d9d8SAndrew Thompson * would be just empty isoc. transfer. 11573671d9d8SAndrew Thompson */ 11583671d9d8SAndrew Thompson 1159ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) { 1160ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, 1161ed6d949aSAndrew Thompson min(offset, usbd_xfer_max_framelen(xfer))); 11628f9e0ef9SAndrew Thompson offset -= usbd_xfer_frame_len(xfer, n); 11633671d9d8SAndrew Thompson } 11643671d9d8SAndrew Thompson 1165a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 11663671d9d8SAndrew Thompson break; 11673671d9d8SAndrew Thompson 11683671d9d8SAndrew Thompson default: /* Error */ 1169ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 11703671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 11713671d9d8SAndrew Thompson goto send_next; 11723671d9d8SAndrew Thompson } 11733671d9d8SAndrew Thompson 11743671d9d8SAndrew Thompson /* transfer cancelled */ 11753671d9d8SAndrew Thompson break; 11763671d9d8SAndrew Thompson } 11773671d9d8SAndrew Thompson } 11783671d9d8SAndrew Thompson 11793671d9d8SAndrew Thompson /* 11803671d9d8SAndrew Thompson * Utility function to forward provided mbuf upstream (i.e. up the stack). 11813671d9d8SAndrew Thompson * Modifies value of the mbuf pointer (sets it to NULL). 11823671d9d8SAndrew Thompson * Save to call from any context. 11833671d9d8SAndrew Thompson */ 11843671d9d8SAndrew Thompson 11853671d9d8SAndrew Thompson static int 11863671d9d8SAndrew Thompson ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) 11873671d9d8SAndrew Thompson { 11883671d9d8SAndrew Thompson hook_p hook; 11893671d9d8SAndrew Thompson int error; 11903671d9d8SAndrew Thompson 11913671d9d8SAndrew Thompson /* 11923671d9d8SAndrew Thompson * Close the race with Netgraph hook newhook/disconnect methods. 11933671d9d8SAndrew Thompson * Save the hook pointer atomically. Two cases are possible: 11943671d9d8SAndrew Thompson * 11953671d9d8SAndrew Thompson * 1) The hook pointer is NULL. It means disconnect method got 11963671d9d8SAndrew Thompson * there first. In this case we are done. 11973671d9d8SAndrew Thompson * 11983671d9d8SAndrew Thompson * 2) The hook pointer is not NULL. It means that hook pointer 11993671d9d8SAndrew Thompson * could be either in valid or invalid (i.e. in the process 12003671d9d8SAndrew Thompson * of disconnect) state. In any case grab an extra reference 12013671d9d8SAndrew Thompson * to protect the hook pointer. 12023671d9d8SAndrew Thompson * 12033671d9d8SAndrew Thompson * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as 12043671d9d8SAndrew Thompson * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). 12053671d9d8SAndrew Thompson */ 12063671d9d8SAndrew Thompson 12073671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 12083671d9d8SAndrew Thompson if ((hook = sc->sc_hook) != NULL) 12093671d9d8SAndrew Thompson NG_HOOK_REF(hook); 12103671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 12113671d9d8SAndrew Thompson 12123671d9d8SAndrew Thompson if (hook == NULL) { 12133671d9d8SAndrew Thompson NG_FREE_M(*m); 12143671d9d8SAndrew Thompson return (ENETDOWN); 12153671d9d8SAndrew Thompson } 12163671d9d8SAndrew Thompson 12173671d9d8SAndrew Thompson NG_SEND_DATA_ONLY(error, hook, *m); 12183671d9d8SAndrew Thompson NG_HOOK_UNREF(hook); 12193671d9d8SAndrew Thompson 12203671d9d8SAndrew Thompson if (error != 0) 12213671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 12223671d9d8SAndrew Thompson 12233671d9d8SAndrew Thompson return (error); 12243671d9d8SAndrew Thompson } /* ubt_fwd_mbuf_up */ 12253671d9d8SAndrew Thompson 12263671d9d8SAndrew Thompson /**************************************************************************** 12273671d9d8SAndrew Thompson **************************************************************************** 12283671d9d8SAndrew Thompson ** Glue 12293671d9d8SAndrew Thompson **************************************************************************** 12303671d9d8SAndrew Thompson ****************************************************************************/ 12313671d9d8SAndrew Thompson 12323671d9d8SAndrew Thompson /* 12333671d9d8SAndrew Thompson * Schedule glue task. Should be called with sc_ng_mtx held. 12343671d9d8SAndrew Thompson * Netgraph context. 12353671d9d8SAndrew Thompson */ 12363671d9d8SAndrew Thompson 12373671d9d8SAndrew Thompson static void 12383671d9d8SAndrew Thompson ubt_task_schedule(ubt_softc_p sc, int action) 12393671d9d8SAndrew Thompson { 12403671d9d8SAndrew Thompson mtx_assert(&sc->sc_ng_mtx, MA_OWNED); 12413671d9d8SAndrew Thompson 12423671d9d8SAndrew Thompson /* 12433671d9d8SAndrew Thompson * Try to handle corner case when "start all" and "stop all" 12443671d9d8SAndrew Thompson * actions can both be set before task is executed. 12453671d9d8SAndrew Thompson * 12463671d9d8SAndrew Thompson * The rules are 12473671d9d8SAndrew Thompson * 12483671d9d8SAndrew Thompson * sc_task_flags action new sc_task_flags 12493671d9d8SAndrew Thompson * ------------------------------------------------------ 12503671d9d8SAndrew Thompson * 0 start start 12513671d9d8SAndrew Thompson * 0 stop stop 12523671d9d8SAndrew Thompson * start start start 12533671d9d8SAndrew Thompson * start stop stop 12543671d9d8SAndrew Thompson * stop start stop|start 12553671d9d8SAndrew Thompson * stop stop stop 12563671d9d8SAndrew Thompson * stop|start start stop|start 12573671d9d8SAndrew Thompson * stop|start stop stop 12583671d9d8SAndrew Thompson */ 12593671d9d8SAndrew Thompson 12603671d9d8SAndrew Thompson if (action != 0) { 12613671d9d8SAndrew Thompson if ((action & UBT_FLAG_T_STOP_ALL) != 0) 12623671d9d8SAndrew Thompson sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; 12633671d9d8SAndrew Thompson 12643671d9d8SAndrew Thompson sc->sc_task_flags |= action; 12653671d9d8SAndrew Thompson } 12663671d9d8SAndrew Thompson 12673671d9d8SAndrew Thompson if (sc->sc_task_flags & UBT_FLAG_T_PENDING) 12683671d9d8SAndrew Thompson return; 12693671d9d8SAndrew Thompson 12703671d9d8SAndrew Thompson if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { 12713671d9d8SAndrew Thompson sc->sc_task_flags |= UBT_FLAG_T_PENDING; 12723671d9d8SAndrew Thompson return; 12733671d9d8SAndrew Thompson } 12743671d9d8SAndrew Thompson 12753671d9d8SAndrew Thompson /* XXX: i think this should never happen */ 12763671d9d8SAndrew Thompson } /* ubt_task_schedule */ 12773671d9d8SAndrew Thompson 12783671d9d8SAndrew Thompson /* 12793671d9d8SAndrew Thompson * Glue task. Examines sc_task_flags and does things depending on it. 12803671d9d8SAndrew Thompson * Taskqueue context. 12813671d9d8SAndrew Thompson */ 12823671d9d8SAndrew Thompson 12833671d9d8SAndrew Thompson static void 12843671d9d8SAndrew Thompson ubt_task(void *context, int pending) 12853671d9d8SAndrew Thompson { 12863671d9d8SAndrew Thompson ubt_softc_p sc = context; 12873671d9d8SAndrew Thompson int task_flags, i; 12883671d9d8SAndrew Thompson 12893671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 12903671d9d8SAndrew Thompson task_flags = sc->sc_task_flags; 12913671d9d8SAndrew Thompson sc->sc_task_flags = 0; 12923671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 12933671d9d8SAndrew Thompson 12943671d9d8SAndrew Thompson /* 12953671d9d8SAndrew Thompson * Stop all USB transfers synchronously. 12963671d9d8SAndrew Thompson * Stop interface #0 and #1 transfers at the same time and in the 1297a593f6b8SAndrew Thompson * same loop. usbd_transfer_drain() will do appropriate locking. 12983671d9d8SAndrew Thompson */ 12993671d9d8SAndrew Thompson 13003671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_STOP_ALL) 13013671d9d8SAndrew Thompson for (i = 0; i < UBT_N_TRANSFER; i ++) 1302a593f6b8SAndrew Thompson usbd_transfer_drain(sc->sc_xfer[i]); 13033671d9d8SAndrew Thompson 13043671d9d8SAndrew Thompson /* Start incoming interrupt and bulk, and all isoc. USB transfers */ 13053671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_ALL) { 13063671d9d8SAndrew Thompson /* 13073671d9d8SAndrew Thompson * Interface #0 13083671d9d8SAndrew Thompson */ 13093671d9d8SAndrew Thompson 13103671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 13113671d9d8SAndrew Thompson 13123671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); 13133671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); 13143671d9d8SAndrew Thompson 13153671d9d8SAndrew Thompson /* 13163671d9d8SAndrew Thompson * Interface #1 13173671d9d8SAndrew Thompson * Start both read and write isoc. transfers by default. 13183671d9d8SAndrew Thompson * Get them going all the time even if we have nothing 13193671d9d8SAndrew Thompson * to send to avoid any delays. 13203671d9d8SAndrew Thompson */ 13213671d9d8SAndrew Thompson 13223671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); 13233671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); 13243671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); 13253671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); 13263671d9d8SAndrew Thompson 13273671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 13283671d9d8SAndrew Thompson } 13293671d9d8SAndrew Thompson 13303671d9d8SAndrew Thompson /* Start outgoing control transfer */ 13313671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_CTRL) { 13323671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 13333671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); 13343671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 13353671d9d8SAndrew Thompson } 13363671d9d8SAndrew Thompson 13373671d9d8SAndrew Thompson /* Start outgoing bulk transfer */ 13383671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_BULK) { 13393671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 13403671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); 13413671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 13423671d9d8SAndrew Thompson } 13433671d9d8SAndrew Thompson } /* ubt_task */ 13443671d9d8SAndrew Thompson 13453671d9d8SAndrew Thompson /**************************************************************************** 13463671d9d8SAndrew Thompson **************************************************************************** 13473671d9d8SAndrew Thompson ** Netgraph specific 13483671d9d8SAndrew Thompson **************************************************************************** 13493671d9d8SAndrew Thompson ****************************************************************************/ 13503671d9d8SAndrew Thompson 13513671d9d8SAndrew Thompson /* 13523671d9d8SAndrew Thompson * Netgraph node constructor. Do not allow to create node of this type. 13533671d9d8SAndrew Thompson * Netgraph context. 13543671d9d8SAndrew Thompson */ 13553671d9d8SAndrew Thompson 13563671d9d8SAndrew Thompson static int 13573671d9d8SAndrew Thompson ng_ubt_constructor(node_p node) 13583671d9d8SAndrew Thompson { 13593671d9d8SAndrew Thompson return (EINVAL); 13603671d9d8SAndrew Thompson } /* ng_ubt_constructor */ 13613671d9d8SAndrew Thompson 13623671d9d8SAndrew Thompson /* 13633671d9d8SAndrew Thompson * Netgraph node destructor. Destroy node only when device has been detached. 13643671d9d8SAndrew Thompson * Netgraph context. 13653671d9d8SAndrew Thompson */ 13663671d9d8SAndrew Thompson 13673671d9d8SAndrew Thompson static int 13683671d9d8SAndrew Thompson ng_ubt_shutdown(node_p node) 13693671d9d8SAndrew Thompson { 13703671d9d8SAndrew Thompson if (node->nd_flags & NGF_REALLY_DIE) { 13713671d9d8SAndrew Thompson /* 13723671d9d8SAndrew Thompson * We came here because the USB device is being 13733671d9d8SAndrew Thompson * detached, so stop being persistant. 13743671d9d8SAndrew Thompson */ 13753671d9d8SAndrew Thompson NG_NODE_SET_PRIVATE(node, NULL); 13763671d9d8SAndrew Thompson NG_NODE_UNREF(node); 13773671d9d8SAndrew Thompson } else 13783671d9d8SAndrew Thompson NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ 13793671d9d8SAndrew Thompson 13803671d9d8SAndrew Thompson return (0); 13813671d9d8SAndrew Thompson } /* ng_ubt_shutdown */ 13823671d9d8SAndrew Thompson 13833671d9d8SAndrew Thompson /* 13843671d9d8SAndrew Thompson * Create new hook. There can only be one. 13853671d9d8SAndrew Thompson * Netgraph context. 13863671d9d8SAndrew Thompson */ 13873671d9d8SAndrew Thompson 13883671d9d8SAndrew Thompson static int 13893671d9d8SAndrew Thompson ng_ubt_newhook(node_p node, hook_p hook, char const *name) 13903671d9d8SAndrew Thompson { 13913671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(node); 13923671d9d8SAndrew Thompson 13933671d9d8SAndrew Thompson if (strcmp(name, NG_UBT_HOOK) != 0) 13943671d9d8SAndrew Thompson return (EINVAL); 13953671d9d8SAndrew Thompson 13963671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 13973671d9d8SAndrew Thompson if (sc->sc_hook != NULL) { 13983671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 13993671d9d8SAndrew Thompson 14003671d9d8SAndrew Thompson return (EISCONN); 14013671d9d8SAndrew Thompson } 14023671d9d8SAndrew Thompson 14033671d9d8SAndrew Thompson sc->sc_hook = hook; 14043671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14053671d9d8SAndrew Thompson 14063671d9d8SAndrew Thompson return (0); 14073671d9d8SAndrew Thompson } /* ng_ubt_newhook */ 14083671d9d8SAndrew Thompson 14093671d9d8SAndrew Thompson /* 14103671d9d8SAndrew Thompson * Connect hook. Start incoming USB transfers. 14113671d9d8SAndrew Thompson * Netgraph context. 14123671d9d8SAndrew Thompson */ 14133671d9d8SAndrew Thompson 14143671d9d8SAndrew Thompson static int 14153671d9d8SAndrew Thompson ng_ubt_connect(hook_p hook) 14163671d9d8SAndrew Thompson { 14173671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 14183671d9d8SAndrew Thompson 14193671d9d8SAndrew Thompson NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 14203671d9d8SAndrew Thompson 14213671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 14223671d9d8SAndrew Thompson ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); 14233671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14243671d9d8SAndrew Thompson 14253671d9d8SAndrew Thompson return (0); 14263671d9d8SAndrew Thompson } /* ng_ubt_connect */ 14273671d9d8SAndrew Thompson 14283671d9d8SAndrew Thompson /* 14293671d9d8SAndrew Thompson * Disconnect hook. 14303671d9d8SAndrew Thompson * Netgraph context. 14313671d9d8SAndrew Thompson */ 14323671d9d8SAndrew Thompson 14333671d9d8SAndrew Thompson static int 14343671d9d8SAndrew Thompson ng_ubt_disconnect(hook_p hook) 14353671d9d8SAndrew Thompson { 14363671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 14373671d9d8SAndrew Thompson 14383671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 14393671d9d8SAndrew Thompson 14403671d9d8SAndrew Thompson if (hook != sc->sc_hook) { 14413671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14423671d9d8SAndrew Thompson 14433671d9d8SAndrew Thompson return (EINVAL); 14443671d9d8SAndrew Thompson } 14453671d9d8SAndrew Thompson 14463671d9d8SAndrew Thompson sc->sc_hook = NULL; 14473671d9d8SAndrew Thompson 14483671d9d8SAndrew Thompson /* Kick off task to stop all USB xfers */ 14493671d9d8SAndrew Thompson ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); 14503671d9d8SAndrew Thompson 14513671d9d8SAndrew Thompson /* Drain queues */ 14523671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); 14533671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); 14543671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); 14553671d9d8SAndrew Thompson 14563671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14573671d9d8SAndrew Thompson 14583671d9d8SAndrew Thompson return (0); 14593671d9d8SAndrew Thompson } /* ng_ubt_disconnect */ 14603671d9d8SAndrew Thompson 14613671d9d8SAndrew Thompson /* 14623671d9d8SAndrew Thompson * Process control message. 14633671d9d8SAndrew Thompson * Netgraph context. 14643671d9d8SAndrew Thompson */ 14653671d9d8SAndrew Thompson 14663671d9d8SAndrew Thompson static int 14673671d9d8SAndrew Thompson ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) 14683671d9d8SAndrew Thompson { 14693671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(node); 14703671d9d8SAndrew Thompson struct ng_mesg *msg, *rsp = NULL; 14713671d9d8SAndrew Thompson struct ng_bt_mbufq *q; 14723671d9d8SAndrew Thompson int error = 0, queue, qlen; 14733671d9d8SAndrew Thompson 14743671d9d8SAndrew Thompson NGI_GET_MSG(item, msg); 14753671d9d8SAndrew Thompson 14763671d9d8SAndrew Thompson switch (msg->header.typecookie) { 14773671d9d8SAndrew Thompson case NGM_GENERIC_COOKIE: 14783671d9d8SAndrew Thompson switch (msg->header.cmd) { 14793671d9d8SAndrew Thompson case NGM_TEXT_STATUS: 14803671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 14813671d9d8SAndrew Thompson if (rsp == NULL) { 14823671d9d8SAndrew Thompson error = ENOMEM; 14833671d9d8SAndrew Thompson break; 14843671d9d8SAndrew Thompson } 14853671d9d8SAndrew Thompson 14863671d9d8SAndrew Thompson snprintf(rsp->data, NG_TEXTRESPONSE, 14873671d9d8SAndrew Thompson "Hook: %s\n" \ 14883671d9d8SAndrew Thompson "Task flags: %#x\n" \ 14893671d9d8SAndrew Thompson "Debug: %d\n" \ 14903671d9d8SAndrew Thompson "CMD queue: [have:%d,max:%d]\n" \ 14913671d9d8SAndrew Thompson "ACL queue: [have:%d,max:%d]\n" \ 14923671d9d8SAndrew Thompson "SCO queue: [have:%d,max:%d]", 14933671d9d8SAndrew Thompson (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", 14943671d9d8SAndrew Thompson sc->sc_task_flags, 14953671d9d8SAndrew Thompson sc->sc_debug, 14963671d9d8SAndrew Thompson sc->sc_cmdq.len, 14973671d9d8SAndrew Thompson sc->sc_cmdq.maxlen, 14983671d9d8SAndrew Thompson sc->sc_aclq.len, 14993671d9d8SAndrew Thompson sc->sc_aclq.maxlen, 15003671d9d8SAndrew Thompson sc->sc_scoq.len, 15013671d9d8SAndrew Thompson sc->sc_scoq.maxlen); 15023671d9d8SAndrew Thompson break; 15033671d9d8SAndrew Thompson 15043671d9d8SAndrew Thompson default: 15053671d9d8SAndrew Thompson error = EINVAL; 15063671d9d8SAndrew Thompson break; 15073671d9d8SAndrew Thompson } 15083671d9d8SAndrew Thompson break; 15093671d9d8SAndrew Thompson 15103671d9d8SAndrew Thompson case NGM_UBT_COOKIE: 15113671d9d8SAndrew Thompson switch (msg->header.cmd) { 15123671d9d8SAndrew Thompson case NGM_UBT_NODE_SET_DEBUG: 15133671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ 15143671d9d8SAndrew Thompson error = EMSGSIZE; 15153671d9d8SAndrew Thompson break; 15163671d9d8SAndrew Thompson } 15173671d9d8SAndrew Thompson 15183671d9d8SAndrew Thompson sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); 15193671d9d8SAndrew Thompson break; 15203671d9d8SAndrew Thompson 15213671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_DEBUG: 15223671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), 15233671d9d8SAndrew Thompson M_NOWAIT); 15243671d9d8SAndrew Thompson if (rsp == NULL) { 15253671d9d8SAndrew Thompson error = ENOMEM; 15263671d9d8SAndrew Thompson break; 15273671d9d8SAndrew Thompson } 15283671d9d8SAndrew Thompson 15293671d9d8SAndrew Thompson *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; 15303671d9d8SAndrew Thompson break; 15313671d9d8SAndrew Thompson 15323671d9d8SAndrew Thompson case NGM_UBT_NODE_SET_QLEN: 15333671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 15343671d9d8SAndrew Thompson error = EMSGSIZE; 15353671d9d8SAndrew Thompson break; 15363671d9d8SAndrew Thompson } 15373671d9d8SAndrew Thompson 15383671d9d8SAndrew Thompson queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 15393671d9d8SAndrew Thompson qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; 15403671d9d8SAndrew Thompson 15413671d9d8SAndrew Thompson switch (queue) { 15423671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_CMD: 15433671d9d8SAndrew Thompson q = &sc->sc_cmdq; 15443671d9d8SAndrew Thompson break; 15453671d9d8SAndrew Thompson 15463671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_ACL: 15473671d9d8SAndrew Thompson q = &sc->sc_aclq; 15483671d9d8SAndrew Thompson break; 15493671d9d8SAndrew Thompson 15503671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_SCO: 15513671d9d8SAndrew Thompson q = &sc->sc_scoq; 15523671d9d8SAndrew Thompson break; 15533671d9d8SAndrew Thompson 15543671d9d8SAndrew Thompson default: 15553671d9d8SAndrew Thompson error = EINVAL; 15563671d9d8SAndrew Thompson goto done; 15573671d9d8SAndrew Thompson /* NOT REACHED */ 15583671d9d8SAndrew Thompson } 15593671d9d8SAndrew Thompson 15603671d9d8SAndrew Thompson q->maxlen = qlen; 15613671d9d8SAndrew Thompson break; 15623671d9d8SAndrew Thompson 15633671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_QLEN: 15643671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 15653671d9d8SAndrew Thompson error = EMSGSIZE; 15663671d9d8SAndrew Thompson break; 15673671d9d8SAndrew Thompson } 15683671d9d8SAndrew Thompson 15693671d9d8SAndrew Thompson queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 15703671d9d8SAndrew Thompson 15713671d9d8SAndrew Thompson switch (queue) { 15723671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_CMD: 15733671d9d8SAndrew Thompson q = &sc->sc_cmdq; 15743671d9d8SAndrew Thompson break; 15753671d9d8SAndrew Thompson 15763671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_ACL: 15773671d9d8SAndrew Thompson q = &sc->sc_aclq; 15783671d9d8SAndrew Thompson break; 15793671d9d8SAndrew Thompson 15803671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_SCO: 15813671d9d8SAndrew Thompson q = &sc->sc_scoq; 15823671d9d8SAndrew Thompson break; 15833671d9d8SAndrew Thompson 15843671d9d8SAndrew Thompson default: 15853671d9d8SAndrew Thompson error = EINVAL; 15863671d9d8SAndrew Thompson goto done; 15873671d9d8SAndrew Thompson /* NOT REACHED */ 15883671d9d8SAndrew Thompson } 15893671d9d8SAndrew Thompson 15903671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), 15913671d9d8SAndrew Thompson M_NOWAIT); 15923671d9d8SAndrew Thompson if (rsp == NULL) { 15933671d9d8SAndrew Thompson error = ENOMEM; 15943671d9d8SAndrew Thompson break; 15953671d9d8SAndrew Thompson } 15963671d9d8SAndrew Thompson 15973671d9d8SAndrew Thompson ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; 15983671d9d8SAndrew Thompson ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; 15993671d9d8SAndrew Thompson break; 16003671d9d8SAndrew Thompson 16013671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_STAT: 16023671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), 16033671d9d8SAndrew Thompson M_NOWAIT); 16043671d9d8SAndrew Thompson if (rsp == NULL) { 16053671d9d8SAndrew Thompson error = ENOMEM; 16063671d9d8SAndrew Thompson break; 16073671d9d8SAndrew Thompson } 16083671d9d8SAndrew Thompson 16093671d9d8SAndrew Thompson bcopy(&sc->sc_stat, rsp->data, 16103671d9d8SAndrew Thompson sizeof(ng_ubt_node_stat_ep)); 16113671d9d8SAndrew Thompson break; 16123671d9d8SAndrew Thompson 16133671d9d8SAndrew Thompson case NGM_UBT_NODE_RESET_STAT: 16143671d9d8SAndrew Thompson UBT_STAT_RESET(sc); 16153671d9d8SAndrew Thompson break; 16163671d9d8SAndrew Thompson 16173671d9d8SAndrew Thompson default: 16183671d9d8SAndrew Thompson error = EINVAL; 16193671d9d8SAndrew Thompson break; 16203671d9d8SAndrew Thompson } 16213671d9d8SAndrew Thompson break; 16223671d9d8SAndrew Thompson 16233671d9d8SAndrew Thompson default: 16243671d9d8SAndrew Thompson error = EINVAL; 16253671d9d8SAndrew Thompson break; 16263671d9d8SAndrew Thompson } 16273671d9d8SAndrew Thompson done: 16283671d9d8SAndrew Thompson NG_RESPOND_MSG(error, node, item, rsp); 16293671d9d8SAndrew Thompson NG_FREE_MSG(msg); 16303671d9d8SAndrew Thompson 16313671d9d8SAndrew Thompson return (error); 16323671d9d8SAndrew Thompson } /* ng_ubt_rcvmsg */ 16333671d9d8SAndrew Thompson 16343671d9d8SAndrew Thompson /* 16353671d9d8SAndrew Thompson * Process data. 16363671d9d8SAndrew Thompson * Netgraph context. 16373671d9d8SAndrew Thompson */ 16383671d9d8SAndrew Thompson 16393671d9d8SAndrew Thompson static int 16403671d9d8SAndrew Thompson ng_ubt_rcvdata(hook_p hook, item_p item) 16413671d9d8SAndrew Thompson { 16423671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 16433671d9d8SAndrew Thompson struct mbuf *m; 16443671d9d8SAndrew Thompson struct ng_bt_mbufq *q; 16453671d9d8SAndrew Thompson int action, error = 0; 16463671d9d8SAndrew Thompson 16473671d9d8SAndrew Thompson if (hook != sc->sc_hook) { 16483671d9d8SAndrew Thompson error = EINVAL; 16493671d9d8SAndrew Thompson goto done; 16503671d9d8SAndrew Thompson } 16513671d9d8SAndrew Thompson 16523671d9d8SAndrew Thompson /* Deatch mbuf and get HCI frame type */ 16533671d9d8SAndrew Thompson NGI_GET_M(item, m); 16543671d9d8SAndrew Thompson 16553671d9d8SAndrew Thompson /* 16563671d9d8SAndrew Thompson * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, 16573671d9d8SAndrew Thompson * 2 bytes connection handle and at least 1 byte of length. 16583671d9d8SAndrew Thompson * Panic on data frame that has size smaller than 4 bytes (it 16593671d9d8SAndrew Thompson * should not happen) 16603671d9d8SAndrew Thompson */ 16613671d9d8SAndrew Thompson 16623671d9d8SAndrew Thompson if (m->m_pkthdr.len < 4) 16633671d9d8SAndrew Thompson panic("HCI frame size is too small! pktlen=%d\n", 16643671d9d8SAndrew Thompson m->m_pkthdr.len); 16653671d9d8SAndrew Thompson 16663671d9d8SAndrew Thompson /* Process HCI frame */ 16673671d9d8SAndrew Thompson switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ 16683671d9d8SAndrew Thompson case NG_HCI_CMD_PKT: 16696d917491SHans Petter Selasky if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE) 16703671d9d8SAndrew Thompson panic("HCI command frame size is too big! " \ 16713671d9d8SAndrew Thompson "buffer size=%zd, packet len=%d\n", 16723671d9d8SAndrew Thompson UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); 16733671d9d8SAndrew Thompson 16743671d9d8SAndrew Thompson q = &sc->sc_cmdq; 16753671d9d8SAndrew Thompson action = UBT_FLAG_T_START_CTRL; 16763671d9d8SAndrew Thompson break; 16773671d9d8SAndrew Thompson 16783671d9d8SAndrew Thompson case NG_HCI_ACL_DATA_PKT: 16793671d9d8SAndrew Thompson if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) 16803671d9d8SAndrew Thompson panic("ACL data frame size is too big! " \ 16813671d9d8SAndrew Thompson "buffer size=%d, packet len=%d\n", 16823671d9d8SAndrew Thompson UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); 16833671d9d8SAndrew Thompson 16843671d9d8SAndrew Thompson q = &sc->sc_aclq; 16853671d9d8SAndrew Thompson action = UBT_FLAG_T_START_BULK; 16863671d9d8SAndrew Thompson break; 16873671d9d8SAndrew Thompson 16883671d9d8SAndrew Thompson case NG_HCI_SCO_DATA_PKT: 16893671d9d8SAndrew Thompson q = &sc->sc_scoq; 16903671d9d8SAndrew Thompson action = 0; 16913671d9d8SAndrew Thompson break; 16923671d9d8SAndrew Thompson 16933671d9d8SAndrew Thompson default: 16943671d9d8SAndrew Thompson UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ 16953671d9d8SAndrew Thompson "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len); 16963671d9d8SAndrew Thompson 16973671d9d8SAndrew Thompson NG_FREE_M(m); 16983671d9d8SAndrew Thompson error = EINVAL; 16993671d9d8SAndrew Thompson goto done; 17003671d9d8SAndrew Thompson /* NOT REACHED */ 17013671d9d8SAndrew Thompson } 17023671d9d8SAndrew Thompson 17033671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 17043671d9d8SAndrew Thompson if (NG_BT_MBUFQ_FULL(q)) { 17053671d9d8SAndrew Thompson NG_BT_MBUFQ_DROP(q); 17063671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 17073671d9d8SAndrew Thompson 17083671d9d8SAndrew Thompson UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", 17093671d9d8SAndrew Thompson *mtod(m, uint8_t *), m->m_pkthdr.len); 17103671d9d8SAndrew Thompson 17113671d9d8SAndrew Thompson NG_FREE_M(m); 17123671d9d8SAndrew Thompson } else { 17133671d9d8SAndrew Thompson /* Loose HCI packet type, enqueue mbuf and kick off task */ 17143671d9d8SAndrew Thompson m_adj(m, sizeof(uint8_t)); 17153671d9d8SAndrew Thompson NG_BT_MBUFQ_ENQUEUE(q, m); 17163671d9d8SAndrew Thompson ubt_task_schedule(sc, action); 17173671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 17183671d9d8SAndrew Thompson } 17193671d9d8SAndrew Thompson done: 17203671d9d8SAndrew Thompson NG_FREE_ITEM(item); 17213671d9d8SAndrew Thompson 17223671d9d8SAndrew Thompson return (error); 17233671d9d8SAndrew Thompson } /* ng_ubt_rcvdata */ 17243671d9d8SAndrew Thompson 17253671d9d8SAndrew Thompson /**************************************************************************** 17263671d9d8SAndrew Thompson **************************************************************************** 17273671d9d8SAndrew Thompson ** Module 17283671d9d8SAndrew Thompson **************************************************************************** 17293671d9d8SAndrew Thompson ****************************************************************************/ 17303671d9d8SAndrew Thompson 17313671d9d8SAndrew Thompson /* 17323671d9d8SAndrew Thompson * Load/Unload the driver module 17333671d9d8SAndrew Thompson */ 17343671d9d8SAndrew Thompson 17353671d9d8SAndrew Thompson static int 17363671d9d8SAndrew Thompson ubt_modevent(module_t mod, int event, void *data) 17373671d9d8SAndrew Thompson { 17383671d9d8SAndrew Thompson int error; 17393671d9d8SAndrew Thompson 17403671d9d8SAndrew Thompson switch (event) { 17413671d9d8SAndrew Thompson case MOD_LOAD: 17423671d9d8SAndrew Thompson error = ng_newtype(&typestruct); 17433671d9d8SAndrew Thompson if (error != 0) 17443671d9d8SAndrew Thompson printf("%s: Could not register Netgraph node type, " \ 17453671d9d8SAndrew Thompson "error=%d\n", NG_UBT_NODE_TYPE, error); 17463671d9d8SAndrew Thompson break; 17473671d9d8SAndrew Thompson 17483671d9d8SAndrew Thompson case MOD_UNLOAD: 17493671d9d8SAndrew Thompson error = ng_rmtype(&typestruct); 17503671d9d8SAndrew Thompson break; 17513671d9d8SAndrew Thompson 17523671d9d8SAndrew Thompson default: 17533671d9d8SAndrew Thompson error = EOPNOTSUPP; 17543671d9d8SAndrew Thompson break; 17553671d9d8SAndrew Thompson } 17563671d9d8SAndrew Thompson 17573671d9d8SAndrew Thompson return (error); 17583671d9d8SAndrew Thompson } /* ubt_modevent */ 17593671d9d8SAndrew Thompson 17603671d9d8SAndrew Thompson static devclass_t ubt_devclass; 17613671d9d8SAndrew Thompson 17623671d9d8SAndrew Thompson static device_method_t ubt_methods[] = 17633671d9d8SAndrew Thompson { 17643671d9d8SAndrew Thompson DEVMETHOD(device_probe, ubt_probe), 17653671d9d8SAndrew Thompson DEVMETHOD(device_attach, ubt_attach), 17663671d9d8SAndrew Thompson DEVMETHOD(device_detach, ubt_detach), 17673671d9d8SAndrew Thompson { 0, 0 } 17683671d9d8SAndrew Thompson }; 17693671d9d8SAndrew Thompson 17703671d9d8SAndrew Thompson static driver_t ubt_driver = 17713671d9d8SAndrew Thompson { 17723671d9d8SAndrew Thompson .name = "ubt", 17733671d9d8SAndrew Thompson .methods = ubt_methods, 17743671d9d8SAndrew Thompson .size = sizeof(struct ubt_softc), 17753671d9d8SAndrew Thompson }; 17763671d9d8SAndrew Thompson 17773671d9d8SAndrew Thompson DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0); 17783671d9d8SAndrew Thompson MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); 17793671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 17803671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 17813671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, usb, 1, 1, 1); 17823671d9d8SAndrew Thompson 1783