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) }, 389184610Salfred}; 390184610Salfred 391184610Salfred/* List of supported bluetooth devices */ 392223486Shselaskystatic const STRUCT_USB_HOST_ID ubt_devs[] = 393187741Semax{ 394187494Semax /* Generic Bluetooth class devices */ 395187494Semax { USB_IFACE_CLASS(UDCLASS_WIRELESS), 396187494Semax USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 397187494Semax USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 398184610Salfred 399184610Salfred /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ 400187494Semax { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) }, 401244956Shselasky 402244956Shselasky /* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */ 403244956Shselasky { USB_VENDOR(USB_VENDOR_BROADCOM), 404244956Shselasky USB_IFACE_CLASS(UICLASS_VENDOR), 405244956Shselasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 406244956Shselasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 407255345Shselasky 408255345Shselasky /* Apple-specific (Broadcom) devices */ 409255345Shselasky { USB_VENDOR(USB_VENDOR_APPLE), 410255345Shselasky USB_IFACE_CLASS(UICLASS_VENDOR), 411255345Shselasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 412255345Shselasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 413255345Shselasky 414255345Shselasky /* Foxconn - Hon Hai */ 415255345Shselasky { USB_VENDOR(USB_VENDOR_FOXCONN), 416255345Shselasky USB_IFACE_CLASS(UICLASS_VENDOR), 417255345Shselasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 418255345Shselasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 419255345Shselasky 420255345Shselasky /* MediaTek MT76x0E */ 421255345Shselasky { USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) }, 422255345Shselasky 423255345Shselasky /* Broadcom SoftSailing reporting vendor specific */ 424255345Shselasky { USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) }, 425255345Shselasky 426255345Shselasky /* Apple MacBookPro 7,1 */ 427255345Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) }, 428255345Shselasky 429255345Shselasky /* Apple iMac11,1 */ 430255345Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) }, 431255345Shselasky 432255345Shselasky /* Apple MacBookPro6,2 */ 433255345Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) }, 434255345Shselasky 435255345Shselasky /* Apple MacBookAir3,1, MacBookAir3,2 */ 436255345Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) }, 437255345Shselasky 438255345Shselasky /* Apple MacBookAir4,1 */ 439255345Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) }, 440255345Shselasky 441255345Shselasky /* MacBookAir6,1 */ 442255345Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) }, 443255345Shselasky 444255345Shselasky /* Apple MacBookPro8,2 */ 445255345Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) }, 446255345Shselasky 447255345Shselasky /* Apple MacMini5,1 */ 448255345Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) }, 449255345Shselasky 450255345Shselasky /* Bluetooth Ultraport Module from IBM */ 451255345Shselasky { USB_VPI(USB_VENDOR_TDK, 0x030a, 0) }, 452255345Shselasky 453255345Shselasky /* ALPS Modules with non-standard ID */ 454255345Shselasky { USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) }, 455255345Shselasky { USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) }, 456255345Shselasky 457255345Shselasky { USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) }, 458255345Shselasky 459255345Shselasky /* Canyon CN-BTU1 with HID interfaces */ 460255345Shselasky { USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) }, 461255345Shselasky 462255345Shselasky /* Broadcom BCM20702A0 */ 463255345Shselasky { USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) }, 464255345Shselasky { USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) }, 465255345Shselasky { USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) }, 466255345Shselasky { USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) }, 467255345Shselasky { USB_VPI(USB_VENDOR_DELL, 0x8197, 0) }, 468184610Salfred}; 469184610Salfred 470184610Salfred/* 471187494Semax * Probe for a USB Bluetooth device. 472187494Semax * USB context. 473184610Salfred */ 474184610Salfred 475184610Salfredstatic int 476184610Salfredubt_probe(device_t dev) 477184610Salfred{ 478192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 479222055Savg int error; 480184610Salfred 481192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) 482184610Salfred return (ENXIO); 483187494Semax 484187494Semax if (uaa->info.bIfaceIndex != 0) 485184610Salfred return (ENXIO); 486187494Semax 487194228Sthompsa if (usbd_lookup_id_by_uaa(ubt_ignore_devs, 488187494Semax sizeof(ubt_ignore_devs), uaa) == 0) 489184610Salfred return (ENXIO); 490187494Semax 491222055Savg error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa); 492222055Savg if (error == 0) 493222055Savg return (BUS_PROBE_GENERIC); 494222055Savg return (error); 495187494Semax} /* ubt_probe */ 496184610Salfred 497184610Salfred/* 498187494Semax * Attach the device. 499187494Semax * USB context. 500184610Salfred */ 501184610Salfred 502184610Salfredstatic int 503184610Salfredubt_attach(device_t dev) 504184610Salfred{ 505192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 506187494Semax struct ubt_softc *sc = device_get_softc(dev); 507192984Sthompsa struct usb_endpoint_descriptor *ed; 508192984Sthompsa struct usb_interface_descriptor *id; 509187494Semax uint16_t wMaxPacketSize; 510187741Semax uint8_t alt_index, i, j; 511187741Semax uint8_t iface_index[2] = { 0, 1 }; 512184610Salfred 513194228Sthompsa device_set_usb_desc(dev); 514184610Salfred 515187741Semax sc->sc_dev = dev; 516187741Semax sc->sc_debug = NG_UBT_WARN_LEVEL; 517184610Salfred 518187494Semax /* 519187494Semax * Create Netgraph node 520187494Semax */ 521187494Semax 522187494Semax if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { 523187741Semax UBT_ALERT(sc, "could not create Netgraph node\n"); 524187494Semax return (ENXIO); 525187494Semax } 526187494Semax 527187494Semax /* Name Netgraph node */ 528187741Semax if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { 529187741Semax UBT_ALERT(sc, "could not name Netgraph node\n"); 530187494Semax NG_NODE_UNREF(sc->sc_node); 531187494Semax return (ENXIO); 532187494Semax } 533187494Semax NG_NODE_SET_PRIVATE(sc->sc_node, sc); 534187494Semax NG_NODE_FORCE_WRITER(sc->sc_node); 535187494Semax 536184610Salfred /* 537184610Salfred * Initialize device softc structure 538184610Salfred */ 539184610Salfred 540187494Semax /* initialize locks */ 541187741Semax mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); 542187741Semax mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); 543187494Semax 544187494Semax /* initialize packet queues */ 545184610Salfred NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); 546184610Salfred NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); 547187494Semax NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); 548184610Salfred 549187494Semax /* initialize glue task */ 550187741Semax TASK_INIT(&sc->sc_task, 0, ubt_task, sc); 551184610Salfred 552184610Salfred /* 553184610Salfred * Configure Bluetooth USB device. Discover all required USB 554184610Salfred * interfaces and endpoints. 555184610Salfred * 556184610Salfred * USB device must present two interfaces: 557184610Salfred * 1) Interface 0 that has 3 endpoints 558184610Salfred * 1) Interrupt endpoint to receive HCI events 559184610Salfred * 2) Bulk IN endpoint to receive ACL data 560184610Salfred * 3) Bulk OUT endpoint to send ACL data 561184610Salfred * 562184610Salfred * 2) Interface 1 then has 2 endpoints 563184610Salfred * 1) Isochronous IN endpoint to receive SCO data 564184610Salfred * 2) Isochronous OUT endpoint to send SCO data 565184610Salfred * 566184610Salfred * Interface 1 (with isochronous endpoints) has several alternate 567184610Salfred * configurations with different packet size. 568184610Salfred */ 569184610Salfred 570184610Salfred /* 571187741Semax * For interface #1 search alternate settings, and find 572187741Semax * the descriptor with the largest wMaxPacketSize 573184610Salfred */ 574184610Salfred 575184610Salfred wMaxPacketSize = 0; 576187494Semax alt_index = 0; 577184610Salfred i = 0; 578184610Salfred j = 0; 579190728Sthompsa ed = NULL; 580187494Semax 581190728Sthompsa /* 582190728Sthompsa * Search through all the descriptors looking for the largest 583190728Sthompsa * packet size: 584190728Sthompsa */ 585194228Sthompsa while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach( 586194228Sthompsa usbd_get_config_descriptor(uaa->device), 587192984Sthompsa (struct usb_descriptor *)ed))) { 588184610Salfred 589190728Sthompsa if ((ed->bDescriptorType == UDESC_INTERFACE) && 590190728Sthompsa (ed->bLength >= sizeof(*id))) { 591192984Sthompsa id = (struct usb_interface_descriptor *)ed; 592190728Sthompsa i = id->bInterfaceNumber; 593190728Sthompsa j = id->bAlternateSetting; 594184610Salfred } 595187494Semax 596190728Sthompsa if ((ed->bDescriptorType == UDESC_ENDPOINT) && 597190728Sthompsa (ed->bLength >= sizeof(*ed)) && 598190728Sthompsa (i == 1)) { 599190728Sthompsa uint16_t temp; 600190728Sthompsa 601190728Sthompsa temp = UGETW(ed->wMaxPacketSize); 602190728Sthompsa if (temp > wMaxPacketSize) { 603190728Sthompsa wMaxPacketSize = temp; 604190728Sthompsa alt_index = j; 605190728Sthompsa } 606184610Salfred } 607184610Salfred } 608184610Salfred 609187741Semax /* Set alt configuration on interface #1 only if we found it */ 610187494Semax if (wMaxPacketSize > 0 && 611194228Sthompsa usbd_set_alt_interface_index(uaa->device, 1, alt_index)) { 612187741Semax UBT_ALERT(sc, "could not set alternate setting %d " \ 613187494Semax "for interface 1!\n", alt_index); 614184610Salfred goto detach; 615184610Salfred } 616184610Salfred 617187741Semax /* Setup transfers for both interfaces */ 618194228Sthompsa if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, 619187741Semax ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { 620187741Semax UBT_ALERT(sc, "could not allocate transfers\n"); 621184610Salfred goto detach; 622184610Salfred } 623184610Salfred 624187494Semax /* Claim all interfaces on the device */ 625194228Sthompsa for (i = 1; usbd_get_iface(uaa->device, i) != NULL; i ++) 626194228Sthompsa usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 627184610Salfred 628187494Semax return (0); /* success */ 629184610Salfred 630184610Salfreddetach: 631184610Salfred ubt_detach(dev); 632184610Salfred 633184610Salfred return (ENXIO); 634187494Semax} /* ubt_attach */ 635184610Salfred 636184610Salfred/* 637187494Semax * Detach the device. 638187494Semax * USB context. 639184610Salfred */ 640184610Salfred 641184610Salfredint 642184610Salfredubt_detach(device_t dev) 643184610Salfred{ 644187494Semax struct ubt_softc *sc = device_get_softc(dev); 645187494Semax node_p node = sc->sc_node; 646184610Salfred 647187494Semax /* Destroy Netgraph node */ 648187494Semax if (node != NULL) { 649187494Semax sc->sc_node = NULL; 650187494Semax NG_NODE_REALLY_DIE(node); 651187494Semax ng_rmnode_self(node); 652184610Salfred } 653184610Salfred 654187741Semax /* Make sure ubt_task in gone */ 655187741Semax taskqueue_drain(taskqueue_swi, &sc->sc_task); 656187741Semax 657187494Semax /* Free USB transfers, if any */ 658194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); 659184610Salfred 660187494Semax /* Destroy queues */ 661187741Semax UBT_NG_LOCK(sc); 662184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); 663184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); 664184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); 665187741Semax UBT_NG_UNLOCK(sc); 666184610Salfred 667187741Semax mtx_destroy(&sc->sc_if_mtx); 668187741Semax mtx_destroy(&sc->sc_ng_mtx); 669187494Semax 670184610Salfred return (0); 671187494Semax} /* ubt_detach */ 672184610Salfred 673187494Semax/* 674187494Semax * Called when outgoing control request (HCI command) has completed, i.e. 675187494Semax * HCI command was sent to the device. 676187494Semax * USB context. 677187494Semax */ 678187494Semax 679184610Salfredstatic void 680194677Sthompsaubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 681184610Salfred{ 682194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 683192984Sthompsa struct usb_device_request req; 684187494Semax struct mbuf *m; 685194677Sthompsa struct usb_page_cache *pc; 686194677Sthompsa int actlen; 687184610Salfred 688194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 689194677Sthompsa 690184610Salfred switch (USB_GET_STATE(xfer)) { 691184610Salfred case USB_ST_TRANSFERRED: 692194677Sthompsa UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen); 693194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 694187741Semax UBT_STAT_PCKTS_SENT(sc); 695187494Semax /* FALLTHROUGH */ 696184610Salfred 697184610Salfred case USB_ST_SETUP: 698187494Semaxsend_next: 699187494Semax /* Get next command mbuf, if any */ 700187741Semax UBT_NG_LOCK(sc); 701184610Salfred NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 702187741Semax UBT_NG_UNLOCK(sc); 703184610Salfred 704184610Salfred if (m == NULL) { 705187494Semax UBT_INFO(sc, "HCI command queue is empty\n"); 706187741Semax break; /* transfer complete */ 707184610Salfred } 708184610Salfred 709187494Semax /* Initialize a USB control request and then schedule it */ 710184610Salfred bzero(&req, sizeof(req)); 711184610Salfred req.bmRequestType = UBT_HCI_REQUEST; 712184610Salfred USETW(req.wLength, m->m_pkthdr.len); 713184610Salfred 714187494Semax UBT_INFO(sc, "Sending control request, " \ 715187494Semax "bmRequestType=0x%02x, wLength=%d\n", 716187494Semax req.bmRequestType, UGETW(req.wLength)); 717184610Salfred 718194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 719194677Sthompsa usbd_copy_in(pc, 0, &req, sizeof(req)); 720194677Sthompsa pc = usbd_xfer_get_frame(xfer, 1); 721194677Sthompsa usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 722184610Salfred 723194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 724194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len); 725194677Sthompsa usbd_xfer_set_frames(xfer, 2); 726184610Salfred 727184610Salfred NG_FREE_M(m); 728184610Salfred 729194228Sthompsa usbd_transfer_submit(xfer); 730187494Semax break; 731184610Salfred 732187494Semax default: /* Error */ 733194677Sthompsa if (error != USB_ERR_CANCELLED) { 734187494Semax UBT_WARN(sc, "control transfer failed: %s\n", 735194677Sthompsa usbd_errstr(error)); 736187494Semax 737187494Semax UBT_STAT_OERROR(sc); 738187494Semax goto send_next; 739184610Salfred } 740187494Semax 741187741Semax /* transfer cancelled */ 742187494Semax break; 743184610Salfred } 744187494Semax} /* ubt_ctrl_write_callback */ 745184610Salfred 746187494Semax/* 747187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e. 748187494Semax * HCI event was received from the device. 749187494Semax * USB context. 750187494Semax */ 751187494Semax 752184610Salfredstatic void 753194677Sthompsaubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 754184610Salfred{ 755194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 756187494Semax struct mbuf *m; 757187494Semax ng_hci_event_pkt_t *hdr; 758194677Sthompsa struct usb_page_cache *pc; 759194677Sthompsa int actlen; 760184610Salfred 761194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 762194677Sthompsa 763187494Semax m = NULL; 764187494Semax 765184610Salfred switch (USB_GET_STATE(xfer)) { 766184610Salfred case USB_ST_TRANSFERRED: 767187494Semax /* Allocate a new mbuf */ 768184610Salfred MGETHDR(m, M_DONTWAIT, MT_DATA); 769184610Salfred if (m == NULL) { 770187494Semax UBT_STAT_IERROR(sc); 771187494Semax goto submit_next; 772184610Salfred } 773187494Semax 774184610Salfred MCLGET(m, M_DONTWAIT); 775184610Salfred if (!(m->m_flags & M_EXT)) { 776187494Semax UBT_STAT_IERROR(sc); 777187494Semax goto submit_next; 778184610Salfred } 779187494Semax 780187494Semax /* Add HCI packet type */ 781187494Semax *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; 782187494Semax m->m_pkthdr.len = m->m_len = 1; 783187494Semax 784194677Sthompsa if (actlen > MCLBYTES - 1) 785194677Sthompsa actlen = MCLBYTES - 1; 786187494Semax 787194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 788194677Sthompsa usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 789194677Sthompsa m->m_pkthdr.len += actlen; 790194677Sthompsa m->m_len += actlen; 791187494Semax 792187494Semax UBT_INFO(sc, "got %d bytes from interrupt pipe\n", 793194677Sthompsa actlen); 794187494Semax 795187494Semax /* Validate packet and send it up the stack */ 796235000Shselasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 797187494Semax UBT_INFO(sc, "HCI event packet is too short\n"); 798187494Semax 799187494Semax UBT_STAT_IERROR(sc); 800187494Semax goto submit_next; 801184610Salfred } 802184610Salfred 803187494Semax hdr = mtod(m, ng_hci_event_pkt_t *); 804187494Semax if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { 805187494Semax UBT_ERR(sc, "Invalid HCI event packet size, " \ 806187494Semax "length=%d, pktlen=%d\n", 807187494Semax hdr->length, m->m_pkthdr.len); 808184610Salfred 809187494Semax UBT_STAT_IERROR(sc); 810187494Semax goto submit_next; 811184610Salfred } 812184610Salfred 813187494Semax UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ 814187494Semax "length=%d\n", m->m_pkthdr.len, hdr->length); 815184610Salfred 816187494Semax UBT_STAT_PCKTS_RECV(sc); 817187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 818184610Salfred 819187741Semax ubt_fwd_mbuf_up(sc, &m); 820187494Semax /* m == NULL at this point */ 821187494Semax /* FALLTHROUGH */ 822184610Salfred 823184610Salfred case USB_ST_SETUP: 824187494Semaxsubmit_next: 825187494Semax NG_FREE_M(m); /* checks for m != NULL */ 826184610Salfred 827194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 828194228Sthompsa usbd_transfer_submit(xfer); 829187494Semax break; 830184610Salfred 831187494Semax default: /* Error */ 832194677Sthompsa if (error != USB_ERR_CANCELLED) { 833187494Semax UBT_WARN(sc, "interrupt transfer failed: %s\n", 834194677Sthompsa usbd_errstr(error)); 835184610Salfred 836187494Semax /* Try to clear stall first */ 837194677Sthompsa usbd_xfer_set_stall(xfer); 838187741Semax goto submit_next; 839187741Semax } 840187741Semax /* transfer cancelled */ 841187494Semax break; 842184610Salfred } 843187494Semax} /* ubt_intr_read_callback */ 844184610Salfred 845187494Semax/* 846187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e. 847187494Semax * ACL packet was received from the device. 848187494Semax * USB context. 849187494Semax */ 850187494Semax 851184610Salfredstatic void 852194677Sthompsaubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 853184610Salfred{ 854194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 855187494Semax struct mbuf *m; 856187494Semax ng_hci_acldata_pkt_t *hdr; 857194677Sthompsa struct usb_page_cache *pc; 858235000Shselasky int len; 859235000Shselasky int actlen; 860184610Salfred 861194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 862194677Sthompsa 863187494Semax m = NULL; 864184610Salfred 865184610Salfred switch (USB_GET_STATE(xfer)) { 866184610Salfred case USB_ST_TRANSFERRED: 867187494Semax /* Allocate new mbuf */ 868184610Salfred MGETHDR(m, M_DONTWAIT, MT_DATA); 869184610Salfred if (m == NULL) { 870187494Semax UBT_STAT_IERROR(sc); 871187494Semax goto submit_next; 872184610Salfred } 873187494Semax 874184610Salfred MCLGET(m, M_DONTWAIT); 875184610Salfred if (!(m->m_flags & M_EXT)) { 876187494Semax UBT_STAT_IERROR(sc); 877187494Semax goto submit_next; 878184610Salfred } 879184610Salfred 880187494Semax /* Add HCI packet type */ 881187494Semax *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; 882187494Semax m->m_pkthdr.len = m->m_len = 1; 883184610Salfred 884194677Sthompsa if (actlen > MCLBYTES - 1) 885194677Sthompsa actlen = MCLBYTES - 1; 886184610Salfred 887194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 888194677Sthompsa usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 889194677Sthompsa m->m_pkthdr.len += actlen; 890194677Sthompsa m->m_len += actlen; 891184610Salfred 892187494Semax UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", 893194677Sthompsa actlen); 894184610Salfred 895187494Semax /* Validate packet and send it up the stack */ 896235000Shselasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 897187494Semax UBT_INFO(sc, "HCI ACL packet is too short\n"); 898184610Salfred 899187494Semax UBT_STAT_IERROR(sc); 900187494Semax goto submit_next; 901184610Salfred } 902184610Salfred 903187494Semax hdr = mtod(m, ng_hci_acldata_pkt_t *); 904187494Semax len = le16toh(hdr->length); 905235000Shselasky if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) { 906187494Semax UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ 907187494Semax "pktlen=%d\n", len, m->m_pkthdr.len); 908184610Salfred 909187494Semax UBT_STAT_IERROR(sc); 910187494Semax goto submit_next; 911184610Salfred } 912184610Salfred 913187494Semax UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ 914187494Semax "length=%d\n", m->m_pkthdr.len, len); 915184610Salfred 916187494Semax UBT_STAT_PCKTS_RECV(sc); 917187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 918184610Salfred 919187741Semax ubt_fwd_mbuf_up(sc, &m); 920187494Semax /* m == NULL at this point */ 921187494Semax /* FALLTHOUGH */ 922184610Salfred 923187494Semax case USB_ST_SETUP: 924187494Semaxsubmit_next: 925187494Semax NG_FREE_M(m); /* checks for m != NULL */ 926184610Salfred 927194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 928194228Sthompsa usbd_transfer_submit(xfer); 929187494Semax break; 930184610Salfred 931187494Semax default: /* Error */ 932194677Sthompsa if (error != USB_ERR_CANCELLED) { 933187494Semax UBT_WARN(sc, "bulk-in transfer failed: %s\n", 934194677Sthompsa usbd_errstr(error)); 935184610Salfred 936187494Semax /* Try to clear stall first */ 937194677Sthompsa usbd_xfer_set_stall(xfer); 938187741Semax goto submit_next; 939187741Semax } 940187741Semax /* transfer cancelled */ 941187494Semax break; 942187494Semax } 943187494Semax} /* ubt_bulk_read_callback */ 944184610Salfred 945187494Semax/* 946187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e. 947187494Semax * ACL packet was sent to the device. 948187494Semax * USB context. 949187494Semax */ 950184610Salfred 951184610Salfredstatic void 952194677Sthompsaubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 953184610Salfred{ 954194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 955187494Semax struct mbuf *m; 956194677Sthompsa struct usb_page_cache *pc; 957194677Sthompsa int actlen; 958184610Salfred 959194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 960194677Sthompsa 961184610Salfred switch (USB_GET_STATE(xfer)) { 962184610Salfred case USB_ST_TRANSFERRED: 963194677Sthompsa UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen); 964194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 965187741Semax UBT_STAT_PCKTS_SENT(sc); 966187494Semax /* FALLTHROUGH */ 967184610Salfred 968187494Semax case USB_ST_SETUP: 969187741Semaxsend_next: 970187494Semax /* Get next mbuf, if any */ 971187741Semax UBT_NG_LOCK(sc); 972184610Salfred NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); 973187741Semax UBT_NG_UNLOCK(sc); 974184610Salfred 975184610Salfred if (m == NULL) { 976187494Semax UBT_INFO(sc, "ACL data queue is empty\n"); 977187741Semax break; /* transfer completed */ 978184610Salfred } 979187494Semax 980184610Salfred /* 981187494Semax * Copy ACL data frame back to a linear USB transfer buffer 982187494Semax * and schedule transfer 983184610Salfred */ 984184610Salfred 985194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 986194677Sthompsa usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 987194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 988184610Salfred 989187494Semax UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", 990187494Semax m->m_pkthdr.len); 991184610Salfred 992184610Salfred NG_FREE_M(m); 993184610Salfred 994194228Sthompsa usbd_transfer_submit(xfer); 995187494Semax break; 996184610Salfred 997187494Semax default: /* Error */ 998194677Sthompsa if (error != USB_ERR_CANCELLED) { 999187494Semax UBT_WARN(sc, "bulk-out transfer failed: %s\n", 1000194677Sthompsa usbd_errstr(error)); 1001184610Salfred 1002187494Semax UBT_STAT_OERROR(sc); 1003184610Salfred 1004184610Salfred /* try to clear stall first */ 1005194677Sthompsa usbd_xfer_set_stall(xfer); 1006187741Semax goto send_next; 1007187741Semax } 1008187741Semax /* transfer cancelled */ 1009187494Semax break; 1010184610Salfred } 1011187494Semax} /* ubt_bulk_write_callback */ 1012184610Salfred 1013187494Semax/* 1014187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e. 1015187494Semax * SCO packet was received from the device. 1016187494Semax * USB context. 1017187494Semax */ 1018187494Semax 1019184610Salfredstatic void 1020194677Sthompsaubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 1021184610Salfred{ 1022194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1023187494Semax int n; 1024194677Sthompsa int actlen, nframes; 1025184610Salfred 1026194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1027194677Sthompsa 1028187494Semax switch (USB_GET_STATE(xfer)) { 1029187494Semax case USB_ST_TRANSFERRED: 1030194677Sthompsa for (n = 0; n < nframes; n ++) 1031187494Semax if (ubt_isoc_read_one_frame(xfer, n) < 0) 1032187494Semax break; 1033187494Semax /* FALLTHROUGH */ 1034184610Salfred 1035187494Semax case USB_ST_SETUP: 1036187494Semaxread_next: 1037194677Sthompsa for (n = 0; n < nframes; n ++) 1038194677Sthompsa usbd_xfer_set_frame_len(xfer, n, 1039194677Sthompsa usbd_xfer_max_framelen(xfer)); 1040184610Salfred 1041194228Sthompsa usbd_transfer_submit(xfer); 1042187494Semax break; 1043184610Salfred 1044187494Semax default: /* Error */ 1045194677Sthompsa if (error != USB_ERR_CANCELLED) { 1046187494Semax UBT_STAT_IERROR(sc); 1047187494Semax goto read_next; 1048187494Semax } 1049184610Salfred 1050187741Semax /* transfer cancelled */ 1051187494Semax break; 1052187494Semax } 1053187494Semax} /* ubt_isoc_read_callback */ 1054184610Salfred 1055187494Semax/* 1056187494Semax * Helper function. Called from ubt_isoc_read_callback() to read 1057187494Semax * SCO data from one frame. 1058187494Semax * USB context. 1059187494Semax */ 1060184610Salfred 1061187494Semaxstatic int 1062192984Sthompsaubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no) 1063187494Semax{ 1064194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1065194677Sthompsa struct usb_page_cache *pc; 1066187494Semax struct mbuf *m; 1067194677Sthompsa int len, want, got, total; 1068184610Salfred 1069187494Semax /* Get existing SCO reassembly buffer */ 1070194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1071187494Semax m = sc->sc_isoc_in_buffer; 1072194682Sthompsa total = usbd_xfer_frame_len(xfer, frame_no); 1073184610Salfred 1074187494Semax /* While we have data in the frame */ 1075194677Sthompsa while (total > 0) { 1076187494Semax if (m == NULL) { 1077187494Semax /* Start new reassembly buffer */ 1078187494Semax MGETHDR(m, M_DONTWAIT, MT_DATA); 1079187494Semax if (m == NULL) { 1080187494Semax UBT_STAT_IERROR(sc); 1081187494Semax return (-1); /* XXX out of sync! */ 1082187494Semax } 1083184610Salfred 1084187494Semax MCLGET(m, M_DONTWAIT); 1085187494Semax if (!(m->m_flags & M_EXT)) { 1086187494Semax UBT_STAT_IERROR(sc); 1087187494Semax NG_FREE_M(m); 1088187494Semax return (-1); /* XXX out of sync! */ 1089184610Salfred } 1090184610Salfred 1091187494Semax /* Expect SCO header */ 1092187494Semax *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; 1093187494Semax m->m_pkthdr.len = m->m_len = got = 1; 1094187494Semax want = sizeof(ng_hci_scodata_pkt_t); 1095187494Semax } else { 1096187494Semax /* 1097187494Semax * Check if we have SCO header and if so 1098187494Semax * adjust amount of data we want 1099187494Semax */ 1100187494Semax got = m->m_pkthdr.len; 1101187494Semax want = sizeof(ng_hci_scodata_pkt_t); 1102184610Salfred 1103187494Semax if (got >= want) 1104187494Semax want += mtod(m, ng_hci_scodata_pkt_t *)->length; 1105184610Salfred } 1106184610Salfred 1107187494Semax /* Append frame data to the SCO reassembly buffer */ 1108194677Sthompsa len = total; 1109187494Semax if (got + len > want) 1110187494Semax len = want - got; 1111184610Salfred 1112194677Sthompsa usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer), 1113187494Semax mtod(m, uint8_t *) + m->m_pkthdr.len, len); 1114184610Salfred 1115187494Semax m->m_pkthdr.len += len; 1116187494Semax m->m_len += len; 1117194677Sthompsa total -= len; 1118184610Salfred 1119187494Semax /* Check if we got everything we wanted, if not - continue */ 1120187494Semax if (got != want) 1121187494Semax continue; 1122184610Salfred 1123187494Semax /* If we got here then we got complete SCO frame */ 1124187494Semax UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ 1125187494Semax "length=%d\n", m->m_pkthdr.len, 1126187494Semax mtod(m, ng_hci_scodata_pkt_t *)->length); 1127184610Salfred 1128187494Semax UBT_STAT_PCKTS_RECV(sc); 1129187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 1130184610Salfred 1131187741Semax ubt_fwd_mbuf_up(sc, &m); 1132187494Semax /* m == NULL at this point */ 1133187494Semax } 1134184610Salfred 1135187494Semax /* Put SCO reassembly buffer back */ 1136187494Semax sc->sc_isoc_in_buffer = m; 1137184610Salfred 1138187494Semax return (0); 1139187494Semax} /* ubt_isoc_read_one_frame */ 1140184610Salfred 1141187494Semax/* 1142187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e. 1143187494Semax * SCO packet was sent to the device. 1144187494Semax * USB context. 1145187494Semax */ 1146184610Salfred 1147184610Salfredstatic void 1148194677Sthompsaubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 1149184610Salfred{ 1150194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1151194677Sthompsa struct usb_page_cache *pc; 1152187494Semax struct mbuf *m; 1153187494Semax int n, space, offset; 1154194677Sthompsa int actlen, nframes; 1155184610Salfred 1156194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1157194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1158194677Sthompsa 1159184610Salfred switch (USB_GET_STATE(xfer)) { 1160184610Salfred case USB_ST_TRANSFERRED: 1161194677Sthompsa UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen); 1162194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 1163187741Semax UBT_STAT_PCKTS_SENT(sc); 1164187494Semax /* FALLTHROUGH */ 1165184610Salfred 1166184610Salfred case USB_ST_SETUP: 1167187494Semaxsend_next: 1168184610Salfred offset = 0; 1169194677Sthompsa space = usbd_xfer_max_framelen(xfer) * nframes; 1170187494Semax m = NULL; 1171184610Salfred 1172187494Semax while (space > 0) { 1173187494Semax if (m == NULL) { 1174187741Semax UBT_NG_LOCK(sc); 1175187494Semax NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); 1176187741Semax UBT_NG_UNLOCK(sc); 1177184610Salfred 1178187494Semax if (m == NULL) 1179187494Semax break; 1180187494Semax } 1181184610Salfred 1182187494Semax n = min(space, m->m_pkthdr.len); 1183187494Semax if (n > 0) { 1184194677Sthompsa usbd_m_copy_in(pc, offset, m,0, n); 1185187494Semax m_adj(m, n); 1186184610Salfred 1187187494Semax offset += n; 1188187494Semax space -= n; 1189187494Semax } 1190184610Salfred 1191187494Semax if (m->m_pkthdr.len == 0) 1192187494Semax NG_FREE_M(m); /* sets m = NULL */ 1193187494Semax } 1194184610Salfred 1195187494Semax /* Put whatever is left from mbuf back on queue */ 1196187494Semax if (m != NULL) { 1197187741Semax UBT_NG_LOCK(sc); 1198187494Semax NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); 1199187741Semax UBT_NG_UNLOCK(sc); 1200184610Salfred } 1201184610Salfred 1202187494Semax /* 1203187494Semax * Calculate sizes for isoc frames. 1204187494Semax * Note that offset could be 0 at this point (i.e. we have 1205187494Semax * nothing to send). That is fine, as we have isoc. transfers 1206187494Semax * going in both directions all the time. In this case it 1207187494Semax * would be just empty isoc. transfer. 1208187494Semax */ 1209187494Semax 1210194677Sthompsa for (n = 0; n < nframes; n ++) { 1211194677Sthompsa usbd_xfer_set_frame_len(xfer, n, 1212194677Sthompsa min(offset, usbd_xfer_max_framelen(xfer))); 1213194682Sthompsa offset -= usbd_xfer_frame_len(xfer, n); 1214187494Semax } 1215187494Semax 1216194228Sthompsa usbd_transfer_submit(xfer); 1217187494Semax break; 1218184610Salfred 1219187494Semax default: /* Error */ 1220194677Sthompsa if (error != USB_ERR_CANCELLED) { 1221187494Semax UBT_STAT_OERROR(sc); 1222187494Semax goto send_next; 1223184610Salfred } 1224187494Semax 1225187741Semax /* transfer cancelled */ 1226187494Semax break; 1227184610Salfred } 1228184610Salfred} 1229184610Salfred 1230187741Semax/* 1231187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack). 1232187741Semax * Modifies value of the mbuf pointer (sets it to NULL). 1233187741Semax * Save to call from any context. 1234187741Semax */ 1235187741Semax 1236187741Semaxstatic int 1237187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) 1238187741Semax{ 1239187741Semax hook_p hook; 1240187741Semax int error; 1241187741Semax 1242187741Semax /* 1243187741Semax * Close the race with Netgraph hook newhook/disconnect methods. 1244187741Semax * Save the hook pointer atomically. Two cases are possible: 1245187741Semax * 1246187741Semax * 1) The hook pointer is NULL. It means disconnect method got 1247187741Semax * there first. In this case we are done. 1248187741Semax * 1249187741Semax * 2) The hook pointer is not NULL. It means that hook pointer 1250187741Semax * could be either in valid or invalid (i.e. in the process 1251187741Semax * of disconnect) state. In any case grab an extra reference 1252187741Semax * to protect the hook pointer. 1253187741Semax * 1254187741Semax * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as 1255187741Semax * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). 1256187741Semax */ 1257187741Semax 1258187741Semax UBT_NG_LOCK(sc); 1259187741Semax if ((hook = sc->sc_hook) != NULL) 1260187741Semax NG_HOOK_REF(hook); 1261187741Semax UBT_NG_UNLOCK(sc); 1262187741Semax 1263187741Semax if (hook == NULL) { 1264187741Semax NG_FREE_M(*m); 1265187741Semax return (ENETDOWN); 1266187741Semax } 1267187741Semax 1268187741Semax NG_SEND_DATA_ONLY(error, hook, *m); 1269187741Semax NG_HOOK_UNREF(hook); 1270187741Semax 1271187741Semax if (error != 0) 1272187741Semax UBT_STAT_IERROR(sc); 1273187741Semax 1274187741Semax return (error); 1275187741Semax} /* ubt_fwd_mbuf_up */ 1276187741Semax 1277184610Salfred/**************************************************************************** 1278184610Salfred **************************************************************************** 1279187494Semax ** Glue 1280184610Salfred **************************************************************************** 1281184610Salfred ****************************************************************************/ 1282184610Salfred 1283184610Salfred/* 1284187741Semax * Schedule glue task. Should be called with sc_ng_mtx held. 1285187494Semax * Netgraph context. 1286184610Salfred */ 1287184610Salfred 1288187741Semaxstatic void 1289187494Semaxubt_task_schedule(ubt_softc_p sc, int action) 1290184610Salfred{ 1291187741Semax mtx_assert(&sc->sc_ng_mtx, MA_OWNED); 1292184610Salfred 1293187741Semax /* 1294187741Semax * Try to handle corner case when "start all" and "stop all" 1295187741Semax * actions can both be set before task is executed. 1296187741Semax * 1297187741Semax * The rules are 1298187741Semax * 1299187741Semax * sc_task_flags action new sc_task_flags 1300187741Semax * ------------------------------------------------------ 1301187741Semax * 0 start start 1302187741Semax * 0 stop stop 1303187741Semax * start start start 1304187741Semax * start stop stop 1305187741Semax * stop start stop|start 1306187741Semax * stop stop stop 1307187741Semax * stop|start start stop|start 1308187741Semax * stop|start stop stop 1309187741Semax */ 1310187494Semax 1311187741Semax if (action != 0) { 1312187741Semax if ((action & UBT_FLAG_T_STOP_ALL) != 0) 1313187494Semax sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; 1314187494Semax 1315187494Semax sc->sc_task_flags |= action; 1316187494Semax } 1317187494Semax 1318187494Semax if (sc->sc_task_flags & UBT_FLAG_T_PENDING) 1319187741Semax return; 1320187494Semax 1321187494Semax if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { 1322187494Semax sc->sc_task_flags |= UBT_FLAG_T_PENDING; 1323187741Semax return; 1324187494Semax } 1325187494Semax 1326187494Semax /* XXX: i think this should never happen */ 1327187494Semax} /* ubt_task_schedule */ 1328187494Semax 1329184610Salfred/* 1330187494Semax * Glue task. Examines sc_task_flags and does things depending on it. 1331187494Semax * Taskqueue context. 1332184610Salfred */ 1333184610Salfred 1334187494Semaxstatic void 1335187494Semaxubt_task(void *context, int pending) 1336184610Salfred{ 1337187741Semax ubt_softc_p sc = context; 1338187741Semax int task_flags, i; 1339184610Salfred 1340187741Semax UBT_NG_LOCK(sc); 1341187494Semax task_flags = sc->sc_task_flags; 1342187494Semax sc->sc_task_flags = 0; 1343187741Semax UBT_NG_UNLOCK(sc); 1344187494Semax 1345187741Semax /* 1346187741Semax * Stop all USB transfers synchronously. 1347187741Semax * Stop interface #0 and #1 transfers at the same time and in the 1348194228Sthompsa * same loop. usbd_transfer_drain() will do appropriate locking. 1349187741Semax */ 1350187494Semax 1351187741Semax if (task_flags & UBT_FLAG_T_STOP_ALL) 1352187741Semax for (i = 0; i < UBT_N_TRANSFER; i ++) 1353194228Sthompsa usbd_transfer_drain(sc->sc_xfer[i]); 1354187494Semax 1355187741Semax /* Start incoming interrupt and bulk, and all isoc. USB transfers */ 1356187494Semax if (task_flags & UBT_FLAG_T_START_ALL) { 1357187494Semax /* 1358187494Semax * Interface #0 1359187494Semax */ 1360187494Semax 1361187741Semax mtx_lock(&sc->sc_if_mtx); 1362187741Semax 1363187494Semax ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); 1364187494Semax ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); 1365187494Semax 1366187494Semax /* 1367187494Semax * Interface #1 1368187494Semax * Start both read and write isoc. transfers by default. 1369187494Semax * Get them going all the time even if we have nothing 1370187494Semax * to send to avoid any delays. 1371187494Semax */ 1372187494Semax 1373187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); 1374187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); 1375187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); 1376187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); 1377187741Semax 1378187741Semax mtx_unlock(&sc->sc_if_mtx); 1379184610Salfred } 1380187494Semax 1381187494Semax /* Start outgoing control transfer */ 1382187494Semax if (task_flags & UBT_FLAG_T_START_CTRL) { 1383187741Semax mtx_lock(&sc->sc_if_mtx); 1384187494Semax ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); 1385187741Semax mtx_unlock(&sc->sc_if_mtx); 1386184610Salfred } 1387184610Salfred 1388187494Semax /* Start outgoing bulk transfer */ 1389187494Semax if (task_flags & UBT_FLAG_T_START_BULK) { 1390187741Semax mtx_lock(&sc->sc_if_mtx); 1391187494Semax ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); 1392187741Semax mtx_unlock(&sc->sc_if_mtx); 1393184610Salfred } 1394187494Semax} /* ubt_task */ 1395187494Semax 1396187494Semax/**************************************************************************** 1397187494Semax **************************************************************************** 1398187494Semax ** Netgraph specific 1399187494Semax **************************************************************************** 1400187494Semax ****************************************************************************/ 1401184610Salfred 1402187494Semax/* 1403187494Semax * Netgraph node constructor. Do not allow to create node of this type. 1404187494Semax * Netgraph context. 1405187494Semax */ 1406184610Salfred 1407187494Semaxstatic int 1408187494Semaxng_ubt_constructor(node_p node) 1409187494Semax{ 1410187494Semax return (EINVAL); 1411187494Semax} /* ng_ubt_constructor */ 1412184610Salfred 1413184610Salfred/* 1414187494Semax * Netgraph node destructor. Destroy node only when device has been detached. 1415187494Semax * Netgraph context. 1416184610Salfred */ 1417184610Salfred 1418184610Salfredstatic int 1419187494Semaxng_ubt_shutdown(node_p node) 1420184610Salfred{ 1421187494Semax if (node->nd_flags & NGF_REALLY_DIE) { 1422187494Semax /* 1423187494Semax * We came here because the USB device is being 1424187494Semax * detached, so stop being persistant. 1425187494Semax */ 1426187494Semax NG_NODE_SET_PRIVATE(node, NULL); 1427187494Semax NG_NODE_UNREF(node); 1428187494Semax } else 1429187494Semax NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ 1430184610Salfred 1431187494Semax return (0); 1432187494Semax} /* ng_ubt_shutdown */ 1433184610Salfred 1434187494Semax/* 1435187494Semax * Create new hook. There can only be one. 1436187494Semax * Netgraph context. 1437187494Semax */ 1438184610Salfred 1439187494Semaxstatic int 1440187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name) 1441187494Semax{ 1442187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1443184610Salfred 1444187494Semax if (strcmp(name, NG_UBT_HOOK) != 0) 1445187494Semax return (EINVAL); 1446184610Salfred 1447187741Semax UBT_NG_LOCK(sc); 1448187741Semax if (sc->sc_hook != NULL) { 1449187741Semax UBT_NG_UNLOCK(sc); 1450187741Semax 1451187494Semax return (EISCONN); 1452187741Semax } 1453184610Salfred 1454187494Semax sc->sc_hook = hook; 1455187741Semax UBT_NG_UNLOCK(sc); 1456184610Salfred 1457187494Semax return (0); 1458187494Semax} /* ng_ubt_newhook */ 1459184610Salfred 1460187494Semax/* 1461187494Semax * Connect hook. Start incoming USB transfers. 1462187494Semax * Netgraph context. 1463187494Semax */ 1464184610Salfred 1465187494Semaxstatic int 1466187494Semaxng_ubt_connect(hook_p hook) 1467187494Semax{ 1468187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1469184610Salfred 1470187494Semax NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 1471184610Salfred 1472187741Semax UBT_NG_LOCK(sc); 1473187494Semax ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); 1474187741Semax UBT_NG_UNLOCK(sc); 1475184610Salfred 1476184610Salfred return (0); 1477187494Semax} /* ng_ubt_connect */ 1478184610Salfred 1479184610Salfred/* 1480187494Semax * Disconnect hook. 1481187494Semax * Netgraph context. 1482184610Salfred */ 1483184610Salfred 1484184610Salfredstatic int 1485184610Salfredng_ubt_disconnect(hook_p hook) 1486184610Salfred{ 1487187741Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1488184610Salfred 1489187741Semax UBT_NG_LOCK(sc); 1490184610Salfred 1491187741Semax if (hook != sc->sc_hook) { 1492187741Semax UBT_NG_UNLOCK(sc); 1493184610Salfred 1494187494Semax return (EINVAL); 1495187741Semax } 1496184610Salfred 1497187494Semax sc->sc_hook = NULL; 1498184610Salfred 1499187741Semax /* Kick off task to stop all USB xfers */ 1500187741Semax ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); 1501184610Salfred 1502187494Semax /* Drain queues */ 1503187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); 1504187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); 1505187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); 1506184610Salfred 1507187741Semax UBT_NG_UNLOCK(sc); 1508184610Salfred 1509187494Semax return (0); 1510187494Semax} /* ng_ubt_disconnect */ 1511187494Semax 1512184610Salfred/* 1513187494Semax * Process control message. 1514187494Semax * Netgraph context. 1515184610Salfred */ 1516184610Salfred 1517184610Salfredstatic int 1518184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) 1519184610Salfred{ 1520187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1521187494Semax struct ng_mesg *msg, *rsp = NULL; 1522187494Semax struct ng_bt_mbufq *q; 1523187494Semax int error = 0, queue, qlen; 1524184610Salfred 1525184610Salfred NGI_GET_MSG(item, msg); 1526184610Salfred 1527184610Salfred switch (msg->header.typecookie) { 1528184610Salfred case NGM_GENERIC_COOKIE: 1529184610Salfred switch (msg->header.cmd) { 1530184610Salfred case NGM_TEXT_STATUS: 1531184610Salfred NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 1532187494Semax if (rsp == NULL) { 1533184610Salfred error = ENOMEM; 1534187494Semax break; 1535187494Semax } 1536187494Semax 1537187494Semax snprintf(rsp->data, NG_TEXTRESPONSE, 1538187494Semax "Hook: %s\n" \ 1539187494Semax "Task flags: %#x\n" \ 1540187494Semax "Debug: %d\n" \ 1541187494Semax "CMD queue: [have:%d,max:%d]\n" \ 1542187494Semax "ACL queue: [have:%d,max:%d]\n" \ 1543187494Semax "SCO queue: [have:%d,max:%d]", 1544187741Semax (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", 1545187494Semax sc->sc_task_flags, 1546187494Semax sc->sc_debug, 1547187494Semax sc->sc_cmdq.len, 1548187494Semax sc->sc_cmdq.maxlen, 1549187494Semax sc->sc_aclq.len, 1550187494Semax sc->sc_aclq.maxlen, 1551187494Semax sc->sc_scoq.len, 1552187494Semax sc->sc_scoq.maxlen); 1553184610Salfred break; 1554184610Salfred 1555184610Salfred default: 1556184610Salfred error = EINVAL; 1557184610Salfred break; 1558184610Salfred } 1559184610Salfred break; 1560184610Salfred 1561184610Salfred case NGM_UBT_COOKIE: 1562184610Salfred switch (msg->header.cmd) { 1563184610Salfred case NGM_UBT_NODE_SET_DEBUG: 1564187494Semax if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ 1565184610Salfred error = EMSGSIZE; 1566187494Semax break; 1567187494Semax } 1568187494Semax 1569187494Semax sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); 1570184610Salfred break; 1571184610Salfred 1572184610Salfred case NGM_UBT_NODE_GET_DEBUG: 1573184610Salfred NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), 1574184610Salfred M_NOWAIT); 1575187494Semax if (rsp == NULL) { 1576184610Salfred error = ENOMEM; 1577187494Semax break; 1578187494Semax } 1579187494Semax 1580187494Semax *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; 1581184610Salfred break; 1582184610Salfred 1583184610Salfred case NGM_UBT_NODE_SET_QLEN: 1584187494Semax if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1585184610Salfred error = EMSGSIZE; 1586187494Semax break; 1587187494Semax } 1588184610Salfred 1589187494Semax queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1590187494Semax qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; 1591184610Salfred 1592187494Semax switch (queue) { 1593187494Semax case NGM_UBT_NODE_QUEUE_CMD: 1594187494Semax q = &sc->sc_cmdq; 1595187494Semax break; 1596184610Salfred 1597187494Semax case NGM_UBT_NODE_QUEUE_ACL: 1598187494Semax q = &sc->sc_aclq; 1599187494Semax break; 1600184610Salfred 1601187494Semax case NGM_UBT_NODE_QUEUE_SCO: 1602187494Semax q = &sc->sc_scoq; 1603187494Semax break; 1604184610Salfred 1605187494Semax default: 1606187494Semax error = EINVAL; 1607187494Semax goto done; 1608187494Semax /* NOT REACHED */ 1609184610Salfred } 1610187494Semax 1611187494Semax q->maxlen = qlen; 1612184610Salfred break; 1613184610Salfred 1614184610Salfred case NGM_UBT_NODE_GET_QLEN: 1615184610Salfred if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1616184610Salfred error = EMSGSIZE; 1617184610Salfred break; 1618184610Salfred } 1619187494Semax 1620184610Salfred queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1621187494Semax 1622184610Salfred switch (queue) { 1623184610Salfred case NGM_UBT_NODE_QUEUE_CMD: 1624184610Salfred q = &sc->sc_cmdq; 1625184610Salfred break; 1626184610Salfred 1627184610Salfred case NGM_UBT_NODE_QUEUE_ACL: 1628184610Salfred q = &sc->sc_aclq; 1629184610Salfred break; 1630184610Salfred 1631184610Salfred case NGM_UBT_NODE_QUEUE_SCO: 1632184610Salfred q = &sc->sc_scoq; 1633184610Salfred break; 1634184610Salfred 1635184610Salfred default: 1636184610Salfred error = EINVAL; 1637187494Semax goto done; 1638187494Semax /* NOT REACHED */ 1639187494Semax } 1640187494Semax 1641187494Semax NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), 1642187494Semax M_NOWAIT); 1643187494Semax if (rsp == NULL) { 1644187494Semax error = ENOMEM; 1645184610Salfred break; 1646184610Salfred } 1647184610Salfred 1648187494Semax ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; 1649187494Semax ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; 1650184610Salfred break; 1651184610Salfred 1652184610Salfred case NGM_UBT_NODE_GET_STAT: 1653184610Salfred NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), 1654184610Salfred M_NOWAIT); 1655187494Semax if (rsp == NULL) { 1656184610Salfred error = ENOMEM; 1657187494Semax break; 1658184610Salfred } 1659187494Semax 1660187494Semax bcopy(&sc->sc_stat, rsp->data, 1661187494Semax sizeof(ng_ubt_node_stat_ep)); 1662184610Salfred break; 1663184610Salfred 1664184610Salfred case NGM_UBT_NODE_RESET_STAT: 1665187494Semax UBT_STAT_RESET(sc); 1666184610Salfred break; 1667184610Salfred 1668184610Salfred default: 1669184610Salfred error = EINVAL; 1670184610Salfred break; 1671184610Salfred } 1672184610Salfred break; 1673184610Salfred 1674184610Salfred default: 1675184610Salfred error = EINVAL; 1676184610Salfred break; 1677184610Salfred } 1678187494Semaxdone: 1679184610Salfred NG_RESPOND_MSG(error, node, item, rsp); 1680184610Salfred NG_FREE_MSG(msg); 1681184610Salfred 1682184610Salfred return (error); 1683187494Semax} /* ng_ubt_rcvmsg */ 1684184610Salfred 1685184610Salfred/* 1686187494Semax * Process data. 1687187494Semax * Netgraph context. 1688184610Salfred */ 1689184610Salfred 1690184610Salfredstatic int 1691184610Salfredng_ubt_rcvdata(hook_p hook, item_p item) 1692184610Salfred{ 1693187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1694187494Semax struct mbuf *m; 1695187494Semax struct ng_bt_mbufq *q; 1696187494Semax int action, error = 0; 1697184610Salfred 1698184610Salfred if (hook != sc->sc_hook) { 1699184610Salfred error = EINVAL; 1700184610Salfred goto done; 1701184610Salfred } 1702187494Semax 1703187494Semax /* Deatch mbuf and get HCI frame type */ 1704184610Salfred NGI_GET_M(item, m); 1705184610Salfred 1706187494Semax /* 1707187494Semax * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, 1708187494Semax * 2 bytes connection handle and at least 1 byte of length. 1709187494Semax * Panic on data frame that has size smaller than 4 bytes (it 1710187494Semax * should not happen) 1711187494Semax */ 1712187494Semax 1713187494Semax if (m->m_pkthdr.len < 4) 1714187494Semax panic("HCI frame size is too small! pktlen=%d\n", 1715187494Semax m->m_pkthdr.len); 1716187494Semax 1717187494Semax /* Process HCI frame */ 1718184610Salfred switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ 1719184610Salfred case NG_HCI_CMD_PKT: 1720235000Shselasky if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE) 1721187494Semax panic("HCI command frame size is too big! " \ 1722187494Semax "buffer size=%zd, packet len=%d\n", 1723187494Semax UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); 1724187494Semax 1725184610Salfred q = &sc->sc_cmdq; 1726187494Semax action = UBT_FLAG_T_START_CTRL; 1727184610Salfred break; 1728184610Salfred 1729184610Salfred case NG_HCI_ACL_DATA_PKT: 1730187494Semax if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) 1731187494Semax panic("ACL data frame size is too big! " \ 1732187494Semax "buffer size=%d, packet len=%d\n", 1733187494Semax UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); 1734187494Semax 1735184610Salfred q = &sc->sc_aclq; 1736187494Semax action = UBT_FLAG_T_START_BULK; 1737184610Salfred break; 1738184610Salfred 1739184610Salfred case NG_HCI_SCO_DATA_PKT: 1740184610Salfred q = &sc->sc_scoq; 1741187494Semax action = 0; 1742184610Salfred break; 1743184610Salfred 1744184610Salfred default: 1745187494Semax UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ 1746187494Semax "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len); 1747184610Salfred 1748184610Salfred NG_FREE_M(m); 1749184610Salfred error = EINVAL; 1750184610Salfred goto done; 1751187494Semax /* NOT REACHED */ 1752184610Salfred } 1753184610Salfred 1754187741Semax UBT_NG_LOCK(sc); 1755184610Salfred if (NG_BT_MBUFQ_FULL(q)) { 1756187494Semax NG_BT_MBUFQ_DROP(q); 1757187741Semax UBT_NG_UNLOCK(sc); 1758187494Semax 1759187494Semax UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", 1760187494Semax *mtod(m, uint8_t *), m->m_pkthdr.len); 1761187494Semax 1762184610Salfred NG_FREE_M(m); 1763184610Salfred } else { 1764187494Semax /* Loose HCI packet type, enqueue mbuf and kick off task */ 1765187494Semax m_adj(m, sizeof(uint8_t)); 1766184610Salfred NG_BT_MBUFQ_ENQUEUE(q, m); 1767187494Semax ubt_task_schedule(sc, action); 1768187741Semax UBT_NG_UNLOCK(sc); 1769184610Salfred } 1770184610Salfreddone: 1771184610Salfred NG_FREE_ITEM(item); 1772184610Salfred 1773187494Semax return (error); 1774187494Semax} /* ng_ubt_rcvdata */ 1775187494Semax 1776187494Semax/**************************************************************************** 1777187494Semax **************************************************************************** 1778187494Semax ** Module 1779187494Semax **************************************************************************** 1780187494Semax ****************************************************************************/ 1781187494Semax 1782187494Semax/* 1783187494Semax * Load/Unload the driver module 1784187494Semax */ 1785187494Semax 1786187494Semaxstatic int 1787187494Semaxubt_modevent(module_t mod, int event, void *data) 1788187494Semax{ 1789187494Semax int error; 1790187494Semax 1791187494Semax switch (event) { 1792187494Semax case MOD_LOAD: 1793187494Semax error = ng_newtype(&typestruct); 1794187494Semax if (error != 0) 1795187494Semax printf("%s: Could not register Netgraph node type, " \ 1796187494Semax "error=%d\n", NG_UBT_NODE_TYPE, error); 1797187494Semax break; 1798187494Semax 1799187494Semax case MOD_UNLOAD: 1800187494Semax error = ng_rmtype(&typestruct); 1801187494Semax break; 1802187494Semax 1803187494Semax default: 1804187494Semax error = EOPNOTSUPP; 1805187494Semax break; 1806184610Salfred } 1807187494Semax 1808184610Salfred return (error); 1809187494Semax} /* ubt_modevent */ 1810187494Semax 1811187494Semaxstatic devclass_t ubt_devclass; 1812187494Semax 1813187494Semaxstatic device_method_t ubt_methods[] = 1814187494Semax{ 1815187494Semax DEVMETHOD(device_probe, ubt_probe), 1816187494Semax DEVMETHOD(device_attach, ubt_attach), 1817187494Semax DEVMETHOD(device_detach, ubt_detach), 1818244956Shselasky DEVMETHOD_END 1819187494Semax}; 1820187494Semax 1821187494Semaxstatic driver_t ubt_driver = 1822187494Semax{ 1823187494Semax .name = "ubt", 1824187494Semax .methods = ubt_methods, 1825187494Semax .size = sizeof(struct ubt_softc), 1826187494Semax}; 1827187494Semax 1828189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0); 1829187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); 1830187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 1831187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 1832188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1); 1833187494Semax 1834