ng_ubt.c revision 194682
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: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c 194682 2009-06-23 06:00:31Z thompsa $ 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/linker_set.h> 104194677Sthompsa#include <sys/module.h> 105194677Sthompsa#include <sys/lock.h> 106194677Sthompsa#include <sys/mutex.h> 107194677Sthompsa#include <sys/condvar.h> 108194677Sthompsa#include <sys/sysctl.h> 109194677Sthompsa#include <sys/sx.h> 110194677Sthompsa#include <sys/unistd.h> 111194677Sthompsa#include <sys/callout.h> 112194677Sthompsa#include <sys/malloc.h> 113194677Sthompsa#include <sys/priv.h> 114194677Sthompsa 115188746Sthompsa#include "usbdevs.h" 116188942Sthompsa#include <dev/usb/usb.h> 117194677Sthompsa#include <dev/usb/usbdi.h> 118194677Sthompsa#include <dev/usb/usbdi_util.h> 119184610Salfred 120194228Sthompsa#define USB_DEBUG_VAR usb_debug 121188942Sthompsa#include <dev/usb/usb_debug.h> 122188942Sthompsa#include <dev/usb/usb_busdma.h> 123184610Salfred 124184610Salfred#include <sys/mbuf.h> 125187494Semax#include <sys/taskqueue.h> 126184610Salfred 127184610Salfred#include <netgraph/ng_message.h> 128184610Salfred#include <netgraph/netgraph.h> 129184610Salfred#include <netgraph/ng_parse.h> 130184610Salfred#include <netgraph/bluetooth/include/ng_bluetooth.h> 131184610Salfred#include <netgraph/bluetooth/include/ng_hci.h> 132184610Salfred#include <netgraph/bluetooth/include/ng_ubt.h> 133192909Sthompsa#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h> 134184610Salfred 135187494Semaxstatic int ubt_modevent(module_t, int, void *); 136187494Semaxstatic device_probe_t ubt_probe; 137187494Semaxstatic device_attach_t ubt_attach; 138187494Semaxstatic device_detach_t ubt_detach; 139184610Salfred 140187741Semaxstatic void ubt_task_schedule(ubt_softc_p, int); 141187494Semaxstatic task_fn_t ubt_task; 142184610Salfred 143194228Sthompsa#define ubt_xfer_start(sc, i) usbd_transfer_start((sc)->sc_xfer[(i)]) 144187741Semax 145187494Semax/* Netgraph methods */ 146187494Semaxstatic ng_constructor_t ng_ubt_constructor; 147187494Semaxstatic ng_shutdown_t ng_ubt_shutdown; 148187494Semaxstatic ng_newhook_t ng_ubt_newhook; 149187494Semaxstatic ng_connect_t ng_ubt_connect; 150187494Semaxstatic ng_disconnect_t ng_ubt_disconnect; 151187494Semaxstatic ng_rcvmsg_t ng_ubt_rcvmsg; 152187494Semaxstatic ng_rcvdata_t ng_ubt_rcvdata; 153184610Salfred 154184610Salfred/* Queue length */ 155187494Semaxstatic const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = 156184610Salfred{ 157187494Semax { "queue", &ng_parse_int32_type, }, 158187494Semax { "qlen", &ng_parse_int32_type, }, 159187494Semax { NULL, } 160184610Salfred}; 161187494Semaxstatic const struct ng_parse_type ng_ubt_node_qlen_type = 162187494Semax{ 163184610Salfred &ng_parse_struct_type, 164184610Salfred &ng_ubt_node_qlen_type_fields 165184610Salfred}; 166184610Salfred 167184610Salfred/* Stat info */ 168187494Semaxstatic const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = 169184610Salfred{ 170187494Semax { "pckts_recv", &ng_parse_uint32_type, }, 171187494Semax { "bytes_recv", &ng_parse_uint32_type, }, 172187494Semax { "pckts_sent", &ng_parse_uint32_type, }, 173187494Semax { "bytes_sent", &ng_parse_uint32_type, }, 174187494Semax { "oerrors", &ng_parse_uint32_type, }, 175187494Semax { "ierrors", &ng_parse_uint32_type, }, 176187494Semax { NULL, } 177184610Salfred}; 178187494Semaxstatic const struct ng_parse_type ng_ubt_node_stat_type = 179187494Semax{ 180184610Salfred &ng_parse_struct_type, 181184610Salfred &ng_ubt_node_stat_type_fields 182184610Salfred}; 183184610Salfred 184184610Salfred/* Netgraph node command list */ 185187494Semaxstatic const struct ng_cmdlist ng_ubt_cmdlist[] = 186187494Semax{ 187184610Salfred { 188184610Salfred NGM_UBT_COOKIE, 189184610Salfred NGM_UBT_NODE_SET_DEBUG, 190184610Salfred "set_debug", 191184610Salfred &ng_parse_uint16_type, 192184610Salfred NULL 193184610Salfred }, 194184610Salfred { 195184610Salfred NGM_UBT_COOKIE, 196184610Salfred NGM_UBT_NODE_GET_DEBUG, 197184610Salfred "get_debug", 198184610Salfred NULL, 199184610Salfred &ng_parse_uint16_type 200184610Salfred }, 201184610Salfred { 202184610Salfred NGM_UBT_COOKIE, 203184610Salfred NGM_UBT_NODE_SET_QLEN, 204184610Salfred "set_qlen", 205184610Salfred &ng_ubt_node_qlen_type, 206184610Salfred NULL 207184610Salfred }, 208184610Salfred { 209184610Salfred NGM_UBT_COOKIE, 210184610Salfred NGM_UBT_NODE_GET_QLEN, 211184610Salfred "get_qlen", 212184610Salfred &ng_ubt_node_qlen_type, 213184610Salfred &ng_ubt_node_qlen_type 214184610Salfred }, 215184610Salfred { 216184610Salfred NGM_UBT_COOKIE, 217184610Salfred NGM_UBT_NODE_GET_STAT, 218184610Salfred "get_stat", 219184610Salfred NULL, 220184610Salfred &ng_ubt_node_stat_type 221184610Salfred }, 222184610Salfred { 223184610Salfred NGM_UBT_COOKIE, 224184610Salfred NGM_UBT_NODE_RESET_STAT, 225184610Salfred "reset_stat", 226184610Salfred NULL, 227184610Salfred NULL 228184610Salfred }, 229187494Semax { 0, } 230184610Salfred}; 231184610Salfred 232184610Salfred/* Netgraph node type */ 233187494Semaxstatic struct ng_type typestruct = 234187494Semax{ 235187494Semax .version = NG_ABI_VERSION, 236187494Semax .name = NG_UBT_NODE_TYPE, 237187494Semax .constructor = ng_ubt_constructor, 238187494Semax .rcvmsg = ng_ubt_rcvmsg, 239187494Semax .shutdown = ng_ubt_shutdown, 240187494Semax .newhook = ng_ubt_newhook, 241187494Semax .connect = ng_ubt_connect, 242187494Semax .rcvdata = ng_ubt_rcvdata, 243187494Semax .disconnect = ng_ubt_disconnect, 244187494Semax .cmdlist = ng_ubt_cmdlist 245184610Salfred}; 246184610Salfred 247187494Semax/**************************************************************************** 248187494Semax **************************************************************************** 249187494Semax ** USB specific 250187494Semax **************************************************************************** 251187494Semax ****************************************************************************/ 252187494Semax 253184610Salfred/* USB methods */ 254193045Sthompsastatic usb_callback_t ubt_ctrl_write_callback; 255193045Sthompsastatic usb_callback_t ubt_intr_read_callback; 256193045Sthompsastatic usb_callback_t ubt_bulk_read_callback; 257193045Sthompsastatic usb_callback_t ubt_bulk_write_callback; 258193045Sthompsastatic usb_callback_t ubt_isoc_read_callback; 259193045Sthompsastatic usb_callback_t ubt_isoc_write_callback; 260184610Salfred 261187741Semaxstatic int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **); 262192984Sthompsastatic int ubt_isoc_read_one_frame(struct usb_xfer *, int); 263184610Salfred 264187494Semax/* 265187494Semax * USB config 266187494Semax * 267187494Semax * The following desribes usb transfers that could be submitted on USB device. 268187494Semax * 269187494Semax * Interface 0 on the USB device must present the following endpoints 270187494Semax * 1) Interrupt endpoint to receive HCI events 271187494Semax * 2) Bulk IN endpoint to receive ACL data 272187494Semax * 3) Bulk OUT endpoint to send ACL data 273187494Semax * 274187494Semax * Interface 1 on the USB device must present the following endpoints 275187494Semax * 1) Isochronous IN endpoint to receive SCO data 276187494Semax * 2) Isochronous OUT endpoint to send SCO data 277187494Semax */ 278184610Salfred 279192984Sthompsastatic const struct usb_config ubt_config[UBT_N_TRANSFER] = 280187494Semax{ 281187494Semax /* 282187494Semax * Interface #0 283187494Semax */ 284184610Salfred 285187494Semax /* Outgoing bulk transfer - ACL packets */ 286187494Semax [UBT_IF_0_BULK_DT_WR] = { 287187494Semax .type = UE_BULK, 288187494Semax .endpoint = UE_ADDR_ANY, 289187494Semax .direction = UE_DIR_OUT, 290187741Semax .if_index = 0, 291190734Sthompsa .bufsize = UBT_BULK_WRITE_BUFFER_SIZE, 292190734Sthompsa .flags = { .pipe_bof = 1, .force_short_xfer = 1, }, 293190734Sthompsa .callback = &ubt_bulk_write_callback, 294184610Salfred }, 295187494Semax /* Incoming bulk transfer - ACL packets */ 296187494Semax [UBT_IF_0_BULK_DT_RD] = { 297187494Semax .type = UE_BULK, 298187494Semax .endpoint = UE_ADDR_ANY, 299187494Semax .direction = UE_DIR_IN, 300187741Semax .if_index = 0, 301190734Sthompsa .bufsize = UBT_BULK_READ_BUFFER_SIZE, 302190734Sthompsa .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 303190734Sthompsa .callback = &ubt_bulk_read_callback, 304184610Salfred }, 305187494Semax /* Incoming interrupt transfer - HCI events */ 306187494Semax [UBT_IF_0_INTR_DT_RD] = { 307187494Semax .type = UE_INTERRUPT, 308187494Semax .endpoint = UE_ADDR_ANY, 309187494Semax .direction = UE_DIR_IN, 310187741Semax .if_index = 0, 311190734Sthompsa .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 312190734Sthompsa .bufsize = UBT_INTR_BUFFER_SIZE, 313190734Sthompsa .callback = &ubt_intr_read_callback, 314184610Salfred }, 315187494Semax /* Outgoing control transfer - HCI commands */ 316187494Semax [UBT_IF_0_CTRL_DT_WR] = { 317187494Semax .type = UE_CONTROL, 318187494Semax .endpoint = 0x00, /* control pipe */ 319187494Semax .direction = UE_DIR_ANY, 320187741Semax .if_index = 0, 321190734Sthompsa .bufsize = UBT_CTRL_BUFFER_SIZE, 322190734Sthompsa .callback = &ubt_ctrl_write_callback, 323190734Sthompsa .timeout = 5000, /* 5 seconds */ 324184610Salfred }, 325184610Salfred 326187494Semax /* 327187494Semax * Interface #1 328187494Semax */ 329184610Salfred 330187494Semax /* Incoming isochronous transfer #1 - SCO packets */ 331187494Semax [UBT_IF_1_ISOC_DT_RD1] = { 332187494Semax .type = UE_ISOCHRONOUS, 333187494Semax .endpoint = UE_ADDR_ANY, 334187494Semax .direction = UE_DIR_IN, 335187741Semax .if_index = 1, 336190734Sthompsa .bufsize = 0, /* use "wMaxPacketSize * frames" */ 337190734Sthompsa .frames = UBT_ISOC_NFRAMES, 338190734Sthompsa .flags = { .short_xfer_ok = 1, }, 339190734Sthompsa .callback = &ubt_isoc_read_callback, 340184610Salfred }, 341187494Semax /* Incoming isochronous transfer #2 - SCO packets */ 342187494Semax [UBT_IF_1_ISOC_DT_RD2] = { 343187494Semax .type = UE_ISOCHRONOUS, 344187494Semax .endpoint = UE_ADDR_ANY, 345187494Semax .direction = UE_DIR_IN, 346187741Semax .if_index = 1, 347190734Sthompsa .bufsize = 0, /* use "wMaxPacketSize * frames" */ 348190734Sthompsa .frames = UBT_ISOC_NFRAMES, 349190734Sthompsa .flags = { .short_xfer_ok = 1, }, 350190734Sthompsa .callback = &ubt_isoc_read_callback, 351184610Salfred }, 352187494Semax /* Outgoing isochronous transfer #1 - SCO packets */ 353187494Semax [UBT_IF_1_ISOC_DT_WR1] = { 354187494Semax .type = UE_ISOCHRONOUS, 355187494Semax .endpoint = UE_ADDR_ANY, 356187494Semax .direction = UE_DIR_OUT, 357187741Semax .if_index = 1, 358190734Sthompsa .bufsize = 0, /* use "wMaxPacketSize * frames" */ 359190734Sthompsa .frames = UBT_ISOC_NFRAMES, 360190734Sthompsa .flags = { .short_xfer_ok = 1, }, 361190734Sthompsa .callback = &ubt_isoc_write_callback, 362184610Salfred }, 363187494Semax /* Outgoing isochronous transfer #2 - SCO packets */ 364187494Semax [UBT_IF_1_ISOC_DT_WR2] = { 365187494Semax .type = UE_ISOCHRONOUS, 366187494Semax .endpoint = UE_ADDR_ANY, 367187494Semax .direction = UE_DIR_OUT, 368187741Semax .if_index = 1, 369190734Sthompsa .bufsize = 0, /* use "wMaxPacketSize * frames" */ 370190734Sthompsa .frames = UBT_ISOC_NFRAMES, 371190734Sthompsa .flags = { .short_xfer_ok = 1, }, 372190734Sthompsa .callback = &ubt_isoc_write_callback, 373184610Salfred }, 374184610Salfred}; 375184610Salfred 376184610Salfred/* 377184610Salfred * If for some reason device should not be attached then put 378184610Salfred * VendorID/ProductID pair into the list below. The format is 379184610Salfred * as follows: 380184610Salfred * 381187494Semax * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, 382184610Salfred * 383184610Salfred * where VENDOR_ID and PRODUCT_ID are hex numbers. 384184610Salfred */ 385187741Semax 386192984Sthompsastatic const struct usb_device_id ubt_ignore_devs[] = 387187741Semax{ 388184610Salfred /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */ 389187494Semax { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) }, 390184610Salfred}; 391184610Salfred 392184610Salfred/* List of supported bluetooth devices */ 393192984Sthompsastatic const struct usb_device_id ubt_devs[] = 394187741Semax{ 395187494Semax /* Generic Bluetooth class devices */ 396187494Semax { USB_IFACE_CLASS(UDCLASS_WIRELESS), 397187494Semax USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 398187494Semax USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 399184610Salfred 400184610Salfred /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ 401187494Semax { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) }, 402184610Salfred}; 403184610Salfred 404184610Salfred/* 405187494Semax * Probe for a USB Bluetooth device. 406187494Semax * USB context. 407184610Salfred */ 408184610Salfred 409184610Salfredstatic int 410184610Salfredubt_probe(device_t dev) 411184610Salfred{ 412192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 413184610Salfred 414192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) 415184610Salfred return (ENXIO); 416187494Semax 417187494Semax if (uaa->info.bIfaceIndex != 0) 418184610Salfred return (ENXIO); 419187494Semax 420187865Semax if (uaa->use_generic == 0) 421187865Semax return (ENXIO); 422187865Semax 423194228Sthompsa if (usbd_lookup_id_by_uaa(ubt_ignore_devs, 424187494Semax sizeof(ubt_ignore_devs), uaa) == 0) 425184610Salfred return (ENXIO); 426187494Semax 427194228Sthompsa return (usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa)); 428187494Semax} /* ubt_probe */ 429184610Salfred 430184610Salfred/* 431187494Semax * Attach the device. 432187494Semax * USB context. 433184610Salfred */ 434184610Salfred 435184610Salfredstatic int 436184610Salfredubt_attach(device_t dev) 437184610Salfred{ 438192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 439187494Semax struct ubt_softc *sc = device_get_softc(dev); 440192984Sthompsa struct usb_endpoint_descriptor *ed; 441192984Sthompsa struct usb_interface_descriptor *id; 442187494Semax uint16_t wMaxPacketSize; 443187741Semax uint8_t alt_index, i, j; 444187741Semax uint8_t iface_index[2] = { 0, 1 }; 445184610Salfred 446194228Sthompsa device_set_usb_desc(dev); 447184610Salfred 448187741Semax sc->sc_dev = dev; 449187741Semax sc->sc_debug = NG_UBT_WARN_LEVEL; 450184610Salfred 451187494Semax /* 452187494Semax * Create Netgraph node 453187494Semax */ 454187494Semax 455187494Semax if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { 456187741Semax UBT_ALERT(sc, "could not create Netgraph node\n"); 457187494Semax return (ENXIO); 458187494Semax } 459187494Semax 460187494Semax /* Name Netgraph node */ 461187741Semax if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { 462187741Semax UBT_ALERT(sc, "could not name Netgraph node\n"); 463187494Semax NG_NODE_UNREF(sc->sc_node); 464187494Semax return (ENXIO); 465187494Semax } 466187494Semax NG_NODE_SET_PRIVATE(sc->sc_node, sc); 467187494Semax NG_NODE_FORCE_WRITER(sc->sc_node); 468187494Semax 469184610Salfred /* 470184610Salfred * Initialize device softc structure 471184610Salfred */ 472184610Salfred 473187494Semax /* initialize locks */ 474187741Semax mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); 475187741Semax mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); 476187494Semax 477187494Semax /* initialize packet queues */ 478184610Salfred NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); 479184610Salfred NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); 480187494Semax NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); 481184610Salfred 482187494Semax /* initialize glue task */ 483187741Semax TASK_INIT(&sc->sc_task, 0, ubt_task, sc); 484184610Salfred 485184610Salfred /* 486184610Salfred * Configure Bluetooth USB device. Discover all required USB 487184610Salfred * interfaces and endpoints. 488184610Salfred * 489184610Salfred * USB device must present two interfaces: 490184610Salfred * 1) Interface 0 that has 3 endpoints 491184610Salfred * 1) Interrupt endpoint to receive HCI events 492184610Salfred * 2) Bulk IN endpoint to receive ACL data 493184610Salfred * 3) Bulk OUT endpoint to send ACL data 494184610Salfred * 495184610Salfred * 2) Interface 1 then has 2 endpoints 496184610Salfred * 1) Isochronous IN endpoint to receive SCO data 497184610Salfred * 2) Isochronous OUT endpoint to send SCO data 498184610Salfred * 499184610Salfred * Interface 1 (with isochronous endpoints) has several alternate 500184610Salfred * configurations with different packet size. 501184610Salfred */ 502184610Salfred 503184610Salfred /* 504187741Semax * For interface #1 search alternate settings, and find 505187741Semax * the descriptor with the largest wMaxPacketSize 506184610Salfred */ 507184610Salfred 508184610Salfred wMaxPacketSize = 0; 509187494Semax alt_index = 0; 510184610Salfred i = 0; 511184610Salfred j = 0; 512190728Sthompsa ed = NULL; 513187494Semax 514190728Sthompsa /* 515190728Sthompsa * Search through all the descriptors looking for the largest 516190728Sthompsa * packet size: 517190728Sthompsa */ 518194228Sthompsa while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach( 519194228Sthompsa usbd_get_config_descriptor(uaa->device), 520192984Sthompsa (struct usb_descriptor *)ed))) { 521184610Salfred 522190728Sthompsa if ((ed->bDescriptorType == UDESC_INTERFACE) && 523190728Sthompsa (ed->bLength >= sizeof(*id))) { 524192984Sthompsa id = (struct usb_interface_descriptor *)ed; 525190728Sthompsa i = id->bInterfaceNumber; 526190728Sthompsa j = id->bAlternateSetting; 527184610Salfred } 528187494Semax 529190728Sthompsa if ((ed->bDescriptorType == UDESC_ENDPOINT) && 530190728Sthompsa (ed->bLength >= sizeof(*ed)) && 531190728Sthompsa (i == 1)) { 532190728Sthompsa uint16_t temp; 533190728Sthompsa 534190728Sthompsa temp = UGETW(ed->wMaxPacketSize); 535190728Sthompsa if (temp > wMaxPacketSize) { 536190728Sthompsa wMaxPacketSize = temp; 537190728Sthompsa alt_index = j; 538190728Sthompsa } 539184610Salfred } 540184610Salfred } 541184610Salfred 542187741Semax /* Set alt configuration on interface #1 only if we found it */ 543187494Semax if (wMaxPacketSize > 0 && 544194228Sthompsa usbd_set_alt_interface_index(uaa->device, 1, alt_index)) { 545187741Semax UBT_ALERT(sc, "could not set alternate setting %d " \ 546187494Semax "for interface 1!\n", alt_index); 547184610Salfred goto detach; 548184610Salfred } 549184610Salfred 550187741Semax /* Setup transfers for both interfaces */ 551194228Sthompsa if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, 552187741Semax ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { 553187741Semax UBT_ALERT(sc, "could not allocate transfers\n"); 554184610Salfred goto detach; 555184610Salfred } 556184610Salfred 557187494Semax /* Claim all interfaces on the device */ 558194228Sthompsa for (i = 1; usbd_get_iface(uaa->device, i) != NULL; i ++) 559194228Sthompsa usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 560184610Salfred 561187494Semax return (0); /* success */ 562184610Salfred 563184610Salfreddetach: 564184610Salfred ubt_detach(dev); 565184610Salfred 566184610Salfred return (ENXIO); 567187494Semax} /* ubt_attach */ 568184610Salfred 569184610Salfred/* 570187494Semax * Detach the device. 571187494Semax * USB context. 572184610Salfred */ 573184610Salfred 574184610Salfredint 575184610Salfredubt_detach(device_t dev) 576184610Salfred{ 577187494Semax struct ubt_softc *sc = device_get_softc(dev); 578187494Semax node_p node = sc->sc_node; 579184610Salfred 580187494Semax /* Destroy Netgraph node */ 581187494Semax if (node != NULL) { 582187494Semax sc->sc_node = NULL; 583187494Semax NG_NODE_REALLY_DIE(node); 584187494Semax ng_rmnode_self(node); 585184610Salfred } 586184610Salfred 587187741Semax /* Make sure ubt_task in gone */ 588187741Semax taskqueue_drain(taskqueue_swi, &sc->sc_task); 589187741Semax 590187494Semax /* Free USB transfers, if any */ 591194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); 592184610Salfred 593187494Semax /* Destroy queues */ 594187741Semax UBT_NG_LOCK(sc); 595184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); 596184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); 597184610Salfred NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); 598187741Semax UBT_NG_UNLOCK(sc); 599184610Salfred 600187741Semax mtx_destroy(&sc->sc_if_mtx); 601187741Semax mtx_destroy(&sc->sc_ng_mtx); 602187494Semax 603184610Salfred return (0); 604187494Semax} /* ubt_detach */ 605184610Salfred 606187494Semax/* 607187494Semax * Called when outgoing control request (HCI command) has completed, i.e. 608187494Semax * HCI command was sent to the device. 609187494Semax * USB context. 610187494Semax */ 611187494Semax 612184610Salfredstatic void 613194677Sthompsaubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 614184610Salfred{ 615194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 616192984Sthompsa struct usb_device_request req; 617187494Semax struct mbuf *m; 618194677Sthompsa struct usb_page_cache *pc; 619194677Sthompsa int actlen; 620184610Salfred 621194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 622194677Sthompsa 623184610Salfred switch (USB_GET_STATE(xfer)) { 624184610Salfred case USB_ST_TRANSFERRED: 625194677Sthompsa UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen); 626194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 627187741Semax UBT_STAT_PCKTS_SENT(sc); 628187494Semax /* FALLTHROUGH */ 629184610Salfred 630184610Salfred case USB_ST_SETUP: 631187494Semaxsend_next: 632187494Semax /* Get next command mbuf, if any */ 633187741Semax UBT_NG_LOCK(sc); 634184610Salfred NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 635187741Semax UBT_NG_UNLOCK(sc); 636184610Salfred 637184610Salfred if (m == NULL) { 638187494Semax UBT_INFO(sc, "HCI command queue is empty\n"); 639187741Semax break; /* transfer complete */ 640184610Salfred } 641184610Salfred 642187494Semax /* Initialize a USB control request and then schedule it */ 643184610Salfred bzero(&req, sizeof(req)); 644184610Salfred req.bmRequestType = UBT_HCI_REQUEST; 645184610Salfred USETW(req.wLength, m->m_pkthdr.len); 646184610Salfred 647187494Semax UBT_INFO(sc, "Sending control request, " \ 648187494Semax "bmRequestType=0x%02x, wLength=%d\n", 649187494Semax req.bmRequestType, UGETW(req.wLength)); 650184610Salfred 651194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 652194677Sthompsa usbd_copy_in(pc, 0, &req, sizeof(req)); 653194677Sthompsa pc = usbd_xfer_get_frame(xfer, 1); 654194677Sthompsa usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 655184610Salfred 656194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 657194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len); 658194677Sthompsa usbd_xfer_set_frames(xfer, 2); 659184610Salfred 660184610Salfred NG_FREE_M(m); 661184610Salfred 662194228Sthompsa usbd_transfer_submit(xfer); 663187494Semax break; 664184610Salfred 665187494Semax default: /* Error */ 666194677Sthompsa if (error != USB_ERR_CANCELLED) { 667187494Semax UBT_WARN(sc, "control transfer failed: %s\n", 668194677Sthompsa usbd_errstr(error)); 669187494Semax 670187494Semax UBT_STAT_OERROR(sc); 671187494Semax goto send_next; 672184610Salfred } 673187494Semax 674187741Semax /* transfer cancelled */ 675187494Semax break; 676184610Salfred } 677187494Semax} /* ubt_ctrl_write_callback */ 678184610Salfred 679187494Semax/* 680187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e. 681187494Semax * HCI event was received from the device. 682187494Semax * USB context. 683187494Semax */ 684187494Semax 685184610Salfredstatic void 686194677Sthompsaubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 687184610Salfred{ 688194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 689187494Semax struct mbuf *m; 690187494Semax ng_hci_event_pkt_t *hdr; 691194677Sthompsa struct usb_page_cache *pc; 692194677Sthompsa int actlen; 693184610Salfred 694194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 695194677Sthompsa 696187494Semax m = NULL; 697187494Semax 698184610Salfred switch (USB_GET_STATE(xfer)) { 699184610Salfred case USB_ST_TRANSFERRED: 700187494Semax /* Allocate a new mbuf */ 701184610Salfred MGETHDR(m, M_DONTWAIT, MT_DATA); 702184610Salfred if (m == NULL) { 703187494Semax UBT_STAT_IERROR(sc); 704187494Semax goto submit_next; 705184610Salfred } 706187494Semax 707184610Salfred MCLGET(m, M_DONTWAIT); 708184610Salfred if (!(m->m_flags & M_EXT)) { 709187494Semax UBT_STAT_IERROR(sc); 710187494Semax goto submit_next; 711184610Salfred } 712187494Semax 713187494Semax /* Add HCI packet type */ 714187494Semax *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; 715187494Semax m->m_pkthdr.len = m->m_len = 1; 716187494Semax 717194677Sthompsa if (actlen > MCLBYTES - 1) 718194677Sthompsa actlen = MCLBYTES - 1; 719187494Semax 720194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 721194677Sthompsa usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 722194677Sthompsa m->m_pkthdr.len += actlen; 723194677Sthompsa m->m_len += actlen; 724187494Semax 725187494Semax UBT_INFO(sc, "got %d bytes from interrupt pipe\n", 726194677Sthompsa actlen); 727187494Semax 728187494Semax /* Validate packet and send it up the stack */ 729187494Semax if (m->m_pkthdr.len < sizeof(*hdr)) { 730187494Semax UBT_INFO(sc, "HCI event packet is too short\n"); 731187494Semax 732187494Semax UBT_STAT_IERROR(sc); 733187494Semax goto submit_next; 734184610Salfred } 735184610Salfred 736187494Semax hdr = mtod(m, ng_hci_event_pkt_t *); 737187494Semax if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { 738187494Semax UBT_ERR(sc, "Invalid HCI event packet size, " \ 739187494Semax "length=%d, pktlen=%d\n", 740187494Semax hdr->length, m->m_pkthdr.len); 741184610Salfred 742187494Semax UBT_STAT_IERROR(sc); 743187494Semax goto submit_next; 744184610Salfred } 745184610Salfred 746187494Semax UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ 747187494Semax "length=%d\n", m->m_pkthdr.len, hdr->length); 748184610Salfred 749187494Semax UBT_STAT_PCKTS_RECV(sc); 750187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 751184610Salfred 752187741Semax ubt_fwd_mbuf_up(sc, &m); 753187494Semax /* m == NULL at this point */ 754187494Semax /* FALLTHROUGH */ 755184610Salfred 756184610Salfred case USB_ST_SETUP: 757187494Semaxsubmit_next: 758187494Semax NG_FREE_M(m); /* checks for m != NULL */ 759184610Salfred 760194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 761194228Sthompsa usbd_transfer_submit(xfer); 762187494Semax break; 763184610Salfred 764187494Semax default: /* Error */ 765194677Sthompsa if (error != USB_ERR_CANCELLED) { 766187494Semax UBT_WARN(sc, "interrupt transfer failed: %s\n", 767194677Sthompsa usbd_errstr(error)); 768184610Salfred 769187494Semax /* Try to clear stall first */ 770194677Sthompsa usbd_xfer_set_stall(xfer); 771187741Semax goto submit_next; 772187741Semax } 773187741Semax /* transfer cancelled */ 774187494Semax break; 775184610Salfred } 776187494Semax} /* ubt_intr_read_callback */ 777184610Salfred 778187494Semax/* 779187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e. 780187494Semax * ACL packet was received from the device. 781187494Semax * USB context. 782187494Semax */ 783187494Semax 784184610Salfredstatic void 785194677Sthompsaubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 786184610Salfred{ 787194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 788187494Semax struct mbuf *m; 789187494Semax ng_hci_acldata_pkt_t *hdr; 790194677Sthompsa struct usb_page_cache *pc; 791187494Semax uint16_t len; 792194677Sthompsa int actlen; 793184610Salfred 794194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 795194677Sthompsa 796187494Semax m = NULL; 797184610Salfred 798184610Salfred switch (USB_GET_STATE(xfer)) { 799184610Salfred case USB_ST_TRANSFERRED: 800187494Semax /* Allocate new mbuf */ 801184610Salfred MGETHDR(m, M_DONTWAIT, MT_DATA); 802184610Salfred if (m == NULL) { 803187494Semax UBT_STAT_IERROR(sc); 804187494Semax goto submit_next; 805184610Salfred } 806187494Semax 807184610Salfred MCLGET(m, M_DONTWAIT); 808184610Salfred if (!(m->m_flags & M_EXT)) { 809187494Semax UBT_STAT_IERROR(sc); 810187494Semax goto submit_next; 811184610Salfred } 812184610Salfred 813187494Semax /* Add HCI packet type */ 814187494Semax *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; 815187494Semax m->m_pkthdr.len = m->m_len = 1; 816184610Salfred 817194677Sthompsa if (actlen > MCLBYTES - 1) 818194677Sthompsa actlen = MCLBYTES - 1; 819184610Salfred 820194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 821194677Sthompsa usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen); 822194677Sthompsa m->m_pkthdr.len += actlen; 823194677Sthompsa m->m_len += actlen; 824184610Salfred 825187494Semax UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", 826194677Sthompsa actlen); 827184610Salfred 828187494Semax /* Validate packet and send it up the stack */ 829187494Semax if (m->m_pkthdr.len < sizeof(*hdr)) { 830187494Semax UBT_INFO(sc, "HCI ACL packet is too short\n"); 831184610Salfred 832187494Semax UBT_STAT_IERROR(sc); 833187494Semax goto submit_next; 834184610Salfred } 835184610Salfred 836187494Semax hdr = mtod(m, ng_hci_acldata_pkt_t *); 837187494Semax len = le16toh(hdr->length); 838187494Semax if (len != (m->m_pkthdr.len - sizeof(*hdr))) { 839187494Semax UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ 840187494Semax "pktlen=%d\n", len, m->m_pkthdr.len); 841184610Salfred 842187494Semax UBT_STAT_IERROR(sc); 843187494Semax goto submit_next; 844184610Salfred } 845184610Salfred 846187494Semax UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ 847187494Semax "length=%d\n", m->m_pkthdr.len, len); 848184610Salfred 849187494Semax UBT_STAT_PCKTS_RECV(sc); 850187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 851184610Salfred 852187741Semax ubt_fwd_mbuf_up(sc, &m); 853187494Semax /* m == NULL at this point */ 854187494Semax /* FALLTHOUGH */ 855184610Salfred 856187494Semax case USB_ST_SETUP: 857187494Semaxsubmit_next: 858187494Semax NG_FREE_M(m); /* checks for m != NULL */ 859184610Salfred 860194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 861194228Sthompsa usbd_transfer_submit(xfer); 862187494Semax break; 863184610Salfred 864187494Semax default: /* Error */ 865194677Sthompsa if (error != USB_ERR_CANCELLED) { 866187494Semax UBT_WARN(sc, "bulk-in transfer failed: %s\n", 867194677Sthompsa usbd_errstr(error)); 868184610Salfred 869187494Semax /* Try to clear stall first */ 870194677Sthompsa usbd_xfer_set_stall(xfer); 871187741Semax goto submit_next; 872187741Semax } 873187741Semax /* transfer cancelled */ 874187494Semax break; 875187494Semax } 876187494Semax} /* ubt_bulk_read_callback */ 877184610Salfred 878187494Semax/* 879187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e. 880187494Semax * ACL packet was sent to the device. 881187494Semax * USB context. 882187494Semax */ 883184610Salfred 884184610Salfredstatic void 885194677Sthompsaubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 886184610Salfred{ 887194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 888187494Semax struct mbuf *m; 889194677Sthompsa struct usb_page_cache *pc; 890194677Sthompsa int actlen; 891184610Salfred 892194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 893194677Sthompsa 894184610Salfred switch (USB_GET_STATE(xfer)) { 895184610Salfred case USB_ST_TRANSFERRED: 896194677Sthompsa UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen); 897194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 898187741Semax UBT_STAT_PCKTS_SENT(sc); 899187494Semax /* FALLTHROUGH */ 900184610Salfred 901187494Semax case USB_ST_SETUP: 902187741Semaxsend_next: 903187494Semax /* Get next mbuf, if any */ 904187741Semax UBT_NG_LOCK(sc); 905184610Salfred NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); 906187741Semax UBT_NG_UNLOCK(sc); 907184610Salfred 908184610Salfred if (m == NULL) { 909187494Semax UBT_INFO(sc, "ACL data queue is empty\n"); 910187741Semax break; /* transfer completed */ 911184610Salfred } 912187494Semax 913184610Salfred /* 914187494Semax * Copy ACL data frame back to a linear USB transfer buffer 915187494Semax * and schedule transfer 916184610Salfred */ 917184610Salfred 918194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 919194677Sthompsa usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 920194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 921184610Salfred 922187494Semax UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", 923187494Semax m->m_pkthdr.len); 924184610Salfred 925184610Salfred NG_FREE_M(m); 926184610Salfred 927194228Sthompsa usbd_transfer_submit(xfer); 928187494Semax break; 929184610Salfred 930187494Semax default: /* Error */ 931194677Sthompsa if (error != USB_ERR_CANCELLED) { 932187494Semax UBT_WARN(sc, "bulk-out transfer failed: %s\n", 933194677Sthompsa usbd_errstr(error)); 934184610Salfred 935187494Semax UBT_STAT_OERROR(sc); 936184610Salfred 937184610Salfred /* try to clear stall first */ 938194677Sthompsa usbd_xfer_set_stall(xfer); 939187741Semax goto send_next; 940187741Semax } 941187741Semax /* transfer cancelled */ 942187494Semax break; 943184610Salfred } 944187494Semax} /* ubt_bulk_write_callback */ 945184610Salfred 946187494Semax/* 947187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e. 948187494Semax * SCO packet was received from the device. 949187494Semax * USB context. 950187494Semax */ 951187494Semax 952184610Salfredstatic void 953194677Sthompsaubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 954184610Salfred{ 955194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 956187494Semax int n; 957194677Sthompsa int actlen, nframes; 958184610Salfred 959194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 960194677Sthompsa 961187494Semax switch (USB_GET_STATE(xfer)) { 962187494Semax case USB_ST_TRANSFERRED: 963194677Sthompsa for (n = 0; n < nframes; n ++) 964187494Semax if (ubt_isoc_read_one_frame(xfer, n) < 0) 965187494Semax break; 966187494Semax /* FALLTHROUGH */ 967184610Salfred 968187494Semax case USB_ST_SETUP: 969187494Semaxread_next: 970194677Sthompsa for (n = 0; n < nframes; n ++) 971194677Sthompsa usbd_xfer_set_frame_len(xfer, n, 972194677Sthompsa usbd_xfer_max_framelen(xfer)); 973184610Salfred 974194228Sthompsa usbd_transfer_submit(xfer); 975187494Semax break; 976184610Salfred 977187494Semax default: /* Error */ 978194677Sthompsa if (error != USB_ERR_CANCELLED) { 979187494Semax UBT_STAT_IERROR(sc); 980187494Semax goto read_next; 981187494Semax } 982184610Salfred 983187741Semax /* transfer cancelled */ 984187494Semax break; 985187494Semax } 986187494Semax} /* ubt_isoc_read_callback */ 987184610Salfred 988187494Semax/* 989187494Semax * Helper function. Called from ubt_isoc_read_callback() to read 990187494Semax * SCO data from one frame. 991187494Semax * USB context. 992187494Semax */ 993184610Salfred 994187494Semaxstatic int 995192984Sthompsaubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no) 996187494Semax{ 997194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 998194677Sthompsa struct usb_page_cache *pc; 999187494Semax struct mbuf *m; 1000194677Sthompsa int len, want, got, total; 1001184610Salfred 1002187494Semax /* Get existing SCO reassembly buffer */ 1003194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1004187494Semax m = sc->sc_isoc_in_buffer; 1005194682Sthompsa total = usbd_xfer_frame_len(xfer, frame_no); 1006184610Salfred 1007187494Semax /* While we have data in the frame */ 1008194677Sthompsa while (total > 0) { 1009187494Semax if (m == NULL) { 1010187494Semax /* Start new reassembly buffer */ 1011187494Semax MGETHDR(m, M_DONTWAIT, MT_DATA); 1012187494Semax if (m == NULL) { 1013187494Semax UBT_STAT_IERROR(sc); 1014187494Semax return (-1); /* XXX out of sync! */ 1015187494Semax } 1016184610Salfred 1017187494Semax MCLGET(m, M_DONTWAIT); 1018187494Semax if (!(m->m_flags & M_EXT)) { 1019187494Semax UBT_STAT_IERROR(sc); 1020187494Semax NG_FREE_M(m); 1021187494Semax return (-1); /* XXX out of sync! */ 1022184610Salfred } 1023184610Salfred 1024187494Semax /* Expect SCO header */ 1025187494Semax *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; 1026187494Semax m->m_pkthdr.len = m->m_len = got = 1; 1027187494Semax want = sizeof(ng_hci_scodata_pkt_t); 1028187494Semax } else { 1029187494Semax /* 1030187494Semax * Check if we have SCO header and if so 1031187494Semax * adjust amount of data we want 1032187494Semax */ 1033187494Semax got = m->m_pkthdr.len; 1034187494Semax want = sizeof(ng_hci_scodata_pkt_t); 1035184610Salfred 1036187494Semax if (got >= want) 1037187494Semax want += mtod(m, ng_hci_scodata_pkt_t *)->length; 1038184610Salfred } 1039184610Salfred 1040187494Semax /* Append frame data to the SCO reassembly buffer */ 1041194677Sthompsa len = total; 1042187494Semax if (got + len > want) 1043187494Semax len = want - got; 1044184610Salfred 1045194677Sthompsa usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer), 1046187494Semax mtod(m, uint8_t *) + m->m_pkthdr.len, len); 1047184610Salfred 1048187494Semax m->m_pkthdr.len += len; 1049187494Semax m->m_len += len; 1050194677Sthompsa total -= len; 1051184610Salfred 1052187494Semax /* Check if we got everything we wanted, if not - continue */ 1053187494Semax if (got != want) 1054187494Semax continue; 1055184610Salfred 1056187494Semax /* If we got here then we got complete SCO frame */ 1057187494Semax UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ 1058187494Semax "length=%d\n", m->m_pkthdr.len, 1059187494Semax mtod(m, ng_hci_scodata_pkt_t *)->length); 1060184610Salfred 1061187494Semax UBT_STAT_PCKTS_RECV(sc); 1062187494Semax UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 1063184610Salfred 1064187741Semax ubt_fwd_mbuf_up(sc, &m); 1065187494Semax /* m == NULL at this point */ 1066187494Semax } 1067184610Salfred 1068187494Semax /* Put SCO reassembly buffer back */ 1069187494Semax sc->sc_isoc_in_buffer = m; 1070184610Salfred 1071187494Semax return (0); 1072187494Semax} /* ubt_isoc_read_one_frame */ 1073184610Salfred 1074187494Semax/* 1075187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e. 1076187494Semax * SCO packet was sent to the device. 1077187494Semax * USB context. 1078187494Semax */ 1079184610Salfred 1080184610Salfredstatic void 1081194677Sthompsaubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 1082184610Salfred{ 1083194677Sthompsa struct ubt_softc *sc = usbd_xfer_softc(xfer); 1084194677Sthompsa struct usb_page_cache *pc; 1085187494Semax struct mbuf *m; 1086187494Semax int n, space, offset; 1087194677Sthompsa int actlen, nframes; 1088184610Salfred 1089194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1090194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 1091194677Sthompsa 1092184610Salfred switch (USB_GET_STATE(xfer)) { 1093184610Salfred case USB_ST_TRANSFERRED: 1094194677Sthompsa UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen); 1095194677Sthompsa UBT_STAT_BYTES_SENT(sc, actlen); 1096187741Semax UBT_STAT_PCKTS_SENT(sc); 1097187494Semax /* FALLTHROUGH */ 1098184610Salfred 1099184610Salfred case USB_ST_SETUP: 1100187494Semaxsend_next: 1101184610Salfred offset = 0; 1102194677Sthompsa space = usbd_xfer_max_framelen(xfer) * nframes; 1103187494Semax m = NULL; 1104184610Salfred 1105187494Semax while (space > 0) { 1106187494Semax if (m == NULL) { 1107187741Semax UBT_NG_LOCK(sc); 1108187494Semax NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); 1109187741Semax UBT_NG_UNLOCK(sc); 1110184610Salfred 1111187494Semax if (m == NULL) 1112187494Semax break; 1113187494Semax } 1114184610Salfred 1115187494Semax n = min(space, m->m_pkthdr.len); 1116187494Semax if (n > 0) { 1117194677Sthompsa usbd_m_copy_in(pc, offset, m,0, n); 1118187494Semax m_adj(m, n); 1119184610Salfred 1120187494Semax offset += n; 1121187494Semax space -= n; 1122187494Semax } 1123184610Salfred 1124187494Semax if (m->m_pkthdr.len == 0) 1125187494Semax NG_FREE_M(m); /* sets m = NULL */ 1126187494Semax } 1127184610Salfred 1128187494Semax /* Put whatever is left from mbuf back on queue */ 1129187494Semax if (m != NULL) { 1130187741Semax UBT_NG_LOCK(sc); 1131187494Semax NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); 1132187741Semax UBT_NG_UNLOCK(sc); 1133184610Salfred } 1134184610Salfred 1135187494Semax /* 1136187494Semax * Calculate sizes for isoc frames. 1137187494Semax * Note that offset could be 0 at this point (i.e. we have 1138187494Semax * nothing to send). That is fine, as we have isoc. transfers 1139187494Semax * going in both directions all the time. In this case it 1140187494Semax * would be just empty isoc. transfer. 1141187494Semax */ 1142187494Semax 1143194677Sthompsa for (n = 0; n < nframes; n ++) { 1144194677Sthompsa usbd_xfer_set_frame_len(xfer, n, 1145194677Sthompsa min(offset, usbd_xfer_max_framelen(xfer))); 1146194682Sthompsa offset -= usbd_xfer_frame_len(xfer, n); 1147187494Semax } 1148187494Semax 1149194228Sthompsa usbd_transfer_submit(xfer); 1150187494Semax break; 1151184610Salfred 1152187494Semax default: /* Error */ 1153194677Sthompsa if (error != USB_ERR_CANCELLED) { 1154187494Semax UBT_STAT_OERROR(sc); 1155187494Semax goto send_next; 1156184610Salfred } 1157187494Semax 1158187741Semax /* transfer cancelled */ 1159187494Semax break; 1160184610Salfred } 1161184610Salfred} 1162184610Salfred 1163187741Semax/* 1164187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack). 1165187741Semax * Modifies value of the mbuf pointer (sets it to NULL). 1166187741Semax * Save to call from any context. 1167187741Semax */ 1168187741Semax 1169187741Semaxstatic int 1170187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) 1171187741Semax{ 1172187741Semax hook_p hook; 1173187741Semax int error; 1174187741Semax 1175187741Semax /* 1176187741Semax * Close the race with Netgraph hook newhook/disconnect methods. 1177187741Semax * Save the hook pointer atomically. Two cases are possible: 1178187741Semax * 1179187741Semax * 1) The hook pointer is NULL. It means disconnect method got 1180187741Semax * there first. In this case we are done. 1181187741Semax * 1182187741Semax * 2) The hook pointer is not NULL. It means that hook pointer 1183187741Semax * could be either in valid or invalid (i.e. in the process 1184187741Semax * of disconnect) state. In any case grab an extra reference 1185187741Semax * to protect the hook pointer. 1186187741Semax * 1187187741Semax * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as 1188187741Semax * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). 1189187741Semax */ 1190187741Semax 1191187741Semax UBT_NG_LOCK(sc); 1192187741Semax if ((hook = sc->sc_hook) != NULL) 1193187741Semax NG_HOOK_REF(hook); 1194187741Semax UBT_NG_UNLOCK(sc); 1195187741Semax 1196187741Semax if (hook == NULL) { 1197187741Semax NG_FREE_M(*m); 1198187741Semax return (ENETDOWN); 1199187741Semax } 1200187741Semax 1201187741Semax NG_SEND_DATA_ONLY(error, hook, *m); 1202187741Semax NG_HOOK_UNREF(hook); 1203187741Semax 1204187741Semax if (error != 0) 1205187741Semax UBT_STAT_IERROR(sc); 1206187741Semax 1207187741Semax return (error); 1208187741Semax} /* ubt_fwd_mbuf_up */ 1209187741Semax 1210184610Salfred/**************************************************************************** 1211184610Salfred **************************************************************************** 1212187494Semax ** Glue 1213184610Salfred **************************************************************************** 1214184610Salfred ****************************************************************************/ 1215184610Salfred 1216184610Salfred/* 1217187741Semax * Schedule glue task. Should be called with sc_ng_mtx held. 1218187494Semax * Netgraph context. 1219184610Salfred */ 1220184610Salfred 1221187741Semaxstatic void 1222187494Semaxubt_task_schedule(ubt_softc_p sc, int action) 1223184610Salfred{ 1224187741Semax mtx_assert(&sc->sc_ng_mtx, MA_OWNED); 1225184610Salfred 1226187741Semax /* 1227187741Semax * Try to handle corner case when "start all" and "stop all" 1228187741Semax * actions can both be set before task is executed. 1229187741Semax * 1230187741Semax * The rules are 1231187741Semax * 1232187741Semax * sc_task_flags action new sc_task_flags 1233187741Semax * ------------------------------------------------------ 1234187741Semax * 0 start start 1235187741Semax * 0 stop stop 1236187741Semax * start start start 1237187741Semax * start stop stop 1238187741Semax * stop start stop|start 1239187741Semax * stop stop stop 1240187741Semax * stop|start start stop|start 1241187741Semax * stop|start stop stop 1242187741Semax */ 1243187494Semax 1244187741Semax if (action != 0) { 1245187741Semax if ((action & UBT_FLAG_T_STOP_ALL) != 0) 1246187494Semax sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; 1247187494Semax 1248187494Semax sc->sc_task_flags |= action; 1249187494Semax } 1250187494Semax 1251187494Semax if (sc->sc_task_flags & UBT_FLAG_T_PENDING) 1252187741Semax return; 1253187494Semax 1254187494Semax if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { 1255187494Semax sc->sc_task_flags |= UBT_FLAG_T_PENDING; 1256187741Semax return; 1257187494Semax } 1258187494Semax 1259187494Semax /* XXX: i think this should never happen */ 1260187494Semax} /* ubt_task_schedule */ 1261187494Semax 1262184610Salfred/* 1263187494Semax * Glue task. Examines sc_task_flags and does things depending on it. 1264187494Semax * Taskqueue context. 1265184610Salfred */ 1266184610Salfred 1267187494Semaxstatic void 1268187494Semaxubt_task(void *context, int pending) 1269184610Salfred{ 1270187741Semax ubt_softc_p sc = context; 1271187741Semax int task_flags, i; 1272184610Salfred 1273187741Semax UBT_NG_LOCK(sc); 1274187494Semax task_flags = sc->sc_task_flags; 1275187494Semax sc->sc_task_flags = 0; 1276187741Semax UBT_NG_UNLOCK(sc); 1277187494Semax 1278187741Semax /* 1279187741Semax * Stop all USB transfers synchronously. 1280187741Semax * Stop interface #0 and #1 transfers at the same time and in the 1281194228Sthompsa * same loop. usbd_transfer_drain() will do appropriate locking. 1282187741Semax */ 1283187494Semax 1284187741Semax if (task_flags & UBT_FLAG_T_STOP_ALL) 1285187741Semax for (i = 0; i < UBT_N_TRANSFER; i ++) 1286194228Sthompsa usbd_transfer_drain(sc->sc_xfer[i]); 1287187494Semax 1288187741Semax /* Start incoming interrupt and bulk, and all isoc. USB transfers */ 1289187494Semax if (task_flags & UBT_FLAG_T_START_ALL) { 1290187494Semax /* 1291187494Semax * Interface #0 1292187494Semax */ 1293187494Semax 1294187741Semax mtx_lock(&sc->sc_if_mtx); 1295187741Semax 1296187494Semax ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); 1297187494Semax ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); 1298187494Semax 1299187494Semax /* 1300187494Semax * Interface #1 1301187494Semax * Start both read and write isoc. transfers by default. 1302187494Semax * Get them going all the time even if we have nothing 1303187494Semax * to send to avoid any delays. 1304187494Semax */ 1305187494Semax 1306187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); 1307187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); 1308187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); 1309187494Semax ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); 1310187741Semax 1311187741Semax mtx_unlock(&sc->sc_if_mtx); 1312184610Salfred } 1313187494Semax 1314187494Semax /* Start outgoing control transfer */ 1315187494Semax if (task_flags & UBT_FLAG_T_START_CTRL) { 1316187741Semax mtx_lock(&sc->sc_if_mtx); 1317187494Semax ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); 1318187741Semax mtx_unlock(&sc->sc_if_mtx); 1319184610Salfred } 1320184610Salfred 1321187494Semax /* Start outgoing bulk transfer */ 1322187494Semax if (task_flags & UBT_FLAG_T_START_BULK) { 1323187741Semax mtx_lock(&sc->sc_if_mtx); 1324187494Semax ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); 1325187741Semax mtx_unlock(&sc->sc_if_mtx); 1326184610Salfred } 1327187494Semax} /* ubt_task */ 1328187494Semax 1329187494Semax/**************************************************************************** 1330187494Semax **************************************************************************** 1331187494Semax ** Netgraph specific 1332187494Semax **************************************************************************** 1333187494Semax ****************************************************************************/ 1334184610Salfred 1335187494Semax/* 1336187494Semax * Netgraph node constructor. Do not allow to create node of this type. 1337187494Semax * Netgraph context. 1338187494Semax */ 1339184610Salfred 1340187494Semaxstatic int 1341187494Semaxng_ubt_constructor(node_p node) 1342187494Semax{ 1343187494Semax return (EINVAL); 1344187494Semax} /* ng_ubt_constructor */ 1345184610Salfred 1346184610Salfred/* 1347187494Semax * Netgraph node destructor. Destroy node only when device has been detached. 1348187494Semax * Netgraph context. 1349184610Salfred */ 1350184610Salfred 1351184610Salfredstatic int 1352187494Semaxng_ubt_shutdown(node_p node) 1353184610Salfred{ 1354187494Semax if (node->nd_flags & NGF_REALLY_DIE) { 1355187494Semax /* 1356187494Semax * We came here because the USB device is being 1357187494Semax * detached, so stop being persistant. 1358187494Semax */ 1359187494Semax NG_NODE_SET_PRIVATE(node, NULL); 1360187494Semax NG_NODE_UNREF(node); 1361187494Semax } else 1362187494Semax NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ 1363184610Salfred 1364187494Semax return (0); 1365187494Semax} /* ng_ubt_shutdown */ 1366184610Salfred 1367187494Semax/* 1368187494Semax * Create new hook. There can only be one. 1369187494Semax * Netgraph context. 1370187494Semax */ 1371184610Salfred 1372187494Semaxstatic int 1373187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name) 1374187494Semax{ 1375187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1376184610Salfred 1377187494Semax if (strcmp(name, NG_UBT_HOOK) != 0) 1378187494Semax return (EINVAL); 1379184610Salfred 1380187741Semax UBT_NG_LOCK(sc); 1381187741Semax if (sc->sc_hook != NULL) { 1382187741Semax UBT_NG_UNLOCK(sc); 1383187741Semax 1384187494Semax return (EISCONN); 1385187741Semax } 1386184610Salfred 1387187494Semax sc->sc_hook = hook; 1388187741Semax UBT_NG_UNLOCK(sc); 1389184610Salfred 1390187494Semax return (0); 1391187494Semax} /* ng_ubt_newhook */ 1392184610Salfred 1393187494Semax/* 1394187494Semax * Connect hook. Start incoming USB transfers. 1395187494Semax * Netgraph context. 1396187494Semax */ 1397184610Salfred 1398187494Semaxstatic int 1399187494Semaxng_ubt_connect(hook_p hook) 1400187494Semax{ 1401187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1402184610Salfred 1403187494Semax NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 1404184610Salfred 1405187741Semax UBT_NG_LOCK(sc); 1406187494Semax ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); 1407187741Semax UBT_NG_UNLOCK(sc); 1408184610Salfred 1409184610Salfred return (0); 1410187494Semax} /* ng_ubt_connect */ 1411184610Salfred 1412184610Salfred/* 1413187494Semax * Disconnect hook. 1414187494Semax * Netgraph context. 1415184610Salfred */ 1416184610Salfred 1417184610Salfredstatic int 1418184610Salfredng_ubt_disconnect(hook_p hook) 1419184610Salfred{ 1420187741Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1421184610Salfred 1422187741Semax UBT_NG_LOCK(sc); 1423184610Salfred 1424187741Semax if (hook != sc->sc_hook) { 1425187741Semax UBT_NG_UNLOCK(sc); 1426184610Salfred 1427187494Semax return (EINVAL); 1428187741Semax } 1429184610Salfred 1430187494Semax sc->sc_hook = NULL; 1431184610Salfred 1432187741Semax /* Kick off task to stop all USB xfers */ 1433187741Semax ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); 1434184610Salfred 1435187494Semax /* Drain queues */ 1436187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); 1437187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); 1438187494Semax NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); 1439184610Salfred 1440187741Semax UBT_NG_UNLOCK(sc); 1441184610Salfred 1442187494Semax return (0); 1443187494Semax} /* ng_ubt_disconnect */ 1444187494Semax 1445184610Salfred/* 1446187494Semax * Process control message. 1447187494Semax * Netgraph context. 1448184610Salfred */ 1449184610Salfred 1450184610Salfredstatic int 1451184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) 1452184610Salfred{ 1453187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1454187494Semax struct ng_mesg *msg, *rsp = NULL; 1455187494Semax struct ng_bt_mbufq *q; 1456187494Semax int error = 0, queue, qlen; 1457184610Salfred 1458184610Salfred NGI_GET_MSG(item, msg); 1459184610Salfred 1460184610Salfred switch (msg->header.typecookie) { 1461184610Salfred case NGM_GENERIC_COOKIE: 1462184610Salfred switch (msg->header.cmd) { 1463184610Salfred case NGM_TEXT_STATUS: 1464184610Salfred NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 1465187494Semax if (rsp == NULL) { 1466184610Salfred error = ENOMEM; 1467187494Semax break; 1468187494Semax } 1469187494Semax 1470187494Semax snprintf(rsp->data, NG_TEXTRESPONSE, 1471187494Semax "Hook: %s\n" \ 1472187494Semax "Task flags: %#x\n" \ 1473187494Semax "Debug: %d\n" \ 1474187494Semax "CMD queue: [have:%d,max:%d]\n" \ 1475187494Semax "ACL queue: [have:%d,max:%d]\n" \ 1476187494Semax "SCO queue: [have:%d,max:%d]", 1477187741Semax (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", 1478187494Semax sc->sc_task_flags, 1479187494Semax sc->sc_debug, 1480187494Semax sc->sc_cmdq.len, 1481187494Semax sc->sc_cmdq.maxlen, 1482187494Semax sc->sc_aclq.len, 1483187494Semax sc->sc_aclq.maxlen, 1484187494Semax sc->sc_scoq.len, 1485187494Semax sc->sc_scoq.maxlen); 1486184610Salfred break; 1487184610Salfred 1488184610Salfred default: 1489184610Salfred error = EINVAL; 1490184610Salfred break; 1491184610Salfred } 1492184610Salfred break; 1493184610Salfred 1494184610Salfred case NGM_UBT_COOKIE: 1495184610Salfred switch (msg->header.cmd) { 1496184610Salfred case NGM_UBT_NODE_SET_DEBUG: 1497187494Semax if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ 1498184610Salfred error = EMSGSIZE; 1499187494Semax break; 1500187494Semax } 1501187494Semax 1502187494Semax sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); 1503184610Salfred break; 1504184610Salfred 1505184610Salfred case NGM_UBT_NODE_GET_DEBUG: 1506184610Salfred NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), 1507184610Salfred M_NOWAIT); 1508187494Semax if (rsp == NULL) { 1509184610Salfred error = ENOMEM; 1510187494Semax break; 1511187494Semax } 1512187494Semax 1513187494Semax *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; 1514184610Salfred break; 1515184610Salfred 1516184610Salfred case NGM_UBT_NODE_SET_QLEN: 1517187494Semax if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1518184610Salfred error = EMSGSIZE; 1519187494Semax break; 1520187494Semax } 1521184610Salfred 1522187494Semax queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1523187494Semax qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; 1524184610Salfred 1525187494Semax switch (queue) { 1526187494Semax case NGM_UBT_NODE_QUEUE_CMD: 1527187494Semax q = &sc->sc_cmdq; 1528187494Semax break; 1529184610Salfred 1530187494Semax case NGM_UBT_NODE_QUEUE_ACL: 1531187494Semax q = &sc->sc_aclq; 1532187494Semax break; 1533184610Salfred 1534187494Semax case NGM_UBT_NODE_QUEUE_SCO: 1535187494Semax q = &sc->sc_scoq; 1536187494Semax break; 1537184610Salfred 1538187494Semax default: 1539187494Semax error = EINVAL; 1540187494Semax goto done; 1541187494Semax /* NOT REACHED */ 1542184610Salfred } 1543187494Semax 1544187494Semax q->maxlen = qlen; 1545184610Salfred break; 1546184610Salfred 1547184610Salfred case NGM_UBT_NODE_GET_QLEN: 1548184610Salfred if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1549184610Salfred error = EMSGSIZE; 1550184610Salfred break; 1551184610Salfred } 1552187494Semax 1553184610Salfred queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1554187494Semax 1555184610Salfred switch (queue) { 1556184610Salfred case NGM_UBT_NODE_QUEUE_CMD: 1557184610Salfred q = &sc->sc_cmdq; 1558184610Salfred break; 1559184610Salfred 1560184610Salfred case NGM_UBT_NODE_QUEUE_ACL: 1561184610Salfred q = &sc->sc_aclq; 1562184610Salfred break; 1563184610Salfred 1564184610Salfred case NGM_UBT_NODE_QUEUE_SCO: 1565184610Salfred q = &sc->sc_scoq; 1566184610Salfred break; 1567184610Salfred 1568184610Salfred default: 1569184610Salfred error = EINVAL; 1570187494Semax goto done; 1571187494Semax /* NOT REACHED */ 1572187494Semax } 1573187494Semax 1574187494Semax NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), 1575187494Semax M_NOWAIT); 1576187494Semax if (rsp == NULL) { 1577187494Semax error = ENOMEM; 1578184610Salfred break; 1579184610Salfred } 1580184610Salfred 1581187494Semax ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; 1582187494Semax ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; 1583184610Salfred break; 1584184610Salfred 1585184610Salfred case NGM_UBT_NODE_GET_STAT: 1586184610Salfred NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), 1587184610Salfred M_NOWAIT); 1588187494Semax if (rsp == NULL) { 1589184610Salfred error = ENOMEM; 1590187494Semax break; 1591184610Salfred } 1592187494Semax 1593187494Semax bcopy(&sc->sc_stat, rsp->data, 1594187494Semax sizeof(ng_ubt_node_stat_ep)); 1595184610Salfred break; 1596184610Salfred 1597184610Salfred case NGM_UBT_NODE_RESET_STAT: 1598187494Semax UBT_STAT_RESET(sc); 1599184610Salfred break; 1600184610Salfred 1601184610Salfred default: 1602184610Salfred error = EINVAL; 1603184610Salfred break; 1604184610Salfred } 1605184610Salfred break; 1606184610Salfred 1607184610Salfred default: 1608184610Salfred error = EINVAL; 1609184610Salfred break; 1610184610Salfred } 1611187494Semaxdone: 1612184610Salfred NG_RESPOND_MSG(error, node, item, rsp); 1613184610Salfred NG_FREE_MSG(msg); 1614184610Salfred 1615184610Salfred return (error); 1616187494Semax} /* ng_ubt_rcvmsg */ 1617184610Salfred 1618184610Salfred/* 1619187494Semax * Process data. 1620187494Semax * Netgraph context. 1621184610Salfred */ 1622184610Salfred 1623184610Salfredstatic int 1624184610Salfredng_ubt_rcvdata(hook_p hook, item_p item) 1625184610Salfred{ 1626187494Semax struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1627187494Semax struct mbuf *m; 1628187494Semax struct ng_bt_mbufq *q; 1629187494Semax int action, error = 0; 1630184610Salfred 1631184610Salfred if (hook != sc->sc_hook) { 1632184610Salfred error = EINVAL; 1633184610Salfred goto done; 1634184610Salfred } 1635187494Semax 1636187494Semax /* Deatch mbuf and get HCI frame type */ 1637184610Salfred NGI_GET_M(item, m); 1638184610Salfred 1639187494Semax /* 1640187494Semax * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, 1641187494Semax * 2 bytes connection handle and at least 1 byte of length. 1642187494Semax * Panic on data frame that has size smaller than 4 bytes (it 1643187494Semax * should not happen) 1644187494Semax */ 1645187494Semax 1646187494Semax if (m->m_pkthdr.len < 4) 1647187494Semax panic("HCI frame size is too small! pktlen=%d\n", 1648187494Semax m->m_pkthdr.len); 1649187494Semax 1650187494Semax /* Process HCI frame */ 1651184610Salfred switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ 1652184610Salfred case NG_HCI_CMD_PKT: 1653187494Semax if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE) 1654187494Semax panic("HCI command frame size is too big! " \ 1655187494Semax "buffer size=%zd, packet len=%d\n", 1656187494Semax UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); 1657187494Semax 1658184610Salfred q = &sc->sc_cmdq; 1659187494Semax action = UBT_FLAG_T_START_CTRL; 1660184610Salfred break; 1661184610Salfred 1662184610Salfred case NG_HCI_ACL_DATA_PKT: 1663187494Semax if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) 1664187494Semax panic("ACL data frame size is too big! " \ 1665187494Semax "buffer size=%d, packet len=%d\n", 1666187494Semax UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); 1667187494Semax 1668184610Salfred q = &sc->sc_aclq; 1669187494Semax action = UBT_FLAG_T_START_BULK; 1670184610Salfred break; 1671184610Salfred 1672184610Salfred case NG_HCI_SCO_DATA_PKT: 1673184610Salfred q = &sc->sc_scoq; 1674187494Semax action = 0; 1675184610Salfred break; 1676184610Salfred 1677184610Salfred default: 1678187494Semax UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ 1679187494Semax "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len); 1680184610Salfred 1681184610Salfred NG_FREE_M(m); 1682184610Salfred error = EINVAL; 1683184610Salfred goto done; 1684187494Semax /* NOT REACHED */ 1685184610Salfred } 1686184610Salfred 1687187741Semax UBT_NG_LOCK(sc); 1688184610Salfred if (NG_BT_MBUFQ_FULL(q)) { 1689187494Semax NG_BT_MBUFQ_DROP(q); 1690187741Semax UBT_NG_UNLOCK(sc); 1691187494Semax 1692187494Semax UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", 1693187494Semax *mtod(m, uint8_t *), m->m_pkthdr.len); 1694187494Semax 1695184610Salfred NG_FREE_M(m); 1696184610Salfred } else { 1697187494Semax /* Loose HCI packet type, enqueue mbuf and kick off task */ 1698187494Semax m_adj(m, sizeof(uint8_t)); 1699184610Salfred NG_BT_MBUFQ_ENQUEUE(q, m); 1700187494Semax ubt_task_schedule(sc, action); 1701187741Semax UBT_NG_UNLOCK(sc); 1702184610Salfred } 1703184610Salfreddone: 1704184610Salfred NG_FREE_ITEM(item); 1705184610Salfred 1706187494Semax return (error); 1707187494Semax} /* ng_ubt_rcvdata */ 1708187494Semax 1709187494Semax/**************************************************************************** 1710187494Semax **************************************************************************** 1711187494Semax ** Module 1712187494Semax **************************************************************************** 1713187494Semax ****************************************************************************/ 1714187494Semax 1715187494Semax/* 1716187494Semax * Load/Unload the driver module 1717187494Semax */ 1718187494Semax 1719187494Semaxstatic int 1720187494Semaxubt_modevent(module_t mod, int event, void *data) 1721187494Semax{ 1722187494Semax int error; 1723187494Semax 1724187494Semax switch (event) { 1725187494Semax case MOD_LOAD: 1726187494Semax error = ng_newtype(&typestruct); 1727187494Semax if (error != 0) 1728187494Semax printf("%s: Could not register Netgraph node type, " \ 1729187494Semax "error=%d\n", NG_UBT_NODE_TYPE, error); 1730187494Semax break; 1731187494Semax 1732187494Semax case MOD_UNLOAD: 1733187494Semax error = ng_rmtype(&typestruct); 1734187494Semax break; 1735187494Semax 1736187494Semax default: 1737187494Semax error = EOPNOTSUPP; 1738187494Semax break; 1739184610Salfred } 1740187494Semax 1741184610Salfred return (error); 1742187494Semax} /* ubt_modevent */ 1743187494Semax 1744187494Semaxstatic devclass_t ubt_devclass; 1745187494Semax 1746187494Semaxstatic device_method_t ubt_methods[] = 1747187494Semax{ 1748187494Semax DEVMETHOD(device_probe, ubt_probe), 1749187494Semax DEVMETHOD(device_attach, ubt_attach), 1750187494Semax DEVMETHOD(device_detach, ubt_detach), 1751187494Semax { 0, 0 } 1752187494Semax}; 1753187494Semax 1754187494Semaxstatic driver_t ubt_driver = 1755187494Semax{ 1756187494Semax .name = "ubt", 1757187494Semax .methods = ubt_methods, 1758187494Semax .size = sizeof(struct ubt_softc), 1759187494Semax}; 1760187494Semax 1761189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0); 1762187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); 1763187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 1764187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 1765188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1); 1766187494Semax 1767