13671d9d8SAndrew Thompson /*
23671d9d8SAndrew Thompson  * ng_ubt.c
33671d9d8SAndrew Thompson  */
43671d9d8SAndrew Thompson 
53671d9d8SAndrew Thompson /*-
63671d9d8SAndrew Thompson  * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
73671d9d8SAndrew Thompson  * All rights reserved.
83671d9d8SAndrew Thompson  *
93671d9d8SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
103671d9d8SAndrew Thompson  * modification, are permitted provided that the following conditions
113671d9d8SAndrew Thompson  * are met:
123671d9d8SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
133671d9d8SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
143671d9d8SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
153671d9d8SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
163671d9d8SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
173671d9d8SAndrew Thompson  *
183671d9d8SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
193671d9d8SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
203671d9d8SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
213671d9d8SAndrew Thompson  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
223671d9d8SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
233671d9d8SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
243671d9d8SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
253671d9d8SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
263671d9d8SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
273671d9d8SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
283671d9d8SAndrew Thompson  * SUCH DAMAGE.
293671d9d8SAndrew Thompson  *
303671d9d8SAndrew Thompson  * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
313671d9d8SAndrew Thompson  * $FreeBSD$
323671d9d8SAndrew Thompson  */
333671d9d8SAndrew Thompson 
343671d9d8SAndrew Thompson /*
353671d9d8SAndrew Thompson  * NOTE: ng_ubt2 driver has a split personality. On one side it is
363671d9d8SAndrew Thompson  * a USB device driver and on the other it is a Netgraph node. This
373671d9d8SAndrew Thompson  * driver will *NOT* create traditional /dev/ enties, only Netgraph
383671d9d8SAndrew Thompson  * node.
393671d9d8SAndrew Thompson  *
403671d9d8SAndrew Thompson  * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
413671d9d8SAndrew Thompson  *
423671d9d8SAndrew Thompson  * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
433671d9d8SAndrew Thompson  *    by USB for any USB request going over device's interface #0 and #1,
443671d9d8SAndrew Thompson  *    i.e. interrupt, control, bulk and isoc. transfers.
453671d9d8SAndrew Thompson  *
463671d9d8SAndrew Thompson  * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
473671d9d8SAndrew Thompson  *    and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
483671d9d8SAndrew Thompson  *    pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
493671d9d8SAndrew Thompson  *    think of it as a spin lock.
503671d9d8SAndrew Thompson  *
513671d9d8SAndrew Thompson  * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
523671d9d8SAndrew Thompson  *
533671d9d8SAndrew Thompson  * 1) USB context. This is where all the USB related stuff happens. All
543671d9d8SAndrew Thompson  *    callbacks run in this context. All callbacks are called (by USB) with
553671d9d8SAndrew Thompson  *    appropriate interface lock held. It is (generally) allowed to grab
563671d9d8SAndrew Thompson  *    any additional locks.
573671d9d8SAndrew Thompson  *
583671d9d8SAndrew Thompson  * 2) Netgraph context. This is where all the Netgraph related stuff happens.
593671d9d8SAndrew Thompson  *    Since we mark node as WRITER, the Netgraph node will be "locked" (from
603671d9d8SAndrew Thompson  *    Netgraph point of view). Any variable that is only modified from the
613671d9d8SAndrew Thompson  *    Netgraph context does not require any additonal locking. It is generally
623671d9d8SAndrew Thompson  *    *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
633671d9d8SAndrew Thompson  *    grab any lock in the Netgraph context that could cause de-scheduling of
643671d9d8SAndrew Thompson  *    the Netgraph thread for significant amount of time. In fact, the only
653671d9d8SAndrew Thompson  *    lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
663671d9d8SAndrew Thompson  *    Also make sure that any code that is called from the Netgraph context
673671d9d8SAndrew Thompson  *    follows the rule above.
683671d9d8SAndrew Thompson  *
693671d9d8SAndrew Thompson  * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
703671d9d8SAndrew Thompson  *    NOT allowed to grab any lock that could cause de-scheduling in the
713671d9d8SAndrew Thompson  *    Netgraph context, and, USB requires us to grab interface lock before
723671d9d8SAndrew Thompson  *    doing things with transfers, it is safer to transition from the Netgraph
733671d9d8SAndrew Thompson  *    context to the Taskqueue context before we can call into USB subsystem.
743671d9d8SAndrew Thompson  *
753671d9d8SAndrew Thompson  * So, to put everything together, the rules are as follows.
763671d9d8SAndrew Thompson  *	It is OK to call from the USB context or the Taskqueue context into
773671d9d8SAndrew Thompson  * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
783671d9d8SAndrew Thompson  * it is allowed to call into the Netgraph context with locks held.
793671d9d8SAndrew Thompson  *	Is it *NOT* OK to call from the Netgraph context into the USB context,
803671d9d8SAndrew Thompson  * because USB requires us to grab interface locks, and, it is safer to
813671d9d8SAndrew Thompson  * avoid it. So, to make things safer we set task flags to indicate which
823671d9d8SAndrew Thompson  * actions we want to perform and schedule ubt_task which would run in the
833671d9d8SAndrew Thompson  * Taskqueue context.
843671d9d8SAndrew Thompson  *	Is is OK to call from the Taskqueue context into the USB context,
853671d9d8SAndrew Thompson  * and, ubt_task does just that (i.e. grabs appropriate interface locks
863671d9d8SAndrew Thompson  * before calling into USB).
873671d9d8SAndrew Thompson  *	Access to the outgoing queues, task flags and hook pointer is
883671d9d8SAndrew Thompson  * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
893671d9d8SAndrew Thompson  * sc_ng_mtx should really be a spin lock (and it is very likely to an
903671d9d8SAndrew Thompson  * equivalent of spin lock due to adaptive nature of FreeBSD mutexes).
913671d9d8SAndrew Thompson  *	All USB callbacks accept softc pointer as a private data. USB ensures
923671d9d8SAndrew Thompson  * that this pointer is valid.
933671d9d8SAndrew Thompson  */
943671d9d8SAndrew Thompson 
95ed6d949aSAndrew Thompson #include <sys/stdint.h>
96ed6d949aSAndrew Thompson #include <sys/stddef.h>
97ed6d949aSAndrew Thompson #include <sys/param.h>
98ed6d949aSAndrew Thompson #include <sys/queue.h>
99ed6d949aSAndrew Thompson #include <sys/types.h>
100ed6d949aSAndrew Thompson #include <sys/systm.h>
101ed6d949aSAndrew Thompson #include <sys/kernel.h>
102ed6d949aSAndrew Thompson #include <sys/bus.h>
103ed6d949aSAndrew Thompson #include <sys/linker_set.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 
1543671d9d8SAndrew Thompson /* Queue length */
1553671d9d8SAndrew Thompson static const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
1563671d9d8SAndrew Thompson {
1573671d9d8SAndrew Thompson 	{ "queue", &ng_parse_int32_type, },
1583671d9d8SAndrew Thompson 	{ "qlen",  &ng_parse_int32_type, },
1593671d9d8SAndrew Thompson 	{ NULL, }
1603671d9d8SAndrew Thompson };
1613671d9d8SAndrew Thompson static const struct ng_parse_type		ng_ubt_node_qlen_type =
1623671d9d8SAndrew Thompson {
1633671d9d8SAndrew Thompson 	&ng_parse_struct_type,
1643671d9d8SAndrew Thompson 	&ng_ubt_node_qlen_type_fields
1653671d9d8SAndrew Thompson };
1663671d9d8SAndrew Thompson 
1673671d9d8SAndrew Thompson /* Stat info */
1683671d9d8SAndrew Thompson static const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
1693671d9d8SAndrew Thompson {
1703671d9d8SAndrew Thompson 	{ "pckts_recv", &ng_parse_uint32_type, },
1713671d9d8SAndrew Thompson 	{ "bytes_recv", &ng_parse_uint32_type, },
1723671d9d8SAndrew Thompson 	{ "pckts_sent", &ng_parse_uint32_type, },
1733671d9d8SAndrew Thompson 	{ "bytes_sent", &ng_parse_uint32_type, },
1743671d9d8SAndrew Thompson 	{ "oerrors",    &ng_parse_uint32_type, },
1753671d9d8SAndrew Thompson 	{ "ierrors",    &ng_parse_uint32_type, },
1763671d9d8SAndrew Thompson 	{ NULL, }
1773671d9d8SAndrew Thompson };
1783671d9d8SAndrew Thompson static const struct ng_parse_type		ng_ubt_node_stat_type =
1793671d9d8SAndrew Thompson {
1803671d9d8SAndrew Thompson 	&ng_parse_struct_type,
1813671d9d8SAndrew Thompson 	&ng_ubt_node_stat_type_fields
1823671d9d8SAndrew Thompson };
1833671d9d8SAndrew Thompson 
1843671d9d8SAndrew Thompson /* Netgraph node command list */
1853671d9d8SAndrew Thompson static const struct ng_cmdlist			ng_ubt_cmdlist[] =
1863671d9d8SAndrew Thompson {
1873671d9d8SAndrew Thompson 	{
1883671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1893671d9d8SAndrew Thompson 		NGM_UBT_NODE_SET_DEBUG,
1903671d9d8SAndrew Thompson 		"set_debug",
1913671d9d8SAndrew Thompson 		&ng_parse_uint16_type,
1923671d9d8SAndrew Thompson 		NULL
1933671d9d8SAndrew Thompson 	},
1943671d9d8SAndrew Thompson 	{
1953671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1963671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_DEBUG,
1973671d9d8SAndrew Thompson 		"get_debug",
1983671d9d8SAndrew Thompson 		NULL,
1993671d9d8SAndrew Thompson 		&ng_parse_uint16_type
2003671d9d8SAndrew Thompson 	},
2013671d9d8SAndrew Thompson 	{
2023671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2033671d9d8SAndrew Thompson 		NGM_UBT_NODE_SET_QLEN,
2043671d9d8SAndrew Thompson 		"set_qlen",
2053671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type,
2063671d9d8SAndrew Thompson 		NULL
2073671d9d8SAndrew Thompson 	},
2083671d9d8SAndrew Thompson 	{
2093671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2103671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_QLEN,
2113671d9d8SAndrew Thompson 		"get_qlen",
2123671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type,
2133671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type
2143671d9d8SAndrew Thompson 	},
2153671d9d8SAndrew Thompson 	{
2163671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2173671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_STAT,
2183671d9d8SAndrew Thompson 		"get_stat",
2193671d9d8SAndrew Thompson 		NULL,
2203671d9d8SAndrew Thompson 		&ng_ubt_node_stat_type
2213671d9d8SAndrew Thompson 	},
2223671d9d8SAndrew Thompson 	{
2233671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2243671d9d8SAndrew Thompson 		NGM_UBT_NODE_RESET_STAT,
2253671d9d8SAndrew Thompson 		"reset_stat",
2263671d9d8SAndrew Thompson 		NULL,
2273671d9d8SAndrew Thompson 		NULL
2283671d9d8SAndrew Thompson 	},
2293671d9d8SAndrew Thompson 	{ 0, }
2303671d9d8SAndrew Thompson };
2313671d9d8SAndrew Thompson 
2323671d9d8SAndrew Thompson /* Netgraph node type */
2333671d9d8SAndrew Thompson static struct ng_type	typestruct =
2343671d9d8SAndrew Thompson {
2353671d9d8SAndrew Thompson 	.version = 	NG_ABI_VERSION,
2363671d9d8SAndrew Thompson 	.name =		NG_UBT_NODE_TYPE,
2373671d9d8SAndrew Thompson 	.constructor =	ng_ubt_constructor,
2383671d9d8SAndrew Thompson 	.rcvmsg =	ng_ubt_rcvmsg,
2393671d9d8SAndrew Thompson 	.shutdown =	ng_ubt_shutdown,
2403671d9d8SAndrew Thompson 	.newhook =	ng_ubt_newhook,
2413671d9d8SAndrew Thompson 	.connect =	ng_ubt_connect,
2423671d9d8SAndrew Thompson 	.rcvdata =	ng_ubt_rcvdata,
2433671d9d8SAndrew Thompson 	.disconnect =	ng_ubt_disconnect,
2443671d9d8SAndrew Thompson 	.cmdlist =	ng_ubt_cmdlist
2453671d9d8SAndrew Thompson };
2463671d9d8SAndrew Thompson 
2473671d9d8SAndrew Thompson /****************************************************************************
2483671d9d8SAndrew Thompson  ****************************************************************************
2493671d9d8SAndrew Thompson  **                              USB specific
2503671d9d8SAndrew Thompson  ****************************************************************************
2513671d9d8SAndrew Thompson  ****************************************************************************/
2523671d9d8SAndrew Thompson 
2533671d9d8SAndrew Thompson /* USB methods */
254e0a69b51SAndrew Thompson static usb_callback_t	ubt_ctrl_write_callback;
255e0a69b51SAndrew Thompson static usb_callback_t	ubt_intr_read_callback;
256e0a69b51SAndrew Thompson static usb_callback_t	ubt_bulk_read_callback;
257e0a69b51SAndrew Thompson static usb_callback_t	ubt_bulk_write_callback;
258e0a69b51SAndrew Thompson static usb_callback_t	ubt_isoc_read_callback;
259e0a69b51SAndrew Thompson static usb_callback_t	ubt_isoc_write_callback;
2603671d9d8SAndrew Thompson 
2613671d9d8SAndrew Thompson static int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
262760bc48eSAndrew Thompson static int		ubt_isoc_read_one_frame(struct usb_xfer *, int);
2633671d9d8SAndrew Thompson 
2643671d9d8SAndrew Thompson /*
2653671d9d8SAndrew Thompson  * USB config
2663671d9d8SAndrew Thompson  *
2673671d9d8SAndrew Thompson  * The following desribes usb transfers that could be submitted on USB device.
2683671d9d8SAndrew Thompson  *
2693671d9d8SAndrew Thompson  * Interface 0 on the USB device must present the following endpoints
2703671d9d8SAndrew Thompson  *	1) Interrupt endpoint to receive HCI events
2713671d9d8SAndrew Thompson  *	2) Bulk IN endpoint to receive ACL data
2723671d9d8SAndrew Thompson  *	3) Bulk OUT endpoint to send ACL data
2733671d9d8SAndrew Thompson  *
2743671d9d8SAndrew Thompson  * Interface 1 on the USB device must present the following endpoints
2753671d9d8SAndrew Thompson  *	1) Isochronous IN endpoint to receive SCO data
2763671d9d8SAndrew Thompson  *	2) Isochronous OUT endpoint to send SCO data
2773671d9d8SAndrew Thompson  */
2783671d9d8SAndrew Thompson 
279760bc48eSAndrew Thompson static const struct usb_config		ubt_config[UBT_N_TRANSFER] =
2803671d9d8SAndrew Thompson {
2813671d9d8SAndrew Thompson 	/*
2823671d9d8SAndrew Thompson 	 * Interface #0
2833671d9d8SAndrew Thompson  	 */
2843671d9d8SAndrew Thompson 
2853671d9d8SAndrew Thompson 	/* Outgoing bulk transfer - ACL packets */
2863671d9d8SAndrew Thompson 	[UBT_IF_0_BULK_DT_WR] = {
2873671d9d8SAndrew Thompson 		.type =		UE_BULK,
2883671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
2893671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
2903671d9d8SAndrew Thompson 		.if_index = 	0,
2913671d9d8SAndrew Thompson 		.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
2923671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
2933671d9d8SAndrew Thompson 		.callback =	&ubt_bulk_write_callback,
2943671d9d8SAndrew Thompson 	},
2953671d9d8SAndrew Thompson 	/* Incoming bulk transfer - ACL packets */
2963671d9d8SAndrew Thompson 	[UBT_IF_0_BULK_DT_RD] = {
2973671d9d8SAndrew Thompson 		.type =		UE_BULK,
2983671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
2993671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3003671d9d8SAndrew Thompson 		.if_index = 	0,
3013671d9d8SAndrew Thompson 		.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
3023671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
3033671d9d8SAndrew Thompson 		.callback =	&ubt_bulk_read_callback,
3043671d9d8SAndrew Thompson 	},
3053671d9d8SAndrew Thompson 	/* Incoming interrupt transfer - HCI events */
3063671d9d8SAndrew Thompson 	[UBT_IF_0_INTR_DT_RD] = {
3073671d9d8SAndrew Thompson 		.type =		UE_INTERRUPT,
3083671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3093671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3103671d9d8SAndrew Thompson 		.if_index = 	0,
3113671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
3123671d9d8SAndrew Thompson 		.bufsize =	UBT_INTR_BUFFER_SIZE,
3133671d9d8SAndrew Thompson 		.callback =	&ubt_intr_read_callback,
3143671d9d8SAndrew Thompson 	},
3153671d9d8SAndrew Thompson 	/* Outgoing control transfer - HCI commands */
3163671d9d8SAndrew Thompson 	[UBT_IF_0_CTRL_DT_WR] = {
3173671d9d8SAndrew Thompson 		.type =		UE_CONTROL,
3183671d9d8SAndrew Thompson 		.endpoint =	0x00,	/* control pipe */
3193671d9d8SAndrew Thompson 		.direction =	UE_DIR_ANY,
3203671d9d8SAndrew Thompson 		.if_index = 	0,
3213671d9d8SAndrew Thompson 		.bufsize =	UBT_CTRL_BUFFER_SIZE,
3223671d9d8SAndrew Thompson 		.callback =	&ubt_ctrl_write_callback,
3233671d9d8SAndrew Thompson 		.timeout =	5000,	/* 5 seconds */
3243671d9d8SAndrew Thompson 	},
3253671d9d8SAndrew Thompson 
3263671d9d8SAndrew Thompson 	/*
3273671d9d8SAndrew Thompson 	 * Interface #1
3283671d9d8SAndrew Thompson  	 */
3293671d9d8SAndrew Thompson 
3303671d9d8SAndrew Thompson 	/* Incoming isochronous transfer #1 - SCO packets */
3313671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_RD1] = {
3323671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3333671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3343671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3353671d9d8SAndrew Thompson 		.if_index = 	1,
3363671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3373671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3383671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3393671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_read_callback,
3403671d9d8SAndrew Thompson 	},
3413671d9d8SAndrew Thompson 	/* Incoming isochronous transfer #2 - SCO packets */
3423671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_RD2] = {
3433671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3443671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3453671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3463671d9d8SAndrew Thompson 		.if_index = 	1,
3473671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3483671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3493671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3503671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_read_callback,
3513671d9d8SAndrew Thompson 	},
3523671d9d8SAndrew Thompson 	/* Outgoing isochronous transfer #1 - SCO packets */
3533671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_WR1] = {
3543671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3553671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3563671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
3573671d9d8SAndrew Thompson 		.if_index = 	1,
3583671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3593671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3603671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3613671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_write_callback,
3623671d9d8SAndrew Thompson 	},
3633671d9d8SAndrew Thompson 	/* Outgoing isochronous transfer #2 - SCO packets */
3643671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_WR2] = {
3653671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3663671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3673671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
3683671d9d8SAndrew Thompson 		.if_index = 	1,
3693671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3703671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3713671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3723671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_write_callback,
3733671d9d8SAndrew Thompson 	},
3743671d9d8SAndrew Thompson };
3753671d9d8SAndrew Thompson 
3763671d9d8SAndrew Thompson /*
3773671d9d8SAndrew Thompson  * If for some reason device should not be attached then put
3783671d9d8SAndrew Thompson  * VendorID/ProductID pair into the list below. The format is
3793671d9d8SAndrew Thompson  * as follows:
3803671d9d8SAndrew Thompson  *
3813671d9d8SAndrew Thompson  *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
3823671d9d8SAndrew Thompson  *
3833671d9d8SAndrew Thompson  * where VENDOR_ID and PRODUCT_ID are hex numbers.
3843671d9d8SAndrew Thompson  */
3853671d9d8SAndrew Thompson 
386760bc48eSAndrew Thompson static const struct usb_device_id ubt_ignore_devs[] =
3873671d9d8SAndrew Thompson {
3883671d9d8SAndrew Thompson 	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
3893671d9d8SAndrew Thompson 	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
3903671d9d8SAndrew Thompson };
3913671d9d8SAndrew Thompson 
3923671d9d8SAndrew Thompson /* List of supported bluetooth devices */
393760bc48eSAndrew Thompson static const struct usb_device_id ubt_devs[] =
3943671d9d8SAndrew Thompson {
3953671d9d8SAndrew Thompson 	/* Generic Bluetooth class devices */
3963671d9d8SAndrew Thompson 	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
3973671d9d8SAndrew Thompson 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
3983671d9d8SAndrew Thompson 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
3993671d9d8SAndrew Thompson 
4003671d9d8SAndrew Thompson 	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
4013671d9d8SAndrew Thompson 	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
4023671d9d8SAndrew Thompson };
4033671d9d8SAndrew Thompson 
4043671d9d8SAndrew Thompson /*
4053671d9d8SAndrew Thompson  * Probe for a USB Bluetooth device.
4063671d9d8SAndrew Thompson  * USB context.
4073671d9d8SAndrew Thompson  */
4083671d9d8SAndrew Thompson 
4093671d9d8SAndrew Thompson static int
4103671d9d8SAndrew Thompson ubt_probe(device_t dev)
4113671d9d8SAndrew Thompson {
412760bc48eSAndrew Thompson 	struct usb_attach_arg	*uaa = device_get_ivars(dev);
4133671d9d8SAndrew Thompson 
4143671d9d8SAndrew Thompson 	if (uaa->usb_mode != USB_MODE_HOST)
4153671d9d8SAndrew Thompson 		return (ENXIO);
4163671d9d8SAndrew Thompson 
4173671d9d8SAndrew Thompson 	if (uaa->info.bIfaceIndex != 0)
4183671d9d8SAndrew Thompson 		return (ENXIO);
4193671d9d8SAndrew Thompson 
4203671d9d8SAndrew Thompson 	if (uaa->use_generic == 0)
4213671d9d8SAndrew Thompson 		return (ENXIO);
4223671d9d8SAndrew Thompson 
423a593f6b8SAndrew Thompson 	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
4243671d9d8SAndrew Thompson 			sizeof(ubt_ignore_devs), uaa) == 0)
4253671d9d8SAndrew Thompson 		return (ENXIO);
4263671d9d8SAndrew Thompson 
427a593f6b8SAndrew Thompson 	return (usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa));
4283671d9d8SAndrew Thompson } /* ubt_probe */
4293671d9d8SAndrew Thompson 
4303671d9d8SAndrew Thompson /*
4313671d9d8SAndrew Thompson  * Attach the device.
4323671d9d8SAndrew Thompson  * USB context.
4333671d9d8SAndrew Thompson  */
4343671d9d8SAndrew Thompson 
4353671d9d8SAndrew Thompson static int
4363671d9d8SAndrew Thompson ubt_attach(device_t dev)
4373671d9d8SAndrew Thompson {
438760bc48eSAndrew Thompson 	struct usb_attach_arg		*uaa = device_get_ivars(dev);
4393671d9d8SAndrew Thompson 	struct ubt_softc		*sc = device_get_softc(dev);
440760bc48eSAndrew Thompson 	struct usb_endpoint_descriptor	*ed;
441760bc48eSAndrew Thompson 	struct usb_interface_descriptor *id;
4423671d9d8SAndrew Thompson 	uint16_t			wMaxPacketSize;
4433671d9d8SAndrew Thompson 	uint8_t				alt_index, i, j;
4443671d9d8SAndrew Thompson 	uint8_t				iface_index[2] = { 0, 1 };
4453671d9d8SAndrew Thompson 
446a593f6b8SAndrew Thompson 	device_set_usb_desc(dev);
4473671d9d8SAndrew Thompson 
4483671d9d8SAndrew Thompson 	sc->sc_dev = dev;
4493671d9d8SAndrew Thompson 	sc->sc_debug = NG_UBT_WARN_LEVEL;
4503671d9d8SAndrew Thompson 
4513671d9d8SAndrew Thompson 	/*
4523671d9d8SAndrew Thompson 	 * Create Netgraph node
4533671d9d8SAndrew Thompson 	 */
4543671d9d8SAndrew Thompson 
4553671d9d8SAndrew Thompson 	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
4563671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not create Netgraph node\n");
4573671d9d8SAndrew Thompson 		return (ENXIO);
4583671d9d8SAndrew Thompson 	}
4593671d9d8SAndrew Thompson 
4603671d9d8SAndrew Thompson 	/* Name Netgraph node */
4613671d9d8SAndrew Thompson 	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
4623671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not name Netgraph node\n");
4633671d9d8SAndrew Thompson 		NG_NODE_UNREF(sc->sc_node);
4643671d9d8SAndrew Thompson 		return (ENXIO);
4653671d9d8SAndrew Thompson 	}
4663671d9d8SAndrew Thompson 	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
4673671d9d8SAndrew Thompson 	NG_NODE_FORCE_WRITER(sc->sc_node);
4683671d9d8SAndrew Thompson 
4693671d9d8SAndrew Thompson 	/*
4703671d9d8SAndrew Thompson 	 * Initialize device softc structure
4713671d9d8SAndrew Thompson 	 */
4723671d9d8SAndrew Thompson 
4733671d9d8SAndrew Thompson 	/* initialize locks */
4743671d9d8SAndrew Thompson 	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
4753671d9d8SAndrew Thompson 	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
4763671d9d8SAndrew Thompson 
4773671d9d8SAndrew Thompson 	/* initialize packet queues */
4783671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
4793671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
4803671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
4813671d9d8SAndrew Thompson 
4823671d9d8SAndrew Thompson 	/* initialize glue task */
4833671d9d8SAndrew Thompson 	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
4843671d9d8SAndrew Thompson 
4853671d9d8SAndrew Thompson 	/*
4863671d9d8SAndrew Thompson 	 * Configure Bluetooth USB device. Discover all required USB
4873671d9d8SAndrew Thompson 	 * interfaces and endpoints.
4883671d9d8SAndrew Thompson 	 *
4893671d9d8SAndrew Thompson 	 * USB device must present two interfaces:
4903671d9d8SAndrew Thompson 	 * 1) Interface 0 that has 3 endpoints
4913671d9d8SAndrew Thompson 	 *	1) Interrupt endpoint to receive HCI events
4923671d9d8SAndrew Thompson 	 *	2) Bulk IN endpoint to receive ACL data
4933671d9d8SAndrew Thompson 	 *	3) Bulk OUT endpoint to send ACL data
4943671d9d8SAndrew Thompson 	 *
4953671d9d8SAndrew Thompson 	 * 2) Interface 1 then has 2 endpoints
4963671d9d8SAndrew Thompson 	 *	1) Isochronous IN endpoint to receive SCO data
4973671d9d8SAndrew Thompson  	 *	2) Isochronous OUT endpoint to send SCO data
4983671d9d8SAndrew Thompson 	 *
4993671d9d8SAndrew Thompson 	 * Interface 1 (with isochronous endpoints) has several alternate
5003671d9d8SAndrew Thompson 	 * configurations with different packet size.
5013671d9d8SAndrew Thompson 	 */
5023671d9d8SAndrew Thompson 
5033671d9d8SAndrew Thompson 	/*
5043671d9d8SAndrew Thompson 	 * For interface #1 search alternate settings, and find
5053671d9d8SAndrew Thompson 	 * the descriptor with the largest wMaxPacketSize
5063671d9d8SAndrew Thompson 	 */
5073671d9d8SAndrew Thompson 
5083671d9d8SAndrew Thompson 	wMaxPacketSize = 0;
5093671d9d8SAndrew Thompson 	alt_index = 0;
5103671d9d8SAndrew Thompson 	i = 0;
5113671d9d8SAndrew Thompson 	j = 0;
5123671d9d8SAndrew Thompson 	ed = NULL;
5133671d9d8SAndrew Thompson 
5143671d9d8SAndrew Thompson 	/*
5153671d9d8SAndrew Thompson 	 * Search through all the descriptors looking for the largest
5163671d9d8SAndrew Thompson 	 * packet size:
5173671d9d8SAndrew Thompson 	 */
518a593f6b8SAndrew Thompson 	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
519a593f6b8SAndrew Thompson 	    usbd_get_config_descriptor(uaa->device),
520760bc48eSAndrew Thompson 	    (struct usb_descriptor *)ed))) {
5213671d9d8SAndrew Thompson 
5223671d9d8SAndrew Thompson 		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
5233671d9d8SAndrew Thompson 		    (ed->bLength >= sizeof(*id))) {
524760bc48eSAndrew Thompson 			id = (struct usb_interface_descriptor *)ed;
5253671d9d8SAndrew Thompson 			i = id->bInterfaceNumber;
5263671d9d8SAndrew Thompson 			j = id->bAlternateSetting;
5273671d9d8SAndrew Thompson 		}
5283671d9d8SAndrew Thompson 
5293671d9d8SAndrew Thompson 		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
5303671d9d8SAndrew Thompson 		    (ed->bLength >= sizeof(*ed)) &&
5313671d9d8SAndrew Thompson 		    (i == 1)) {
5323671d9d8SAndrew Thompson 			uint16_t temp;
5333671d9d8SAndrew Thompson 
5343671d9d8SAndrew Thompson 			temp = UGETW(ed->wMaxPacketSize);
5353671d9d8SAndrew Thompson 			if (temp > wMaxPacketSize) {
5363671d9d8SAndrew Thompson 				wMaxPacketSize = temp;
5373671d9d8SAndrew Thompson 				alt_index = j;
5383671d9d8SAndrew Thompson 			}
5393671d9d8SAndrew Thompson 		}
5403671d9d8SAndrew Thompson 	}
5413671d9d8SAndrew Thompson 
5423671d9d8SAndrew Thompson 	/* Set alt configuration on interface #1 only if we found it */
5433671d9d8SAndrew Thompson 	if (wMaxPacketSize > 0 &&
544a593f6b8SAndrew Thompson 	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
5453671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not set alternate setting %d " \
5463671d9d8SAndrew Thompson 			"for interface 1!\n", alt_index);
5473671d9d8SAndrew Thompson 		goto detach;
5483671d9d8SAndrew Thompson 	}
5493671d9d8SAndrew Thompson 
5503671d9d8SAndrew Thompson 	/* Setup transfers for both interfaces */
551a593f6b8SAndrew Thompson 	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
5523671d9d8SAndrew Thompson 			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
5533671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not allocate transfers\n");
5543671d9d8SAndrew Thompson 		goto detach;
5553671d9d8SAndrew Thompson 	}
5563671d9d8SAndrew Thompson 
5573671d9d8SAndrew Thompson 	/* Claim all interfaces on the device */
558a593f6b8SAndrew Thompson 	for (i = 1; usbd_get_iface(uaa->device, i) != NULL; i ++)
559a593f6b8SAndrew Thompson 		usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
5603671d9d8SAndrew Thompson 
5613671d9d8SAndrew Thompson 	return (0); /* success */
5623671d9d8SAndrew Thompson 
5633671d9d8SAndrew Thompson detach:
5643671d9d8SAndrew Thompson 	ubt_detach(dev);
5653671d9d8SAndrew Thompson 
5663671d9d8SAndrew Thompson 	return (ENXIO);
5673671d9d8SAndrew Thompson } /* ubt_attach */
5683671d9d8SAndrew Thompson 
5693671d9d8SAndrew Thompson /*
5703671d9d8SAndrew Thompson  * Detach the device.
5713671d9d8SAndrew Thompson  * USB context.
5723671d9d8SAndrew Thompson  */
5733671d9d8SAndrew Thompson 
5743671d9d8SAndrew Thompson int
5753671d9d8SAndrew Thompson ubt_detach(device_t dev)
5763671d9d8SAndrew Thompson {
5773671d9d8SAndrew Thompson 	struct ubt_softc	*sc = device_get_softc(dev);
5783671d9d8SAndrew Thompson 	node_p			node = sc->sc_node;
5793671d9d8SAndrew Thompson 
5803671d9d8SAndrew Thompson 	/* Destroy Netgraph node */
5813671d9d8SAndrew Thompson 	if (node != NULL) {
5823671d9d8SAndrew Thompson 		sc->sc_node = NULL;
5833671d9d8SAndrew Thompson 		NG_NODE_REALLY_DIE(node);
5843671d9d8SAndrew Thompson 		ng_rmnode_self(node);
5853671d9d8SAndrew Thompson 	}
5863671d9d8SAndrew Thompson 
5873671d9d8SAndrew Thompson 	/* Make sure ubt_task in gone */
5883671d9d8SAndrew Thompson 	taskqueue_drain(taskqueue_swi, &sc->sc_task);
5893671d9d8SAndrew Thompson 
5903671d9d8SAndrew Thompson 	/* Free USB transfers, if any */
591a593f6b8SAndrew Thompson 	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
5923671d9d8SAndrew Thompson 
5933671d9d8SAndrew Thompson 	/* Destroy queues */
5943671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
5953671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
5963671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
5973671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
5983671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
5993671d9d8SAndrew Thompson 
6003671d9d8SAndrew Thompson 	mtx_destroy(&sc->sc_if_mtx);
6013671d9d8SAndrew Thompson 	mtx_destroy(&sc->sc_ng_mtx);
6023671d9d8SAndrew Thompson 
6033671d9d8SAndrew Thompson 	return (0);
6043671d9d8SAndrew Thompson } /* ubt_detach */
6053671d9d8SAndrew Thompson 
6063671d9d8SAndrew Thompson /*
6073671d9d8SAndrew Thompson  * Called when outgoing control request (HCI command) has completed, i.e.
6083671d9d8SAndrew Thompson  * HCI command was sent to the device.
6093671d9d8SAndrew Thompson  * USB context.
6103671d9d8SAndrew Thompson  */
6113671d9d8SAndrew Thompson 
6123671d9d8SAndrew Thompson static void
613ed6d949aSAndrew Thompson ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
6143671d9d8SAndrew Thompson {
615ed6d949aSAndrew Thompson 	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
616760bc48eSAndrew Thompson 	struct usb_device_request	req;
6173671d9d8SAndrew Thompson 	struct mbuf			*m;
618ed6d949aSAndrew Thompson 	struct usb_page_cache		*pc;
619ed6d949aSAndrew Thompson 	int				actlen;
620ed6d949aSAndrew Thompson 
621ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
6223671d9d8SAndrew Thompson 
6233671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
6243671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
625ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
626ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
6273671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
6283671d9d8SAndrew Thompson 		/* FALLTHROUGH */
6293671d9d8SAndrew Thompson 
6303671d9d8SAndrew Thompson 	case USB_ST_SETUP:
6313671d9d8SAndrew Thompson send_next:
6323671d9d8SAndrew Thompson 		/* Get next command mbuf, if any */
6333671d9d8SAndrew Thompson 		UBT_NG_LOCK(sc);
6343671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
6353671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
6363671d9d8SAndrew Thompson 
6373671d9d8SAndrew Thompson 		if (m == NULL) {
6383671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI command queue is empty\n");
6393671d9d8SAndrew Thompson 			break;	/* transfer complete */
6403671d9d8SAndrew Thompson 		}
6413671d9d8SAndrew Thompson 
6423671d9d8SAndrew Thompson 		/* Initialize a USB control request and then schedule it */
6433671d9d8SAndrew Thompson 		bzero(&req, sizeof(req));
6443671d9d8SAndrew Thompson 		req.bmRequestType = UBT_HCI_REQUEST;
6453671d9d8SAndrew Thompson 		USETW(req.wLength, m->m_pkthdr.len);
6463671d9d8SAndrew Thompson 
6473671d9d8SAndrew Thompson 		UBT_INFO(sc, "Sending control request, " \
6483671d9d8SAndrew Thompson 			"bmRequestType=0x%02x, wLength=%d\n",
6493671d9d8SAndrew Thompson 			req.bmRequestType, UGETW(req.wLength));
6503671d9d8SAndrew Thompson 
651ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
652ed6d949aSAndrew Thompson 		usbd_copy_in(pc, 0, &req, sizeof(req));
653ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 1);
654ed6d949aSAndrew Thompson 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
6553671d9d8SAndrew Thompson 
656ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
657ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
658ed6d949aSAndrew Thompson 		usbd_xfer_set_frames(xfer, 2);
6593671d9d8SAndrew Thompson 
6603671d9d8SAndrew Thompson 		NG_FREE_M(m);
6613671d9d8SAndrew Thompson 
662a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
6633671d9d8SAndrew Thompson 		break;
6643671d9d8SAndrew Thompson 
6653671d9d8SAndrew Thompson 	default: /* Error */
666ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
6673671d9d8SAndrew Thompson 			UBT_WARN(sc, "control transfer failed: %s\n",
668ed6d949aSAndrew Thompson 				usbd_errstr(error));
6693671d9d8SAndrew Thompson 
6703671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
6713671d9d8SAndrew Thompson 			goto send_next;
6723671d9d8SAndrew Thompson 		}
6733671d9d8SAndrew Thompson 
6743671d9d8SAndrew Thompson 		/* transfer cancelled */
6753671d9d8SAndrew Thompson 		break;
6763671d9d8SAndrew Thompson 	}
6773671d9d8SAndrew Thompson } /* ubt_ctrl_write_callback */
6783671d9d8SAndrew Thompson 
6793671d9d8SAndrew Thompson /*
6803671d9d8SAndrew Thompson  * Called when incoming interrupt transfer (HCI event) has completed, i.e.
6813671d9d8SAndrew Thompson  * HCI event was received from the device.
6823671d9d8SAndrew Thompson  * USB context.
6833671d9d8SAndrew Thompson  */
6843671d9d8SAndrew Thompson 
6853671d9d8SAndrew Thompson static void
686ed6d949aSAndrew Thompson ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
6873671d9d8SAndrew Thompson {
688ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
6893671d9d8SAndrew Thompson 	struct mbuf		*m;
6903671d9d8SAndrew Thompson 	ng_hci_event_pkt_t	*hdr;
691ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
692ed6d949aSAndrew Thompson 	int			actlen;
693ed6d949aSAndrew Thompson 
694ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
6953671d9d8SAndrew Thompson 
6963671d9d8SAndrew Thompson 	m = NULL;
6973671d9d8SAndrew Thompson 
6983671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
6993671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
7003671d9d8SAndrew Thompson 		/* Allocate a new mbuf */
7013671d9d8SAndrew Thompson 		MGETHDR(m, M_DONTWAIT, MT_DATA);
7023671d9d8SAndrew Thompson 		if (m == NULL) {
7033671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7043671d9d8SAndrew Thompson 			goto submit_next;
7053671d9d8SAndrew Thompson 		}
7063671d9d8SAndrew Thompson 
7073671d9d8SAndrew Thompson 		MCLGET(m, M_DONTWAIT);
7083671d9d8SAndrew Thompson 		if (!(m->m_flags & M_EXT)) {
7093671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7103671d9d8SAndrew Thompson 			goto submit_next;
7113671d9d8SAndrew Thompson 		}
7123671d9d8SAndrew Thompson 
7133671d9d8SAndrew Thompson 		/* Add HCI packet type */
7143671d9d8SAndrew Thompson 		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
7153671d9d8SAndrew Thompson 		m->m_pkthdr.len = m->m_len = 1;
7163671d9d8SAndrew Thompson 
717ed6d949aSAndrew Thompson 		if (actlen > MCLBYTES - 1)
718ed6d949aSAndrew Thompson 			actlen = MCLBYTES - 1;
7193671d9d8SAndrew Thompson 
720ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
721ed6d949aSAndrew Thompson 		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
722ed6d949aSAndrew Thompson 		m->m_pkthdr.len += actlen;
723ed6d949aSAndrew Thompson 		m->m_len += actlen;
7243671d9d8SAndrew Thompson 
7253671d9d8SAndrew Thompson 		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
726ed6d949aSAndrew Thompson 			actlen);
7273671d9d8SAndrew Thompson 
7283671d9d8SAndrew Thompson 		/* Validate packet and send it up the stack */
7293671d9d8SAndrew Thompson 		if (m->m_pkthdr.len < sizeof(*hdr)) {
7303671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI event packet is too short\n");
7313671d9d8SAndrew Thompson 
7323671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7333671d9d8SAndrew Thompson 			goto submit_next;
7343671d9d8SAndrew Thompson 		}
7353671d9d8SAndrew Thompson 
7363671d9d8SAndrew Thompson 		hdr = mtod(m, ng_hci_event_pkt_t *);
7373671d9d8SAndrew Thompson 		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
7383671d9d8SAndrew Thompson 			UBT_ERR(sc, "Invalid HCI event packet size, " \
7393671d9d8SAndrew Thompson 				"length=%d, pktlen=%d\n",
7403671d9d8SAndrew Thompson 				hdr->length, m->m_pkthdr.len);
7413671d9d8SAndrew Thompson 
7423671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7433671d9d8SAndrew Thompson 			goto submit_next;
7443671d9d8SAndrew Thompson 		}
7453671d9d8SAndrew Thompson 
7463671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
7473671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len, hdr->length);
7483671d9d8SAndrew Thompson 
7493671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
7503671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
7513671d9d8SAndrew Thompson 
7523671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
7533671d9d8SAndrew Thompson 		/* m == NULL at this point */
7543671d9d8SAndrew Thompson 		/* FALLTHROUGH */
7553671d9d8SAndrew Thompson 
7563671d9d8SAndrew Thompson 	case USB_ST_SETUP:
7573671d9d8SAndrew Thompson submit_next:
7583671d9d8SAndrew Thompson 		NG_FREE_M(m); /* checks for m != NULL */
7593671d9d8SAndrew Thompson 
760ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
761a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
7623671d9d8SAndrew Thompson 		break;
7633671d9d8SAndrew Thompson 
7643671d9d8SAndrew Thompson 	default: /* Error */
765ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
7663671d9d8SAndrew Thompson 			UBT_WARN(sc, "interrupt transfer failed: %s\n",
767ed6d949aSAndrew Thompson 				usbd_errstr(error));
7683671d9d8SAndrew Thompson 
7693671d9d8SAndrew Thompson 			/* Try to clear stall first */
770ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
7713671d9d8SAndrew Thompson 			goto submit_next;
7723671d9d8SAndrew Thompson 		}
7733671d9d8SAndrew Thompson 			/* transfer cancelled */
7743671d9d8SAndrew Thompson 		break;
7753671d9d8SAndrew Thompson 	}
7763671d9d8SAndrew Thompson } /* ubt_intr_read_callback */
7773671d9d8SAndrew Thompson 
7783671d9d8SAndrew Thompson /*
7793671d9d8SAndrew Thompson  * Called when incoming bulk transfer (ACL packet) has completed, i.e.
7803671d9d8SAndrew Thompson  * ACL packet was received from the device.
7813671d9d8SAndrew Thompson  * USB context.
7823671d9d8SAndrew Thompson  */
7833671d9d8SAndrew Thompson 
7843671d9d8SAndrew Thompson static void
785ed6d949aSAndrew Thompson ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
7863671d9d8SAndrew Thompson {
787ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
7883671d9d8SAndrew Thompson 	struct mbuf		*m;
7893671d9d8SAndrew Thompson 	ng_hci_acldata_pkt_t	*hdr;
790ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
7913671d9d8SAndrew Thompson 	uint16_t		len;
792ed6d949aSAndrew Thompson 	int			actlen;
793ed6d949aSAndrew Thompson 
794ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
7953671d9d8SAndrew Thompson 
7963671d9d8SAndrew Thompson 	m = NULL;
7973671d9d8SAndrew Thompson 
7983671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
7993671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
8003671d9d8SAndrew Thompson 		/* Allocate new mbuf */
8013671d9d8SAndrew Thompson 		MGETHDR(m, M_DONTWAIT, MT_DATA);
8023671d9d8SAndrew Thompson 		if (m == NULL) {
8033671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8043671d9d8SAndrew Thompson 			goto submit_next;
8053671d9d8SAndrew Thompson 		}
8063671d9d8SAndrew Thompson 
8073671d9d8SAndrew Thompson 		MCLGET(m, M_DONTWAIT);
8083671d9d8SAndrew Thompson 		if (!(m->m_flags & M_EXT)) {
8093671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8103671d9d8SAndrew Thompson 			goto submit_next;
8113671d9d8SAndrew Thompson 		}
8123671d9d8SAndrew Thompson 
8133671d9d8SAndrew Thompson 		/* Add HCI packet type */
8143671d9d8SAndrew Thompson 		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
8153671d9d8SAndrew Thompson 		m->m_pkthdr.len = m->m_len = 1;
8163671d9d8SAndrew Thompson 
817ed6d949aSAndrew Thompson 		if (actlen > MCLBYTES - 1)
818ed6d949aSAndrew Thompson 			actlen = MCLBYTES - 1;
8193671d9d8SAndrew Thompson 
820ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
821ed6d949aSAndrew Thompson 		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
822ed6d949aSAndrew Thompson 		m->m_pkthdr.len += actlen;
823ed6d949aSAndrew Thompson 		m->m_len += actlen;
8243671d9d8SAndrew Thompson 
8253671d9d8SAndrew Thompson 		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
826ed6d949aSAndrew Thompson 			actlen);
8273671d9d8SAndrew Thompson 
8283671d9d8SAndrew Thompson 		/* Validate packet and send it up the stack */
8293671d9d8SAndrew Thompson 		if (m->m_pkthdr.len < sizeof(*hdr)) {
8303671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI ACL packet is too short\n");
8313671d9d8SAndrew Thompson 
8323671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8333671d9d8SAndrew Thompson 			goto submit_next;
8343671d9d8SAndrew Thompson 		}
8353671d9d8SAndrew Thompson 
8363671d9d8SAndrew Thompson 		hdr = mtod(m, ng_hci_acldata_pkt_t *);
8373671d9d8SAndrew Thompson 		len = le16toh(hdr->length);
8383671d9d8SAndrew Thompson 		if (len != (m->m_pkthdr.len - sizeof(*hdr))) {
8393671d9d8SAndrew Thompson 			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
8403671d9d8SAndrew Thompson 				"pktlen=%d\n", len, m->m_pkthdr.len);
8413671d9d8SAndrew Thompson 
8423671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8433671d9d8SAndrew Thompson 			goto submit_next;
8443671d9d8SAndrew Thompson 		}
8453671d9d8SAndrew Thompson 
8463671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
8473671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len, len);
8483671d9d8SAndrew Thompson 
8493671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
8503671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
8513671d9d8SAndrew Thompson 
8523671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
8533671d9d8SAndrew Thompson 		/* m == NULL at this point */
8543671d9d8SAndrew Thompson 		/* FALLTHOUGH */
8553671d9d8SAndrew Thompson 
8563671d9d8SAndrew Thompson 	case USB_ST_SETUP:
8573671d9d8SAndrew Thompson submit_next:
8583671d9d8SAndrew Thompson 		NG_FREE_M(m); /* checks for m != NULL */
8593671d9d8SAndrew Thompson 
860ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
861a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
8623671d9d8SAndrew Thompson 		break;
8633671d9d8SAndrew Thompson 
8643671d9d8SAndrew Thompson 	default: /* Error */
865ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
8663671d9d8SAndrew Thompson 			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
867ed6d949aSAndrew Thompson 				usbd_errstr(error));
8683671d9d8SAndrew Thompson 
8693671d9d8SAndrew Thompson 			/* Try to clear stall first */
870ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
8713671d9d8SAndrew Thompson 			goto submit_next;
8723671d9d8SAndrew Thompson 		}
8733671d9d8SAndrew Thompson 			/* transfer cancelled */
8743671d9d8SAndrew Thompson 		break;
8753671d9d8SAndrew Thompson 	}
8763671d9d8SAndrew Thompson } /* ubt_bulk_read_callback */
8773671d9d8SAndrew Thompson 
8783671d9d8SAndrew Thompson /*
8793671d9d8SAndrew Thompson  * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
8803671d9d8SAndrew Thompson  * ACL packet was sent to the device.
8813671d9d8SAndrew Thompson  * USB context.
8823671d9d8SAndrew Thompson  */
8833671d9d8SAndrew Thompson 
8843671d9d8SAndrew Thompson static void
885ed6d949aSAndrew Thompson ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
8863671d9d8SAndrew Thompson {
887ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
8883671d9d8SAndrew Thompson 	struct mbuf		*m;
889ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
890ed6d949aSAndrew Thompson 	int			actlen;
891ed6d949aSAndrew Thompson 
892ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
8933671d9d8SAndrew Thompson 
8943671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
8953671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
896ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
897ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
8983671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
8993671d9d8SAndrew Thompson 		/* FALLTHROUGH */
9003671d9d8SAndrew Thompson 
9013671d9d8SAndrew Thompson 	case USB_ST_SETUP:
9023671d9d8SAndrew Thompson send_next:
9033671d9d8SAndrew Thompson 		/* Get next mbuf, if any */
9043671d9d8SAndrew Thompson 		UBT_NG_LOCK(sc);
9053671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
9063671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
9073671d9d8SAndrew Thompson 
9083671d9d8SAndrew Thompson 		if (m == NULL) {
9093671d9d8SAndrew Thompson 			UBT_INFO(sc, "ACL data queue is empty\n");
9103671d9d8SAndrew Thompson 			break; /* transfer completed */
9113671d9d8SAndrew Thompson 		}
9123671d9d8SAndrew Thompson 
9133671d9d8SAndrew Thompson 		/*
9143671d9d8SAndrew Thompson 		 * Copy ACL data frame back to a linear USB transfer buffer
9153671d9d8SAndrew Thompson 		 * and schedule transfer
9163671d9d8SAndrew Thompson 		 */
9173671d9d8SAndrew Thompson 
918ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
919ed6d949aSAndrew Thompson 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
920ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
9213671d9d8SAndrew Thompson 
9223671d9d8SAndrew Thompson 		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
9233671d9d8SAndrew Thompson 			m->m_pkthdr.len);
9243671d9d8SAndrew Thompson 
9253671d9d8SAndrew Thompson 		NG_FREE_M(m);
9263671d9d8SAndrew Thompson 
927a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
9283671d9d8SAndrew Thompson 		break;
9293671d9d8SAndrew Thompson 
9303671d9d8SAndrew Thompson 	default: /* Error */
931ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
9323671d9d8SAndrew Thompson 			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
933ed6d949aSAndrew Thompson 				usbd_errstr(error));
9343671d9d8SAndrew Thompson 
9353671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
9363671d9d8SAndrew Thompson 
9373671d9d8SAndrew Thompson 			/* try to clear stall first */
938ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
9393671d9d8SAndrew Thompson 			goto send_next;
9403671d9d8SAndrew Thompson 		}
9413671d9d8SAndrew Thompson 			/* transfer cancelled */
9423671d9d8SAndrew Thompson 		break;
9433671d9d8SAndrew Thompson 	}
9443671d9d8SAndrew Thompson } /* ubt_bulk_write_callback */
9453671d9d8SAndrew Thompson 
9463671d9d8SAndrew Thompson /*
9473671d9d8SAndrew Thompson  * Called when incoming isoc transfer (SCO packet) has completed, i.e.
9483671d9d8SAndrew Thompson  * SCO packet was received from the device.
9493671d9d8SAndrew Thompson  * USB context.
9503671d9d8SAndrew Thompson  */
9513671d9d8SAndrew Thompson 
9523671d9d8SAndrew Thompson static void
953ed6d949aSAndrew Thompson ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
9543671d9d8SAndrew Thompson {
955ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
9563671d9d8SAndrew Thompson 	int			n;
957ed6d949aSAndrew Thompson 	int actlen, nframes;
958ed6d949aSAndrew Thompson 
959ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
9603671d9d8SAndrew Thompson 
9613671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9623671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
963ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++)
9643671d9d8SAndrew Thompson 			if (ubt_isoc_read_one_frame(xfer, n) < 0)
9653671d9d8SAndrew Thompson 				break;
9663671d9d8SAndrew Thompson 		/* FALLTHROUGH */
9673671d9d8SAndrew Thompson 
9683671d9d8SAndrew Thompson 	case USB_ST_SETUP:
9693671d9d8SAndrew Thompson read_next:
970ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++)
971ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, n,
972ed6d949aSAndrew Thompson 			    usbd_xfer_max_framelen(xfer));
9733671d9d8SAndrew Thompson 
974a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
9753671d9d8SAndrew Thompson 		break;
9763671d9d8SAndrew Thompson 
9773671d9d8SAndrew Thompson 	default: /* Error */
978ed6d949aSAndrew Thompson                 if (error != USB_ERR_CANCELLED) {
9793671d9d8SAndrew Thompson                         UBT_STAT_IERROR(sc);
9803671d9d8SAndrew Thompson                         goto read_next;
9813671d9d8SAndrew Thompson                 }
9823671d9d8SAndrew Thompson 
9833671d9d8SAndrew Thompson 		/* transfer cancelled */
9843671d9d8SAndrew Thompson 		break;
9853671d9d8SAndrew Thompson 	}
9863671d9d8SAndrew Thompson } /* ubt_isoc_read_callback */
9873671d9d8SAndrew Thompson 
9883671d9d8SAndrew Thompson /*
9893671d9d8SAndrew Thompson  * Helper function. Called from ubt_isoc_read_callback() to read
9903671d9d8SAndrew Thompson  * SCO data from one frame.
9913671d9d8SAndrew Thompson  * USB context.
9923671d9d8SAndrew Thompson  */
9933671d9d8SAndrew Thompson 
9943671d9d8SAndrew Thompson static int
995760bc48eSAndrew Thompson ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
9963671d9d8SAndrew Thompson {
997ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
998ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
9993671d9d8SAndrew Thompson 	struct mbuf		*m;
1000ed6d949aSAndrew Thompson 	int			len, want, got, total;
10013671d9d8SAndrew Thompson 
10023671d9d8SAndrew Thompson 	/* Get existing SCO reassembly buffer */
1003ed6d949aSAndrew Thompson 	pc = usbd_xfer_get_frame(xfer, 0);
10043671d9d8SAndrew Thompson 	m = sc->sc_isoc_in_buffer;
10058f9e0ef9SAndrew Thompson 	total = usbd_xfer_frame_len(xfer, frame_no);
10063671d9d8SAndrew Thompson 
10073671d9d8SAndrew Thompson 	/* While we have data in the frame */
1008ed6d949aSAndrew Thompson 	while (total > 0) {
10093671d9d8SAndrew Thompson 		if (m == NULL) {
10103671d9d8SAndrew Thompson 			/* Start new reassembly buffer */
10113671d9d8SAndrew Thompson 			MGETHDR(m, M_DONTWAIT, MT_DATA);
10123671d9d8SAndrew Thompson 			if (m == NULL) {
10133671d9d8SAndrew Thompson 				UBT_STAT_IERROR(sc);
10143671d9d8SAndrew Thompson 				return (-1);	/* XXX out of sync! */
10153671d9d8SAndrew Thompson 			}
10163671d9d8SAndrew Thompson 
10173671d9d8SAndrew Thompson 			MCLGET(m, M_DONTWAIT);
10183671d9d8SAndrew Thompson 			if (!(m->m_flags & M_EXT)) {
10193671d9d8SAndrew Thompson 				UBT_STAT_IERROR(sc);
10203671d9d8SAndrew Thompson 				NG_FREE_M(m);
10213671d9d8SAndrew Thompson 				return (-1);	/* XXX out of sync! */
10223671d9d8SAndrew Thompson 			}
10233671d9d8SAndrew Thompson 
10243671d9d8SAndrew Thompson 			/* Expect SCO header */
10253671d9d8SAndrew Thompson 			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
10263671d9d8SAndrew Thompson 			m->m_pkthdr.len = m->m_len = got = 1;
10273671d9d8SAndrew Thompson 			want = sizeof(ng_hci_scodata_pkt_t);
10283671d9d8SAndrew Thompson 		} else {
10293671d9d8SAndrew Thompson 			/*
10303671d9d8SAndrew Thompson 			 * Check if we have SCO header and if so
10313671d9d8SAndrew Thompson 			 * adjust amount of data we want
10323671d9d8SAndrew Thompson 			 */
10333671d9d8SAndrew Thompson 			got = m->m_pkthdr.len;
10343671d9d8SAndrew Thompson 			want = sizeof(ng_hci_scodata_pkt_t);
10353671d9d8SAndrew Thompson 
10363671d9d8SAndrew Thompson 			if (got >= want)
10373671d9d8SAndrew Thompson 				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
10383671d9d8SAndrew Thompson 		}
10393671d9d8SAndrew Thompson 
10403671d9d8SAndrew Thompson 		/* Append frame data to the SCO reassembly buffer */
1041ed6d949aSAndrew Thompson 		len = total;
10423671d9d8SAndrew Thompson 		if (got + len > want)
10433671d9d8SAndrew Thompson 			len = want - got;
10443671d9d8SAndrew Thompson 
1045ed6d949aSAndrew Thompson 		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
10463671d9d8SAndrew Thompson 			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
10473671d9d8SAndrew Thompson 
10483671d9d8SAndrew Thompson 		m->m_pkthdr.len += len;
10493671d9d8SAndrew Thompson 		m->m_len += len;
1050ed6d949aSAndrew Thompson 		total -= len;
10513671d9d8SAndrew Thompson 
10523671d9d8SAndrew Thompson 		/* Check if we got everything we wanted, if not - continue */
10533671d9d8SAndrew Thompson 		if (got != want)
10543671d9d8SAndrew Thompson 			continue;
10553671d9d8SAndrew Thompson 
10563671d9d8SAndrew Thompson 		/* If we got here then we got complete SCO frame */
10573671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
10583671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len,
10593671d9d8SAndrew Thompson 			mtod(m, ng_hci_scodata_pkt_t *)->length);
10603671d9d8SAndrew Thompson 
10613671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
10623671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
10633671d9d8SAndrew Thompson 
10643671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
10653671d9d8SAndrew Thompson 		/* m == NULL at this point */
10663671d9d8SAndrew Thompson 	}
10673671d9d8SAndrew Thompson 
10683671d9d8SAndrew Thompson 	/* Put SCO reassembly buffer back */
10693671d9d8SAndrew Thompson 	sc->sc_isoc_in_buffer = m;
10703671d9d8SAndrew Thompson 
10713671d9d8SAndrew Thompson 	return (0);
10723671d9d8SAndrew Thompson } /* ubt_isoc_read_one_frame */
10733671d9d8SAndrew Thompson 
10743671d9d8SAndrew Thompson /*
10753671d9d8SAndrew Thompson  * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
10763671d9d8SAndrew Thompson  * SCO packet was sent to the device.
10773671d9d8SAndrew Thompson  * USB context.
10783671d9d8SAndrew Thompson  */
10793671d9d8SAndrew Thompson 
10803671d9d8SAndrew Thompson static void
1081ed6d949aSAndrew Thompson ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
10823671d9d8SAndrew Thompson {
1083ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1084ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
10853671d9d8SAndrew Thompson 	struct mbuf		*m;
10863671d9d8SAndrew Thompson 	int			n, space, offset;
1087ed6d949aSAndrew Thompson 	int			actlen, nframes;
1088ed6d949aSAndrew Thompson 
1089ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1090ed6d949aSAndrew Thompson 	pc = usbd_xfer_get_frame(xfer, 0);
10913671d9d8SAndrew Thompson 
10923671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
10933671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
1094ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1095ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
10963671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
10973671d9d8SAndrew Thompson 		/* FALLTHROUGH */
10983671d9d8SAndrew Thompson 
10993671d9d8SAndrew Thompson 	case USB_ST_SETUP:
11003671d9d8SAndrew Thompson send_next:
11013671d9d8SAndrew Thompson 		offset = 0;
1102ed6d949aSAndrew Thompson 		space = usbd_xfer_max_framelen(xfer) * nframes;
11033671d9d8SAndrew Thompson 		m = NULL;
11043671d9d8SAndrew Thompson 
11053671d9d8SAndrew Thompson 		while (space > 0) {
11063671d9d8SAndrew Thompson 			if (m == NULL) {
11073671d9d8SAndrew Thompson 				UBT_NG_LOCK(sc);
11083671d9d8SAndrew Thompson 				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
11093671d9d8SAndrew Thompson 				UBT_NG_UNLOCK(sc);
11103671d9d8SAndrew Thompson 
11113671d9d8SAndrew Thompson 				if (m == NULL)
11123671d9d8SAndrew Thompson 					break;
11133671d9d8SAndrew Thompson 			}
11143671d9d8SAndrew Thompson 
11153671d9d8SAndrew Thompson 			n = min(space, m->m_pkthdr.len);
11163671d9d8SAndrew Thompson 			if (n > 0) {
1117ed6d949aSAndrew Thompson 				usbd_m_copy_in(pc, offset, m,0, n);
11183671d9d8SAndrew Thompson 				m_adj(m, n);
11193671d9d8SAndrew Thompson 
11203671d9d8SAndrew Thompson 				offset += n;
11213671d9d8SAndrew Thompson 				space -= n;
11223671d9d8SAndrew Thompson 			}
11233671d9d8SAndrew Thompson 
11243671d9d8SAndrew Thompson 			if (m->m_pkthdr.len == 0)
11253671d9d8SAndrew Thompson 				NG_FREE_M(m); /* sets m = NULL */
11263671d9d8SAndrew Thompson 		}
11273671d9d8SAndrew Thompson 
11283671d9d8SAndrew Thompson 		/* Put whatever is left from mbuf back on queue */
11293671d9d8SAndrew Thompson 		if (m != NULL) {
11303671d9d8SAndrew Thompson 			UBT_NG_LOCK(sc);
11313671d9d8SAndrew Thompson 			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
11323671d9d8SAndrew Thompson 			UBT_NG_UNLOCK(sc);
11333671d9d8SAndrew Thompson 		}
11343671d9d8SAndrew Thompson 
11353671d9d8SAndrew Thompson 		/*
11363671d9d8SAndrew Thompson 		 * Calculate sizes for isoc frames.
11373671d9d8SAndrew Thompson 		 * Note that offset could be 0 at this point (i.e. we have
11383671d9d8SAndrew Thompson 		 * nothing to send). That is fine, as we have isoc. transfers
11393671d9d8SAndrew Thompson 		 * going in both directions all the time. In this case it
11403671d9d8SAndrew Thompson 		 * would be just empty isoc. transfer.
11413671d9d8SAndrew Thompson 		 */
11423671d9d8SAndrew Thompson 
1143ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++) {
1144ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, n,
1145ed6d949aSAndrew Thompson 			    min(offset, usbd_xfer_max_framelen(xfer)));
11468f9e0ef9SAndrew Thompson 			offset -= usbd_xfer_frame_len(xfer, n);
11473671d9d8SAndrew Thompson 		}
11483671d9d8SAndrew Thompson 
1149a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
11503671d9d8SAndrew Thompson 		break;
11513671d9d8SAndrew Thompson 
11523671d9d8SAndrew Thompson 	default: /* Error */
1153ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
11543671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
11553671d9d8SAndrew Thompson 			goto send_next;
11563671d9d8SAndrew Thompson 		}
11573671d9d8SAndrew Thompson 
11583671d9d8SAndrew Thompson 		/* transfer cancelled */
11593671d9d8SAndrew Thompson 		break;
11603671d9d8SAndrew Thompson 	}
11613671d9d8SAndrew Thompson }
11623671d9d8SAndrew Thompson 
11633671d9d8SAndrew Thompson /*
11643671d9d8SAndrew Thompson  * Utility function to forward provided mbuf upstream (i.e. up the stack).
11653671d9d8SAndrew Thompson  * Modifies value of the mbuf pointer (sets it to NULL).
11663671d9d8SAndrew Thompson  * Save to call from any context.
11673671d9d8SAndrew Thompson  */
11683671d9d8SAndrew Thompson 
11693671d9d8SAndrew Thompson static int
11703671d9d8SAndrew Thompson ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
11713671d9d8SAndrew Thompson {
11723671d9d8SAndrew Thompson 	hook_p	hook;
11733671d9d8SAndrew Thompson 	int	error;
11743671d9d8SAndrew Thompson 
11753671d9d8SAndrew Thompson 	/*
11763671d9d8SAndrew Thompson 	 * Close the race with Netgraph hook newhook/disconnect methods.
11773671d9d8SAndrew Thompson 	 * Save the hook pointer atomically. Two cases are possible:
11783671d9d8SAndrew Thompson 	 *
11793671d9d8SAndrew Thompson 	 * 1) The hook pointer is NULL. It means disconnect method got
11803671d9d8SAndrew Thompson 	 *    there first. In this case we are done.
11813671d9d8SAndrew Thompson 	 *
11823671d9d8SAndrew Thompson 	 * 2) The hook pointer is not NULL. It means that hook pointer
11833671d9d8SAndrew Thompson 	 *    could be either in valid or invalid (i.e. in the process
11843671d9d8SAndrew Thompson 	 *    of disconnect) state. In any case grab an extra reference
11853671d9d8SAndrew Thompson 	 *    to protect the hook pointer.
11863671d9d8SAndrew Thompson 	 *
11873671d9d8SAndrew Thompson 	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
11883671d9d8SAndrew Thompson 	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
11893671d9d8SAndrew Thompson 	 */
11903671d9d8SAndrew Thompson 
11913671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
11923671d9d8SAndrew Thompson 	if ((hook = sc->sc_hook) != NULL)
11933671d9d8SAndrew Thompson 		NG_HOOK_REF(hook);
11943671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
11953671d9d8SAndrew Thompson 
11963671d9d8SAndrew Thompson 	if (hook == NULL) {
11973671d9d8SAndrew Thompson 		NG_FREE_M(*m);
11983671d9d8SAndrew Thompson 		return (ENETDOWN);
11993671d9d8SAndrew Thompson 	}
12003671d9d8SAndrew Thompson 
12013671d9d8SAndrew Thompson 	NG_SEND_DATA_ONLY(error, hook, *m);
12023671d9d8SAndrew Thompson 	NG_HOOK_UNREF(hook);
12033671d9d8SAndrew Thompson 
12043671d9d8SAndrew Thompson 	if (error != 0)
12053671d9d8SAndrew Thompson 		UBT_STAT_IERROR(sc);
12063671d9d8SAndrew Thompson 
12073671d9d8SAndrew Thompson 	return (error);
12083671d9d8SAndrew Thompson } /* ubt_fwd_mbuf_up */
12093671d9d8SAndrew Thompson 
12103671d9d8SAndrew Thompson /****************************************************************************
12113671d9d8SAndrew Thompson  ****************************************************************************
12123671d9d8SAndrew Thompson  **                                 Glue
12133671d9d8SAndrew Thompson  ****************************************************************************
12143671d9d8SAndrew Thompson  ****************************************************************************/
12153671d9d8SAndrew Thompson 
12163671d9d8SAndrew Thompson /*
12173671d9d8SAndrew Thompson  * Schedule glue task. Should be called with sc_ng_mtx held.
12183671d9d8SAndrew Thompson  * Netgraph context.
12193671d9d8SAndrew Thompson  */
12203671d9d8SAndrew Thompson 
12213671d9d8SAndrew Thompson static void
12223671d9d8SAndrew Thompson ubt_task_schedule(ubt_softc_p sc, int action)
12233671d9d8SAndrew Thompson {
12243671d9d8SAndrew Thompson 	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
12253671d9d8SAndrew Thompson 
12263671d9d8SAndrew Thompson 	/*
12273671d9d8SAndrew Thompson 	 * Try to handle corner case when "start all" and "stop all"
12283671d9d8SAndrew Thompson 	 * actions can both be set before task is executed.
12293671d9d8SAndrew Thompson 	 *
12303671d9d8SAndrew Thompson 	 * The rules are
12313671d9d8SAndrew Thompson 	 *
12323671d9d8SAndrew Thompson 	 * sc_task_flags	action		new sc_task_flags
12333671d9d8SAndrew Thompson 	 * ------------------------------------------------------
12343671d9d8SAndrew Thompson 	 * 0			start		start
12353671d9d8SAndrew Thompson 	 * 0			stop		stop
12363671d9d8SAndrew Thompson 	 * start		start		start
12373671d9d8SAndrew Thompson 	 * start		stop		stop
12383671d9d8SAndrew Thompson 	 * stop			start		stop|start
12393671d9d8SAndrew Thompson 	 * stop			stop		stop
12403671d9d8SAndrew Thompson 	 * stop|start		start		stop|start
12413671d9d8SAndrew Thompson 	 * stop|start		stop		stop
12423671d9d8SAndrew Thompson 	 */
12433671d9d8SAndrew Thompson 
12443671d9d8SAndrew Thompson 	if (action != 0) {
12453671d9d8SAndrew Thompson 		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
12463671d9d8SAndrew Thompson 			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
12473671d9d8SAndrew Thompson 
12483671d9d8SAndrew Thompson 		sc->sc_task_flags |= action;
12493671d9d8SAndrew Thompson 	}
12503671d9d8SAndrew Thompson 
12513671d9d8SAndrew Thompson 	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
12523671d9d8SAndrew Thompson 		return;
12533671d9d8SAndrew Thompson 
12543671d9d8SAndrew Thompson 	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
12553671d9d8SAndrew Thompson 		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
12563671d9d8SAndrew Thompson 		return;
12573671d9d8SAndrew Thompson 	}
12583671d9d8SAndrew Thompson 
12593671d9d8SAndrew Thompson 	/* XXX: i think this should never happen */
12603671d9d8SAndrew Thompson } /* ubt_task_schedule */
12613671d9d8SAndrew Thompson 
12623671d9d8SAndrew Thompson /*
12633671d9d8SAndrew Thompson  * Glue task. Examines sc_task_flags and does things depending on it.
12643671d9d8SAndrew Thompson  * Taskqueue context.
12653671d9d8SAndrew Thompson  */
12663671d9d8SAndrew Thompson 
12673671d9d8SAndrew Thompson static void
12683671d9d8SAndrew Thompson ubt_task(void *context, int pending)
12693671d9d8SAndrew Thompson {
12703671d9d8SAndrew Thompson 	ubt_softc_p	sc = context;
12713671d9d8SAndrew Thompson 	int		task_flags, i;
12723671d9d8SAndrew Thompson 
12733671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
12743671d9d8SAndrew Thompson 	task_flags = sc->sc_task_flags;
12753671d9d8SAndrew Thompson 	sc->sc_task_flags = 0;
12763671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
12773671d9d8SAndrew Thompson 
12783671d9d8SAndrew Thompson 	/*
12793671d9d8SAndrew Thompson 	 * Stop all USB transfers synchronously.
12803671d9d8SAndrew Thompson 	 * Stop interface #0 and #1 transfers at the same time and in the
1281a593f6b8SAndrew Thompson 	 * same loop. usbd_transfer_drain() will do appropriate locking.
12823671d9d8SAndrew Thompson 	 */
12833671d9d8SAndrew Thompson 
12843671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_STOP_ALL)
12853671d9d8SAndrew Thompson 		for (i = 0; i < UBT_N_TRANSFER; i ++)
1286a593f6b8SAndrew Thompson 			usbd_transfer_drain(sc->sc_xfer[i]);
12873671d9d8SAndrew Thompson 
12883671d9d8SAndrew Thompson 	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
12893671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_ALL) {
12903671d9d8SAndrew Thompson 		/*
12913671d9d8SAndrew Thompson 		 * Interface #0
12923671d9d8SAndrew Thompson 		 */
12933671d9d8SAndrew Thompson 
12943671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
12953671d9d8SAndrew Thompson 
12963671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
12973671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
12983671d9d8SAndrew Thompson 
12993671d9d8SAndrew Thompson 		/*
13003671d9d8SAndrew Thompson 		 * Interface #1
13013671d9d8SAndrew Thompson 		 * Start both read and write isoc. transfers by default.
13023671d9d8SAndrew Thompson 		 * Get them going all the time even if we have nothing
13033671d9d8SAndrew Thompson 		 * to send to avoid any delays.
13043671d9d8SAndrew Thompson 		 */
13053671d9d8SAndrew Thompson 
13063671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
13073671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
13083671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
13093671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
13103671d9d8SAndrew Thompson 
13113671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
13123671d9d8SAndrew Thompson 	}
13133671d9d8SAndrew Thompson 
13143671d9d8SAndrew Thompson  	/* Start outgoing control transfer */
13153671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_CTRL) {
13163671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
13173671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
13183671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
13193671d9d8SAndrew Thompson 	}
13203671d9d8SAndrew Thompson 
13213671d9d8SAndrew Thompson 	/* Start outgoing bulk transfer */
13223671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_BULK) {
13233671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
13243671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
13253671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
13263671d9d8SAndrew Thompson 	}
13273671d9d8SAndrew Thompson } /* ubt_task */
13283671d9d8SAndrew Thompson 
13293671d9d8SAndrew Thompson /****************************************************************************
13303671d9d8SAndrew Thompson  ****************************************************************************
13313671d9d8SAndrew Thompson  **                        Netgraph specific
13323671d9d8SAndrew Thompson  ****************************************************************************
13333671d9d8SAndrew Thompson  ****************************************************************************/
13343671d9d8SAndrew Thompson 
13353671d9d8SAndrew Thompson /*
13363671d9d8SAndrew Thompson  * Netgraph node constructor. Do not allow to create node of this type.
13373671d9d8SAndrew Thompson  * Netgraph context.
13383671d9d8SAndrew Thompson  */
13393671d9d8SAndrew Thompson 
13403671d9d8SAndrew Thompson static int
13413671d9d8SAndrew Thompson ng_ubt_constructor(node_p node)
13423671d9d8SAndrew Thompson {
13433671d9d8SAndrew Thompson 	return (EINVAL);
13443671d9d8SAndrew Thompson } /* ng_ubt_constructor */
13453671d9d8SAndrew Thompson 
13463671d9d8SAndrew Thompson /*
13473671d9d8SAndrew Thompson  * Netgraph node destructor. Destroy node only when device has been detached.
13483671d9d8SAndrew Thompson  * Netgraph context.
13493671d9d8SAndrew Thompson  */
13503671d9d8SAndrew Thompson 
13513671d9d8SAndrew Thompson static int
13523671d9d8SAndrew Thompson ng_ubt_shutdown(node_p node)
13533671d9d8SAndrew Thompson {
13543671d9d8SAndrew Thompson 	if (node->nd_flags & NGF_REALLY_DIE) {
13553671d9d8SAndrew Thompson 		/*
13563671d9d8SAndrew Thompson                  * We came here because the USB device is being
13573671d9d8SAndrew Thompson 		 * detached, so stop being persistant.
13583671d9d8SAndrew Thompson                  */
13593671d9d8SAndrew Thompson 		NG_NODE_SET_PRIVATE(node, NULL);
13603671d9d8SAndrew Thompson 		NG_NODE_UNREF(node);
13613671d9d8SAndrew Thompson 	} else
13623671d9d8SAndrew Thompson 		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
13633671d9d8SAndrew Thompson 
13643671d9d8SAndrew Thompson 	return (0);
13653671d9d8SAndrew Thompson } /* ng_ubt_shutdown */
13663671d9d8SAndrew Thompson 
13673671d9d8SAndrew Thompson /*
13683671d9d8SAndrew Thompson  * Create new hook. There can only be one.
13693671d9d8SAndrew Thompson  * Netgraph context.
13703671d9d8SAndrew Thompson  */
13713671d9d8SAndrew Thompson 
13723671d9d8SAndrew Thompson static int
13733671d9d8SAndrew Thompson ng_ubt_newhook(node_p node, hook_p hook, char const *name)
13743671d9d8SAndrew Thompson {
13753671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
13763671d9d8SAndrew Thompson 
13773671d9d8SAndrew Thompson 	if (strcmp(name, NG_UBT_HOOK) != 0)
13783671d9d8SAndrew Thompson 		return (EINVAL);
13793671d9d8SAndrew Thompson 
13803671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
13813671d9d8SAndrew Thompson 	if (sc->sc_hook != NULL) {
13823671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
13833671d9d8SAndrew Thompson 
13843671d9d8SAndrew Thompson 		return (EISCONN);
13853671d9d8SAndrew Thompson 	}
13863671d9d8SAndrew Thompson 
13873671d9d8SAndrew Thompson 	sc->sc_hook = hook;
13883671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
13893671d9d8SAndrew Thompson 
13903671d9d8SAndrew Thompson 	return (0);
13913671d9d8SAndrew Thompson } /* ng_ubt_newhook */
13923671d9d8SAndrew Thompson 
13933671d9d8SAndrew Thompson /*
13943671d9d8SAndrew Thompson  * Connect hook. Start incoming USB transfers.
13953671d9d8SAndrew Thompson  * Netgraph context.
13963671d9d8SAndrew Thompson  */
13973671d9d8SAndrew Thompson 
13983671d9d8SAndrew Thompson static int
13993671d9d8SAndrew Thompson ng_ubt_connect(hook_p hook)
14003671d9d8SAndrew Thompson {
14013671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
14023671d9d8SAndrew Thompson 
14033671d9d8SAndrew Thompson 	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
14043671d9d8SAndrew Thompson 
14053671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
14063671d9d8SAndrew Thompson 	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
14073671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
14083671d9d8SAndrew Thompson 
14093671d9d8SAndrew Thompson 	return (0);
14103671d9d8SAndrew Thompson } /* ng_ubt_connect */
14113671d9d8SAndrew Thompson 
14123671d9d8SAndrew Thompson /*
14133671d9d8SAndrew Thompson  * Disconnect hook.
14143671d9d8SAndrew Thompson  * Netgraph context.
14153671d9d8SAndrew Thompson  */
14163671d9d8SAndrew Thompson 
14173671d9d8SAndrew Thompson static int
14183671d9d8SAndrew Thompson ng_ubt_disconnect(hook_p hook)
14193671d9d8SAndrew Thompson {
14203671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
14213671d9d8SAndrew Thompson 
14223671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
14233671d9d8SAndrew Thompson 
14243671d9d8SAndrew Thompson 	if (hook != sc->sc_hook) {
14253671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
14263671d9d8SAndrew Thompson 
14273671d9d8SAndrew Thompson 		return (EINVAL);
14283671d9d8SAndrew Thompson 	}
14293671d9d8SAndrew Thompson 
14303671d9d8SAndrew Thompson 	sc->sc_hook = NULL;
14313671d9d8SAndrew Thompson 
14323671d9d8SAndrew Thompson 	/* Kick off task to stop all USB xfers */
14333671d9d8SAndrew Thompson 	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
14343671d9d8SAndrew Thompson 
14353671d9d8SAndrew Thompson 	/* Drain queues */
14363671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
14373671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
14383671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
14393671d9d8SAndrew Thompson 
14403671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
14413671d9d8SAndrew Thompson 
14423671d9d8SAndrew Thompson 	return (0);
14433671d9d8SAndrew Thompson } /* ng_ubt_disconnect */
14443671d9d8SAndrew Thompson 
14453671d9d8SAndrew Thompson /*
14463671d9d8SAndrew Thompson  * Process control message.
14473671d9d8SAndrew Thompson  * Netgraph context.
14483671d9d8SAndrew Thompson  */
14493671d9d8SAndrew Thompson 
14503671d9d8SAndrew Thompson static int
14513671d9d8SAndrew Thompson ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
14523671d9d8SAndrew Thompson {
14533671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
14543671d9d8SAndrew Thompson 	struct ng_mesg		*msg, *rsp = NULL;
14553671d9d8SAndrew Thompson 	struct ng_bt_mbufq	*q;
14563671d9d8SAndrew Thompson 	int			error = 0, queue, qlen;
14573671d9d8SAndrew Thompson 
14583671d9d8SAndrew Thompson 	NGI_GET_MSG(item, msg);
14593671d9d8SAndrew Thompson 
14603671d9d8SAndrew Thompson 	switch (msg->header.typecookie) {
14613671d9d8SAndrew Thompson 	case NGM_GENERIC_COOKIE:
14623671d9d8SAndrew Thompson 		switch (msg->header.cmd) {
14633671d9d8SAndrew Thompson 		case NGM_TEXT_STATUS:
14643671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
14653671d9d8SAndrew Thompson 			if (rsp == NULL) {
14663671d9d8SAndrew Thompson 				error = ENOMEM;
14673671d9d8SAndrew Thompson 				break;
14683671d9d8SAndrew Thompson 			}
14693671d9d8SAndrew Thompson 
14703671d9d8SAndrew Thompson 			snprintf(rsp->data, NG_TEXTRESPONSE,
14713671d9d8SAndrew Thompson 				"Hook: %s\n" \
14723671d9d8SAndrew Thompson 				"Task flags: %#x\n" \
14733671d9d8SAndrew Thompson 				"Debug: %d\n" \
14743671d9d8SAndrew Thompson 				"CMD queue: [have:%d,max:%d]\n" \
14753671d9d8SAndrew Thompson 				"ACL queue: [have:%d,max:%d]\n" \
14763671d9d8SAndrew Thompson 				"SCO queue: [have:%d,max:%d]",
14773671d9d8SAndrew Thompson 				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
14783671d9d8SAndrew Thompson 				sc->sc_task_flags,
14793671d9d8SAndrew Thompson 				sc->sc_debug,
14803671d9d8SAndrew Thompson 				sc->sc_cmdq.len,
14813671d9d8SAndrew Thompson 				sc->sc_cmdq.maxlen,
14823671d9d8SAndrew Thompson 				sc->sc_aclq.len,
14833671d9d8SAndrew Thompson 				sc->sc_aclq.maxlen,
14843671d9d8SAndrew Thompson 				sc->sc_scoq.len,
14853671d9d8SAndrew Thompson 				sc->sc_scoq.maxlen);
14863671d9d8SAndrew Thompson 			break;
14873671d9d8SAndrew Thompson 
14883671d9d8SAndrew Thompson 		default:
14893671d9d8SAndrew Thompson 			error = EINVAL;
14903671d9d8SAndrew Thompson 			break;
14913671d9d8SAndrew Thompson 		}
14923671d9d8SAndrew Thompson 		break;
14933671d9d8SAndrew Thompson 
14943671d9d8SAndrew Thompson 	case NGM_UBT_COOKIE:
14953671d9d8SAndrew Thompson 		switch (msg->header.cmd) {
14963671d9d8SAndrew Thompson 		case NGM_UBT_NODE_SET_DEBUG:
14973671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
14983671d9d8SAndrew Thompson 				error = EMSGSIZE;
14993671d9d8SAndrew Thompson 				break;
15003671d9d8SAndrew Thompson 			}
15013671d9d8SAndrew Thompson 
15023671d9d8SAndrew Thompson 			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
15033671d9d8SAndrew Thompson 			break;
15043671d9d8SAndrew Thompson 
15053671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_DEBUG:
15063671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
15073671d9d8SAndrew Thompson 			    M_NOWAIT);
15083671d9d8SAndrew Thompson 			if (rsp == NULL) {
15093671d9d8SAndrew Thompson 				error = ENOMEM;
15103671d9d8SAndrew Thompson 				break;
15113671d9d8SAndrew Thompson 			}
15123671d9d8SAndrew Thompson 
15133671d9d8SAndrew Thompson 			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
15143671d9d8SAndrew Thompson 			break;
15153671d9d8SAndrew Thompson 
15163671d9d8SAndrew Thompson 		case NGM_UBT_NODE_SET_QLEN:
15173671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
15183671d9d8SAndrew Thompson 				error = EMSGSIZE;
15193671d9d8SAndrew Thompson 				break;
15203671d9d8SAndrew Thompson 			}
15213671d9d8SAndrew Thompson 
15223671d9d8SAndrew Thompson 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
15233671d9d8SAndrew Thompson 			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
15243671d9d8SAndrew Thompson 
15253671d9d8SAndrew Thompson 			switch (queue) {
15263671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_CMD:
15273671d9d8SAndrew Thompson 				q = &sc->sc_cmdq;
15283671d9d8SAndrew Thompson 				break;
15293671d9d8SAndrew Thompson 
15303671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_ACL:
15313671d9d8SAndrew Thompson 				q = &sc->sc_aclq;
15323671d9d8SAndrew Thompson 				break;
15333671d9d8SAndrew Thompson 
15343671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_SCO:
15353671d9d8SAndrew Thompson 				q = &sc->sc_scoq;
15363671d9d8SAndrew Thompson 				break;
15373671d9d8SAndrew Thompson 
15383671d9d8SAndrew Thompson 			default:
15393671d9d8SAndrew Thompson 				error = EINVAL;
15403671d9d8SAndrew Thompson 				goto done;
15413671d9d8SAndrew Thompson 				/* NOT REACHED */
15423671d9d8SAndrew Thompson 			}
15433671d9d8SAndrew Thompson 
15443671d9d8SAndrew Thompson 			q->maxlen = qlen;
15453671d9d8SAndrew Thompson 			break;
15463671d9d8SAndrew Thompson 
15473671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_QLEN:
15483671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
15493671d9d8SAndrew Thompson 				error = EMSGSIZE;
15503671d9d8SAndrew Thompson 				break;
15513671d9d8SAndrew Thompson 			}
15523671d9d8SAndrew Thompson 
15533671d9d8SAndrew Thompson 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
15543671d9d8SAndrew Thompson 
15553671d9d8SAndrew Thompson 			switch (queue) {
15563671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_CMD:
15573671d9d8SAndrew Thompson 				q = &sc->sc_cmdq;
15583671d9d8SAndrew Thompson 				break;
15593671d9d8SAndrew Thompson 
15603671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_ACL:
15613671d9d8SAndrew Thompson 				q = &sc->sc_aclq;
15623671d9d8SAndrew Thompson 				break;
15633671d9d8SAndrew Thompson 
15643671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_SCO:
15653671d9d8SAndrew Thompson 				q = &sc->sc_scoq;
15663671d9d8SAndrew Thompson 				break;
15673671d9d8SAndrew Thompson 
15683671d9d8SAndrew Thompson 			default:
15693671d9d8SAndrew Thompson 				error = EINVAL;
15703671d9d8SAndrew Thompson 				goto done;
15713671d9d8SAndrew Thompson 				/* NOT REACHED */
15723671d9d8SAndrew Thompson 			}
15733671d9d8SAndrew Thompson 
15743671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
15753671d9d8SAndrew Thompson 				M_NOWAIT);
15763671d9d8SAndrew Thompson 			if (rsp == NULL) {
15773671d9d8SAndrew Thompson 				error = ENOMEM;
15783671d9d8SAndrew Thompson 				break;
15793671d9d8SAndrew Thompson 			}
15803671d9d8SAndrew Thompson 
15813671d9d8SAndrew Thompson 			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
15823671d9d8SAndrew Thompson 			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
15833671d9d8SAndrew Thompson 			break;
15843671d9d8SAndrew Thompson 
15853671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_STAT:
15863671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
15873671d9d8SAndrew Thompson 			    M_NOWAIT);
15883671d9d8SAndrew Thompson 			if (rsp == NULL) {
15893671d9d8SAndrew Thompson 				error = ENOMEM;
15903671d9d8SAndrew Thompson 				break;
15913671d9d8SAndrew Thompson 			}
15923671d9d8SAndrew Thompson 
15933671d9d8SAndrew Thompson 			bcopy(&sc->sc_stat, rsp->data,
15943671d9d8SAndrew Thompson 				sizeof(ng_ubt_node_stat_ep));
15953671d9d8SAndrew Thompson 			break;
15963671d9d8SAndrew Thompson 
15973671d9d8SAndrew Thompson 		case NGM_UBT_NODE_RESET_STAT:
15983671d9d8SAndrew Thompson 			UBT_STAT_RESET(sc);
15993671d9d8SAndrew Thompson 			break;
16003671d9d8SAndrew Thompson 
16013671d9d8SAndrew Thompson 		default:
16023671d9d8SAndrew Thompson 			error = EINVAL;
16033671d9d8SAndrew Thompson 			break;
16043671d9d8SAndrew Thompson 		}
16053671d9d8SAndrew Thompson 		break;
16063671d9d8SAndrew Thompson 
16073671d9d8SAndrew Thompson 	default:
16083671d9d8SAndrew Thompson 		error = EINVAL;
16093671d9d8SAndrew Thompson 		break;
16103671d9d8SAndrew Thompson 	}
16113671d9d8SAndrew Thompson done:
16123671d9d8SAndrew Thompson 	NG_RESPOND_MSG(error, node, item, rsp);
16133671d9d8SAndrew Thompson 	NG_FREE_MSG(msg);
16143671d9d8SAndrew Thompson 
16153671d9d8SAndrew Thompson 	return (error);
16163671d9d8SAndrew Thompson } /* ng_ubt_rcvmsg */
16173671d9d8SAndrew Thompson 
16183671d9d8SAndrew Thompson /*
16193671d9d8SAndrew Thompson  * Process data.
16203671d9d8SAndrew Thompson  * Netgraph context.
16213671d9d8SAndrew Thompson  */
16223671d9d8SAndrew Thompson 
16233671d9d8SAndrew Thompson static int
16243671d9d8SAndrew Thompson ng_ubt_rcvdata(hook_p hook, item_p item)
16253671d9d8SAndrew Thompson {
16263671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
16273671d9d8SAndrew Thompson 	struct mbuf		*m;
16283671d9d8SAndrew Thompson 	struct ng_bt_mbufq	*q;
16293671d9d8SAndrew Thompson 	int			action, error = 0;
16303671d9d8SAndrew Thompson 
16313671d9d8SAndrew Thompson 	if (hook != sc->sc_hook) {
16323671d9d8SAndrew Thompson 		error = EINVAL;
16333671d9d8SAndrew Thompson 		goto done;
16343671d9d8SAndrew Thompson 	}
16353671d9d8SAndrew Thompson 
16363671d9d8SAndrew Thompson 	/* Deatch mbuf and get HCI frame type */
16373671d9d8SAndrew Thompson 	NGI_GET_M(item, m);
16383671d9d8SAndrew Thompson 
16393671d9d8SAndrew Thompson 	/*
16403671d9d8SAndrew Thompson 	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
16413671d9d8SAndrew Thompson 	 * 2 bytes connection handle and at least 1 byte of length.
16423671d9d8SAndrew Thompson 	 * Panic on data frame that has size smaller than 4 bytes (it
16433671d9d8SAndrew Thompson 	 * should not happen)
16443671d9d8SAndrew Thompson 	 */
16453671d9d8SAndrew Thompson 
16463671d9d8SAndrew Thompson 	if (m->m_pkthdr.len < 4)
16473671d9d8SAndrew Thompson 		panic("HCI frame size is too small! pktlen=%d\n",
16483671d9d8SAndrew Thompson 			m->m_pkthdr.len);
16493671d9d8SAndrew Thompson 
16503671d9d8SAndrew Thompson 	/* Process HCI frame */
16513671d9d8SAndrew Thompson 	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
16523671d9d8SAndrew Thompson 	case NG_HCI_CMD_PKT:
16533671d9d8SAndrew Thompson 		if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE)
16543671d9d8SAndrew Thompson 			panic("HCI command frame size is too big! " \
16553671d9d8SAndrew Thompson 				"buffer size=%zd, packet len=%d\n",
16563671d9d8SAndrew Thompson 				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
16573671d9d8SAndrew Thompson 
16583671d9d8SAndrew Thompson 		q = &sc->sc_cmdq;
16593671d9d8SAndrew Thompson 		action = UBT_FLAG_T_START_CTRL;
16603671d9d8SAndrew Thompson 		break;
16613671d9d8SAndrew Thompson 
16623671d9d8SAndrew Thompson 	case NG_HCI_ACL_DATA_PKT:
16633671d9d8SAndrew Thompson 		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
16643671d9d8SAndrew Thompson 			panic("ACL data frame size is too big! " \
16653671d9d8SAndrew Thompson 				"buffer size=%d, packet len=%d\n",
16663671d9d8SAndrew Thompson 				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
16673671d9d8SAndrew Thompson 
16683671d9d8SAndrew Thompson 		q = &sc->sc_aclq;
16693671d9d8SAndrew Thompson 		action = UBT_FLAG_T_START_BULK;
16703671d9d8SAndrew Thompson 		break;
16713671d9d8SAndrew Thompson 
16723671d9d8SAndrew Thompson 	case NG_HCI_SCO_DATA_PKT:
16733671d9d8SAndrew Thompson 		q = &sc->sc_scoq;
16743671d9d8SAndrew Thompson 		action = 0;
16753671d9d8SAndrew Thompson 		break;
16763671d9d8SAndrew Thompson 
16773671d9d8SAndrew Thompson 	default:
16783671d9d8SAndrew Thompson 		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
16793671d9d8SAndrew Thompson 			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
16803671d9d8SAndrew Thompson 
16813671d9d8SAndrew Thompson 		NG_FREE_M(m);
16823671d9d8SAndrew Thompson 		error = EINVAL;
16833671d9d8SAndrew Thompson 		goto done;
16843671d9d8SAndrew Thompson 		/* NOT REACHED */
16853671d9d8SAndrew Thompson 	}
16863671d9d8SAndrew Thompson 
16873671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
16883671d9d8SAndrew Thompson 	if (NG_BT_MBUFQ_FULL(q)) {
16893671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DROP(q);
16903671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
16913671d9d8SAndrew Thompson 
16923671d9d8SAndrew Thompson 		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
16933671d9d8SAndrew Thompson 			*mtod(m, uint8_t *), m->m_pkthdr.len);
16943671d9d8SAndrew Thompson 
16953671d9d8SAndrew Thompson 		NG_FREE_M(m);
16963671d9d8SAndrew Thompson 	} else {
16973671d9d8SAndrew Thompson 		/* Loose HCI packet type, enqueue mbuf and kick off task */
16983671d9d8SAndrew Thompson 		m_adj(m, sizeof(uint8_t));
16993671d9d8SAndrew Thompson 		NG_BT_MBUFQ_ENQUEUE(q, m);
17003671d9d8SAndrew Thompson 		ubt_task_schedule(sc, action);
17013671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
17023671d9d8SAndrew Thompson 	}
17033671d9d8SAndrew Thompson done:
17043671d9d8SAndrew Thompson 	NG_FREE_ITEM(item);
17053671d9d8SAndrew Thompson 
17063671d9d8SAndrew Thompson 	return (error);
17073671d9d8SAndrew Thompson } /* ng_ubt_rcvdata */
17083671d9d8SAndrew Thompson 
17093671d9d8SAndrew Thompson /****************************************************************************
17103671d9d8SAndrew Thompson  ****************************************************************************
17113671d9d8SAndrew Thompson  **                              Module
17123671d9d8SAndrew Thompson  ****************************************************************************
17133671d9d8SAndrew Thompson  ****************************************************************************/
17143671d9d8SAndrew Thompson 
17153671d9d8SAndrew Thompson /*
17163671d9d8SAndrew Thompson  * Load/Unload the driver module
17173671d9d8SAndrew Thompson  */
17183671d9d8SAndrew Thompson 
17193671d9d8SAndrew Thompson static int
17203671d9d8SAndrew Thompson ubt_modevent(module_t mod, int event, void *data)
17213671d9d8SAndrew Thompson {
17223671d9d8SAndrew Thompson 	int	error;
17233671d9d8SAndrew Thompson 
17243671d9d8SAndrew Thompson 	switch (event) {
17253671d9d8SAndrew Thompson 	case MOD_LOAD:
17263671d9d8SAndrew Thompson 		error = ng_newtype(&typestruct);
17273671d9d8SAndrew Thompson 		if (error != 0)
17283671d9d8SAndrew Thompson 			printf("%s: Could not register Netgraph node type, " \
17293671d9d8SAndrew Thompson 				"error=%d\n", NG_UBT_NODE_TYPE, error);
17303671d9d8SAndrew Thompson 		break;
17313671d9d8SAndrew Thompson 
17323671d9d8SAndrew Thompson 	case MOD_UNLOAD:
17333671d9d8SAndrew Thompson 		error = ng_rmtype(&typestruct);
17343671d9d8SAndrew Thompson 		break;
17353671d9d8SAndrew Thompson 
17363671d9d8SAndrew Thompson 	default:
17373671d9d8SAndrew Thompson 		error = EOPNOTSUPP;
17383671d9d8SAndrew Thompson 		break;
17393671d9d8SAndrew Thompson 	}
17403671d9d8SAndrew Thompson 
17413671d9d8SAndrew Thompson 	return (error);
17423671d9d8SAndrew Thompson } /* ubt_modevent */
17433671d9d8SAndrew Thompson 
17443671d9d8SAndrew Thompson static devclass_t	ubt_devclass;
17453671d9d8SAndrew Thompson 
17463671d9d8SAndrew Thompson static device_method_t	ubt_methods[] =
17473671d9d8SAndrew Thompson {
17483671d9d8SAndrew Thompson 	DEVMETHOD(device_probe,	ubt_probe),
17493671d9d8SAndrew Thompson 	DEVMETHOD(device_attach, ubt_attach),
17503671d9d8SAndrew Thompson 	DEVMETHOD(device_detach, ubt_detach),
17513671d9d8SAndrew Thompson 	{ 0, 0 }
17523671d9d8SAndrew Thompson };
17533671d9d8SAndrew Thompson 
17543671d9d8SAndrew Thompson static driver_t		ubt_driver =
17553671d9d8SAndrew Thompson {
17563671d9d8SAndrew Thompson 	.name =	   "ubt",
17573671d9d8SAndrew Thompson 	.methods = ubt_methods,
17583671d9d8SAndrew Thompson 	.size =	   sizeof(struct ubt_softc),
17593671d9d8SAndrew Thompson };
17603671d9d8SAndrew Thompson 
17613671d9d8SAndrew Thompson DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
17623671d9d8SAndrew Thompson MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
17633671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
17643671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
17653671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
17663671d9d8SAndrew Thompson 
1767