ng_ubt.c revision 192909
1106163Sroberto/* 2106163Sroberto * ng_ubt.c 3106163Sroberto */ 4106163Sroberto 5106163Sroberto/*- 6106163Sroberto * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7182007Sroberto * All rights reserved. 8106163Sroberto * 9106163Sroberto * Redistribution and use in source and binary forms, with or without 10106163Sroberto * modification, are permitted provided that the following conditions 11106163Sroberto * are met: 12106163Sroberto * 1. Redistributions of source code must retain the above copyright 13106163Sroberto * notice, this list of conditions and the following disclaimer. 14106163Sroberto * 2. Redistributions in binary form must reproduce the above copyright 15106163Sroberto * notice, this list of conditions and the following disclaimer in the 16106163Sroberto * documentation and/or other materials provided with the distribution. 17106163Sroberto * 18106163Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19106163Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20106163Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21106163Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22106163Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23106163Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24106163Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25106163Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26106163Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27106163Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28106163Sroberto * SUCH DAMAGE. 29106163Sroberto * 30106163Sroberto * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $ 31106163Sroberto * $FreeBSD: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c 192909 2009-05-27 16:43:40Z thompsa $ 32106163Sroberto */ 33106163Sroberto 34106163Sroberto/* 35106163Sroberto * NOTE: ng_ubt2 driver has a split personality. On one side it is 36106163Sroberto * a USB device driver and on the other it is a Netgraph node. This 37106163Sroberto * driver will *NOT* create traditional /dev/ enties, only Netgraph 38106163Sroberto * node. 39106163Sroberto * 40106163Sroberto * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes) 41106163Sroberto * 42106163Sroberto * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used 43106163Sroberto * by USB for any USB request going over device's interface #0 and #1, 44106163Sroberto * i.e. interrupt, control, bulk and isoc. transfers. 45106163Sroberto * 46106163Sroberto * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph 47106163Sroberto * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook 48106163Sroberto * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact, 49106163Sroberto * think of it as a spin lock. 50106163Sroberto * 51106163Sroberto * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts. 52106163Sroberto * 53106163Sroberto * 1) USB context. This is where all the USB related stuff happens. All 54106163Sroberto * callbacks run in this context. All callbacks are called (by USB) with 55106163Sroberto * appropriate interface lock held. It is (generally) allowed to grab 56106163Sroberto * any additional locks. 57106163Sroberto * 58182007Sroberto * 2) Netgraph context. This is where all the Netgraph related stuff happens. 59106163Sroberto * Since we mark node as WRITER, the Netgraph node will be "locked" (from 60106163Sroberto * Netgraph point of view). Any variable that is only modified from the 61106163Sroberto * Netgraph context does not require any additonal locking. It is generally 62182007Sroberto * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT* 63182007Sroberto * grab any lock in the Netgraph context that could cause de-scheduling of 64182007Sroberto * the Netgraph thread for significant amount of time. In fact, the only 65182007Sroberto * lock that is allowed in the Netgraph context is the sc_ng_mtx lock. 66182007Sroberto * Also make sure that any code that is called from the Netgraph context 67182007Sroberto * follows the rule above. 68182007Sroberto * 69182007Sroberto * 3) Taskqueue context. This is where ubt_task runs. Since we are generally 70182007Sroberto * NOT allowed to grab any lock that could cause de-scheduling in the 71182007Sroberto * Netgraph context, and, USB requires us to grab interface lock before 72182007Sroberto * doing things with transfers, it is safer to transition from the Netgraph 73182007Sroberto * context to the Taskqueue context before we can call into USB subsystem. 74182007Sroberto * 75182007Sroberto * So, to put everything together, the rules are as follows. 76182007Sroberto * It is OK to call from the USB context or the Taskqueue context into 77106163Sroberto * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words 78106163Sroberto * it is allowed to call into the Netgraph context with locks held. 79106163Sroberto * Is it *NOT* OK to call from the Netgraph context into the USB context, 80106163Sroberto * because USB requires us to grab interface locks, and, it is safer to 81106163Sroberto * avoid it. So, to make things safer we set task flags to indicate which 82106163Sroberto * actions we want to perform and schedule ubt_task which would run in the 83106163Sroberto * Taskqueue context. 84106163Sroberto * Is is OK to call from the Taskqueue context into the USB context, 85106163Sroberto * and, ubt_task does just that (i.e. grabs appropriate interface locks 86106163Sroberto * before calling into USB). 87106163Sroberto * Access to the outgoing queues, task flags and hook pointer is 88106163Sroberto * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again, 89106163Sroberto * sc_ng_mtx should really be a spin lock (and it is very likely to an 90106163Sroberto * equivalent of spin lock due to adaptive nature of FreeBSD mutexes). 91106163Sroberto * All USB callbacks accept softc pointer as a private data. USB ensures 92106163Sroberto * that this pointer is valid. 93106163Sroberto */ 94106163Sroberto 95106163Sroberto#include "usbdevs.h" 96106163Sroberto#include <dev/usb/usb.h> 97106163Sroberto#include <dev/usb/usb_mfunc.h> 98106163Sroberto#include <dev/usb/usb_error.h> 99106163Sroberto 100106163Sroberto#define USB_DEBUG_VAR usb2_debug 101106163Sroberto 102106163Sroberto#include <dev/usb/usb_core.h> 103106163Sroberto#include <dev/usb/usb_debug.h> 104106163Sroberto#include <dev/usb/usb_parse.h> 105106163Sroberto#include <dev/usb/usb_lookup.h> 106106163Sroberto#include <dev/usb/usb_util.h> 107106163Sroberto#include <dev/usb/usb_busdma.h> 108106163Sroberto#include <dev/usb/usb_process.h> 109106163Sroberto#include <dev/usb/usb_transfer.h> 110106163Sroberto 111106163Sroberto#include <sys/mbuf.h> 112106163Sroberto#include <sys/taskqueue.h> 113106163Sroberto 114106163Sroberto#include <netgraph/ng_message.h> 115106163Sroberto#include <netgraph/netgraph.h> 116106163Sroberto#include <netgraph/ng_parse.h> 117106163Sroberto#include <netgraph/bluetooth/include/ng_bluetooth.h> 118106163Sroberto#include <netgraph/bluetooth/include/ng_hci.h> 119106163Sroberto#include <netgraph/bluetooth/include/ng_ubt.h> 120106163Sroberto#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h> 121106163Sroberto 122182007Srobertostatic int ubt_modevent(module_t, int, void *); 123182007Srobertostatic device_probe_t ubt_probe; 124182007Srobertostatic device_attach_t ubt_attach; 125182007Srobertostatic device_detach_t ubt_detach; 126182007Sroberto 127182007Srobertostatic void ubt_task_schedule(ubt_softc_p, int); 128182007Srobertostatic task_fn_t ubt_task; 129182007Sroberto 130182007Sroberto#define ubt_xfer_start(sc, i) usb2_transfer_start((sc)->sc_xfer[(i)]) 131182007Sroberto 132182007Sroberto/* Netgraph methods */ 133182007Srobertostatic ng_constructor_t ng_ubt_constructor; 134106163Srobertostatic ng_shutdown_t ng_ubt_shutdown; 135106163Srobertostatic ng_newhook_t ng_ubt_newhook; 136106163Srobertostatic ng_connect_t ng_ubt_connect; 137106163Srobertostatic ng_disconnect_t ng_ubt_disconnect; 138106163Srobertostatic ng_rcvmsg_t ng_ubt_rcvmsg; 139106163Srobertostatic ng_rcvdata_t ng_ubt_rcvdata; 140106163Sroberto 141106163Sroberto/* Queue length */ 142106163Srobertostatic const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = 143106163Sroberto{ 144106163Sroberto { "queue", &ng_parse_int32_type, }, 145106163Sroberto { "qlen", &ng_parse_int32_type, }, 146106163Sroberto { NULL, } 147106163Sroberto}; 148182007Srobertostatic const struct ng_parse_type ng_ubt_node_qlen_type = 149182007Sroberto{ 150106163Sroberto &ng_parse_struct_type, 151106163Sroberto &ng_ubt_node_qlen_type_fields 152106163Sroberto}; 153106163Sroberto 154106163Sroberto/* Stat info */ 155106163Srobertostatic const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = 156106163Sroberto{ 157106163Sroberto { "pckts_recv", &ng_parse_uint32_type, }, 158106163Sroberto { "bytes_recv", &ng_parse_uint32_type, }, 159106163Sroberto { "pckts_sent", &ng_parse_uint32_type, }, 160106163Sroberto { "bytes_sent", &ng_parse_uint32_type, }, 161106163Sroberto { "oerrors", &ng_parse_uint32_type, }, 162106163Sroberto { "ierrors", &ng_parse_uint32_type, }, 163106163Sroberto { NULL, } 164106163Sroberto}; 165106163Srobertostatic const struct ng_parse_type ng_ubt_node_stat_type = 166182007Sroberto{ 167106163Sroberto &ng_parse_struct_type, 168106163Sroberto &ng_ubt_node_stat_type_fields 169106163Sroberto}; 170106163Sroberto 171106163Sroberto/* Netgraph node command list */ 172106163Srobertostatic const struct ng_cmdlist ng_ubt_cmdlist[] = 173106163Sroberto{ 174106163Sroberto { 175106163Sroberto NGM_UBT_COOKIE, 176182007Sroberto NGM_UBT_NODE_SET_DEBUG, 177106163Sroberto "set_debug", 178106163Sroberto &ng_parse_uint16_type, 179106163Sroberto NULL 180182007Sroberto }, 181106163Sroberto { 182106163Sroberto NGM_UBT_COOKIE, 183106163Sroberto NGM_UBT_NODE_GET_DEBUG, 184106163Sroberto "get_debug", 185106163Sroberto NULL, 186106163Sroberto &ng_parse_uint16_type 187106163Sroberto }, 188106163Sroberto { 189106163Sroberto NGM_UBT_COOKIE, 190106163Sroberto NGM_UBT_NODE_SET_QLEN, 191106163Sroberto "set_qlen", 192106163Sroberto &ng_ubt_node_qlen_type, 193106163Sroberto NULL 194106163Sroberto }, 195106163Sroberto { 196106163Sroberto NGM_UBT_COOKIE, 197106163Sroberto NGM_UBT_NODE_GET_QLEN, 198106163Sroberto "get_qlen", 199106163Sroberto &ng_ubt_node_qlen_type, 200106163Sroberto &ng_ubt_node_qlen_type 201106163Sroberto }, 202106163Sroberto { 203106163Sroberto NGM_UBT_COOKIE, 204106163Sroberto NGM_UBT_NODE_GET_STAT, 205106163Sroberto "get_stat", 206106163Sroberto NULL, 207106163Sroberto &ng_ubt_node_stat_type 208106163Sroberto }, 209106163Sroberto { 210106163Sroberto NGM_UBT_COOKIE, 211106163Sroberto NGM_UBT_NODE_RESET_STAT, 212106163Sroberto "reset_stat", 213106163Sroberto NULL, 214106163Sroberto NULL 215106163Sroberto }, 216106163Sroberto { 0, } 217106163Sroberto}; 218106163Sroberto 219106163Sroberto/* Netgraph node type */ 220106163Srobertostatic struct ng_type typestruct = 221106163Sroberto{ 222106163Sroberto .version = NG_ABI_VERSION, 223132451Sroberto .name = NG_UBT_NODE_TYPE, 224106163Sroberto .constructor = ng_ubt_constructor, 225106163Sroberto .rcvmsg = ng_ubt_rcvmsg, 226106163Sroberto .shutdown = ng_ubt_shutdown, 227106163Sroberto .newhook = ng_ubt_newhook, 228106163Sroberto .connect = ng_ubt_connect, 229106163Sroberto .rcvdata = ng_ubt_rcvdata, 230106163Sroberto .disconnect = ng_ubt_disconnect, 231106163Sroberto .cmdlist = ng_ubt_cmdlist 232106163Sroberto}; 233106163Sroberto 234106163Sroberto/**************************************************************************** 235106163Sroberto **************************************************************************** 236106163Sroberto ** USB specific 237132451Sroberto **************************************************************************** 238106163Sroberto ****************************************************************************/ 239132451Sroberto 240106163Sroberto/* USB methods */ 241106163Srobertostatic usb2_callback_t ubt_ctrl_write_callback; 242106163Srobertostatic usb2_callback_t ubt_intr_read_callback; 243182007Srobertostatic usb2_callback_t ubt_bulk_read_callback; 244106163Srobertostatic usb2_callback_t ubt_bulk_write_callback; 245106163Srobertostatic usb2_callback_t ubt_isoc_read_callback; 246132451Srobertostatic usb2_callback_t ubt_isoc_write_callback; 247106163Sroberto 248106163Srobertostatic int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **); 249106163Srobertostatic int ubt_isoc_read_one_frame(struct usb2_xfer *, int); 250106163Sroberto 251106163Sroberto/* 252106163Sroberto * USB config 253106163Sroberto * 254106163Sroberto * The following desribes usb transfers that could be submitted on USB device. 255106163Sroberto * 256106163Sroberto * Interface 0 on the USB device must present the following endpoints 257106163Sroberto * 1) Interrupt endpoint to receive HCI events 258106163Sroberto * 2) Bulk IN endpoint to receive ACL data 259106163Sroberto * 3) Bulk OUT endpoint to send ACL data 260106163Sroberto * 261106163Sroberto * Interface 1 on the USB device must present the following endpoints 262106163Sroberto * 1) Isochronous IN endpoint to receive SCO data 263106163Sroberto * 2) Isochronous OUT endpoint to send SCO data 264106163Sroberto */ 265106163Sroberto 266106163Srobertostatic const struct usb2_config ubt_config[UBT_N_TRANSFER] = 267106163Sroberto{ 268106163Sroberto /* 269132451Sroberto * Interface #0 270106163Sroberto */ 271132451Sroberto 272106163Sroberto /* Outgoing bulk transfer - ACL packets */ 273106163Sroberto [UBT_IF_0_BULK_DT_WR] = { 274106163Sroberto .type = UE_BULK, 275106163Sroberto .endpoint = UE_ADDR_ANY, 276106163Sroberto .direction = UE_DIR_OUT, 277106163Sroberto .if_index = 0, 278106163Sroberto .bufsize = UBT_BULK_WRITE_BUFFER_SIZE, 279106163Sroberto .flags = { .pipe_bof = 1, .force_short_xfer = 1, }, 280106163Sroberto .callback = &ubt_bulk_write_callback, 281106163Sroberto }, 282106163Sroberto /* Incoming bulk transfer - ACL packets */ 283106163Sroberto [UBT_IF_0_BULK_DT_RD] = { 284106163Sroberto .type = UE_BULK, 285106163Sroberto .endpoint = UE_ADDR_ANY, 286106163Sroberto .direction = UE_DIR_IN, 287106163Sroberto .if_index = 0, 288106163Sroberto .bufsize = UBT_BULK_READ_BUFFER_SIZE, 289182007Sroberto .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 290182007Sroberto .callback = &ubt_bulk_read_callback, 291182007Sroberto }, 292182007Sroberto /* Incoming interrupt transfer - HCI events */ 293182007Sroberto [UBT_IF_0_INTR_DT_RD] = { 294182007Sroberto .type = UE_INTERRUPT, 295182007Sroberto .endpoint = UE_ADDR_ANY, 296182007Sroberto .direction = UE_DIR_IN, 297182007Sroberto .if_index = 0, 298182007Sroberto .flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, 299182007Sroberto .bufsize = UBT_INTR_BUFFER_SIZE, 300182007Sroberto .callback = &ubt_intr_read_callback, 301182007Sroberto }, 302106163Sroberto /* Outgoing control transfer - HCI commands */ 303106163Sroberto [UBT_IF_0_CTRL_DT_WR] = { 304132451Sroberto .type = UE_CONTROL, 305106163Sroberto .endpoint = 0x00, /* control pipe */ 306106163Sroberto .direction = UE_DIR_ANY, 307106163Sroberto .if_index = 0, 308106163Sroberto .bufsize = UBT_CTRL_BUFFER_SIZE, 309106163Sroberto .callback = &ubt_ctrl_write_callback, 310106163Sroberto .timeout = 5000, /* 5 seconds */ 311106163Sroberto }, 312106163Sroberto 313106163Sroberto /* 314106163Sroberto * Interface #1 315106163Sroberto */ 316106163Sroberto 317106163Sroberto /* Incoming isochronous transfer #1 - SCO packets */ 318106163Sroberto [UBT_IF_1_ISOC_DT_RD1] = { 319106163Sroberto .type = UE_ISOCHRONOUS, 320106163Sroberto .endpoint = UE_ADDR_ANY, 321106163Sroberto .direction = UE_DIR_IN, 322106163Sroberto .if_index = 1, 323106163Sroberto .bufsize = 0, /* use "wMaxPacketSize * frames" */ 324106163Sroberto .frames = UBT_ISOC_NFRAMES, 325106163Sroberto .flags = { .short_xfer_ok = 1, }, 326106163Sroberto .callback = &ubt_isoc_read_callback, 327106163Sroberto }, 328106163Sroberto /* Incoming isochronous transfer #2 - SCO packets */ 329106163Sroberto [UBT_IF_1_ISOC_DT_RD2] = { 330106163Sroberto .type = UE_ISOCHRONOUS, 331106163Sroberto .endpoint = UE_ADDR_ANY, 332106163Sroberto .direction = UE_DIR_IN, 333106163Sroberto .if_index = 1, 334106163Sroberto .bufsize = 0, /* use "wMaxPacketSize * frames" */ 335106163Sroberto .frames = UBT_ISOC_NFRAMES, 336106163Sroberto .flags = { .short_xfer_ok = 1, }, 337106163Sroberto .callback = &ubt_isoc_read_callback, 338106163Sroberto }, 339106163Sroberto /* Outgoing isochronous transfer #1 - SCO packets */ 340106163Sroberto [UBT_IF_1_ISOC_DT_WR1] = { 341106163Sroberto .type = UE_ISOCHRONOUS, 342106163Sroberto .endpoint = UE_ADDR_ANY, 343106163Sroberto .direction = UE_DIR_OUT, 344106163Sroberto .if_index = 1, 345106163Sroberto .bufsize = 0, /* use "wMaxPacketSize * frames" */ 346106163Sroberto .frames = UBT_ISOC_NFRAMES, 347106163Sroberto .flags = { .short_xfer_ok = 1, }, 348106163Sroberto .callback = &ubt_isoc_write_callback, 349106163Sroberto }, 350106163Sroberto /* Outgoing isochronous transfer #2 - SCO packets */ 351106163Sroberto [UBT_IF_1_ISOC_DT_WR2] = { 352106163Sroberto .type = UE_ISOCHRONOUS, 353106163Sroberto .endpoint = UE_ADDR_ANY, 354106163Sroberto .direction = UE_DIR_OUT, 355106163Sroberto .if_index = 1, 356106163Sroberto .bufsize = 0, /* use "wMaxPacketSize * frames" */ 357106163Sroberto .frames = UBT_ISOC_NFRAMES, 358106163Sroberto .flags = { .short_xfer_ok = 1, }, 359106163Sroberto .callback = &ubt_isoc_write_callback, 360106163Sroberto }, 361106163Sroberto}; 362106163Sroberto 363106163Sroberto/* 364106163Sroberto * If for some reason device should not be attached then put 365106163Sroberto * VendorID/ProductID pair into the list below. The format is 366106163Sroberto * as follows: 367106163Sroberto * 368106163Sroberto * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, 369106163Sroberto * 370106163Sroberto * where VENDOR_ID and PRODUCT_ID are hex numbers. 371106163Sroberto */ 372106163Sroberto 373106163Srobertostatic const struct usb2_device_id ubt_ignore_devs[] = 374106163Sroberto{ 375106163Sroberto /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */ 376106163Sroberto { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) }, 377106163Sroberto}; 378106163Sroberto 379106163Sroberto/* List of supported bluetooth devices */ 380106163Srobertostatic const struct usb2_device_id ubt_devs[] = 381106163Sroberto{ 382106163Sroberto /* Generic Bluetooth class devices */ 383106163Sroberto { USB_IFACE_CLASS(UDCLASS_WIRELESS), 384106163Sroberto USB_IFACE_SUBCLASS(UDSUBCLASS_RF), 385106163Sroberto USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, 386106163Sroberto 387106163Sroberto /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ 388106163Sroberto { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) }, 389106163Sroberto}; 390106163Sroberto 391106163Sroberto/* 392106163Sroberto * Probe for a USB Bluetooth device. 393106163Sroberto * USB context. 394106163Sroberto */ 395106163Sroberto 396106163Srobertostatic int 397106163Srobertoubt_probe(device_t dev) 398106163Sroberto{ 399106163Sroberto struct usb2_attach_arg *uaa = device_get_ivars(dev); 400106163Sroberto 401106163Sroberto if (uaa->usb_mode != USB_MODE_HOST) 402106163Sroberto return (ENXIO); 403106163Sroberto 404106163Sroberto if (uaa->info.bIfaceIndex != 0) 405106163Sroberto return (ENXIO); 406106163Sroberto 407106163Sroberto if (uaa->use_generic == 0) 408106163Sroberto return (ENXIO); 409106163Sroberto 410106163Sroberto if (usb2_lookup_id_by_uaa(ubt_ignore_devs, 411106163Sroberto sizeof(ubt_ignore_devs), uaa) == 0) 412106163Sroberto return (ENXIO); 413106163Sroberto 414106163Sroberto return (usb2_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa)); 415106163Sroberto} /* ubt_probe */ 416106163Sroberto 417106163Sroberto/* 418106163Sroberto * Attach the device. 419106163Sroberto * USB context. 420106163Sroberto */ 421106163Sroberto 422106163Srobertostatic int 423106163Srobertoubt_attach(device_t dev) 424106163Sroberto{ 425106163Sroberto struct usb2_attach_arg *uaa = device_get_ivars(dev); 426106163Sroberto struct ubt_softc *sc = device_get_softc(dev); 427106163Sroberto struct usb2_endpoint_descriptor *ed; 428106163Sroberto struct usb2_interface_descriptor *id; 429106163Sroberto uint16_t wMaxPacketSize; 430106163Sroberto uint8_t alt_index, i, j; 431106163Sroberto uint8_t iface_index[2] = { 0, 1 }; 432106163Sroberto 433182007Sroberto device_set_usb2_desc(dev); 434182007Sroberto 435182007Sroberto sc->sc_dev = dev; 436182007Sroberto sc->sc_debug = NG_UBT_WARN_LEVEL; 437106163Sroberto 438106163Sroberto /* 439106163Sroberto * Create Netgraph node 440106163Sroberto */ 441106163Sroberto 442106163Sroberto if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { 443106163Sroberto UBT_ALERT(sc, "could not create Netgraph node\n"); 444106163Sroberto return (ENXIO); 445106163Sroberto } 446106163Sroberto 447106163Sroberto /* Name Netgraph node */ 448106163Sroberto if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { 449106163Sroberto UBT_ALERT(sc, "could not name Netgraph node\n"); 450106163Sroberto NG_NODE_UNREF(sc->sc_node); 451106163Sroberto return (ENXIO); 452106163Sroberto } 453106163Sroberto NG_NODE_SET_PRIVATE(sc->sc_node, sc); 454106163Sroberto NG_NODE_FORCE_WRITER(sc->sc_node); 455106163Sroberto 456106163Sroberto /* 457106163Sroberto * Initialize device softc structure 458106163Sroberto */ 459106163Sroberto 460106163Sroberto /* initialize locks */ 461106163Sroberto mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); 462106163Sroberto mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); 463106163Sroberto 464106163Sroberto /* initialize packet queues */ 465106163Sroberto NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); 466106163Sroberto NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); 467106163Sroberto NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); 468106163Sroberto 469106163Sroberto /* initialize glue task */ 470106163Sroberto TASK_INIT(&sc->sc_task, 0, ubt_task, sc); 471106163Sroberto 472106163Sroberto /* 473106163Sroberto * Configure Bluetooth USB device. Discover all required USB 474106163Sroberto * interfaces and endpoints. 475132451Sroberto * 476106163Sroberto * USB device must present two interfaces: 477106163Sroberto * 1) Interface 0 that has 3 endpoints 478106163Sroberto * 1) Interrupt endpoint to receive HCI events 479106163Sroberto * 2) Bulk IN endpoint to receive ACL data 480106163Sroberto * 3) Bulk OUT endpoint to send ACL data 481106163Sroberto * 482106163Sroberto * 2) Interface 1 then has 2 endpoints 483106163Sroberto * 1) Isochronous IN endpoint to receive SCO data 484106163Sroberto * 2) Isochronous OUT endpoint to send SCO data 485106163Sroberto * 486106163Sroberto * Interface 1 (with isochronous endpoints) has several alternate 487106163Sroberto * configurations with different packet size. 488106163Sroberto */ 489106163Sroberto 490106163Sroberto /* 491182007Sroberto * For interface #1 search alternate settings, and find 492182007Sroberto * the descriptor with the largest wMaxPacketSize 493182007Sroberto */ 494182007Sroberto 495106163Sroberto wMaxPacketSize = 0; 496106163Sroberto alt_index = 0; 497106163Sroberto i = 0; 498106163Sroberto j = 0; 499106163Sroberto ed = NULL; 500106163Sroberto 501106163Sroberto /* 502182007Sroberto * Search through all the descriptors looking for the largest 503182007Sroberto * packet size: 504182007Sroberto */ 505182007Sroberto while ((ed = (struct usb2_endpoint_descriptor *)usb2_desc_foreach( 506182007Sroberto usb2_get_config_descriptor(uaa->device), 507106163Sroberto (struct usb2_descriptor *)ed))) { 508106163Sroberto 509106163Sroberto if ((ed->bDescriptorType == UDESC_INTERFACE) && 510106163Sroberto (ed->bLength >= sizeof(*id))) { 511106163Sroberto id = (struct usb2_interface_descriptor *)ed; 512132451Sroberto i = id->bInterfaceNumber; 513132451Sroberto j = id->bAlternateSetting; 514182007Sroberto } 515106163Sroberto 516106163Sroberto if ((ed->bDescriptorType == UDESC_ENDPOINT) && 517106163Sroberto (ed->bLength >= sizeof(*ed)) && 518106163Sroberto (i == 1)) { 519106163Sroberto uint16_t temp; 520106163Sroberto 521106163Sroberto temp = UGETW(ed->wMaxPacketSize); 522106163Sroberto if (temp > wMaxPacketSize) { 523182007Sroberto wMaxPacketSize = temp; 524182007Sroberto alt_index = j; 525106163Sroberto } 526106163Sroberto } 527106163Sroberto } 528106163Sroberto 529106163Sroberto /* Set alt configuration on interface #1 only if we found it */ 530106163Sroberto if (wMaxPacketSize > 0 && 531106163Sroberto usb2_set_alt_interface_index(uaa->device, 1, alt_index)) { 532106163Sroberto UBT_ALERT(sc, "could not set alternate setting %d " \ 533106163Sroberto "for interface 1!\n", alt_index); 534106163Sroberto goto detach; 535106163Sroberto } 536106163Sroberto 537106163Sroberto /* Setup transfers for both interfaces */ 538106163Sroberto if (usb2_transfer_setup(uaa->device, iface_index, sc->sc_xfer, 539106163Sroberto ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { 540106163Sroberto UBT_ALERT(sc, "could not allocate transfers\n"); 541106163Sroberto goto detach; 542106163Sroberto } 543106163Sroberto 544106163Sroberto /* Claim all interfaces on the device */ 545106163Sroberto for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++) 546106163Sroberto usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 547106163Sroberto 548106163Sroberto return (0); /* success */ 549106163Sroberto 550182007Srobertodetach: 551106163Sroberto ubt_detach(dev); 552182007Sroberto 553182007Sroberto return (ENXIO); 554182007Sroberto} /* ubt_attach */ 555182007Sroberto 556182007Sroberto/* 557182007Sroberto * Detach the device. 558106163Sroberto * USB context. 559106163Sroberto */ 560106163Sroberto 561106163Srobertoint 562106163Srobertoubt_detach(device_t dev) 563182007Sroberto{ 564182007Sroberto struct ubt_softc *sc = device_get_softc(dev); 565182007Sroberto node_p node = sc->sc_node; 566182007Sroberto 567182007Sroberto /* Destroy Netgraph node */ 568106163Sroberto if (node != NULL) { 569106163Sroberto sc->sc_node = NULL; 570106163Sroberto NG_NODE_REALLY_DIE(node); 571182007Sroberto ng_rmnode_self(node); 572182007Sroberto } 573182007Sroberto 574182007Sroberto /* Make sure ubt_task in gone */ 575182007Sroberto taskqueue_drain(taskqueue_swi, &sc->sc_task); 576182007Sroberto 577182007Sroberto /* Free USB transfers, if any */ 578182007Sroberto usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); 579182007Sroberto 580182007Sroberto /* Destroy queues */ 581182007Sroberto UBT_NG_LOCK(sc); 582182007Sroberto NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); 583182007Sroberto NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); 584182007Sroberto NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); 585182007Sroberto UBT_NG_UNLOCK(sc); 586182007Sroberto 587182007Sroberto mtx_destroy(&sc->sc_if_mtx); 588182007Sroberto mtx_destroy(&sc->sc_ng_mtx); 589182007Sroberto 590182007Sroberto return (0); 591182007Sroberto} /* ubt_detach */ 592182007Sroberto 593182007Sroberto/* 594182007Sroberto * Called when outgoing control request (HCI command) has completed, i.e. 595182007Sroberto * HCI command was sent to the device. 596182007Sroberto * USB context. 597182007Sroberto */ 598182007Sroberto 599182007Srobertostatic void 600182007Srobertoubt_ctrl_write_callback(struct usb2_xfer *xfer) 601182007Sroberto{ 602182007Sroberto struct ubt_softc *sc = xfer->priv_sc; 603182007Sroberto struct usb2_device_request req; 604182007Sroberto struct mbuf *m; 605182007Sroberto 606106163Sroberto switch (USB_GET_STATE(xfer)) { 607106163Sroberto case USB_ST_TRANSFERRED: 608106163Sroberto UBT_INFO(sc, "sent %d bytes to control pipe\n", xfer->actlen); 609106163Sroberto UBT_STAT_BYTES_SENT(sc, xfer->actlen); 610182007Sroberto UBT_STAT_PCKTS_SENT(sc); 611182007Sroberto /* FALLTHROUGH */ 612182007Sroberto 613182007Sroberto case USB_ST_SETUP: 614182007Srobertosend_next: 615182007Sroberto /* Get next command mbuf, if any */ 616106163Sroberto UBT_NG_LOCK(sc); 617106163Sroberto NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 618106163Sroberto UBT_NG_UNLOCK(sc); 619106163Sroberto 620106163Sroberto if (m == NULL) { 621182007Sroberto UBT_INFO(sc, "HCI command queue is empty\n"); 622182007Sroberto break; /* transfer complete */ 623182007Sroberto } 624182007Sroberto 625182007Sroberto /* Initialize a USB control request and then schedule it */ 626106163Sroberto bzero(&req, sizeof(req)); 627106163Sroberto req.bmRequestType = UBT_HCI_REQUEST; 628106163Sroberto USETW(req.wLength, m->m_pkthdr.len); 629106163Sroberto 630106163Sroberto UBT_INFO(sc, "Sending control request, " \ 631106163Sroberto "bmRequestType=0x%02x, wLength=%d\n", 632106163Sroberto req.bmRequestType, UGETW(req.wLength)); 633106163Sroberto 634182007Sroberto usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); 635106163Sroberto usb2_m_copy_in(xfer->frbuffers + 1, 0, m, 0, m->m_pkthdr.len); 636106163Sroberto 637106163Sroberto xfer->frlengths[0] = sizeof(req); 638106163Sroberto xfer->frlengths[1] = m->m_pkthdr.len; 639106163Sroberto xfer->nframes = 2; 640106163Sroberto 641106163Sroberto NG_FREE_M(m); 642106163Sroberto 643106163Sroberto usb2_start_hardware(xfer); 644106163Sroberto break; 645106163Sroberto 646106163Sroberto default: /* Error */ 647106163Sroberto if (xfer->error != USB_ERR_CANCELLED) { 648106163Sroberto UBT_WARN(sc, "control transfer failed: %s\n", 649106163Sroberto usb2_errstr(xfer->error)); 650106163Sroberto 651106163Sroberto UBT_STAT_OERROR(sc); 652106163Sroberto goto send_next; 653106163Sroberto } 654106163Sroberto 655106163Sroberto /* transfer cancelled */ 656106163Sroberto break; 657182007Sroberto } 658182007Sroberto} /* ubt_ctrl_write_callback */ 659106163Sroberto 660106163Sroberto/* 661106163Sroberto * Called when incoming interrupt transfer (HCI event) has completed, i.e. 662106163Sroberto * HCI event was received from the device. 663106163Sroberto * USB context. 664106163Sroberto */ 665106163Sroberto 666106163Srobertostatic void 667106163Srobertoubt_intr_read_callback(struct usb2_xfer *xfer) 668106163Sroberto{ 669106163Sroberto struct ubt_softc *sc = xfer->priv_sc; 670106163Sroberto struct mbuf *m; 671106163Sroberto ng_hci_event_pkt_t *hdr; 672106163Sroberto 673106163Sroberto m = NULL; 674106163Sroberto 675106163Sroberto switch (USB_GET_STATE(xfer)) { 676106163Sroberto case USB_ST_TRANSFERRED: 677106163Sroberto /* Allocate a new mbuf */ 678106163Sroberto MGETHDR(m, M_DONTWAIT, MT_DATA); 679106163Sroberto if (m == NULL) { 680106163Sroberto UBT_STAT_IERROR(sc); 681106163Sroberto goto submit_next; 682106163Sroberto } 683106163Sroberto 684106163Sroberto MCLGET(m, M_DONTWAIT); 685106163Sroberto if (!(m->m_flags & M_EXT)) { 686182007Sroberto UBT_STAT_IERROR(sc); 687182007Sroberto goto submit_next; 688182007Sroberto } 689182007Sroberto 690182007Sroberto /* Add HCI packet type */ 691182007Sroberto *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; 692106163Sroberto m->m_pkthdr.len = m->m_len = 1; 693106163Sroberto 694106163Sroberto if (xfer->actlen > MCLBYTES - 1) 695106163Sroberto xfer->actlen = MCLBYTES - 1; 696106163Sroberto 697106163Sroberto usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, 698106163Sroberto xfer->actlen); 699182007Sroberto m->m_pkthdr.len += xfer->actlen; 700182007Sroberto m->m_len += xfer->actlen; 701182007Sroberto 702182007Sroberto UBT_INFO(sc, "got %d bytes from interrupt pipe\n", 703182007Sroberto xfer->actlen); 704182007Sroberto 705106163Sroberto /* Validate packet and send it up the stack */ 706106163Sroberto if (m->m_pkthdr.len < sizeof(*hdr)) { 707106163Sroberto UBT_INFO(sc, "HCI event packet is too short\n"); 708106163Sroberto 709106163Sroberto UBT_STAT_IERROR(sc); 710106163Sroberto goto submit_next; 711106163Sroberto } 712106163Sroberto 713106163Sroberto hdr = mtod(m, ng_hci_event_pkt_t *); 714106163Sroberto if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { 715106163Sroberto UBT_ERR(sc, "Invalid HCI event packet size, " \ 716106163Sroberto "length=%d, pktlen=%d\n", 717106163Sroberto hdr->length, m->m_pkthdr.len); 718106163Sroberto 719106163Sroberto UBT_STAT_IERROR(sc); 720106163Sroberto goto submit_next; 721106163Sroberto } 722106163Sroberto 723106163Sroberto UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ 724182007Sroberto "length=%d\n", m->m_pkthdr.len, hdr->length); 725182007Sroberto 726182007Sroberto UBT_STAT_PCKTS_RECV(sc); 727182007Sroberto UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 728182007Sroberto 729182007Sroberto ubt_fwd_mbuf_up(sc, &m); 730182007Sroberto /* m == NULL at this point */ 731182007Sroberto /* FALLTHROUGH */ 732182007Sroberto 733182007Sroberto case USB_ST_SETUP: 734182007Srobertosubmit_next: 735182007Sroberto NG_FREE_M(m); /* checks for m != NULL */ 736182007Sroberto 737182007Sroberto xfer->frlengths[0] = xfer->max_data_length; 738182007Sroberto usb2_start_hardware(xfer); 739182007Sroberto break; 740182007Sroberto 741182007Sroberto default: /* Error */ 742182007Sroberto if (xfer->error != USB_ERR_CANCELLED) { 743182007Sroberto UBT_WARN(sc, "interrupt transfer failed: %s\n", 744182007Sroberto usb2_errstr(xfer->error)); 745182007Sroberto 746182007Sroberto /* Try to clear stall first */ 747182007Sroberto xfer->flags.stall_pipe = 1; 748182007Sroberto goto submit_next; 749182007Sroberto } 750182007Sroberto /* transfer cancelled */ 751182007Sroberto break; 752182007Sroberto } 753182007Sroberto} /* ubt_intr_read_callback */ 754182007Sroberto 755182007Sroberto/* 756182007Sroberto * Called when incoming bulk transfer (ACL packet) has completed, i.e. 757182007Sroberto * ACL packet was received from the device. 758182007Sroberto * USB context. 759182007Sroberto */ 760182007Sroberto 761182007Srobertostatic void 762182007Srobertoubt_bulk_read_callback(struct usb2_xfer *xfer) 763182007Sroberto{ 764182007Sroberto struct ubt_softc *sc = xfer->priv_sc; 765182007Sroberto struct mbuf *m; 766182007Sroberto ng_hci_acldata_pkt_t *hdr; 767182007Sroberto uint16_t len; 768182007Sroberto 769182007Sroberto m = NULL; 770182007Sroberto 771182007Sroberto switch (USB_GET_STATE(xfer)) { 772182007Sroberto case USB_ST_TRANSFERRED: 773182007Sroberto /* Allocate new mbuf */ 774182007Sroberto MGETHDR(m, M_DONTWAIT, MT_DATA); 775182007Sroberto if (m == NULL) { 776182007Sroberto UBT_STAT_IERROR(sc); 777182007Sroberto goto submit_next; 778182007Sroberto } 779182007Sroberto 780182007Sroberto MCLGET(m, M_DONTWAIT); 781182007Sroberto if (!(m->m_flags & M_EXT)) { 782182007Sroberto UBT_STAT_IERROR(sc); 783182007Sroberto goto submit_next; 784182007Sroberto } 785182007Sroberto 786182007Sroberto /* Add HCI packet type */ 787182007Sroberto *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; 788182007Sroberto m->m_pkthdr.len = m->m_len = 1; 789182007Sroberto 790182007Sroberto if (xfer->actlen > MCLBYTES - 1) 791182007Sroberto xfer->actlen = MCLBYTES - 1; 792182007Sroberto 793182007Sroberto usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, 794182007Sroberto xfer->actlen); 795182007Sroberto m->m_pkthdr.len += xfer->actlen; 796182007Sroberto m->m_len += xfer->actlen; 797182007Sroberto 798182007Sroberto UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", 799182007Sroberto xfer->actlen); 800182007Sroberto 801182007Sroberto /* Validate packet and send it up the stack */ 802182007Sroberto if (m->m_pkthdr.len < sizeof(*hdr)) { 803182007Sroberto UBT_INFO(sc, "HCI ACL packet is too short\n"); 804182007Sroberto 805182007Sroberto UBT_STAT_IERROR(sc); 806182007Sroberto goto submit_next; 807182007Sroberto } 808182007Sroberto 809182007Sroberto hdr = mtod(m, ng_hci_acldata_pkt_t *); 810182007Sroberto len = le16toh(hdr->length); 811182007Sroberto if (len != (m->m_pkthdr.len - sizeof(*hdr))) { 812182007Sroberto UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ 813182007Sroberto "pktlen=%d\n", len, m->m_pkthdr.len); 814182007Sroberto 815182007Sroberto UBT_STAT_IERROR(sc); 816182007Sroberto goto submit_next; 817182007Sroberto } 818182007Sroberto 819182007Sroberto UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ 820182007Sroberto "length=%d\n", m->m_pkthdr.len, len); 821182007Sroberto 822182007Sroberto UBT_STAT_PCKTS_RECV(sc); 823182007Sroberto UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 824182007Sroberto 825182007Sroberto ubt_fwd_mbuf_up(sc, &m); 826182007Sroberto /* m == NULL at this point */ 827182007Sroberto /* FALLTHOUGH */ 828182007Sroberto 829182007Sroberto case USB_ST_SETUP: 830182007Srobertosubmit_next: 831182007Sroberto NG_FREE_M(m); /* checks for m != NULL */ 832182007Sroberto 833182007Sroberto xfer->frlengths[0] = xfer->max_data_length; 834182007Sroberto usb2_start_hardware(xfer); 835182007Sroberto break; 836182007Sroberto 837182007Sroberto default: /* Error */ 838182007Sroberto if (xfer->error != USB_ERR_CANCELLED) { 839182007Sroberto UBT_WARN(sc, "bulk-in transfer failed: %s\n", 840182007Sroberto usb2_errstr(xfer->error)); 841182007Sroberto 842182007Sroberto /* Try to clear stall first */ 843182007Sroberto xfer->flags.stall_pipe = 1; 844182007Sroberto goto submit_next; 845182007Sroberto } 846182007Sroberto /* transfer cancelled */ 847182007Sroberto break; 848182007Sroberto } 849182007Sroberto} /* ubt_bulk_read_callback */ 850182007Sroberto 851182007Sroberto/* 852182007Sroberto * Called when outgoing bulk transfer (ACL packet) has completed, i.e. 853182007Sroberto * ACL packet was sent to the device. 854182007Sroberto * USB context. 855182007Sroberto */ 856182007Sroberto 857182007Srobertostatic void 858182007Srobertoubt_bulk_write_callback(struct usb2_xfer *xfer) 859182007Sroberto{ 860182007Sroberto struct ubt_softc *sc = xfer->priv_sc; 861182007Sroberto struct mbuf *m; 862182007Sroberto 863182007Sroberto switch (USB_GET_STATE(xfer)) { 864182007Sroberto case USB_ST_TRANSFERRED: 865182007Sroberto UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", xfer->actlen); 866182007Sroberto UBT_STAT_BYTES_SENT(sc, xfer->actlen); 867182007Sroberto UBT_STAT_PCKTS_SENT(sc); 868182007Sroberto /* FALLTHROUGH */ 869106163Sroberto 870106163Sroberto case USB_ST_SETUP: 871106163Srobertosend_next: 872106163Sroberto /* Get next mbuf, if any */ 873106163Sroberto UBT_NG_LOCK(sc); 874106163Sroberto NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); 875106163Sroberto UBT_NG_UNLOCK(sc); 876106163Sroberto 877106163Sroberto if (m == NULL) { 878106163Sroberto UBT_INFO(sc, "ACL data queue is empty\n"); 879106163Sroberto break; /* transfer completed */ 880106163Sroberto } 881106163Sroberto 882106163Sroberto /* 883106163Sroberto * Copy ACL data frame back to a linear USB transfer buffer 884106163Sroberto * and schedule transfer 885106163Sroberto */ 886106163Sroberto 887106163Sroberto usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); 888106163Sroberto xfer->frlengths[0] = m->m_pkthdr.len; 889106163Sroberto 890106163Sroberto UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", 891106163Sroberto m->m_pkthdr.len); 892106163Sroberto 893106163Sroberto NG_FREE_M(m); 894106163Sroberto 895106163Sroberto usb2_start_hardware(xfer); 896106163Sroberto break; 897106163Sroberto 898106163Sroberto default: /* Error */ 899106163Sroberto if (xfer->error != USB_ERR_CANCELLED) { 900106163Sroberto UBT_WARN(sc, "bulk-out transfer failed: %s\n", 901106163Sroberto usb2_errstr(xfer->error)); 902106163Sroberto 903106163Sroberto UBT_STAT_OERROR(sc); 904106163Sroberto 905106163Sroberto /* try to clear stall first */ 906106163Sroberto xfer->flags.stall_pipe = 1; 907106163Sroberto goto send_next; 908106163Sroberto } 909106163Sroberto /* transfer cancelled */ 910182007Sroberto break; 911182007Sroberto } 912182007Sroberto} /* ubt_bulk_write_callback */ 913182007Sroberto 914106163Sroberto/* 915106163Sroberto * Called when incoming isoc transfer (SCO packet) has completed, i.e. 916106163Sroberto * SCO packet was received from the device. 917106163Sroberto * USB context. 918106163Sroberto */ 919106163Sroberto 920106163Srobertostatic void 921106163Srobertoubt_isoc_read_callback(struct usb2_xfer *xfer) 922106163Sroberto{ 923106163Sroberto struct ubt_softc *sc = xfer->priv_sc; 924106163Sroberto int n; 925106163Sroberto 926106163Sroberto switch (USB_GET_STATE(xfer)) { 927106163Sroberto case USB_ST_TRANSFERRED: 928106163Sroberto for (n = 0; n < xfer->nframes; n ++) 929106163Sroberto if (ubt_isoc_read_one_frame(xfer, n) < 0) 930106163Sroberto break; 931106163Sroberto /* FALLTHROUGH */ 932106163Sroberto 933106163Sroberto case USB_ST_SETUP: 934106163Srobertoread_next: 935182007Sroberto for (n = 0; n < xfer->nframes; n ++) 936182007Sroberto xfer->frlengths[n] = xfer->max_frame_size; 937182007Sroberto 938182007Sroberto usb2_start_hardware(xfer); 939182007Sroberto break; 940182007Sroberto 941106163Sroberto default: /* Error */ 942106163Sroberto if (xfer->error != USB_ERR_CANCELLED) { 943106163Sroberto UBT_STAT_IERROR(sc); 944106163Sroberto goto read_next; 945182007Sroberto } 946182007Sroberto 947182007Sroberto /* transfer cancelled */ 948182007Sroberto break; 949182007Sroberto } 950182007Sroberto} /* ubt_isoc_read_callback */ 951182007Sroberto 952182007Sroberto/* 953182007Sroberto * Helper function. Called from ubt_isoc_read_callback() to read 954182007Sroberto * SCO data from one frame. 955182007Sroberto * USB context. 956182007Sroberto */ 957106163Sroberto 958182007Srobertostatic int 959106163Srobertoubt_isoc_read_one_frame(struct usb2_xfer *xfer, int frame_no) 960106163Sroberto{ 961182007Sroberto struct ubt_softc *sc = xfer->priv_sc; 962182007Sroberto struct mbuf *m; 963182007Sroberto int len, want, got; 964106163Sroberto 965182007Sroberto /* Get existing SCO reassembly buffer */ 966106163Sroberto m = sc->sc_isoc_in_buffer; 967182007Sroberto sc->sc_isoc_in_buffer = NULL; 968182007Sroberto 969182007Sroberto /* While we have data in the frame */ 970182007Sroberto while ((len = xfer->frlengths[frame_no]) > 0) { 971106163Sroberto if (m == NULL) { 972106163Sroberto /* Start new reassembly buffer */ 973106163Sroberto MGETHDR(m, M_DONTWAIT, MT_DATA); 974106163Sroberto if (m == NULL) { 975106163Sroberto UBT_STAT_IERROR(sc); 976182007Sroberto return (-1); /* XXX out of sync! */ 977106163Sroberto } 978106163Sroberto 979182007Sroberto MCLGET(m, M_DONTWAIT); 980106163Sroberto if (!(m->m_flags & M_EXT)) { 981106163Sroberto UBT_STAT_IERROR(sc); 982182007Sroberto NG_FREE_M(m); 983182007Sroberto return (-1); /* XXX out of sync! */ 984106163Sroberto } 985182007Sroberto 986106163Sroberto /* Expect SCO header */ 987106163Sroberto *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; 988182007Sroberto m->m_pkthdr.len = m->m_len = got = 1; 989106163Sroberto want = sizeof(ng_hci_scodata_pkt_t); 990106163Sroberto } else { 991182007Sroberto /* 992182007Sroberto * Check if we have SCO header and if so 993182007Sroberto * adjust amount of data we want 994182007Sroberto */ 995182007Sroberto got = m->m_pkthdr.len; 996182007Sroberto want = sizeof(ng_hci_scodata_pkt_t); 997182007Sroberto 998182007Sroberto if (got >= want) 999182007Sroberto want += mtod(m, ng_hci_scodata_pkt_t *)->length; 1000182007Sroberto } 1001182007Sroberto 1002182007Sroberto /* Append frame data to the SCO reassembly buffer */ 1003182007Sroberto if (got + len > want) 1004106163Sroberto len = want - got; 1005106163Sroberto 1006106163Sroberto usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size, 1007106163Sroberto mtod(m, uint8_t *) + m->m_pkthdr.len, len); 1008106163Sroberto 1009106163Sroberto m->m_pkthdr.len += len; 1010106163Sroberto m->m_len += len; 1011106163Sroberto xfer->frlengths[frame_no] -= len; 1012 1013 /* Check if we got everything we wanted, if not - continue */ 1014 if (got != want) 1015 continue; 1016 1017 /* If we got here then we got complete SCO frame */ 1018 UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ 1019 "length=%d\n", m->m_pkthdr.len, 1020 mtod(m, ng_hci_scodata_pkt_t *)->length); 1021 1022 UBT_STAT_PCKTS_RECV(sc); 1023 UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); 1024 1025 ubt_fwd_mbuf_up(sc, &m); 1026 /* m == NULL at this point */ 1027 } 1028 1029 /* Put SCO reassembly buffer back */ 1030 sc->sc_isoc_in_buffer = m; 1031 1032 return (0); 1033} /* ubt_isoc_read_one_frame */ 1034 1035/* 1036 * Called when outgoing isoc transfer (SCO packet) has completed, i.e. 1037 * SCO packet was sent to the device. 1038 * USB context. 1039 */ 1040 1041static void 1042ubt_isoc_write_callback(struct usb2_xfer *xfer) 1043{ 1044 struct ubt_softc *sc = xfer->priv_sc; 1045 struct mbuf *m; 1046 int n, space, offset; 1047 1048 switch (USB_GET_STATE(xfer)) { 1049 case USB_ST_TRANSFERRED: 1050 UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", xfer->actlen); 1051 UBT_STAT_BYTES_SENT(sc, xfer->actlen); 1052 UBT_STAT_PCKTS_SENT(sc); 1053 /* FALLTHROUGH */ 1054 1055 case USB_ST_SETUP: 1056send_next: 1057 offset = 0; 1058 space = xfer->max_frame_size * xfer->nframes; 1059 m = NULL; 1060 1061 while (space > 0) { 1062 if (m == NULL) { 1063 UBT_NG_LOCK(sc); 1064 NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); 1065 UBT_NG_UNLOCK(sc); 1066 1067 if (m == NULL) 1068 break; 1069 } 1070 1071 n = min(space, m->m_pkthdr.len); 1072 if (n > 0) { 1073 usb2_m_copy_in(xfer->frbuffers, offset, m,0, n); 1074 m_adj(m, n); 1075 1076 offset += n; 1077 space -= n; 1078 } 1079 1080 if (m->m_pkthdr.len == 0) 1081 NG_FREE_M(m); /* sets m = NULL */ 1082 } 1083 1084 /* Put whatever is left from mbuf back on queue */ 1085 if (m != NULL) { 1086 UBT_NG_LOCK(sc); 1087 NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); 1088 UBT_NG_UNLOCK(sc); 1089 } 1090 1091 /* 1092 * Calculate sizes for isoc frames. 1093 * Note that offset could be 0 at this point (i.e. we have 1094 * nothing to send). That is fine, as we have isoc. transfers 1095 * going in both directions all the time. In this case it 1096 * would be just empty isoc. transfer. 1097 */ 1098 1099 for (n = 0; n < xfer->nframes; n ++) { 1100 xfer->frlengths[n] = min(offset, xfer->max_frame_size); 1101 offset -= xfer->frlengths[n]; 1102 } 1103 1104 usb2_start_hardware(xfer); 1105 break; 1106 1107 default: /* Error */ 1108 if (xfer->error != USB_ERR_CANCELLED) { 1109 UBT_STAT_OERROR(sc); 1110 goto send_next; 1111 } 1112 1113 /* transfer cancelled */ 1114 break; 1115 } 1116} 1117 1118/* 1119 * Utility function to forward provided mbuf upstream (i.e. up the stack). 1120 * Modifies value of the mbuf pointer (sets it to NULL). 1121 * Save to call from any context. 1122 */ 1123 1124static int 1125ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) 1126{ 1127 hook_p hook; 1128 int error; 1129 1130 /* 1131 * Close the race with Netgraph hook newhook/disconnect methods. 1132 * Save the hook pointer atomically. Two cases are possible: 1133 * 1134 * 1) The hook pointer is NULL. It means disconnect method got 1135 * there first. In this case we are done. 1136 * 1137 * 2) The hook pointer is not NULL. It means that hook pointer 1138 * could be either in valid or invalid (i.e. in the process 1139 * of disconnect) state. In any case grab an extra reference 1140 * to protect the hook pointer. 1141 * 1142 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as 1143 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). 1144 */ 1145 1146 UBT_NG_LOCK(sc); 1147 if ((hook = sc->sc_hook) != NULL) 1148 NG_HOOK_REF(hook); 1149 UBT_NG_UNLOCK(sc); 1150 1151 if (hook == NULL) { 1152 NG_FREE_M(*m); 1153 return (ENETDOWN); 1154 } 1155 1156 NG_SEND_DATA_ONLY(error, hook, *m); 1157 NG_HOOK_UNREF(hook); 1158 1159 if (error != 0) 1160 UBT_STAT_IERROR(sc); 1161 1162 return (error); 1163} /* ubt_fwd_mbuf_up */ 1164 1165/**************************************************************************** 1166 **************************************************************************** 1167 ** Glue 1168 **************************************************************************** 1169 ****************************************************************************/ 1170 1171/* 1172 * Schedule glue task. Should be called with sc_ng_mtx held. 1173 * Netgraph context. 1174 */ 1175 1176static void 1177ubt_task_schedule(ubt_softc_p sc, int action) 1178{ 1179 mtx_assert(&sc->sc_ng_mtx, MA_OWNED); 1180 1181 /* 1182 * Try to handle corner case when "start all" and "stop all" 1183 * actions can both be set before task is executed. 1184 * 1185 * The rules are 1186 * 1187 * sc_task_flags action new sc_task_flags 1188 * ------------------------------------------------------ 1189 * 0 start start 1190 * 0 stop stop 1191 * start start start 1192 * start stop stop 1193 * stop start stop|start 1194 * stop stop stop 1195 * stop|start start stop|start 1196 * stop|start stop stop 1197 */ 1198 1199 if (action != 0) { 1200 if ((action & UBT_FLAG_T_STOP_ALL) != 0) 1201 sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; 1202 1203 sc->sc_task_flags |= action; 1204 } 1205 1206 if (sc->sc_task_flags & UBT_FLAG_T_PENDING) 1207 return; 1208 1209 if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { 1210 sc->sc_task_flags |= UBT_FLAG_T_PENDING; 1211 return; 1212 } 1213 1214 /* XXX: i think this should never happen */ 1215} /* ubt_task_schedule */ 1216 1217/* 1218 * Glue task. Examines sc_task_flags and does things depending on it. 1219 * Taskqueue context. 1220 */ 1221 1222static void 1223ubt_task(void *context, int pending) 1224{ 1225 ubt_softc_p sc = context; 1226 int task_flags, i; 1227 1228 UBT_NG_LOCK(sc); 1229 task_flags = sc->sc_task_flags; 1230 sc->sc_task_flags = 0; 1231 UBT_NG_UNLOCK(sc); 1232 1233 /* 1234 * Stop all USB transfers synchronously. 1235 * Stop interface #0 and #1 transfers at the same time and in the 1236 * same loop. usb2_transfer_drain() will do appropriate locking. 1237 */ 1238 1239 if (task_flags & UBT_FLAG_T_STOP_ALL) 1240 for (i = 0; i < UBT_N_TRANSFER; i ++) 1241 usb2_transfer_drain(sc->sc_xfer[i]); 1242 1243 /* Start incoming interrupt and bulk, and all isoc. USB transfers */ 1244 if (task_flags & UBT_FLAG_T_START_ALL) { 1245 /* 1246 * Interface #0 1247 */ 1248 1249 mtx_lock(&sc->sc_if_mtx); 1250 1251 ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); 1252 ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); 1253 1254 /* 1255 * Interface #1 1256 * Start both read and write isoc. transfers by default. 1257 * Get them going all the time even if we have nothing 1258 * to send to avoid any delays. 1259 */ 1260 1261 ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); 1262 ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); 1263 ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); 1264 ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); 1265 1266 mtx_unlock(&sc->sc_if_mtx); 1267 } 1268 1269 /* Start outgoing control transfer */ 1270 if (task_flags & UBT_FLAG_T_START_CTRL) { 1271 mtx_lock(&sc->sc_if_mtx); 1272 ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); 1273 mtx_unlock(&sc->sc_if_mtx); 1274 } 1275 1276 /* Start outgoing bulk transfer */ 1277 if (task_flags & UBT_FLAG_T_START_BULK) { 1278 mtx_lock(&sc->sc_if_mtx); 1279 ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); 1280 mtx_unlock(&sc->sc_if_mtx); 1281 } 1282} /* ubt_task */ 1283 1284/**************************************************************************** 1285 **************************************************************************** 1286 ** Netgraph specific 1287 **************************************************************************** 1288 ****************************************************************************/ 1289 1290/* 1291 * Netgraph node constructor. Do not allow to create node of this type. 1292 * Netgraph context. 1293 */ 1294 1295static int 1296ng_ubt_constructor(node_p node) 1297{ 1298 return (EINVAL); 1299} /* ng_ubt_constructor */ 1300 1301/* 1302 * Netgraph node destructor. Destroy node only when device has been detached. 1303 * Netgraph context. 1304 */ 1305 1306static int 1307ng_ubt_shutdown(node_p node) 1308{ 1309 if (node->nd_flags & NGF_REALLY_DIE) { 1310 /* 1311 * We came here because the USB device is being 1312 * detached, so stop being persistant. 1313 */ 1314 NG_NODE_SET_PRIVATE(node, NULL); 1315 NG_NODE_UNREF(node); 1316 } else 1317 NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ 1318 1319 return (0); 1320} /* ng_ubt_shutdown */ 1321 1322/* 1323 * Create new hook. There can only be one. 1324 * Netgraph context. 1325 */ 1326 1327static int 1328ng_ubt_newhook(node_p node, hook_p hook, char const *name) 1329{ 1330 struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1331 1332 if (strcmp(name, NG_UBT_HOOK) != 0) 1333 return (EINVAL); 1334 1335 UBT_NG_LOCK(sc); 1336 if (sc->sc_hook != NULL) { 1337 UBT_NG_UNLOCK(sc); 1338 1339 return (EISCONN); 1340 } 1341 1342 sc->sc_hook = hook; 1343 UBT_NG_UNLOCK(sc); 1344 1345 return (0); 1346} /* ng_ubt_newhook */ 1347 1348/* 1349 * Connect hook. Start incoming USB transfers. 1350 * Netgraph context. 1351 */ 1352 1353static int 1354ng_ubt_connect(hook_p hook) 1355{ 1356 struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1357 1358 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 1359 1360 UBT_NG_LOCK(sc); 1361 ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); 1362 UBT_NG_UNLOCK(sc); 1363 1364 return (0); 1365} /* ng_ubt_connect */ 1366 1367/* 1368 * Disconnect hook. 1369 * Netgraph context. 1370 */ 1371 1372static int 1373ng_ubt_disconnect(hook_p hook) 1374{ 1375 struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1376 1377 UBT_NG_LOCK(sc); 1378 1379 if (hook != sc->sc_hook) { 1380 UBT_NG_UNLOCK(sc); 1381 1382 return (EINVAL); 1383 } 1384 1385 sc->sc_hook = NULL; 1386 1387 /* Kick off task to stop all USB xfers */ 1388 ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); 1389 1390 /* Drain queues */ 1391 NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); 1392 NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); 1393 NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); 1394 1395 UBT_NG_UNLOCK(sc); 1396 1397 return (0); 1398} /* ng_ubt_disconnect */ 1399 1400/* 1401 * Process control message. 1402 * Netgraph context. 1403 */ 1404 1405static int 1406ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) 1407{ 1408 struct ubt_softc *sc = NG_NODE_PRIVATE(node); 1409 struct ng_mesg *msg, *rsp = NULL; 1410 struct ng_bt_mbufq *q; 1411 int error = 0, queue, qlen; 1412 1413 NGI_GET_MSG(item, msg); 1414 1415 switch (msg->header.typecookie) { 1416 case NGM_GENERIC_COOKIE: 1417 switch (msg->header.cmd) { 1418 case NGM_TEXT_STATUS: 1419 NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 1420 if (rsp == NULL) { 1421 error = ENOMEM; 1422 break; 1423 } 1424 1425 snprintf(rsp->data, NG_TEXTRESPONSE, 1426 "Hook: %s\n" \ 1427 "Task flags: %#x\n" \ 1428 "Debug: %d\n" \ 1429 "CMD queue: [have:%d,max:%d]\n" \ 1430 "ACL queue: [have:%d,max:%d]\n" \ 1431 "SCO queue: [have:%d,max:%d]", 1432 (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", 1433 sc->sc_task_flags, 1434 sc->sc_debug, 1435 sc->sc_cmdq.len, 1436 sc->sc_cmdq.maxlen, 1437 sc->sc_aclq.len, 1438 sc->sc_aclq.maxlen, 1439 sc->sc_scoq.len, 1440 sc->sc_scoq.maxlen); 1441 break; 1442 1443 default: 1444 error = EINVAL; 1445 break; 1446 } 1447 break; 1448 1449 case NGM_UBT_COOKIE: 1450 switch (msg->header.cmd) { 1451 case NGM_UBT_NODE_SET_DEBUG: 1452 if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ 1453 error = EMSGSIZE; 1454 break; 1455 } 1456 1457 sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); 1458 break; 1459 1460 case NGM_UBT_NODE_GET_DEBUG: 1461 NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), 1462 M_NOWAIT); 1463 if (rsp == NULL) { 1464 error = ENOMEM; 1465 break; 1466 } 1467 1468 *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; 1469 break; 1470 1471 case NGM_UBT_NODE_SET_QLEN: 1472 if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1473 error = EMSGSIZE; 1474 break; 1475 } 1476 1477 queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1478 qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; 1479 1480 switch (queue) { 1481 case NGM_UBT_NODE_QUEUE_CMD: 1482 q = &sc->sc_cmdq; 1483 break; 1484 1485 case NGM_UBT_NODE_QUEUE_ACL: 1486 q = &sc->sc_aclq; 1487 break; 1488 1489 case NGM_UBT_NODE_QUEUE_SCO: 1490 q = &sc->sc_scoq; 1491 break; 1492 1493 default: 1494 error = EINVAL; 1495 goto done; 1496 /* NOT REACHED */ 1497 } 1498 1499 q->maxlen = qlen; 1500 break; 1501 1502 case NGM_UBT_NODE_GET_QLEN: 1503 if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { 1504 error = EMSGSIZE; 1505 break; 1506 } 1507 1508 queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; 1509 1510 switch (queue) { 1511 case NGM_UBT_NODE_QUEUE_CMD: 1512 q = &sc->sc_cmdq; 1513 break; 1514 1515 case NGM_UBT_NODE_QUEUE_ACL: 1516 q = &sc->sc_aclq; 1517 break; 1518 1519 case NGM_UBT_NODE_QUEUE_SCO: 1520 q = &sc->sc_scoq; 1521 break; 1522 1523 default: 1524 error = EINVAL; 1525 goto done; 1526 /* NOT REACHED */ 1527 } 1528 1529 NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), 1530 M_NOWAIT); 1531 if (rsp == NULL) { 1532 error = ENOMEM; 1533 break; 1534 } 1535 1536 ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; 1537 ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; 1538 break; 1539 1540 case NGM_UBT_NODE_GET_STAT: 1541 NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), 1542 M_NOWAIT); 1543 if (rsp == NULL) { 1544 error = ENOMEM; 1545 break; 1546 } 1547 1548 bcopy(&sc->sc_stat, rsp->data, 1549 sizeof(ng_ubt_node_stat_ep)); 1550 break; 1551 1552 case NGM_UBT_NODE_RESET_STAT: 1553 UBT_STAT_RESET(sc); 1554 break; 1555 1556 default: 1557 error = EINVAL; 1558 break; 1559 } 1560 break; 1561 1562 default: 1563 error = EINVAL; 1564 break; 1565 } 1566done: 1567 NG_RESPOND_MSG(error, node, item, rsp); 1568 NG_FREE_MSG(msg); 1569 1570 return (error); 1571} /* ng_ubt_rcvmsg */ 1572 1573/* 1574 * Process data. 1575 * Netgraph context. 1576 */ 1577 1578static int 1579ng_ubt_rcvdata(hook_p hook, item_p item) 1580{ 1581 struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1582 struct mbuf *m; 1583 struct ng_bt_mbufq *q; 1584 int action, error = 0; 1585 1586 if (hook != sc->sc_hook) { 1587 error = EINVAL; 1588 goto done; 1589 } 1590 1591 /* Deatch mbuf and get HCI frame type */ 1592 NGI_GET_M(item, m); 1593 1594 /* 1595 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, 1596 * 2 bytes connection handle and at least 1 byte of length. 1597 * Panic on data frame that has size smaller than 4 bytes (it 1598 * should not happen) 1599 */ 1600 1601 if (m->m_pkthdr.len < 4) 1602 panic("HCI frame size is too small! pktlen=%d\n", 1603 m->m_pkthdr.len); 1604 1605 /* Process HCI frame */ 1606 switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ 1607 case NG_HCI_CMD_PKT: 1608 if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE) 1609 panic("HCI command frame size is too big! " \ 1610 "buffer size=%zd, packet len=%d\n", 1611 UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); 1612 1613 q = &sc->sc_cmdq; 1614 action = UBT_FLAG_T_START_CTRL; 1615 break; 1616 1617 case NG_HCI_ACL_DATA_PKT: 1618 if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) 1619 panic("ACL data frame size is too big! " \ 1620 "buffer size=%d, packet len=%d\n", 1621 UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); 1622 1623 q = &sc->sc_aclq; 1624 action = UBT_FLAG_T_START_BULK; 1625 break; 1626 1627 case NG_HCI_SCO_DATA_PKT: 1628 q = &sc->sc_scoq; 1629 action = 0; 1630 break; 1631 1632 default: 1633 UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ 1634 "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len); 1635 1636 NG_FREE_M(m); 1637 error = EINVAL; 1638 goto done; 1639 /* NOT REACHED */ 1640 } 1641 1642 UBT_NG_LOCK(sc); 1643 if (NG_BT_MBUFQ_FULL(q)) { 1644 NG_BT_MBUFQ_DROP(q); 1645 UBT_NG_UNLOCK(sc); 1646 1647 UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", 1648 *mtod(m, uint8_t *), m->m_pkthdr.len); 1649 1650 NG_FREE_M(m); 1651 } else { 1652 /* Loose HCI packet type, enqueue mbuf and kick off task */ 1653 m_adj(m, sizeof(uint8_t)); 1654 NG_BT_MBUFQ_ENQUEUE(q, m); 1655 ubt_task_schedule(sc, action); 1656 UBT_NG_UNLOCK(sc); 1657 } 1658done: 1659 NG_FREE_ITEM(item); 1660 1661 return (error); 1662} /* ng_ubt_rcvdata */ 1663 1664/**************************************************************************** 1665 **************************************************************************** 1666 ** Module 1667 **************************************************************************** 1668 ****************************************************************************/ 1669 1670/* 1671 * Load/Unload the driver module 1672 */ 1673 1674static int 1675ubt_modevent(module_t mod, int event, void *data) 1676{ 1677 int error; 1678 1679 switch (event) { 1680 case MOD_LOAD: 1681 error = ng_newtype(&typestruct); 1682 if (error != 0) 1683 printf("%s: Could not register Netgraph node type, " \ 1684 "error=%d\n", NG_UBT_NODE_TYPE, error); 1685 break; 1686 1687 case MOD_UNLOAD: 1688 error = ng_rmtype(&typestruct); 1689 break; 1690 1691 default: 1692 error = EOPNOTSUPP; 1693 break; 1694 } 1695 1696 return (error); 1697} /* ubt_modevent */ 1698 1699static devclass_t ubt_devclass; 1700 1701static device_method_t ubt_methods[] = 1702{ 1703 DEVMETHOD(device_probe, ubt_probe), 1704 DEVMETHOD(device_attach, ubt_attach), 1705 DEVMETHOD(device_detach, ubt_detach), 1706 { 0, 0 } 1707}; 1708 1709static driver_t ubt_driver = 1710{ 1711 .name = "ubt", 1712 .methods = ubt_methods, 1713 .size = sizeof(struct ubt_softc), 1714}; 1715 1716DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0); 1717MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); 1718MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 1719MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 1720MODULE_DEPEND(ng_ubt, usb, 1, 1, 1); 1721 1722