13671d9d8SAndrew Thompson /*
23671d9d8SAndrew Thompson * ng_ubt.c
33671d9d8SAndrew Thompson */
43671d9d8SAndrew Thompson
53671d9d8SAndrew Thompson /*-
64d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
7fe267a55SPedro F. Giffuni *
83671d9d8SAndrew Thompson * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
93671d9d8SAndrew Thompson * All rights reserved.
103671d9d8SAndrew Thompson *
113671d9d8SAndrew Thompson * Redistribution and use in source and binary forms, with or without
123671d9d8SAndrew Thompson * modification, are permitted provided that the following conditions
133671d9d8SAndrew Thompson * are met:
143671d9d8SAndrew Thompson * 1. Redistributions of source code must retain the above copyright
153671d9d8SAndrew Thompson * notice, this list of conditions and the following disclaimer.
163671d9d8SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
173671d9d8SAndrew Thompson * notice, this list of conditions and the following disclaimer in the
183671d9d8SAndrew Thompson * documentation and/or other materials provided with the distribution.
193671d9d8SAndrew Thompson *
203671d9d8SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
213671d9d8SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
223671d9d8SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
233671d9d8SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
243671d9d8SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
253671d9d8SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
263671d9d8SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
273671d9d8SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
283671d9d8SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
293671d9d8SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
303671d9d8SAndrew Thompson * SUCH DAMAGE.
313671d9d8SAndrew Thompson *
323671d9d8SAndrew Thompson * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
333671d9d8SAndrew Thompson */
343671d9d8SAndrew Thompson
353671d9d8SAndrew Thompson /*
363671d9d8SAndrew Thompson * NOTE: ng_ubt2 driver has a split personality. On one side it is
373671d9d8SAndrew Thompson * a USB device driver and on the other it is a Netgraph node. This
383671d9d8SAndrew Thompson * driver will *NOT* create traditional /dev/ enties, only Netgraph
393671d9d8SAndrew Thompson * node.
403671d9d8SAndrew Thompson *
413671d9d8SAndrew Thompson * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
423671d9d8SAndrew Thompson *
433671d9d8SAndrew Thompson * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
443671d9d8SAndrew Thompson * by USB for any USB request going over device's interface #0 and #1,
453671d9d8SAndrew Thompson * i.e. interrupt, control, bulk and isoc. transfers.
463671d9d8SAndrew Thompson *
473671d9d8SAndrew Thompson * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
483671d9d8SAndrew Thompson * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
493671d9d8SAndrew Thompson * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
503671d9d8SAndrew Thompson * think of it as a spin lock.
513671d9d8SAndrew Thompson *
523671d9d8SAndrew Thompson * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
533671d9d8SAndrew Thompson *
543671d9d8SAndrew Thompson * 1) USB context. This is where all the USB related stuff happens. All
553671d9d8SAndrew Thompson * callbacks run in this context. All callbacks are called (by USB) with
563671d9d8SAndrew Thompson * appropriate interface lock held. It is (generally) allowed to grab
573671d9d8SAndrew Thompson * any additional locks.
583671d9d8SAndrew Thompson *
593671d9d8SAndrew Thompson * 2) Netgraph context. This is where all the Netgraph related stuff happens.
603671d9d8SAndrew Thompson * Since we mark node as WRITER, the Netgraph node will be "locked" (from
613671d9d8SAndrew Thompson * Netgraph point of view). Any variable that is only modified from the
62053359b7SPedro F. Giffuni * Netgraph context does not require any additional locking. It is generally
633671d9d8SAndrew Thompson * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
643671d9d8SAndrew Thompson * grab any lock in the Netgraph context that could cause de-scheduling of
653671d9d8SAndrew Thompson * the Netgraph thread for significant amount of time. In fact, the only
663671d9d8SAndrew Thompson * lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
673671d9d8SAndrew Thompson * Also make sure that any code that is called from the Netgraph context
683671d9d8SAndrew Thompson * follows the rule above.
693671d9d8SAndrew Thompson *
703671d9d8SAndrew Thompson * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
713671d9d8SAndrew Thompson * NOT allowed to grab any lock that could cause de-scheduling in the
723671d9d8SAndrew Thompson * Netgraph context, and, USB requires us to grab interface lock before
733671d9d8SAndrew Thompson * doing things with transfers, it is safer to transition from the Netgraph
743671d9d8SAndrew Thompson * context to the Taskqueue context before we can call into USB subsystem.
753671d9d8SAndrew Thompson *
763671d9d8SAndrew Thompson * So, to put everything together, the rules are as follows.
773671d9d8SAndrew Thompson * It is OK to call from the USB context or the Taskqueue context into
783671d9d8SAndrew Thompson * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
793671d9d8SAndrew Thompson * it is allowed to call into the Netgraph context with locks held.
803671d9d8SAndrew Thompson * Is it *NOT* OK to call from the Netgraph context into the USB context,
813671d9d8SAndrew Thompson * because USB requires us to grab interface locks, and, it is safer to
823671d9d8SAndrew Thompson * avoid it. So, to make things safer we set task flags to indicate which
833671d9d8SAndrew Thompson * actions we want to perform and schedule ubt_task which would run in the
843671d9d8SAndrew Thompson * Taskqueue context.
853671d9d8SAndrew Thompson * Is is OK to call from the Taskqueue context into the USB context,
863671d9d8SAndrew Thompson * and, ubt_task does just that (i.e. grabs appropriate interface locks
873671d9d8SAndrew Thompson * before calling into USB).
883671d9d8SAndrew Thompson * Access to the outgoing queues, task flags and hook pointer is
893671d9d8SAndrew Thompson * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
903671d9d8SAndrew Thompson * sc_ng_mtx should really be a spin lock (and it is very likely to an
913671d9d8SAndrew Thompson * equivalent of spin lock due to adaptive nature of FreeBSD mutexes).
923671d9d8SAndrew Thompson * All USB callbacks accept softc pointer as a private data. USB ensures
933671d9d8SAndrew Thompson * that this pointer is valid.
943671d9d8SAndrew Thompson */
953671d9d8SAndrew Thompson
96ed6d949aSAndrew Thompson #include <sys/stdint.h>
97ed6d949aSAndrew Thompson #include <sys/stddef.h>
98ed6d949aSAndrew Thompson #include <sys/param.h>
99ed6d949aSAndrew Thompson #include <sys/queue.h>
100ed6d949aSAndrew Thompson #include <sys/types.h>
101ed6d949aSAndrew Thompson #include <sys/systm.h>
102ed6d949aSAndrew Thompson #include <sys/kernel.h>
103ed6d949aSAndrew Thompson #include <sys/bus.h>
104ed6d949aSAndrew Thompson #include <sys/module.h>
105ed6d949aSAndrew Thompson #include <sys/lock.h>
106ed6d949aSAndrew Thompson #include <sys/mutex.h>
107ed6d949aSAndrew Thompson #include <sys/condvar.h>
108ed6d949aSAndrew Thompson #include <sys/sysctl.h>
109ed6d949aSAndrew Thompson #include <sys/sx.h>
110ed6d949aSAndrew Thompson #include <sys/unistd.h>
111ed6d949aSAndrew Thompson #include <sys/callout.h>
112ed6d949aSAndrew Thompson #include <sys/malloc.h>
113ed6d949aSAndrew Thompson #include <sys/priv.h>
114ed6d949aSAndrew Thompson
1153671d9d8SAndrew Thompson #include "usbdevs.h"
1163671d9d8SAndrew Thompson #include <dev/usb/usb.h>
117ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
118ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
1193671d9d8SAndrew Thompson
120a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug
1213671d9d8SAndrew Thompson #include <dev/usb/usb_debug.h>
1223671d9d8SAndrew Thompson #include <dev/usb/usb_busdma.h>
1233671d9d8SAndrew Thompson
1243671d9d8SAndrew Thompson #include <sys/mbuf.h>
1253671d9d8SAndrew Thompson #include <sys/taskqueue.h>
1263671d9d8SAndrew Thompson
1273671d9d8SAndrew Thompson #include <netgraph/ng_message.h>
1283671d9d8SAndrew Thompson #include <netgraph/netgraph.h>
1293671d9d8SAndrew Thompson #include <netgraph/ng_parse.h>
1303671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h>
1313671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_hci.h>
1323671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_ubt.h>
13306079f14SAndrew Thompson #include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>
1343671d9d8SAndrew Thompson
1353671d9d8SAndrew Thompson static int ubt_modevent(module_t, int, void *);
1363671d9d8SAndrew Thompson static device_probe_t ubt_probe;
1373671d9d8SAndrew Thompson static device_attach_t ubt_attach;
1383671d9d8SAndrew Thompson static device_detach_t ubt_detach;
1393671d9d8SAndrew Thompson
1403671d9d8SAndrew Thompson static void ubt_task_schedule(ubt_softc_p, int);
1413671d9d8SAndrew Thompson static task_fn_t ubt_task;
1423671d9d8SAndrew Thompson
143a593f6b8SAndrew Thompson #define ubt_xfer_start(sc, i) usbd_transfer_start((sc)->sc_xfer[(i)])
1443671d9d8SAndrew Thompson
1453671d9d8SAndrew Thompson /* Netgraph methods */
1463671d9d8SAndrew Thompson static ng_constructor_t ng_ubt_constructor;
1473671d9d8SAndrew Thompson static ng_shutdown_t ng_ubt_shutdown;
1483671d9d8SAndrew Thompson static ng_newhook_t ng_ubt_newhook;
1493671d9d8SAndrew Thompson static ng_connect_t ng_ubt_connect;
1503671d9d8SAndrew Thompson static ng_disconnect_t ng_ubt_disconnect;
1513671d9d8SAndrew Thompson static ng_rcvmsg_t ng_ubt_rcvmsg;
1523671d9d8SAndrew Thompson static ng_rcvdata_t ng_ubt_rcvdata;
1533671d9d8SAndrew Thompson
15467cbbf19SHans Petter Selasky static int ng_usb_isoc_enable = 1;
15567cbbf19SHans Petter Selasky
15603f03934SHans Petter Selasky SYSCTL_INT(_net_bluetooth, OID_AUTO, usb_isoc_enable, CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
15767cbbf19SHans Petter Selasky &ng_usb_isoc_enable, 0, "enable isochronous transfers");
15867cbbf19SHans Petter Selasky
1593671d9d8SAndrew Thompson /* Queue length */
1603671d9d8SAndrew Thompson static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] =
1613671d9d8SAndrew Thompson {
1623671d9d8SAndrew Thompson { "queue", &ng_parse_int32_type, },
1633671d9d8SAndrew Thompson { "qlen", &ng_parse_int32_type, },
1643671d9d8SAndrew Thompson { NULL, }
1653671d9d8SAndrew Thompson };
1663671d9d8SAndrew Thompson static const struct ng_parse_type ng_ubt_node_qlen_type =
1673671d9d8SAndrew Thompson {
1683671d9d8SAndrew Thompson &ng_parse_struct_type,
1693671d9d8SAndrew Thompson &ng_ubt_node_qlen_type_fields
1703671d9d8SAndrew Thompson };
1713671d9d8SAndrew Thompson
1723671d9d8SAndrew Thompson /* Stat info */
1733671d9d8SAndrew Thompson static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] =
1743671d9d8SAndrew Thompson {
1753671d9d8SAndrew Thompson { "pckts_recv", &ng_parse_uint32_type, },
1763671d9d8SAndrew Thompson { "bytes_recv", &ng_parse_uint32_type, },
1773671d9d8SAndrew Thompson { "pckts_sent", &ng_parse_uint32_type, },
1783671d9d8SAndrew Thompson { "bytes_sent", &ng_parse_uint32_type, },
1793671d9d8SAndrew Thompson { "oerrors", &ng_parse_uint32_type, },
1803671d9d8SAndrew Thompson { "ierrors", &ng_parse_uint32_type, },
1813671d9d8SAndrew Thompson { NULL, }
1823671d9d8SAndrew Thompson };
1833671d9d8SAndrew Thompson static const struct ng_parse_type ng_ubt_node_stat_type =
1843671d9d8SAndrew Thompson {
1853671d9d8SAndrew Thompson &ng_parse_struct_type,
1863671d9d8SAndrew Thompson &ng_ubt_node_stat_type_fields
1873671d9d8SAndrew Thompson };
1883671d9d8SAndrew Thompson
1893671d9d8SAndrew Thompson /* Netgraph node command list */
1903671d9d8SAndrew Thompson static const struct ng_cmdlist ng_ubt_cmdlist[] =
1913671d9d8SAndrew Thompson {
1923671d9d8SAndrew Thompson {
1933671d9d8SAndrew Thompson NGM_UBT_COOKIE,
1943671d9d8SAndrew Thompson NGM_UBT_NODE_SET_DEBUG,
1953671d9d8SAndrew Thompson "set_debug",
1963671d9d8SAndrew Thompson &ng_parse_uint16_type,
1973671d9d8SAndrew Thompson NULL
1983671d9d8SAndrew Thompson },
1993671d9d8SAndrew Thompson {
2003671d9d8SAndrew Thompson NGM_UBT_COOKIE,
2013671d9d8SAndrew Thompson NGM_UBT_NODE_GET_DEBUG,
2023671d9d8SAndrew Thompson "get_debug",
2033671d9d8SAndrew Thompson NULL,
2043671d9d8SAndrew Thompson &ng_parse_uint16_type
2053671d9d8SAndrew Thompson },
2063671d9d8SAndrew Thompson {
2073671d9d8SAndrew Thompson NGM_UBT_COOKIE,
2083671d9d8SAndrew Thompson NGM_UBT_NODE_SET_QLEN,
2093671d9d8SAndrew Thompson "set_qlen",
2103671d9d8SAndrew Thompson &ng_ubt_node_qlen_type,
2113671d9d8SAndrew Thompson NULL
2123671d9d8SAndrew Thompson },
2133671d9d8SAndrew Thompson {
2143671d9d8SAndrew Thompson NGM_UBT_COOKIE,
2153671d9d8SAndrew Thompson NGM_UBT_NODE_GET_QLEN,
2163671d9d8SAndrew Thompson "get_qlen",
2173671d9d8SAndrew Thompson &ng_ubt_node_qlen_type,
2183671d9d8SAndrew Thompson &ng_ubt_node_qlen_type
2193671d9d8SAndrew Thompson },
2203671d9d8SAndrew Thompson {
2213671d9d8SAndrew Thompson NGM_UBT_COOKIE,
2223671d9d8SAndrew Thompson NGM_UBT_NODE_GET_STAT,
2233671d9d8SAndrew Thompson "get_stat",
2243671d9d8SAndrew Thompson NULL,
2253671d9d8SAndrew Thompson &ng_ubt_node_stat_type
2263671d9d8SAndrew Thompson },
2273671d9d8SAndrew Thompson {
2283671d9d8SAndrew Thompson NGM_UBT_COOKIE,
2293671d9d8SAndrew Thompson NGM_UBT_NODE_RESET_STAT,
2303671d9d8SAndrew Thompson "reset_stat",
2313671d9d8SAndrew Thompson NULL,
2323671d9d8SAndrew Thompson NULL
2333671d9d8SAndrew Thompson },
2343671d9d8SAndrew Thompson { 0, }
2353671d9d8SAndrew Thompson };
2363671d9d8SAndrew Thompson
2373671d9d8SAndrew Thompson /* Netgraph node type */
2383671d9d8SAndrew Thompson static struct ng_type typestruct =
2393671d9d8SAndrew Thompson {
2403671d9d8SAndrew Thompson .version = NG_ABI_VERSION,
2413671d9d8SAndrew Thompson .name = NG_UBT_NODE_TYPE,
2423671d9d8SAndrew Thompson .constructor = ng_ubt_constructor,
2433671d9d8SAndrew Thompson .rcvmsg = ng_ubt_rcvmsg,
2443671d9d8SAndrew Thompson .shutdown = ng_ubt_shutdown,
2453671d9d8SAndrew Thompson .newhook = ng_ubt_newhook,
2463671d9d8SAndrew Thompson .connect = ng_ubt_connect,
2473671d9d8SAndrew Thompson .rcvdata = ng_ubt_rcvdata,
2483671d9d8SAndrew Thompson .disconnect = ng_ubt_disconnect,
2493671d9d8SAndrew Thompson .cmdlist = ng_ubt_cmdlist
2503671d9d8SAndrew Thompson };
2513671d9d8SAndrew Thompson
2523671d9d8SAndrew Thompson /****************************************************************************
2533671d9d8SAndrew Thompson ****************************************************************************
2543671d9d8SAndrew Thompson ** USB specific
2553671d9d8SAndrew Thompson ****************************************************************************
2563671d9d8SAndrew Thompson ****************************************************************************/
2573671d9d8SAndrew Thompson
2583671d9d8SAndrew Thompson /* USB methods */
2593544d43bSVladimir Kondratyev static usb_callback_t ubt_probe_intr_callback;
260e0a69b51SAndrew Thompson static usb_callback_t ubt_ctrl_write_callback;
261e0a69b51SAndrew Thompson static usb_callback_t ubt_intr_read_callback;
262e0a69b51SAndrew Thompson static usb_callback_t ubt_bulk_read_callback;
263e0a69b51SAndrew Thompson static usb_callback_t ubt_bulk_write_callback;
264e0a69b51SAndrew Thompson static usb_callback_t ubt_isoc_read_callback;
265e0a69b51SAndrew Thompson static usb_callback_t ubt_isoc_write_callback;
2663671d9d8SAndrew Thompson
2673671d9d8SAndrew Thompson static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
268760bc48eSAndrew Thompson static int ubt_isoc_read_one_frame(struct usb_xfer *, int);
2693671d9d8SAndrew Thompson
2703671d9d8SAndrew Thompson /*
2713671d9d8SAndrew Thompson * USB config
2723671d9d8SAndrew Thompson *
2733671d9d8SAndrew Thompson * The following desribes usb transfers that could be submitted on USB device.
2743671d9d8SAndrew Thompson *
2753671d9d8SAndrew Thompson * Interface 0 on the USB device must present the following endpoints
2763671d9d8SAndrew Thompson * 1) Interrupt endpoint to receive HCI events
2773671d9d8SAndrew Thompson * 2) Bulk IN endpoint to receive ACL data
2783671d9d8SAndrew Thompson * 3) Bulk OUT endpoint to send ACL data
2793671d9d8SAndrew Thompson *
2803671d9d8SAndrew Thompson * Interface 1 on the USB device must present the following endpoints
2813671d9d8SAndrew Thompson * 1) Isochronous IN endpoint to receive SCO data
2823671d9d8SAndrew Thompson * 2) Isochronous OUT endpoint to send SCO data
2833671d9d8SAndrew Thompson */
2843671d9d8SAndrew Thompson
285760bc48eSAndrew Thompson static const struct usb_config ubt_config[UBT_N_TRANSFER] =
2863671d9d8SAndrew Thompson {
2873671d9d8SAndrew Thompson /*
2883671d9d8SAndrew Thompson * Interface #0
2893671d9d8SAndrew Thompson */
2903671d9d8SAndrew Thompson
2913671d9d8SAndrew Thompson /* Outgoing bulk transfer - ACL packets */
2923671d9d8SAndrew Thompson [UBT_IF_0_BULK_DT_WR] = {
2933671d9d8SAndrew Thompson .type = UE_BULK,
2943671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY,
2953671d9d8SAndrew Thompson .direction = UE_DIR_OUT,
2963671d9d8SAndrew Thompson .if_index = 0,
2973671d9d8SAndrew Thompson .bufsize = UBT_BULK_WRITE_BUFFER_SIZE,
2983671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .force_short_xfer = 1, },
2993671d9d8SAndrew Thompson .callback = &ubt_bulk_write_callback,
3003671d9d8SAndrew Thompson },
3013671d9d8SAndrew Thompson /* Incoming bulk transfer - ACL packets */
3023671d9d8SAndrew Thompson [UBT_IF_0_BULK_DT_RD] = {
3033671d9d8SAndrew Thompson .type = UE_BULK,
3043671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY,
3053671d9d8SAndrew Thompson .direction = UE_DIR_IN,
3063671d9d8SAndrew Thompson .if_index = 0,
3073671d9d8SAndrew Thompson .bufsize = UBT_BULK_READ_BUFFER_SIZE,
3083671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .short_xfer_ok = 1, },
3093671d9d8SAndrew Thompson .callback = &ubt_bulk_read_callback,
3103671d9d8SAndrew Thompson },
3113671d9d8SAndrew Thompson /* Incoming interrupt transfer - HCI events */
3123671d9d8SAndrew Thompson [UBT_IF_0_INTR_DT_RD] = {
3133671d9d8SAndrew Thompson .type = UE_INTERRUPT,
3143671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY,
3153671d9d8SAndrew Thompson .direction = UE_DIR_IN,
3163671d9d8SAndrew Thompson .if_index = 0,
3173671d9d8SAndrew Thompson .flags = { .pipe_bof = 1, .short_xfer_ok = 1, },
3183671d9d8SAndrew Thompson .bufsize = UBT_INTR_BUFFER_SIZE,
3193671d9d8SAndrew Thompson .callback = &ubt_intr_read_callback,
3203671d9d8SAndrew Thompson },
3213671d9d8SAndrew Thompson /* Outgoing control transfer - HCI commands */
3223671d9d8SAndrew Thompson [UBT_IF_0_CTRL_DT_WR] = {
3233671d9d8SAndrew Thompson .type = UE_CONTROL,
3243671d9d8SAndrew Thompson .endpoint = 0x00, /* control pipe */
3253671d9d8SAndrew Thompson .direction = UE_DIR_ANY,
3263671d9d8SAndrew Thompson .if_index = 0,
3273671d9d8SAndrew Thompson .bufsize = UBT_CTRL_BUFFER_SIZE,
3283671d9d8SAndrew Thompson .callback = &ubt_ctrl_write_callback,
3293671d9d8SAndrew Thompson .timeout = 5000, /* 5 seconds */
3303671d9d8SAndrew Thompson },
3313671d9d8SAndrew Thompson
3323671d9d8SAndrew Thompson /*
3333671d9d8SAndrew Thompson * Interface #1
3343671d9d8SAndrew Thompson */
3353671d9d8SAndrew Thompson
3363671d9d8SAndrew Thompson /* Incoming isochronous transfer #1 - SCO packets */
3373671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_RD1] = {
3383671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS,
3393671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY,
3403671d9d8SAndrew Thompson .direction = UE_DIR_IN,
3413671d9d8SAndrew Thompson .if_index = 1,
3423671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */
3433671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES,
3443671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, },
3453671d9d8SAndrew Thompson .callback = &ubt_isoc_read_callback,
3463671d9d8SAndrew Thompson },
3473671d9d8SAndrew Thompson /* Incoming isochronous transfer #2 - SCO packets */
3483671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_RD2] = {
3493671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS,
3503671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY,
3513671d9d8SAndrew Thompson .direction = UE_DIR_IN,
3523671d9d8SAndrew Thompson .if_index = 1,
3533671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */
3543671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES,
3553671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, },
3563671d9d8SAndrew Thompson .callback = &ubt_isoc_read_callback,
3573671d9d8SAndrew Thompson },
3583671d9d8SAndrew Thompson /* Outgoing isochronous transfer #1 - SCO packets */
3593671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_WR1] = {
3603671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS,
3613671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY,
3623671d9d8SAndrew Thompson .direction = UE_DIR_OUT,
3633671d9d8SAndrew Thompson .if_index = 1,
3643671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */
3653671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES,
3663671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, },
3673671d9d8SAndrew Thompson .callback = &ubt_isoc_write_callback,
3683671d9d8SAndrew Thompson },
3693671d9d8SAndrew Thompson /* Outgoing isochronous transfer #2 - SCO packets */
3703671d9d8SAndrew Thompson [UBT_IF_1_ISOC_DT_WR2] = {
3713671d9d8SAndrew Thompson .type = UE_ISOCHRONOUS,
3723671d9d8SAndrew Thompson .endpoint = UE_ADDR_ANY,
3733671d9d8SAndrew Thompson .direction = UE_DIR_OUT,
3743671d9d8SAndrew Thompson .if_index = 1,
3753671d9d8SAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */
3763671d9d8SAndrew Thompson .frames = UBT_ISOC_NFRAMES,
3773671d9d8SAndrew Thompson .flags = { .short_xfer_ok = 1, },
3783671d9d8SAndrew Thompson .callback = &ubt_isoc_write_callback,
3793671d9d8SAndrew Thompson },
3803671d9d8SAndrew Thompson };
3813671d9d8SAndrew Thompson
3823671d9d8SAndrew Thompson /*
3833671d9d8SAndrew Thompson * If for some reason device should not be attached then put
3843671d9d8SAndrew Thompson * VendorID/ProductID pair into the list below. The format is
3853671d9d8SAndrew Thompson * as follows:
3863671d9d8SAndrew Thompson *
3873671d9d8SAndrew Thompson * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
3883671d9d8SAndrew Thompson *
3893671d9d8SAndrew Thompson * where VENDOR_ID and PRODUCT_ID are hex numbers.
3903671d9d8SAndrew Thompson */
3913671d9d8SAndrew Thompson
392f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_ignore_devs[] =
3933671d9d8SAndrew Thompson {
3943671d9d8SAndrew Thompson /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
3953671d9d8SAndrew Thompson { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
396c36c780dSAdrian Chadd
397c36c780dSAdrian Chadd /* Atheros 3011 with sflash firmware */
398c36c780dSAdrian Chadd { USB_VPI(0x0cf3, 0x3002, 0) },
399c36c780dSAdrian Chadd { USB_VPI(0x0cf3, 0xe019, 0) },
400c36c780dSAdrian Chadd { USB_VPI(0x13d3, 0x3304, 0) },
401c36c780dSAdrian Chadd { USB_VPI(0x0930, 0x0215, 0) },
402c36c780dSAdrian Chadd { USB_VPI(0x0489, 0xe03d, 0) },
403c36c780dSAdrian Chadd { USB_VPI(0x0489, 0xe027, 0) },
404c36c780dSAdrian Chadd
405c36c780dSAdrian Chadd /* Atheros AR9285 Malbec with sflash firmware */
406c36c780dSAdrian Chadd { USB_VPI(0x03f0, 0x311d, 0) },
407c36c780dSAdrian Chadd
408c36c780dSAdrian Chadd /* Atheros 3012 with sflash firmware */
4090818ec92SAdrian Chadd { USB_VPI(0x0cf3, 0x3004, 0), USB_DEV_BCD_LTEQ(1) },
4100818ec92SAdrian Chadd { USB_VPI(0x0cf3, 0x311d, 0), USB_DEV_BCD_LTEQ(1) },
4110818ec92SAdrian Chadd { USB_VPI(0x13d3, 0x3375, 0), USB_DEV_BCD_LTEQ(1) },
4120818ec92SAdrian Chadd { USB_VPI(0x04ca, 0x3005, 0), USB_DEV_BCD_LTEQ(1) },
4130818ec92SAdrian Chadd { USB_VPI(0x04ca, 0x3006, 0), USB_DEV_BCD_LTEQ(1) },
4140818ec92SAdrian Chadd { USB_VPI(0x04ca, 0x3008, 0), USB_DEV_BCD_LTEQ(1) },
4150818ec92SAdrian Chadd { USB_VPI(0x13d3, 0x3362, 0), USB_DEV_BCD_LTEQ(1) },
4160818ec92SAdrian Chadd { USB_VPI(0x0cf3, 0xe004, 0), USB_DEV_BCD_LTEQ(1) },
4170818ec92SAdrian Chadd { USB_VPI(0x0930, 0x0219, 0), USB_DEV_BCD_LTEQ(1) },
4180818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe057, 0), USB_DEV_BCD_LTEQ(1) },
4190818ec92SAdrian Chadd { USB_VPI(0x13d3, 0x3393, 0), USB_DEV_BCD_LTEQ(1) },
4200818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe04e, 0), USB_DEV_BCD_LTEQ(1) },
4210818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe056, 0), USB_DEV_BCD_LTEQ(1) },
422c36c780dSAdrian Chadd
423c36c780dSAdrian Chadd /* Atheros AR5BBU12 with sflash firmware */
4240818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe02c, 0), USB_DEV_BCD_LTEQ(1) },
425c36c780dSAdrian Chadd
426c36c780dSAdrian Chadd /* Atheros AR5BBU12 with sflash firmware */
4270818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) },
4280818ec92SAdrian Chadd { USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) },
4293544d43bSVladimir Kondratyev
430788a171cSVladimir Kondratyev /* Intel Wireless controllers are handled in ng_ubt_intel.c */
431788a171cSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x07dc, 0) },
432788a171cSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0a2a, 0) },
433788a171cSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0aa7, 0) },
4343544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0a2b, 0) },
4353544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0aaa, 0) },
4363544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0025, 0) },
4373544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0026, 0) },
4383544d43bSVladimir Kondratyev { USB_VPI(USB_VENDOR_INTEL2, 0x0029, 0) },
439b25ba58aSMark Johnston
440b25ba58aSMark Johnston /*
441b25ba58aSMark Johnston * Some Intel controllers are not yet supported by ng_ubt_intel and
442b25ba58aSMark Johnston * should be ignored.
443b25ba58aSMark Johnston */
444b25ba58aSMark Johnston { USB_VPI(USB_VENDOR_INTEL2, 0x0032, 0) },
445b25ba58aSMark Johnston { USB_VPI(USB_VENDOR_INTEL2, 0x0033, 0) },
4463671d9d8SAndrew Thompson };
4473671d9d8SAndrew Thompson
4483671d9d8SAndrew Thompson /* List of supported bluetooth devices */
449f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_devs[] =
4503671d9d8SAndrew Thompson {
4513671d9d8SAndrew Thompson /* Generic Bluetooth class devices */
4523671d9d8SAndrew Thompson { USB_IFACE_CLASS(UDCLASS_WIRELESS),
4533671d9d8SAndrew Thompson USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
4543671d9d8SAndrew Thompson USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
4553671d9d8SAndrew Thompson
4563671d9d8SAndrew Thompson /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
4573671d9d8SAndrew Thompson { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
45870a0e340SGleb Smirnoff
45970a0e340SGleb Smirnoff /* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
46070a0e340SGleb Smirnoff { USB_VENDOR(USB_VENDOR_BROADCOM),
461ac76cdc6SRaphael Kubo da Costa USB_IFACE_CLASS(UICLASS_VENDOR),
462ac76cdc6SRaphael Kubo da Costa USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
463ac76cdc6SRaphael Kubo da Costa USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
46433f4aa11SHans Petter Selasky
46533f4aa11SHans Petter Selasky /* Apple-specific (Broadcom) devices */
46633f4aa11SHans Petter Selasky { USB_VENDOR(USB_VENDOR_APPLE),
46733f4aa11SHans Petter Selasky USB_IFACE_CLASS(UICLASS_VENDOR),
46833f4aa11SHans Petter Selasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
46933f4aa11SHans Petter Selasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
47033f4aa11SHans Petter Selasky
47133f4aa11SHans Petter Selasky /* Foxconn - Hon Hai */
47233f4aa11SHans Petter Selasky { USB_VENDOR(USB_VENDOR_FOXCONN),
47333f4aa11SHans Petter Selasky USB_IFACE_CLASS(UICLASS_VENDOR),
47433f4aa11SHans Petter Selasky USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
47533f4aa11SHans Petter Selasky USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
47633f4aa11SHans Petter Selasky
47733f4aa11SHans Petter Selasky /* MediaTek MT76x0E */
47833f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },
47933f4aa11SHans Petter Selasky
48033f4aa11SHans Petter Selasky /* Broadcom SoftSailing reporting vendor specific */
48133f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },
48233f4aa11SHans Petter Selasky
48333f4aa11SHans Petter Selasky /* Apple MacBookPro 7,1 */
48433f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },
48533f4aa11SHans Petter Selasky
48633f4aa11SHans Petter Selasky /* Apple iMac11,1 */
48733f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },
48833f4aa11SHans Petter Selasky
48933f4aa11SHans Petter Selasky /* Apple MacBookPro6,2 */
49033f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },
49133f4aa11SHans Petter Selasky
49233f4aa11SHans Petter Selasky /* Apple MacBookAir3,1, MacBookAir3,2 */
49333f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },
49433f4aa11SHans Petter Selasky
49533f4aa11SHans Petter Selasky /* Apple MacBookAir4,1 */
49633f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },
49733f4aa11SHans Petter Selasky
49833f4aa11SHans Petter Selasky /* MacBookAir6,1 */
49933f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },
50033f4aa11SHans Petter Selasky
50133f4aa11SHans Petter Selasky /* Apple MacBookPro8,2 */
50233f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },
50333f4aa11SHans Petter Selasky
50433f4aa11SHans Petter Selasky /* Apple MacMini5,1 */
50533f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },
50633f4aa11SHans Petter Selasky
50733f4aa11SHans Petter Selasky /* Bluetooth Ultraport Module from IBM */
50833f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },
50933f4aa11SHans Petter Selasky
51033f4aa11SHans Petter Selasky /* ALPS Modules with non-standard ID */
51133f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },
51233f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },
51333f4aa11SHans Petter Selasky
51433f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },
51533f4aa11SHans Petter Selasky
51633f4aa11SHans Petter Selasky /* Canyon CN-BTU1 with HID interfaces */
51733f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },
51833f4aa11SHans Petter Selasky
51933f4aa11SHans Petter Selasky /* Broadcom BCM20702A0 */
52033f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },
521c6721651SEitan Adler { USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },
52233f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },
52333f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },
52433f4aa11SHans Petter Selasky { USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
5253a3992fbSMark Johnston { USB_VPI(USB_VENDOR_BELKIN, 0x065a, 0) },
5263671d9d8SAndrew Thompson };
5273671d9d8SAndrew Thompson
5283671d9d8SAndrew Thompson /*
5293544d43bSVladimir Kondratyev * Does a synchronous (waits for completion event) execution of HCI command.
5303544d43bSVladimir Kondratyev * Size of both command and response buffers are passed in length field of
5313544d43bSVladimir Kondratyev * corresponding structures in "Parameter Total Length" format i.e.
5323544d43bSVladimir Kondratyev * not including HCI packet headers.
5333544d43bSVladimir Kondratyev *
5343544d43bSVladimir Kondratyev * Must not be used after USB transfers have been configured in attach routine.
5353544d43bSVladimir Kondratyev */
5363544d43bSVladimir Kondratyev
5373544d43bSVladimir Kondratyev usb_error_t
ubt_do_hci_request(struct usb_device * udev,struct ubt_hci_cmd * cmd,void * evt,usb_timeout_t timeout)5383544d43bSVladimir Kondratyev ubt_do_hci_request(struct usb_device *udev, struct ubt_hci_cmd *cmd,
5393544d43bSVladimir Kondratyev void *evt, usb_timeout_t timeout)
5403544d43bSVladimir Kondratyev {
5413544d43bSVladimir Kondratyev static const struct usb_config ubt_probe_config = {
5423544d43bSVladimir Kondratyev .type = UE_INTERRUPT,
5433544d43bSVladimir Kondratyev .endpoint = UE_ADDR_ANY,
5443544d43bSVladimir Kondratyev .direction = UE_DIR_IN,
5453544d43bSVladimir Kondratyev .flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
5463544d43bSVladimir Kondratyev .bufsize = UBT_INTR_BUFFER_SIZE,
5473544d43bSVladimir Kondratyev .callback = &ubt_probe_intr_callback,
5483544d43bSVladimir Kondratyev };
5493544d43bSVladimir Kondratyev struct usb_device_request req;
5503544d43bSVladimir Kondratyev struct usb_xfer *xfer[1];
5513544d43bSVladimir Kondratyev struct mtx mtx;
5523544d43bSVladimir Kondratyev usb_error_t error = USB_ERR_NORMAL_COMPLETION;
5533544d43bSVladimir Kondratyev uint8_t iface_index = 0;
5543544d43bSVladimir Kondratyev
5553544d43bSVladimir Kondratyev /* Initialize a USB control request and then do it */
5563544d43bSVladimir Kondratyev bzero(&req, sizeof(req));
5573544d43bSVladimir Kondratyev req.bmRequestType = UBT_HCI_REQUEST;
5583544d43bSVladimir Kondratyev req.wIndex[0] = iface_index;
5593544d43bSVladimir Kondratyev USETW(req.wLength, UBT_HCI_CMD_SIZE(cmd));
5603544d43bSVladimir Kondratyev
5613544d43bSVladimir Kondratyev error = usbd_do_request(udev, NULL, &req, cmd);
5623544d43bSVladimir Kondratyev if (error != USB_ERR_NORMAL_COMPLETION) {
5633544d43bSVladimir Kondratyev printf("ng_ubt: usbd_do_request error=%s\n",
5643544d43bSVladimir Kondratyev usbd_errstr(error));
5653544d43bSVladimir Kondratyev return (error);
5663544d43bSVladimir Kondratyev }
5673544d43bSVladimir Kondratyev
5683544d43bSVladimir Kondratyev if (evt == NULL)
5693544d43bSVladimir Kondratyev return (USB_ERR_NORMAL_COMPLETION);
5703544d43bSVladimir Kondratyev
5713544d43bSVladimir Kondratyev /* Initialize INTR endpoint xfer and wait for response */
572443127c5SXin LI mtx_init(&mtx, "ubt pb", NULL, MTX_DEF | MTX_NEW);
5733544d43bSVladimir Kondratyev
5743544d43bSVladimir Kondratyev error = usbd_transfer_setup(udev, &iface_index, xfer,
5753544d43bSVladimir Kondratyev &ubt_probe_config, 1, evt, &mtx);
5763544d43bSVladimir Kondratyev if (error == USB_ERR_NORMAL_COMPLETION) {
5773544d43bSVladimir Kondratyev mtx_lock(&mtx);
5783544d43bSVladimir Kondratyev usbd_transfer_start(*xfer);
5793544d43bSVladimir Kondratyev
5803544d43bSVladimir Kondratyev if (msleep_sbt(evt, &mtx, 0, "ubt pb", SBT_1MS * timeout,
5813544d43bSVladimir Kondratyev 0, C_HARDCLOCK) == EWOULDBLOCK) {
5823544d43bSVladimir Kondratyev printf("ng_ubt: HCI command 0x%04x timed out\n",
5833544d43bSVladimir Kondratyev le16toh(cmd->opcode));
5843544d43bSVladimir Kondratyev error = USB_ERR_TIMEOUT;
5853544d43bSVladimir Kondratyev }
5863544d43bSVladimir Kondratyev
5873544d43bSVladimir Kondratyev usbd_transfer_stop(*xfer);
5883544d43bSVladimir Kondratyev mtx_unlock(&mtx);
5893544d43bSVladimir Kondratyev
5903544d43bSVladimir Kondratyev usbd_transfer_unsetup(xfer, 1);
5913544d43bSVladimir Kondratyev } else
5923544d43bSVladimir Kondratyev printf("ng_ubt: usbd_transfer_setup error=%s\n",
5933544d43bSVladimir Kondratyev usbd_errstr(error));
5943544d43bSVladimir Kondratyev
5953544d43bSVladimir Kondratyev mtx_destroy(&mtx);
5963544d43bSVladimir Kondratyev
5973544d43bSVladimir Kondratyev return (error);
5983544d43bSVladimir Kondratyev }
5993544d43bSVladimir Kondratyev
6003544d43bSVladimir Kondratyev /*
6013671d9d8SAndrew Thompson * Probe for a USB Bluetooth device.
6023671d9d8SAndrew Thompson * USB context.
6033671d9d8SAndrew Thompson */
6043671d9d8SAndrew Thompson
6053671d9d8SAndrew Thompson static int
ubt_probe(device_t dev)6063671d9d8SAndrew Thompson ubt_probe(device_t dev)
6073671d9d8SAndrew Thompson {
608760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev);
6099d28e15eSHans Petter Selasky const struct usb_device_id *id;
6103671d9d8SAndrew Thompson
6113671d9d8SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST)
6123671d9d8SAndrew Thompson return (ENXIO);
6133671d9d8SAndrew Thompson
614a593f6b8SAndrew Thompson if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
6153671d9d8SAndrew Thompson sizeof(ubt_ignore_devs), uaa) == 0)
6163671d9d8SAndrew Thompson return (ENXIO);
6173671d9d8SAndrew Thompson
6189d28e15eSHans Petter Selasky id = usbd_lookup_id_by_info(ubt_devs,
6199d28e15eSHans Petter Selasky sizeof(ubt_devs), &uaa->info);
6209d28e15eSHans Petter Selasky if (id == NULL)
6219d28e15eSHans Petter Selasky return (ENXIO);
6229d28e15eSHans Petter Selasky
6239d28e15eSHans Petter Selasky if (uaa->info.bIfaceIndex != 0) {
6249d28e15eSHans Petter Selasky /* make sure we are matching the interface */
6259d28e15eSHans Petter Selasky if (id->match_flag_int_class &&
6269d28e15eSHans Petter Selasky id->match_flag_int_subclass &&
6279d28e15eSHans Petter Selasky id->match_flag_int_protocol)
628a5db8fd1SAndriy Gapon return (BUS_PROBE_GENERIC);
6299d28e15eSHans Petter Selasky else
6309d28e15eSHans Petter Selasky return (ENXIO);
6319d28e15eSHans Petter Selasky } else {
6329d28e15eSHans Petter Selasky return (BUS_PROBE_GENERIC);
6339d28e15eSHans Petter Selasky }
6343671d9d8SAndrew Thompson } /* ubt_probe */
6353671d9d8SAndrew Thompson
6363671d9d8SAndrew Thompson /*
6373671d9d8SAndrew Thompson * Attach the device.
6383671d9d8SAndrew Thompson * USB context.
6393671d9d8SAndrew Thompson */
6403671d9d8SAndrew Thompson
6413671d9d8SAndrew Thompson static int
ubt_attach(device_t dev)6423671d9d8SAndrew Thompson ubt_attach(device_t dev)
6433671d9d8SAndrew Thompson {
644760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev);
6453671d9d8SAndrew Thompson struct ubt_softc *sc = device_get_softc(dev);
646760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed;
647760bc48eSAndrew Thompson struct usb_interface_descriptor *id;
6489d28e15eSHans Petter Selasky struct usb_interface *iface[2];
6494ac6682cSHans Petter Selasky uint32_t wMaxPacketSize;
6503671d9d8SAndrew Thompson uint8_t alt_index, i, j;
6519d28e15eSHans Petter Selasky uint8_t iface_index[2];
6523671d9d8SAndrew Thompson
653a593f6b8SAndrew Thompson device_set_usb_desc(dev);
6543671d9d8SAndrew Thompson
6559d28e15eSHans Petter Selasky iface_index[0] = uaa->info.bIfaceIndex;
6569d28e15eSHans Petter Selasky iface_index[1] = uaa->info.bIfaceIndex + 1;
6579d28e15eSHans Petter Selasky
6589d28e15eSHans Petter Selasky iface[0] = usbd_get_iface(uaa->device, iface_index[0]);
6599d28e15eSHans Petter Selasky iface[1] = usbd_get_iface(uaa->device, iface_index[1]);
6609d28e15eSHans Petter Selasky
6613671d9d8SAndrew Thompson sc->sc_dev = dev;
6623671d9d8SAndrew Thompson sc->sc_debug = NG_UBT_WARN_LEVEL;
66367272c5aSCraig Rodrigues
6643671d9d8SAndrew Thompson /*
6659d28e15eSHans Petter Selasky * Sanity checks.
6669d28e15eSHans Petter Selasky */
6679d28e15eSHans Petter Selasky
6689d28e15eSHans Petter Selasky if (iface[0] == NULL || iface[1] == NULL ||
6699d28e15eSHans Petter Selasky iface[0]->idesc == NULL || iface[1]->idesc == NULL) {
6709d28e15eSHans Petter Selasky UBT_ALERT(sc, "could not get two interfaces\n");
6719d28e15eSHans Petter Selasky return (ENXIO);
6729d28e15eSHans Petter Selasky }
6739d28e15eSHans Petter Selasky
6749d28e15eSHans Petter Selasky /*
6753671d9d8SAndrew Thompson * Create Netgraph node
6763671d9d8SAndrew Thompson */
6773671d9d8SAndrew Thompson
6783671d9d8SAndrew Thompson if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
6793671d9d8SAndrew Thompson UBT_ALERT(sc, "could not create Netgraph node\n");
6803671d9d8SAndrew Thompson return (ENXIO);
6813671d9d8SAndrew Thompson }
6823671d9d8SAndrew Thompson
6833671d9d8SAndrew Thompson /* Name Netgraph node */
6843671d9d8SAndrew Thompson if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
6853671d9d8SAndrew Thompson UBT_ALERT(sc, "could not name Netgraph node\n");
6863671d9d8SAndrew Thompson NG_NODE_UNREF(sc->sc_node);
6873671d9d8SAndrew Thompson return (ENXIO);
6883671d9d8SAndrew Thompson }
6893671d9d8SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc);
6903671d9d8SAndrew Thompson NG_NODE_FORCE_WRITER(sc->sc_node);
6913671d9d8SAndrew Thompson
6923671d9d8SAndrew Thompson /*
6933671d9d8SAndrew Thompson * Initialize device softc structure
6943671d9d8SAndrew Thompson */
6953671d9d8SAndrew Thompson
6963671d9d8SAndrew Thompson /* initialize locks */
6973671d9d8SAndrew Thompson mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
6983671d9d8SAndrew Thompson mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
6993671d9d8SAndrew Thompson
7003671d9d8SAndrew Thompson /* initialize packet queues */
7013671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
7023671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
7033671d9d8SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
7043671d9d8SAndrew Thompson
7053671d9d8SAndrew Thompson /* initialize glue task */
7063671d9d8SAndrew Thompson TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
7073671d9d8SAndrew Thompson
7083671d9d8SAndrew Thompson /*
7093671d9d8SAndrew Thompson * Configure Bluetooth USB device. Discover all required USB
7103671d9d8SAndrew Thompson * interfaces and endpoints.
7113671d9d8SAndrew Thompson *
7123671d9d8SAndrew Thompson * USB device must present two interfaces:
7133671d9d8SAndrew Thompson * 1) Interface 0 that has 3 endpoints
7143671d9d8SAndrew Thompson * 1) Interrupt endpoint to receive HCI events
7153671d9d8SAndrew Thompson * 2) Bulk IN endpoint to receive ACL data
7163671d9d8SAndrew Thompson * 3) Bulk OUT endpoint to send ACL data
7173671d9d8SAndrew Thompson *
7183671d9d8SAndrew Thompson * 2) Interface 1 then has 2 endpoints
7193671d9d8SAndrew Thompson * 1) Isochronous IN endpoint to receive SCO data
7203671d9d8SAndrew Thompson * 2) Isochronous OUT endpoint to send SCO data
7213671d9d8SAndrew Thompson *
7223671d9d8SAndrew Thompson * Interface 1 (with isochronous endpoints) has several alternate
7233671d9d8SAndrew Thompson * configurations with different packet size.
7243671d9d8SAndrew Thompson */
7253671d9d8SAndrew Thompson
7263671d9d8SAndrew Thompson /*
7273671d9d8SAndrew Thompson * For interface #1 search alternate settings, and find
7283671d9d8SAndrew Thompson * the descriptor with the largest wMaxPacketSize
7293671d9d8SAndrew Thompson */
7303671d9d8SAndrew Thompson
7313671d9d8SAndrew Thompson wMaxPacketSize = 0;
7323671d9d8SAndrew Thompson alt_index = 0;
7333671d9d8SAndrew Thompson i = 0;
7343671d9d8SAndrew Thompson j = 0;
7353671d9d8SAndrew Thompson ed = NULL;
7363671d9d8SAndrew Thompson
7373671d9d8SAndrew Thompson /*
7383671d9d8SAndrew Thompson * Search through all the descriptors looking for the largest
7393671d9d8SAndrew Thompson * packet size:
7403671d9d8SAndrew Thompson */
741a593f6b8SAndrew Thompson while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
742a593f6b8SAndrew Thompson usbd_get_config_descriptor(uaa->device),
743760bc48eSAndrew Thompson (struct usb_descriptor *)ed))) {
7443671d9d8SAndrew Thompson if ((ed->bDescriptorType == UDESC_INTERFACE) &&
7453671d9d8SAndrew Thompson (ed->bLength >= sizeof(*id))) {
746760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)ed;
7479d28e15eSHans Petter Selasky i = (id->bInterfaceNumber == iface[1]->idesc->bInterfaceNumber);
7483671d9d8SAndrew Thompson j = id->bAlternateSetting;
7493671d9d8SAndrew Thompson }
7503671d9d8SAndrew Thompson
7513671d9d8SAndrew Thompson if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
7523671d9d8SAndrew Thompson (ed->bLength >= sizeof(*ed)) &&
7539d28e15eSHans Petter Selasky (i != 0)) {
7544ac6682cSHans Petter Selasky uint32_t temp;
7553671d9d8SAndrew Thompson
7564ac6682cSHans Petter Selasky temp = usbd_get_max_frame_length(
7574ac6682cSHans Petter Selasky ed, NULL, usbd_get_speed(uaa->device));
7583671d9d8SAndrew Thompson if (temp > wMaxPacketSize) {
7593671d9d8SAndrew Thompson wMaxPacketSize = temp;
7603671d9d8SAndrew Thompson alt_index = j;
7613671d9d8SAndrew Thompson }
7623671d9d8SAndrew Thompson }
7633671d9d8SAndrew Thompson }
7643671d9d8SAndrew Thompson
7653671d9d8SAndrew Thompson /* Set alt configuration on interface #1 only if we found it */
7663671d9d8SAndrew Thompson if (wMaxPacketSize > 0 &&
7679d28e15eSHans Petter Selasky usbd_set_alt_interface_index(uaa->device, iface_index[1], alt_index)) {
7683671d9d8SAndrew Thompson UBT_ALERT(sc, "could not set alternate setting %d " \
7693671d9d8SAndrew Thompson "for interface 1!\n", alt_index);
7703671d9d8SAndrew Thompson goto detach;
7713671d9d8SAndrew Thompson }
7723671d9d8SAndrew Thompson
7733671d9d8SAndrew Thompson /* Setup transfers for both interfaces */
77467cbbf19SHans Petter Selasky if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, ubt_config,
77567cbbf19SHans Petter Selasky ng_usb_isoc_enable ? UBT_N_TRANSFER : UBT_IF_1_ISOC_DT_RD1,
77667cbbf19SHans Petter Selasky sc, &sc->sc_if_mtx)) {
7773671d9d8SAndrew Thompson UBT_ALERT(sc, "could not allocate transfers\n");
7783671d9d8SAndrew Thompson goto detach;
7793671d9d8SAndrew Thompson }
7803671d9d8SAndrew Thompson
7819d28e15eSHans Petter Selasky /* Claim second interface belonging to the Bluetooth part */
7829d28e15eSHans Petter Selasky usbd_set_parent_iface(uaa->device, iface_index[1], uaa->info.bIfaceIndex);
7833671d9d8SAndrew Thompson
7843671d9d8SAndrew Thompson return (0); /* success */
7853671d9d8SAndrew Thompson
7863671d9d8SAndrew Thompson detach:
7873671d9d8SAndrew Thompson ubt_detach(dev);
7883671d9d8SAndrew Thompson
7893671d9d8SAndrew Thompson return (ENXIO);
7903671d9d8SAndrew Thompson } /* ubt_attach */
7913671d9d8SAndrew Thompson
7923671d9d8SAndrew Thompson /*
7933671d9d8SAndrew Thompson * Detach the device.
7943671d9d8SAndrew Thompson * USB context.
7953671d9d8SAndrew Thompson */
7963671d9d8SAndrew Thompson
7973671d9d8SAndrew Thompson int
ubt_detach(device_t dev)7983671d9d8SAndrew Thompson ubt_detach(device_t dev)
7993671d9d8SAndrew Thompson {
8003671d9d8SAndrew Thompson struct ubt_softc *sc = device_get_softc(dev);
8013671d9d8SAndrew Thompson node_p node = sc->sc_node;
8023671d9d8SAndrew Thompson
8033671d9d8SAndrew Thompson /* Destroy Netgraph node */
8043671d9d8SAndrew Thompson if (node != NULL) {
8053671d9d8SAndrew Thompson sc->sc_node = NULL;
8063671d9d8SAndrew Thompson NG_NODE_REALLY_DIE(node);
8073671d9d8SAndrew Thompson ng_rmnode_self(node);
8083671d9d8SAndrew Thompson }
8093671d9d8SAndrew Thompson
8103671d9d8SAndrew Thompson /* Make sure ubt_task in gone */
8113671d9d8SAndrew Thompson taskqueue_drain(taskqueue_swi, &sc->sc_task);
8123671d9d8SAndrew Thompson
8133671d9d8SAndrew Thompson /* Free USB transfers, if any */
814a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
8153671d9d8SAndrew Thompson
8163671d9d8SAndrew Thompson /* Destroy queues */
8173671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
8183671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
8193671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
8203671d9d8SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
8213671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
8223671d9d8SAndrew Thompson
8233671d9d8SAndrew Thompson mtx_destroy(&sc->sc_if_mtx);
8243671d9d8SAndrew Thompson mtx_destroy(&sc->sc_ng_mtx);
8253671d9d8SAndrew Thompson
8263671d9d8SAndrew Thompson return (0);
8273671d9d8SAndrew Thompson } /* ubt_detach */
8283671d9d8SAndrew Thompson
8293671d9d8SAndrew Thompson /*
8303544d43bSVladimir Kondratyev * Called when incoming interrupt transfer (HCI event) has completed, i.e.
8313544d43bSVladimir Kondratyev * HCI event was received from the device during device probe stage.
8323544d43bSVladimir Kondratyev * USB context.
8333544d43bSVladimir Kondratyev */
8343544d43bSVladimir Kondratyev
8353544d43bSVladimir Kondratyev static void
ubt_probe_intr_callback(struct usb_xfer * xfer,usb_error_t error)8363544d43bSVladimir Kondratyev ubt_probe_intr_callback(struct usb_xfer *xfer, usb_error_t error)
8373544d43bSVladimir Kondratyev {
8383544d43bSVladimir Kondratyev struct ubt_hci_event *evt = usbd_xfer_softc(xfer);
8393544d43bSVladimir Kondratyev struct usb_page_cache *pc;
8403544d43bSVladimir Kondratyev int actlen;
8413544d43bSVladimir Kondratyev
8423544d43bSVladimir Kondratyev usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
8433544d43bSVladimir Kondratyev
8443544d43bSVladimir Kondratyev switch (USB_GET_STATE(xfer)) {
8453544d43bSVladimir Kondratyev case USB_ST_TRANSFERRED:
8463544d43bSVladimir Kondratyev if (actlen > UBT_HCI_EVENT_SIZE(evt))
8473544d43bSVladimir Kondratyev actlen = UBT_HCI_EVENT_SIZE(evt);
8483544d43bSVladimir Kondratyev pc = usbd_xfer_get_frame(xfer, 0);
8493544d43bSVladimir Kondratyev usbd_copy_out(pc, 0, evt, actlen);
8503544d43bSVladimir Kondratyev /* OneShot mode */
8513544d43bSVladimir Kondratyev wakeup(evt);
8523544d43bSVladimir Kondratyev break;
8533544d43bSVladimir Kondratyev
8543544d43bSVladimir Kondratyev case USB_ST_SETUP:
8553544d43bSVladimir Kondratyev submit_next:
8563544d43bSVladimir Kondratyev usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
8573544d43bSVladimir Kondratyev usbd_transfer_submit(xfer);
8583544d43bSVladimir Kondratyev break;
8593544d43bSVladimir Kondratyev
8603544d43bSVladimir Kondratyev default:
8613544d43bSVladimir Kondratyev if (error != USB_ERR_CANCELLED) {
8623544d43bSVladimir Kondratyev printf("ng_ubt: interrupt transfer failed: %s\n",
8633544d43bSVladimir Kondratyev usbd_errstr(error));
86452489f2aSVladimir Kondratyev /* Try clear stall first */
86552489f2aSVladimir Kondratyev usbd_xfer_set_stall(xfer);
8663544d43bSVladimir Kondratyev goto submit_next;
8673544d43bSVladimir Kondratyev }
8683544d43bSVladimir Kondratyev break;
8693544d43bSVladimir Kondratyev }
8703544d43bSVladimir Kondratyev } /* ubt_probe_intr_callback */
8713544d43bSVladimir Kondratyev
8723544d43bSVladimir Kondratyev /*
8733671d9d8SAndrew Thompson * Called when outgoing control request (HCI command) has completed, i.e.
8743671d9d8SAndrew Thompson * HCI command was sent to the device.
8753671d9d8SAndrew Thompson * USB context.
8763671d9d8SAndrew Thompson */
8773671d9d8SAndrew Thompson
8783671d9d8SAndrew Thompson static void
ubt_ctrl_write_callback(struct usb_xfer * xfer,usb_error_t error)879ed6d949aSAndrew Thompson ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
8803671d9d8SAndrew Thompson {
881ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer);
882760bc48eSAndrew Thompson struct usb_device_request req;
8833671d9d8SAndrew Thompson struct mbuf *m;
884ed6d949aSAndrew Thompson struct usb_page_cache *pc;
885ed6d949aSAndrew Thompson int actlen;
886ed6d949aSAndrew Thompson
887ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
8883671d9d8SAndrew Thompson
8893671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) {
8903671d9d8SAndrew Thompson case USB_ST_TRANSFERRED:
891ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
892ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen);
8933671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc);
8943671d9d8SAndrew Thompson /* FALLTHROUGH */
8953671d9d8SAndrew Thompson
8963671d9d8SAndrew Thompson case USB_ST_SETUP:
8973671d9d8SAndrew Thompson send_next:
8983671d9d8SAndrew Thompson /* Get next command mbuf, if any */
8993671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
9003671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
9013671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
9023671d9d8SAndrew Thompson
9033671d9d8SAndrew Thompson if (m == NULL) {
9043671d9d8SAndrew Thompson UBT_INFO(sc, "HCI command queue is empty\n");
9053671d9d8SAndrew Thompson break; /* transfer complete */
9063671d9d8SAndrew Thompson }
9073671d9d8SAndrew Thompson
9083671d9d8SAndrew Thompson /* Initialize a USB control request and then schedule it */
9093671d9d8SAndrew Thompson bzero(&req, sizeof(req));
9103671d9d8SAndrew Thompson req.bmRequestType = UBT_HCI_REQUEST;
9113671d9d8SAndrew Thompson USETW(req.wLength, m->m_pkthdr.len);
9123671d9d8SAndrew Thompson
9133671d9d8SAndrew Thompson UBT_INFO(sc, "Sending control request, " \
9143671d9d8SAndrew Thompson "bmRequestType=0x%02x, wLength=%d\n",
9153671d9d8SAndrew Thompson req.bmRequestType, UGETW(req.wLength));
9163671d9d8SAndrew Thompson
917ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
918ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, &req, sizeof(req));
919ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 1);
920ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
9213671d9d8SAndrew Thompson
922ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
923ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
924ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, 2);
9253671d9d8SAndrew Thompson
9263671d9d8SAndrew Thompson NG_FREE_M(m);
9273671d9d8SAndrew Thompson
928a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
9293671d9d8SAndrew Thompson break;
9303671d9d8SAndrew Thompson
9313671d9d8SAndrew Thompson default: /* Error */
932ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
9333671d9d8SAndrew Thompson UBT_WARN(sc, "control transfer failed: %s\n",
934ed6d949aSAndrew Thompson usbd_errstr(error));
9353671d9d8SAndrew Thompson
9363671d9d8SAndrew Thompson UBT_STAT_OERROR(sc);
9373671d9d8SAndrew Thompson goto send_next;
9383671d9d8SAndrew Thompson }
9393671d9d8SAndrew Thompson
9403671d9d8SAndrew Thompson /* transfer cancelled */
9413671d9d8SAndrew Thompson break;
9423671d9d8SAndrew Thompson }
9433671d9d8SAndrew Thompson } /* ubt_ctrl_write_callback */
9443671d9d8SAndrew Thompson
9453671d9d8SAndrew Thompson /*
9463671d9d8SAndrew Thompson * Called when incoming interrupt transfer (HCI event) has completed, i.e.
9473671d9d8SAndrew Thompson * HCI event was received from the device.
9483671d9d8SAndrew Thompson * USB context.
9493671d9d8SAndrew Thompson */
9503671d9d8SAndrew Thompson
9513671d9d8SAndrew Thompson static void
ubt_intr_read_callback(struct usb_xfer * xfer,usb_error_t error)952ed6d949aSAndrew Thompson ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
9533671d9d8SAndrew Thompson {
954ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer);
9553671d9d8SAndrew Thompson struct mbuf *m;
9563671d9d8SAndrew Thompson ng_hci_event_pkt_t *hdr;
957ed6d949aSAndrew Thompson struct usb_page_cache *pc;
958ed6d949aSAndrew Thompson int actlen;
959ed6d949aSAndrew Thompson
960ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
9613671d9d8SAndrew Thompson
9623671d9d8SAndrew Thompson m = NULL;
9633671d9d8SAndrew Thompson
9643671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) {
9653671d9d8SAndrew Thompson case USB_ST_TRANSFERRED:
9663671d9d8SAndrew Thompson /* Allocate a new mbuf */
967eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA);
9683671d9d8SAndrew Thompson if (m == NULL) {
9693671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
9703671d9d8SAndrew Thompson goto submit_next;
9713671d9d8SAndrew Thompson }
9723671d9d8SAndrew Thompson
9732a8c860fSRobert Watson if (!(MCLGET(m, M_NOWAIT))) {
9743671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
9753671d9d8SAndrew Thompson goto submit_next;
9763671d9d8SAndrew Thompson }
9773671d9d8SAndrew Thompson
9783671d9d8SAndrew Thompson /* Add HCI packet type */
9793671d9d8SAndrew Thompson *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
9803671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = 1;
9813671d9d8SAndrew Thompson
982ed6d949aSAndrew Thompson if (actlen > MCLBYTES - 1)
983ed6d949aSAndrew Thompson actlen = MCLBYTES - 1;
9843671d9d8SAndrew Thompson
985ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
986ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
987ed6d949aSAndrew Thompson m->m_pkthdr.len += actlen;
988ed6d949aSAndrew Thompson m->m_len += actlen;
9893671d9d8SAndrew Thompson
9903671d9d8SAndrew Thompson UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
991ed6d949aSAndrew Thompson actlen);
9923671d9d8SAndrew Thompson
9933671d9d8SAndrew Thompson /* Validate packet and send it up the stack */
9946d917491SHans Petter Selasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
9953671d9d8SAndrew Thompson UBT_INFO(sc, "HCI event packet is too short\n");
9963671d9d8SAndrew Thompson
9973671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
9983671d9d8SAndrew Thompson goto submit_next;
9993671d9d8SAndrew Thompson }
10003671d9d8SAndrew Thompson
10013671d9d8SAndrew Thompson hdr = mtod(m, ng_hci_event_pkt_t *);
10023671d9d8SAndrew Thompson if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
10033671d9d8SAndrew Thompson UBT_ERR(sc, "Invalid HCI event packet size, " \
10043671d9d8SAndrew Thompson "length=%d, pktlen=%d\n",
10053671d9d8SAndrew Thompson hdr->length, m->m_pkthdr.len);
10063671d9d8SAndrew Thompson
10073671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
10083671d9d8SAndrew Thompson goto submit_next;
10093671d9d8SAndrew Thompson }
10103671d9d8SAndrew Thompson
10113671d9d8SAndrew Thompson UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
10123671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, hdr->length);
10133671d9d8SAndrew Thompson
10143671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc);
10153671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
10163671d9d8SAndrew Thompson
10173671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m);
10183671d9d8SAndrew Thompson /* m == NULL at this point */
10193671d9d8SAndrew Thompson /* FALLTHROUGH */
10203671d9d8SAndrew Thompson
10213671d9d8SAndrew Thompson case USB_ST_SETUP:
10223671d9d8SAndrew Thompson submit_next:
10233671d9d8SAndrew Thompson NG_FREE_M(m); /* checks for m != NULL */
10243671d9d8SAndrew Thompson
1025ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1026a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
10273671d9d8SAndrew Thompson break;
10283671d9d8SAndrew Thompson
10293671d9d8SAndrew Thompson default: /* Error */
1030ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
10313671d9d8SAndrew Thompson UBT_WARN(sc, "interrupt transfer failed: %s\n",
1032ed6d949aSAndrew Thompson usbd_errstr(error));
10333671d9d8SAndrew Thompson
10343671d9d8SAndrew Thompson /* Try to clear stall first */
1035ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer);
10363671d9d8SAndrew Thompson goto submit_next;
10373671d9d8SAndrew Thompson }
10383671d9d8SAndrew Thompson /* transfer cancelled */
10393671d9d8SAndrew Thompson break;
10403671d9d8SAndrew Thompson }
10413671d9d8SAndrew Thompson } /* ubt_intr_read_callback */
10423671d9d8SAndrew Thompson
10433671d9d8SAndrew Thompson /*
10443671d9d8SAndrew Thompson * Called when incoming bulk transfer (ACL packet) has completed, i.e.
10453671d9d8SAndrew Thompson * ACL packet was received from the device.
10463671d9d8SAndrew Thompson * USB context.
10473671d9d8SAndrew Thompson */
10483671d9d8SAndrew Thompson
10493671d9d8SAndrew Thompson static void
ubt_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)1050ed6d949aSAndrew Thompson ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
10513671d9d8SAndrew Thompson {
1052ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer);
10533671d9d8SAndrew Thompson struct mbuf *m;
10543671d9d8SAndrew Thompson ng_hci_acldata_pkt_t *hdr;
1055ed6d949aSAndrew Thompson struct usb_page_cache *pc;
10566d917491SHans Petter Selasky int len;
1057ed6d949aSAndrew Thompson int actlen;
1058ed6d949aSAndrew Thompson
1059ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
10603671d9d8SAndrew Thompson
10613671d9d8SAndrew Thompson m = NULL;
10623671d9d8SAndrew Thompson
10633671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) {
10643671d9d8SAndrew Thompson case USB_ST_TRANSFERRED:
10653671d9d8SAndrew Thompson /* Allocate new mbuf */
1066eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA);
10673671d9d8SAndrew Thompson if (m == NULL) {
10683671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
10693671d9d8SAndrew Thompson goto submit_next;
10703671d9d8SAndrew Thompson }
10713671d9d8SAndrew Thompson
10722a8c860fSRobert Watson if (!(MCLGET(m, M_NOWAIT))) {
10733671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
10743671d9d8SAndrew Thompson goto submit_next;
10753671d9d8SAndrew Thompson }
10763671d9d8SAndrew Thompson
10773671d9d8SAndrew Thompson /* Add HCI packet type */
10783671d9d8SAndrew Thompson *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
10793671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = 1;
10803671d9d8SAndrew Thompson
1081ed6d949aSAndrew Thompson if (actlen > MCLBYTES - 1)
1082ed6d949aSAndrew Thompson actlen = MCLBYTES - 1;
10833671d9d8SAndrew Thompson
1084ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
1085ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
1086ed6d949aSAndrew Thompson m->m_pkthdr.len += actlen;
1087ed6d949aSAndrew Thompson m->m_len += actlen;
10883671d9d8SAndrew Thompson
10893671d9d8SAndrew Thompson UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
1090ed6d949aSAndrew Thompson actlen);
10913671d9d8SAndrew Thompson
10923671d9d8SAndrew Thompson /* Validate packet and send it up the stack */
10936d917491SHans Petter Selasky if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
10943671d9d8SAndrew Thompson UBT_INFO(sc, "HCI ACL packet is too short\n");
10953671d9d8SAndrew Thompson
10963671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
10973671d9d8SAndrew Thompson goto submit_next;
10983671d9d8SAndrew Thompson }
10993671d9d8SAndrew Thompson
11003671d9d8SAndrew Thompson hdr = mtod(m, ng_hci_acldata_pkt_t *);
11013671d9d8SAndrew Thompson len = le16toh(hdr->length);
11026d917491SHans Petter Selasky if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
11033671d9d8SAndrew Thompson UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
11043671d9d8SAndrew Thompson "pktlen=%d\n", len, m->m_pkthdr.len);
11053671d9d8SAndrew Thompson
11063671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
11073671d9d8SAndrew Thompson goto submit_next;
11083671d9d8SAndrew Thompson }
11093671d9d8SAndrew Thompson
11103671d9d8SAndrew Thompson UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
11113671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len, len);
11123671d9d8SAndrew Thompson
11133671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc);
11143671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
11153671d9d8SAndrew Thompson
11163671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m);
11173671d9d8SAndrew Thompson /* m == NULL at this point */
11183671d9d8SAndrew Thompson /* FALLTHOUGH */
11193671d9d8SAndrew Thompson
11203671d9d8SAndrew Thompson case USB_ST_SETUP:
11213671d9d8SAndrew Thompson submit_next:
11223671d9d8SAndrew Thompson NG_FREE_M(m); /* checks for m != NULL */
11233671d9d8SAndrew Thompson
1124ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1125a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
11263671d9d8SAndrew Thompson break;
11273671d9d8SAndrew Thompson
11283671d9d8SAndrew Thompson default: /* Error */
1129ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
11303671d9d8SAndrew Thompson UBT_WARN(sc, "bulk-in transfer failed: %s\n",
1131ed6d949aSAndrew Thompson usbd_errstr(error));
11323671d9d8SAndrew Thompson
11333671d9d8SAndrew Thompson /* Try to clear stall first */
1134ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer);
11353671d9d8SAndrew Thompson goto submit_next;
11363671d9d8SAndrew Thompson }
11373671d9d8SAndrew Thompson /* transfer cancelled */
11383671d9d8SAndrew Thompson break;
11393671d9d8SAndrew Thompson }
11403671d9d8SAndrew Thompson } /* ubt_bulk_read_callback */
11413671d9d8SAndrew Thompson
11423671d9d8SAndrew Thompson /*
11433671d9d8SAndrew Thompson * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
11443671d9d8SAndrew Thompson * ACL packet was sent to the device.
11453671d9d8SAndrew Thompson * USB context.
11463671d9d8SAndrew Thompson */
11473671d9d8SAndrew Thompson
11483671d9d8SAndrew Thompson static void
ubt_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)1149ed6d949aSAndrew Thompson ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
11503671d9d8SAndrew Thompson {
1151ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer);
11523671d9d8SAndrew Thompson struct mbuf *m;
1153ed6d949aSAndrew Thompson struct usb_page_cache *pc;
1154ed6d949aSAndrew Thompson int actlen;
1155ed6d949aSAndrew Thompson
1156ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
11573671d9d8SAndrew Thompson
11583671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) {
11593671d9d8SAndrew Thompson case USB_ST_TRANSFERRED:
1160ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
1161ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen);
11623671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc);
11633671d9d8SAndrew Thompson /* FALLTHROUGH */
11643671d9d8SAndrew Thompson
11653671d9d8SAndrew Thompson case USB_ST_SETUP:
11663671d9d8SAndrew Thompson send_next:
11673671d9d8SAndrew Thompson /* Get next mbuf, if any */
11683671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
11693671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
11703671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
11713671d9d8SAndrew Thompson
11723671d9d8SAndrew Thompson if (m == NULL) {
11733671d9d8SAndrew Thompson UBT_INFO(sc, "ACL data queue is empty\n");
11743671d9d8SAndrew Thompson break; /* transfer completed */
11753671d9d8SAndrew Thompson }
11763671d9d8SAndrew Thompson
11773671d9d8SAndrew Thompson /*
11783671d9d8SAndrew Thompson * Copy ACL data frame back to a linear USB transfer buffer
11793671d9d8SAndrew Thompson * and schedule transfer
11803671d9d8SAndrew Thompson */
11813671d9d8SAndrew Thompson
1182ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
1183ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
1184ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
11853671d9d8SAndrew Thompson
11863671d9d8SAndrew Thompson UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
11873671d9d8SAndrew Thompson m->m_pkthdr.len);
11883671d9d8SAndrew Thompson
11893671d9d8SAndrew Thompson NG_FREE_M(m);
11903671d9d8SAndrew Thompson
1191a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
11923671d9d8SAndrew Thompson break;
11933671d9d8SAndrew Thompson
11943671d9d8SAndrew Thompson default: /* Error */
1195ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
11963671d9d8SAndrew Thompson UBT_WARN(sc, "bulk-out transfer failed: %s\n",
1197ed6d949aSAndrew Thompson usbd_errstr(error));
11983671d9d8SAndrew Thompson
11993671d9d8SAndrew Thompson UBT_STAT_OERROR(sc);
12003671d9d8SAndrew Thompson
12013671d9d8SAndrew Thompson /* try to clear stall first */
1202ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer);
12033671d9d8SAndrew Thompson goto send_next;
12043671d9d8SAndrew Thompson }
12053671d9d8SAndrew Thompson /* transfer cancelled */
12063671d9d8SAndrew Thompson break;
12073671d9d8SAndrew Thompson }
12083671d9d8SAndrew Thompson } /* ubt_bulk_write_callback */
12093671d9d8SAndrew Thompson
12103671d9d8SAndrew Thompson /*
12113671d9d8SAndrew Thompson * Called when incoming isoc transfer (SCO packet) has completed, i.e.
12123671d9d8SAndrew Thompson * SCO packet was received from the device.
12133671d9d8SAndrew Thompson * USB context.
12143671d9d8SAndrew Thompson */
12153671d9d8SAndrew Thompson
12163671d9d8SAndrew Thompson static void
ubt_isoc_read_callback(struct usb_xfer * xfer,usb_error_t error)1217ed6d949aSAndrew Thompson ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
12183671d9d8SAndrew Thompson {
1219ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer);
12203671d9d8SAndrew Thompson int n;
1221ed6d949aSAndrew Thompson int actlen, nframes;
1222ed6d949aSAndrew Thompson
1223ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
12243671d9d8SAndrew Thompson
12253671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) {
12263671d9d8SAndrew Thompson case USB_ST_TRANSFERRED:
1227ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++)
12283671d9d8SAndrew Thompson if (ubt_isoc_read_one_frame(xfer, n) < 0)
12293671d9d8SAndrew Thompson break;
12303671d9d8SAndrew Thompson /* FALLTHROUGH */
12313671d9d8SAndrew Thompson
12323671d9d8SAndrew Thompson case USB_ST_SETUP:
12333671d9d8SAndrew Thompson read_next:
1234ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++)
1235ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n,
1236ed6d949aSAndrew Thompson usbd_xfer_max_framelen(xfer));
12373671d9d8SAndrew Thompson
1238a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
12393671d9d8SAndrew Thompson break;
12403671d9d8SAndrew Thompson
12413671d9d8SAndrew Thompson default: /* Error */
1242ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
12433671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
12443671d9d8SAndrew Thompson goto read_next;
12453671d9d8SAndrew Thompson }
12463671d9d8SAndrew Thompson
12473671d9d8SAndrew Thompson /* transfer cancelled */
12483671d9d8SAndrew Thompson break;
12493671d9d8SAndrew Thompson }
12503671d9d8SAndrew Thompson } /* ubt_isoc_read_callback */
12513671d9d8SAndrew Thompson
12523671d9d8SAndrew Thompson /*
12533671d9d8SAndrew Thompson * Helper function. Called from ubt_isoc_read_callback() to read
12543671d9d8SAndrew Thompson * SCO data from one frame.
12553671d9d8SAndrew Thompson * USB context.
12563671d9d8SAndrew Thompson */
12573671d9d8SAndrew Thompson
12583671d9d8SAndrew Thompson static int
ubt_isoc_read_one_frame(struct usb_xfer * xfer,int frame_no)1259760bc48eSAndrew Thompson ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
12603671d9d8SAndrew Thompson {
1261ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer);
1262ed6d949aSAndrew Thompson struct usb_page_cache *pc;
12633671d9d8SAndrew Thompson struct mbuf *m;
1264ed6d949aSAndrew Thompson int len, want, got, total;
12653671d9d8SAndrew Thompson
12663671d9d8SAndrew Thompson /* Get existing SCO reassembly buffer */
1267ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
12683671d9d8SAndrew Thompson m = sc->sc_isoc_in_buffer;
12698f9e0ef9SAndrew Thompson total = usbd_xfer_frame_len(xfer, frame_no);
12703671d9d8SAndrew Thompson
12713671d9d8SAndrew Thompson /* While we have data in the frame */
1272ed6d949aSAndrew Thompson while (total > 0) {
12733671d9d8SAndrew Thompson if (m == NULL) {
12743671d9d8SAndrew Thompson /* Start new reassembly buffer */
1275eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA);
12763671d9d8SAndrew Thompson if (m == NULL) {
12773671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
12783671d9d8SAndrew Thompson return (-1); /* XXX out of sync! */
12793671d9d8SAndrew Thompson }
12803671d9d8SAndrew Thompson
12812a8c860fSRobert Watson if (!(MCLGET(m, M_NOWAIT))) {
12823671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
12833671d9d8SAndrew Thompson NG_FREE_M(m);
12843671d9d8SAndrew Thompson return (-1); /* XXX out of sync! */
12853671d9d8SAndrew Thompson }
12863671d9d8SAndrew Thompson
12873671d9d8SAndrew Thompson /* Expect SCO header */
12883671d9d8SAndrew Thompson *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
12893671d9d8SAndrew Thompson m->m_pkthdr.len = m->m_len = got = 1;
12903671d9d8SAndrew Thompson want = sizeof(ng_hci_scodata_pkt_t);
12913671d9d8SAndrew Thompson } else {
12923671d9d8SAndrew Thompson /*
12933671d9d8SAndrew Thompson * Check if we have SCO header and if so
12943671d9d8SAndrew Thompson * adjust amount of data we want
12953671d9d8SAndrew Thompson */
12963671d9d8SAndrew Thompson got = m->m_pkthdr.len;
12973671d9d8SAndrew Thompson want = sizeof(ng_hci_scodata_pkt_t);
12983671d9d8SAndrew Thompson
12993671d9d8SAndrew Thompson if (got >= want)
13003671d9d8SAndrew Thompson want += mtod(m, ng_hci_scodata_pkt_t *)->length;
13013671d9d8SAndrew Thompson }
13023671d9d8SAndrew Thompson
13033671d9d8SAndrew Thompson /* Append frame data to the SCO reassembly buffer */
1304ed6d949aSAndrew Thompson len = total;
13053671d9d8SAndrew Thompson if (got + len > want)
13063671d9d8SAndrew Thompson len = want - got;
13073671d9d8SAndrew Thompson
1308ed6d949aSAndrew Thompson usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
13093671d9d8SAndrew Thompson mtod(m, uint8_t *) + m->m_pkthdr.len, len);
13103671d9d8SAndrew Thompson
13113671d9d8SAndrew Thompson m->m_pkthdr.len += len;
13123671d9d8SAndrew Thompson m->m_len += len;
1313ed6d949aSAndrew Thompson total -= len;
13143671d9d8SAndrew Thompson
13153671d9d8SAndrew Thompson /* Check if we got everything we wanted, if not - continue */
13163671d9d8SAndrew Thompson if (got != want)
13173671d9d8SAndrew Thompson continue;
13183671d9d8SAndrew Thompson
13193671d9d8SAndrew Thompson /* If we got here then we got complete SCO frame */
13203671d9d8SAndrew Thompson UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
13213671d9d8SAndrew Thompson "length=%d\n", m->m_pkthdr.len,
13223671d9d8SAndrew Thompson mtod(m, ng_hci_scodata_pkt_t *)->length);
13233671d9d8SAndrew Thompson
13243671d9d8SAndrew Thompson UBT_STAT_PCKTS_RECV(sc);
13253671d9d8SAndrew Thompson UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
13263671d9d8SAndrew Thompson
13273671d9d8SAndrew Thompson ubt_fwd_mbuf_up(sc, &m);
13283671d9d8SAndrew Thompson /* m == NULL at this point */
13293671d9d8SAndrew Thompson }
13303671d9d8SAndrew Thompson
13313671d9d8SAndrew Thompson /* Put SCO reassembly buffer back */
13323671d9d8SAndrew Thompson sc->sc_isoc_in_buffer = m;
13333671d9d8SAndrew Thompson
13343671d9d8SAndrew Thompson return (0);
13353671d9d8SAndrew Thompson } /* ubt_isoc_read_one_frame */
13363671d9d8SAndrew Thompson
13373671d9d8SAndrew Thompson /*
13383671d9d8SAndrew Thompson * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
13393671d9d8SAndrew Thompson * SCO packet was sent to the device.
13403671d9d8SAndrew Thompson * USB context.
13413671d9d8SAndrew Thompson */
13423671d9d8SAndrew Thompson
13433671d9d8SAndrew Thompson static void
ubt_isoc_write_callback(struct usb_xfer * xfer,usb_error_t error)1344ed6d949aSAndrew Thompson ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
13453671d9d8SAndrew Thompson {
1346ed6d949aSAndrew Thompson struct ubt_softc *sc = usbd_xfer_softc(xfer);
1347ed6d949aSAndrew Thompson struct usb_page_cache *pc;
13483671d9d8SAndrew Thompson struct mbuf *m;
13493671d9d8SAndrew Thompson int n, space, offset;
1350ed6d949aSAndrew Thompson int actlen, nframes;
1351ed6d949aSAndrew Thompson
1352ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1353ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
13543671d9d8SAndrew Thompson
13553671d9d8SAndrew Thompson switch (USB_GET_STATE(xfer)) {
13563671d9d8SAndrew Thompson case USB_ST_TRANSFERRED:
1357ed6d949aSAndrew Thompson UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1358ed6d949aSAndrew Thompson UBT_STAT_BYTES_SENT(sc, actlen);
13593671d9d8SAndrew Thompson UBT_STAT_PCKTS_SENT(sc);
13603671d9d8SAndrew Thompson /* FALLTHROUGH */
13613671d9d8SAndrew Thompson
13623671d9d8SAndrew Thompson case USB_ST_SETUP:
13633671d9d8SAndrew Thompson send_next:
13643671d9d8SAndrew Thompson offset = 0;
1365ed6d949aSAndrew Thompson space = usbd_xfer_max_framelen(xfer) * nframes;
13663671d9d8SAndrew Thompson m = NULL;
13673671d9d8SAndrew Thompson
13683671d9d8SAndrew Thompson while (space > 0) {
13693671d9d8SAndrew Thompson if (m == NULL) {
13703671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
13713671d9d8SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
13723671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
13733671d9d8SAndrew Thompson
13743671d9d8SAndrew Thompson if (m == NULL)
13753671d9d8SAndrew Thompson break;
13763671d9d8SAndrew Thompson }
13773671d9d8SAndrew Thompson
13783671d9d8SAndrew Thompson n = min(space, m->m_pkthdr.len);
13793671d9d8SAndrew Thompson if (n > 0) {
1380ed6d949aSAndrew Thompson usbd_m_copy_in(pc, offset, m,0, n);
13813671d9d8SAndrew Thompson m_adj(m, n);
13823671d9d8SAndrew Thompson
13833671d9d8SAndrew Thompson offset += n;
13843671d9d8SAndrew Thompson space -= n;
13853671d9d8SAndrew Thompson }
13863671d9d8SAndrew Thompson
13873671d9d8SAndrew Thompson if (m->m_pkthdr.len == 0)
13883671d9d8SAndrew Thompson NG_FREE_M(m); /* sets m = NULL */
13893671d9d8SAndrew Thompson }
13903671d9d8SAndrew Thompson
13913671d9d8SAndrew Thompson /* Put whatever is left from mbuf back on queue */
13923671d9d8SAndrew Thompson if (m != NULL) {
13933671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
13943671d9d8SAndrew Thompson NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
13953671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
13963671d9d8SAndrew Thompson }
13973671d9d8SAndrew Thompson
13983671d9d8SAndrew Thompson /*
13993671d9d8SAndrew Thompson * Calculate sizes for isoc frames.
14003671d9d8SAndrew Thompson * Note that offset could be 0 at this point (i.e. we have
14013671d9d8SAndrew Thompson * nothing to send). That is fine, as we have isoc. transfers
14023671d9d8SAndrew Thompson * going in both directions all the time. In this case it
14033671d9d8SAndrew Thompson * would be just empty isoc. transfer.
14043671d9d8SAndrew Thompson */
14053671d9d8SAndrew Thompson
1406ed6d949aSAndrew Thompson for (n = 0; n < nframes; n ++) {
1407ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n,
1408ed6d949aSAndrew Thompson min(offset, usbd_xfer_max_framelen(xfer)));
14098f9e0ef9SAndrew Thompson offset -= usbd_xfer_frame_len(xfer, n);
14103671d9d8SAndrew Thompson }
14113671d9d8SAndrew Thompson
1412a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
14133671d9d8SAndrew Thompson break;
14143671d9d8SAndrew Thompson
14153671d9d8SAndrew Thompson default: /* Error */
1416ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
14173671d9d8SAndrew Thompson UBT_STAT_OERROR(sc);
14183671d9d8SAndrew Thompson goto send_next;
14193671d9d8SAndrew Thompson }
14203671d9d8SAndrew Thompson
14213671d9d8SAndrew Thompson /* transfer cancelled */
14223671d9d8SAndrew Thompson break;
14233671d9d8SAndrew Thompson }
14243671d9d8SAndrew Thompson }
14253671d9d8SAndrew Thompson
14263671d9d8SAndrew Thompson /*
14273671d9d8SAndrew Thompson * Utility function to forward provided mbuf upstream (i.e. up the stack).
14283671d9d8SAndrew Thompson * Modifies value of the mbuf pointer (sets it to NULL).
14293671d9d8SAndrew Thompson * Save to call from any context.
14303671d9d8SAndrew Thompson */
14313671d9d8SAndrew Thompson
14323671d9d8SAndrew Thompson static int
ubt_fwd_mbuf_up(ubt_softc_p sc,struct mbuf ** m)14333671d9d8SAndrew Thompson ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
14343671d9d8SAndrew Thompson {
14353671d9d8SAndrew Thompson hook_p hook;
14363671d9d8SAndrew Thompson int error;
14373671d9d8SAndrew Thompson
14383671d9d8SAndrew Thompson /*
14393671d9d8SAndrew Thompson * Close the race with Netgraph hook newhook/disconnect methods.
14403671d9d8SAndrew Thompson * Save the hook pointer atomically. Two cases are possible:
14413671d9d8SAndrew Thompson *
14423671d9d8SAndrew Thompson * 1) The hook pointer is NULL. It means disconnect method got
14433671d9d8SAndrew Thompson * there first. In this case we are done.
14443671d9d8SAndrew Thompson *
14453671d9d8SAndrew Thompson * 2) The hook pointer is not NULL. It means that hook pointer
14463671d9d8SAndrew Thompson * could be either in valid or invalid (i.e. in the process
14473671d9d8SAndrew Thompson * of disconnect) state. In any case grab an extra reference
14483671d9d8SAndrew Thompson * to protect the hook pointer.
14493671d9d8SAndrew Thompson *
14503671d9d8SAndrew Thompson * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
14513671d9d8SAndrew Thompson * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
14523671d9d8SAndrew Thompson */
14533671d9d8SAndrew Thompson
14543671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
14553671d9d8SAndrew Thompson if ((hook = sc->sc_hook) != NULL)
14563671d9d8SAndrew Thompson NG_HOOK_REF(hook);
14573671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
14583671d9d8SAndrew Thompson
14593671d9d8SAndrew Thompson if (hook == NULL) {
14603671d9d8SAndrew Thompson NG_FREE_M(*m);
14613671d9d8SAndrew Thompson return (ENETDOWN);
14623671d9d8SAndrew Thompson }
14633671d9d8SAndrew Thompson
14643671d9d8SAndrew Thompson NG_SEND_DATA_ONLY(error, hook, *m);
14653671d9d8SAndrew Thompson NG_HOOK_UNREF(hook);
14663671d9d8SAndrew Thompson
14673671d9d8SAndrew Thompson if (error != 0)
14683671d9d8SAndrew Thompson UBT_STAT_IERROR(sc);
14693671d9d8SAndrew Thompson
14703671d9d8SAndrew Thompson return (error);
14713671d9d8SAndrew Thompson } /* ubt_fwd_mbuf_up */
14723671d9d8SAndrew Thompson
14733671d9d8SAndrew Thompson /****************************************************************************
14743671d9d8SAndrew Thompson ****************************************************************************
14753671d9d8SAndrew Thompson ** Glue
14763671d9d8SAndrew Thompson ****************************************************************************
14773671d9d8SAndrew Thompson ****************************************************************************/
14783671d9d8SAndrew Thompson
14793671d9d8SAndrew Thompson /*
14803671d9d8SAndrew Thompson * Schedule glue task. Should be called with sc_ng_mtx held.
14813671d9d8SAndrew Thompson * Netgraph context.
14823671d9d8SAndrew Thompson */
14833671d9d8SAndrew Thompson
14843671d9d8SAndrew Thompson static void
ubt_task_schedule(ubt_softc_p sc,int action)14853671d9d8SAndrew Thompson ubt_task_schedule(ubt_softc_p sc, int action)
14863671d9d8SAndrew Thompson {
14873671d9d8SAndrew Thompson mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
14883671d9d8SAndrew Thompson
14893671d9d8SAndrew Thompson /*
14903671d9d8SAndrew Thompson * Try to handle corner case when "start all" and "stop all"
14913671d9d8SAndrew Thompson * actions can both be set before task is executed.
14923671d9d8SAndrew Thompson *
14933671d9d8SAndrew Thompson * The rules are
14943671d9d8SAndrew Thompson *
14953671d9d8SAndrew Thompson * sc_task_flags action new sc_task_flags
14963671d9d8SAndrew Thompson * ------------------------------------------------------
14973671d9d8SAndrew Thompson * 0 start start
14983671d9d8SAndrew Thompson * 0 stop stop
14993671d9d8SAndrew Thompson * start start start
15003671d9d8SAndrew Thompson * start stop stop
15013671d9d8SAndrew Thompson * stop start stop|start
15023671d9d8SAndrew Thompson * stop stop stop
15033671d9d8SAndrew Thompson * stop|start start stop|start
15043671d9d8SAndrew Thompson * stop|start stop stop
15053671d9d8SAndrew Thompson */
15063671d9d8SAndrew Thompson
15073671d9d8SAndrew Thompson if (action != 0) {
15083671d9d8SAndrew Thompson if ((action & UBT_FLAG_T_STOP_ALL) != 0)
15093671d9d8SAndrew Thompson sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
15103671d9d8SAndrew Thompson
15113671d9d8SAndrew Thompson sc->sc_task_flags |= action;
15123671d9d8SAndrew Thompson }
15133671d9d8SAndrew Thompson
15143671d9d8SAndrew Thompson if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
15153671d9d8SAndrew Thompson return;
15163671d9d8SAndrew Thompson
15173671d9d8SAndrew Thompson if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
15183671d9d8SAndrew Thompson sc->sc_task_flags |= UBT_FLAG_T_PENDING;
15193671d9d8SAndrew Thompson return;
15203671d9d8SAndrew Thompson }
15213671d9d8SAndrew Thompson
15223671d9d8SAndrew Thompson /* XXX: i think this should never happen */
15233671d9d8SAndrew Thompson } /* ubt_task_schedule */
15243671d9d8SAndrew Thompson
15253671d9d8SAndrew Thompson /*
15263671d9d8SAndrew Thompson * Glue task. Examines sc_task_flags and does things depending on it.
15273671d9d8SAndrew Thompson * Taskqueue context.
15283671d9d8SAndrew Thompson */
15293671d9d8SAndrew Thompson
15303671d9d8SAndrew Thompson static void
ubt_task(void * context,int pending)15313671d9d8SAndrew Thompson ubt_task(void *context, int pending)
15323671d9d8SAndrew Thompson {
15333671d9d8SAndrew Thompson ubt_softc_p sc = context;
15343671d9d8SAndrew Thompson int task_flags, i;
15353671d9d8SAndrew Thompson
15363671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
15373671d9d8SAndrew Thompson task_flags = sc->sc_task_flags;
15383671d9d8SAndrew Thompson sc->sc_task_flags = 0;
15393671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
15403671d9d8SAndrew Thompson
15413671d9d8SAndrew Thompson /*
15423671d9d8SAndrew Thompson * Stop all USB transfers synchronously.
15433671d9d8SAndrew Thompson * Stop interface #0 and #1 transfers at the same time and in the
1544a593f6b8SAndrew Thompson * same loop. usbd_transfer_drain() will do appropriate locking.
15453671d9d8SAndrew Thompson */
15463671d9d8SAndrew Thompson
15473671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_STOP_ALL)
15483671d9d8SAndrew Thompson for (i = 0; i < UBT_N_TRANSFER; i ++)
1549a593f6b8SAndrew Thompson usbd_transfer_drain(sc->sc_xfer[i]);
15503671d9d8SAndrew Thompson
15513671d9d8SAndrew Thompson /* Start incoming interrupt and bulk, and all isoc. USB transfers */
15523671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_ALL) {
15533671d9d8SAndrew Thompson /*
15543671d9d8SAndrew Thompson * Interface #0
15553671d9d8SAndrew Thompson */
15563671d9d8SAndrew Thompson
15573671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx);
15583671d9d8SAndrew Thompson
15593671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
15603671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
15613671d9d8SAndrew Thompson
15623671d9d8SAndrew Thompson /*
15633671d9d8SAndrew Thompson * Interface #1
15643671d9d8SAndrew Thompson * Start both read and write isoc. transfers by default.
15653671d9d8SAndrew Thompson * Get them going all the time even if we have nothing
15663671d9d8SAndrew Thompson * to send to avoid any delays.
15673671d9d8SAndrew Thompson */
15683671d9d8SAndrew Thompson
15693671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
15703671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
15713671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
15723671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
15733671d9d8SAndrew Thompson
15743671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx);
15753671d9d8SAndrew Thompson }
15763671d9d8SAndrew Thompson
15773671d9d8SAndrew Thompson /* Start outgoing control transfer */
15783671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_CTRL) {
15793671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx);
15803671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
15813671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx);
15823671d9d8SAndrew Thompson }
15833671d9d8SAndrew Thompson
15843671d9d8SAndrew Thompson /* Start outgoing bulk transfer */
15853671d9d8SAndrew Thompson if (task_flags & UBT_FLAG_T_START_BULK) {
15863671d9d8SAndrew Thompson mtx_lock(&sc->sc_if_mtx);
15873671d9d8SAndrew Thompson ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
15883671d9d8SAndrew Thompson mtx_unlock(&sc->sc_if_mtx);
15893671d9d8SAndrew Thompson }
15903671d9d8SAndrew Thompson } /* ubt_task */
15913671d9d8SAndrew Thompson
15923671d9d8SAndrew Thompson /****************************************************************************
15933671d9d8SAndrew Thompson ****************************************************************************
15943671d9d8SAndrew Thompson ** Netgraph specific
15953671d9d8SAndrew Thompson ****************************************************************************
15963671d9d8SAndrew Thompson ****************************************************************************/
15973671d9d8SAndrew Thompson
15983671d9d8SAndrew Thompson /*
15993671d9d8SAndrew Thompson * Netgraph node constructor. Do not allow to create node of this type.
16003671d9d8SAndrew Thompson * Netgraph context.
16013671d9d8SAndrew Thompson */
16023671d9d8SAndrew Thompson
16033671d9d8SAndrew Thompson static int
ng_ubt_constructor(node_p node)16043671d9d8SAndrew Thompson ng_ubt_constructor(node_p node)
16053671d9d8SAndrew Thompson {
16063671d9d8SAndrew Thompson return (EINVAL);
16073671d9d8SAndrew Thompson } /* ng_ubt_constructor */
16083671d9d8SAndrew Thompson
16093671d9d8SAndrew Thompson /*
16103671d9d8SAndrew Thompson * Netgraph node destructor. Destroy node only when device has been detached.
16113671d9d8SAndrew Thompson * Netgraph context.
16123671d9d8SAndrew Thompson */
16133671d9d8SAndrew Thompson
16143671d9d8SAndrew Thompson static int
ng_ubt_shutdown(node_p node)16153671d9d8SAndrew Thompson ng_ubt_shutdown(node_p node)
16163671d9d8SAndrew Thompson {
16173671d9d8SAndrew Thompson if (node->nd_flags & NGF_REALLY_DIE) {
16183671d9d8SAndrew Thompson /*
16193671d9d8SAndrew Thompson * We came here because the USB device is being
1620053359b7SPedro F. Giffuni * detached, so stop being persistent.
16213671d9d8SAndrew Thompson */
16223671d9d8SAndrew Thompson NG_NODE_SET_PRIVATE(node, NULL);
16233671d9d8SAndrew Thompson NG_NODE_UNREF(node);
16243671d9d8SAndrew Thompson } else
16253671d9d8SAndrew Thompson NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
16263671d9d8SAndrew Thompson
16273671d9d8SAndrew Thompson return (0);
16283671d9d8SAndrew Thompson } /* ng_ubt_shutdown */
16293671d9d8SAndrew Thompson
16303671d9d8SAndrew Thompson /*
16313671d9d8SAndrew Thompson * Create new hook. There can only be one.
16323671d9d8SAndrew Thompson * Netgraph context.
16333671d9d8SAndrew Thompson */
16343671d9d8SAndrew Thompson
16353671d9d8SAndrew Thompson static int
ng_ubt_newhook(node_p node,hook_p hook,char const * name)16363671d9d8SAndrew Thompson ng_ubt_newhook(node_p node, hook_p hook, char const *name)
16373671d9d8SAndrew Thompson {
16383671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(node);
16393671d9d8SAndrew Thompson
16403671d9d8SAndrew Thompson if (strcmp(name, NG_UBT_HOOK) != 0)
16413671d9d8SAndrew Thompson return (EINVAL);
16423671d9d8SAndrew Thompson
16433671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
16443671d9d8SAndrew Thompson if (sc->sc_hook != NULL) {
16453671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
16463671d9d8SAndrew Thompson
16473671d9d8SAndrew Thompson return (EISCONN);
16483671d9d8SAndrew Thompson }
16493671d9d8SAndrew Thompson
16503671d9d8SAndrew Thompson sc->sc_hook = hook;
16513671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
16523671d9d8SAndrew Thompson
16533671d9d8SAndrew Thompson return (0);
16543671d9d8SAndrew Thompson } /* ng_ubt_newhook */
16553671d9d8SAndrew Thompson
16563671d9d8SAndrew Thompson /*
16573671d9d8SAndrew Thompson * Connect hook. Start incoming USB transfers.
16583671d9d8SAndrew Thompson * Netgraph context.
16593671d9d8SAndrew Thompson */
16603671d9d8SAndrew Thompson
16613671d9d8SAndrew Thompson static int
ng_ubt_connect(hook_p hook)16623671d9d8SAndrew Thompson ng_ubt_connect(hook_p hook)
16633671d9d8SAndrew Thompson {
16643671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
16653671d9d8SAndrew Thompson
16663671d9d8SAndrew Thompson NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
16673671d9d8SAndrew Thompson
16683671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
16693671d9d8SAndrew Thompson ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
16703671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
16713671d9d8SAndrew Thompson
16723671d9d8SAndrew Thompson return (0);
16733671d9d8SAndrew Thompson } /* ng_ubt_connect */
16743671d9d8SAndrew Thompson
16753671d9d8SAndrew Thompson /*
16763671d9d8SAndrew Thompson * Disconnect hook.
16773671d9d8SAndrew Thompson * Netgraph context.
16783671d9d8SAndrew Thompson */
16793671d9d8SAndrew Thompson
16803671d9d8SAndrew Thompson static int
ng_ubt_disconnect(hook_p hook)16813671d9d8SAndrew Thompson ng_ubt_disconnect(hook_p hook)
16823671d9d8SAndrew Thompson {
16833671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
16843671d9d8SAndrew Thompson
16853671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
16863671d9d8SAndrew Thompson
16873671d9d8SAndrew Thompson if (hook != sc->sc_hook) {
16883671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
16893671d9d8SAndrew Thompson
16903671d9d8SAndrew Thompson return (EINVAL);
16913671d9d8SAndrew Thompson }
16923671d9d8SAndrew Thompson
16933671d9d8SAndrew Thompson sc->sc_hook = NULL;
16943671d9d8SAndrew Thompson
16953671d9d8SAndrew Thompson /* Kick off task to stop all USB xfers */
16963671d9d8SAndrew Thompson ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
16973671d9d8SAndrew Thompson
16983671d9d8SAndrew Thompson /* Drain queues */
16993671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
17003671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
17013671d9d8SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
17023671d9d8SAndrew Thompson
17033671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
17043671d9d8SAndrew Thompson
17053671d9d8SAndrew Thompson return (0);
17063671d9d8SAndrew Thompson } /* ng_ubt_disconnect */
17073671d9d8SAndrew Thompson
17083671d9d8SAndrew Thompson /*
17093671d9d8SAndrew Thompson * Process control message.
17103671d9d8SAndrew Thompson * Netgraph context.
17113671d9d8SAndrew Thompson */
17123671d9d8SAndrew Thompson
17133671d9d8SAndrew Thompson static int
ng_ubt_rcvmsg(node_p node,item_p item,hook_p lasthook)17143671d9d8SAndrew Thompson ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
17153671d9d8SAndrew Thompson {
17163671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(node);
17173671d9d8SAndrew Thompson struct ng_mesg *msg, *rsp = NULL;
17183671d9d8SAndrew Thompson struct ng_bt_mbufq *q;
17193671d9d8SAndrew Thompson int error = 0, queue, qlen;
17203671d9d8SAndrew Thompson
17213671d9d8SAndrew Thompson NGI_GET_MSG(item, msg);
17223671d9d8SAndrew Thompson
17233671d9d8SAndrew Thompson switch (msg->header.typecookie) {
17243671d9d8SAndrew Thompson case NGM_GENERIC_COOKIE:
17253671d9d8SAndrew Thompson switch (msg->header.cmd) {
17263671d9d8SAndrew Thompson case NGM_TEXT_STATUS:
17273671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
17283671d9d8SAndrew Thompson if (rsp == NULL) {
17293671d9d8SAndrew Thompson error = ENOMEM;
17303671d9d8SAndrew Thompson break;
17313671d9d8SAndrew Thompson }
17323671d9d8SAndrew Thompson
17333671d9d8SAndrew Thompson snprintf(rsp->data, NG_TEXTRESPONSE,
17343671d9d8SAndrew Thompson "Hook: %s\n" \
17353671d9d8SAndrew Thompson "Task flags: %#x\n" \
17363671d9d8SAndrew Thompson "Debug: %d\n" \
17373671d9d8SAndrew Thompson "CMD queue: [have:%d,max:%d]\n" \
17383671d9d8SAndrew Thompson "ACL queue: [have:%d,max:%d]\n" \
17393671d9d8SAndrew Thompson "SCO queue: [have:%d,max:%d]",
17403671d9d8SAndrew Thompson (sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
17413671d9d8SAndrew Thompson sc->sc_task_flags,
17423671d9d8SAndrew Thompson sc->sc_debug,
17433671d9d8SAndrew Thompson sc->sc_cmdq.len,
17443671d9d8SAndrew Thompson sc->sc_cmdq.maxlen,
17453671d9d8SAndrew Thompson sc->sc_aclq.len,
17463671d9d8SAndrew Thompson sc->sc_aclq.maxlen,
17473671d9d8SAndrew Thompson sc->sc_scoq.len,
17483671d9d8SAndrew Thompson sc->sc_scoq.maxlen);
17493671d9d8SAndrew Thompson break;
17503671d9d8SAndrew Thompson
17513671d9d8SAndrew Thompson default:
17523671d9d8SAndrew Thompson error = EINVAL;
17533671d9d8SAndrew Thompson break;
17543671d9d8SAndrew Thompson }
17553671d9d8SAndrew Thompson break;
17563671d9d8SAndrew Thompson
17573671d9d8SAndrew Thompson case NGM_UBT_COOKIE:
17583671d9d8SAndrew Thompson switch (msg->header.cmd) {
17593671d9d8SAndrew Thompson case NGM_UBT_NODE_SET_DEBUG:
17603671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
17613671d9d8SAndrew Thompson error = EMSGSIZE;
17623671d9d8SAndrew Thompson break;
17633671d9d8SAndrew Thompson }
17643671d9d8SAndrew Thompson
17653671d9d8SAndrew Thompson sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
17663671d9d8SAndrew Thompson break;
17673671d9d8SAndrew Thompson
17683671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_DEBUG:
17693671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
17703671d9d8SAndrew Thompson M_NOWAIT);
17713671d9d8SAndrew Thompson if (rsp == NULL) {
17723671d9d8SAndrew Thompson error = ENOMEM;
17733671d9d8SAndrew Thompson break;
17743671d9d8SAndrew Thompson }
17753671d9d8SAndrew Thompson
17763671d9d8SAndrew Thompson *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
17773671d9d8SAndrew Thompson break;
17783671d9d8SAndrew Thompson
17793671d9d8SAndrew Thompson case NGM_UBT_NODE_SET_QLEN:
17803671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
17813671d9d8SAndrew Thompson error = EMSGSIZE;
17823671d9d8SAndrew Thompson break;
17833671d9d8SAndrew Thompson }
17843671d9d8SAndrew Thompson
17853671d9d8SAndrew Thompson queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
17863671d9d8SAndrew Thompson qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
17873671d9d8SAndrew Thompson
17883671d9d8SAndrew Thompson switch (queue) {
17893671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_CMD:
17903671d9d8SAndrew Thompson q = &sc->sc_cmdq;
17913671d9d8SAndrew Thompson break;
17923671d9d8SAndrew Thompson
17933671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_ACL:
17943671d9d8SAndrew Thompson q = &sc->sc_aclq;
17953671d9d8SAndrew Thompson break;
17963671d9d8SAndrew Thompson
17973671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_SCO:
17983671d9d8SAndrew Thompson q = &sc->sc_scoq;
17993671d9d8SAndrew Thompson break;
18003671d9d8SAndrew Thompson
18013671d9d8SAndrew Thompson default:
18023671d9d8SAndrew Thompson error = EINVAL;
18033671d9d8SAndrew Thompson goto done;
18043671d9d8SAndrew Thompson /* NOT REACHED */
18053671d9d8SAndrew Thompson }
18063671d9d8SAndrew Thompson
18073671d9d8SAndrew Thompson q->maxlen = qlen;
18083671d9d8SAndrew Thompson break;
18093671d9d8SAndrew Thompson
18103671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_QLEN:
18113671d9d8SAndrew Thompson if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
18123671d9d8SAndrew Thompson error = EMSGSIZE;
18133671d9d8SAndrew Thompson break;
18143671d9d8SAndrew Thompson }
18153671d9d8SAndrew Thompson
18163671d9d8SAndrew Thompson queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
18173671d9d8SAndrew Thompson
18183671d9d8SAndrew Thompson switch (queue) {
18193671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_CMD:
18203671d9d8SAndrew Thompson q = &sc->sc_cmdq;
18213671d9d8SAndrew Thompson break;
18223671d9d8SAndrew Thompson
18233671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_ACL:
18243671d9d8SAndrew Thompson q = &sc->sc_aclq;
18253671d9d8SAndrew Thompson break;
18263671d9d8SAndrew Thompson
18273671d9d8SAndrew Thompson case NGM_UBT_NODE_QUEUE_SCO:
18283671d9d8SAndrew Thompson q = &sc->sc_scoq;
18293671d9d8SAndrew Thompson break;
18303671d9d8SAndrew Thompson
18313671d9d8SAndrew Thompson default:
18323671d9d8SAndrew Thompson error = EINVAL;
18333671d9d8SAndrew Thompson goto done;
18343671d9d8SAndrew Thompson /* NOT REACHED */
18353671d9d8SAndrew Thompson }
18363671d9d8SAndrew Thompson
18373671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
18383671d9d8SAndrew Thompson M_NOWAIT);
18393671d9d8SAndrew Thompson if (rsp == NULL) {
18403671d9d8SAndrew Thompson error = ENOMEM;
18413671d9d8SAndrew Thompson break;
18423671d9d8SAndrew Thompson }
18433671d9d8SAndrew Thompson
18443671d9d8SAndrew Thompson ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
18453671d9d8SAndrew Thompson ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
18463671d9d8SAndrew Thompson break;
18473671d9d8SAndrew Thompson
18483671d9d8SAndrew Thompson case NGM_UBT_NODE_GET_STAT:
18493671d9d8SAndrew Thompson NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
18503671d9d8SAndrew Thompson M_NOWAIT);
18513671d9d8SAndrew Thompson if (rsp == NULL) {
18523671d9d8SAndrew Thompson error = ENOMEM;
18533671d9d8SAndrew Thompson break;
18543671d9d8SAndrew Thompson }
18553671d9d8SAndrew Thompson
18563671d9d8SAndrew Thompson bcopy(&sc->sc_stat, rsp->data,
18573671d9d8SAndrew Thompson sizeof(ng_ubt_node_stat_ep));
18583671d9d8SAndrew Thompson break;
18593671d9d8SAndrew Thompson
18603671d9d8SAndrew Thompson case NGM_UBT_NODE_RESET_STAT:
18613671d9d8SAndrew Thompson UBT_STAT_RESET(sc);
18623671d9d8SAndrew Thompson break;
18633671d9d8SAndrew Thompson
18643671d9d8SAndrew Thompson default:
18653671d9d8SAndrew Thompson error = EINVAL;
18663671d9d8SAndrew Thompson break;
18673671d9d8SAndrew Thompson }
18683671d9d8SAndrew Thompson break;
18693671d9d8SAndrew Thompson
18703671d9d8SAndrew Thompson default:
18713671d9d8SAndrew Thompson error = EINVAL;
18723671d9d8SAndrew Thompson break;
18733671d9d8SAndrew Thompson }
18743671d9d8SAndrew Thompson done:
18753671d9d8SAndrew Thompson NG_RESPOND_MSG(error, node, item, rsp);
18763671d9d8SAndrew Thompson NG_FREE_MSG(msg);
18773671d9d8SAndrew Thompson
18783671d9d8SAndrew Thompson return (error);
18793671d9d8SAndrew Thompson } /* ng_ubt_rcvmsg */
18803671d9d8SAndrew Thompson
18813671d9d8SAndrew Thompson /*
18823671d9d8SAndrew Thompson * Process data.
18833671d9d8SAndrew Thompson * Netgraph context.
18843671d9d8SAndrew Thompson */
18853671d9d8SAndrew Thompson
18863671d9d8SAndrew Thompson static int
ng_ubt_rcvdata(hook_p hook,item_p item)18873671d9d8SAndrew Thompson ng_ubt_rcvdata(hook_p hook, item_p item)
18883671d9d8SAndrew Thompson {
18893671d9d8SAndrew Thompson struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
18903671d9d8SAndrew Thompson struct mbuf *m;
18913671d9d8SAndrew Thompson struct ng_bt_mbufq *q;
18923671d9d8SAndrew Thompson int action, error = 0;
18933671d9d8SAndrew Thompson
18943671d9d8SAndrew Thompson if (hook != sc->sc_hook) {
18953671d9d8SAndrew Thompson error = EINVAL;
18963671d9d8SAndrew Thompson goto done;
18973671d9d8SAndrew Thompson }
18983671d9d8SAndrew Thompson
18993671d9d8SAndrew Thompson /* Deatch mbuf and get HCI frame type */
19003671d9d8SAndrew Thompson NGI_GET_M(item, m);
19013671d9d8SAndrew Thompson
19023671d9d8SAndrew Thompson /*
19033671d9d8SAndrew Thompson * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
19043671d9d8SAndrew Thompson * 2 bytes connection handle and at least 1 byte of length.
19053671d9d8SAndrew Thompson * Panic on data frame that has size smaller than 4 bytes (it
19063671d9d8SAndrew Thompson * should not happen)
19073671d9d8SAndrew Thompson */
19083671d9d8SAndrew Thompson
19093671d9d8SAndrew Thompson if (m->m_pkthdr.len < 4)
19103671d9d8SAndrew Thompson panic("HCI frame size is too small! pktlen=%d\n",
19113671d9d8SAndrew Thompson m->m_pkthdr.len);
19123671d9d8SAndrew Thompson
19133671d9d8SAndrew Thompson /* Process HCI frame */
19143671d9d8SAndrew Thompson switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */
19153671d9d8SAndrew Thompson case NG_HCI_CMD_PKT:
19166d917491SHans Petter Selasky if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
19173671d9d8SAndrew Thompson panic("HCI command frame size is too big! " \
19183671d9d8SAndrew Thompson "buffer size=%zd, packet len=%d\n",
19193671d9d8SAndrew Thompson UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
19203671d9d8SAndrew Thompson
19213671d9d8SAndrew Thompson q = &sc->sc_cmdq;
19223671d9d8SAndrew Thompson action = UBT_FLAG_T_START_CTRL;
19233671d9d8SAndrew Thompson break;
19243671d9d8SAndrew Thompson
19253671d9d8SAndrew Thompson case NG_HCI_ACL_DATA_PKT:
19263671d9d8SAndrew Thompson if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
19273671d9d8SAndrew Thompson panic("ACL data frame size is too big! " \
19283671d9d8SAndrew Thompson "buffer size=%d, packet len=%d\n",
19293671d9d8SAndrew Thompson UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
19303671d9d8SAndrew Thompson
19313671d9d8SAndrew Thompson q = &sc->sc_aclq;
19323671d9d8SAndrew Thompson action = UBT_FLAG_T_START_BULK;
19333671d9d8SAndrew Thompson break;
19343671d9d8SAndrew Thompson
19353671d9d8SAndrew Thompson case NG_HCI_SCO_DATA_PKT:
19363671d9d8SAndrew Thompson q = &sc->sc_scoq;
19373671d9d8SAndrew Thompson action = 0;
19383671d9d8SAndrew Thompson break;
19393671d9d8SAndrew Thompson
19403671d9d8SAndrew Thompson default:
19413671d9d8SAndrew Thompson UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
19423671d9d8SAndrew Thompson "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
19433671d9d8SAndrew Thompson
19443671d9d8SAndrew Thompson NG_FREE_M(m);
19453671d9d8SAndrew Thompson error = EINVAL;
19463671d9d8SAndrew Thompson goto done;
19473671d9d8SAndrew Thompson /* NOT REACHED */
19483671d9d8SAndrew Thompson }
19493671d9d8SAndrew Thompson
19503671d9d8SAndrew Thompson UBT_NG_LOCK(sc);
19513671d9d8SAndrew Thompson if (NG_BT_MBUFQ_FULL(q)) {
19523671d9d8SAndrew Thompson NG_BT_MBUFQ_DROP(q);
19533671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
19543671d9d8SAndrew Thompson
19553671d9d8SAndrew Thompson UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
19563671d9d8SAndrew Thompson *mtod(m, uint8_t *), m->m_pkthdr.len);
19573671d9d8SAndrew Thompson
19583671d9d8SAndrew Thompson NG_FREE_M(m);
19593671d9d8SAndrew Thompson } else {
19603671d9d8SAndrew Thompson /* Loose HCI packet type, enqueue mbuf and kick off task */
19613671d9d8SAndrew Thompson m_adj(m, sizeof(uint8_t));
19623671d9d8SAndrew Thompson NG_BT_MBUFQ_ENQUEUE(q, m);
19633671d9d8SAndrew Thompson ubt_task_schedule(sc, action);
19643671d9d8SAndrew Thompson UBT_NG_UNLOCK(sc);
19653671d9d8SAndrew Thompson }
19663671d9d8SAndrew Thompson done:
19673671d9d8SAndrew Thompson NG_FREE_ITEM(item);
19683671d9d8SAndrew Thompson
19693671d9d8SAndrew Thompson return (error);
19703671d9d8SAndrew Thompson } /* ng_ubt_rcvdata */
19713671d9d8SAndrew Thompson
19723671d9d8SAndrew Thompson /****************************************************************************
19733671d9d8SAndrew Thompson ****************************************************************************
19743671d9d8SAndrew Thompson ** Module
19753671d9d8SAndrew Thompson ****************************************************************************
19763671d9d8SAndrew Thompson ****************************************************************************/
19773671d9d8SAndrew Thompson
19783671d9d8SAndrew Thompson /*
19793671d9d8SAndrew Thompson * Load/Unload the driver module
19803671d9d8SAndrew Thompson */
19813671d9d8SAndrew Thompson
19823671d9d8SAndrew Thompson static int
ubt_modevent(module_t mod,int event,void * data)19833671d9d8SAndrew Thompson ubt_modevent(module_t mod, int event, void *data)
19843671d9d8SAndrew Thompson {
19853671d9d8SAndrew Thompson int error;
19863671d9d8SAndrew Thompson
19873671d9d8SAndrew Thompson switch (event) {
19883671d9d8SAndrew Thompson case MOD_LOAD:
19893671d9d8SAndrew Thompson error = ng_newtype(&typestruct);
19903671d9d8SAndrew Thompson if (error != 0)
19913671d9d8SAndrew Thompson printf("%s: Could not register Netgraph node type, " \
19923671d9d8SAndrew Thompson "error=%d\n", NG_UBT_NODE_TYPE, error);
19933671d9d8SAndrew Thompson break;
19943671d9d8SAndrew Thompson
19953671d9d8SAndrew Thompson case MOD_UNLOAD:
19963671d9d8SAndrew Thompson error = ng_rmtype(&typestruct);
19973671d9d8SAndrew Thompson break;
19983671d9d8SAndrew Thompson
19993671d9d8SAndrew Thompson default:
20003671d9d8SAndrew Thompson error = EOPNOTSUPP;
20013671d9d8SAndrew Thompson break;
20023671d9d8SAndrew Thompson }
20033671d9d8SAndrew Thompson
20043671d9d8SAndrew Thompson return (error);
20053671d9d8SAndrew Thompson } /* ubt_modevent */
20063671d9d8SAndrew Thompson
20073671d9d8SAndrew Thompson static device_method_t ubt_methods[] =
20083671d9d8SAndrew Thompson {
20093671d9d8SAndrew Thompson DEVMETHOD(device_probe, ubt_probe),
20103671d9d8SAndrew Thompson DEVMETHOD(device_attach, ubt_attach),
20113671d9d8SAndrew Thompson DEVMETHOD(device_detach, ubt_detach),
2012b42a2049SRaphael Kubo da Costa DEVMETHOD_END
20133671d9d8SAndrew Thompson };
20143671d9d8SAndrew Thompson
20153544d43bSVladimir Kondratyev driver_t ubt_driver =
20163671d9d8SAndrew Thompson {
20173671d9d8SAndrew Thompson .name = "ubt",
20183671d9d8SAndrew Thompson .methods = ubt_methods,
20193671d9d8SAndrew Thompson .size = sizeof(struct ubt_softc),
20203671d9d8SAndrew Thompson };
20213671d9d8SAndrew Thompson
2022d83185d9SJohn Baldwin DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_modevent, 0);
20233671d9d8SAndrew Thompson MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
20243671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
20253671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
20268fa95293SHans Petter Selasky MODULE_DEPEND(ng_ubt, ng_bluetooth, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
20273671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
2028f809f280SWarner Losh USB_PNP_HOST_INFO(ubt_devs);
2029