Path: blob/main/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
35065 views
/*1* ng_ubt.c2*/34/*-5* SPDX-License-Identifier: BSD-2-Clause6*7* Copyright (c) 2001-2009 Maksim Yevmenkin <[email protected]>8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*31* $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $32*/3334/*35* NOTE: ng_ubt2 driver has a split personality. On one side it is36* a USB device driver and on the other it is a Netgraph node. This37* driver will *NOT* create traditional /dev/ enties, only Netgraph38* node.39*40* NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)41*42* 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used43* by USB for any USB request going over device's interface #0 and #1,44* i.e. interrupt, control, bulk and isoc. transfers.45*46* 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph47* and Taskqueue) data, such as outgoing mbuf queues, task flags and hook48* pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,49* think of it as a spin lock.50*51* NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.52*53* 1) USB context. This is where all the USB related stuff happens. All54* callbacks run in this context. All callbacks are called (by USB) with55* appropriate interface lock held. It is (generally) allowed to grab56* any additional locks.57*58* 2) Netgraph context. This is where all the Netgraph related stuff happens.59* Since we mark node as WRITER, the Netgraph node will be "locked" (from60* Netgraph point of view). Any variable that is only modified from the61* Netgraph context does not require any additional locking. It is generally62* *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*63* grab any lock in the Netgraph context that could cause de-scheduling of64* the Netgraph thread for significant amount of time. In fact, the only65* lock that is allowed in the Netgraph context is the sc_ng_mtx lock.66* Also make sure that any code that is called from the Netgraph context67* follows the rule above.68*69* 3) Taskqueue context. This is where ubt_task runs. Since we are generally70* NOT allowed to grab any lock that could cause de-scheduling in the71* Netgraph context, and, USB requires us to grab interface lock before72* doing things with transfers, it is safer to transition from the Netgraph73* context to the Taskqueue context before we can call into USB subsystem.74*75* So, to put everything together, the rules are as follows.76* It is OK to call from the USB context or the Taskqueue context into77* the Netgraph context (i.e. call NG_SEND_xxx functions). In other words78* it is allowed to call into the Netgraph context with locks held.79* Is it *NOT* OK to call from the Netgraph context into the USB context,80* because USB requires us to grab interface locks, and, it is safer to81* avoid it. So, to make things safer we set task flags to indicate which82* actions we want to perform and schedule ubt_task which would run in the83* Taskqueue context.84* Is is OK to call from the Taskqueue context into the USB context,85* and, ubt_task does just that (i.e. grabs appropriate interface locks86* before calling into USB).87* Access to the outgoing queues, task flags and hook pointer is88* controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,89* sc_ng_mtx should really be a spin lock (and it is very likely to an90* equivalent of spin lock due to adaptive nature of FreeBSD mutexes).91* All USB callbacks accept softc pointer as a private data. USB ensures92* that this pointer is valid.93*/9495#include <sys/stdint.h>96#include <sys/stddef.h>97#include <sys/param.h>98#include <sys/queue.h>99#include <sys/types.h>100#include <sys/systm.h>101#include <sys/kernel.h>102#include <sys/bus.h>103#include <sys/module.h>104#include <sys/lock.h>105#include <sys/mutex.h>106#include <sys/condvar.h>107#include <sys/sysctl.h>108#include <sys/sx.h>109#include <sys/unistd.h>110#include <sys/callout.h>111#include <sys/malloc.h>112#include <sys/priv.h>113114#include "usbdevs.h"115#include <dev/usb/usb.h>116#include <dev/usb/usbdi.h>117#include <dev/usb/usbdi_util.h>118119#define USB_DEBUG_VAR usb_debug120#include <dev/usb/usb_debug.h>121#include <dev/usb/usb_busdma.h>122123#include <sys/mbuf.h>124#include <sys/taskqueue.h>125126#include <netgraph/ng_message.h>127#include <netgraph/netgraph.h>128#include <netgraph/ng_parse.h>129#include <netgraph/bluetooth/include/ng_bluetooth.h>130#include <netgraph/bluetooth/include/ng_hci.h>131#include <netgraph/bluetooth/include/ng_ubt.h>132#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>133134static int ubt_modevent(module_t, int, void *);135static device_probe_t ubt_probe;136static device_attach_t ubt_attach;137static device_detach_t ubt_detach;138139static void ubt_task_schedule(ubt_softc_p, int);140static task_fn_t ubt_task;141142#define ubt_xfer_start(sc, i) usbd_transfer_start((sc)->sc_xfer[(i)])143144/* Netgraph methods */145static ng_constructor_t ng_ubt_constructor;146static ng_shutdown_t ng_ubt_shutdown;147static ng_newhook_t ng_ubt_newhook;148static ng_connect_t ng_ubt_connect;149static ng_disconnect_t ng_ubt_disconnect;150static ng_rcvmsg_t ng_ubt_rcvmsg;151static ng_rcvdata_t ng_ubt_rcvdata;152153static int ng_usb_isoc_enable = 1;154155SYSCTL_INT(_net_bluetooth, OID_AUTO, usb_isoc_enable, CTLFLAG_RWTUN | CTLFLAG_MPSAFE,156&ng_usb_isoc_enable, 0, "enable isochronous transfers");157158/* Queue length */159static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] =160{161{ "queue", &ng_parse_int32_type, },162{ "qlen", &ng_parse_int32_type, },163{ NULL, }164};165static const struct ng_parse_type ng_ubt_node_qlen_type =166{167&ng_parse_struct_type,168&ng_ubt_node_qlen_type_fields169};170171/* Stat info */172static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] =173{174{ "pckts_recv", &ng_parse_uint32_type, },175{ "bytes_recv", &ng_parse_uint32_type, },176{ "pckts_sent", &ng_parse_uint32_type, },177{ "bytes_sent", &ng_parse_uint32_type, },178{ "oerrors", &ng_parse_uint32_type, },179{ "ierrors", &ng_parse_uint32_type, },180{ NULL, }181};182static const struct ng_parse_type ng_ubt_node_stat_type =183{184&ng_parse_struct_type,185&ng_ubt_node_stat_type_fields186};187188/* Netgraph node command list */189static const struct ng_cmdlist ng_ubt_cmdlist[] =190{191{192NGM_UBT_COOKIE,193NGM_UBT_NODE_SET_DEBUG,194"set_debug",195&ng_parse_uint16_type,196NULL197},198{199NGM_UBT_COOKIE,200NGM_UBT_NODE_GET_DEBUG,201"get_debug",202NULL,203&ng_parse_uint16_type204},205{206NGM_UBT_COOKIE,207NGM_UBT_NODE_SET_QLEN,208"set_qlen",209&ng_ubt_node_qlen_type,210NULL211},212{213NGM_UBT_COOKIE,214NGM_UBT_NODE_GET_QLEN,215"get_qlen",216&ng_ubt_node_qlen_type,217&ng_ubt_node_qlen_type218},219{220NGM_UBT_COOKIE,221NGM_UBT_NODE_GET_STAT,222"get_stat",223NULL,224&ng_ubt_node_stat_type225},226{227NGM_UBT_COOKIE,228NGM_UBT_NODE_RESET_STAT,229"reset_stat",230NULL,231NULL232},233{ 0, }234};235236/* Netgraph node type */237static struct ng_type typestruct =238{239.version = NG_ABI_VERSION,240.name = NG_UBT_NODE_TYPE,241.constructor = ng_ubt_constructor,242.rcvmsg = ng_ubt_rcvmsg,243.shutdown = ng_ubt_shutdown,244.newhook = ng_ubt_newhook,245.connect = ng_ubt_connect,246.rcvdata = ng_ubt_rcvdata,247.disconnect = ng_ubt_disconnect,248.cmdlist = ng_ubt_cmdlist249};250251/****************************************************************************252****************************************************************************253** USB specific254****************************************************************************255****************************************************************************/256257/* USB methods */258static usb_callback_t ubt_probe_intr_callback;259static usb_callback_t ubt_ctrl_write_callback;260static usb_callback_t ubt_intr_read_callback;261static usb_callback_t ubt_bulk_read_callback;262static usb_callback_t ubt_bulk_write_callback;263static usb_callback_t ubt_isoc_read_callback;264static usb_callback_t ubt_isoc_write_callback;265266static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);267static int ubt_isoc_read_one_frame(struct usb_xfer *, int);268269/*270* USB config271*272* The following desribes usb transfers that could be submitted on USB device.273*274* Interface 0 on the USB device must present the following endpoints275* 1) Interrupt endpoint to receive HCI events276* 2) Bulk IN endpoint to receive ACL data277* 3) Bulk OUT endpoint to send ACL data278*279* Interface 1 on the USB device must present the following endpoints280* 1) Isochronous IN endpoint to receive SCO data281* 2) Isochronous OUT endpoint to send SCO data282*/283284static const struct usb_config ubt_config[UBT_N_TRANSFER] =285{286/*287* Interface #0288*/289290/* Outgoing bulk transfer - ACL packets */291[UBT_IF_0_BULK_DT_WR] = {292.type = UE_BULK,293.endpoint = UE_ADDR_ANY,294.direction = UE_DIR_OUT,295.if_index = 0,296.bufsize = UBT_BULK_WRITE_BUFFER_SIZE,297.flags = { .pipe_bof = 1, .force_short_xfer = 1, },298.callback = &ubt_bulk_write_callback,299},300/* Incoming bulk transfer - ACL packets */301[UBT_IF_0_BULK_DT_RD] = {302.type = UE_BULK,303.endpoint = UE_ADDR_ANY,304.direction = UE_DIR_IN,305.if_index = 0,306.bufsize = UBT_BULK_READ_BUFFER_SIZE,307.flags = { .pipe_bof = 1, .short_xfer_ok = 1, },308.callback = &ubt_bulk_read_callback,309},310/* Incoming interrupt transfer - HCI events */311[UBT_IF_0_INTR_DT_RD] = {312.type = UE_INTERRUPT,313.endpoint = UE_ADDR_ANY,314.direction = UE_DIR_IN,315.if_index = 0,316.flags = { .pipe_bof = 1, .short_xfer_ok = 1, },317.bufsize = UBT_INTR_BUFFER_SIZE,318.callback = &ubt_intr_read_callback,319},320/* Outgoing control transfer - HCI commands */321[UBT_IF_0_CTRL_DT_WR] = {322.type = UE_CONTROL,323.endpoint = 0x00, /* control pipe */324.direction = UE_DIR_ANY,325.if_index = 0,326.bufsize = UBT_CTRL_BUFFER_SIZE,327.callback = &ubt_ctrl_write_callback,328.timeout = 5000, /* 5 seconds */329},330331/*332* Interface #1333*/334335/* Incoming isochronous transfer #1 - SCO packets */336[UBT_IF_1_ISOC_DT_RD1] = {337.type = UE_ISOCHRONOUS,338.endpoint = UE_ADDR_ANY,339.direction = UE_DIR_IN,340.if_index = 1,341.bufsize = 0, /* use "wMaxPacketSize * frames" */342.frames = UBT_ISOC_NFRAMES,343.flags = { .short_xfer_ok = 1, },344.callback = &ubt_isoc_read_callback,345},346/* Incoming isochronous transfer #2 - SCO packets */347[UBT_IF_1_ISOC_DT_RD2] = {348.type = UE_ISOCHRONOUS,349.endpoint = UE_ADDR_ANY,350.direction = UE_DIR_IN,351.if_index = 1,352.bufsize = 0, /* use "wMaxPacketSize * frames" */353.frames = UBT_ISOC_NFRAMES,354.flags = { .short_xfer_ok = 1, },355.callback = &ubt_isoc_read_callback,356},357/* Outgoing isochronous transfer #1 - SCO packets */358[UBT_IF_1_ISOC_DT_WR1] = {359.type = UE_ISOCHRONOUS,360.endpoint = UE_ADDR_ANY,361.direction = UE_DIR_OUT,362.if_index = 1,363.bufsize = 0, /* use "wMaxPacketSize * frames" */364.frames = UBT_ISOC_NFRAMES,365.flags = { .short_xfer_ok = 1, },366.callback = &ubt_isoc_write_callback,367},368/* Outgoing isochronous transfer #2 - SCO packets */369[UBT_IF_1_ISOC_DT_WR2] = {370.type = UE_ISOCHRONOUS,371.endpoint = UE_ADDR_ANY,372.direction = UE_DIR_OUT,373.if_index = 1,374.bufsize = 0, /* use "wMaxPacketSize * frames" */375.frames = UBT_ISOC_NFRAMES,376.flags = { .short_xfer_ok = 1, },377.callback = &ubt_isoc_write_callback,378},379};380381/*382* If for some reason device should not be attached then put383* VendorID/ProductID pair into the list below. The format is384* as follows:385*386* { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },387*388* where VENDOR_ID and PRODUCT_ID are hex numbers.389*/390391static const STRUCT_USB_HOST_ID ubt_ignore_devs[] =392{393/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */394{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },395396/* Atheros 3011 with sflash firmware */397{ USB_VPI(0x0cf3, 0x3002, 0) },398{ USB_VPI(0x0cf3, 0xe019, 0) },399{ USB_VPI(0x13d3, 0x3304, 0) },400{ USB_VPI(0x0930, 0x0215, 0) },401{ USB_VPI(0x0489, 0xe03d, 0) },402{ USB_VPI(0x0489, 0xe027, 0) },403404/* Atheros AR9285 Malbec with sflash firmware */405{ USB_VPI(0x03f0, 0x311d, 0) },406407/* Atheros 3012 with sflash firmware */408{ USB_VPI(0x0cf3, 0x3004, 0), USB_DEV_BCD_LTEQ(1) },409{ USB_VPI(0x0cf3, 0x311d, 0), USB_DEV_BCD_LTEQ(1) },410{ USB_VPI(0x13d3, 0x3375, 0), USB_DEV_BCD_LTEQ(1) },411{ USB_VPI(0x04ca, 0x3005, 0), USB_DEV_BCD_LTEQ(1) },412{ USB_VPI(0x04ca, 0x3006, 0), USB_DEV_BCD_LTEQ(1) },413{ USB_VPI(0x04ca, 0x3008, 0), USB_DEV_BCD_LTEQ(1) },414{ USB_VPI(0x13d3, 0x3362, 0), USB_DEV_BCD_LTEQ(1) },415{ USB_VPI(0x0cf3, 0xe004, 0), USB_DEV_BCD_LTEQ(1) },416{ USB_VPI(0x0930, 0x0219, 0), USB_DEV_BCD_LTEQ(1) },417{ USB_VPI(0x0489, 0xe057, 0), USB_DEV_BCD_LTEQ(1) },418{ USB_VPI(0x13d3, 0x3393, 0), USB_DEV_BCD_LTEQ(1) },419{ USB_VPI(0x0489, 0xe04e, 0), USB_DEV_BCD_LTEQ(1) },420{ USB_VPI(0x0489, 0xe056, 0), USB_DEV_BCD_LTEQ(1) },421422/* Atheros AR5BBU12 with sflash firmware */423{ USB_VPI(0x0489, 0xe02c, 0), USB_DEV_BCD_LTEQ(1) },424425/* Atheros AR5BBU12 with sflash firmware */426{ USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) },427{ USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) },428429/* Intel Wireless controllers are handled in ng_ubt_intel.c */430{ USB_VPI(USB_VENDOR_INTEL2, 0x07dc, 0) },431{ USB_VPI(USB_VENDOR_INTEL2, 0x0a2a, 0) },432{ USB_VPI(USB_VENDOR_INTEL2, 0x0aa7, 0) },433{ USB_VPI(USB_VENDOR_INTEL2, 0x0a2b, 0) },434{ USB_VPI(USB_VENDOR_INTEL2, 0x0aaa, 0) },435{ USB_VPI(USB_VENDOR_INTEL2, 0x0025, 0) },436{ USB_VPI(USB_VENDOR_INTEL2, 0x0026, 0) },437{ USB_VPI(USB_VENDOR_INTEL2, 0x0029, 0) },438439/*440* Some Intel controllers are not yet supported by ng_ubt_intel and441* should be ignored.442*/443{ USB_VPI(USB_VENDOR_INTEL2, 0x0032, 0) },444{ USB_VPI(USB_VENDOR_INTEL2, 0x0033, 0) },445446/* MediaTek MT7925 */447{ USB_VPI(USB_VENDOR_AZUREWAVE, 0x3602, 0) },448{ USB_VPI(USB_VENDOR_AZUREWAVE, 0x3604, 0) },449};450451/* List of supported bluetooth devices */452static const STRUCT_USB_HOST_ID ubt_devs[] =453{454/* Generic Bluetooth class devices */455{ USB_IFACE_CLASS(UDCLASS_WIRELESS),456USB_IFACE_SUBCLASS(UDSUBCLASS_RF),457USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },458459/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */460{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },461462/* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */463{ USB_VENDOR(USB_VENDOR_BROADCOM),464USB_IFACE_CLASS(UICLASS_VENDOR),465USB_IFACE_SUBCLASS(UDSUBCLASS_RF),466USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },467468/* Apple-specific (Broadcom) devices */469{ USB_VENDOR(USB_VENDOR_APPLE),470USB_IFACE_CLASS(UICLASS_VENDOR),471USB_IFACE_SUBCLASS(UDSUBCLASS_RF),472USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },473474/* Foxconn - Hon Hai */475{ USB_VENDOR(USB_VENDOR_FOXCONN),476USB_IFACE_CLASS(UICLASS_VENDOR),477USB_IFACE_SUBCLASS(UDSUBCLASS_RF),478USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },479480/* MediaTek MT76x0E */481{ USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },482483/* Broadcom SoftSailing reporting vendor specific */484{ USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },485486/* Apple MacBookPro 7,1 */487{ USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },488489/* Apple iMac11,1 */490{ USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },491492/* Apple MacBookPro6,2 */493{ USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },494495/* Apple MacBookAir3,1, MacBookAir3,2 */496{ USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },497498/* Apple MacBookAir4,1 */499{ USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },500501/* MacBookAir6,1 */502{ USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },503504/* Apple MacBookPro8,2 */505{ USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },506507/* Apple MacMini5,1 */508{ USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },509510/* Bluetooth Ultraport Module from IBM */511{ USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },512513/* ALPS Modules with non-standard ID */514{ USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },515{ USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },516517{ USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },518519/* Canyon CN-BTU1 with HID interfaces */520{ USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },521522/* Broadcom BCM20702A0 */523{ USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },524{ USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },525{ USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },526{ USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },527{ USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },528{ USB_VPI(USB_VENDOR_BELKIN, 0x065a, 0) },529};530531/*532* Does a synchronous (waits for completion event) execution of HCI command.533* Size of both command and response buffers are passed in length field of534* corresponding structures in "Parameter Total Length" format i.e.535* not including HCI packet headers.536* Expected event code must be placed into "Event code" of the response buffer.537*538* Must not be used after USB transfers have been configured in attach routine.539*/540541usb_error_t542ubt_do_hci_request(struct usb_device *udev, struct ubt_hci_cmd *cmd,543void *evt, usb_timeout_t timeout)544{545static const struct usb_config ubt_probe_config = {546.type = UE_INTERRUPT,547.endpoint = UE_ADDR_ANY,548.direction = UE_DIR_IN,549.flags = { .pipe_bof = 1, .short_xfer_ok = 1 },550.bufsize = UBT_INTR_BUFFER_SIZE,551.callback = &ubt_probe_intr_callback,552};553struct usb_device_request req;554struct usb_xfer *xfer[1];555struct mtx mtx;556usb_error_t error = USB_ERR_NORMAL_COMPLETION;557uint8_t iface_index = 0;558559/* Initialize a USB control request and then do it */560bzero(&req, sizeof(req));561req.bmRequestType = UBT_HCI_REQUEST;562req.wIndex[0] = iface_index;563USETW(req.wLength, UBT_HCI_CMD_SIZE(cmd));564565error = usbd_do_request(udev, NULL, &req, cmd);566if (error != USB_ERR_NORMAL_COMPLETION) {567printf("ng_ubt: usbd_do_request error=%s\n",568usbd_errstr(error));569return (error);570}571572if (evt == NULL)573return (USB_ERR_NORMAL_COMPLETION);574575/* Save operation code if we expect completion event in response */576if(((struct ubt_hci_event *)evt)->header.event ==577NG_HCI_EVENT_COMMAND_COMPL)578((struct ubt_hci_event_command_compl *)evt)->opcode =579cmd->opcode;580581/* Initialize INTR endpoint xfer and wait for response */582mtx_init(&mtx, "ubt pb", NULL, MTX_DEF | MTX_NEW);583584error = usbd_transfer_setup(udev, &iface_index, xfer,585&ubt_probe_config, 1, evt, &mtx);586if (error == USB_ERR_NORMAL_COMPLETION) {587mtx_lock(&mtx);588usbd_transfer_start(*xfer);589590if (msleep_sbt(evt, &mtx, 0, "ubt pb", SBT_1MS * timeout,5910, C_HARDCLOCK) == EWOULDBLOCK) {592printf("ng_ubt: HCI command 0x%04x timed out\n",593le16toh(cmd->opcode));594error = USB_ERR_TIMEOUT;595}596597usbd_transfer_stop(*xfer);598mtx_unlock(&mtx);599600usbd_transfer_unsetup(xfer, 1);601} else602printf("ng_ubt: usbd_transfer_setup error=%s\n",603usbd_errstr(error));604605mtx_destroy(&mtx);606607return (error);608}609610/*611* Probe for a USB Bluetooth device.612* USB context.613*/614615static int616ubt_probe(device_t dev)617{618struct usb_attach_arg *uaa = device_get_ivars(dev);619const struct usb_device_id *id;620621if (uaa->usb_mode != USB_MODE_HOST)622return (ENXIO);623624if (usbd_lookup_id_by_uaa(ubt_ignore_devs,625sizeof(ubt_ignore_devs), uaa) == 0)626return (ENXIO);627if (usbd_lookup_id_by_uaa(ubt_rtl_devs,628ubt_rtl_devs_sizeof, uaa) == 0)629return (ENXIO);630631id = usbd_lookup_id_by_info(ubt_devs,632sizeof(ubt_devs), &uaa->info);633if (id == NULL)634return (ENXIO);635636if (uaa->info.bIfaceIndex != 0) {637/* make sure we are matching the interface */638if (id->match_flag_int_class &&639id->match_flag_int_subclass &&640id->match_flag_int_protocol)641return (BUS_PROBE_GENERIC);642else643return (ENXIO);644} else {645return (BUS_PROBE_GENERIC);646}647} /* ubt_probe */648649/*650* Attach the device.651* USB context.652*/653654static int655ubt_attach(device_t dev)656{657struct usb_attach_arg *uaa = device_get_ivars(dev);658struct ubt_softc *sc = device_get_softc(dev);659struct usb_endpoint_descriptor *ed;660struct usb_interface_descriptor *id;661struct usb_interface *iface[2];662uint32_t wMaxPacketSize;663uint8_t alt_index, i, j;664uint8_t iface_index[2];665666device_set_usb_desc(dev);667668iface_index[0] = uaa->info.bIfaceIndex;669iface_index[1] = uaa->info.bIfaceIndex + 1;670671iface[0] = usbd_get_iface(uaa->device, iface_index[0]);672iface[1] = usbd_get_iface(uaa->device, iface_index[1]);673674sc->sc_dev = dev;675sc->sc_debug = NG_UBT_WARN_LEVEL;676677/*678* Sanity checks.679*/680681if (iface[0] == NULL || iface[1] == NULL ||682iface[0]->idesc == NULL || iface[1]->idesc == NULL) {683UBT_ALERT(sc, "could not get two interfaces\n");684return (ENXIO);685}686687/*688* Create Netgraph node689*/690691if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {692UBT_ALERT(sc, "could not create Netgraph node\n");693return (ENXIO);694}695696/* Name Netgraph node */697if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {698UBT_ALERT(sc, "could not name Netgraph node\n");699NG_NODE_UNREF(sc->sc_node);700return (ENXIO);701}702NG_NODE_SET_PRIVATE(sc->sc_node, sc);703NG_NODE_FORCE_WRITER(sc->sc_node);704705/*706* Initialize device softc structure707*/708709/* initialize locks */710mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);711mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);712713/* initialize packet queues */714NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);715NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);716NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);717718/* initialize glue task */719TASK_INIT(&sc->sc_task, 0, ubt_task, sc);720721/*722* Configure Bluetooth USB device. Discover all required USB723* interfaces and endpoints.724*725* USB device must present two interfaces:726* 1) Interface 0 that has 3 endpoints727* 1) Interrupt endpoint to receive HCI events728* 2) Bulk IN endpoint to receive ACL data729* 3) Bulk OUT endpoint to send ACL data730*731* 2) Interface 1 then has 2 endpoints732* 1) Isochronous IN endpoint to receive SCO data733* 2) Isochronous OUT endpoint to send SCO data734*735* Interface 1 (with isochronous endpoints) has several alternate736* configurations with different packet size.737*/738739/*740* For interface #1 search alternate settings, and find741* the descriptor with the largest wMaxPacketSize742*/743744wMaxPacketSize = 0;745alt_index = 0;746i = 0;747j = 0;748ed = NULL;749750/*751* Search through all the descriptors looking for the largest752* packet size:753*/754while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(755usbd_get_config_descriptor(uaa->device),756(struct usb_descriptor *)ed))) {757if ((ed->bDescriptorType == UDESC_INTERFACE) &&758(ed->bLength >= sizeof(*id))) {759id = (struct usb_interface_descriptor *)ed;760i = (id->bInterfaceNumber == iface[1]->idesc->bInterfaceNumber);761j = id->bAlternateSetting;762}763764if ((ed->bDescriptorType == UDESC_ENDPOINT) &&765(ed->bLength >= sizeof(*ed)) &&766(i != 0)) {767uint32_t temp;768769temp = usbd_get_max_frame_length(770ed, NULL, usbd_get_speed(uaa->device));771if (temp > wMaxPacketSize) {772wMaxPacketSize = temp;773alt_index = j;774}775}776}777778/* Set alt configuration on interface #1 only if we found it */779if (wMaxPacketSize > 0 &&780usbd_set_alt_interface_index(uaa->device, iface_index[1], alt_index)) {781UBT_ALERT(sc, "could not set alternate setting %d " \782"for interface 1!\n", alt_index);783goto detach;784}785786/* Setup transfers for both interfaces */787if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, ubt_config,788ng_usb_isoc_enable ? UBT_N_TRANSFER : UBT_IF_1_ISOC_DT_RD1,789sc, &sc->sc_if_mtx)) {790UBT_ALERT(sc, "could not allocate transfers\n");791goto detach;792}793794/* Claim second interface belonging to the Bluetooth part */795usbd_set_parent_iface(uaa->device, iface_index[1], uaa->info.bIfaceIndex);796797return (0); /* success */798799detach:800ubt_detach(dev);801802return (ENXIO);803} /* ubt_attach */804805/*806* Detach the device.807* USB context.808*/809810int811ubt_detach(device_t dev)812{813struct ubt_softc *sc = device_get_softc(dev);814node_p node = sc->sc_node;815816/* Destroy Netgraph node */817if (node != NULL) {818sc->sc_node = NULL;819NG_NODE_REALLY_DIE(node);820ng_rmnode_self(node);821}822823/* Make sure ubt_task in gone */824taskqueue_drain(taskqueue_swi, &sc->sc_task);825826/* Free USB transfers, if any */827usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);828829/* Destroy queues */830UBT_NG_LOCK(sc);831NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);832NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);833NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);834UBT_NG_UNLOCK(sc);835836mtx_destroy(&sc->sc_if_mtx);837mtx_destroy(&sc->sc_ng_mtx);838839return (0);840} /* ubt_detach */841842/*843* Called when incoming interrupt transfer (HCI event) has completed, i.e.844* HCI event was received from the device during device probe stage.845* USB context.846*/847848static void849ubt_probe_intr_callback(struct usb_xfer *xfer, usb_error_t error)850{851struct ubt_hci_event *evt = usbd_xfer_softc(xfer);852struct usb_page_cache *pc;853int actlen;854struct ubt_hci_evhdr evhdr;855uint16_t opcode;856857usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);858859switch (USB_GET_STATE(xfer)) {860case USB_ST_TRANSFERRED:861if (actlen > UBT_HCI_EVENT_SIZE(evt))862actlen = UBT_HCI_EVENT_SIZE(evt);863if (actlen < sizeof(evhdr))864goto submit_next;865pc = usbd_xfer_get_frame(xfer, 0);866usbd_copy_out(pc, 0, &evhdr, sizeof(evhdr));867/* Check for expected event code */868if (evt->header.event != 0 &&869(evt->header.event != evhdr.event))870goto submit_next;871/* For completion events check operation code as well */872if (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL) {873if (actlen < sizeof(struct ubt_hci_event_command_compl))874goto submit_next;875usbd_copy_out(pc,876offsetof(struct ubt_hci_event_command_compl, opcode),877&opcode, sizeof(opcode));878if (opcode !=879((struct ubt_hci_event_command_compl *)evt)->opcode)880goto submit_next;881}882usbd_copy_out(pc, 0, evt, actlen);883/* OneShot mode */884wakeup(evt);885break;886887case USB_ST_SETUP:888submit_next:889usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));890usbd_transfer_submit(xfer);891break;892893default:894if (error != USB_ERR_CANCELLED) {895printf("ng_ubt: interrupt transfer failed: %s\n",896usbd_errstr(error));897/* Try clear stall first */898usbd_xfer_set_stall(xfer);899goto submit_next;900}901break;902}903} /* ubt_probe_intr_callback */904905/*906* Called when outgoing control request (HCI command) has completed, i.e.907* HCI command was sent to the device.908* USB context.909*/910911static void912ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)913{914struct ubt_softc *sc = usbd_xfer_softc(xfer);915struct usb_device_request req;916struct mbuf *m;917struct usb_page_cache *pc;918int actlen;919920usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);921922switch (USB_GET_STATE(xfer)) {923case USB_ST_TRANSFERRED:924UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);925UBT_STAT_BYTES_SENT(sc, actlen);926UBT_STAT_PCKTS_SENT(sc);927/* FALLTHROUGH */928929case USB_ST_SETUP:930send_next:931/* Get next command mbuf, if any */932UBT_NG_LOCK(sc);933NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);934UBT_NG_UNLOCK(sc);935936if (m == NULL) {937UBT_INFO(sc, "HCI command queue is empty\n");938break; /* transfer complete */939}940941/* Initialize a USB control request and then schedule it */942bzero(&req, sizeof(req));943req.bmRequestType = UBT_HCI_REQUEST;944USETW(req.wLength, m->m_pkthdr.len);945946UBT_INFO(sc, "Sending control request, " \947"bmRequestType=0x%02x, wLength=%d\n",948req.bmRequestType, UGETW(req.wLength));949950pc = usbd_xfer_get_frame(xfer, 0);951usbd_copy_in(pc, 0, &req, sizeof(req));952pc = usbd_xfer_get_frame(xfer, 1);953usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);954955usbd_xfer_set_frame_len(xfer, 0, sizeof(req));956usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);957usbd_xfer_set_frames(xfer, 2);958959NG_FREE_M(m);960961usbd_transfer_submit(xfer);962break;963964default: /* Error */965if (error != USB_ERR_CANCELLED) {966UBT_WARN(sc, "control transfer failed: %s\n",967usbd_errstr(error));968969UBT_STAT_OERROR(sc);970goto send_next;971}972973/* transfer cancelled */974break;975}976} /* ubt_ctrl_write_callback */977978/*979* Called when incoming interrupt transfer (HCI event) has completed, i.e.980* HCI event was received from the device.981* USB context.982*/983984static void985ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)986{987struct ubt_softc *sc = usbd_xfer_softc(xfer);988struct mbuf *m;989ng_hci_event_pkt_t *hdr;990struct usb_page_cache *pc;991int actlen;992993usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);994995m = NULL;996997switch (USB_GET_STATE(xfer)) {998case USB_ST_TRANSFERRED:999/* Allocate a new mbuf */1000MGETHDR(m, M_NOWAIT, MT_DATA);1001if (m == NULL) {1002UBT_STAT_IERROR(sc);1003goto submit_next;1004}10051006if (!(MCLGET(m, M_NOWAIT))) {1007UBT_STAT_IERROR(sc);1008goto submit_next;1009}10101011/* Add HCI packet type */1012*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;1013m->m_pkthdr.len = m->m_len = 1;10141015if (actlen > MCLBYTES - 1)1016actlen = MCLBYTES - 1;10171018pc = usbd_xfer_get_frame(xfer, 0);1019usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);1020m->m_pkthdr.len += actlen;1021m->m_len += actlen;10221023UBT_INFO(sc, "got %d bytes from interrupt pipe\n",1024actlen);10251026/* Validate packet and send it up the stack */1027if (m->m_pkthdr.len < (int)sizeof(*hdr)) {1028UBT_INFO(sc, "HCI event packet is too short\n");10291030UBT_STAT_IERROR(sc);1031goto submit_next;1032}10331034hdr = mtod(m, ng_hci_event_pkt_t *);1035if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {1036UBT_ERR(sc, "Invalid HCI event packet size, " \1037"length=%d, pktlen=%d\n",1038hdr->length, m->m_pkthdr.len);10391040UBT_STAT_IERROR(sc);1041goto submit_next;1042}10431044UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \1045"length=%d\n", m->m_pkthdr.len, hdr->length);10461047UBT_STAT_PCKTS_RECV(sc);1048UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);10491050ubt_fwd_mbuf_up(sc, &m);1051/* m == NULL at this point */1052/* FALLTHROUGH */10531054case USB_ST_SETUP:1055submit_next:1056NG_FREE_M(m); /* checks for m != NULL */10571058usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));1059usbd_transfer_submit(xfer);1060break;10611062default: /* Error */1063if (error != USB_ERR_CANCELLED) {1064UBT_WARN(sc, "interrupt transfer failed: %s\n",1065usbd_errstr(error));10661067/* Try to clear stall first */1068usbd_xfer_set_stall(xfer);1069goto submit_next;1070}1071/* transfer cancelled */1072break;1073}1074} /* ubt_intr_read_callback */10751076/*1077* Called when incoming bulk transfer (ACL packet) has completed, i.e.1078* ACL packet was received from the device.1079* USB context.1080*/10811082static void1083ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)1084{1085struct ubt_softc *sc = usbd_xfer_softc(xfer);1086struct mbuf *m;1087ng_hci_acldata_pkt_t *hdr;1088struct usb_page_cache *pc;1089int len;1090int actlen;10911092usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);10931094m = NULL;10951096switch (USB_GET_STATE(xfer)) {1097case USB_ST_TRANSFERRED:1098/* Allocate new mbuf */1099MGETHDR(m, M_NOWAIT, MT_DATA);1100if (m == NULL) {1101UBT_STAT_IERROR(sc);1102goto submit_next;1103}11041105if (!(MCLGET(m, M_NOWAIT))) {1106UBT_STAT_IERROR(sc);1107goto submit_next;1108}11091110/* Add HCI packet type */1111*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;1112m->m_pkthdr.len = m->m_len = 1;11131114if (actlen > MCLBYTES - 1)1115actlen = MCLBYTES - 1;11161117pc = usbd_xfer_get_frame(xfer, 0);1118usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);1119m->m_pkthdr.len += actlen;1120m->m_len += actlen;11211122UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",1123actlen);11241125/* Validate packet and send it up the stack */1126if (m->m_pkthdr.len < (int)sizeof(*hdr)) {1127UBT_INFO(sc, "HCI ACL packet is too short\n");11281129UBT_STAT_IERROR(sc);1130goto submit_next;1131}11321133hdr = mtod(m, ng_hci_acldata_pkt_t *);1134len = le16toh(hdr->length);1135if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {1136UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \1137"pktlen=%d\n", len, m->m_pkthdr.len);11381139UBT_STAT_IERROR(sc);1140goto submit_next;1141}11421143UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \1144"length=%d\n", m->m_pkthdr.len, len);11451146UBT_STAT_PCKTS_RECV(sc);1147UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);11481149ubt_fwd_mbuf_up(sc, &m);1150/* m == NULL at this point */1151/* FALLTHOUGH */11521153case USB_ST_SETUP:1154submit_next:1155NG_FREE_M(m); /* checks for m != NULL */11561157usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));1158usbd_transfer_submit(xfer);1159break;11601161default: /* Error */1162if (error != USB_ERR_CANCELLED) {1163UBT_WARN(sc, "bulk-in transfer failed: %s\n",1164usbd_errstr(error));11651166/* Try to clear stall first */1167usbd_xfer_set_stall(xfer);1168goto submit_next;1169}1170/* transfer cancelled */1171break;1172}1173} /* ubt_bulk_read_callback */11741175/*1176* Called when outgoing bulk transfer (ACL packet) has completed, i.e.1177* ACL packet was sent to the device.1178* USB context.1179*/11801181static void1182ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)1183{1184struct ubt_softc *sc = usbd_xfer_softc(xfer);1185struct mbuf *m;1186struct usb_page_cache *pc;1187int actlen;11881189usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);11901191switch (USB_GET_STATE(xfer)) {1192case USB_ST_TRANSFERRED:1193UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);1194UBT_STAT_BYTES_SENT(sc, actlen);1195UBT_STAT_PCKTS_SENT(sc);1196/* FALLTHROUGH */11971198case USB_ST_SETUP:1199send_next:1200/* Get next mbuf, if any */1201UBT_NG_LOCK(sc);1202NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);1203UBT_NG_UNLOCK(sc);12041205if (m == NULL) {1206UBT_INFO(sc, "ACL data queue is empty\n");1207break; /* transfer completed */1208}12091210/*1211* Copy ACL data frame back to a linear USB transfer buffer1212* and schedule transfer1213*/12141215pc = usbd_xfer_get_frame(xfer, 0);1216usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);1217usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);12181219UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",1220m->m_pkthdr.len);12211222NG_FREE_M(m);12231224usbd_transfer_submit(xfer);1225break;12261227default: /* Error */1228if (error != USB_ERR_CANCELLED) {1229UBT_WARN(sc, "bulk-out transfer failed: %s\n",1230usbd_errstr(error));12311232UBT_STAT_OERROR(sc);12331234/* try to clear stall first */1235usbd_xfer_set_stall(xfer);1236goto send_next;1237}1238/* transfer cancelled */1239break;1240}1241} /* ubt_bulk_write_callback */12421243/*1244* Called when incoming isoc transfer (SCO packet) has completed, i.e.1245* SCO packet was received from the device.1246* USB context.1247*/12481249static void1250ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)1251{1252struct ubt_softc *sc = usbd_xfer_softc(xfer);1253int n;1254int actlen, nframes;12551256usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);12571258switch (USB_GET_STATE(xfer)) {1259case USB_ST_TRANSFERRED:1260for (n = 0; n < nframes; n ++)1261if (ubt_isoc_read_one_frame(xfer, n) < 0)1262break;1263/* FALLTHROUGH */12641265case USB_ST_SETUP:1266read_next:1267for (n = 0; n < nframes; n ++)1268usbd_xfer_set_frame_len(xfer, n,1269usbd_xfer_max_framelen(xfer));12701271usbd_transfer_submit(xfer);1272break;12731274default: /* Error */1275if (error != USB_ERR_CANCELLED) {1276UBT_STAT_IERROR(sc);1277goto read_next;1278}12791280/* transfer cancelled */1281break;1282}1283} /* ubt_isoc_read_callback */12841285/*1286* Helper function. Called from ubt_isoc_read_callback() to read1287* SCO data from one frame.1288* USB context.1289*/12901291static int1292ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)1293{1294struct ubt_softc *sc = usbd_xfer_softc(xfer);1295struct usb_page_cache *pc;1296struct mbuf *m;1297int len, want, got, total;12981299/* Get existing SCO reassembly buffer */1300pc = usbd_xfer_get_frame(xfer, 0);1301m = sc->sc_isoc_in_buffer;1302total = usbd_xfer_frame_len(xfer, frame_no);13031304/* While we have data in the frame */1305while (total > 0) {1306if (m == NULL) {1307/* Start new reassembly buffer */1308MGETHDR(m, M_NOWAIT, MT_DATA);1309if (m == NULL) {1310UBT_STAT_IERROR(sc);1311return (-1); /* XXX out of sync! */1312}13131314if (!(MCLGET(m, M_NOWAIT))) {1315UBT_STAT_IERROR(sc);1316NG_FREE_M(m);1317return (-1); /* XXX out of sync! */1318}13191320/* Expect SCO header */1321*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;1322m->m_pkthdr.len = m->m_len = got = 1;1323want = sizeof(ng_hci_scodata_pkt_t);1324} else {1325/*1326* Check if we have SCO header and if so1327* adjust amount of data we want1328*/1329got = m->m_pkthdr.len;1330want = sizeof(ng_hci_scodata_pkt_t);13311332if (got >= want)1333want += mtod(m, ng_hci_scodata_pkt_t *)->length;1334}13351336/* Append frame data to the SCO reassembly buffer */1337len = total;1338if (got + len > want)1339len = want - got;13401341usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),1342mtod(m, uint8_t *) + m->m_pkthdr.len, len);13431344m->m_pkthdr.len += len;1345m->m_len += len;1346total -= len;13471348/* Check if we got everything we wanted, if not - continue */1349if (got != want)1350continue;13511352/* If we got here then we got complete SCO frame */1353UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \1354"length=%d\n", m->m_pkthdr.len,1355mtod(m, ng_hci_scodata_pkt_t *)->length);13561357UBT_STAT_PCKTS_RECV(sc);1358UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);13591360ubt_fwd_mbuf_up(sc, &m);1361/* m == NULL at this point */1362}13631364/* Put SCO reassembly buffer back */1365sc->sc_isoc_in_buffer = m;13661367return (0);1368} /* ubt_isoc_read_one_frame */13691370/*1371* Called when outgoing isoc transfer (SCO packet) has completed, i.e.1372* SCO packet was sent to the device.1373* USB context.1374*/13751376static void1377ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)1378{1379struct ubt_softc *sc = usbd_xfer_softc(xfer);1380struct usb_page_cache *pc;1381struct mbuf *m;1382int n, space, offset;1383int actlen, nframes;13841385usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);1386pc = usbd_xfer_get_frame(xfer, 0);13871388switch (USB_GET_STATE(xfer)) {1389case USB_ST_TRANSFERRED:1390UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);1391UBT_STAT_BYTES_SENT(sc, actlen);1392UBT_STAT_PCKTS_SENT(sc);1393/* FALLTHROUGH */13941395case USB_ST_SETUP:1396send_next:1397offset = 0;1398space = usbd_xfer_max_framelen(xfer) * nframes;1399m = NULL;14001401while (space > 0) {1402if (m == NULL) {1403UBT_NG_LOCK(sc);1404NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);1405UBT_NG_UNLOCK(sc);14061407if (m == NULL)1408break;1409}14101411n = min(space, m->m_pkthdr.len);1412if (n > 0) {1413usbd_m_copy_in(pc, offset, m,0, n);1414m_adj(m, n);14151416offset += n;1417space -= n;1418}14191420if (m->m_pkthdr.len == 0)1421NG_FREE_M(m); /* sets m = NULL */1422}14231424/* Put whatever is left from mbuf back on queue */1425if (m != NULL) {1426UBT_NG_LOCK(sc);1427NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);1428UBT_NG_UNLOCK(sc);1429}14301431/*1432* Calculate sizes for isoc frames.1433* Note that offset could be 0 at this point (i.e. we have1434* nothing to send). That is fine, as we have isoc. transfers1435* going in both directions all the time. In this case it1436* would be just empty isoc. transfer.1437*/14381439for (n = 0; n < nframes; n ++) {1440usbd_xfer_set_frame_len(xfer, n,1441min(offset, usbd_xfer_max_framelen(xfer)));1442offset -= usbd_xfer_frame_len(xfer, n);1443}14441445usbd_transfer_submit(xfer);1446break;14471448default: /* Error */1449if (error != USB_ERR_CANCELLED) {1450UBT_STAT_OERROR(sc);1451goto send_next;1452}14531454/* transfer cancelled */1455break;1456}1457}14581459/*1460* Utility function to forward provided mbuf upstream (i.e. up the stack).1461* Modifies value of the mbuf pointer (sets it to NULL).1462* Save to call from any context.1463*/14641465static int1466ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)1467{1468hook_p hook;1469int error;14701471/*1472* Close the race with Netgraph hook newhook/disconnect methods.1473* Save the hook pointer atomically. Two cases are possible:1474*1475* 1) The hook pointer is NULL. It means disconnect method got1476* there first. In this case we are done.1477*1478* 2) The hook pointer is not NULL. It means that hook pointer1479* could be either in valid or invalid (i.e. in the process1480* of disconnect) state. In any case grab an extra reference1481* to protect the hook pointer.1482*1483* It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as1484* it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().1485*/14861487UBT_NG_LOCK(sc);1488if ((hook = sc->sc_hook) != NULL)1489NG_HOOK_REF(hook);1490UBT_NG_UNLOCK(sc);14911492if (hook == NULL) {1493NG_FREE_M(*m);1494return (ENETDOWN);1495}14961497NG_SEND_DATA_ONLY(error, hook, *m);1498NG_HOOK_UNREF(hook);14991500if (error != 0)1501UBT_STAT_IERROR(sc);15021503return (error);1504} /* ubt_fwd_mbuf_up */15051506/****************************************************************************1507****************************************************************************1508** Glue1509****************************************************************************1510****************************************************************************/15111512/*1513* Schedule glue task. Should be called with sc_ng_mtx held.1514* Netgraph context.1515*/15161517static void1518ubt_task_schedule(ubt_softc_p sc, int action)1519{1520mtx_assert(&sc->sc_ng_mtx, MA_OWNED);15211522/*1523* Try to handle corner case when "start all" and "stop all"1524* actions can both be set before task is executed.1525*1526* The rules are1527*1528* sc_task_flags action new sc_task_flags1529* ------------------------------------------------------1530* 0 start start1531* 0 stop stop1532* start start start1533* start stop stop1534* stop start stop|start1535* stop stop stop1536* stop|start start stop|start1537* stop|start stop stop1538*/15391540if (action != 0) {1541if ((action & UBT_FLAG_T_STOP_ALL) != 0)1542sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;15431544sc->sc_task_flags |= action;1545}15461547if (sc->sc_task_flags & UBT_FLAG_T_PENDING)1548return;15491550if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {1551sc->sc_task_flags |= UBT_FLAG_T_PENDING;1552return;1553}15541555/* XXX: i think this should never happen */1556} /* ubt_task_schedule */15571558/*1559* Glue task. Examines sc_task_flags and does things depending on it.1560* Taskqueue context.1561*/15621563static void1564ubt_task(void *context, int pending)1565{1566ubt_softc_p sc = context;1567int task_flags, i;15681569UBT_NG_LOCK(sc);1570task_flags = sc->sc_task_flags;1571sc->sc_task_flags = 0;1572UBT_NG_UNLOCK(sc);15731574/*1575* Stop all USB transfers synchronously.1576* Stop interface #0 and #1 transfers at the same time and in the1577* same loop. usbd_transfer_drain() will do appropriate locking.1578*/15791580if (task_flags & UBT_FLAG_T_STOP_ALL)1581for (i = 0; i < UBT_N_TRANSFER; i ++)1582usbd_transfer_drain(sc->sc_xfer[i]);15831584/* Start incoming interrupt and bulk, and all isoc. USB transfers */1585if (task_flags & UBT_FLAG_T_START_ALL) {1586/*1587* Interface #01588*/15891590mtx_lock(&sc->sc_if_mtx);15911592ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);1593ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);15941595/*1596* Interface #11597* Start both read and write isoc. transfers by default.1598* Get them going all the time even if we have nothing1599* to send to avoid any delays.1600*/16011602ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);1603ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);1604ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);1605ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);16061607mtx_unlock(&sc->sc_if_mtx);1608}16091610/* Start outgoing control transfer */1611if (task_flags & UBT_FLAG_T_START_CTRL) {1612mtx_lock(&sc->sc_if_mtx);1613ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);1614mtx_unlock(&sc->sc_if_mtx);1615}16161617/* Start outgoing bulk transfer */1618if (task_flags & UBT_FLAG_T_START_BULK) {1619mtx_lock(&sc->sc_if_mtx);1620ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);1621mtx_unlock(&sc->sc_if_mtx);1622}1623} /* ubt_task */16241625/****************************************************************************1626****************************************************************************1627** Netgraph specific1628****************************************************************************1629****************************************************************************/16301631/*1632* Netgraph node constructor. Do not allow to create node of this type.1633* Netgraph context.1634*/16351636static int1637ng_ubt_constructor(node_p node)1638{1639return (EINVAL);1640} /* ng_ubt_constructor */16411642/*1643* Netgraph node destructor. Destroy node only when device has been detached.1644* Netgraph context.1645*/16461647static int1648ng_ubt_shutdown(node_p node)1649{1650if (node->nd_flags & NGF_REALLY_DIE) {1651/*1652* We came here because the USB device is being1653* detached, so stop being persistent.1654*/1655NG_NODE_SET_PRIVATE(node, NULL);1656NG_NODE_UNREF(node);1657} else1658NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */16591660return (0);1661} /* ng_ubt_shutdown */16621663/*1664* Create new hook. There can only be one.1665* Netgraph context.1666*/16671668static int1669ng_ubt_newhook(node_p node, hook_p hook, char const *name)1670{1671struct ubt_softc *sc = NG_NODE_PRIVATE(node);16721673if (strcmp(name, NG_UBT_HOOK) != 0)1674return (EINVAL);16751676UBT_NG_LOCK(sc);1677if (sc->sc_hook != NULL) {1678UBT_NG_UNLOCK(sc);16791680return (EISCONN);1681}16821683sc->sc_hook = hook;1684UBT_NG_UNLOCK(sc);16851686return (0);1687} /* ng_ubt_newhook */16881689/*1690* Connect hook. Start incoming USB transfers.1691* Netgraph context.1692*/16931694static int1695ng_ubt_connect(hook_p hook)1696{1697struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));16981699NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));17001701UBT_NG_LOCK(sc);1702ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);1703UBT_NG_UNLOCK(sc);17041705return (0);1706} /* ng_ubt_connect */17071708/*1709* Disconnect hook.1710* Netgraph context.1711*/17121713static int1714ng_ubt_disconnect(hook_p hook)1715{1716struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));17171718UBT_NG_LOCK(sc);17191720if (hook != sc->sc_hook) {1721UBT_NG_UNLOCK(sc);17221723return (EINVAL);1724}17251726sc->sc_hook = NULL;17271728/* Kick off task to stop all USB xfers */1729ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);17301731/* Drain queues */1732NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);1733NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);1734NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);17351736UBT_NG_UNLOCK(sc);17371738return (0);1739} /* ng_ubt_disconnect */17401741/*1742* Process control message.1743* Netgraph context.1744*/17451746static int1747ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)1748{1749struct ubt_softc *sc = NG_NODE_PRIVATE(node);1750struct ng_mesg *msg, *rsp = NULL;1751struct ng_bt_mbufq *q;1752int error = 0, queue, qlen;17531754NGI_GET_MSG(item, msg);17551756switch (msg->header.typecookie) {1757case NGM_GENERIC_COOKIE:1758switch (msg->header.cmd) {1759case NGM_TEXT_STATUS:1760NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);1761if (rsp == NULL) {1762error = ENOMEM;1763break;1764}17651766snprintf(rsp->data, NG_TEXTRESPONSE,1767"Hook: %s\n" \1768"Task flags: %#x\n" \1769"Debug: %d\n" \1770"CMD queue: [have:%d,max:%d]\n" \1771"ACL queue: [have:%d,max:%d]\n" \1772"SCO queue: [have:%d,max:%d]",1773(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",1774sc->sc_task_flags,1775sc->sc_debug,1776sc->sc_cmdq.len,1777sc->sc_cmdq.maxlen,1778sc->sc_aclq.len,1779sc->sc_aclq.maxlen,1780sc->sc_scoq.len,1781sc->sc_scoq.maxlen);1782break;17831784default:1785error = EINVAL;1786break;1787}1788break;17891790case NGM_UBT_COOKIE:1791switch (msg->header.cmd) {1792case NGM_UBT_NODE_SET_DEBUG:1793if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){1794error = EMSGSIZE;1795break;1796}17971798sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));1799break;18001801case NGM_UBT_NODE_GET_DEBUG:1802NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),1803M_NOWAIT);1804if (rsp == NULL) {1805error = ENOMEM;1806break;1807}18081809*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;1810break;18111812case NGM_UBT_NODE_SET_QLEN:1813if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {1814error = EMSGSIZE;1815break;1816}18171818queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;1819qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;18201821switch (queue) {1822case NGM_UBT_NODE_QUEUE_CMD:1823q = &sc->sc_cmdq;1824break;18251826case NGM_UBT_NODE_QUEUE_ACL:1827q = &sc->sc_aclq;1828break;18291830case NGM_UBT_NODE_QUEUE_SCO:1831q = &sc->sc_scoq;1832break;18331834default:1835error = EINVAL;1836goto done;1837/* NOT REACHED */1838}18391840q->maxlen = qlen;1841break;18421843case NGM_UBT_NODE_GET_QLEN:1844if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {1845error = EMSGSIZE;1846break;1847}18481849queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;18501851switch (queue) {1852case NGM_UBT_NODE_QUEUE_CMD:1853q = &sc->sc_cmdq;1854break;18551856case NGM_UBT_NODE_QUEUE_ACL:1857q = &sc->sc_aclq;1858break;18591860case NGM_UBT_NODE_QUEUE_SCO:1861q = &sc->sc_scoq;1862break;18631864default:1865error = EINVAL;1866goto done;1867/* NOT REACHED */1868}18691870NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),1871M_NOWAIT);1872if (rsp == NULL) {1873error = ENOMEM;1874break;1875}18761877((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;1878((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;1879break;18801881case NGM_UBT_NODE_GET_STAT:1882NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),1883M_NOWAIT);1884if (rsp == NULL) {1885error = ENOMEM;1886break;1887}18881889bcopy(&sc->sc_stat, rsp->data,1890sizeof(ng_ubt_node_stat_ep));1891break;18921893case NGM_UBT_NODE_RESET_STAT:1894UBT_STAT_RESET(sc);1895break;18961897default:1898error = EINVAL;1899break;1900}1901break;19021903default:1904error = EINVAL;1905break;1906}1907done:1908NG_RESPOND_MSG(error, node, item, rsp);1909NG_FREE_MSG(msg);19101911return (error);1912} /* ng_ubt_rcvmsg */19131914/*1915* Process data.1916* Netgraph context.1917*/19181919static int1920ng_ubt_rcvdata(hook_p hook, item_p item)1921{1922struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));1923struct mbuf *m;1924struct ng_bt_mbufq *q;1925int action, error = 0;19261927if (hook != sc->sc_hook) {1928error = EINVAL;1929goto done;1930}19311932/* Deatch mbuf and get HCI frame type */1933NGI_GET_M(item, m);19341935/*1936* Minimal size of the HCI frame is 4 bytes: 1 byte frame type,1937* 2 bytes connection handle and at least 1 byte of length.1938* Panic on data frame that has size smaller than 4 bytes (it1939* should not happen)1940*/19411942if (m->m_pkthdr.len < 4)1943panic("HCI frame size is too small! pktlen=%d\n",1944m->m_pkthdr.len);19451946/* Process HCI frame */1947switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */1948case NG_HCI_CMD_PKT:1949if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)1950panic("HCI command frame size is too big! " \1951"buffer size=%zd, packet len=%d\n",1952UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);19531954q = &sc->sc_cmdq;1955action = UBT_FLAG_T_START_CTRL;1956break;19571958case NG_HCI_ACL_DATA_PKT:1959if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)1960panic("ACL data frame size is too big! " \1961"buffer size=%d, packet len=%d\n",1962UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);19631964q = &sc->sc_aclq;1965action = UBT_FLAG_T_START_BULK;1966break;19671968case NG_HCI_SCO_DATA_PKT:1969q = &sc->sc_scoq;1970action = 0;1971break;19721973default:1974UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \1975"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);19761977NG_FREE_M(m);1978error = EINVAL;1979goto done;1980/* NOT REACHED */1981}19821983UBT_NG_LOCK(sc);1984if (NG_BT_MBUFQ_FULL(q)) {1985NG_BT_MBUFQ_DROP(q);1986UBT_NG_UNLOCK(sc);19871988UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",1989*mtod(m, uint8_t *), m->m_pkthdr.len);19901991NG_FREE_M(m);1992} else {1993/* Loose HCI packet type, enqueue mbuf and kick off task */1994m_adj(m, sizeof(uint8_t));1995NG_BT_MBUFQ_ENQUEUE(q, m);1996ubt_task_schedule(sc, action);1997UBT_NG_UNLOCK(sc);1998}1999done:2000NG_FREE_ITEM(item);20012002return (error);2003} /* ng_ubt_rcvdata */20042005/****************************************************************************2006****************************************************************************2007** Module2008****************************************************************************2009****************************************************************************/20102011/*2012* Load/Unload the driver module2013*/20142015static int2016ubt_modevent(module_t mod, int event, void *data)2017{2018int error;20192020switch (event) {2021case MOD_LOAD:2022error = ng_newtype(&typestruct);2023if (error != 0)2024printf("%s: Could not register Netgraph node type, " \2025"error=%d\n", NG_UBT_NODE_TYPE, error);2026break;20272028case MOD_UNLOAD:2029error = ng_rmtype(&typestruct);2030break;20312032default:2033error = EOPNOTSUPP;2034break;2035}20362037return (error);2038} /* ubt_modevent */20392040static device_method_t ubt_methods[] =2041{2042DEVMETHOD(device_probe, ubt_probe),2043DEVMETHOD(device_attach, ubt_attach),2044DEVMETHOD(device_detach, ubt_detach),2045DEVMETHOD_END2046};20472048driver_t ubt_driver =2049{2050.name = "ubt",2051.methods = ubt_methods,2052.size = sizeof(struct ubt_softc),2053};20542055DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_modevent, 0);2056MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);2057MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);2058MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);2059MODULE_DEPEND(ng_ubt, ng_bluetooth, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);2060MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);2061USB_PNP_HOST_INFO(ubt_devs);206220632064