13671d9d8SAndrew Thompson /* 23671d9d8SAndrew Thompson * ng_ubt.c 33671d9d8SAndrew Thompson */ 43671d9d8SAndrew Thompson 53671d9d8SAndrew Thompson /*- 64d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 7fe267a55SPedro F. Giffuni * 83671d9d8SAndrew Thompson * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> 93671d9d8SAndrew Thompson * All rights reserved. 103671d9d8SAndrew Thompson * 113671d9d8SAndrew Thompson * Redistribution and use in source and binary forms, with or without 123671d9d8SAndrew Thompson * modification, are permitted provided that the following conditions 133671d9d8SAndrew Thompson * are met: 143671d9d8SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 153671d9d8SAndrew Thompson * notice, this list of conditions and the following disclaimer. 163671d9d8SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 173671d9d8SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 183671d9d8SAndrew Thompson * documentation and/or other materials provided with the distribution. 193671d9d8SAndrew Thompson * 203671d9d8SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 213671d9d8SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 223671d9d8SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 233671d9d8SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 243671d9d8SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 253671d9d8SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 263671d9d8SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 273671d9d8SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 283671d9d8SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 293671d9d8SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 303671d9d8SAndrew Thompson * SUCH DAMAGE. 313671d9d8SAndrew Thompson * 323671d9d8SAndrew Thompson * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $ 333671d9d8SAndrew Thompson */ 343671d9d8SAndrew Thompson 353671d9d8SAndrew Thompson /* 363671d9d8SAndrew Thompson * NOTE: ng_ubt2 driver has a split personality. On one side it is 373671d9d8SAndrew Thompson * a USB device driver and on the other it is a Netgraph node. This 383671d9d8SAndrew Thompson * driver will *NOT* create traditional /dev/ enties, only Netgraph 393671d9d8SAndrew Thompson * node. 403671d9d8SAndrew Thompson * 413671d9d8SAndrew Thompson * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes) 423671d9d8SAndrew Thompson * 433671d9d8SAndrew Thompson * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used 443671d9d8SAndrew Thompson * by USB for any USB request going over device's interface #0 and #1, 453671d9d8SAndrew Thompson * i.e. interrupt, control, bulk and isoc. transfers. 463671d9d8SAndrew Thompson * 473671d9d8SAndrew Thompson * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph 483671d9d8SAndrew Thompson * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook 493671d9d8SAndrew Thompson * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact, 503671d9d8SAndrew Thompson * think of it as a spin lock. 513671d9d8SAndrew Thompson * 523671d9d8SAndrew Thompson * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts. 533671d9d8SAndrew Thompson * 543671d9d8SAndrew Thompson * 1) USB context. This is where all the USB related stuff happens. All 553671d9d8SAndrew Thompson * callbacks run in this context. All callbacks are called (by USB) with 563671d9d8SAndrew Thompson * appropriate interface lock held. It is (generally) allowed to grab 573671d9d8SAndrew Thompson * any additional locks. 583671d9d8SAndrew Thompson * 593671d9d8SAndrew Thompson * 2) Netgraph context. This is where all the Netgraph related stuff happens. 603671d9d8SAndrew Thompson * Since we mark node as WRITER, the Netgraph node will be "locked" (from 613671d9d8SAndrew Thompson * Netgraph point of view). Any variable that is only modified from the 62053359b7SPedro F. Giffuni * Netgraph context does not require any additional locking. It is generally 633671d9d8SAndrew Thompson * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT* 643671d9d8SAndrew Thompson * grab any lock in the Netgraph context that could cause de-scheduling of 653671d9d8SAndrew Thompson * the Netgraph thread for significant amount of time. In fact, the only 663671d9d8SAndrew Thompson * lock that is allowed in the Netgraph context is the sc_ng_mtx lock. 673671d9d8SAndrew Thompson * Also make sure that any code that is called from the Netgraph context 683671d9d8SAndrew Thompson * follows the rule above. 693671d9d8SAndrew Thompson * 703671d9d8SAndrew Thompson * 3) Taskqueue context. This is where ubt_task runs. Since we are generally 713671d9d8SAndrew Thompson * NOT allowed to grab any lock that could cause de-scheduling in the 723671d9d8SAndrew Thompson * Netgraph context, and, USB requires us to grab interface lock before 733671d9d8SAndrew Thompson * doing things with transfers, it is safer to transition from the Netgraph 743671d9d8SAndrew Thompson * context to the Taskqueue context before we can call into USB subsystem. 753671d9d8SAndrew Thompson * 763671d9d8SAndrew Thompson * So, to put everything together, the rules are as follows. 773671d9d8SAndrew Thompson * It is OK to call from the USB context or the Taskqueue context into 783671d9d8SAndrew Thompson * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words 793671d9d8SAndrew Thompson * it is allowed to call into the Netgraph context with locks held. 803671d9d8SAndrew Thompson * Is it *NOT* OK to call from the Netgraph context into the USB context, 813671d9d8SAndrew Thompson * because USB requires us to grab interface locks, and, it is safer to 823671d9d8SAndrew Thompson * avoid it. So, to make things safer we set task flags to indicate which 833671d9d8SAndrew Thompson * actions we want to perform and schedule ubt_task which would run in the 843671d9d8SAndrew Thompson * Taskqueue context. 853671d9d8SAndrew Thompson * Is is OK to call from the Taskqueue context into the USB context, 863671d9d8SAndrew Thompson * and, ubt_task does just that (i.e. grabs appropriate interface locks 873671d9d8SAndrew Thompson * before calling into USB). 883671d9d8SAndrew Thompson * Access to the outgoing queues, task flags and hook pointer is 893671d9d8SAndrew Thompson * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again, 903671d9d8SAndrew Thompson * sc_ng_mtx should really be a spin lock (and it is very likely to an 913671d9d8SAndrew Thompson * equivalent of spin lock due to adaptive nature of FreeBSD mutexes). 923671d9d8SAndrew Thompson * All USB callbacks accept softc pointer as a private data. USB ensures 933671d9d8SAndrew Thompson * that this pointer is valid. 943671d9d8SAndrew Thompson */ 953671d9d8SAndrew Thompson 96ed6d949aSAndrew Thompson #include <sys/stdint.h> 97ed6d949aSAndrew Thompson #include <sys/stddef.h> 98ed6d949aSAndrew Thompson #include <sys/param.h> 99ed6d949aSAndrew Thompson #include <sys/queue.h> 100ed6d949aSAndrew Thompson #include <sys/types.h> 101ed6d949aSAndrew Thompson #include <sys/systm.h> 102ed6d949aSAndrew Thompson #include <sys/kernel.h> 103ed6d949aSAndrew Thompson #include <sys/bus.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 15467cbbf19SHans Petter Selasky static int ng_usb_isoc_enable = 1; 15567cbbf19SHans Petter Selasky 15603f03934SHans Petter Selasky SYSCTL_INT(_net_bluetooth, OID_AUTO, usb_isoc_enable, CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 15767cbbf19SHans Petter Selasky &ng_usb_isoc_enable, 0, "enable isochronous transfers"); 15867cbbf19SHans Petter Selasky 1593671d9d8SAndrew Thompson /* Queue length */ 1603671d9d8SAndrew Thompson static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = 1613671d9d8SAndrew Thompson { 1623671d9d8SAndrew Thompson { "queue", &ng_parse_int32_type, }, 1633671d9d8SAndrew Thompson { "qlen", &ng_parse_int32_type, }, 1643671d9d8SAndrew Thompson { NULL, } 1653671d9d8SAndrew Thompson }; 1663671d9d8SAndrew Thompson static const struct ng_parse_type ng_ubt_node_qlen_type = 1673671d9d8SAndrew Thompson { 1683671d9d8SAndrew Thompson &ng_parse_struct_type, 1693671d9d8SAndrew Thompson &ng_ubt_node_qlen_type_fields 1703671d9d8SAndrew Thompson }; 1713671d9d8SAndrew Thompson 1723671d9d8SAndrew Thompson /* Stat info */ 1733671d9d8SAndrew Thompson static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = 1743671d9d8SAndrew Thompson { 1753671d9d8SAndrew Thompson { "pckts_recv", &ng_parse_uint32_type, }, 1763671d9d8SAndrew Thompson { "bytes_recv", &ng_parse_uint32_type, }, 1773671d9d8SAndrew Thompson { "pckts_sent", &ng_parse_uint32_type, }, 1783671d9d8SAndrew Thompson { "bytes_sent", &ng_parse_uint32_type, }, 1793671d9d8SAndrew Thompson { "oerrors", &ng_parse_uint32_type, }, 1803671d9d8SAndrew Thompson { "ierrors", &ng_parse_uint32_type, }, 1813671d9d8SAndrew Thompson { NULL, } 1823671d9d8SAndrew Thompson }; 1833671d9d8SAndrew Thompson static const struct ng_parse_type ng_ubt_node_stat_type = 1843671d9d8SAndrew Thompson { 1853671d9d8SAndrew Thompson &ng_parse_struct_type, 1863671d9d8SAndrew Thompson &ng_ubt_node_stat_type_fields 1873671d9d8SAndrew Thompson }; 1883671d9d8SAndrew Thompson 1893671d9d8SAndrew Thompson /* Netgraph node command list */ 1903671d9d8SAndrew Thompson static const struct ng_cmdlist ng_ubt_cmdlist[] = 1913671d9d8SAndrew Thompson { 1923671d9d8SAndrew Thompson { 1933671d9d8SAndrew Thompson NGM_UBT_COOKIE, 1943671d9d8SAndrew Thompson NGM_UBT_NODE_SET_DEBUG, 1953671d9d8SAndrew Thompson "set_debug", 1963671d9d8SAndrew Thompson &ng_parse_uint16_type, 1973671d9d8SAndrew Thompson NULL 1983671d9d8SAndrew Thompson }, 1993671d9d8SAndrew Thompson { 2003671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2013671d9d8SAndrew Thompson NGM_UBT_NODE_GET_DEBUG, 2023671d9d8SAndrew Thompson "get_debug", 2033671d9d8SAndrew Thompson NULL, 2043671d9d8SAndrew Thompson &ng_parse_uint16_type 2053671d9d8SAndrew Thompson }, 2063671d9d8SAndrew Thompson { 2073671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2083671d9d8SAndrew Thompson NGM_UBT_NODE_SET_QLEN, 2093671d9d8SAndrew Thompson "set_qlen", 2103671d9d8SAndrew Thompson &ng_ubt_node_qlen_type, 2113671d9d8SAndrew Thompson NULL 2123671d9d8SAndrew Thompson }, 2133671d9d8SAndrew Thompson { 2143671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2153671d9d8SAndrew Thompson NGM_UBT_NODE_GET_QLEN, 2163671d9d8SAndrew Thompson "get_qlen", 2173671d9d8SAndrew Thompson &ng_ubt_node_qlen_type, 2183671d9d8SAndrew Thompson &ng_ubt_node_qlen_type 2193671d9d8SAndrew Thompson }, 2203671d9d8SAndrew Thompson { 2213671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2223671d9d8SAndrew Thompson NGM_UBT_NODE_GET_STAT, 2233671d9d8SAndrew Thompson "get_stat", 2243671d9d8SAndrew Thompson NULL, 2253671d9d8SAndrew Thompson &ng_ubt_node_stat_type 2263671d9d8SAndrew Thompson }, 2273671d9d8SAndrew Thompson { 2283671d9d8SAndrew Thompson NGM_UBT_COOKIE, 2293671d9d8SAndrew Thompson NGM_UBT_NODE_RESET_STAT, 2303671d9d8SAndrew Thompson "reset_stat", 2313671d9d8SAndrew Thompson NULL, 2323671d9d8SAndrew Thompson NULL 2333671d9d8SAndrew Thompson }, 2343671d9d8SAndrew Thompson { 0, } 2353671d9d8SAndrew Thompson }; 2363671d9d8SAndrew Thompson 2373671d9d8SAndrew Thompson /* Netgraph node type */ 2383671d9d8SAndrew Thompson static struct ng_type typestruct = 2393671d9d8SAndrew Thompson { 2403671d9d8SAndrew Thompson .version = NG_ABI_VERSION, 2413671d9d8SAndrew Thompson .name = NG_UBT_NODE_TYPE, 2423671d9d8SAndrew Thompson .constructor = ng_ubt_constructor, 2433671d9d8SAndrew Thompson .rcvmsg = ng_ubt_rcvmsg, 2443671d9d8SAndrew Thompson .shutdown = ng_ubt_shutdown, 2453671d9d8SAndrew Thompson .newhook = ng_ubt_newhook, 2463671d9d8SAndrew Thompson .connect = ng_ubt_connect, 2473671d9d8SAndrew Thompson .rcvdata = ng_ubt_rcvdata, 2483671d9d8SAndrew Thompson .disconnect = ng_ubt_disconnect, 2493671d9d8SAndrew Thompson .cmdlist = ng_ubt_cmdlist 2503671d9d8SAndrew Thompson }; 2513671d9d8SAndrew Thompson 2523671d9d8SAndrew Thompson /**************************************************************************** 2533671d9d8SAndrew Thompson **************************************************************************** 2543671d9d8SAndrew Thompson ** USB specific 2553671d9d8SAndrew Thompson **************************************************************************** 2563671d9d8SAndrew Thompson ****************************************************************************/ 2573671d9d8SAndrew Thompson 2583671d9d8SAndrew Thompson /* USB methods */ 2593544d43bSVladimir Kondratyev static usb_callback_t ubt_probe_intr_callback; 260e0a69b51SAndrew Thompson static usb_callback_t ubt_ctrl_write_callback; 261e0a69b51SAndrew Thompson static usb_callback_t ubt_intr_read_callback; 262e0a69b51SAndrew Thompson static usb_callback_t ubt_bulk_read_callback; 263e0a69b51SAndrew Thompson static usb_callback_t ubt_bulk_write_callback; 264e0a69b51SAndrew Thompson static usb_callback_t ubt_isoc_read_callback; 265e0a69b51SAndrew Thompson static usb_callback_t ubt_isoc_write_callback; 2663671d9d8SAndrew Thompson 2673671d9d8SAndrew Thompson static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **); 268760bc48eSAndrew Thompson static int ubt_isoc_read_one_frame(struct usb_xfer *, int); 2693671d9d8SAndrew Thompson 2703671d9d8SAndrew Thompson /* 2713671d9d8SAndrew Thompson * USB config 2723671d9d8SAndrew Thompson * 2733671d9d8SAndrew Thompson * The following desribes usb transfers that could be submitted on USB device. 2743671d9d8SAndrew Thompson * 2753671d9d8SAndrew Thompson * Interface 0 on the USB device must present the following endpoints 2763671d9d8SAndrew Thompson * 1) Interrupt endpoint to receive HCI events 2773671d9d8SAndrew Thompson * 2) Bulk IN endpoint to receive ACL data 2783671d9d8SAndrew Thompson * 3) Bulk OUT endpoint to send ACL data 2793671d9d8SAndrew Thompson * 2803671d9d8SAndrew Thompson * Interface 1 on the USB device must present the following endpoints 2813671d9d8SAndrew Thompson * 1) Isochronous IN endpoint to receive SCO data 2823671d9d8SAndrew Thompson * 2) Isochronous OUT endpoint to send SCO data 2833671d9d8SAndrew Thompson */ 2843671d9d8SAndrew Thompson 285760bc48eSAndrew Thompson static const struct usb_config ubt_config[UBT_N_TRANSFER] = 2863671d9d8SAndrew Thompson { 2873671d9d8SAndrew Thompson /* 2883671d9d8SAndrew Thompson * Interface #0 2893671d9d8SAndrew Thompson */ 2903671d9d8SAndrew Thompson 2913671d9d8SAndrew Thompson /* Outgoing bulk transfer - ACL packets */ 2923671d9d8SAndrew Thompson [UBT_IF_0_BULK_DT_WR] = { 2933671d9d8SAndrew Thompson .type = UE_BULK, 2943671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 2953671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 2963671d9d8SAndrew Thompson .if_index = 0, 2973671d9d8SAndrew Thompson .bufsize = UBT_BULK_WRITE_BUFFER_SIZE, 2983671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .force_short_xfer = 1, }, 2993671d9d8SAndrew Thompson .callback = &ubt_bulk_write_callback, 3003671d9d8SAndrew Thompson }, 3013671d9d8SAndrew Thompson /* Incoming bulk transfer - ACL packets */ 3023671d9d8SAndrew Thompson [UBT_IF_0_BULK_DT_RD] = { 3033671d9d8SAndrew Thompson .type = UE_BULK, 3043671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3053671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3063671d9d8SAndrew Thompson .if_index = 0, 3073671d9d8SAndrew Thompson .bufsize = UBT_BULK_READ_BUFFER_SIZE, 3083671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 3093671d9d8SAndrew Thompson .callback = &ubt_bulk_read_callback, 3103671d9d8SAndrew Thompson }, 3113671d9d8SAndrew Thompson /* Incoming interrupt transfer - HCI events */ 3123671d9d8SAndrew Thompson [UBT_IF_0_INTR_DT_RD] = { 3133671d9d8SAndrew Thompson .type = UE_INTERRUPT, 3143671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3153671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3163671d9d8SAndrew Thompson .if_index = 0, 3173671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 3183671d9d8SAndrew Thompson .bufsize = UBT_INTR_BUFFER_SIZE, 3193671d9d8SAndrew Thompson .callback = &ubt_intr_read_callback, 3203671d9d8SAndrew Thompson }, 3213671d9d8SAndrew Thompson /* Outgoing control transfer - HCI commands */ 3223671d9d8SAndrew Thompson [UBT_IF_0_CTRL_DT_WR] = { 3233671d9d8SAndrew Thompson .type = UE_CONTROL, 3243671d9d8SAndrew Thompson .endpoint = 0x00, /* control pipe */ 3253671d9d8SAndrew Thompson .direction = UE_DIR_ANY, 3263671d9d8SAndrew Thompson .if_index = 0, 3273671d9d8SAndrew Thompson .bufsize = UBT_CTRL_BUFFER_SIZE, 3283671d9d8SAndrew Thompson .callback = &ubt_ctrl_write_callback, 3293671d9d8SAndrew Thompson .timeout = 5000, /* 5 seconds */ 3303671d9d8SAndrew Thompson }, 3313671d9d8SAndrew Thompson 3323671d9d8SAndrew Thompson /* 3333671d9d8SAndrew Thompson * Interface #1 3343671d9d8SAndrew Thompson */ 3353671d9d8SAndrew Thompson 3363671d9d8SAndrew Thompson /* Incoming isochronous transfer #1 - SCO packets */ 3373671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_RD1] = { 3383671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3393671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3403671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3413671d9d8SAndrew Thompson .if_index = 1, 3423671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3433671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3443671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3453671d9d8SAndrew Thompson .callback = &ubt_isoc_read_callback, 3463671d9d8SAndrew Thompson }, 3473671d9d8SAndrew Thompson /* Incoming isochronous transfer #2 - SCO packets */ 3483671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_RD2] = { 3493671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3503671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3513671d9d8SAndrew Thompson .direction = UE_DIR_IN, 3523671d9d8SAndrew Thompson .if_index = 1, 3533671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3543671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3553671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3563671d9d8SAndrew Thompson .callback = &ubt_isoc_read_callback, 3573671d9d8SAndrew Thompson }, 3583671d9d8SAndrew Thompson /* Outgoing isochronous transfer #1 - SCO packets */ 3593671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_WR1] = { 3603671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3613671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3623671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 3633671d9d8SAndrew Thompson .if_index = 1, 3643671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3653671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3663671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3673671d9d8SAndrew Thompson .callback = &ubt_isoc_write_callback, 3683671d9d8SAndrew Thompson }, 3693671d9d8SAndrew Thompson /* Outgoing isochronous transfer #2 - SCO packets */ 3703671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_WR2] = { 3713671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS, 3723671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY, 3733671d9d8SAndrew Thompson .direction = UE_DIR_OUT, 3743671d9d8SAndrew Thompson .if_index = 1, 3753671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 3763671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES, 3773671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, }, 3783671d9d8SAndrew Thompson .callback = &ubt_isoc_write_callback, 3793671d9d8SAndrew Thompson }, 3803671d9d8SAndrew Thompson }; 3813671d9d8SAndrew Thompson 3823671d9d8SAndrew Thompson /* 3833671d9d8SAndrew Thompson * If for some reason device should not be attached then put 3843671d9d8SAndrew Thompson * VendorID/ProductID pair into the list below. The format is 3853671d9d8SAndrew Thompson * as follows: 3863671d9d8SAndrew Thompson * 3873671d9d8SAndrew Thompson * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, 3883671d9d8SAndrew Thompson * 3893671d9d8SAndrew Thompson * where VENDOR_ID and PRODUCT_ID are hex numbers. 3903671d9d8SAndrew Thompson */ 3913671d9d8SAndrew Thompson 392f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_ignore_devs[] = 3933671d9d8SAndrew Thompson { 3943671d9d8SAndrew Thompson /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */ 3953671d9d8SAndrew Thompson { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) }, 396c36c780dSAdrian Chadd 397c36c780dSAdrian Chadd /* Atheros 3011 with sflash firmware */ 398c36c780dSAdrian Chadd { USB_VPI(0x0cf3, 0x3002, 0) }, 399c36c780dSAdrian Chadd { USB_VPI(0x0cf3, 0xe019, 0) }, 400c36c780dSAdrian Chadd { USB_VPI(0x13d3, 0x3304, 0) }, 401c36c780dSAdrian Chadd { USB_VPI(0x0930, 0x0215, 0) }, 402c36c780dSAdrian Chadd { USB_VPI(0x0489, 0xe03d, 0) }, 403c36c780dSAdrian Chadd { USB_VPI(0x0489, 0xe027, 0) }, 404c36c780dSAdrian Chadd 405c36c780dSAdrian Chadd /* Atheros AR9285 Malbec with sflash firmware */ 406c36c780dSAdrian Chadd { USB_VPI(0x03f0, 0x311d, 0) }, 407c36c780dSAdrian Chadd 408c36c780dSAdrian Chadd /* Atheros 3012 with sflash firmware */ 4090818ec92SAdrian Chadd { USB_VPI(0x0cf3, 0x3004, 0), USB_DEV_BCD_LTEQ(1) }, 4100818ec92SAdrian Chadd { USB_VPI(0x0cf3, 0x311d, 0), USB_DEV_BCD_LTEQ(1) }, 4110818ec92SAdrian Chadd { USB_VPI(0x13d3, 0x3375, 0), USB_DEV_BCD_LTEQ(1) }, 4120818ec92SAdrian Chadd { USB_VPI(0x04ca, 0x3005, 0), USB_DEV_BCD_LTEQ(1) }, 4130818ec92SAdrian Chadd { USB_VPI(0x04ca, 0x3006, 0), USB_DEV_BCD_LTEQ(1) }, 4140818ec92SAdrian Chadd { USB_VPI(0x04ca, 0x3008, 0), USB_DEV_BCD_LTEQ(1) }, 4150818ec92SAdrian Chadd { USB_VPI(0x13d3, 0x3362, 0), USB_DEV_BCD_LTEQ(1) }, 4160818ec92SAdrian Chadd { USB_VPI(0x0cf3, 0xe004, 0), USB_DEV_BCD_LTEQ(1) }, 4170818ec92SAdrian Chadd { USB_VPI(0x0930, 0x0219, 0), USB_DEV_BCD_LTEQ(1) }, 4180818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe057, 0), USB_DEV_BCD_LTEQ(1) }, 4190818ec92SAdrian Chadd { USB_VPI(0x13d3, 0x3393, 0), USB_DEV_BCD_LTEQ(1) }, 4200818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe04e, 0), USB_DEV_BCD_LTEQ(1) }, 4210818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe056, 0), USB_DEV_BCD_LTEQ(1) }, 422c36c780dSAdrian Chadd 423c36c780dSAdrian Chadd /* Atheros AR5BBU12 with sflash firmware */ 4240818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe02c, 0), USB_DEV_BCD_LTEQ(1) }, 425c36c780dSAdrian Chadd 426c36c780dSAdrian Chadd /* Atheros AR5BBU12 with sflash firmware */ 4270818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) }, 4280818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) }, 4293544d43bSVladimir Kondratyev 430788a171cSVladimir Kondratyev /* Intel Wireless controllers are handled in ng_ubt_intel.c */ 431788a171cSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x07dc, 0) }, 432788a171cSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0a2a, 0) }, 433788a171cSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0aa7, 0) }, 4343544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0a2b, 0) }, 4353544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0aaa, 0) }, 4363544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0025, 0) }, 4373544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0026, 0) }, 4383544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0029, 0) }, 439b25ba58aSMark Johnston 440b25ba58aSMark Johnston /* 441b25ba58aSMark Johnston * Some Intel controllers are not yet supported by ng_ubt_intel and 442b25ba58aSMark Johnston * should be ignored. 443b25ba58aSMark Johnston */ 444b25ba58aSMark Johnston { USB_VPI(USB_VENDOR_INTEL2, 0x0032, 0) }, 445b25ba58aSMark Johnston { USB_VPI(USB_VENDOR_INTEL2, 0x0033, 0) }, 4468b21c469SKevin Lo 4478b21c469SKevin Lo /* MediaTek MT7925 */ 4488b21c469SKevin Lo { USB_VPI(USB_VENDOR_AZUREWAVE, 0x3602, 0) }, 4498b21c469SKevin Lo { USB_VPI(USB_VENDOR_AZUREWAVE, 0x3604, 0) }, 4503671d9d8SAndrew Thompson }; 4513671d9d8SAndrew Thompson 4523671d9d8SAndrew Thompson /* List of supported bluetooth devices */ 453f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_devs[] = 4543671d9d8SAndrew Thompson { 4553671d9d8SAndrew Thompson /* Generic Bluetooth class devices */ 4563671d9d8SAndrew Thompson { USB_IFACE_CLASS(UDCLASS_WIRELESS), 4573671d9d8SAndrew Thompson USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 4583671d9d8SAndrew Thompson USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 4593671d9d8SAndrew Thompson 4603671d9d8SAndrew Thompson /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ 4613671d9d8SAndrew Thompson { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) }, 46270a0e340SGleb Smirnoff 46370a0e340SGleb Smirnoff /* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */ 46470a0e340SGleb Smirnoff { USB_VENDOR(USB_VENDOR_BROADCOM), 465ac76cdc6SRaphael Kubo da Costa USB_IFACE_CLASS(UICLASS_VENDOR), 466ac76cdc6SRaphael Kubo da Costa USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 467ac76cdc6SRaphael Kubo da Costa USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 46833f4aa11SHans Petter Selasky 46933f4aa11SHans Petter Selasky /* Apple-specific (Broadcom) devices */ 47033f4aa11SHans Petter Selasky { USB_VENDOR(USB_VENDOR_APPLE), 47133f4aa11SHans Petter Selasky USB_IFACE_CLASS(UICLASS_VENDOR), 47233f4aa11SHans Petter Selasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 47333f4aa11SHans Petter Selasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 47433f4aa11SHans Petter Selasky 47533f4aa11SHans Petter Selasky /* Foxconn - Hon Hai */ 47633f4aa11SHans Petter Selasky { USB_VENDOR(USB_VENDOR_FOXCONN), 47733f4aa11SHans Petter Selasky USB_IFACE_CLASS(UICLASS_VENDOR), 47833f4aa11SHans Petter Selasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 47933f4aa11SHans Petter Selasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 48033f4aa11SHans Petter Selasky 48133f4aa11SHans Petter Selasky /* MediaTek MT76x0E */ 48233f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) }, 48333f4aa11SHans Petter Selasky 48433f4aa11SHans Petter Selasky /* Broadcom SoftSailing reporting vendor specific */ 48533f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) }, 48633f4aa11SHans Petter Selasky 48733f4aa11SHans Petter Selasky /* Apple MacBookPro 7,1 */ 48833f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) }, 48933f4aa11SHans Petter Selasky 49033f4aa11SHans Petter Selasky /* Apple iMac11,1 */ 49133f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) }, 49233f4aa11SHans Petter Selasky 49333f4aa11SHans Petter Selasky /* Apple MacBookPro6,2 */ 49433f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) }, 49533f4aa11SHans Petter Selasky 49633f4aa11SHans Petter Selasky /* Apple MacBookAir3,1, MacBookAir3,2 */ 49733f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) }, 49833f4aa11SHans Petter Selasky 49933f4aa11SHans Petter Selasky /* Apple MacBookAir4,1 */ 50033f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) }, 50133f4aa11SHans Petter Selasky 50233f4aa11SHans Petter Selasky /* MacBookAir6,1 */ 50333f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) }, 50433f4aa11SHans Petter Selasky 50533f4aa11SHans Petter Selasky /* Apple MacBookPro8,2 */ 50633f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) }, 50733f4aa11SHans Petter Selasky 50833f4aa11SHans Petter Selasky /* Apple MacMini5,1 */ 50933f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) }, 51033f4aa11SHans Petter Selasky 51133f4aa11SHans Petter Selasky /* Bluetooth Ultraport Module from IBM */ 51233f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_TDK, 0x030a, 0) }, 51333f4aa11SHans Petter Selasky 51433f4aa11SHans Petter Selasky /* ALPS Modules with non-standard ID */ 51533f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) }, 51633f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) }, 51733f4aa11SHans Petter Selasky 51833f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) }, 51933f4aa11SHans Petter Selasky 52033f4aa11SHans Petter Selasky /* Canyon CN-BTU1 with HID interfaces */ 52133f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) }, 52233f4aa11SHans Petter Selasky 52333f4aa11SHans Petter Selasky /* Broadcom BCM20702A0 */ 52433f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) }, 525c6721651SEitan Adler { USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) }, 52633f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) }, 52733f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) }, 52833f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_DELL, 0x8197, 0) }, 5293a3992fbSMark Johnston { USB_VPI(USB_VENDOR_BELKIN, 0x065a, 0) }, 5303671d9d8SAndrew Thompson }; 5313671d9d8SAndrew Thompson 5323671d9d8SAndrew Thompson /* 5333544d43bSVladimir Kondratyev * Does a synchronous (waits for completion event) execution of HCI command. 5343544d43bSVladimir Kondratyev * Size of both command and response buffers are passed in length field of 5353544d43bSVladimir Kondratyev * corresponding structures in "Parameter Total Length" format i.e. 5363544d43bSVladimir Kondratyev * not including HCI packet headers. 5370b23c50aSVladimir Kondratyev * Expected event code must be placed into "Event code" of the response buffer. 5383544d43bSVladimir Kondratyev * 5393544d43bSVladimir Kondratyev * Must not be used after USB transfers have been configured in attach routine. 5403544d43bSVladimir Kondratyev */ 5413544d43bSVladimir Kondratyev 5423544d43bSVladimir Kondratyev usb_error_t 5433544d43bSVladimir Kondratyev ubt_do_hci_request(struct usb_device *udev, struct ubt_hci_cmd *cmd, 5443544d43bSVladimir Kondratyev void *evt, usb_timeout_t timeout) 5453544d43bSVladimir Kondratyev { 5463544d43bSVladimir Kondratyev static const struct usb_config ubt_probe_config = { 5473544d43bSVladimir Kondratyev .type = UE_INTERRUPT, 5483544d43bSVladimir Kondratyev .endpoint = UE_ADDR_ANY, 5493544d43bSVladimir Kondratyev .direction = UE_DIR_IN, 5503544d43bSVladimir Kondratyev .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, 5513544d43bSVladimir Kondratyev .bufsize = UBT_INTR_BUFFER_SIZE, 5523544d43bSVladimir Kondratyev .callback = &ubt_probe_intr_callback, 5533544d43bSVladimir Kondratyev }; 5543544d43bSVladimir Kondratyev struct usb_device_request req; 5553544d43bSVladimir Kondratyev struct usb_xfer *xfer[1]; 5563544d43bSVladimir Kondratyev struct mtx mtx; 5573544d43bSVladimir Kondratyev usb_error_t error = USB_ERR_NORMAL_COMPLETION; 5583544d43bSVladimir Kondratyev uint8_t iface_index = 0; 5593544d43bSVladimir Kondratyev 5603544d43bSVladimir Kondratyev /* Initialize a USB control request and then do it */ 5613544d43bSVladimir Kondratyev bzero(&req, sizeof(req)); 5623544d43bSVladimir Kondratyev req.bmRequestType = UBT_HCI_REQUEST; 5633544d43bSVladimir Kondratyev req.wIndex[0] = iface_index; 5643544d43bSVladimir Kondratyev USETW(req.wLength, UBT_HCI_CMD_SIZE(cmd)); 5653544d43bSVladimir Kondratyev 5663544d43bSVladimir Kondratyev error = usbd_do_request(udev, NULL, &req, cmd); 5673544d43bSVladimir Kondratyev if (error != USB_ERR_NORMAL_COMPLETION) { 5683544d43bSVladimir Kondratyev printf("ng_ubt: usbd_do_request error=%s\n", 5693544d43bSVladimir Kondratyev usbd_errstr(error)); 5703544d43bSVladimir Kondratyev return (error); 5713544d43bSVladimir Kondratyev } 5723544d43bSVladimir Kondratyev 5733544d43bSVladimir Kondratyev if (evt == NULL) 5743544d43bSVladimir Kondratyev return (USB_ERR_NORMAL_COMPLETION); 5753544d43bSVladimir Kondratyev 5760b23c50aSVladimir Kondratyev /* Save operation code if we expect completion event in response */ 5770b23c50aSVladimir Kondratyev if(((struct ubt_hci_event *)evt)->header.event == 5780b23c50aSVladimir Kondratyev NG_HCI_EVENT_COMMAND_COMPL) 5790b23c50aSVladimir Kondratyev ((struct ubt_hci_event_command_compl *)evt)->opcode = 5800b23c50aSVladimir Kondratyev cmd->opcode; 5810b23c50aSVladimir Kondratyev 5823544d43bSVladimir Kondratyev /* Initialize INTR endpoint xfer and wait for response */ 583443127c5SXin LI mtx_init(&mtx, "ubt pb", NULL, MTX_DEF | MTX_NEW); 5843544d43bSVladimir Kondratyev 5853544d43bSVladimir Kondratyev error = usbd_transfer_setup(udev, &iface_index, xfer, 5863544d43bSVladimir Kondratyev &ubt_probe_config, 1, evt, &mtx); 5873544d43bSVladimir Kondratyev if (error == USB_ERR_NORMAL_COMPLETION) { 5883544d43bSVladimir Kondratyev mtx_lock(&mtx); 5893544d43bSVladimir Kondratyev usbd_transfer_start(*xfer); 5903544d43bSVladimir Kondratyev 5913544d43bSVladimir Kondratyev if (msleep_sbt(evt, &mtx, 0, "ubt pb", SBT_1MS * timeout, 5923544d43bSVladimir Kondratyev 0, C_HARDCLOCK) == EWOULDBLOCK) { 5933544d43bSVladimir Kondratyev printf("ng_ubt: HCI command 0x%04x timed out\n", 5943544d43bSVladimir Kondratyev le16toh(cmd->opcode)); 5953544d43bSVladimir Kondratyev error = USB_ERR_TIMEOUT; 5963544d43bSVladimir Kondratyev } 5973544d43bSVladimir Kondratyev 5983544d43bSVladimir Kondratyev usbd_transfer_stop(*xfer); 5993544d43bSVladimir Kondratyev mtx_unlock(&mtx); 6003544d43bSVladimir Kondratyev 6013544d43bSVladimir Kondratyev usbd_transfer_unsetup(xfer, 1); 6023544d43bSVladimir Kondratyev } else 6033544d43bSVladimir Kondratyev printf("ng_ubt: usbd_transfer_setup error=%s\n", 6043544d43bSVladimir Kondratyev usbd_errstr(error)); 6053544d43bSVladimir Kondratyev 6063544d43bSVladimir Kondratyev mtx_destroy(&mtx); 6073544d43bSVladimir Kondratyev 6083544d43bSVladimir Kondratyev return (error); 6093544d43bSVladimir Kondratyev } 6103544d43bSVladimir Kondratyev 6113544d43bSVladimir Kondratyev /* 6123671d9d8SAndrew Thompson * Probe for a USB Bluetooth device. 6133671d9d8SAndrew Thompson * USB context. 6143671d9d8SAndrew Thompson */ 6153671d9d8SAndrew Thompson 6163671d9d8SAndrew Thompson static int 6173671d9d8SAndrew Thompson ubt_probe(device_t dev) 6183671d9d8SAndrew Thompson { 619760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 6209d28e15eSHans Petter Selasky const struct usb_device_id *id; 6213671d9d8SAndrew Thompson 6223671d9d8SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) 6233671d9d8SAndrew Thompson return (ENXIO); 6243671d9d8SAndrew Thompson 625a593f6b8SAndrew Thompson if (usbd_lookup_id_by_uaa(ubt_ignore_devs, 6263671d9d8SAndrew Thompson sizeof(ubt_ignore_devs), uaa) == 0) 6273671d9d8SAndrew Thompson return (ENXIO); 628*24ae172aSVladimir Kondratyev if (usbd_lookup_id_by_uaa(ubt_rtl_devs, 629*24ae172aSVladimir Kondratyev ubt_rtl_devs_sizeof, uaa) == 0) 630*24ae172aSVladimir Kondratyev return (ENXIO); 6313671d9d8SAndrew Thompson 6329d28e15eSHans Petter Selasky id = usbd_lookup_id_by_info(ubt_devs, 6339d28e15eSHans Petter Selasky sizeof(ubt_devs), &uaa->info); 6349d28e15eSHans Petter Selasky if (id == NULL) 6359d28e15eSHans Petter Selasky return (ENXIO); 6369d28e15eSHans Petter Selasky 6379d28e15eSHans Petter Selasky if (uaa->info.bIfaceIndex != 0) { 6389d28e15eSHans Petter Selasky /* make sure we are matching the interface */ 6399d28e15eSHans Petter Selasky if (id->match_flag_int_class && 6409d28e15eSHans Petter Selasky id->match_flag_int_subclass && 6419d28e15eSHans Petter Selasky id->match_flag_int_protocol) 642a5db8fd1SAndriy Gapon return (BUS_PROBE_GENERIC); 6439d28e15eSHans Petter Selasky else 6449d28e15eSHans Petter Selasky return (ENXIO); 6459d28e15eSHans Petter Selasky } else { 6469d28e15eSHans Petter Selasky return (BUS_PROBE_GENERIC); 6479d28e15eSHans Petter Selasky } 6483671d9d8SAndrew Thompson } /* ubt_probe */ 6493671d9d8SAndrew Thompson 6503671d9d8SAndrew Thompson /* 6513671d9d8SAndrew Thompson * Attach the device. 6523671d9d8SAndrew Thompson * USB context. 6533671d9d8SAndrew Thompson */ 6543671d9d8SAndrew Thompson 6553671d9d8SAndrew Thompson static int 6563671d9d8SAndrew Thompson ubt_attach(device_t dev) 6573671d9d8SAndrew Thompson { 658760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 6593671d9d8SAndrew Thompson struct ubt_softc *sc = device_get_softc(dev); 660760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 661760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 6629d28e15eSHans Petter Selasky struct usb_interface *iface[2]; 6634ac6682cSHans Petter Selasky uint32_t wMaxPacketSize; 6643671d9d8SAndrew Thompson uint8_t alt_index, i, j; 6659d28e15eSHans Petter Selasky uint8_t iface_index[2]; 6663671d9d8SAndrew Thompson 667a593f6b8SAndrew Thompson device_set_usb_desc(dev); 6683671d9d8SAndrew Thompson 6699d28e15eSHans Petter Selasky iface_index[0] = uaa->info.bIfaceIndex; 6709d28e15eSHans Petter Selasky iface_index[1] = uaa->info.bIfaceIndex + 1; 6719d28e15eSHans Petter Selasky 6729d28e15eSHans Petter Selasky iface[0] = usbd_get_iface(uaa->device, iface_index[0]); 6739d28e15eSHans Petter Selasky iface[1] = usbd_get_iface(uaa->device, iface_index[1]); 6749d28e15eSHans Petter Selasky 6753671d9d8SAndrew Thompson sc->sc_dev = dev; 6763671d9d8SAndrew Thompson sc->sc_debug = NG_UBT_WARN_LEVEL; 67767272c5aSCraig Rodrigues 6783671d9d8SAndrew Thompson /* 6799d28e15eSHans Petter Selasky * Sanity checks. 6809d28e15eSHans Petter Selasky */ 6819d28e15eSHans Petter Selasky 6829d28e15eSHans Petter Selasky if (iface[0] == NULL || iface[1] == NULL || 6839d28e15eSHans Petter Selasky iface[0]->idesc == NULL || iface[1]->idesc == NULL) { 6849d28e15eSHans Petter Selasky UBT_ALERT(sc, "could not get two interfaces\n"); 6859d28e15eSHans Petter Selasky return (ENXIO); 6869d28e15eSHans Petter Selasky } 6879d28e15eSHans Petter Selasky 6889d28e15eSHans Petter Selasky /* 6893671d9d8SAndrew Thompson * Create Netgraph node 6903671d9d8SAndrew Thompson */ 6913671d9d8SAndrew Thompson 6923671d9d8SAndrew Thompson if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { 6933671d9d8SAndrew Thompson UBT_ALERT(sc, "could not create Netgraph node\n"); 6943671d9d8SAndrew Thompson return (ENXIO); 6953671d9d8SAndrew Thompson } 6963671d9d8SAndrew Thompson 6973671d9d8SAndrew Thompson /* Name Netgraph node */ 6983671d9d8SAndrew Thompson if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { 6993671d9d8SAndrew Thompson UBT_ALERT(sc, "could not name Netgraph node\n"); 7003671d9d8SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 7013671d9d8SAndrew Thompson return (ENXIO); 7023671d9d8SAndrew Thompson } 7033671d9d8SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 7043671d9d8SAndrew Thompson NG_NODE_FORCE_WRITER(sc->sc_node); 7053671d9d8SAndrew Thompson 7063671d9d8SAndrew Thompson /* 7073671d9d8SAndrew Thompson * Initialize device softc structure 7083671d9d8SAndrew Thompson */ 7093671d9d8SAndrew Thompson 7103671d9d8SAndrew Thompson /* initialize locks */ 7113671d9d8SAndrew Thompson mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); 7123671d9d8SAndrew Thompson mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); 7133671d9d8SAndrew Thompson 7143671d9d8SAndrew Thompson /* initialize packet queues */ 7153671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); 7163671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); 7173671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); 7183671d9d8SAndrew Thompson 7193671d9d8SAndrew Thompson /* initialize glue task */ 7203671d9d8SAndrew Thompson TASK_INIT(&sc->sc_task, 0, ubt_task, sc); 7213671d9d8SAndrew Thompson 7223671d9d8SAndrew Thompson /* 7233671d9d8SAndrew Thompson * Configure Bluetooth USB device. Discover all required USB 7243671d9d8SAndrew Thompson * interfaces and endpoints. 7253671d9d8SAndrew Thompson * 7263671d9d8SAndrew Thompson * USB device must present two interfaces: 7273671d9d8SAndrew Thompson * 1) Interface 0 that has 3 endpoints 7283671d9d8SAndrew Thompson * 1) Interrupt endpoint to receive HCI events 7293671d9d8SAndrew Thompson * 2) Bulk IN endpoint to receive ACL data 7303671d9d8SAndrew Thompson * 3) Bulk OUT endpoint to send ACL data 7313671d9d8SAndrew Thompson * 7323671d9d8SAndrew Thompson * 2) Interface 1 then has 2 endpoints 7333671d9d8SAndrew Thompson * 1) Isochronous IN endpoint to receive SCO data 7343671d9d8SAndrew Thompson * 2) Isochronous OUT endpoint to send SCO data 7353671d9d8SAndrew Thompson * 7363671d9d8SAndrew Thompson * Interface 1 (with isochronous endpoints) has several alternate 7373671d9d8SAndrew Thompson * configurations with different packet size. 7383671d9d8SAndrew Thompson */ 7393671d9d8SAndrew Thompson 7403671d9d8SAndrew Thompson /* 7413671d9d8SAndrew Thompson * For interface #1 search alternate settings, and find 7423671d9d8SAndrew Thompson * the descriptor with the largest wMaxPacketSize 7433671d9d8SAndrew Thompson */ 7443671d9d8SAndrew Thompson 7453671d9d8SAndrew Thompson wMaxPacketSize = 0; 7463671d9d8SAndrew Thompson alt_index = 0; 7473671d9d8SAndrew Thompson i = 0; 7483671d9d8SAndrew Thompson j = 0; 7493671d9d8SAndrew Thompson ed = NULL; 7503671d9d8SAndrew Thompson 7513671d9d8SAndrew Thompson /* 7523671d9d8SAndrew Thompson * Search through all the descriptors looking for the largest 7533671d9d8SAndrew Thompson * packet size: 7543671d9d8SAndrew Thompson */ 755a593f6b8SAndrew Thompson while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach( 756a593f6b8SAndrew Thompson usbd_get_config_descriptor(uaa->device), 757760bc48eSAndrew Thompson (struct usb_descriptor *)ed))) { 7583671d9d8SAndrew Thompson if ((ed->bDescriptorType == UDESC_INTERFACE) && 7593671d9d8SAndrew Thompson (ed->bLength >= sizeof(*id))) { 760760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)ed; 7619d28e15eSHans Petter Selasky i = (id->bInterfaceNumber == iface[1]->idesc->bInterfaceNumber); 7623671d9d8SAndrew Thompson j = id->bAlternateSetting; 7633671d9d8SAndrew Thompson } 7643671d9d8SAndrew Thompson 7653671d9d8SAndrew Thompson if ((ed->bDescriptorType == UDESC_ENDPOINT) && 7663671d9d8SAndrew Thompson (ed->bLength >= sizeof(*ed)) && 7679d28e15eSHans Petter Selasky (i != 0)) { 7684ac6682cSHans Petter Selasky uint32_t temp; 7693671d9d8SAndrew Thompson 7704ac6682cSHans Petter Selasky temp = usbd_get_max_frame_length( 7714ac6682cSHans Petter Selasky ed, NULL, usbd_get_speed(uaa->device)); 7723671d9d8SAndrew Thompson if (temp > wMaxPacketSize) { 7733671d9d8SAndrew Thompson wMaxPacketSize = temp; 7743671d9d8SAndrew Thompson alt_index = j; 7753671d9d8SAndrew Thompson } 7763671d9d8SAndrew Thompson } 7773671d9d8SAndrew Thompson } 7783671d9d8SAndrew Thompson 7793671d9d8SAndrew Thompson /* Set alt configuration on interface #1 only if we found it */ 7803671d9d8SAndrew Thompson if (wMaxPacketSize > 0 && 7819d28e15eSHans Petter Selasky usbd_set_alt_interface_index(uaa->device, iface_index[1], alt_index)) { 7823671d9d8SAndrew Thompson UBT_ALERT(sc, "could not set alternate setting %d " \ 7833671d9d8SAndrew Thompson "for interface 1!\n", alt_index); 7843671d9d8SAndrew Thompson goto detach; 7853671d9d8SAndrew Thompson } 7863671d9d8SAndrew Thompson 7873671d9d8SAndrew Thompson /* Setup transfers for both interfaces */ 78867cbbf19SHans Petter Selasky if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, ubt_config, 78967cbbf19SHans Petter Selasky ng_usb_isoc_enable ? UBT_N_TRANSFER : UBT_IF_1_ISOC_DT_RD1, 79067cbbf19SHans Petter Selasky sc, &sc->sc_if_mtx)) { 7913671d9d8SAndrew Thompson UBT_ALERT(sc, "could not allocate transfers\n"); 7923671d9d8SAndrew Thompson goto detach; 7933671d9d8SAndrew Thompson } 7943671d9d8SAndrew Thompson 7959d28e15eSHans Petter Selasky /* Claim second interface belonging to the Bluetooth part */ 7969d28e15eSHans Petter Selasky usbd_set_parent_iface(uaa->device, iface_index[1], uaa->info.bIfaceIndex); 7973671d9d8SAndrew Thompson 7983671d9d8SAndrew Thompson return (0); /* success */ 7993671d9d8SAndrew Thompson 8003671d9d8SAndrew Thompson detach: 8013671d9d8SAndrew Thompson ubt_detach(dev); 8023671d9d8SAndrew Thompson 8033671d9d8SAndrew Thompson return (ENXIO); 8043671d9d8SAndrew Thompson } /* ubt_attach */ 8053671d9d8SAndrew Thompson 8063671d9d8SAndrew Thompson /* 8073671d9d8SAndrew Thompson * Detach the device. 8083671d9d8SAndrew Thompson * USB context. 8093671d9d8SAndrew Thompson */ 8103671d9d8SAndrew Thompson 8113671d9d8SAndrew Thompson int 8123671d9d8SAndrew Thompson ubt_detach(device_t dev) 8133671d9d8SAndrew Thompson { 8143671d9d8SAndrew Thompson struct ubt_softc *sc = device_get_softc(dev); 8153671d9d8SAndrew Thompson node_p node = sc->sc_node; 8163671d9d8SAndrew Thompson 8173671d9d8SAndrew Thompson /* Destroy Netgraph node */ 8183671d9d8SAndrew Thompson if (node != NULL) { 8193671d9d8SAndrew Thompson sc->sc_node = NULL; 8203671d9d8SAndrew Thompson NG_NODE_REALLY_DIE(node); 8213671d9d8SAndrew Thompson ng_rmnode_self(node); 8223671d9d8SAndrew Thompson } 8233671d9d8SAndrew Thompson 8243671d9d8SAndrew Thompson /* Make sure ubt_task in gone */ 8253671d9d8SAndrew Thompson taskqueue_drain(taskqueue_swi, &sc->sc_task); 8263671d9d8SAndrew Thompson 8273671d9d8SAndrew Thompson /* Free USB transfers, if any */ 828a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); 8293671d9d8SAndrew Thompson 8303671d9d8SAndrew Thompson /* Destroy queues */ 8313671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 8323671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); 8333671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); 8343671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); 8353671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 8363671d9d8SAndrew Thompson 8373671d9d8SAndrew Thompson mtx_destroy(&sc->sc_if_mtx); 8383671d9d8SAndrew Thompson mtx_destroy(&sc->sc_ng_mtx); 8393671d9d8SAndrew Thompson 8403671d9d8SAndrew Thompson return (0); 8413671d9d8SAndrew Thompson } /* ubt_detach */ 8423671d9d8SAndrew Thompson 8433671d9d8SAndrew Thompson /* 8443544d43bSVladimir Kondratyev * Called when incoming interrupt transfer (HCI event) has completed, i.e. 8453544d43bSVladimir Kondratyev * HCI event was received from the device during device probe stage. 8463544d43bSVladimir Kondratyev * USB context. 8473544d43bSVladimir Kondratyev */ 8483544d43bSVladimir Kondratyev 8493544d43bSVladimir Kondratyev static void 8503544d43bSVladimir Kondratyev ubt_probe_intr_callback(struct usb_xfer *xfer, usb_error_t error) 8513544d43bSVladimir Kondratyev { 8523544d43bSVladimir Kondratyev struct ubt_hci_event *evt = usbd_xfer_softc(xfer); 8533544d43bSVladimir Kondratyev struct usb_page_cache *pc; 8543544d43bSVladimir Kondratyev int actlen; 8550b23c50aSVladimir Kondratyev struct ubt_hci_evhdr evhdr; 8560b23c50aSVladimir Kondratyev uint16_t opcode; 8573544d43bSVladimir Kondratyev 8583544d43bSVladimir Kondratyev usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 8593544d43bSVladimir Kondratyev 8603544d43bSVladimir Kondratyev switch (USB_GET_STATE(xfer)) { 8613544d43bSVladimir Kondratyev case USB_ST_TRANSFERRED: 8623544d43bSVladimir Kondratyev if (actlen > UBT_HCI_EVENT_SIZE(evt)) 8633544d43bSVladimir Kondratyev actlen = UBT_HCI_EVENT_SIZE(evt); 8640b23c50aSVladimir Kondratyev if (actlen < sizeof(evhdr)) 8650b23c50aSVladimir Kondratyev goto submit_next; 8663544d43bSVladimir Kondratyev pc = usbd_xfer_get_frame(xfer, 0); 8670b23c50aSVladimir Kondratyev usbd_copy_out(pc, 0, &evhdr, sizeof(evhdr)); 8680b23c50aSVladimir Kondratyev /* Check for expected event code */ 8690b23c50aSVladimir Kondratyev if (evt->header.event != 0 && 8700b23c50aSVladimir Kondratyev (evt->header.event != evhdr.event)) 8710b23c50aSVladimir Kondratyev goto submit_next; 8720b23c50aSVladimir Kondratyev /* For completion events check operation code as well */ 8730b23c50aSVladimir Kondratyev if (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL) { 8740b23c50aSVladimir Kondratyev if (actlen < sizeof(struct ubt_hci_event_command_compl)) 8750b23c50aSVladimir Kondratyev goto submit_next; 8760b23c50aSVladimir Kondratyev usbd_copy_out(pc, 8770b23c50aSVladimir Kondratyev offsetof(struct ubt_hci_event_command_compl, opcode), 8780b23c50aSVladimir Kondratyev &opcode, sizeof(opcode)); 8790b23c50aSVladimir Kondratyev if (opcode != 8800b23c50aSVladimir Kondratyev ((struct ubt_hci_event_command_compl *)evt)->opcode) 8810b23c50aSVladimir Kondratyev goto submit_next; 8820b23c50aSVladimir Kondratyev } 8833544d43bSVladimir Kondratyev usbd_copy_out(pc, 0, evt, actlen); 8843544d43bSVladimir Kondratyev /* OneShot mode */ 8853544d43bSVladimir Kondratyev wakeup(evt); 8863544d43bSVladimir Kondratyev break; 8873544d43bSVladimir Kondratyev 8883544d43bSVladimir Kondratyev case USB_ST_SETUP: 8893544d43bSVladimir Kondratyev submit_next: 8903544d43bSVladimir Kondratyev usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 8913544d43bSVladimir Kondratyev usbd_transfer_submit(xfer); 8923544d43bSVladimir Kondratyev break; 8933544d43bSVladimir Kondratyev 8943544d43bSVladimir Kondratyev default: 8953544d43bSVladimir Kondratyev if (error != USB_ERR_CANCELLED) { 8963544d43bSVladimir Kondratyev printf("ng_ubt: interrupt transfer failed: %s\n", 8973544d43bSVladimir Kondratyev usbd_errstr(error)); 89852489f2aSVladimir Kondratyev /* Try clear stall first */ 89952489f2aSVladimir Kondratyev usbd_xfer_set_stall(xfer); 9003544d43bSVladimir Kondratyev goto submit_next; 9013544d43bSVladimir Kondratyev } 9023544d43bSVladimir Kondratyev break; 9033544d43bSVladimir Kondratyev } 9043544d43bSVladimir Kondratyev } /* ubt_probe_intr_callback */ 9053544d43bSVladimir Kondratyev 9063544d43bSVladimir Kondratyev /* 9073671d9d8SAndrew Thompson * Called when outgoing control request (HCI command) has completed, i.e. 9083671d9d8SAndrew Thompson * HCI command was sent to the device. 9093671d9d8SAndrew Thompson * USB context. 9103671d9d8SAndrew Thompson */ 9113671d9d8SAndrew Thompson 9123671d9d8SAndrew Thompson static void 913ed6d949aSAndrew Thompson ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 9143671d9d8SAndrew Thompson { 915ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 916760bc48eSAndrew Thompson struct usb_device_request req; 9173671d9d8SAndrew Thompson struct mbuf *m; 918ed6d949aSAndrew Thompson struct usb_page_cache *pc; 919ed6d949aSAndrew Thompson int actlen; 920ed6d949aSAndrew Thompson 921ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 9223671d9d8SAndrew Thompson 9233671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 9243671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 925ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen); 926ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 9273671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 9283671d9d8SAndrew Thompson /* FALLTHROUGH */ 9293671d9d8SAndrew Thompson 9303671d9d8SAndrew Thompson case USB_ST_SETUP: 9313671d9d8SAndrew Thompson send_next: 9323671d9d8SAndrew Thompson /* Get next command mbuf, if any */ 9333671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 9343671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 9353671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 9363671d9d8SAndrew Thompson 9373671d9d8SAndrew Thompson if (m == NULL) { 9383671d9d8SAndrew Thompson UBT_INFO(sc, "HCI command queue is empty\n"); 9393671d9d8SAndrew Thompson break; /* transfer complete */ 9403671d9d8SAndrew Thompson } 9413671d9d8SAndrew Thompson 9423671d9d8SAndrew Thompson /* Initialize a USB control request and then schedule it */ 9433671d9d8SAndrew Thompson bzero(&req, sizeof(req)); 9443671d9d8SAndrew Thompson req.bmRequestType = UBT_HCI_REQUEST; 9453671d9d8SAndrew Thompson USETW(req.wLength, m->m_pkthdr.len); 9463671d9d8SAndrew Thompson 9473671d9d8SAndrew Thompson UBT_INFO(sc, "Sending control request, " \ 9483671d9d8SAndrew Thompson "bmRequestType=0x%02x, wLength=%d\n", 9493671d9d8SAndrew Thompson req.bmRequestType, UGETW(req.wLength)); 9503671d9d8SAndrew Thompson 951ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 952ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, &req, sizeof(req)); 953ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 1); 954ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 9553671d9d8SAndrew Thompson 956ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 957ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len); 958ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, 2); 9593671d9d8SAndrew Thompson 9603671d9d8SAndrew Thompson NG_FREE_M(m); 9613671d9d8SAndrew Thompson 962a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 9633671d9d8SAndrew Thompson break; 9643671d9d8SAndrew Thompson 9653671d9d8SAndrew Thompson default: /* Error */ 966ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 9673671d9d8SAndrew Thompson UBT_WARN(sc, "control transfer failed: %s\n", 968ed6d949aSAndrew Thompson usbd_errstr(error)); 9693671d9d8SAndrew Thompson 9703671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 9713671d9d8SAndrew Thompson goto send_next; 9723671d9d8SAndrew Thompson } 9733671d9d8SAndrew Thompson 9743671d9d8SAndrew Thompson /* transfer cancelled */ 9753671d9d8SAndrew Thompson break; 9763671d9d8SAndrew Thompson } 9773671d9d8SAndrew Thompson } /* ubt_ctrl_write_callback */ 9783671d9d8SAndrew Thompson 9793671d9d8SAndrew Thompson /* 9803671d9d8SAndrew Thompson * Called when incoming interrupt transfer (HCI event) has completed, i.e. 9813671d9d8SAndrew Thompson * HCI event was received from the device. 9823671d9d8SAndrew Thompson * USB context. 9833671d9d8SAndrew Thompson */ 9843671d9d8SAndrew Thompson 9853671d9d8SAndrew Thompson static void 986ed6d949aSAndrew Thompson ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 9873671d9d8SAndrew Thompson { 988ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 9893671d9d8SAndrew Thompson struct mbuf *m; 9903671d9d8SAndrew Thompson ng_hci_event_pkt_t *hdr; 991ed6d949aSAndrew Thompson struct usb_page_cache *pc; 992ed6d949aSAndrew Thompson int actlen; 993ed6d949aSAndrew Thompson 994ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 9953671d9d8SAndrew Thompson 9963671d9d8SAndrew Thompson m = NULL; 9973671d9d8SAndrew Thompson 9983671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 9993671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 10003671d9d8SAndrew Thompson /* Allocate a new mbuf */ 1001eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 10023671d9d8SAndrew Thompson if (m == NULL) { 10033671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 10043671d9d8SAndrew Thompson goto submit_next; 10053671d9d8SAndrew Thompson } 10063671d9d8SAndrew Thompson 10072a8c860fSRobert Watson if (!(MCLGET(m, M_NOWAIT))) { 10083671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 10093671d9d8SAndrew Thompson goto submit_next; 10103671d9d8SAndrew Thompson } 10113671d9d8SAndrew Thompson 10123671d9d8SAndrew Thompson /* Add HCI packet type */ 10133671d9d8SAndrew Thompson *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; 10143671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = 1; 10153671d9d8SAndrew Thompson 1016ed6d949aSAndrew Thompson if (actlen > MCLBYTES - 1) 1017ed6d949aSAndrew Thompson actlen = MCLBYTES - 1; 10183671d9d8SAndrew Thompson 1019ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 1020ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 1021ed6d949aSAndrew Thompson m->m_pkthdr.len += actlen; 1022ed6d949aSAndrew Thompson m->m_len += actlen; 10233671d9d8SAndrew Thompson 10243671d9d8SAndrew Thompson UBT_INFO(sc, "got %d bytes from interrupt pipe\n", 1025ed6d949aSAndrew Thompson actlen); 10263671d9d8SAndrew Thompson 10273671d9d8SAndrew Thompson /* Validate packet and send it up the stack */ 10286d917491SHans Petter Selasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 10293671d9d8SAndrew Thompson UBT_INFO(sc, "HCI event packet is too short\n"); 10303671d9d8SAndrew Thompson 10313671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 10323671d9d8SAndrew Thompson goto submit_next; 10333671d9d8SAndrew Thompson } 10343671d9d8SAndrew Thompson 10353671d9d8SAndrew Thompson hdr = mtod(m, ng_hci_event_pkt_t *); 10363671d9d8SAndrew Thompson if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { 10373671d9d8SAndrew Thompson UBT_ERR(sc, "Invalid HCI event packet size, " \ 10383671d9d8SAndrew Thompson "length=%d, pktlen=%d\n", 10393671d9d8SAndrew Thompson hdr->length, m->m_pkthdr.len); 10403671d9d8SAndrew Thompson 10413671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 10423671d9d8SAndrew Thompson goto submit_next; 10433671d9d8SAndrew Thompson } 10443671d9d8SAndrew Thompson 10453671d9d8SAndrew Thompson UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ 10463671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, hdr->length); 10473671d9d8SAndrew Thompson 10483671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 10493671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 10503671d9d8SAndrew Thompson 10513671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 10523671d9d8SAndrew Thompson /* m == NULL at this point */ 10533671d9d8SAndrew Thompson /* FALLTHROUGH */ 10543671d9d8SAndrew Thompson 10553671d9d8SAndrew Thompson case USB_ST_SETUP: 10563671d9d8SAndrew Thompson submit_next: 10573671d9d8SAndrew Thompson NG_FREE_M(m); /* checks for m != NULL */ 10583671d9d8SAndrew Thompson 1059ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 1060a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 10613671d9d8SAndrew Thompson break; 10623671d9d8SAndrew Thompson 10633671d9d8SAndrew Thompson default: /* Error */ 1064ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 10653671d9d8SAndrew Thompson UBT_WARN(sc, "interrupt transfer failed: %s\n", 1066ed6d949aSAndrew Thompson usbd_errstr(error)); 10673671d9d8SAndrew Thompson 10683671d9d8SAndrew Thompson /* Try to clear stall first */ 1069ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 10703671d9d8SAndrew Thompson goto submit_next; 10713671d9d8SAndrew Thompson } 10723671d9d8SAndrew Thompson /* transfer cancelled */ 10733671d9d8SAndrew Thompson break; 10743671d9d8SAndrew Thompson } 10753671d9d8SAndrew Thompson } /* ubt_intr_read_callback */ 10763671d9d8SAndrew Thompson 10773671d9d8SAndrew Thompson /* 10783671d9d8SAndrew Thompson * Called when incoming bulk transfer (ACL packet) has completed, i.e. 10793671d9d8SAndrew Thompson * ACL packet was received from the device. 10803671d9d8SAndrew Thompson * USB context. 10813671d9d8SAndrew Thompson */ 10823671d9d8SAndrew Thompson 10833671d9d8SAndrew Thompson static void 1084ed6d949aSAndrew Thompson ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 10853671d9d8SAndrew Thompson { 1086ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 10873671d9d8SAndrew Thompson struct mbuf *m; 10883671d9d8SAndrew Thompson ng_hci_acldata_pkt_t *hdr; 1089ed6d949aSAndrew Thompson struct usb_page_cache *pc; 10906d917491SHans Petter Selasky int len; 1091ed6d949aSAndrew Thompson int actlen; 1092ed6d949aSAndrew Thompson 1093ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 10943671d9d8SAndrew Thompson 10953671d9d8SAndrew Thompson m = NULL; 10963671d9d8SAndrew Thompson 10973671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 10983671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 10993671d9d8SAndrew Thompson /* Allocate new mbuf */ 1100eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 11013671d9d8SAndrew Thompson if (m == NULL) { 11023671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 11033671d9d8SAndrew Thompson goto submit_next; 11043671d9d8SAndrew Thompson } 11053671d9d8SAndrew Thompson 11062a8c860fSRobert Watson if (!(MCLGET(m, M_NOWAIT))) { 11073671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 11083671d9d8SAndrew Thompson goto submit_next; 11093671d9d8SAndrew Thompson } 11103671d9d8SAndrew Thompson 11113671d9d8SAndrew Thompson /* Add HCI packet type */ 11123671d9d8SAndrew Thompson *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; 11133671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = 1; 11143671d9d8SAndrew Thompson 1115ed6d949aSAndrew Thompson if (actlen > MCLBYTES - 1) 1116ed6d949aSAndrew Thompson actlen = MCLBYTES - 1; 11173671d9d8SAndrew Thompson 1118ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 1119ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 1120ed6d949aSAndrew Thompson m->m_pkthdr.len += actlen; 1121ed6d949aSAndrew Thompson m->m_len += actlen; 11223671d9d8SAndrew Thompson 11233671d9d8SAndrew Thompson UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", 1124ed6d949aSAndrew Thompson actlen); 11253671d9d8SAndrew Thompson 11263671d9d8SAndrew Thompson /* Validate packet and send it up the stack */ 11276d917491SHans Petter Selasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 11283671d9d8SAndrew Thompson UBT_INFO(sc, "HCI ACL packet is too short\n"); 11293671d9d8SAndrew Thompson 11303671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 11313671d9d8SAndrew Thompson goto submit_next; 11323671d9d8SAndrew Thompson } 11333671d9d8SAndrew Thompson 11343671d9d8SAndrew Thompson hdr = mtod(m, ng_hci_acldata_pkt_t *); 11353671d9d8SAndrew Thompson len = le16toh(hdr->length); 11366d917491SHans Petter Selasky if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) { 11373671d9d8SAndrew Thompson UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ 11383671d9d8SAndrew Thompson "pktlen=%d\n", len, m->m_pkthdr.len); 11393671d9d8SAndrew Thompson 11403671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 11413671d9d8SAndrew Thompson goto submit_next; 11423671d9d8SAndrew Thompson } 11433671d9d8SAndrew Thompson 11443671d9d8SAndrew Thompson UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ 11453671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, len); 11463671d9d8SAndrew Thompson 11473671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 11483671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 11493671d9d8SAndrew Thompson 11503671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 11513671d9d8SAndrew Thompson /* m == NULL at this point */ 11523671d9d8SAndrew Thompson /* FALLTHOUGH */ 11533671d9d8SAndrew Thompson 11543671d9d8SAndrew Thompson case USB_ST_SETUP: 11553671d9d8SAndrew Thompson submit_next: 11563671d9d8SAndrew Thompson NG_FREE_M(m); /* checks for m != NULL */ 11573671d9d8SAndrew Thompson 1158ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 1159a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 11603671d9d8SAndrew Thompson break; 11613671d9d8SAndrew Thompson 11623671d9d8SAndrew Thompson default: /* Error */ 1163ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 11643671d9d8SAndrew Thompson UBT_WARN(sc, "bulk-in transfer failed: %s\n", 1165ed6d949aSAndrew Thompson usbd_errstr(error)); 11663671d9d8SAndrew Thompson 11673671d9d8SAndrew Thompson /* Try to clear stall first */ 1168ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 11693671d9d8SAndrew Thompson goto submit_next; 11703671d9d8SAndrew Thompson } 11713671d9d8SAndrew Thompson /* transfer cancelled */ 11723671d9d8SAndrew Thompson break; 11733671d9d8SAndrew Thompson } 11743671d9d8SAndrew Thompson } /* ubt_bulk_read_callback */ 11753671d9d8SAndrew Thompson 11763671d9d8SAndrew Thompson /* 11773671d9d8SAndrew Thompson * Called when outgoing bulk transfer (ACL packet) has completed, i.e. 11783671d9d8SAndrew Thompson * ACL packet was sent to the device. 11793671d9d8SAndrew Thompson * USB context. 11803671d9d8SAndrew Thompson */ 11813671d9d8SAndrew Thompson 11823671d9d8SAndrew Thompson static void 1183ed6d949aSAndrew Thompson ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 11843671d9d8SAndrew Thompson { 1185ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 11863671d9d8SAndrew Thompson struct mbuf *m; 1187ed6d949aSAndrew Thompson struct usb_page_cache *pc; 1188ed6d949aSAndrew Thompson int actlen; 1189ed6d949aSAndrew Thompson 1190ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 11913671d9d8SAndrew Thompson 11923671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 11933671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 1194ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen); 1195ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 11963671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 11973671d9d8SAndrew Thompson /* FALLTHROUGH */ 11983671d9d8SAndrew Thompson 11993671d9d8SAndrew Thompson case USB_ST_SETUP: 12003671d9d8SAndrew Thompson send_next: 12013671d9d8SAndrew Thompson /* Get next mbuf, if any */ 12023671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 12033671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); 12043671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 12053671d9d8SAndrew Thompson 12063671d9d8SAndrew Thompson if (m == NULL) { 12073671d9d8SAndrew Thompson UBT_INFO(sc, "ACL data queue is empty\n"); 12083671d9d8SAndrew Thompson break; /* transfer completed */ 12093671d9d8SAndrew Thompson } 12103671d9d8SAndrew Thompson 12113671d9d8SAndrew Thompson /* 12123671d9d8SAndrew Thompson * Copy ACL data frame back to a linear USB transfer buffer 12133671d9d8SAndrew Thompson * and schedule transfer 12143671d9d8SAndrew Thompson */ 12153671d9d8SAndrew Thompson 1216ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 1217ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 1218ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 12193671d9d8SAndrew Thompson 12203671d9d8SAndrew Thompson UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", 12213671d9d8SAndrew Thompson m->m_pkthdr.len); 12223671d9d8SAndrew Thompson 12233671d9d8SAndrew Thompson NG_FREE_M(m); 12243671d9d8SAndrew Thompson 1225a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 12263671d9d8SAndrew Thompson break; 12273671d9d8SAndrew Thompson 12283671d9d8SAndrew Thompson default: /* Error */ 1229ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 12303671d9d8SAndrew Thompson UBT_WARN(sc, "bulk-out transfer failed: %s\n", 1231ed6d949aSAndrew Thompson usbd_errstr(error)); 12323671d9d8SAndrew Thompson 12333671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 12343671d9d8SAndrew Thompson 12353671d9d8SAndrew Thompson /* try to clear stall first */ 1236ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 12373671d9d8SAndrew Thompson goto send_next; 12383671d9d8SAndrew Thompson } 12393671d9d8SAndrew Thompson /* transfer cancelled */ 12403671d9d8SAndrew Thompson break; 12413671d9d8SAndrew Thompson } 12423671d9d8SAndrew Thompson } /* ubt_bulk_write_callback */ 12433671d9d8SAndrew Thompson 12443671d9d8SAndrew Thompson /* 12453671d9d8SAndrew Thompson * Called when incoming isoc transfer (SCO packet) has completed, i.e. 12463671d9d8SAndrew Thompson * SCO packet was received from the device. 12473671d9d8SAndrew Thompson * USB context. 12483671d9d8SAndrew Thompson */ 12493671d9d8SAndrew Thompson 12503671d9d8SAndrew Thompson static void 1251ed6d949aSAndrew Thompson ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 12523671d9d8SAndrew Thompson { 1253ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 12543671d9d8SAndrew Thompson int n; 1255ed6d949aSAndrew Thompson int actlen, nframes; 1256ed6d949aSAndrew Thompson 1257ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 12583671d9d8SAndrew Thompson 12593671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 12603671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 1261ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) 12623671d9d8SAndrew Thompson if (ubt_isoc_read_one_frame(xfer, n) < 0) 12633671d9d8SAndrew Thompson break; 12643671d9d8SAndrew Thompson /* FALLTHROUGH */ 12653671d9d8SAndrew Thompson 12663671d9d8SAndrew Thompson case USB_ST_SETUP: 12673671d9d8SAndrew Thompson read_next: 1268ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) 1269ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, 1270ed6d949aSAndrew Thompson usbd_xfer_max_framelen(xfer)); 12713671d9d8SAndrew Thompson 1272a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 12733671d9d8SAndrew Thompson break; 12743671d9d8SAndrew Thompson 12753671d9d8SAndrew Thompson default: /* Error */ 1276ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 12773671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 12783671d9d8SAndrew Thompson goto read_next; 12793671d9d8SAndrew Thompson } 12803671d9d8SAndrew Thompson 12813671d9d8SAndrew Thompson /* transfer cancelled */ 12823671d9d8SAndrew Thompson break; 12833671d9d8SAndrew Thompson } 12843671d9d8SAndrew Thompson } /* ubt_isoc_read_callback */ 12853671d9d8SAndrew Thompson 12863671d9d8SAndrew Thompson /* 12873671d9d8SAndrew Thompson * Helper function. Called from ubt_isoc_read_callback() to read 12883671d9d8SAndrew Thompson * SCO data from one frame. 12893671d9d8SAndrew Thompson * USB context. 12903671d9d8SAndrew Thompson */ 12913671d9d8SAndrew Thompson 12923671d9d8SAndrew Thompson static int 1293760bc48eSAndrew Thompson ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no) 12943671d9d8SAndrew Thompson { 1295ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 1296ed6d949aSAndrew Thompson struct usb_page_cache *pc; 12973671d9d8SAndrew Thompson struct mbuf *m; 1298ed6d949aSAndrew Thompson int len, want, got, total; 12993671d9d8SAndrew Thompson 13003671d9d8SAndrew Thompson /* Get existing SCO reassembly buffer */ 1301ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 13023671d9d8SAndrew Thompson m = sc->sc_isoc_in_buffer; 13038f9e0ef9SAndrew Thompson total = usbd_xfer_frame_len(xfer, frame_no); 13043671d9d8SAndrew Thompson 13053671d9d8SAndrew Thompson /* While we have data in the frame */ 1306ed6d949aSAndrew Thompson while (total > 0) { 13073671d9d8SAndrew Thompson if (m == NULL) { 13083671d9d8SAndrew Thompson /* Start new reassembly buffer */ 1309eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 13103671d9d8SAndrew Thompson if (m == NULL) { 13113671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 13123671d9d8SAndrew Thompson return (-1); /* XXX out of sync! */ 13133671d9d8SAndrew Thompson } 13143671d9d8SAndrew Thompson 13152a8c860fSRobert Watson if (!(MCLGET(m, M_NOWAIT))) { 13163671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 13173671d9d8SAndrew Thompson NG_FREE_M(m); 13183671d9d8SAndrew Thompson return (-1); /* XXX out of sync! */ 13193671d9d8SAndrew Thompson } 13203671d9d8SAndrew Thompson 13213671d9d8SAndrew Thompson /* Expect SCO header */ 13223671d9d8SAndrew Thompson *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; 13233671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = got = 1; 13243671d9d8SAndrew Thompson want = sizeof(ng_hci_scodata_pkt_t); 13253671d9d8SAndrew Thompson } else { 13263671d9d8SAndrew Thompson /* 13273671d9d8SAndrew Thompson * Check if we have SCO header and if so 13283671d9d8SAndrew Thompson * adjust amount of data we want 13293671d9d8SAndrew Thompson */ 13303671d9d8SAndrew Thompson got = m->m_pkthdr.len; 13313671d9d8SAndrew Thompson want = sizeof(ng_hci_scodata_pkt_t); 13323671d9d8SAndrew Thompson 13333671d9d8SAndrew Thompson if (got >= want) 13343671d9d8SAndrew Thompson want += mtod(m, ng_hci_scodata_pkt_t *)->length; 13353671d9d8SAndrew Thompson } 13363671d9d8SAndrew Thompson 13373671d9d8SAndrew Thompson /* Append frame data to the SCO reassembly buffer */ 1338ed6d949aSAndrew Thompson len = total; 13393671d9d8SAndrew Thompson if (got + len > want) 13403671d9d8SAndrew Thompson len = want - got; 13413671d9d8SAndrew Thompson 1342ed6d949aSAndrew Thompson usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer), 13433671d9d8SAndrew Thompson mtod(m, uint8_t *) + m->m_pkthdr.len, len); 13443671d9d8SAndrew Thompson 13453671d9d8SAndrew Thompson m->m_pkthdr.len += len; 13463671d9d8SAndrew Thompson m->m_len += len; 1347ed6d949aSAndrew Thompson total -= len; 13483671d9d8SAndrew Thompson 13493671d9d8SAndrew Thompson /* Check if we got everything we wanted, if not - continue */ 13503671d9d8SAndrew Thompson if (got != want) 13513671d9d8SAndrew Thompson continue; 13523671d9d8SAndrew Thompson 13533671d9d8SAndrew Thompson /* If we got here then we got complete SCO frame */ 13543671d9d8SAndrew Thompson UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ 13553671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, 13563671d9d8SAndrew Thompson mtod(m, ng_hci_scodata_pkt_t *)->length); 13573671d9d8SAndrew Thompson 13583671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc); 13593671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 13603671d9d8SAndrew Thompson 13613671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m); 13623671d9d8SAndrew Thompson /* m == NULL at this point */ 13633671d9d8SAndrew Thompson } 13643671d9d8SAndrew Thompson 13653671d9d8SAndrew Thompson /* Put SCO reassembly buffer back */ 13663671d9d8SAndrew Thompson sc->sc_isoc_in_buffer = m; 13673671d9d8SAndrew Thompson 13683671d9d8SAndrew Thompson return (0); 13693671d9d8SAndrew Thompson } /* ubt_isoc_read_one_frame */ 13703671d9d8SAndrew Thompson 13713671d9d8SAndrew Thompson /* 13723671d9d8SAndrew Thompson * Called when outgoing isoc transfer (SCO packet) has completed, i.e. 13733671d9d8SAndrew Thompson * SCO packet was sent to the device. 13743671d9d8SAndrew Thompson * USB context. 13753671d9d8SAndrew Thompson */ 13763671d9d8SAndrew Thompson 13773671d9d8SAndrew Thompson static void 1378ed6d949aSAndrew Thompson ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 13793671d9d8SAndrew Thompson { 1380ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer); 1381ed6d949aSAndrew Thompson struct usb_page_cache *pc; 13823671d9d8SAndrew Thompson struct mbuf *m; 13833671d9d8SAndrew Thompson int n, space, offset; 1384ed6d949aSAndrew Thompson int actlen, nframes; 1385ed6d949aSAndrew Thompson 1386ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1387ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 13883671d9d8SAndrew Thompson 13893671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) { 13903671d9d8SAndrew Thompson case USB_ST_TRANSFERRED: 1391ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen); 1392ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen); 13933671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc); 13943671d9d8SAndrew Thompson /* FALLTHROUGH */ 13953671d9d8SAndrew Thompson 13963671d9d8SAndrew Thompson case USB_ST_SETUP: 13973671d9d8SAndrew Thompson send_next: 13983671d9d8SAndrew Thompson offset = 0; 1399ed6d949aSAndrew Thompson space = usbd_xfer_max_framelen(xfer) * nframes; 14003671d9d8SAndrew Thompson m = NULL; 14013671d9d8SAndrew Thompson 14023671d9d8SAndrew Thompson while (space > 0) { 14033671d9d8SAndrew Thompson if (m == NULL) { 14043671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 14053671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); 14063671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14073671d9d8SAndrew Thompson 14083671d9d8SAndrew Thompson if (m == NULL) 14093671d9d8SAndrew Thompson break; 14103671d9d8SAndrew Thompson } 14113671d9d8SAndrew Thompson 14123671d9d8SAndrew Thompson n = min(space, m->m_pkthdr.len); 14133671d9d8SAndrew Thompson if (n > 0) { 1414ed6d949aSAndrew Thompson usbd_m_copy_in(pc, offset, m,0, n); 14153671d9d8SAndrew Thompson m_adj(m, n); 14163671d9d8SAndrew Thompson 14173671d9d8SAndrew Thompson offset += n; 14183671d9d8SAndrew Thompson space -= n; 14193671d9d8SAndrew Thompson } 14203671d9d8SAndrew Thompson 14213671d9d8SAndrew Thompson if (m->m_pkthdr.len == 0) 14223671d9d8SAndrew Thompson NG_FREE_M(m); /* sets m = NULL */ 14233671d9d8SAndrew Thompson } 14243671d9d8SAndrew Thompson 14253671d9d8SAndrew Thompson /* Put whatever is left from mbuf back on queue */ 14263671d9d8SAndrew Thompson if (m != NULL) { 14273671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 14283671d9d8SAndrew Thompson NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); 14293671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14303671d9d8SAndrew Thompson } 14313671d9d8SAndrew Thompson 14323671d9d8SAndrew Thompson /* 14333671d9d8SAndrew Thompson * Calculate sizes for isoc frames. 14343671d9d8SAndrew Thompson * Note that offset could be 0 at this point (i.e. we have 14353671d9d8SAndrew Thompson * nothing to send). That is fine, as we have isoc. transfers 14363671d9d8SAndrew Thompson * going in both directions all the time. In this case it 14373671d9d8SAndrew Thompson * would be just empty isoc. transfer. 14383671d9d8SAndrew Thompson */ 14393671d9d8SAndrew Thompson 1440ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) { 1441ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, 1442ed6d949aSAndrew Thompson min(offset, usbd_xfer_max_framelen(xfer))); 14438f9e0ef9SAndrew Thompson offset -= usbd_xfer_frame_len(xfer, n); 14443671d9d8SAndrew Thompson } 14453671d9d8SAndrew Thompson 1446a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 14473671d9d8SAndrew Thompson break; 14483671d9d8SAndrew Thompson 14493671d9d8SAndrew Thompson default: /* Error */ 1450ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 14513671d9d8SAndrew Thompson UBT_STAT_OERROR(sc); 14523671d9d8SAndrew Thompson goto send_next; 14533671d9d8SAndrew Thompson } 14543671d9d8SAndrew Thompson 14553671d9d8SAndrew Thompson /* transfer cancelled */ 14563671d9d8SAndrew Thompson break; 14573671d9d8SAndrew Thompson } 14583671d9d8SAndrew Thompson } 14593671d9d8SAndrew Thompson 14603671d9d8SAndrew Thompson /* 14613671d9d8SAndrew Thompson * Utility function to forward provided mbuf upstream (i.e. up the stack). 14623671d9d8SAndrew Thompson * Modifies value of the mbuf pointer (sets it to NULL). 14633671d9d8SAndrew Thompson * Save to call from any context. 14643671d9d8SAndrew Thompson */ 14653671d9d8SAndrew Thompson 14663671d9d8SAndrew Thompson static int 14673671d9d8SAndrew Thompson ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) 14683671d9d8SAndrew Thompson { 14693671d9d8SAndrew Thompson hook_p hook; 14703671d9d8SAndrew Thompson int error; 14713671d9d8SAndrew Thompson 14723671d9d8SAndrew Thompson /* 14733671d9d8SAndrew Thompson * Close the race with Netgraph hook newhook/disconnect methods. 14743671d9d8SAndrew Thompson * Save the hook pointer atomically. Two cases are possible: 14753671d9d8SAndrew Thompson * 14763671d9d8SAndrew Thompson * 1) The hook pointer is NULL. It means disconnect method got 14773671d9d8SAndrew Thompson * there first. In this case we are done. 14783671d9d8SAndrew Thompson * 14793671d9d8SAndrew Thompson * 2) The hook pointer is not NULL. It means that hook pointer 14803671d9d8SAndrew Thompson * could be either in valid or invalid (i.e. in the process 14813671d9d8SAndrew Thompson * of disconnect) state. In any case grab an extra reference 14823671d9d8SAndrew Thompson * to protect the hook pointer. 14833671d9d8SAndrew Thompson * 14843671d9d8SAndrew Thompson * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as 14853671d9d8SAndrew Thompson * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). 14863671d9d8SAndrew Thompson */ 14873671d9d8SAndrew Thompson 14883671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 14893671d9d8SAndrew Thompson if ((hook = sc->sc_hook) != NULL) 14903671d9d8SAndrew Thompson NG_HOOK_REF(hook); 14913671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 14923671d9d8SAndrew Thompson 14933671d9d8SAndrew Thompson if (hook == NULL) { 14943671d9d8SAndrew Thompson NG_FREE_M(*m); 14953671d9d8SAndrew Thompson return (ENETDOWN); 14963671d9d8SAndrew Thompson } 14973671d9d8SAndrew Thompson 14983671d9d8SAndrew Thompson NG_SEND_DATA_ONLY(error, hook, *m); 14993671d9d8SAndrew Thompson NG_HOOK_UNREF(hook); 15003671d9d8SAndrew Thompson 15013671d9d8SAndrew Thompson if (error != 0) 15023671d9d8SAndrew Thompson UBT_STAT_IERROR(sc); 15033671d9d8SAndrew Thompson 15043671d9d8SAndrew Thompson return (error); 15053671d9d8SAndrew Thompson } /* ubt_fwd_mbuf_up */ 15063671d9d8SAndrew Thompson 15073671d9d8SAndrew Thompson /**************************************************************************** 15083671d9d8SAndrew Thompson **************************************************************************** 15093671d9d8SAndrew Thompson ** Glue 15103671d9d8SAndrew Thompson **************************************************************************** 15113671d9d8SAndrew Thompson ****************************************************************************/ 15123671d9d8SAndrew Thompson 15133671d9d8SAndrew Thompson /* 15143671d9d8SAndrew Thompson * Schedule glue task. Should be called with sc_ng_mtx held. 15153671d9d8SAndrew Thompson * Netgraph context. 15163671d9d8SAndrew Thompson */ 15173671d9d8SAndrew Thompson 15183671d9d8SAndrew Thompson static void 15193671d9d8SAndrew Thompson ubt_task_schedule(ubt_softc_p sc, int action) 15203671d9d8SAndrew Thompson { 15213671d9d8SAndrew Thompson mtx_assert(&sc->sc_ng_mtx, MA_OWNED); 15223671d9d8SAndrew Thompson 15233671d9d8SAndrew Thompson /* 15243671d9d8SAndrew Thompson * Try to handle corner case when "start all" and "stop all" 15253671d9d8SAndrew Thompson * actions can both be set before task is executed. 15263671d9d8SAndrew Thompson * 15273671d9d8SAndrew Thompson * The rules are 15283671d9d8SAndrew Thompson * 15293671d9d8SAndrew Thompson * sc_task_flags action new sc_task_flags 15303671d9d8SAndrew Thompson * ------------------------------------------------------ 15313671d9d8SAndrew Thompson * 0 start start 15323671d9d8SAndrew Thompson * 0 stop stop 15333671d9d8SAndrew Thompson * start start start 15343671d9d8SAndrew Thompson * start stop stop 15353671d9d8SAndrew Thompson * stop start stop|start 15363671d9d8SAndrew Thompson * stop stop stop 15373671d9d8SAndrew Thompson * stop|start start stop|start 15383671d9d8SAndrew Thompson * stop|start stop stop 15393671d9d8SAndrew Thompson */ 15403671d9d8SAndrew Thompson 15413671d9d8SAndrew Thompson if (action != 0) { 15423671d9d8SAndrew Thompson if ((action & UBT_FLAG_T_STOP_ALL) != 0) 15433671d9d8SAndrew Thompson sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; 15443671d9d8SAndrew Thompson 15453671d9d8SAndrew Thompson sc->sc_task_flags |= action; 15463671d9d8SAndrew Thompson } 15473671d9d8SAndrew Thompson 15483671d9d8SAndrew Thompson if (sc->sc_task_flags & UBT_FLAG_T_PENDING) 15493671d9d8SAndrew Thompson return; 15503671d9d8SAndrew Thompson 15513671d9d8SAndrew Thompson if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { 15523671d9d8SAndrew Thompson sc->sc_task_flags |= UBT_FLAG_T_PENDING; 15533671d9d8SAndrew Thompson return; 15543671d9d8SAndrew Thompson } 15553671d9d8SAndrew Thompson 15563671d9d8SAndrew Thompson /* XXX: i think this should never happen */ 15573671d9d8SAndrew Thompson } /* ubt_task_schedule */ 15583671d9d8SAndrew Thompson 15593671d9d8SAndrew Thompson /* 15603671d9d8SAndrew Thompson * Glue task. Examines sc_task_flags and does things depending on it. 15613671d9d8SAndrew Thompson * Taskqueue context. 15623671d9d8SAndrew Thompson */ 15633671d9d8SAndrew Thompson 15643671d9d8SAndrew Thompson static void 15653671d9d8SAndrew Thompson ubt_task(void *context, int pending) 15663671d9d8SAndrew Thompson { 15673671d9d8SAndrew Thompson ubt_softc_p sc = context; 15683671d9d8SAndrew Thompson int task_flags, i; 15693671d9d8SAndrew Thompson 15703671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 15713671d9d8SAndrew Thompson task_flags = sc->sc_task_flags; 15723671d9d8SAndrew Thompson sc->sc_task_flags = 0; 15733671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 15743671d9d8SAndrew Thompson 15753671d9d8SAndrew Thompson /* 15763671d9d8SAndrew Thompson * Stop all USB transfers synchronously. 15773671d9d8SAndrew Thompson * Stop interface #0 and #1 transfers at the same time and in the 1578a593f6b8SAndrew Thompson * same loop. usbd_transfer_drain() will do appropriate locking. 15793671d9d8SAndrew Thompson */ 15803671d9d8SAndrew Thompson 15813671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_STOP_ALL) 15823671d9d8SAndrew Thompson for (i = 0; i < UBT_N_TRANSFER; i ++) 1583a593f6b8SAndrew Thompson usbd_transfer_drain(sc->sc_xfer[i]); 15843671d9d8SAndrew Thompson 15853671d9d8SAndrew Thompson /* Start incoming interrupt and bulk, and all isoc. USB transfers */ 15863671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_ALL) { 15873671d9d8SAndrew Thompson /* 15883671d9d8SAndrew Thompson * Interface #0 15893671d9d8SAndrew Thompson */ 15903671d9d8SAndrew Thompson 15913671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 15923671d9d8SAndrew Thompson 15933671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); 15943671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); 15953671d9d8SAndrew Thompson 15963671d9d8SAndrew Thompson /* 15973671d9d8SAndrew Thompson * Interface #1 15983671d9d8SAndrew Thompson * Start both read and write isoc. transfers by default. 15993671d9d8SAndrew Thompson * Get them going all the time even if we have nothing 16003671d9d8SAndrew Thompson * to send to avoid any delays. 16013671d9d8SAndrew Thompson */ 16023671d9d8SAndrew Thompson 16033671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); 16043671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); 16053671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); 16063671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); 16073671d9d8SAndrew Thompson 16083671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 16093671d9d8SAndrew Thompson } 16103671d9d8SAndrew Thompson 16113671d9d8SAndrew Thompson /* Start outgoing control transfer */ 16123671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_CTRL) { 16133671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 16143671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); 16153671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 16163671d9d8SAndrew Thompson } 16173671d9d8SAndrew Thompson 16183671d9d8SAndrew Thompson /* Start outgoing bulk transfer */ 16193671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_BULK) { 16203671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx); 16213671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); 16223671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx); 16233671d9d8SAndrew Thompson } 16243671d9d8SAndrew Thompson } /* ubt_task */ 16253671d9d8SAndrew Thompson 16263671d9d8SAndrew Thompson /**************************************************************************** 16273671d9d8SAndrew Thompson **************************************************************************** 16283671d9d8SAndrew Thompson ** Netgraph specific 16293671d9d8SAndrew Thompson **************************************************************************** 16303671d9d8SAndrew Thompson ****************************************************************************/ 16313671d9d8SAndrew Thompson 16323671d9d8SAndrew Thompson /* 16333671d9d8SAndrew Thompson * Netgraph node constructor. Do not allow to create node of this type. 16343671d9d8SAndrew Thompson * Netgraph context. 16353671d9d8SAndrew Thompson */ 16363671d9d8SAndrew Thompson 16373671d9d8SAndrew Thompson static int 16383671d9d8SAndrew Thompson ng_ubt_constructor(node_p node) 16393671d9d8SAndrew Thompson { 16403671d9d8SAndrew Thompson return (EINVAL); 16413671d9d8SAndrew Thompson } /* ng_ubt_constructor */ 16423671d9d8SAndrew Thompson 16433671d9d8SAndrew Thompson /* 16443671d9d8SAndrew Thompson * Netgraph node destructor. Destroy node only when device has been detached. 16453671d9d8SAndrew Thompson * Netgraph context. 16463671d9d8SAndrew Thompson */ 16473671d9d8SAndrew Thompson 16483671d9d8SAndrew Thompson static int 16493671d9d8SAndrew Thompson ng_ubt_shutdown(node_p node) 16503671d9d8SAndrew Thompson { 16513671d9d8SAndrew Thompson if (node->nd_flags & NGF_REALLY_DIE) { 16523671d9d8SAndrew Thompson /* 16533671d9d8SAndrew Thompson * We came here because the USB device is being 1654053359b7SPedro F. Giffuni * detached, so stop being persistent. 16553671d9d8SAndrew Thompson */ 16563671d9d8SAndrew Thompson NG_NODE_SET_PRIVATE(node, NULL); 16573671d9d8SAndrew Thompson NG_NODE_UNREF(node); 16583671d9d8SAndrew Thompson } else 16593671d9d8SAndrew Thompson NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ 16603671d9d8SAndrew Thompson 16613671d9d8SAndrew Thompson return (0); 16623671d9d8SAndrew Thompson } /* ng_ubt_shutdown */ 16633671d9d8SAndrew Thompson 16643671d9d8SAndrew Thompson /* 16653671d9d8SAndrew Thompson * Create new hook. There can only be one. 16663671d9d8SAndrew Thompson * Netgraph context. 16673671d9d8SAndrew Thompson */ 16683671d9d8SAndrew Thompson 16693671d9d8SAndrew Thompson static int 16703671d9d8SAndrew Thompson ng_ubt_newhook(node_p node, hook_p hook, char const *name) 16713671d9d8SAndrew Thompson { 16723671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(node); 16733671d9d8SAndrew Thompson 16743671d9d8SAndrew Thompson if (strcmp(name, NG_UBT_HOOK) != 0) 16753671d9d8SAndrew Thompson return (EINVAL); 16763671d9d8SAndrew Thompson 16773671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 16783671d9d8SAndrew Thompson if (sc->sc_hook != NULL) { 16793671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 16803671d9d8SAndrew Thompson 16813671d9d8SAndrew Thompson return (EISCONN); 16823671d9d8SAndrew Thompson } 16833671d9d8SAndrew Thompson 16843671d9d8SAndrew Thompson sc->sc_hook = hook; 16853671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 16863671d9d8SAndrew Thompson 16873671d9d8SAndrew Thompson return (0); 16883671d9d8SAndrew Thompson } /* ng_ubt_newhook */ 16893671d9d8SAndrew Thompson 16903671d9d8SAndrew Thompson /* 16913671d9d8SAndrew Thompson * Connect hook. Start incoming USB transfers. 16923671d9d8SAndrew Thompson * Netgraph context. 16933671d9d8SAndrew Thompson */ 16943671d9d8SAndrew Thompson 16953671d9d8SAndrew Thompson static int 16963671d9d8SAndrew Thompson ng_ubt_connect(hook_p hook) 16973671d9d8SAndrew Thompson { 16983671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 16993671d9d8SAndrew Thompson 17003671d9d8SAndrew Thompson NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 17013671d9d8SAndrew Thompson 17023671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 17033671d9d8SAndrew Thompson ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); 17043671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 17053671d9d8SAndrew Thompson 17063671d9d8SAndrew Thompson return (0); 17073671d9d8SAndrew Thompson } /* ng_ubt_connect */ 17083671d9d8SAndrew Thompson 17093671d9d8SAndrew Thompson /* 17103671d9d8SAndrew Thompson * Disconnect hook. 17113671d9d8SAndrew Thompson * Netgraph context. 17123671d9d8SAndrew Thompson */ 17133671d9d8SAndrew Thompson 17143671d9d8SAndrew Thompson static int 17153671d9d8SAndrew Thompson ng_ubt_disconnect(hook_p hook) 17163671d9d8SAndrew Thompson { 17173671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 17183671d9d8SAndrew Thompson 17193671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 17203671d9d8SAndrew Thompson 17213671d9d8SAndrew Thompson if (hook != sc->sc_hook) { 17223671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 17233671d9d8SAndrew Thompson 17243671d9d8SAndrew Thompson return (EINVAL); 17253671d9d8SAndrew Thompson } 17263671d9d8SAndrew Thompson 17273671d9d8SAndrew Thompson sc->sc_hook = NULL; 17283671d9d8SAndrew Thompson 17293671d9d8SAndrew Thompson /* Kick off task to stop all USB xfers */ 17303671d9d8SAndrew Thompson ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); 17313671d9d8SAndrew Thompson 17323671d9d8SAndrew Thompson /* Drain queues */ 17333671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); 17343671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); 17353671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); 17363671d9d8SAndrew Thompson 17373671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 17383671d9d8SAndrew Thompson 17393671d9d8SAndrew Thompson return (0); 17403671d9d8SAndrew Thompson } /* ng_ubt_disconnect */ 17413671d9d8SAndrew Thompson 17423671d9d8SAndrew Thompson /* 17433671d9d8SAndrew Thompson * Process control message. 17443671d9d8SAndrew Thompson * Netgraph context. 17453671d9d8SAndrew Thompson */ 17463671d9d8SAndrew Thompson 17473671d9d8SAndrew Thompson static int 17483671d9d8SAndrew Thompson ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) 17493671d9d8SAndrew Thompson { 17503671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(node); 17513671d9d8SAndrew Thompson struct ng_mesg *msg, *rsp = NULL; 17523671d9d8SAndrew Thompson struct ng_bt_mbufq *q; 17533671d9d8SAndrew Thompson int error = 0, queue, qlen; 17543671d9d8SAndrew Thompson 17553671d9d8SAndrew Thompson NGI_GET_MSG(item, msg); 17563671d9d8SAndrew Thompson 17573671d9d8SAndrew Thompson switch (msg->header.typecookie) { 17583671d9d8SAndrew Thompson case NGM_GENERIC_COOKIE: 17593671d9d8SAndrew Thompson switch (msg->header.cmd) { 17603671d9d8SAndrew Thompson case NGM_TEXT_STATUS: 17613671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 17623671d9d8SAndrew Thompson if (rsp == NULL) { 17633671d9d8SAndrew Thompson error = ENOMEM; 17643671d9d8SAndrew Thompson break; 17653671d9d8SAndrew Thompson } 17663671d9d8SAndrew Thompson 17673671d9d8SAndrew Thompson snprintf(rsp->data, NG_TEXTRESPONSE, 17683671d9d8SAndrew Thompson "Hook: %s\n" \ 17693671d9d8SAndrew Thompson "Task flags: %#x\n" \ 17703671d9d8SAndrew Thompson "Debug: %d\n" \ 17713671d9d8SAndrew Thompson "CMD queue: [have:%d,max:%d]\n" \ 17723671d9d8SAndrew Thompson "ACL queue: [have:%d,max:%d]\n" \ 17733671d9d8SAndrew Thompson "SCO queue: [have:%d,max:%d]", 17743671d9d8SAndrew Thompson (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", 17753671d9d8SAndrew Thompson sc->sc_task_flags, 17763671d9d8SAndrew Thompson sc->sc_debug, 17773671d9d8SAndrew Thompson sc->sc_cmdq.len, 17783671d9d8SAndrew Thompson sc->sc_cmdq.maxlen, 17793671d9d8SAndrew Thompson sc->sc_aclq.len, 17803671d9d8SAndrew Thompson sc->sc_aclq.maxlen, 17813671d9d8SAndrew Thompson sc->sc_scoq.len, 17823671d9d8SAndrew Thompson sc->sc_scoq.maxlen); 17833671d9d8SAndrew Thompson break; 17843671d9d8SAndrew Thompson 17853671d9d8SAndrew Thompson default: 17863671d9d8SAndrew Thompson error = EINVAL; 17873671d9d8SAndrew Thompson break; 17883671d9d8SAndrew Thompson } 17893671d9d8SAndrew Thompson break; 17903671d9d8SAndrew Thompson 17913671d9d8SAndrew Thompson case NGM_UBT_COOKIE: 17923671d9d8SAndrew Thompson switch (msg->header.cmd) { 17933671d9d8SAndrew Thompson case NGM_UBT_NODE_SET_DEBUG: 17943671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ 17953671d9d8SAndrew Thompson error = EMSGSIZE; 17963671d9d8SAndrew Thompson break; 17973671d9d8SAndrew Thompson } 17983671d9d8SAndrew Thompson 17993671d9d8SAndrew Thompson sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); 18003671d9d8SAndrew Thompson break; 18013671d9d8SAndrew Thompson 18023671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_DEBUG: 18033671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), 18043671d9d8SAndrew Thompson M_NOWAIT); 18053671d9d8SAndrew Thompson if (rsp == NULL) { 18063671d9d8SAndrew Thompson error = ENOMEM; 18073671d9d8SAndrew Thompson break; 18083671d9d8SAndrew Thompson } 18093671d9d8SAndrew Thompson 18103671d9d8SAndrew Thompson *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; 18113671d9d8SAndrew Thompson break; 18123671d9d8SAndrew Thompson 18133671d9d8SAndrew Thompson case NGM_UBT_NODE_SET_QLEN: 18143671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 18153671d9d8SAndrew Thompson error = EMSGSIZE; 18163671d9d8SAndrew Thompson break; 18173671d9d8SAndrew Thompson } 18183671d9d8SAndrew Thompson 18193671d9d8SAndrew Thompson queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 18203671d9d8SAndrew Thompson qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; 18213671d9d8SAndrew Thompson 18223671d9d8SAndrew Thompson switch (queue) { 18233671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_CMD: 18243671d9d8SAndrew Thompson q = &sc->sc_cmdq; 18253671d9d8SAndrew Thompson break; 18263671d9d8SAndrew Thompson 18273671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_ACL: 18283671d9d8SAndrew Thompson q = &sc->sc_aclq; 18293671d9d8SAndrew Thompson break; 18303671d9d8SAndrew Thompson 18313671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_SCO: 18323671d9d8SAndrew Thompson q = &sc->sc_scoq; 18333671d9d8SAndrew Thompson break; 18343671d9d8SAndrew Thompson 18353671d9d8SAndrew Thompson default: 18363671d9d8SAndrew Thompson error = EINVAL; 18373671d9d8SAndrew Thompson goto done; 18383671d9d8SAndrew Thompson /* NOT REACHED */ 18393671d9d8SAndrew Thompson } 18403671d9d8SAndrew Thompson 18413671d9d8SAndrew Thompson q->maxlen = qlen; 18423671d9d8SAndrew Thompson break; 18433671d9d8SAndrew Thompson 18443671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_QLEN: 18453671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 18463671d9d8SAndrew Thompson error = EMSGSIZE; 18473671d9d8SAndrew Thompson break; 18483671d9d8SAndrew Thompson } 18493671d9d8SAndrew Thompson 18503671d9d8SAndrew Thompson queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 18513671d9d8SAndrew Thompson 18523671d9d8SAndrew Thompson switch (queue) { 18533671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_CMD: 18543671d9d8SAndrew Thompson q = &sc->sc_cmdq; 18553671d9d8SAndrew Thompson break; 18563671d9d8SAndrew Thompson 18573671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_ACL: 18583671d9d8SAndrew Thompson q = &sc->sc_aclq; 18593671d9d8SAndrew Thompson break; 18603671d9d8SAndrew Thompson 18613671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_SCO: 18623671d9d8SAndrew Thompson q = &sc->sc_scoq; 18633671d9d8SAndrew Thompson break; 18643671d9d8SAndrew Thompson 18653671d9d8SAndrew Thompson default: 18663671d9d8SAndrew Thompson error = EINVAL; 18673671d9d8SAndrew Thompson goto done; 18683671d9d8SAndrew Thompson /* NOT REACHED */ 18693671d9d8SAndrew Thompson } 18703671d9d8SAndrew Thompson 18713671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), 18723671d9d8SAndrew Thompson M_NOWAIT); 18733671d9d8SAndrew Thompson if (rsp == NULL) { 18743671d9d8SAndrew Thompson error = ENOMEM; 18753671d9d8SAndrew Thompson break; 18763671d9d8SAndrew Thompson } 18773671d9d8SAndrew Thompson 18783671d9d8SAndrew Thompson ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; 18793671d9d8SAndrew Thompson ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; 18803671d9d8SAndrew Thompson break; 18813671d9d8SAndrew Thompson 18823671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_STAT: 18833671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), 18843671d9d8SAndrew Thompson M_NOWAIT); 18853671d9d8SAndrew Thompson if (rsp == NULL) { 18863671d9d8SAndrew Thompson error = ENOMEM; 18873671d9d8SAndrew Thompson break; 18883671d9d8SAndrew Thompson } 18893671d9d8SAndrew Thompson 18903671d9d8SAndrew Thompson bcopy(&sc->sc_stat, rsp->data, 18913671d9d8SAndrew Thompson sizeof(ng_ubt_node_stat_ep)); 18923671d9d8SAndrew Thompson break; 18933671d9d8SAndrew Thompson 18943671d9d8SAndrew Thompson case NGM_UBT_NODE_RESET_STAT: 18953671d9d8SAndrew Thompson UBT_STAT_RESET(sc); 18963671d9d8SAndrew Thompson break; 18973671d9d8SAndrew Thompson 18983671d9d8SAndrew Thompson default: 18993671d9d8SAndrew Thompson error = EINVAL; 19003671d9d8SAndrew Thompson break; 19013671d9d8SAndrew Thompson } 19023671d9d8SAndrew Thompson break; 19033671d9d8SAndrew Thompson 19043671d9d8SAndrew Thompson default: 19053671d9d8SAndrew Thompson error = EINVAL; 19063671d9d8SAndrew Thompson break; 19073671d9d8SAndrew Thompson } 19083671d9d8SAndrew Thompson done: 19093671d9d8SAndrew Thompson NG_RESPOND_MSG(error, node, item, rsp); 19103671d9d8SAndrew Thompson NG_FREE_MSG(msg); 19113671d9d8SAndrew Thompson 19123671d9d8SAndrew Thompson return (error); 19133671d9d8SAndrew Thompson } /* ng_ubt_rcvmsg */ 19143671d9d8SAndrew Thompson 19153671d9d8SAndrew Thompson /* 19163671d9d8SAndrew Thompson * Process data. 19173671d9d8SAndrew Thompson * Netgraph context. 19183671d9d8SAndrew Thompson */ 19193671d9d8SAndrew Thompson 19203671d9d8SAndrew Thompson static int 19213671d9d8SAndrew Thompson ng_ubt_rcvdata(hook_p hook, item_p item) 19223671d9d8SAndrew Thompson { 19233671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 19243671d9d8SAndrew Thompson struct mbuf *m; 19253671d9d8SAndrew Thompson struct ng_bt_mbufq *q; 19263671d9d8SAndrew Thompson int action, error = 0; 19273671d9d8SAndrew Thompson 19283671d9d8SAndrew Thompson if (hook != sc->sc_hook) { 19293671d9d8SAndrew Thompson error = EINVAL; 19303671d9d8SAndrew Thompson goto done; 19313671d9d8SAndrew Thompson } 19323671d9d8SAndrew Thompson 19333671d9d8SAndrew Thompson /* Deatch mbuf and get HCI frame type */ 19343671d9d8SAndrew Thompson NGI_GET_M(item, m); 19353671d9d8SAndrew Thompson 19363671d9d8SAndrew Thompson /* 19373671d9d8SAndrew Thompson * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, 19383671d9d8SAndrew Thompson * 2 bytes connection handle and at least 1 byte of length. 19393671d9d8SAndrew Thompson * Panic on data frame that has size smaller than 4 bytes (it 19403671d9d8SAndrew Thompson * should not happen) 19413671d9d8SAndrew Thompson */ 19423671d9d8SAndrew Thompson 19433671d9d8SAndrew Thompson if (m->m_pkthdr.len < 4) 19443671d9d8SAndrew Thompson panic("HCI frame size is too small! pktlen=%d\n", 19453671d9d8SAndrew Thompson m->m_pkthdr.len); 19463671d9d8SAndrew Thompson 19473671d9d8SAndrew Thompson /* Process HCI frame */ 19483671d9d8SAndrew Thompson switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ 19493671d9d8SAndrew Thompson case NG_HCI_CMD_PKT: 19506d917491SHans Petter Selasky if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE) 19513671d9d8SAndrew Thompson panic("HCI command frame size is too big! " \ 19523671d9d8SAndrew Thompson "buffer size=%zd, packet len=%d\n", 19533671d9d8SAndrew Thompson UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); 19543671d9d8SAndrew Thompson 19553671d9d8SAndrew Thompson q = &sc->sc_cmdq; 19563671d9d8SAndrew Thompson action = UBT_FLAG_T_START_CTRL; 19573671d9d8SAndrew Thompson break; 19583671d9d8SAndrew Thompson 19593671d9d8SAndrew Thompson case NG_HCI_ACL_DATA_PKT: 19603671d9d8SAndrew Thompson if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) 19613671d9d8SAndrew Thompson panic("ACL data frame size is too big! " \ 19623671d9d8SAndrew Thompson "buffer size=%d, packet len=%d\n", 19633671d9d8SAndrew Thompson UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); 19643671d9d8SAndrew Thompson 19653671d9d8SAndrew Thompson q = &sc->sc_aclq; 19663671d9d8SAndrew Thompson action = UBT_FLAG_T_START_BULK; 19673671d9d8SAndrew Thompson break; 19683671d9d8SAndrew Thompson 19693671d9d8SAndrew Thompson case NG_HCI_SCO_DATA_PKT: 19703671d9d8SAndrew Thompson q = &sc->sc_scoq; 19713671d9d8SAndrew Thompson action = 0; 19723671d9d8SAndrew Thompson break; 19733671d9d8SAndrew Thompson 19743671d9d8SAndrew Thompson default: 19753671d9d8SAndrew Thompson UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ 19763671d9d8SAndrew Thompson "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len); 19773671d9d8SAndrew Thompson 19783671d9d8SAndrew Thompson NG_FREE_M(m); 19793671d9d8SAndrew Thompson error = EINVAL; 19803671d9d8SAndrew Thompson goto done; 19813671d9d8SAndrew Thompson /* NOT REACHED */ 19823671d9d8SAndrew Thompson } 19833671d9d8SAndrew Thompson 19843671d9d8SAndrew Thompson UBT_NG_LOCK(sc); 19853671d9d8SAndrew Thompson if (NG_BT_MBUFQ_FULL(q)) { 19863671d9d8SAndrew Thompson NG_BT_MBUFQ_DROP(q); 19873671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 19883671d9d8SAndrew Thompson 19893671d9d8SAndrew Thompson UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", 19903671d9d8SAndrew Thompson *mtod(m, uint8_t *), m->m_pkthdr.len); 19913671d9d8SAndrew Thompson 19923671d9d8SAndrew Thompson NG_FREE_M(m); 19933671d9d8SAndrew Thompson } else { 19943671d9d8SAndrew Thompson /* Loose HCI packet type, enqueue mbuf and kick off task */ 19953671d9d8SAndrew Thompson m_adj(m, sizeof(uint8_t)); 19963671d9d8SAndrew Thompson NG_BT_MBUFQ_ENQUEUE(q, m); 19973671d9d8SAndrew Thompson ubt_task_schedule(sc, action); 19983671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc); 19993671d9d8SAndrew Thompson } 20003671d9d8SAndrew Thompson done: 20013671d9d8SAndrew Thompson NG_FREE_ITEM(item); 20023671d9d8SAndrew Thompson 20033671d9d8SAndrew Thompson return (error); 20043671d9d8SAndrew Thompson } /* ng_ubt_rcvdata */ 20053671d9d8SAndrew Thompson 20063671d9d8SAndrew Thompson /**************************************************************************** 20073671d9d8SAndrew Thompson **************************************************************************** 20083671d9d8SAndrew Thompson ** Module 20093671d9d8SAndrew Thompson **************************************************************************** 20103671d9d8SAndrew Thompson ****************************************************************************/ 20113671d9d8SAndrew Thompson 20123671d9d8SAndrew Thompson /* 20133671d9d8SAndrew Thompson * Load/Unload the driver module 20143671d9d8SAndrew Thompson */ 20153671d9d8SAndrew Thompson 20163671d9d8SAndrew Thompson static int 20173671d9d8SAndrew Thompson ubt_modevent(module_t mod, int event, void *data) 20183671d9d8SAndrew Thompson { 20193671d9d8SAndrew Thompson int error; 20203671d9d8SAndrew Thompson 20213671d9d8SAndrew Thompson switch (event) { 20223671d9d8SAndrew Thompson case MOD_LOAD: 20233671d9d8SAndrew Thompson error = ng_newtype(&typestruct); 20243671d9d8SAndrew Thompson if (error != 0) 20253671d9d8SAndrew Thompson printf("%s: Could not register Netgraph node type, " \ 20263671d9d8SAndrew Thompson "error=%d\n", NG_UBT_NODE_TYPE, error); 20273671d9d8SAndrew Thompson break; 20283671d9d8SAndrew Thompson 20293671d9d8SAndrew Thompson case MOD_UNLOAD: 20303671d9d8SAndrew Thompson error = ng_rmtype(&typestruct); 20313671d9d8SAndrew Thompson break; 20323671d9d8SAndrew Thompson 20333671d9d8SAndrew Thompson default: 20343671d9d8SAndrew Thompson error = EOPNOTSUPP; 20353671d9d8SAndrew Thompson break; 20363671d9d8SAndrew Thompson } 20373671d9d8SAndrew Thompson 20383671d9d8SAndrew Thompson return (error); 20393671d9d8SAndrew Thompson } /* ubt_modevent */ 20403671d9d8SAndrew Thompson 20413671d9d8SAndrew Thompson static device_method_t ubt_methods[] = 20423671d9d8SAndrew Thompson { 20433671d9d8SAndrew Thompson DEVMETHOD(device_probe, ubt_probe), 20443671d9d8SAndrew Thompson DEVMETHOD(device_attach, ubt_attach), 20453671d9d8SAndrew Thompson DEVMETHOD(device_detach, ubt_detach), 2046b42a2049SRaphael Kubo da Costa DEVMETHOD_END 20473671d9d8SAndrew Thompson }; 20483671d9d8SAndrew Thompson 20493544d43bSVladimir Kondratyev driver_t ubt_driver = 20503671d9d8SAndrew Thompson { 20513671d9d8SAndrew Thompson .name = "ubt", 20523671d9d8SAndrew Thompson .methods = ubt_methods, 20533671d9d8SAndrew Thompson .size = sizeof(struct ubt_softc), 20543671d9d8SAndrew Thompson }; 20553671d9d8SAndrew Thompson 2056d83185d9SJohn Baldwin DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_modevent, 0); 20573671d9d8SAndrew Thompson MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); 20583671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 20593671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 20608fa95293SHans Petter Selasky MODULE_DEPEND(ng_ubt, ng_bluetooth, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 20613671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, usb, 1, 1, 1); 2062f809f280SWarner Losh USB_PNP_HOST_INFO(ubt_devs); 2063