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