ng_ubt.c revision 244956
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: stable/9/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c 244956 2013-01-02 07:33:19Z hselasky $ 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) }, 407184610Salfred}; 408184610Salfred 409184610Salfred/* 410187494Semax * Probe for a USB Bluetooth device. 411187494Semax * USB context. 412184610Salfred */ 413184610Salfred 414184610Salfredstatic int 415184610Salfredubt_probe(device_t dev) 416184610Salfred{ 417192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 418222055Savg int error; 419184610Salfred 420192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) 421184610Salfred return (ENXIO); 422187494Semax 423187494Semax if (uaa->info.bIfaceIndex != 0) 424184610Salfred return (ENXIO); 425187494Semax 426194228Sthompsa if (usbd_lookup_id_by_uaa(ubt_ignore_devs, 427187494Semax sizeof(ubt_ignore_devs), uaa) == 0) 428184610Salfred return (ENXIO); 429187494Semax 430222055Savg error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa); 431222055Savg if (error == 0) 432222055Savg return (BUS_PROBE_GENERIC); 433222055Savg return (error); 434187494Semax} /* ubt_probe */ 435184610Salfred 436184610Salfred/* 437187494Semax * Attach the device. 438187494Semax * USB context. 439184610Salfred */ 440184610Salfred 441184610Salfredstatic int 442184610Salfredubt_attach(device_t dev) 443184610Salfred{ 444192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 445187494Semax struct ubt_softc *sc = device_get_softc(dev); 446192984Sthompsa struct usb_endpoint_descriptor *ed; 447192984Sthompsa struct usb_interface_descriptor *id; 448187494Semax uint16_t wMaxPacketSize; 449187741Semax uint8_t alt_index, i, j; 450187741Semax uint8_t iface_index[2] = { 0, 1 }; 451184610Salfred 452194228Sthompsa device_set_usb_desc(dev); 453184610Salfred 454187741Semax sc->sc_dev = dev; 455187741Semax sc->sc_debug = NG_UBT_WARN_LEVEL; 456184610Salfred 457187494Semax /* 458187494Semax * Create Netgraph node 459187494Semax */ 460187494Semax 461187494Semax if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { 462187741Semax UBT_ALERT(sc, "could not create Netgraph node\n"); 463187494Semax return (ENXIO); 464187494Semax } 465187494Semax 466187494Semax /* Name Netgraph node */ 467187741Semax if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { 468187741Semax UBT_ALERT(sc, "could not name Netgraph node\n"); 469187494Semax NG_NODE_UNREF(sc->sc_node); 470187494Semax return (ENXIO); 471187494Semax } 472187494Semax NG_NODE_SET_PRIVATE(sc->sc_node, sc); 473187494Semax NG_NODE_FORCE_WRITER(sc->sc_node); 474187494Semax 475184610Salfred /* 476184610Salfred * Initialize device softc structure 477184610Salfred */ 478184610Salfred 479187494Semax /* initialize locks */ 480187741Semax mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); 481187741Semax mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); 482187494Semax 483187494Semax /* initialize packet queues */ 484184610Salfred NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); 485184610Salfred NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); 486187494Semax NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); 487184610Salfred 488187494Semax /* initialize glue task */ 489187741Semax TASK_INIT(&sc->sc_task, 0, ubt_task, sc); 490184610Salfred 491184610Salfred /* 492184610Salfred * Configure Bluetooth USB device. Discover all required USB 493184610Salfred * interfaces and endpoints. 494184610Salfred * 495184610Salfred * USB device must present two interfaces: 496184610Salfred * 1) Interface 0 that has 3 endpoints 497184610Salfred * 1) Interrupt endpoint to receive HCI events 498184610Salfred * 2) Bulk IN endpoint to receive ACL data 499184610Salfred * 3) Bulk OUT endpoint to send ACL data 500184610Salfred * 501184610Salfred * 2) Interface 1 then has 2 endpoints 502184610Salfred * 1) Isochronous IN endpoint to receive SCO data 503184610Salfred * 2) Isochronous OUT endpoint to send SCO data 504184610Salfred * 505184610Salfred * Interface 1 (with isochronous endpoints) has several alternate 506184610Salfred * configurations with different packet size. 507184610Salfred */ 508184610Salfred 509184610Salfred /* 510187741Semax * For interface #1 search alternate settings, and find 511187741Semax * the descriptor with the largest wMaxPacketSize 512184610Salfred */ 513184610Salfred 514184610Salfred wMaxPacketSize = 0; 515187494Semax alt_index = 0; 516184610Salfred i = 0; 517184610Salfred j = 0; 518190728Sthompsa ed = NULL; 519187494Semax 520190728Sthompsa /* 521190728Sthompsa * Search through all the descriptors looking for the largest 522190728Sthompsa * packet size: 523190728Sthompsa */ 524194228Sthompsa while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach( 525194228Sthompsa usbd_get_config_descriptor(uaa->device), 526192984Sthompsa (struct usb_descriptor *)ed))) { 527184610Salfred 528190728Sthompsa if ((ed->bDescriptorType == UDESC_INTERFACE) && 529190728Sthompsa (ed->bLength >= sizeof(*id))) { 530192984Sthompsa id = (struct usb_interface_descriptor *)ed; 531190728Sthompsa i = id->bInterfaceNumber; 532190728Sthompsa j = id->bAlternateSetting; 533184610Salfred } 534187494Semax 535190728Sthompsa if ((ed->bDescriptorType == UDESC_ENDPOINT) && 536190728Sthompsa (ed->bLength >= sizeof(*ed)) && 537190728Sthompsa (i == 1)) { 538190728Sthompsa uint16_t temp; 539190728Sthompsa 540190728Sthompsa temp = UGETW(ed->wMaxPacketSize); 541190728Sthompsa if (temp > wMaxPacketSize) { 542190728Sthompsa wMaxPacketSize = temp; 543190728Sthompsa alt_index = j; 544190728Sthompsa } 545184610Salfred } 546184610Salfred } 547184610Salfred 548187741Semax /* Set alt configuration on interface #1 only if we found it */ 549187494Semax if (wMaxPacketSize > 0 && 550194228Sthompsa usbd_set_alt_interface_index(uaa->device, 1, alt_index)) { 551187741Semax UBT_ALERT(sc, "could not set alternate setting %d " \ 552187494Semax "for interface 1!\n", alt_index); 553184610Salfred goto detach; 554184610Salfred } 555184610Salfred 556187741Semax /* Setup transfers for both interfaces */ 557194228Sthompsa if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, 558187741Semax ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { 559187741Semax UBT_ALERT(sc, "could not allocate transfers\n"); 560184610Salfred goto detach; 561184610Salfred } 562184610Salfred 563187494Semax /* Claim all interfaces on the device */ 564194228Sthompsa for (i = 1; usbd_get_iface(uaa->device, i) != NULL; i ++) 565194228Sthompsa usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 566184610Salfred 567187494Semax return (0); /* success */ 568184610Salfred 569184610Salfreddetach: 570184610Salfred ubt_detach(dev); 571184610Salfred 572184610Salfred return (ENXIO); 573187494Semax} /* ubt_attach */ 574184610Salfred 575184610Salfred/* 576187494Semax * Detach the device. 577187494Semax * USB context. 578184610Salfred */ 579184610Salfred 580184610Salfredint 581184610Salfredubt_detach(device_t dev) 582184610Salfred{ 583187494Semax struct ubt_softc *sc = device_get_softc(dev); 584187494Semax node_p node = sc->sc_node; 585184610Salfred 586187494Semax /* Destroy Netgraph node */ 587187494Semax if (node != NULL) { 588187494Semax sc->sc_node = NULL; 589187494Semax NG_NODE_REALLY_DIE(node); 590187494Semax ng_rmnode_self(node); 591184610Salfred } 592184610Salfred 593187741Semax /* Make sure ubt_task in gone */ 594187741Semax taskqueue_drain(taskqueue_swi, &sc->sc_task); 595187741Semax 596187494Semax /* Free USB transfers, if any */ 597194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); 598184610Salfred 599187494Semax /* Destroy queues */ 600187741Semax UBT_NG_LOCK(sc); 601184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); 602184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); 603184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); 604187741Semax UBT_NG_UNLOCK(sc); 605184610Salfred 606187741Semax mtx_destroy(&sc->sc_if_mtx); 607187741Semax mtx_destroy(&sc->sc_ng_mtx); 608187494Semax 609184610Salfred return (0); 610187494Semax} /* ubt_detach */ 611184610Salfred 612187494Semax/* 613187494Semax * Called when outgoing control request (HCI command) has completed, i.e. 614187494Semax * HCI command was sent to the device. 615187494Semax * USB context. 616187494Semax */ 617187494Semax 618184610Salfredstatic void 619194677Sthompsaubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 620184610Salfred{ 621194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 622192984Sthompsa struct usb_device_request req; 623187494Semax struct mbuf *m; 624194677Sthompsa struct usb_page_cache *pc; 625194677Sthompsa int actlen; 626184610Salfred 627194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 628194677Sthompsa 629184610Salfred switch (USB_GET_STATE(xfer)) { 630184610Salfred case USB_ST_TRANSFERRED: 631194677Sthompsa UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen); 632194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 633187741Semax UBT_STAT_PCKTS_SENT(sc); 634187494Semax /* FALLTHROUGH */ 635184610Salfred 636184610Salfred case USB_ST_SETUP: 637187494Semaxsend_next: 638187494Semax /* Get next command mbuf, if any */ 639187741Semax UBT_NG_LOCK(sc); 640184610Salfred NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 641187741Semax UBT_NG_UNLOCK(sc); 642184610Salfred 643184610Salfred if (m == NULL) { 644187494Semax UBT_INFO(sc, "HCI command queue is empty\n"); 645187741Semax break; /* transfer complete */ 646184610Salfred } 647184610Salfred 648187494Semax /* Initialize a USB control request and then schedule it */ 649184610Salfred bzero(&req, sizeof(req)); 650184610Salfred req.bmRequestType = UBT_HCI_REQUEST; 651184610Salfred USETW(req.wLength, m->m_pkthdr.len); 652184610Salfred 653187494Semax UBT_INFO(sc, "Sending control request, " \ 654187494Semax "bmRequestType=0x%02x, wLength=%d\n", 655187494Semax req.bmRequestType, UGETW(req.wLength)); 656184610Salfred 657194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 658194677Sthompsa usbd_copy_in(pc, 0, &req, sizeof(req)); 659194677Sthompsa pc = usbd_xfer_get_frame(xfer, 1); 660194677Sthompsa usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 661184610Salfred 662194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 663194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len); 664194677Sthompsa usbd_xfer_set_frames(xfer, 2); 665184610Salfred 666184610Salfred NG_FREE_M(m); 667184610Salfred 668194228Sthompsa usbd_transfer_submit(xfer); 669187494Semax break; 670184610Salfred 671187494Semax default: /* Error */ 672194677Sthompsa if (error != USB_ERR_CANCELLED) { 673187494Semax UBT_WARN(sc, "control transfer failed: %s\n", 674194677Sthompsa usbd_errstr(error)); 675187494Semax 676187494Semax UBT_STAT_OERROR(sc); 677187494Semax goto send_next; 678184610Salfred } 679187494Semax 680187741Semax /* transfer cancelled */ 681187494Semax break; 682184610Salfred } 683187494Semax} /* ubt_ctrl_write_callback */ 684184610Salfred 685187494Semax/* 686187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e. 687187494Semax * HCI event was received from the device. 688187494Semax * USB context. 689187494Semax */ 690187494Semax 691184610Salfredstatic void 692194677Sthompsaubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 693184610Salfred{ 694194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 695187494Semax struct mbuf *m; 696187494Semax ng_hci_event_pkt_t *hdr; 697194677Sthompsa struct usb_page_cache *pc; 698194677Sthompsa int actlen; 699184610Salfred 700194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 701194677Sthompsa 702187494Semax m = NULL; 703187494Semax 704184610Salfred switch (USB_GET_STATE(xfer)) { 705184610Salfred case USB_ST_TRANSFERRED: 706187494Semax /* Allocate a new mbuf */ 707184610Salfred MGETHDR(m, M_DONTWAIT, MT_DATA); 708184610Salfred if (m == NULL) { 709187494Semax UBT_STAT_IERROR(sc); 710187494Semax goto submit_next; 711184610Salfred } 712187494Semax 713184610Salfred MCLGET(m, M_DONTWAIT); 714184610Salfred if (!(m->m_flags & M_EXT)) { 715187494Semax UBT_STAT_IERROR(sc); 716187494Semax goto submit_next; 717184610Salfred } 718187494Semax 719187494Semax /* Add HCI packet type */ 720187494Semax *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; 721187494Semax m->m_pkthdr.len = m->m_len = 1; 722187494Semax 723194677Sthompsa if (actlen > MCLBYTES - 1) 724194677Sthompsa actlen = MCLBYTES - 1; 725187494Semax 726194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 727194677Sthompsa usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 728194677Sthompsa m->m_pkthdr.len += actlen; 729194677Sthompsa m->m_len += actlen; 730187494Semax 731187494Semax UBT_INFO(sc, "got %d bytes from interrupt pipe\n", 732194677Sthompsa actlen); 733187494Semax 734187494Semax /* Validate packet and send it up the stack */ 735235000Shselasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 736187494Semax UBT_INFO(sc, "HCI event packet is too short\n"); 737187494Semax 738187494Semax UBT_STAT_IERROR(sc); 739187494Semax goto submit_next; 740184610Salfred } 741184610Salfred 742187494Semax hdr = mtod(m, ng_hci_event_pkt_t *); 743187494Semax if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { 744187494Semax UBT_ERR(sc, "Invalid HCI event packet size, " \ 745187494Semax "length=%d, pktlen=%d\n", 746187494Semax hdr->length, m->m_pkthdr.len); 747184610Salfred 748187494Semax UBT_STAT_IERROR(sc); 749187494Semax goto submit_next; 750184610Salfred } 751184610Salfred 752187494Semax UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ 753187494Semax "length=%d\n", m->m_pkthdr.len, hdr->length); 754184610Salfred 755187494Semax UBT_STAT_PCKTS_RECV(sc); 756187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 757184610Salfred 758187741Semax ubt_fwd_mbuf_up(sc, &m); 759187494Semax /* m == NULL at this point */ 760187494Semax /* FALLTHROUGH */ 761184610Salfred 762184610Salfred case USB_ST_SETUP: 763187494Semaxsubmit_next: 764187494Semax NG_FREE_M(m); /* checks for m != NULL */ 765184610Salfred 766194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 767194228Sthompsa usbd_transfer_submit(xfer); 768187494Semax break; 769184610Salfred 770187494Semax default: /* Error */ 771194677Sthompsa if (error != USB_ERR_CANCELLED) { 772187494Semax UBT_WARN(sc, "interrupt transfer failed: %s\n", 773194677Sthompsa usbd_errstr(error)); 774184610Salfred 775187494Semax /* Try to clear stall first */ 776194677Sthompsa usbd_xfer_set_stall(xfer); 777187741Semax goto submit_next; 778187741Semax } 779187741Semax /* transfer cancelled */ 780187494Semax break; 781184610Salfred } 782187494Semax} /* ubt_intr_read_callback */ 783184610Salfred 784187494Semax/* 785187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e. 786187494Semax * ACL packet was received from the device. 787187494Semax * USB context. 788187494Semax */ 789187494Semax 790184610Salfredstatic void 791194677Sthompsaubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 792184610Salfred{ 793194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 794187494Semax struct mbuf *m; 795187494Semax ng_hci_acldata_pkt_t *hdr; 796194677Sthompsa struct usb_page_cache *pc; 797235000Shselasky int len; 798235000Shselasky int actlen; 799184610Salfred 800194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 801194677Sthompsa 802187494Semax m = NULL; 803184610Salfred 804184610Salfred switch (USB_GET_STATE(xfer)) { 805184610Salfred case USB_ST_TRANSFERRED: 806187494Semax /* Allocate new mbuf */ 807184610Salfred MGETHDR(m, M_DONTWAIT, MT_DATA); 808184610Salfred if (m == NULL) { 809187494Semax UBT_STAT_IERROR(sc); 810187494Semax goto submit_next; 811184610Salfred } 812187494Semax 813184610Salfred MCLGET(m, M_DONTWAIT); 814184610Salfred if (!(m->m_flags & M_EXT)) { 815187494Semax UBT_STAT_IERROR(sc); 816187494Semax goto submit_next; 817184610Salfred } 818184610Salfred 819187494Semax /* Add HCI packet type */ 820187494Semax *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; 821187494Semax m->m_pkthdr.len = m->m_len = 1; 822184610Salfred 823194677Sthompsa if (actlen > MCLBYTES - 1) 824194677Sthompsa actlen = MCLBYTES - 1; 825184610Salfred 826194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 827194677Sthompsa usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 828194677Sthompsa m->m_pkthdr.len += actlen; 829194677Sthompsa m->m_len += actlen; 830184610Salfred 831187494Semax UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", 832194677Sthompsa actlen); 833184610Salfred 834187494Semax /* Validate packet and send it up the stack */ 835235000Shselasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) { 836187494Semax UBT_INFO(sc, "HCI ACL packet is too short\n"); 837184610Salfred 838187494Semax UBT_STAT_IERROR(sc); 839187494Semax goto submit_next; 840184610Salfred } 841184610Salfred 842187494Semax hdr = mtod(m, ng_hci_acldata_pkt_t *); 843187494Semax len = le16toh(hdr->length); 844235000Shselasky if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) { 845187494Semax UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ 846187494Semax "pktlen=%d\n", len, m->m_pkthdr.len); 847184610Salfred 848187494Semax UBT_STAT_IERROR(sc); 849187494Semax goto submit_next; 850184610Salfred } 851184610Salfred 852187494Semax UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ 853187494Semax "length=%d\n", m->m_pkthdr.len, len); 854184610Salfred 855187494Semax UBT_STAT_PCKTS_RECV(sc); 856187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 857184610Salfred 858187741Semax ubt_fwd_mbuf_up(sc, &m); 859187494Semax /* m == NULL at this point */ 860187494Semax /* FALLTHOUGH */ 861184610Salfred 862187494Semax case USB_ST_SETUP: 863187494Semaxsubmit_next: 864187494Semax NG_FREE_M(m); /* checks for m != NULL */ 865184610Salfred 866194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 867194228Sthompsa usbd_transfer_submit(xfer); 868187494Semax break; 869184610Salfred 870187494Semax default: /* Error */ 871194677Sthompsa if (error != USB_ERR_CANCELLED) { 872187494Semax UBT_WARN(sc, "bulk-in transfer failed: %s\n", 873194677Sthompsa usbd_errstr(error)); 874184610Salfred 875187494Semax /* Try to clear stall first */ 876194677Sthompsa usbd_xfer_set_stall(xfer); 877187741Semax goto submit_next; 878187741Semax } 879187741Semax /* transfer cancelled */ 880187494Semax break; 881187494Semax } 882187494Semax} /* ubt_bulk_read_callback */ 883184610Salfred 884187494Semax/* 885187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e. 886187494Semax * ACL packet was sent to the device. 887187494Semax * USB context. 888187494Semax */ 889184610Salfred 890184610Salfredstatic void 891194677Sthompsaubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 892184610Salfred{ 893194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 894187494Semax struct mbuf *m; 895194677Sthompsa struct usb_page_cache *pc; 896194677Sthompsa int actlen; 897184610Salfred 898194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 899194677Sthompsa 900184610Salfred switch (USB_GET_STATE(xfer)) { 901184610Salfred case USB_ST_TRANSFERRED: 902194677Sthompsa UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen); 903194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 904187741Semax UBT_STAT_PCKTS_SENT(sc); 905187494Semax /* FALLTHROUGH */ 906184610Salfred 907187494Semax case USB_ST_SETUP: 908187741Semaxsend_next: 909187494Semax /* Get next mbuf, if any */ 910187741Semax UBT_NG_LOCK(sc); 911184610Salfred NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); 912187741Semax UBT_NG_UNLOCK(sc); 913184610Salfred 914184610Salfred if (m == NULL) { 915187494Semax UBT_INFO(sc, "ACL data queue is empty\n"); 916187741Semax break; /* transfer completed */ 917184610Salfred } 918187494Semax 919184610Salfred /* 920187494Semax * Copy ACL data frame back to a linear USB transfer buffer 921187494Semax * and schedule transfer 922184610Salfred */ 923184610Salfred 924194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 925194677Sthompsa usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 926194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 927184610Salfred 928187494Semax UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", 929187494Semax m->m_pkthdr.len); 930184610Salfred 931184610Salfred NG_FREE_M(m); 932184610Salfred 933194228Sthompsa usbd_transfer_submit(xfer); 934187494Semax break; 935184610Salfred 936187494Semax default: /* Error */ 937194677Sthompsa if (error != USB_ERR_CANCELLED) { 938187494Semax UBT_WARN(sc, "bulk-out transfer failed: %s\n", 939194677Sthompsa usbd_errstr(error)); 940184610Salfred 941187494Semax UBT_STAT_OERROR(sc); 942184610Salfred 943184610Salfred /* try to clear stall first */ 944194677Sthompsa usbd_xfer_set_stall(xfer); 945187741Semax goto send_next; 946187741Semax } 947187741Semax /* transfer cancelled */ 948187494Semax break; 949184610Salfred } 950187494Semax} /* ubt_bulk_write_callback */ 951184610Salfred 952187494Semax/* 953187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e. 954187494Semax * SCO packet was received from the device. 955187494Semax * USB context. 956187494Semax */ 957187494Semax 958184610Salfredstatic void 959194677Sthompsaubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 960184610Salfred{ 961194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 962187494Semax int n; 963194677Sthompsa int actlen, nframes; 964184610Salfred 965194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 966194677Sthompsa 967187494Semax switch (USB_GET_STATE(xfer)) { 968187494Semax case USB_ST_TRANSFERRED: 969194677Sthompsa for (n = 0; n < nframes; n ++) 970187494Semax if (ubt_isoc_read_one_frame(xfer, n) < 0) 971187494Semax break; 972187494Semax /* FALLTHROUGH */ 973184610Salfred 974187494Semax case USB_ST_SETUP: 975187494Semaxread_next: 976194677Sthompsa for (n = 0; n < nframes; n ++) 977194677Sthompsa usbd_xfer_set_frame_len(xfer, n, 978194677Sthompsa usbd_xfer_max_framelen(xfer)); 979184610Salfred 980194228Sthompsa usbd_transfer_submit(xfer); 981187494Semax break; 982184610Salfred 983187494Semax default: /* Error */ 984194677Sthompsa if (error != USB_ERR_CANCELLED) { 985187494Semax UBT_STAT_IERROR(sc); 986187494Semax goto read_next; 987187494Semax } 988184610Salfred 989187741Semax /* transfer cancelled */ 990187494Semax break; 991187494Semax } 992187494Semax} /* ubt_isoc_read_callback */ 993184610Salfred 994187494Semax/* 995187494Semax * Helper function. Called from ubt_isoc_read_callback() to read 996187494Semax * SCO data from one frame. 997187494Semax * USB context. 998187494Semax */ 999184610Salfred 1000187494Semaxstatic int 1001192984Sthompsaubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no) 1002187494Semax{ 1003194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1004194677Sthompsa struct usb_page_cache *pc; 1005187494Semax struct mbuf *m; 1006194677Sthompsa int len, want, got, total; 1007184610Salfred 1008187494Semax /* Get existing SCO reassembly buffer */ 1009194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1010187494Semax m = sc->sc_isoc_in_buffer; 1011194682Sthompsa total = usbd_xfer_frame_len(xfer, frame_no); 1012184610Salfred 1013187494Semax /* While we have data in the frame */ 1014194677Sthompsa while (total > 0) { 1015187494Semax if (m == NULL) { 1016187494Semax /* Start new reassembly buffer */ 1017187494Semax MGETHDR(m, M_DONTWAIT, MT_DATA); 1018187494Semax if (m == NULL) { 1019187494Semax UBT_STAT_IERROR(sc); 1020187494Semax return (-1); /* XXX out of sync! */ 1021187494Semax } 1022184610Salfred 1023187494Semax MCLGET(m, M_DONTWAIT); 1024187494Semax if (!(m->m_flags & M_EXT)) { 1025187494Semax UBT_STAT_IERROR(sc); 1026187494Semax NG_FREE_M(m); 1027187494Semax return (-1); /* XXX out of sync! */ 1028184610Salfred } 1029184610Salfred 1030187494Semax /* Expect SCO header */ 1031187494Semax *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; 1032187494Semax m->m_pkthdr.len = m->m_len = got = 1; 1033187494Semax want = sizeof(ng_hci_scodata_pkt_t); 1034187494Semax } else { 1035187494Semax /* 1036187494Semax * Check if we have SCO header and if so 1037187494Semax * adjust amount of data we want 1038187494Semax */ 1039187494Semax got = m->m_pkthdr.len; 1040187494Semax want = sizeof(ng_hci_scodata_pkt_t); 1041184610Salfred 1042187494Semax if (got >= want) 1043187494Semax want += mtod(m, ng_hci_scodata_pkt_t *)->length; 1044184610Salfred } 1045184610Salfred 1046187494Semax /* Append frame data to the SCO reassembly buffer */ 1047194677Sthompsa len = total; 1048187494Semax if (got + len > want) 1049187494Semax len = want - got; 1050184610Salfred 1051194677Sthompsa usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer), 1052187494Semax mtod(m, uint8_t *) + m->m_pkthdr.len, len); 1053184610Salfred 1054187494Semax m->m_pkthdr.len += len; 1055187494Semax m->m_len += len; 1056194677Sthompsa total -= len; 1057184610Salfred 1058187494Semax /* Check if we got everything we wanted, if not - continue */ 1059187494Semax if (got != want) 1060187494Semax continue; 1061184610Salfred 1062187494Semax /* If we got here then we got complete SCO frame */ 1063187494Semax UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ 1064187494Semax "length=%d\n", m->m_pkthdr.len, 1065187494Semax mtod(m, ng_hci_scodata_pkt_t *)->length); 1066184610Salfred 1067187494Semax UBT_STAT_PCKTS_RECV(sc); 1068187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 1069184610Salfred 1070187741Semax ubt_fwd_mbuf_up(sc, &m); 1071187494Semax /* m == NULL at this point */ 1072187494Semax } 1073184610Salfred 1074187494Semax /* Put SCO reassembly buffer back */ 1075187494Semax sc->sc_isoc_in_buffer = m; 1076184610Salfred 1077187494Semax return (0); 1078187494Semax} /* ubt_isoc_read_one_frame */ 1079184610Salfred 1080187494Semax/* 1081187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e. 1082187494Semax * SCO packet was sent to the device. 1083187494Semax * USB context. 1084187494Semax */ 1085184610Salfred 1086184610Salfredstatic void 1087194677Sthompsaubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 1088184610Salfred{ 1089194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1090194677Sthompsa struct usb_page_cache *pc; 1091187494Semax struct mbuf *m; 1092187494Semax int n, space, offset; 1093194677Sthompsa int actlen, nframes; 1094184610Salfred 1095194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1096194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1097194677Sthompsa 1098184610Salfred switch (USB_GET_STATE(xfer)) { 1099184610Salfred case USB_ST_TRANSFERRED: 1100194677Sthompsa UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen); 1101194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 1102187741Semax UBT_STAT_PCKTS_SENT(sc); 1103187494Semax /* FALLTHROUGH */ 1104184610Salfred 1105184610Salfred case USB_ST_SETUP: 1106187494Semaxsend_next: 1107184610Salfred offset = 0; 1108194677Sthompsa space = usbd_xfer_max_framelen(xfer) * nframes; 1109187494Semax m = NULL; 1110184610Salfred 1111187494Semax while (space > 0) { 1112187494Semax if (m == NULL) { 1113187741Semax UBT_NG_LOCK(sc); 1114187494Semax NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); 1115187741Semax UBT_NG_UNLOCK(sc); 1116184610Salfred 1117187494Semax if (m == NULL) 1118187494Semax break; 1119187494Semax } 1120184610Salfred 1121187494Semax n = min(space, m->m_pkthdr.len); 1122187494Semax if (n > 0) { 1123194677Sthompsa usbd_m_copy_in(pc, offset, m,0, n); 1124187494Semax m_adj(m, n); 1125184610Salfred 1126187494Semax offset += n; 1127187494Semax space -= n; 1128187494Semax } 1129184610Salfred 1130187494Semax if (m->m_pkthdr.len == 0) 1131187494Semax NG_FREE_M(m); /* sets m = NULL */ 1132187494Semax } 1133184610Salfred 1134187494Semax /* Put whatever is left from mbuf back on queue */ 1135187494Semax if (m != NULL) { 1136187741Semax UBT_NG_LOCK(sc); 1137187494Semax NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); 1138187741Semax UBT_NG_UNLOCK(sc); 1139184610Salfred } 1140184610Salfred 1141187494Semax /* 1142187494Semax * Calculate sizes for isoc frames. 1143187494Semax * Note that offset could be 0 at this point (i.e. we have 1144187494Semax * nothing to send). That is fine, as we have isoc. transfers 1145187494Semax * going in both directions all the time. In this case it 1146187494Semax * would be just empty isoc. transfer. 1147187494Semax */ 1148187494Semax 1149194677Sthompsa for (n = 0; n < nframes; n ++) { 1150194677Sthompsa usbd_xfer_set_frame_len(xfer, n, 1151194677Sthompsa min(offset, usbd_xfer_max_framelen(xfer))); 1152194682Sthompsa offset -= usbd_xfer_frame_len(xfer, n); 1153187494Semax } 1154187494Semax 1155194228Sthompsa usbd_transfer_submit(xfer); 1156187494Semax break; 1157184610Salfred 1158187494Semax default: /* Error */ 1159194677Sthompsa if (error != USB_ERR_CANCELLED) { 1160187494Semax UBT_STAT_OERROR(sc); 1161187494Semax goto send_next; 1162184610Salfred } 1163187494Semax 1164187741Semax /* transfer cancelled */ 1165187494Semax break; 1166184610Salfred } 1167184610Salfred} 1168184610Salfred 1169187741Semax/* 1170187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack). 1171187741Semax * Modifies value of the mbuf pointer (sets it to NULL). 1172187741Semax * Save to call from any context. 1173187741Semax */ 1174187741Semax 1175187741Semaxstatic int 1176187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) 1177187741Semax{ 1178187741Semax hook_p hook; 1179187741Semax int error; 1180187741Semax 1181187741Semax /* 1182187741Semax * Close the race with Netgraph hook newhook/disconnect methods. 1183187741Semax * Save the hook pointer atomically. Two cases are possible: 1184187741Semax * 1185187741Semax * 1) The hook pointer is NULL. It means disconnect method got 1186187741Semax * there first. In this case we are done. 1187187741Semax * 1188187741Semax * 2) The hook pointer is not NULL. It means that hook pointer 1189187741Semax * could be either in valid or invalid (i.e. in the process 1190187741Semax * of disconnect) state. In any case grab an extra reference 1191187741Semax * to protect the hook pointer. 1192187741Semax * 1193187741Semax * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as 1194187741Semax * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). 1195187741Semax */ 1196187741Semax 1197187741Semax UBT_NG_LOCK(sc); 1198187741Semax if ((hook = sc->sc_hook) != NULL) 1199187741Semax NG_HOOK_REF(hook); 1200187741Semax UBT_NG_UNLOCK(sc); 1201187741Semax 1202187741Semax if (hook == NULL) { 1203187741Semax NG_FREE_M(*m); 1204187741Semax return (ENETDOWN); 1205187741Semax } 1206187741Semax 1207187741Semax NG_SEND_DATA_ONLY(error, hook, *m); 1208187741Semax NG_HOOK_UNREF(hook); 1209187741Semax 1210187741Semax if (error != 0) 1211187741Semax UBT_STAT_IERROR(sc); 1212187741Semax 1213187741Semax return (error); 1214187741Semax} /* ubt_fwd_mbuf_up */ 1215187741Semax 1216184610Salfred/**************************************************************************** 1217184610Salfred **************************************************************************** 1218187494Semax ** Glue 1219184610Salfred **************************************************************************** 1220184610Salfred ****************************************************************************/ 1221184610Salfred 1222184610Salfred/* 1223187741Semax * Schedule glue task. Should be called with sc_ng_mtx held. 1224187494Semax * Netgraph context. 1225184610Salfred */ 1226184610Salfred 1227187741Semaxstatic void 1228187494Semaxubt_task_schedule(ubt_softc_p sc, int action) 1229184610Salfred{ 1230187741Semax mtx_assert(&sc->sc_ng_mtx, MA_OWNED); 1231184610Salfred 1232187741Semax /* 1233187741Semax * Try to handle corner case when "start all" and "stop all" 1234187741Semax * actions can both be set before task is executed. 1235187741Semax * 1236187741Semax * The rules are 1237187741Semax * 1238187741Semax * sc_task_flags action new sc_task_flags 1239187741Semax * ------------------------------------------------------ 1240187741Semax * 0 start start 1241187741Semax * 0 stop stop 1242187741Semax * start start start 1243187741Semax * start stop stop 1244187741Semax * stop start stop|start 1245187741Semax * stop stop stop 1246187741Semax * stop|start start stop|start 1247187741Semax * stop|start stop stop 1248187741Semax */ 1249187494Semax 1250187741Semax if (action != 0) { 1251187741Semax if ((action & UBT_FLAG_T_STOP_ALL) != 0) 1252187494Semax sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; 1253187494Semax 1254187494Semax sc->sc_task_flags |= action; 1255187494Semax } 1256187494Semax 1257187494Semax if (sc->sc_task_flags & UBT_FLAG_T_PENDING) 1258187741Semax return; 1259187494Semax 1260187494Semax if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { 1261187494Semax sc->sc_task_flags |= UBT_FLAG_T_PENDING; 1262187741Semax return; 1263187494Semax } 1264187494Semax 1265187494Semax /* XXX: i think this should never happen */ 1266187494Semax} /* ubt_task_schedule */ 1267187494Semax 1268184610Salfred/* 1269187494Semax * Glue task. Examines sc_task_flags and does things depending on it. 1270187494Semax * Taskqueue context. 1271184610Salfred */ 1272184610Salfred 1273187494Semaxstatic void 1274187494Semaxubt_task(void *context, int pending) 1275184610Salfred{ 1276187741Semax ubt_softc_p sc = context; 1277187741Semax int task_flags, i; 1278184610Salfred 1279187741Semax UBT_NG_LOCK(sc); 1280187494Semax task_flags = sc->sc_task_flags; 1281187494Semax sc->sc_task_flags = 0; 1282187741Semax UBT_NG_UNLOCK(sc); 1283187494Semax 1284187741Semax /* 1285187741Semax * Stop all USB transfers synchronously. 1286187741Semax * Stop interface #0 and #1 transfers at the same time and in the 1287194228Sthompsa * same loop. usbd_transfer_drain() will do appropriate locking. 1288187741Semax */ 1289187494Semax 1290187741Semax if (task_flags & UBT_FLAG_T_STOP_ALL) 1291187741Semax for (i = 0; i < UBT_N_TRANSFER; i ++) 1292194228Sthompsa usbd_transfer_drain(sc->sc_xfer[i]); 1293187494Semax 1294187741Semax /* Start incoming interrupt and bulk, and all isoc. USB transfers */ 1295187494Semax if (task_flags & UBT_FLAG_T_START_ALL) { 1296187494Semax /* 1297187494Semax * Interface #0 1298187494Semax */ 1299187494Semax 1300187741Semax mtx_lock(&sc->sc_if_mtx); 1301187741Semax 1302187494Semax ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); 1303187494Semax ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); 1304187494Semax 1305187494Semax /* 1306187494Semax * Interface #1 1307187494Semax * Start both read and write isoc. transfers by default. 1308187494Semax * Get them going all the time even if we have nothing 1309187494Semax * to send to avoid any delays. 1310187494Semax */ 1311187494Semax 1312187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); 1313187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); 1314187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); 1315187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); 1316187741Semax 1317187741Semax mtx_unlock(&sc->sc_if_mtx); 1318184610Salfred } 1319187494Semax 1320187494Semax /* Start outgoing control transfer */ 1321187494Semax if (task_flags & UBT_FLAG_T_START_CTRL) { 1322187741Semax mtx_lock(&sc->sc_if_mtx); 1323187494Semax ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); 1324187741Semax mtx_unlock(&sc->sc_if_mtx); 1325184610Salfred } 1326184610Salfred 1327187494Semax /* Start outgoing bulk transfer */ 1328187494Semax if (task_flags & UBT_FLAG_T_START_BULK) { 1329187741Semax mtx_lock(&sc->sc_if_mtx); 1330187494Semax ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); 1331187741Semax mtx_unlock(&sc->sc_if_mtx); 1332184610Salfred } 1333187494Semax} /* ubt_task */ 1334187494Semax 1335187494Semax/**************************************************************************** 1336187494Semax **************************************************************************** 1337187494Semax ** Netgraph specific 1338187494Semax **************************************************************************** 1339187494Semax ****************************************************************************/ 1340184610Salfred 1341187494Semax/* 1342187494Semax * Netgraph node constructor. Do not allow to create node of this type. 1343187494Semax * Netgraph context. 1344187494Semax */ 1345184610Salfred 1346187494Semaxstatic int 1347187494Semaxng_ubt_constructor(node_p node) 1348187494Semax{ 1349187494Semax return (EINVAL); 1350187494Semax} /* ng_ubt_constructor */ 1351184610Salfred 1352184610Salfred/* 1353187494Semax * Netgraph node destructor. Destroy node only when device has been detached. 1354187494Semax * Netgraph context. 1355184610Salfred */ 1356184610Salfred 1357184610Salfredstatic int 1358187494Semaxng_ubt_shutdown(node_p node) 1359184610Salfred{ 1360187494Semax if (node->nd_flags & NGF_REALLY_DIE) { 1361187494Semax /* 1362187494Semax * We came here because the USB device is being 1363187494Semax * detached, so stop being persistant. 1364187494Semax */ 1365187494Semax NG_NODE_SET_PRIVATE(node, NULL); 1366187494Semax NG_NODE_UNREF(node); 1367187494Semax } else 1368187494Semax NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ 1369184610Salfred 1370187494Semax return (0); 1371187494Semax} /* ng_ubt_shutdown */ 1372184610Salfred 1373187494Semax/* 1374187494Semax * Create new hook. There can only be one. 1375187494Semax * Netgraph context. 1376187494Semax */ 1377184610Salfred 1378187494Semaxstatic int 1379187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name) 1380187494Semax{ 1381187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1382184610Salfred 1383187494Semax if (strcmp(name, NG_UBT_HOOK) != 0) 1384187494Semax return (EINVAL); 1385184610Salfred 1386187741Semax UBT_NG_LOCK(sc); 1387187741Semax if (sc->sc_hook != NULL) { 1388187741Semax UBT_NG_UNLOCK(sc); 1389187741Semax 1390187494Semax return (EISCONN); 1391187741Semax } 1392184610Salfred 1393187494Semax sc->sc_hook = hook; 1394187741Semax UBT_NG_UNLOCK(sc); 1395184610Salfred 1396187494Semax return (0); 1397187494Semax} /* ng_ubt_newhook */ 1398184610Salfred 1399187494Semax/* 1400187494Semax * Connect hook. Start incoming USB transfers. 1401187494Semax * Netgraph context. 1402187494Semax */ 1403184610Salfred 1404187494Semaxstatic int 1405187494Semaxng_ubt_connect(hook_p hook) 1406187494Semax{ 1407187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1408184610Salfred 1409187494Semax NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 1410184610Salfred 1411187741Semax UBT_NG_LOCK(sc); 1412187494Semax ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); 1413187741Semax UBT_NG_UNLOCK(sc); 1414184610Salfred 1415184610Salfred return (0); 1416187494Semax} /* ng_ubt_connect */ 1417184610Salfred 1418184610Salfred/* 1419187494Semax * Disconnect hook. 1420187494Semax * Netgraph context. 1421184610Salfred */ 1422184610Salfred 1423184610Salfredstatic int 1424184610Salfredng_ubt_disconnect(hook_p hook) 1425184610Salfred{ 1426187741Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1427184610Salfred 1428187741Semax UBT_NG_LOCK(sc); 1429184610Salfred 1430187741Semax if (hook != sc->sc_hook) { 1431187741Semax UBT_NG_UNLOCK(sc); 1432184610Salfred 1433187494Semax return (EINVAL); 1434187741Semax } 1435184610Salfred 1436187494Semax sc->sc_hook = NULL; 1437184610Salfred 1438187741Semax /* Kick off task to stop all USB xfers */ 1439187741Semax ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); 1440184610Salfred 1441187494Semax /* Drain queues */ 1442187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); 1443187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); 1444187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); 1445184610Salfred 1446187741Semax UBT_NG_UNLOCK(sc); 1447184610Salfred 1448187494Semax return (0); 1449187494Semax} /* ng_ubt_disconnect */ 1450187494Semax 1451184610Salfred/* 1452187494Semax * Process control message. 1453187494Semax * Netgraph context. 1454184610Salfred */ 1455184610Salfred 1456184610Salfredstatic int 1457184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) 1458184610Salfred{ 1459187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1460187494Semax struct ng_mesg *msg, *rsp = NULL; 1461187494Semax struct ng_bt_mbufq *q; 1462187494Semax int error = 0, queue, qlen; 1463184610Salfred 1464184610Salfred NGI_GET_MSG(item, msg); 1465184610Salfred 1466184610Salfred switch (msg->header.typecookie) { 1467184610Salfred case NGM_GENERIC_COOKIE: 1468184610Salfred switch (msg->header.cmd) { 1469184610Salfred case NGM_TEXT_STATUS: 1470184610Salfred NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 1471187494Semax if (rsp == NULL) { 1472184610Salfred error = ENOMEM; 1473187494Semax break; 1474187494Semax } 1475187494Semax 1476187494Semax snprintf(rsp->data, NG_TEXTRESPONSE, 1477187494Semax "Hook: %s\n" \ 1478187494Semax "Task flags: %#x\n" \ 1479187494Semax "Debug: %d\n" \ 1480187494Semax "CMD queue: [have:%d,max:%d]\n" \ 1481187494Semax "ACL queue: [have:%d,max:%d]\n" \ 1482187494Semax "SCO queue: [have:%d,max:%d]", 1483187741Semax (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", 1484187494Semax sc->sc_task_flags, 1485187494Semax sc->sc_debug, 1486187494Semax sc->sc_cmdq.len, 1487187494Semax sc->sc_cmdq.maxlen, 1488187494Semax sc->sc_aclq.len, 1489187494Semax sc->sc_aclq.maxlen, 1490187494Semax sc->sc_scoq.len, 1491187494Semax sc->sc_scoq.maxlen); 1492184610Salfred break; 1493184610Salfred 1494184610Salfred default: 1495184610Salfred error = EINVAL; 1496184610Salfred break; 1497184610Salfred } 1498184610Salfred break; 1499184610Salfred 1500184610Salfred case NGM_UBT_COOKIE: 1501184610Salfred switch (msg->header.cmd) { 1502184610Salfred case NGM_UBT_NODE_SET_DEBUG: 1503187494Semax if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ 1504184610Salfred error = EMSGSIZE; 1505187494Semax break; 1506187494Semax } 1507187494Semax 1508187494Semax sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); 1509184610Salfred break; 1510184610Salfred 1511184610Salfred case NGM_UBT_NODE_GET_DEBUG: 1512184610Salfred NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), 1513184610Salfred M_NOWAIT); 1514187494Semax if (rsp == NULL) { 1515184610Salfred error = ENOMEM; 1516187494Semax break; 1517187494Semax } 1518187494Semax 1519187494Semax *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; 1520184610Salfred break; 1521184610Salfred 1522184610Salfred case NGM_UBT_NODE_SET_QLEN: 1523187494Semax if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1524184610Salfred error = EMSGSIZE; 1525187494Semax break; 1526187494Semax } 1527184610Salfred 1528187494Semax queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1529187494Semax qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; 1530184610Salfred 1531187494Semax switch (queue) { 1532187494Semax case NGM_UBT_NODE_QUEUE_CMD: 1533187494Semax q = &sc->sc_cmdq; 1534187494Semax break; 1535184610Salfred 1536187494Semax case NGM_UBT_NODE_QUEUE_ACL: 1537187494Semax q = &sc->sc_aclq; 1538187494Semax break; 1539184610Salfred 1540187494Semax case NGM_UBT_NODE_QUEUE_SCO: 1541187494Semax q = &sc->sc_scoq; 1542187494Semax break; 1543184610Salfred 1544187494Semax default: 1545187494Semax error = EINVAL; 1546187494Semax goto done; 1547187494Semax /* NOT REACHED */ 1548184610Salfred } 1549187494Semax 1550187494Semax q->maxlen = qlen; 1551184610Salfred break; 1552184610Salfred 1553184610Salfred case NGM_UBT_NODE_GET_QLEN: 1554184610Salfred if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1555184610Salfred error = EMSGSIZE; 1556184610Salfred break; 1557184610Salfred } 1558187494Semax 1559184610Salfred queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1560187494Semax 1561184610Salfred switch (queue) { 1562184610Salfred case NGM_UBT_NODE_QUEUE_CMD: 1563184610Salfred q = &sc->sc_cmdq; 1564184610Salfred break; 1565184610Salfred 1566184610Salfred case NGM_UBT_NODE_QUEUE_ACL: 1567184610Salfred q = &sc->sc_aclq; 1568184610Salfred break; 1569184610Salfred 1570184610Salfred case NGM_UBT_NODE_QUEUE_SCO: 1571184610Salfred q = &sc->sc_scoq; 1572184610Salfred break; 1573184610Salfred 1574184610Salfred default: 1575184610Salfred error = EINVAL; 1576187494Semax goto done; 1577187494Semax /* NOT REACHED */ 1578187494Semax } 1579187494Semax 1580187494Semax NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), 1581187494Semax M_NOWAIT); 1582187494Semax if (rsp == NULL) { 1583187494Semax error = ENOMEM; 1584184610Salfred break; 1585184610Salfred } 1586184610Salfred 1587187494Semax ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; 1588187494Semax ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; 1589184610Salfred break; 1590184610Salfred 1591184610Salfred case NGM_UBT_NODE_GET_STAT: 1592184610Salfred NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), 1593184610Salfred M_NOWAIT); 1594187494Semax if (rsp == NULL) { 1595184610Salfred error = ENOMEM; 1596187494Semax break; 1597184610Salfred } 1598187494Semax 1599187494Semax bcopy(&sc->sc_stat, rsp->data, 1600187494Semax sizeof(ng_ubt_node_stat_ep)); 1601184610Salfred break; 1602184610Salfred 1603184610Salfred case NGM_UBT_NODE_RESET_STAT: 1604187494Semax UBT_STAT_RESET(sc); 1605184610Salfred break; 1606184610Salfred 1607184610Salfred default: 1608184610Salfred error = EINVAL; 1609184610Salfred break; 1610184610Salfred } 1611184610Salfred break; 1612184610Salfred 1613184610Salfred default: 1614184610Salfred error = EINVAL; 1615184610Salfred break; 1616184610Salfred } 1617187494Semaxdone: 1618184610Salfred NG_RESPOND_MSG(error, node, item, rsp); 1619184610Salfred NG_FREE_MSG(msg); 1620184610Salfred 1621184610Salfred return (error); 1622187494Semax} /* ng_ubt_rcvmsg */ 1623184610Salfred 1624184610Salfred/* 1625187494Semax * Process data. 1626187494Semax * Netgraph context. 1627184610Salfred */ 1628184610Salfred 1629184610Salfredstatic int 1630184610Salfredng_ubt_rcvdata(hook_p hook, item_p item) 1631184610Salfred{ 1632187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1633187494Semax struct mbuf *m; 1634187494Semax struct ng_bt_mbufq *q; 1635187494Semax int action, error = 0; 1636184610Salfred 1637184610Salfred if (hook != sc->sc_hook) { 1638184610Salfred error = EINVAL; 1639184610Salfred goto done; 1640184610Salfred } 1641187494Semax 1642187494Semax /* Deatch mbuf and get HCI frame type */ 1643184610Salfred NGI_GET_M(item, m); 1644184610Salfred 1645187494Semax /* 1646187494Semax * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, 1647187494Semax * 2 bytes connection handle and at least 1 byte of length. 1648187494Semax * Panic on data frame that has size smaller than 4 bytes (it 1649187494Semax * should not happen) 1650187494Semax */ 1651187494Semax 1652187494Semax if (m->m_pkthdr.len < 4) 1653187494Semax panic("HCI frame size is too small! pktlen=%d\n", 1654187494Semax m->m_pkthdr.len); 1655187494Semax 1656187494Semax /* Process HCI frame */ 1657184610Salfred switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ 1658184610Salfred case NG_HCI_CMD_PKT: 1659235000Shselasky if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE) 1660187494Semax panic("HCI command frame size is too big! " \ 1661187494Semax "buffer size=%zd, packet len=%d\n", 1662187494Semax UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); 1663187494Semax 1664184610Salfred q = &sc->sc_cmdq; 1665187494Semax action = UBT_FLAG_T_START_CTRL; 1666184610Salfred break; 1667184610Salfred 1668184610Salfred case NG_HCI_ACL_DATA_PKT: 1669187494Semax if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) 1670187494Semax panic("ACL data frame size is too big! " \ 1671187494Semax "buffer size=%d, packet len=%d\n", 1672187494Semax UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); 1673187494Semax 1674184610Salfred q = &sc->sc_aclq; 1675187494Semax action = UBT_FLAG_T_START_BULK; 1676184610Salfred break; 1677184610Salfred 1678184610Salfred case NG_HCI_SCO_DATA_PKT: 1679184610Salfred q = &sc->sc_scoq; 1680187494Semax action = 0; 1681184610Salfred break; 1682184610Salfred 1683184610Salfred default: 1684187494Semax UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ 1685187494Semax "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len); 1686184610Salfred 1687184610Salfred NG_FREE_M(m); 1688184610Salfred error = EINVAL; 1689184610Salfred goto done; 1690187494Semax /* NOT REACHED */ 1691184610Salfred } 1692184610Salfred 1693187741Semax UBT_NG_LOCK(sc); 1694184610Salfred if (NG_BT_MBUFQ_FULL(q)) { 1695187494Semax NG_BT_MBUFQ_DROP(q); 1696187741Semax UBT_NG_UNLOCK(sc); 1697187494Semax 1698187494Semax UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", 1699187494Semax *mtod(m, uint8_t *), m->m_pkthdr.len); 1700187494Semax 1701184610Salfred NG_FREE_M(m); 1702184610Salfred } else { 1703187494Semax /* Loose HCI packet type, enqueue mbuf and kick off task */ 1704187494Semax m_adj(m, sizeof(uint8_t)); 1705184610Salfred NG_BT_MBUFQ_ENQUEUE(q, m); 1706187494Semax ubt_task_schedule(sc, action); 1707187741Semax UBT_NG_UNLOCK(sc); 1708184610Salfred } 1709184610Salfreddone: 1710184610Salfred NG_FREE_ITEM(item); 1711184610Salfred 1712187494Semax return (error); 1713187494Semax} /* ng_ubt_rcvdata */ 1714187494Semax 1715187494Semax/**************************************************************************** 1716187494Semax **************************************************************************** 1717187494Semax ** Module 1718187494Semax **************************************************************************** 1719187494Semax ****************************************************************************/ 1720187494Semax 1721187494Semax/* 1722187494Semax * Load/Unload the driver module 1723187494Semax */ 1724187494Semax 1725187494Semaxstatic int 1726187494Semaxubt_modevent(module_t mod, int event, void *data) 1727187494Semax{ 1728187494Semax int error; 1729187494Semax 1730187494Semax switch (event) { 1731187494Semax case MOD_LOAD: 1732187494Semax error = ng_newtype(&typestruct); 1733187494Semax if (error != 0) 1734187494Semax printf("%s: Could not register Netgraph node type, " \ 1735187494Semax "error=%d\n", NG_UBT_NODE_TYPE, error); 1736187494Semax break; 1737187494Semax 1738187494Semax case MOD_UNLOAD: 1739187494Semax error = ng_rmtype(&typestruct); 1740187494Semax break; 1741187494Semax 1742187494Semax default: 1743187494Semax error = EOPNOTSUPP; 1744187494Semax break; 1745184610Salfred } 1746187494Semax 1747184610Salfred return (error); 1748187494Semax} /* ubt_modevent */ 1749187494Semax 1750187494Semaxstatic devclass_t ubt_devclass; 1751187494Semax 1752187494Semaxstatic device_method_t ubt_methods[] = 1753187494Semax{ 1754187494Semax DEVMETHOD(device_probe, ubt_probe), 1755187494Semax DEVMETHOD(device_attach, ubt_attach), 1756187494Semax DEVMETHOD(device_detach, ubt_detach), 1757244956Shselasky DEVMETHOD_END 1758187494Semax}; 1759187494Semax 1760187494Semaxstatic driver_t ubt_driver = 1761187494Semax{ 1762187494Semax .name = "ubt", 1763187494Semax .methods = ubt_methods, 1764187494Semax .size = sizeof(struct ubt_softc), 1765187494Semax}; 1766187494Semax 1767189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0); 1768187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); 1769187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 1770187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 1771188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1); 1772187494Semax 1773