1184610Salfred/* 2184610Salfred * ng_ubt.c 3184610Salfred */ 4184610Salfred 5184610Salfred/*- 6187494Semax * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7184610Salfred * All rights reserved. 8184610Salfred * 9184610Salfred * Redistribution and use in source and binary forms, with or without 10184610Salfred * modification, are permitted provided that the following conditions 11184610Salfred * are met: 12184610Salfred * 1. Redistributions of source code must retain the above copyright 13184610Salfred * notice, this list of conditions and the following disclaimer. 14184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 15184610Salfred * notice, this list of conditions and the following disclaimer in the 16184610Salfred * documentation and/or other materials provided with the distribution. 17184610Salfred * 18184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28184610Salfred * SUCH DAMAGE. 29184610Salfred * 30184610Salfred * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $ 31184610Salfred * $FreeBSD$ 32184610Salfred */ 33184610Salfred 34187494Semax/* 35187494Semax * NOTE: ng_ubt2 driver has a split personality. On one side it is 36187741Semax * a USB device driver and on the other it is a Netgraph node. This 37187494Semax * driver will *NOT* create traditional /dev/ enties, only Netgraph 38187494Semax * node. 39187494Semax * 40187741Semax * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes) 41187494Semax * 42187741Semax * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used 43187741Semax * by USB for any USB request going over device's interface #0 and #1, 44187741Semax * i.e. interrupt, control, bulk and isoc. transfers. 45187494Semax * 46187741Semax * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph 47187741Semax * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook 48187741Semax * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact, 49187741Semax * think of it as a spin lock. 50187494Semax * 51187494Semax * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts. 52187494Semax * 53187494Semax * 1) USB context. This is where all the USB related stuff happens. All 54187741Semax * callbacks run in this context. All callbacks are called (by USB) with 55187494Semax * appropriate interface lock held. It is (generally) allowed to grab 56187494Semax * any additional locks. 57187494Semax * 58187494Semax * 2) Netgraph context. This is where all the Netgraph related stuff happens. 59187494Semax * Since we mark node as WRITER, the Netgraph node will be "locked" (from 60187494Semax * Netgraph point of view). Any variable that is only modified from the 61187494Semax * Netgraph context does not require any additonal locking. It is generally 62187741Semax * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT* 63187741Semax * grab any lock in the Netgraph context that could cause de-scheduling of 64187741Semax * the Netgraph thread for significant amount of time. In fact, the only 65187741Semax * lock that is allowed in the Netgraph context is the sc_ng_mtx lock. 66187741Semax * Also make sure that any code that is called from the Netgraph context 67187741Semax * follows the rule above. 68187494Semax * 69187741Semax * 3) Taskqueue context. This is where ubt_task runs. Since we are generally 70187741Semax * NOT allowed to grab any lock that could cause de-scheduling in the 71187741Semax * Netgraph context, and, USB requires us to grab interface lock before 72187741Semax * doing things with transfers, it is safer to transition from the Netgraph 73187741Semax * context to the Taskqueue context before we can call into USB subsystem. 74187494Semax * 75187494Semax * So, to put everything together, the rules are as follows. 76187494Semax * It is OK to call from the USB context or the Taskqueue context into 77187494Semax * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words 78187494Semax * it is allowed to call into the Netgraph context with locks held. 79187494Semax * Is it *NOT* OK to call from the Netgraph context into the USB context, 80187741Semax * because USB requires us to grab interface locks, and, it is safer to 81187741Semax * avoid it. So, to make things safer we set task flags to indicate which 82187741Semax * actions we want to perform and schedule ubt_task which would run in the 83187741Semax * Taskqueue context. 84187494Semax * Is is OK to call from the Taskqueue context into the USB context, 85187494Semax * and, ubt_task does just that (i.e. grabs appropriate interface locks 86187741Semax * before calling into USB). 87187741Semax * Access to the outgoing queues, task flags and hook pointer is 88187741Semax * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again, 89187741Semax * sc_ng_mtx should really be a spin lock (and it is very likely to an 90189002Sed * equivalent of spin lock due to adaptive nature of FreeBSD mutexes). 91187741Semax * All USB callbacks accept softc pointer as a private data. USB ensures 92187741Semax * that this pointer is valid. 93187494Semax */ 94187494Semax 95194677Sthompsa#include <sys/stdint.h> 96194677Sthompsa#include <sys/stddef.h> 97194677Sthompsa#include <sys/param.h> 98194677Sthompsa#include <sys/queue.h> 99194677Sthompsa#include <sys/types.h> 100194677Sthompsa#include <sys/systm.h> 101194677Sthompsa#include <sys/kernel.h> 102194677Sthompsa#include <sys/bus.h> 103194677Sthompsa#include <sys/module.h> 104194677Sthompsa#include <sys/lock.h> 105194677Sthompsa#include <sys/mutex.h> 106194677Sthompsa#include <sys/condvar.h> 107194677Sthompsa#include <sys/sysctl.h> 108194677Sthompsa#include <sys/sx.h> 109194677Sthompsa#include <sys/unistd.h> 110194677Sthompsa#include <sys/callout.h> 111194677Sthompsa#include <sys/malloc.h> 112194677Sthompsa#include <sys/priv.h> 113194677Sthompsa 114188746Sthompsa#include "usbdevs.h" 115188942Sthompsa#include <dev/usb/usb.h> 116194677Sthompsa#include <dev/usb/usbdi.h> 117194677Sthompsa#include <dev/usb/usbdi_util.h> 118184610Salfred 119194228Sthompsa#define USB_DEBUG_VAR usb_debug 120188942Sthompsa#include <dev/usb/usb_debug.h> 121188942Sthompsa#include <dev/usb/usb_busdma.h> 122184610Salfred 123184610Salfred#include <sys/mbuf.h> 124187494Semax#include <sys/taskqueue.h> 125184610Salfred 126184610Salfred#include <netgraph/ng_message.h> 127184610Salfred#include <netgraph/netgraph.h> 128184610Salfred#include <netgraph/ng_parse.h> 129184610Salfred#include <netgraph/bluetooth/include/ng_bluetooth.h> 130184610Salfred#include <netgraph/bluetooth/include/ng_hci.h> 131184610Salfred#include <netgraph/bluetooth/include/ng_ubt.h> 132192909Sthompsa#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h> 133184610Salfred 134187494Semaxstatic int ubt_modevent(module_t, int, void *); 135187494Semaxstatic device_probe_t ubt_probe; 136187494Semaxstatic device_attach_t ubt_attach; 137187494Semaxstatic device_detach_t ubt_detach; 138184610Salfred 139187741Semaxstatic void ubt_task_schedule(ubt_softc_p, int); 140187494Semaxstatic task_fn_t ubt_task; 141184610Salfred 142194228Sthompsa#define ubt_xfer_start(sc, i) usbd_transfer_start((sc)->sc_xfer[(i)]) 143187741Semax 144187494Semax/* Netgraph methods */ 145187494Semaxstatic ng_constructor_t ng_ubt_constructor; 146187494Semaxstatic ng_shutdown_t ng_ubt_shutdown; 147187494Semaxstatic ng_newhook_t ng_ubt_newhook; 148187494Semaxstatic ng_connect_t ng_ubt_connect; 149187494Semaxstatic ng_disconnect_t ng_ubt_disconnect; 150187494Semaxstatic ng_rcvmsg_t ng_ubt_rcvmsg; 151187494Semaxstatic ng_rcvdata_t ng_ubt_rcvdata; 152184610Salfred 153184610Salfred/* Queue length */ 154187494Semaxstatic const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = 155184610Salfred{ 156187494Semax { "queue", &ng_parse_int32_type, }, 157187494Semax { "qlen", &ng_parse_int32_type, }, 158187494Semax { NULL, } 159184610Salfred}; 160187494Semaxstatic const struct ng_parse_type ng_ubt_node_qlen_type = 161187494Semax{ 162184610Salfred &ng_parse_struct_type, 163184610Salfred &ng_ubt_node_qlen_type_fields 164184610Salfred}; 165184610Salfred 166184610Salfred/* Stat info */ 167187494Semaxstatic const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = 168184610Salfred{ 169187494Semax { "pckts_recv", &ng_parse_uint32_type, }, 170187494Semax { "bytes_recv", &ng_parse_uint32_type, }, 171187494Semax { "pckts_sent", &ng_parse_uint32_type, }, 172187494Semax { "bytes_sent", &ng_parse_uint32_type, }, 173187494Semax { "oerrors", &ng_parse_uint32_type, }, 174187494Semax { "ierrors", &ng_parse_uint32_type, }, 175187494Semax { NULL, } 176184610Salfred}; 177187494Semaxstatic const struct ng_parse_type ng_ubt_node_stat_type = 178187494Semax{ 179184610Salfred &ng_parse_struct_type, 180184610Salfred &ng_ubt_node_stat_type_fields 181184610Salfred}; 182184610Salfred 183184610Salfred/* Netgraph node command list */ 184187494Semaxstatic const struct ng_cmdlist ng_ubt_cmdlist[] = 185187494Semax{ 186184610Salfred { 187184610Salfred NGM_UBT_COOKIE, 188184610Salfred NGM_UBT_NODE_SET_DEBUG, 189184610Salfred "set_debug", 190184610Salfred &ng_parse_uint16_type, 191184610Salfred NULL 192184610Salfred }, 193184610Salfred { 194184610Salfred NGM_UBT_COOKIE, 195184610Salfred NGM_UBT_NODE_GET_DEBUG, 196184610Salfred "get_debug", 197184610Salfred NULL, 198184610Salfred &ng_parse_uint16_type 199184610Salfred }, 200184610Salfred { 201184610Salfred NGM_UBT_COOKIE, 202184610Salfred NGM_UBT_NODE_SET_QLEN, 203184610Salfred "set_qlen", 204184610Salfred &ng_ubt_node_qlen_type, 205184610Salfred NULL 206184610Salfred }, 207184610Salfred { 208184610Salfred NGM_UBT_COOKIE, 209184610Salfred NGM_UBT_NODE_GET_QLEN, 210184610Salfred "get_qlen", 211184610Salfred &ng_ubt_node_qlen_type, 212184610Salfred &ng_ubt_node_qlen_type 213184610Salfred }, 214184610Salfred { 215184610Salfred NGM_UBT_COOKIE, 216184610Salfred NGM_UBT_NODE_GET_STAT, 217184610Salfred "get_stat", 218184610Salfred NULL, 219184610Salfred &ng_ubt_node_stat_type 220184610Salfred }, 221184610Salfred { 222184610Salfred NGM_UBT_COOKIE, 223184610Salfred NGM_UBT_NODE_RESET_STAT, 224184610Salfred "reset_stat", 225184610Salfred NULL, 226184610Salfred NULL 227184610Salfred }, 228187494Semax { 0, } 229184610Salfred}; 230184610Salfred 231184610Salfred/* Netgraph node type */ 232187494Semaxstatic struct ng_type typestruct = 233187494Semax{ 234187494Semax .version = NG_ABI_VERSION, 235187494Semax .name = NG_UBT_NODE_TYPE, 236187494Semax .constructor = ng_ubt_constructor, 237187494Semax .rcvmsg = ng_ubt_rcvmsg, 238187494Semax .shutdown = ng_ubt_shutdown, 239187494Semax .newhook = ng_ubt_newhook, 240187494Semax .connect = ng_ubt_connect, 241187494Semax .rcvdata = ng_ubt_rcvdata, 242187494Semax .disconnect = ng_ubt_disconnect, 243187494Semax .cmdlist = ng_ubt_cmdlist 244184610Salfred}; 245184610Salfred 246187494Semax/**************************************************************************** 247187494Semax **************************************************************************** 248187494Semax ** USB specific 249187494Semax **************************************************************************** 250187494Semax ****************************************************************************/ 251187494Semax 252184610Salfred/* USB methods */ 253193045Sthompsastatic usb_callback_t ubt_ctrl_write_callback; 254193045Sthompsastatic usb_callback_t ubt_intr_read_callback; 255193045Sthompsastatic usb_callback_t ubt_bulk_read_callback; 256193045Sthompsastatic usb_callback_t ubt_bulk_write_callback; 257193045Sthompsastatic usb_callback_t ubt_isoc_read_callback; 258193045Sthompsastatic usb_callback_t ubt_isoc_write_callback; 259184610Salfred 260187741Semaxstatic int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **); 261192984Sthompsastatic int ubt_isoc_read_one_frame(struct usb_xfer *, int); 262184610Salfred 263187494Semax/* 264187494Semax * USB config 265187494Semax * 266187494Semax * The following desribes usb transfers that could be submitted on USB device. 267187494Semax * 268187494Semax * Interface 0 on the USB device must present the following endpoints 269187494Semax * 1) Interrupt endpoint to receive HCI events 270187494Semax * 2) Bulk IN endpoint to receive ACL data 271187494Semax * 3) Bulk OUT endpoint to send ACL data 272187494Semax * 273187494Semax * Interface 1 on the USB device must present the following endpoints 274187494Semax * 1) Isochronous IN endpoint to receive SCO data 275187494Semax * 2) Isochronous OUT endpoint to send SCO data 276187494Semax */ 277184610Salfred 278192984Sthompsastatic const struct usb_config ubt_config[UBT_N_TRANSFER] = 279187494Semax{ 280187494Semax /* 281187494Semax * Interface #0 282187494Semax */ 283184610Salfred 284187494Semax /* Outgoing bulk transfer - ACL packets */ 285187494Semax [UBT_IF_0_BULK_DT_WR] = { 286187494Semax .type = UE_BULK, 287187494Semax .endpoint = UE_ADDR_ANY, 288187494Semax .direction = UE_DIR_OUT, 289187741Semax .if_index = 0, 290190734Sthompsa .bufsize = UBT_BULK_WRITE_BUFFER_SIZE, 291190734Sthompsa .flags = { .pipe_bof = 1, .force_short_xfer = 1, }, 292190734Sthompsa .callback = &ubt_bulk_write_callback, 293184610Salfred }, 294187494Semax /* Incoming bulk transfer - ACL packets */ 295187494Semax [UBT_IF_0_BULK_DT_RD] = { 296187494Semax .type = UE_BULK, 297187494Semax .endpoint = UE_ADDR_ANY, 298187494Semax .direction = UE_DIR_IN, 299187741Semax .if_index = 0, 300190734Sthompsa .bufsize = UBT_BULK_READ_BUFFER_SIZE, 301190734Sthompsa .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 302190734Sthompsa .callback = &ubt_bulk_read_callback, 303184610Salfred }, 304187494Semax /* Incoming interrupt transfer - HCI events */ 305187494Semax [UBT_IF_0_INTR_DT_RD] = { 306187494Semax .type = UE_INTERRUPT, 307187494Semax .endpoint = UE_ADDR_ANY, 308187494Semax .direction = UE_DIR_IN, 309187741Semax .if_index = 0, 310190734Sthompsa .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 311190734Sthompsa .bufsize = UBT_INTR_BUFFER_SIZE, 312190734Sthompsa .callback = &ubt_intr_read_callback, 313184610Salfred }, 314187494Semax /* Outgoing control transfer - HCI commands */ 315187494Semax [UBT_IF_0_CTRL_DT_WR] = { 316187494Semax .type = UE_CONTROL, 317187494Semax .endpoint = 0x00, /* control pipe */ 318187494Semax .direction = UE_DIR_ANY, 319187741Semax .if_index = 0, 320190734Sthompsa .bufsize = UBT_CTRL_BUFFER_SIZE, 321190734Sthompsa .callback = &ubt_ctrl_write_callback, 322190734Sthompsa .timeout = 5000, /* 5 seconds */ 323184610Salfred }, 324184610Salfred 325187494Semax /* 326187494Semax * Interface #1 327187494Semax */ 328184610Salfred 329187494Semax /* Incoming isochronous transfer #1 - SCO packets */ 330187494Semax [UBT_IF_1_ISOC_DT_RD1] = { 331187494Semax .type = UE_ISOCHRONOUS, 332187494Semax .endpoint = UE_ADDR_ANY, 333187494Semax .direction = UE_DIR_IN, 334187741Semax .if_index = 1, 335190734Sthompsa .bufsize = 0, /* use "wMaxPacketSize * frames" */ 336190734Sthompsa .frames = UBT_ISOC_NFRAMES, 337190734Sthompsa .flags = { .short_xfer_ok = 1, }, 338190734Sthompsa .callback = &ubt_isoc_read_callback, 339184610Salfred }, 340187494Semax /* Incoming isochronous transfer #2 - SCO packets */ 341187494Semax [UBT_IF_1_ISOC_DT_RD2] = { 342187494Semax .type = UE_ISOCHRONOUS, 343187494Semax .endpoint = UE_ADDR_ANY, 344187494Semax .direction = UE_DIR_IN, 345187741Semax .if_index = 1, 346190734Sthompsa .bufsize = 0, /* use "wMaxPacketSize * frames" */ 347190734Sthompsa .frames = UBT_ISOC_NFRAMES, 348190734Sthompsa .flags = { .short_xfer_ok = 1, }, 349190734Sthompsa .callback = &ubt_isoc_read_callback, 350184610Salfred }, 351187494Semax /* Outgoing isochronous transfer #1 - SCO packets */ 352187494Semax [UBT_IF_1_ISOC_DT_WR1] = { 353187494Semax .type = UE_ISOCHRONOUS, 354187494Semax .endpoint = UE_ADDR_ANY, 355187494Semax .direction = UE_DIR_OUT, 356187741Semax .if_index = 1, 357190734Sthompsa .bufsize = 0, /* use "wMaxPacketSize * frames" */ 358190734Sthompsa .frames = UBT_ISOC_NFRAMES, 359190734Sthompsa .flags = { .short_xfer_ok = 1, }, 360190734Sthompsa .callback = &ubt_isoc_write_callback, 361184610Salfred }, 362187494Semax /* Outgoing isochronous transfer #2 - SCO packets */ 363187494Semax [UBT_IF_1_ISOC_DT_WR2] = { 364187494Semax .type = UE_ISOCHRONOUS, 365187494Semax .endpoint = UE_ADDR_ANY, 366187494Semax .direction = UE_DIR_OUT, 367187741Semax .if_index = 1, 368190734Sthompsa .bufsize = 0, /* use "wMaxPacketSize * frames" */ 369190734Sthompsa .frames = UBT_ISOC_NFRAMES, 370190734Sthompsa .flags = { .short_xfer_ok = 1, }, 371190734Sthompsa .callback = &ubt_isoc_write_callback, 372184610Salfred }, 373184610Salfred}; 374184610Salfred 375184610Salfred/* 376184610Salfred * If for some reason device should not be attached then put 377184610Salfred * VendorID/ProductID pair into the list below. The format is 378184610Salfred * as follows: 379184610Salfred * 380187494Semax * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, 381184610Salfred * 382184610Salfred * where VENDOR_ID and PRODUCT_ID are hex numbers. 383184610Salfred */ 384187741Semax 385223486Shselaskystatic const STRUCT_USB_HOST_ID ubt_ignore_devs[] = 386187741Semax{ 387184610Salfred /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */ 388187494Semax { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) }, 389249178Sadrian 390249178Sadrian /* Atheros 3011 with sflash firmware */ 391249178Sadrian { USB_VPI(0x0cf3, 0x3002, 0) }, 392249178Sadrian { USB_VPI(0x0cf3, 0xe019, 0) }, 393249178Sadrian { USB_VPI(0x13d3, 0x3304, 0) }, 394249178Sadrian { USB_VPI(0x0930, 0x0215, 0) }, 395249178Sadrian { USB_VPI(0x0489, 0xe03d, 0) }, 396249178Sadrian { USB_VPI(0x0489, 0xe027, 0) }, 397249178Sadrian 398249178Sadrian /* Atheros AR9285 Malbec with sflash firmware */ 399249178Sadrian { USB_VPI(0x03f0, 0x311d, 0) }, 400249178Sadrian 401249178Sadrian /* Atheros 3012 with sflash firmware */ 402249178Sadrian { USB_VPI(0x0cf3, 0x3004, 0) }, 403249178Sadrian { USB_VPI(0x0cf3, 0x311d, 0) }, 404249178Sadrian { USB_VPI(0x13d3, 0x3375, 0) }, 405249178Sadrian { USB_VPI(0x04ca, 0x3005, 0) }, 406249178Sadrian { USB_VPI(0x04ca, 0x3006, 0) }, 407249178Sadrian { USB_VPI(0x04ca, 0x3008, 0) }, 408249178Sadrian { USB_VPI(0x13d3, 0x3362, 0) }, 409249178Sadrian { USB_VPI(0x0cf3, 0xe004, 0) }, 410249178Sadrian { USB_VPI(0x0930, 0x0219, 0) }, 411249178Sadrian { USB_VPI(0x0489, 0xe057, 0) }, 412249178Sadrian { USB_VPI(0x13d3, 0x3393, 0) }, 413249178Sadrian { USB_VPI(0x0489, 0xe04e, 0) }, 414249178Sadrian { USB_VPI(0x0489, 0xe056, 0) }, 415249178Sadrian 416249178Sadrian /* Atheros AR5BBU12 with sflash firmware */ 417249178Sadrian { USB_VPI(0x0489, 0xe02c, 0) }, 418249178Sadrian 419249178Sadrian /* Atheros AR5BBU12 with sflash firmware */ 420249178Sadrian { USB_VPI(0x0489, 0xe03c, 0) }, 421249178Sadrian { USB_VPI(0x0489, 0xe036, 0) }, 422184610Salfred}; 423184610Salfred 424184610Salfred/* List of supported bluetooth devices */ 425223486Shselaskystatic const STRUCT_USB_HOST_ID ubt_devs[] = 426187741Semax{ 427187494Semax /* Generic Bluetooth class devices */ 428187494Semax { USB_IFACE_CLASS(UDCLASS_WIRELESS), 429187494Semax USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 430187494Semax USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 431184610Salfred 432184610Salfred /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ 433187494Semax { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) }, 434244704Sglebius 435244704Sglebius /* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */ 436244704Sglebius { USB_VENDOR(USB_VENDOR_BROADCOM), 437244714Srakuco USB_IFACE_CLASS(UICLASS_VENDOR), 438244714Srakuco USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 439244714Srakuco USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 440255090Shselasky 441255090Shselasky /* Apple-specific (Broadcom) devices */ 442255090Shselasky { USB_VENDOR(USB_VENDOR_APPLE), 443255090Shselasky USB_IFACE_CLASS(UICLASS_VENDOR), 444255090Shselasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 445255090Shselasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 446255090Shselasky 447255090Shselasky /* Foxconn - Hon Hai */ 448255090Shselasky { USB_VENDOR(USB_VENDOR_FOXCONN), 449255090Shselasky USB_IFACE_CLASS(UICLASS_VENDOR), 450255090Shselasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 451255090Shselasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 452255090Shselasky 453255090Shselasky /* MediaTek MT76x0E */ 454255090Shselasky { USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) }, 455255090Shselasky 456255090Shselasky /* Broadcom SoftSailing reporting vendor specific */ 457255090Shselasky { USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) }, 458255090Shselasky 459255090Shselasky /* Apple MacBookPro 7,1 */ 460255090Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) }, 461255090Shselasky 462255090Shselasky /* Apple iMac11,1 */ 463255090Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) }, 464255090Shselasky 465255090Shselasky /* Apple MacBookPro6,2 */ 466255090Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) }, 467255090Shselasky 468255090Shselasky /* Apple MacBookAir3,1, MacBookAir3,2 */ 469255090Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) }, 470255090Shselasky 471255090Shselasky /* Apple MacBookAir4,1 */ 472255090Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) }, 473255090Shselasky 474255090Shselasky /* MacBookAir6,1 */ 475255090Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) }, 476255090Shselasky 477255090Shselasky /* Apple MacBookPro8,2 */ 478255090Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) }, 479255090Shselasky 480255090Shselasky /* Apple MacMini5,1 */ 481255090Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) }, 482255090Shselasky 483255090Shselasky /* Bluetooth Ultraport Module from IBM */ 484255090Shselasky { USB_VPI(USB_VENDOR_TDK, 0x030a, 0) }, 485255090Shselasky 486255090Shselasky /* ALPS Modules with non-standard ID */ 487255090Shselasky { USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) }, 488255090Shselasky { USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) }, 489255090Shselasky 490255090Shselasky { USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) }, 491255090Shselasky 492255090Shselasky /* Canyon CN-BTU1 with HID interfaces */ 493255090Shselasky { USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) }, 494255090Shselasky 495255090Shselasky /* Broadcom BCM20702A0 */ 496255090Shselasky { USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) }, 497255128Seadler { USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) }, 498255090Shselasky { USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) }, 499255090Shselasky { USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) }, 500255090Shselasky { USB_VPI(USB_VENDOR_DELL, 0x8197, 0) }, 501184610Salfred}; 502184610Salfred 503184610Salfred/* 504187494Semax * Probe for a USB Bluetooth device. 505187494Semax * USB context. 506184610Salfred */ 507184610Salfred 508184610Salfredstatic int 509184610Salfredubt_probe(device_t dev) 510184610Salfred{ 511192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 512222055Savg int error; 513184610Salfred 514192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) 515184610Salfred return (ENXIO); 516187494Semax 517187494Semax if (uaa->info.bIfaceIndex != 0) 518184610Salfred return (ENXIO); 519187494Semax 520194228Sthompsa if (usbd_lookup_id_by_uaa(ubt_ignore_devs, 521187494Semax sizeof(ubt_ignore_devs), uaa) == 0) 522184610Salfred return (ENXIO); 523187494Semax 524222055Savg error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa); 525222055Savg if (error == 0) 526222055Savg return (BUS_PROBE_GENERIC); 527222055Savg return (error); 528187494Semax} /* ubt_probe */ 529184610Salfred 530184610Salfred/* 531187494Semax * Attach the device. 532187494Semax * USB context. 533184610Salfred */ 534184610Salfred 535184610Salfredstatic int 536184610Salfredubt_attach(device_t dev) 537184610Salfred{ 538192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 539187494Semax struct ubt_softc *sc = device_get_softc(dev); 540192984Sthompsa struct usb_endpoint_descriptor *ed; 541192984Sthompsa struct usb_interface_descriptor *id; 542241078Shselasky struct usb_interface *iface; 543187494Semax uint16_t wMaxPacketSize; 544187741Semax uint8_t alt_index, i, j; 545187741Semax uint8_t iface_index[2] = { 0, 1 }; 546184610Salfred 547194228Sthompsa device_set_usb_desc(dev); 548184610Salfred 549187741Semax sc->sc_dev = dev; 550187741Semax sc->sc_debug = NG_UBT_WARN_LEVEL; 551253347Srodrigc 552187494Semax /* 553187494Semax * Create Netgraph node 554187494Semax */ 555187494Semax 556187494Semax if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { 557187741Semax UBT_ALERT(sc, "could not create Netgraph node\n"); 558187494Semax return (ENXIO); 559187494Semax } 560187494Semax 561187494Semax /* Name Netgraph node */ 562187741Semax if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { 563187741Semax UBT_ALERT(sc, "could not name Netgraph node\n"); 564187494Semax NG_NODE_UNREF(sc->sc_node); 565187494Semax return (ENXIO); 566187494Semax } 567187494Semax NG_NODE_SET_PRIVATE(sc->sc_node, sc); 568187494Semax NG_NODE_FORCE_WRITER(sc->sc_node); 569187494Semax 570184610Salfred /* 571184610Salfred * Initialize device softc structure 572184610Salfred */ 573184610Salfred 574187494Semax /* initialize locks */ 575187741Semax mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); 576187741Semax mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); 577187494Semax 578187494Semax /* initialize packet queues */ 579184610Salfred NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); 580184610Salfred NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); 581187494Semax NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); 582184610Salfred 583187494Semax /* initialize glue task */ 584187741Semax TASK_INIT(&sc->sc_task, 0, ubt_task, sc); 585184610Salfred 586184610Salfred /* 587184610Salfred * Configure Bluetooth USB device. Discover all required USB 588184610Salfred * interfaces and endpoints. 589184610Salfred * 590184610Salfred * USB device must present two interfaces: 591184610Salfred * 1) Interface 0 that has 3 endpoints 592184610Salfred * 1) Interrupt endpoint to receive HCI events 593184610Salfred * 2) Bulk IN endpoint to receive ACL data 594184610Salfred * 3) Bulk OUT endpoint to send ACL data 595184610Salfred * 596184610Salfred * 2) Interface 1 then has 2 endpoints 597184610Salfred * 1) Isochronous IN endpoint to receive SCO data 598184610Salfred * 2) Isochronous OUT endpoint to send SCO data 599184610Salfred * 600184610Salfred * Interface 1 (with isochronous endpoints) has several alternate 601184610Salfred * configurations with different packet size. 602184610Salfred */ 603184610Salfred 604184610Salfred /* 605187741Semax * For interface #1 search alternate settings, and find 606187741Semax * the descriptor with the largest wMaxPacketSize 607184610Salfred */ 608184610Salfred 609184610Salfred wMaxPacketSize = 0; 610187494Semax alt_index = 0; 611184610Salfred i = 0; 612184610Salfred j = 0; 613190728Sthompsa ed = NULL; 614187494Semax 615190728Sthompsa /* 616190728Sthompsa * Search through all the descriptors looking for the largest 617190728Sthompsa * packet size: 618190728Sthompsa */ 619194228Sthompsa while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach( 620194228Sthompsa usbd_get_config_descriptor(uaa->device), 621192984Sthompsa (struct usb_descriptor *)ed))) { 622184610Salfred 623190728Sthompsa if ((ed->bDescriptorType == UDESC_INTERFACE) && 624190728Sthompsa (ed->bLength >= sizeof(*id))) { 625192984Sthompsa id = (struct usb_interface_descriptor *)ed; 626190728Sthompsa i = id->bInterfaceNumber; 627190728Sthompsa j = id->bAlternateSetting; 628184610Salfred } 629187494Semax 630190728Sthompsa if ((ed->bDescriptorType == UDESC_ENDPOINT) && 631190728Sthompsa (ed->bLength >= sizeof(*ed)) && 632190728Sthompsa (i == 1)) { 633190728Sthompsa uint16_t temp; 634190728Sthompsa 635190728Sthompsa temp = UGETW(ed->wMaxPacketSize); 636190728Sthompsa if (temp > wMaxPacketSize) { 637190728Sthompsa wMaxPacketSize = temp; 638190728Sthompsa alt_index = j; 639190728Sthompsa } 640184610Salfred } 641184610Salfred } 642184610Salfred 643187741Semax /* Set alt configuration on interface #1 only if we found it */ 644187494Semax if (wMaxPacketSize > 0 && 645194228Sthompsa usbd_set_alt_interface_index(uaa->device, 1, alt_index)) { 646187741Semax UBT_ALERT(sc, "could not set alternate setting %d " \ 647187494Semax "for interface 1!\n", alt_index); 648184610Salfred goto detach; 649184610Salfred } 650184610Salfred 651187741Semax /* Setup transfers for both interfaces */ 652194228Sthompsa if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, 653187741Semax ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { 654187741Semax UBT_ALERT(sc, "could not allocate transfers\n"); 655184610Salfred goto detach; 656184610Salfred } 657184610Salfred 658241078Shselasky /* Claim all interfaces belonging to the Bluetooth part */ 659241078Shselasky for (i = 1;; i++) { 660241078Shselasky iface = usbd_get_iface(uaa->device, i); 661241078Shselasky if (iface == NULL) 662241078Shselasky break; 663241078Shselasky id = usbd_get_interface_descriptor(iface); 664184610Salfred 665241078Shselasky if ((id != NULL) && 666241078Shselasky (id->bInterfaceClass == UICLASS_WIRELESS) && 667241078Shselasky (id->bInterfaceSubClass == UISUBCLASS_RF) && 668241078Shselasky (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) { 669241078Shselasky usbd_set_parent_iface(uaa->device, i, 670241078Shselasky uaa->info.bIfaceIndex); 671241078Shselasky } 672241078Shselasky } 673187494Semax return (0); /* success */ 674184610Salfred 675184610Salfreddetach: 676184610Salfred ubt_detach(dev); 677184610Salfred 678184610Salfred return (ENXIO); 679187494Semax} /* ubt_attach */ 680184610Salfred 681184610Salfred/* 682187494Semax * Detach the device. 683187494Semax * USB context. 684184610Salfred */ 685184610Salfred 686184610Salfredint 687184610Salfredubt_detach(device_t dev) 688184610Salfred{ 689187494Semax struct ubt_softc *sc = device_get_softc(dev); 690187494Semax node_p node = sc->sc_node; 691184610Salfred 692187494Semax /* Destroy Netgraph node */ 693187494Semax if (node != NULL) { 694187494Semax sc->sc_node = NULL; 695187494Semax NG_NODE_REALLY_DIE(node); 696187494Semax ng_rmnode_self(node); 697184610Salfred } 698184610Salfred 699187741Semax /* Make sure ubt_task in gone */ 700187741Semax taskqueue_drain(taskqueue_swi, &sc->sc_task); 701187741Semax 702187494Semax /* Free USB transfers, if any */ 703194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); 704184610Salfred 705187494Semax /* Destroy queues */ 706187741Semax UBT_NG_LOCK(sc); 707184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); 708184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); 709184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); 710187741Semax UBT_NG_UNLOCK(sc); 711184610Salfred 712187741Semax mtx_destroy(&sc->sc_if_mtx); 713187741Semax mtx_destroy(&sc->sc_ng_mtx); 714187494Semax 715184610Salfred return (0); 716187494Semax} /* ubt_detach */ 717184610Salfred 718187494Semax/* 719187494Semax * Called when outgoing control request (HCI command) has completed, i.e. 720187494Semax * HCI command was sent to the device. 721187494Semax * USB context. 722187494Semax */ 723187494Semax 724184610Salfredstatic void 725194677Sthompsaubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 726184610Salfred{ 727194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 728192984Sthompsa struct usb_device_request req; 729187494Semax struct mbuf *m; 730194677Sthompsa struct usb_page_cache *pc; 731194677Sthompsa int actlen; 732184610Salfred 733194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 734194677Sthompsa 735184610Salfred switch (USB_GET_STATE(xfer)) { 736184610Salfred case USB_ST_TRANSFERRED: 737194677Sthompsa UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen); 738194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 739187741Semax UBT_STAT_PCKTS_SENT(sc); 740187494Semax /* FALLTHROUGH */ 741184610Salfred 742184610Salfred case USB_ST_SETUP: 743187494Semaxsend_next: 744187494Semax /* Get next command mbuf, if any */ 745187741Semax UBT_NG_LOCK(sc); 746184610Salfred NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 747187741Semax UBT_NG_UNLOCK(sc); 748184610Salfred 749184610Salfred if (m == NULL) { 750187494Semax UBT_INFO(sc, "HCI command queue is empty\n"); 751187741Semax break; /* transfer complete */ 752184610Salfred } 753184610Salfred 754187494Semax /* Initialize a USB control request and then schedule it */ 755184610Salfred bzero(&req, sizeof(req)); 756184610Salfred req.bmRequestType = UBT_HCI_REQUEST; 757184610Salfred USETW(req.wLength, m->m_pkthdr.len); 758184610Salfred 759187494Semax UBT_INFO(sc, "Sending control request, " \ 760187494Semax "bmRequestType=0x%02x, wLength=%d\n", 761187494Semax req.bmRequestType, UGETW(req.wLength)); 762184610Salfred 763194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 764194677Sthompsa usbd_copy_in(pc, 0, &req, sizeof(req)); 765194677Sthompsa pc = usbd_xfer_get_frame(xfer, 1); 766194677Sthompsa usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 767184610Salfred 768194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 769194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len); 770194677Sthompsa usbd_xfer_set_frames(xfer, 2); 771184610Salfred 772184610Salfred NG_FREE_M(m); 773184610Salfred 774194228Sthompsa usbd_transfer_submit(xfer); 775187494Semax break; 776184610Salfred 777187494Semax default: /* Error */ 778194677Sthompsa if (error != USB_ERR_CANCELLED) { 779187494Semax UBT_WARN(sc, "control transfer failed: %s\n", 780194677Sthompsa usbd_errstr(error)); 781187494Semax 782187494Semax UBT_STAT_OERROR(sc); 783187494Semax goto send_next; 784184610Salfred } 785187494Semax 786187741Semax /* transfer cancelled */ 787187494Semax break; 788184610Salfred } 789187494Semax} /* ubt_ctrl_write_callback */ 790184610Salfred 791187494Semax/* 792187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e. 793187494Semax * HCI event was received from the device. 794187494Semax * USB context. 795187494Semax */ 796187494Semax 797184610Salfredstatic void 798194677Sthompsaubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 799184610Salfred{ 800194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 801187494Semax struct mbuf *m; 802187494Semax ng_hci_event_pkt_t *hdr; 803194677Sthompsa struct usb_page_cache *pc; 804194677Sthompsa int actlen; 805184610Salfred 806194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 807194677Sthompsa 808187494Semax m = NULL; 809187494Semax 810184610Salfred switch (USB_GET_STATE(xfer)) { 811184610Salfred case USB_ST_TRANSFERRED: 812187494Semax /* Allocate a new mbuf */ 813243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 814184610Salfred if (m == NULL) { 815187494Semax UBT_STAT_IERROR(sc); 816187494Semax goto submit_next; 817184610Salfred } 818187494Semax 819243882Sglebius MCLGET(m, M_NOWAIT); 820184610Salfred if (!(m->m_flags & M_EXT)) { 821187494Semax UBT_STAT_IERROR(sc); 822187494Semax goto submit_next; 823184610Salfred } 824187494Semax 825187494Semax /* Add HCI packet type */ 826187494Semax *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; 827187494Semax m->m_pkthdr.len = m->m_len = 1; 828187494Semax 829194677Sthompsa if (actlen > MCLBYTES - 1) 830194677Sthompsa actlen = MCLBYTES - 1; 831187494Semax 832194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 833194677Sthompsa usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 834194677Sthompsa m->m_pkthdr.len += actlen; 835194677Sthompsa m->m_len += actlen; 836187494Semax 837187494Semax UBT_INFO(sc, "got %d bytes from interrupt pipe\n", 838194677Sthompsa actlen); 839187494Semax 840187494Semax /* Validate packet and send it up the stack */ 841233774Shselasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 842187494Semax UBT_INFO(sc, "HCI event packet is too short\n"); 843187494Semax 844187494Semax UBT_STAT_IERROR(sc); 845187494Semax goto submit_next; 846184610Salfred } 847184610Salfred 848187494Semax hdr = mtod(m, ng_hci_event_pkt_t *); 849187494Semax if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { 850187494Semax UBT_ERR(sc, "Invalid HCI event packet size, " \ 851187494Semax "length=%d, pktlen=%d\n", 852187494Semax hdr->length, m->m_pkthdr.len); 853184610Salfred 854187494Semax UBT_STAT_IERROR(sc); 855187494Semax goto submit_next; 856184610Salfred } 857184610Salfred 858187494Semax UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ 859187494Semax "length=%d\n", m->m_pkthdr.len, hdr->length); 860184610Salfred 861187494Semax UBT_STAT_PCKTS_RECV(sc); 862187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 863184610Salfred 864187741Semax ubt_fwd_mbuf_up(sc, &m); 865187494Semax /* m == NULL at this point */ 866187494Semax /* FALLTHROUGH */ 867184610Salfred 868184610Salfred case USB_ST_SETUP: 869187494Semaxsubmit_next: 870187494Semax NG_FREE_M(m); /* checks for m != NULL */ 871184610Salfred 872194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 873194228Sthompsa usbd_transfer_submit(xfer); 874187494Semax break; 875184610Salfred 876187494Semax default: /* Error */ 877194677Sthompsa if (error != USB_ERR_CANCELLED) { 878187494Semax UBT_WARN(sc, "interrupt transfer failed: %s\n", 879194677Sthompsa usbd_errstr(error)); 880184610Salfred 881187494Semax /* Try to clear stall first */ 882194677Sthompsa usbd_xfer_set_stall(xfer); 883187741Semax goto submit_next; 884187741Semax } 885187741Semax /* transfer cancelled */ 886187494Semax break; 887184610Salfred } 888187494Semax} /* ubt_intr_read_callback */ 889184610Salfred 890187494Semax/* 891187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e. 892187494Semax * ACL packet was received from the device. 893187494Semax * USB context. 894187494Semax */ 895187494Semax 896184610Salfredstatic void 897194677Sthompsaubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 898184610Salfred{ 899194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 900187494Semax struct mbuf *m; 901187494Semax ng_hci_acldata_pkt_t *hdr; 902194677Sthompsa struct usb_page_cache *pc; 903233774Shselasky int len; 904233774Shselasky int actlen; 905184610Salfred 906194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 907194677Sthompsa 908187494Semax m = NULL; 909184610Salfred 910184610Salfred switch (USB_GET_STATE(xfer)) { 911184610Salfred case USB_ST_TRANSFERRED: 912187494Semax /* Allocate new mbuf */ 913243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 914184610Salfred if (m == NULL) { 915187494Semax UBT_STAT_IERROR(sc); 916187494Semax goto submit_next; 917184610Salfred } 918187494Semax 919243882Sglebius MCLGET(m, M_NOWAIT); 920184610Salfred if (!(m->m_flags & M_EXT)) { 921187494Semax UBT_STAT_IERROR(sc); 922187494Semax goto submit_next; 923184610Salfred } 924184610Salfred 925187494Semax /* Add HCI packet type */ 926187494Semax *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; 927187494Semax m->m_pkthdr.len = m->m_len = 1; 928184610Salfred 929194677Sthompsa if (actlen > MCLBYTES - 1) 930194677Sthompsa actlen = MCLBYTES - 1; 931184610Salfred 932194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 933194677Sthompsa usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 934194677Sthompsa m->m_pkthdr.len += actlen; 935194677Sthompsa m->m_len += actlen; 936184610Salfred 937187494Semax UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", 938194677Sthompsa actlen); 939184610Salfred 940187494Semax /* Validate packet and send it up the stack */ 941233774Shselasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 942187494Semax UBT_INFO(sc, "HCI ACL packet is too short\n"); 943184610Salfred 944187494Semax UBT_STAT_IERROR(sc); 945187494Semax goto submit_next; 946184610Salfred } 947184610Salfred 948187494Semax hdr = mtod(m, ng_hci_acldata_pkt_t *); 949187494Semax len = le16toh(hdr->length); 950233774Shselasky if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) { 951187494Semax UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ 952187494Semax "pktlen=%d\n", len, m->m_pkthdr.len); 953184610Salfred 954187494Semax UBT_STAT_IERROR(sc); 955187494Semax goto submit_next; 956184610Salfred } 957184610Salfred 958187494Semax UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ 959187494Semax "length=%d\n", m->m_pkthdr.len, len); 960184610Salfred 961187494Semax UBT_STAT_PCKTS_RECV(sc); 962187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 963184610Salfred 964187741Semax ubt_fwd_mbuf_up(sc, &m); 965187494Semax /* m == NULL at this point */ 966187494Semax /* FALLTHOUGH */ 967184610Salfred 968187494Semax case USB_ST_SETUP: 969187494Semaxsubmit_next: 970187494Semax NG_FREE_M(m); /* checks for m != NULL */ 971184610Salfred 972194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 973194228Sthompsa usbd_transfer_submit(xfer); 974187494Semax break; 975184610Salfred 976187494Semax default: /* Error */ 977194677Sthompsa if (error != USB_ERR_CANCELLED) { 978187494Semax UBT_WARN(sc, "bulk-in transfer failed: %s\n", 979194677Sthompsa usbd_errstr(error)); 980184610Salfred 981187494Semax /* Try to clear stall first */ 982194677Sthompsa usbd_xfer_set_stall(xfer); 983187741Semax goto submit_next; 984187741Semax } 985187741Semax /* transfer cancelled */ 986187494Semax break; 987187494Semax } 988187494Semax} /* ubt_bulk_read_callback */ 989184610Salfred 990187494Semax/* 991187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e. 992187494Semax * ACL packet was sent to the device. 993187494Semax * USB context. 994187494Semax */ 995184610Salfred 996184610Salfredstatic void 997194677Sthompsaubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 998184610Salfred{ 999194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1000187494Semax struct mbuf *m; 1001194677Sthompsa struct usb_page_cache *pc; 1002194677Sthompsa int actlen; 1003184610Salfred 1004194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 1005194677Sthompsa 1006184610Salfred switch (USB_GET_STATE(xfer)) { 1007184610Salfred case USB_ST_TRANSFERRED: 1008194677Sthompsa UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen); 1009194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 1010187741Semax UBT_STAT_PCKTS_SENT(sc); 1011187494Semax /* FALLTHROUGH */ 1012184610Salfred 1013187494Semax case USB_ST_SETUP: 1014187741Semaxsend_next: 1015187494Semax /* Get next mbuf, if any */ 1016187741Semax UBT_NG_LOCK(sc); 1017184610Salfred NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); 1018187741Semax UBT_NG_UNLOCK(sc); 1019184610Salfred 1020184610Salfred if (m == NULL) { 1021187494Semax UBT_INFO(sc, "ACL data queue is empty\n"); 1022187741Semax break; /* transfer completed */ 1023184610Salfred } 1024187494Semax 1025184610Salfred /* 1026187494Semax * Copy ACL data frame back to a linear USB transfer buffer 1027187494Semax * and schedule transfer 1028184610Salfred */ 1029184610Salfred 1030194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1031194677Sthompsa usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 1032194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 1033184610Salfred 1034187494Semax UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", 1035187494Semax m->m_pkthdr.len); 1036184610Salfred 1037184610Salfred NG_FREE_M(m); 1038184610Salfred 1039194228Sthompsa usbd_transfer_submit(xfer); 1040187494Semax break; 1041184610Salfred 1042187494Semax default: /* Error */ 1043194677Sthompsa if (error != USB_ERR_CANCELLED) { 1044187494Semax UBT_WARN(sc, "bulk-out transfer failed: %s\n", 1045194677Sthompsa usbd_errstr(error)); 1046184610Salfred 1047187494Semax UBT_STAT_OERROR(sc); 1048184610Salfred 1049184610Salfred /* try to clear stall first */ 1050194677Sthompsa usbd_xfer_set_stall(xfer); 1051187741Semax goto send_next; 1052187741Semax } 1053187741Semax /* transfer cancelled */ 1054187494Semax break; 1055184610Salfred } 1056187494Semax} /* ubt_bulk_write_callback */ 1057184610Salfred 1058187494Semax/* 1059187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e. 1060187494Semax * SCO packet was received from the device. 1061187494Semax * USB context. 1062187494Semax */ 1063187494Semax 1064184610Salfredstatic void 1065194677Sthompsaubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 1066184610Salfred{ 1067194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1068187494Semax int n; 1069194677Sthompsa int actlen, nframes; 1070184610Salfred 1071194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1072194677Sthompsa 1073187494Semax switch (USB_GET_STATE(xfer)) { 1074187494Semax case USB_ST_TRANSFERRED: 1075194677Sthompsa for (n = 0; n < nframes; n ++) 1076187494Semax if (ubt_isoc_read_one_frame(xfer, n) < 0) 1077187494Semax break; 1078187494Semax /* FALLTHROUGH */ 1079184610Salfred 1080187494Semax case USB_ST_SETUP: 1081187494Semaxread_next: 1082194677Sthompsa for (n = 0; n < nframes; n ++) 1083194677Sthompsa usbd_xfer_set_frame_len(xfer, n, 1084194677Sthompsa usbd_xfer_max_framelen(xfer)); 1085184610Salfred 1086194228Sthompsa usbd_transfer_submit(xfer); 1087187494Semax break; 1088184610Salfred 1089187494Semax default: /* Error */ 1090194677Sthompsa if (error != USB_ERR_CANCELLED) { 1091187494Semax UBT_STAT_IERROR(sc); 1092187494Semax goto read_next; 1093187494Semax } 1094184610Salfred 1095187741Semax /* transfer cancelled */ 1096187494Semax break; 1097187494Semax } 1098187494Semax} /* ubt_isoc_read_callback */ 1099184610Salfred 1100187494Semax/* 1101187494Semax * Helper function. Called from ubt_isoc_read_callback() to read 1102187494Semax * SCO data from one frame. 1103187494Semax * USB context. 1104187494Semax */ 1105184610Salfred 1106187494Semaxstatic int 1107192984Sthompsaubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no) 1108187494Semax{ 1109194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1110194677Sthompsa struct usb_page_cache *pc; 1111187494Semax struct mbuf *m; 1112194677Sthompsa int len, want, got, total; 1113184610Salfred 1114187494Semax /* Get existing SCO reassembly buffer */ 1115194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1116187494Semax m = sc->sc_isoc_in_buffer; 1117194682Sthompsa total = usbd_xfer_frame_len(xfer, frame_no); 1118184610Salfred 1119187494Semax /* While we have data in the frame */ 1120194677Sthompsa while (total > 0) { 1121187494Semax if (m == NULL) { 1122187494Semax /* Start new reassembly buffer */ 1123243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 1124187494Semax if (m == NULL) { 1125187494Semax UBT_STAT_IERROR(sc); 1126187494Semax return (-1); /* XXX out of sync! */ 1127187494Semax } 1128184610Salfred 1129243882Sglebius MCLGET(m, M_NOWAIT); 1130187494Semax if (!(m->m_flags & M_EXT)) { 1131187494Semax UBT_STAT_IERROR(sc); 1132187494Semax NG_FREE_M(m); 1133187494Semax return (-1); /* XXX out of sync! */ 1134184610Salfred } 1135184610Salfred 1136187494Semax /* Expect SCO header */ 1137187494Semax *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; 1138187494Semax m->m_pkthdr.len = m->m_len = got = 1; 1139187494Semax want = sizeof(ng_hci_scodata_pkt_t); 1140187494Semax } else { 1141187494Semax /* 1142187494Semax * Check if we have SCO header and if so 1143187494Semax * adjust amount of data we want 1144187494Semax */ 1145187494Semax got = m->m_pkthdr.len; 1146187494Semax want = sizeof(ng_hci_scodata_pkt_t); 1147184610Salfred 1148187494Semax if (got >= want) 1149187494Semax want += mtod(m, ng_hci_scodata_pkt_t *)->length; 1150184610Salfred } 1151184610Salfred 1152187494Semax /* Append frame data to the SCO reassembly buffer */ 1153194677Sthompsa len = total; 1154187494Semax if (got + len > want) 1155187494Semax len = want - got; 1156184610Salfred 1157194677Sthompsa usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer), 1158187494Semax mtod(m, uint8_t *) + m->m_pkthdr.len, len); 1159184610Salfred 1160187494Semax m->m_pkthdr.len += len; 1161187494Semax m->m_len += len; 1162194677Sthompsa total -= len; 1163184610Salfred 1164187494Semax /* Check if we got everything we wanted, if not - continue */ 1165187494Semax if (got != want) 1166187494Semax continue; 1167184610Salfred 1168187494Semax /* If we got here then we got complete SCO frame */ 1169187494Semax UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ 1170187494Semax "length=%d\n", m->m_pkthdr.len, 1171187494Semax mtod(m, ng_hci_scodata_pkt_t *)->length); 1172184610Salfred 1173187494Semax UBT_STAT_PCKTS_RECV(sc); 1174187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 1175184610Salfred 1176187741Semax ubt_fwd_mbuf_up(sc, &m); 1177187494Semax /* m == NULL at this point */ 1178187494Semax } 1179184610Salfred 1180187494Semax /* Put SCO reassembly buffer back */ 1181187494Semax sc->sc_isoc_in_buffer = m; 1182184610Salfred 1183187494Semax return (0); 1184187494Semax} /* ubt_isoc_read_one_frame */ 1185184610Salfred 1186187494Semax/* 1187187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e. 1188187494Semax * SCO packet was sent to the device. 1189187494Semax * USB context. 1190187494Semax */ 1191184610Salfred 1192184610Salfredstatic void 1193194677Sthompsaubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 1194184610Salfred{ 1195194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1196194677Sthompsa struct usb_page_cache *pc; 1197187494Semax struct mbuf *m; 1198187494Semax int n, space, offset; 1199194677Sthompsa int actlen, nframes; 1200184610Salfred 1201194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1202194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1203194677Sthompsa 1204184610Salfred switch (USB_GET_STATE(xfer)) { 1205184610Salfred case USB_ST_TRANSFERRED: 1206194677Sthompsa UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen); 1207194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 1208187741Semax UBT_STAT_PCKTS_SENT(sc); 1209187494Semax /* FALLTHROUGH */ 1210184610Salfred 1211184610Salfred case USB_ST_SETUP: 1212187494Semaxsend_next: 1213184610Salfred offset = 0; 1214194677Sthompsa space = usbd_xfer_max_framelen(xfer) * nframes; 1215187494Semax m = NULL; 1216184610Salfred 1217187494Semax while (space > 0) { 1218187494Semax if (m == NULL) { 1219187741Semax UBT_NG_LOCK(sc); 1220187494Semax NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); 1221187741Semax UBT_NG_UNLOCK(sc); 1222184610Salfred 1223187494Semax if (m == NULL) 1224187494Semax break; 1225187494Semax } 1226184610Salfred 1227187494Semax n = min(space, m->m_pkthdr.len); 1228187494Semax if (n > 0) { 1229194677Sthompsa usbd_m_copy_in(pc, offset, m,0, n); 1230187494Semax m_adj(m, n); 1231184610Salfred 1232187494Semax offset += n; 1233187494Semax space -= n; 1234187494Semax } 1235184610Salfred 1236187494Semax if (m->m_pkthdr.len == 0) 1237187494Semax NG_FREE_M(m); /* sets m = NULL */ 1238187494Semax } 1239184610Salfred 1240187494Semax /* Put whatever is left from mbuf back on queue */ 1241187494Semax if (m != NULL) { 1242187741Semax UBT_NG_LOCK(sc); 1243187494Semax NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); 1244187741Semax UBT_NG_UNLOCK(sc); 1245184610Salfred } 1246184610Salfred 1247187494Semax /* 1248187494Semax * Calculate sizes for isoc frames. 1249187494Semax * Note that offset could be 0 at this point (i.e. we have 1250187494Semax * nothing to send). That is fine, as we have isoc. transfers 1251187494Semax * going in both directions all the time. In this case it 1252187494Semax * would be just empty isoc. transfer. 1253187494Semax */ 1254187494Semax 1255194677Sthompsa for (n = 0; n < nframes; n ++) { 1256194677Sthompsa usbd_xfer_set_frame_len(xfer, n, 1257194677Sthompsa min(offset, usbd_xfer_max_framelen(xfer))); 1258194682Sthompsa offset -= usbd_xfer_frame_len(xfer, n); 1259187494Semax } 1260187494Semax 1261194228Sthompsa usbd_transfer_submit(xfer); 1262187494Semax break; 1263184610Salfred 1264187494Semax default: /* Error */ 1265194677Sthompsa if (error != USB_ERR_CANCELLED) { 1266187494Semax UBT_STAT_OERROR(sc); 1267187494Semax goto send_next; 1268184610Salfred } 1269187494Semax 1270187741Semax /* transfer cancelled */ 1271187494Semax break; 1272184610Salfred } 1273184610Salfred} 1274184610Salfred 1275187741Semax/* 1276187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack). 1277187741Semax * Modifies value of the mbuf pointer (sets it to NULL). 1278187741Semax * Save to call from any context. 1279187741Semax */ 1280187741Semax 1281187741Semaxstatic int 1282187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) 1283187741Semax{ 1284187741Semax hook_p hook; 1285187741Semax int error; 1286187741Semax 1287187741Semax /* 1288187741Semax * Close the race with Netgraph hook newhook/disconnect methods. 1289187741Semax * Save the hook pointer atomically. Two cases are possible: 1290187741Semax * 1291187741Semax * 1) The hook pointer is NULL. It means disconnect method got 1292187741Semax * there first. In this case we are done. 1293187741Semax * 1294187741Semax * 2) The hook pointer is not NULL. It means that hook pointer 1295187741Semax * could be either in valid or invalid (i.e. in the process 1296187741Semax * of disconnect) state. In any case grab an extra reference 1297187741Semax * to protect the hook pointer. 1298187741Semax * 1299187741Semax * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as 1300187741Semax * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). 1301187741Semax */ 1302187741Semax 1303187741Semax UBT_NG_LOCK(sc); 1304187741Semax if ((hook = sc->sc_hook) != NULL) 1305187741Semax NG_HOOK_REF(hook); 1306187741Semax UBT_NG_UNLOCK(sc); 1307187741Semax 1308187741Semax if (hook == NULL) { 1309187741Semax NG_FREE_M(*m); 1310187741Semax return (ENETDOWN); 1311187741Semax } 1312187741Semax 1313187741Semax NG_SEND_DATA_ONLY(error, hook, *m); 1314187741Semax NG_HOOK_UNREF(hook); 1315187741Semax 1316187741Semax if (error != 0) 1317187741Semax UBT_STAT_IERROR(sc); 1318187741Semax 1319187741Semax return (error); 1320187741Semax} /* ubt_fwd_mbuf_up */ 1321187741Semax 1322184610Salfred/**************************************************************************** 1323184610Salfred **************************************************************************** 1324187494Semax ** Glue 1325184610Salfred **************************************************************************** 1326184610Salfred ****************************************************************************/ 1327184610Salfred 1328184610Salfred/* 1329187741Semax * Schedule glue task. Should be called with sc_ng_mtx held. 1330187494Semax * Netgraph context. 1331184610Salfred */ 1332184610Salfred 1333187741Semaxstatic void 1334187494Semaxubt_task_schedule(ubt_softc_p sc, int action) 1335184610Salfred{ 1336187741Semax mtx_assert(&sc->sc_ng_mtx, MA_OWNED); 1337184610Salfred 1338187741Semax /* 1339187741Semax * Try to handle corner case when "start all" and "stop all" 1340187741Semax * actions can both be set before task is executed. 1341187741Semax * 1342187741Semax * The rules are 1343187741Semax * 1344187741Semax * sc_task_flags action new sc_task_flags 1345187741Semax * ------------------------------------------------------ 1346187741Semax * 0 start start 1347187741Semax * 0 stop stop 1348187741Semax * start start start 1349187741Semax * start stop stop 1350187741Semax * stop start stop|start 1351187741Semax * stop stop stop 1352187741Semax * stop|start start stop|start 1353187741Semax * stop|start stop stop 1354187741Semax */ 1355187494Semax 1356187741Semax if (action != 0) { 1357187741Semax if ((action & UBT_FLAG_T_STOP_ALL) != 0) 1358187494Semax sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; 1359187494Semax 1360187494Semax sc->sc_task_flags |= action; 1361187494Semax } 1362187494Semax 1363187494Semax if (sc->sc_task_flags & UBT_FLAG_T_PENDING) 1364187741Semax return; 1365187494Semax 1366187494Semax if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { 1367187494Semax sc->sc_task_flags |= UBT_FLAG_T_PENDING; 1368187741Semax return; 1369187494Semax } 1370187494Semax 1371187494Semax /* XXX: i think this should never happen */ 1372187494Semax} /* ubt_task_schedule */ 1373187494Semax 1374184610Salfred/* 1375187494Semax * Glue task. Examines sc_task_flags and does things depending on it. 1376187494Semax * Taskqueue context. 1377184610Salfred */ 1378184610Salfred 1379187494Semaxstatic void 1380187494Semaxubt_task(void *context, int pending) 1381184610Salfred{ 1382187741Semax ubt_softc_p sc = context; 1383187741Semax int task_flags, i; 1384184610Salfred 1385187741Semax UBT_NG_LOCK(sc); 1386187494Semax task_flags = sc->sc_task_flags; 1387187494Semax sc->sc_task_flags = 0; 1388187741Semax UBT_NG_UNLOCK(sc); 1389187494Semax 1390187741Semax /* 1391187741Semax * Stop all USB transfers synchronously. 1392187741Semax * Stop interface #0 and #1 transfers at the same time and in the 1393194228Sthompsa * same loop. usbd_transfer_drain() will do appropriate locking. 1394187741Semax */ 1395187494Semax 1396187741Semax if (task_flags & UBT_FLAG_T_STOP_ALL) 1397187741Semax for (i = 0; i < UBT_N_TRANSFER; i ++) 1398194228Sthompsa usbd_transfer_drain(sc->sc_xfer[i]); 1399187494Semax 1400187741Semax /* Start incoming interrupt and bulk, and all isoc. USB transfers */ 1401187494Semax if (task_flags & UBT_FLAG_T_START_ALL) { 1402187494Semax /* 1403187494Semax * Interface #0 1404187494Semax */ 1405187494Semax 1406187741Semax mtx_lock(&sc->sc_if_mtx); 1407187741Semax 1408187494Semax ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); 1409187494Semax ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); 1410187494Semax 1411187494Semax /* 1412187494Semax * Interface #1 1413187494Semax * Start both read and write isoc. transfers by default. 1414187494Semax * Get them going all the time even if we have nothing 1415187494Semax * to send to avoid any delays. 1416187494Semax */ 1417187494Semax 1418187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); 1419187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); 1420187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); 1421187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); 1422187741Semax 1423187741Semax mtx_unlock(&sc->sc_if_mtx); 1424184610Salfred } 1425187494Semax 1426187494Semax /* Start outgoing control transfer */ 1427187494Semax if (task_flags & UBT_FLAG_T_START_CTRL) { 1428187741Semax mtx_lock(&sc->sc_if_mtx); 1429187494Semax ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); 1430187741Semax mtx_unlock(&sc->sc_if_mtx); 1431184610Salfred } 1432184610Salfred 1433187494Semax /* Start outgoing bulk transfer */ 1434187494Semax if (task_flags & UBT_FLAG_T_START_BULK) { 1435187741Semax mtx_lock(&sc->sc_if_mtx); 1436187494Semax ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); 1437187741Semax mtx_unlock(&sc->sc_if_mtx); 1438184610Salfred } 1439187494Semax} /* ubt_task */ 1440187494Semax 1441187494Semax/**************************************************************************** 1442187494Semax **************************************************************************** 1443187494Semax ** Netgraph specific 1444187494Semax **************************************************************************** 1445187494Semax ****************************************************************************/ 1446184610Salfred 1447187494Semax/* 1448187494Semax * Netgraph node constructor. Do not allow to create node of this type. 1449187494Semax * Netgraph context. 1450187494Semax */ 1451184610Salfred 1452187494Semaxstatic int 1453187494Semaxng_ubt_constructor(node_p node) 1454187494Semax{ 1455187494Semax return (EINVAL); 1456187494Semax} /* ng_ubt_constructor */ 1457184610Salfred 1458184610Salfred/* 1459187494Semax * Netgraph node destructor. Destroy node only when device has been detached. 1460187494Semax * Netgraph context. 1461184610Salfred */ 1462184610Salfred 1463184610Salfredstatic int 1464187494Semaxng_ubt_shutdown(node_p node) 1465184610Salfred{ 1466187494Semax if (node->nd_flags & NGF_REALLY_DIE) { 1467187494Semax /* 1468187494Semax * We came here because the USB device is being 1469187494Semax * detached, so stop being persistant. 1470187494Semax */ 1471187494Semax NG_NODE_SET_PRIVATE(node, NULL); 1472187494Semax NG_NODE_UNREF(node); 1473187494Semax } else 1474187494Semax NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ 1475184610Salfred 1476187494Semax return (0); 1477187494Semax} /* ng_ubt_shutdown */ 1478184610Salfred 1479187494Semax/* 1480187494Semax * Create new hook. There can only be one. 1481187494Semax * Netgraph context. 1482187494Semax */ 1483184610Salfred 1484187494Semaxstatic int 1485187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name) 1486187494Semax{ 1487187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1488184610Salfred 1489187494Semax if (strcmp(name, NG_UBT_HOOK) != 0) 1490187494Semax return (EINVAL); 1491184610Salfred 1492187741Semax UBT_NG_LOCK(sc); 1493187741Semax if (sc->sc_hook != NULL) { 1494187741Semax UBT_NG_UNLOCK(sc); 1495187741Semax 1496187494Semax return (EISCONN); 1497187741Semax } 1498184610Salfred 1499187494Semax sc->sc_hook = hook; 1500187741Semax UBT_NG_UNLOCK(sc); 1501184610Salfred 1502187494Semax return (0); 1503187494Semax} /* ng_ubt_newhook */ 1504184610Salfred 1505187494Semax/* 1506187494Semax * Connect hook. Start incoming USB transfers. 1507187494Semax * Netgraph context. 1508187494Semax */ 1509184610Salfred 1510187494Semaxstatic int 1511187494Semaxng_ubt_connect(hook_p hook) 1512187494Semax{ 1513187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1514184610Salfred 1515187494Semax NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 1516184610Salfred 1517187741Semax UBT_NG_LOCK(sc); 1518187494Semax ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); 1519187741Semax UBT_NG_UNLOCK(sc); 1520184610Salfred 1521184610Salfred return (0); 1522187494Semax} /* ng_ubt_connect */ 1523184610Salfred 1524184610Salfred/* 1525187494Semax * Disconnect hook. 1526187494Semax * Netgraph context. 1527184610Salfred */ 1528184610Salfred 1529184610Salfredstatic int 1530184610Salfredng_ubt_disconnect(hook_p hook) 1531184610Salfred{ 1532187741Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1533184610Salfred 1534187741Semax UBT_NG_LOCK(sc); 1535184610Salfred 1536187741Semax if (hook != sc->sc_hook) { 1537187741Semax UBT_NG_UNLOCK(sc); 1538184610Salfred 1539187494Semax return (EINVAL); 1540187741Semax } 1541184610Salfred 1542187494Semax sc->sc_hook = NULL; 1543184610Salfred 1544187741Semax /* Kick off task to stop all USB xfers */ 1545187741Semax ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); 1546184610Salfred 1547187494Semax /* Drain queues */ 1548187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); 1549187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); 1550187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); 1551184610Salfred 1552187741Semax UBT_NG_UNLOCK(sc); 1553184610Salfred 1554187494Semax return (0); 1555187494Semax} /* ng_ubt_disconnect */ 1556187494Semax 1557184610Salfred/* 1558187494Semax * Process control message. 1559187494Semax * Netgraph context. 1560184610Salfred */ 1561184610Salfred 1562184610Salfredstatic int 1563184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) 1564184610Salfred{ 1565187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1566187494Semax struct ng_mesg *msg, *rsp = NULL; 1567187494Semax struct ng_bt_mbufq *q; 1568187494Semax int error = 0, queue, qlen; 1569184610Salfred 1570184610Salfred NGI_GET_MSG(item, msg); 1571184610Salfred 1572184610Salfred switch (msg->header.typecookie) { 1573184610Salfred case NGM_GENERIC_COOKIE: 1574184610Salfred switch (msg->header.cmd) { 1575184610Salfred case NGM_TEXT_STATUS: 1576184610Salfred NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 1577187494Semax if (rsp == NULL) { 1578184610Salfred error = ENOMEM; 1579187494Semax break; 1580187494Semax } 1581187494Semax 1582187494Semax snprintf(rsp->data, NG_TEXTRESPONSE, 1583187494Semax "Hook: %s\n" \ 1584187494Semax "Task flags: %#x\n" \ 1585187494Semax "Debug: %d\n" \ 1586187494Semax "CMD queue: [have:%d,max:%d]\n" \ 1587187494Semax "ACL queue: [have:%d,max:%d]\n" \ 1588187494Semax "SCO queue: [have:%d,max:%d]", 1589187741Semax (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", 1590187494Semax sc->sc_task_flags, 1591187494Semax sc->sc_debug, 1592187494Semax sc->sc_cmdq.len, 1593187494Semax sc->sc_cmdq.maxlen, 1594187494Semax sc->sc_aclq.len, 1595187494Semax sc->sc_aclq.maxlen, 1596187494Semax sc->sc_scoq.len, 1597187494Semax sc->sc_scoq.maxlen); 1598184610Salfred break; 1599184610Salfred 1600184610Salfred default: 1601184610Salfred error = EINVAL; 1602184610Salfred break; 1603184610Salfred } 1604184610Salfred break; 1605184610Salfred 1606184610Salfred case NGM_UBT_COOKIE: 1607184610Salfred switch (msg->header.cmd) { 1608184610Salfred case NGM_UBT_NODE_SET_DEBUG: 1609187494Semax if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ 1610184610Salfred error = EMSGSIZE; 1611187494Semax break; 1612187494Semax } 1613187494Semax 1614187494Semax sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); 1615184610Salfred break; 1616184610Salfred 1617184610Salfred case NGM_UBT_NODE_GET_DEBUG: 1618184610Salfred NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), 1619184610Salfred M_NOWAIT); 1620187494Semax if (rsp == NULL) { 1621184610Salfred error = ENOMEM; 1622187494Semax break; 1623187494Semax } 1624187494Semax 1625187494Semax *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; 1626184610Salfred break; 1627184610Salfred 1628184610Salfred case NGM_UBT_NODE_SET_QLEN: 1629187494Semax if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1630184610Salfred error = EMSGSIZE; 1631187494Semax break; 1632187494Semax } 1633184610Salfred 1634187494Semax queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1635187494Semax qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; 1636184610Salfred 1637187494Semax switch (queue) { 1638187494Semax case NGM_UBT_NODE_QUEUE_CMD: 1639187494Semax q = &sc->sc_cmdq; 1640187494Semax break; 1641184610Salfred 1642187494Semax case NGM_UBT_NODE_QUEUE_ACL: 1643187494Semax q = &sc->sc_aclq; 1644187494Semax break; 1645184610Salfred 1646187494Semax case NGM_UBT_NODE_QUEUE_SCO: 1647187494Semax q = &sc->sc_scoq; 1648187494Semax break; 1649184610Salfred 1650187494Semax default: 1651187494Semax error = EINVAL; 1652187494Semax goto done; 1653187494Semax /* NOT REACHED */ 1654184610Salfred } 1655187494Semax 1656187494Semax q->maxlen = qlen; 1657184610Salfred break; 1658184610Salfred 1659184610Salfred case NGM_UBT_NODE_GET_QLEN: 1660184610Salfred if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1661184610Salfred error = EMSGSIZE; 1662184610Salfred break; 1663184610Salfred } 1664187494Semax 1665184610Salfred queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1666187494Semax 1667184610Salfred switch (queue) { 1668184610Salfred case NGM_UBT_NODE_QUEUE_CMD: 1669184610Salfred q = &sc->sc_cmdq; 1670184610Salfred break; 1671184610Salfred 1672184610Salfred case NGM_UBT_NODE_QUEUE_ACL: 1673184610Salfred q = &sc->sc_aclq; 1674184610Salfred break; 1675184610Salfred 1676184610Salfred case NGM_UBT_NODE_QUEUE_SCO: 1677184610Salfred q = &sc->sc_scoq; 1678184610Salfred break; 1679184610Salfred 1680184610Salfred default: 1681184610Salfred error = EINVAL; 1682187494Semax goto done; 1683187494Semax /* NOT REACHED */ 1684187494Semax } 1685187494Semax 1686187494Semax NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), 1687187494Semax M_NOWAIT); 1688187494Semax if (rsp == NULL) { 1689187494Semax error = ENOMEM; 1690184610Salfred break; 1691184610Salfred } 1692184610Salfred 1693187494Semax ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; 1694187494Semax ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; 1695184610Salfred break; 1696184610Salfred 1697184610Salfred case NGM_UBT_NODE_GET_STAT: 1698184610Salfred NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), 1699184610Salfred M_NOWAIT); 1700187494Semax if (rsp == NULL) { 1701184610Salfred error = ENOMEM; 1702187494Semax break; 1703184610Salfred } 1704187494Semax 1705187494Semax bcopy(&sc->sc_stat, rsp->data, 1706187494Semax sizeof(ng_ubt_node_stat_ep)); 1707184610Salfred break; 1708184610Salfred 1709184610Salfred case NGM_UBT_NODE_RESET_STAT: 1710187494Semax UBT_STAT_RESET(sc); 1711184610Salfred break; 1712184610Salfred 1713184610Salfred default: 1714184610Salfred error = EINVAL; 1715184610Salfred break; 1716184610Salfred } 1717184610Salfred break; 1718184610Salfred 1719184610Salfred default: 1720184610Salfred error = EINVAL; 1721184610Salfred break; 1722184610Salfred } 1723187494Semaxdone: 1724184610Salfred NG_RESPOND_MSG(error, node, item, rsp); 1725184610Salfred NG_FREE_MSG(msg); 1726184610Salfred 1727184610Salfred return (error); 1728187494Semax} /* ng_ubt_rcvmsg */ 1729184610Salfred 1730184610Salfred/* 1731187494Semax * Process data. 1732187494Semax * Netgraph context. 1733184610Salfred */ 1734184610Salfred 1735184610Salfredstatic int 1736184610Salfredng_ubt_rcvdata(hook_p hook, item_p item) 1737184610Salfred{ 1738187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1739187494Semax struct mbuf *m; 1740187494Semax struct ng_bt_mbufq *q; 1741187494Semax int action, error = 0; 1742184610Salfred 1743184610Salfred if (hook != sc->sc_hook) { 1744184610Salfred error = EINVAL; 1745184610Salfred goto done; 1746184610Salfred } 1747187494Semax 1748187494Semax /* Deatch mbuf and get HCI frame type */ 1749184610Salfred NGI_GET_M(item, m); 1750184610Salfred 1751187494Semax /* 1752187494Semax * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, 1753187494Semax * 2 bytes connection handle and at least 1 byte of length. 1754187494Semax * Panic on data frame that has size smaller than 4 bytes (it 1755187494Semax * should not happen) 1756187494Semax */ 1757187494Semax 1758187494Semax if (m->m_pkthdr.len < 4) 1759187494Semax panic("HCI frame size is too small! pktlen=%d\n", 1760187494Semax m->m_pkthdr.len); 1761187494Semax 1762187494Semax /* Process HCI frame */ 1763184610Salfred switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ 1764184610Salfred case NG_HCI_CMD_PKT: 1765233774Shselasky if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE) 1766187494Semax panic("HCI command frame size is too big! " \ 1767187494Semax "buffer size=%zd, packet len=%d\n", 1768187494Semax UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); 1769187494Semax 1770184610Salfred q = &sc->sc_cmdq; 1771187494Semax action = UBT_FLAG_T_START_CTRL; 1772184610Salfred break; 1773184610Salfred 1774184610Salfred case NG_HCI_ACL_DATA_PKT: 1775187494Semax if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) 1776187494Semax panic("ACL data frame size is too big! " \ 1777187494Semax "buffer size=%d, packet len=%d\n", 1778187494Semax UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); 1779187494Semax 1780184610Salfred q = &sc->sc_aclq; 1781187494Semax action = UBT_FLAG_T_START_BULK; 1782184610Salfred break; 1783184610Salfred 1784184610Salfred case NG_HCI_SCO_DATA_PKT: 1785184610Salfred q = &sc->sc_scoq; 1786187494Semax action = 0; 1787184610Salfred break; 1788184610Salfred 1789184610Salfred default: 1790187494Semax UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ 1791187494Semax "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len); 1792184610Salfred 1793184610Salfred NG_FREE_M(m); 1794184610Salfred error = EINVAL; 1795184610Salfred goto done; 1796187494Semax /* NOT REACHED */ 1797184610Salfred } 1798184610Salfred 1799187741Semax UBT_NG_LOCK(sc); 1800184610Salfred if (NG_BT_MBUFQ_FULL(q)) { 1801187494Semax NG_BT_MBUFQ_DROP(q); 1802187741Semax UBT_NG_UNLOCK(sc); 1803187494Semax 1804187494Semax UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", 1805187494Semax *mtod(m, uint8_t *), m->m_pkthdr.len); 1806187494Semax 1807184610Salfred NG_FREE_M(m); 1808184610Salfred } else { 1809187494Semax /* Loose HCI packet type, enqueue mbuf and kick off task */ 1810187494Semax m_adj(m, sizeof(uint8_t)); 1811184610Salfred NG_BT_MBUFQ_ENQUEUE(q, m); 1812187494Semax ubt_task_schedule(sc, action); 1813187741Semax UBT_NG_UNLOCK(sc); 1814184610Salfred } 1815184610Salfreddone: 1816184610Salfred NG_FREE_ITEM(item); 1817184610Salfred 1818187494Semax return (error); 1819187494Semax} /* ng_ubt_rcvdata */ 1820187494Semax 1821187494Semax/**************************************************************************** 1822187494Semax **************************************************************************** 1823187494Semax ** Module 1824187494Semax **************************************************************************** 1825187494Semax ****************************************************************************/ 1826187494Semax 1827187494Semax/* 1828187494Semax * Load/Unload the driver module 1829187494Semax */ 1830187494Semax 1831187494Semaxstatic int 1832187494Semaxubt_modevent(module_t mod, int event, void *data) 1833187494Semax{ 1834187494Semax int error; 1835187494Semax 1836187494Semax switch (event) { 1837187494Semax case MOD_LOAD: 1838187494Semax error = ng_newtype(&typestruct); 1839187494Semax if (error != 0) 1840187494Semax printf("%s: Could not register Netgraph node type, " \ 1841187494Semax "error=%d\n", NG_UBT_NODE_TYPE, error); 1842187494Semax break; 1843187494Semax 1844187494Semax case MOD_UNLOAD: 1845187494Semax error = ng_rmtype(&typestruct); 1846187494Semax break; 1847187494Semax 1848187494Semax default: 1849187494Semax error = EOPNOTSUPP; 1850187494Semax break; 1851184610Salfred } 1852187494Semax 1853184610Salfred return (error); 1854187494Semax} /* ubt_modevent */ 1855187494Semax 1856187494Semaxstatic devclass_t ubt_devclass; 1857187494Semax 1858187494Semaxstatic device_method_t ubt_methods[] = 1859187494Semax{ 1860187494Semax DEVMETHOD(device_probe, ubt_probe), 1861187494Semax DEVMETHOD(device_attach, ubt_attach), 1862187494Semax DEVMETHOD(device_detach, ubt_detach), 1863244715Srakuco DEVMETHOD_END 1864187494Semax}; 1865187494Semax 1866187494Semaxstatic driver_t ubt_driver = 1867187494Semax{ 1868187494Semax .name = "ubt", 1869187494Semax .methods = ubt_methods, 1870187494Semax .size = sizeof(struct ubt_softc), 1871187494Semax}; 1872187494Semax 1873189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0); 1874187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); 1875187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 1876187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 1877188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1); 1878187494Semax 1879