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