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/module.h>
104ed6d949aSAndrew Thompson #include <sys/lock.h>
105ed6d949aSAndrew Thompson #include <sys/mutex.h>
106ed6d949aSAndrew Thompson #include <sys/condvar.h>
107ed6d949aSAndrew Thompson #include <sys/sysctl.h>
108ed6d949aSAndrew Thompson #include <sys/sx.h>
109ed6d949aSAndrew Thompson #include <sys/unistd.h>
110ed6d949aSAndrew Thompson #include <sys/callout.h>
111ed6d949aSAndrew Thompson #include <sys/malloc.h>
112ed6d949aSAndrew Thompson #include <sys/priv.h>
113ed6d949aSAndrew Thompson 
1143671d9d8SAndrew Thompson #include "usbdevs.h"
1153671d9d8SAndrew Thompson #include <dev/usb/usb.h>
116ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
117ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
1183671d9d8SAndrew Thompson 
119a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_debug
1203671d9d8SAndrew Thompson #include <dev/usb/usb_debug.h>
1213671d9d8SAndrew Thompson #include <dev/usb/usb_busdma.h>
1223671d9d8SAndrew Thompson 
1233671d9d8SAndrew Thompson #include <sys/mbuf.h>
1243671d9d8SAndrew Thompson #include <sys/taskqueue.h>
1253671d9d8SAndrew Thompson 
1263671d9d8SAndrew Thompson #include <netgraph/ng_message.h>
1273671d9d8SAndrew Thompson #include <netgraph/netgraph.h>
1283671d9d8SAndrew Thompson #include <netgraph/ng_parse.h>
1293671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h>
1303671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_hci.h>
1313671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_ubt.h>
13206079f14SAndrew Thompson #include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>
1333671d9d8SAndrew Thompson 
1343671d9d8SAndrew Thompson static int		ubt_modevent(module_t, int, void *);
1353671d9d8SAndrew Thompson static device_probe_t	ubt_probe;
1363671d9d8SAndrew Thompson static device_attach_t	ubt_attach;
1373671d9d8SAndrew Thompson static device_detach_t	ubt_detach;
1383671d9d8SAndrew Thompson 
1393671d9d8SAndrew Thompson static void		ubt_task_schedule(ubt_softc_p, int);
1403671d9d8SAndrew Thompson static task_fn_t	ubt_task;
1413671d9d8SAndrew Thompson 
142a593f6b8SAndrew Thompson #define	ubt_xfer_start(sc, i)	usbd_transfer_start((sc)->sc_xfer[(i)])
1433671d9d8SAndrew Thompson 
1443671d9d8SAndrew Thompson /* Netgraph methods */
1453671d9d8SAndrew Thompson static ng_constructor_t	ng_ubt_constructor;
1463671d9d8SAndrew Thompson static ng_shutdown_t	ng_ubt_shutdown;
1473671d9d8SAndrew Thompson static ng_newhook_t	ng_ubt_newhook;
1483671d9d8SAndrew Thompson static ng_connect_t	ng_ubt_connect;
1493671d9d8SAndrew Thompson static ng_disconnect_t	ng_ubt_disconnect;
1503671d9d8SAndrew Thompson static ng_rcvmsg_t	ng_ubt_rcvmsg;
1513671d9d8SAndrew Thompson static ng_rcvdata_t	ng_ubt_rcvdata;
1523671d9d8SAndrew Thompson 
1533671d9d8SAndrew Thompson /* Queue length */
1543671d9d8SAndrew Thompson static const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
1553671d9d8SAndrew Thompson {
1563671d9d8SAndrew Thompson 	{ "queue", &ng_parse_int32_type, },
1573671d9d8SAndrew Thompson 	{ "qlen",  &ng_parse_int32_type, },
1583671d9d8SAndrew Thompson 	{ NULL, }
1593671d9d8SAndrew Thompson };
1603671d9d8SAndrew Thompson static const struct ng_parse_type		ng_ubt_node_qlen_type =
1613671d9d8SAndrew Thompson {
1623671d9d8SAndrew Thompson 	&ng_parse_struct_type,
1633671d9d8SAndrew Thompson 	&ng_ubt_node_qlen_type_fields
1643671d9d8SAndrew Thompson };
1653671d9d8SAndrew Thompson 
1663671d9d8SAndrew Thompson /* Stat info */
1673671d9d8SAndrew Thompson static const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
1683671d9d8SAndrew Thompson {
1693671d9d8SAndrew Thompson 	{ "pckts_recv", &ng_parse_uint32_type, },
1703671d9d8SAndrew Thompson 	{ "bytes_recv", &ng_parse_uint32_type, },
1713671d9d8SAndrew Thompson 	{ "pckts_sent", &ng_parse_uint32_type, },
1723671d9d8SAndrew Thompson 	{ "bytes_sent", &ng_parse_uint32_type, },
1733671d9d8SAndrew Thompson 	{ "oerrors",    &ng_parse_uint32_type, },
1743671d9d8SAndrew Thompson 	{ "ierrors",    &ng_parse_uint32_type, },
1753671d9d8SAndrew Thompson 	{ NULL, }
1763671d9d8SAndrew Thompson };
1773671d9d8SAndrew Thompson static const struct ng_parse_type		ng_ubt_node_stat_type =
1783671d9d8SAndrew Thompson {
1793671d9d8SAndrew Thompson 	&ng_parse_struct_type,
1803671d9d8SAndrew Thompson 	&ng_ubt_node_stat_type_fields
1813671d9d8SAndrew Thompson };
1823671d9d8SAndrew Thompson 
1833671d9d8SAndrew Thompson /* Netgraph node command list */
1843671d9d8SAndrew Thompson static const struct ng_cmdlist			ng_ubt_cmdlist[] =
1853671d9d8SAndrew Thompson {
1863671d9d8SAndrew Thompson 	{
1873671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1883671d9d8SAndrew Thompson 		NGM_UBT_NODE_SET_DEBUG,
1893671d9d8SAndrew Thompson 		"set_debug",
1903671d9d8SAndrew Thompson 		&ng_parse_uint16_type,
1913671d9d8SAndrew Thompson 		NULL
1923671d9d8SAndrew Thompson 	},
1933671d9d8SAndrew Thompson 	{
1943671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1953671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_DEBUG,
1963671d9d8SAndrew Thompson 		"get_debug",
1973671d9d8SAndrew Thompson 		NULL,
1983671d9d8SAndrew Thompson 		&ng_parse_uint16_type
1993671d9d8SAndrew Thompson 	},
2003671d9d8SAndrew Thompson 	{
2013671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2023671d9d8SAndrew Thompson 		NGM_UBT_NODE_SET_QLEN,
2033671d9d8SAndrew Thompson 		"set_qlen",
2043671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type,
2053671d9d8SAndrew Thompson 		NULL
2063671d9d8SAndrew Thompson 	},
2073671d9d8SAndrew Thompson 	{
2083671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2093671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_QLEN,
2103671d9d8SAndrew Thompson 		"get_qlen",
2113671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type,
2123671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type
2133671d9d8SAndrew Thompson 	},
2143671d9d8SAndrew Thompson 	{
2153671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2163671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_STAT,
2173671d9d8SAndrew Thompson 		"get_stat",
2183671d9d8SAndrew Thompson 		NULL,
2193671d9d8SAndrew Thompson 		&ng_ubt_node_stat_type
2203671d9d8SAndrew Thompson 	},
2213671d9d8SAndrew Thompson 	{
2223671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2233671d9d8SAndrew Thompson 		NGM_UBT_NODE_RESET_STAT,
2243671d9d8SAndrew Thompson 		"reset_stat",
2253671d9d8SAndrew Thompson 		NULL,
2263671d9d8SAndrew Thompson 		NULL
2273671d9d8SAndrew Thompson 	},
2283671d9d8SAndrew Thompson 	{ 0, }
2293671d9d8SAndrew Thompson };
2303671d9d8SAndrew Thompson 
2313671d9d8SAndrew Thompson /* Netgraph node type */
2323671d9d8SAndrew Thompson static struct ng_type	typestruct =
2333671d9d8SAndrew Thompson {
2343671d9d8SAndrew Thompson 	.version = 	NG_ABI_VERSION,
2353671d9d8SAndrew Thompson 	.name =		NG_UBT_NODE_TYPE,
2363671d9d8SAndrew Thompson 	.constructor =	ng_ubt_constructor,
2373671d9d8SAndrew Thompson 	.rcvmsg =	ng_ubt_rcvmsg,
2383671d9d8SAndrew Thompson 	.shutdown =	ng_ubt_shutdown,
2393671d9d8SAndrew Thompson 	.newhook =	ng_ubt_newhook,
2403671d9d8SAndrew Thompson 	.connect =	ng_ubt_connect,
2413671d9d8SAndrew Thompson 	.rcvdata =	ng_ubt_rcvdata,
2423671d9d8SAndrew Thompson 	.disconnect =	ng_ubt_disconnect,
2433671d9d8SAndrew Thompson 	.cmdlist =	ng_ubt_cmdlist
2443671d9d8SAndrew Thompson };
2453671d9d8SAndrew Thompson 
2463671d9d8SAndrew Thompson /****************************************************************************
2473671d9d8SAndrew Thompson  ****************************************************************************
2483671d9d8SAndrew Thompson  **                              USB specific
2493671d9d8SAndrew Thompson  ****************************************************************************
2503671d9d8SAndrew Thompson  ****************************************************************************/
2513671d9d8SAndrew Thompson 
2523671d9d8SAndrew Thompson /* USB methods */
253e0a69b51SAndrew Thompson static usb_callback_t	ubt_ctrl_write_callback;
254e0a69b51SAndrew Thompson static usb_callback_t	ubt_intr_read_callback;
255e0a69b51SAndrew Thompson static usb_callback_t	ubt_bulk_read_callback;
256e0a69b51SAndrew Thompson static usb_callback_t	ubt_bulk_write_callback;
257e0a69b51SAndrew Thompson static usb_callback_t	ubt_isoc_read_callback;
258e0a69b51SAndrew Thompson static usb_callback_t	ubt_isoc_write_callback;
2593671d9d8SAndrew Thompson 
2603671d9d8SAndrew Thompson static int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
261760bc48eSAndrew Thompson static int		ubt_isoc_read_one_frame(struct usb_xfer *, int);
2623671d9d8SAndrew Thompson 
2633671d9d8SAndrew Thompson /*
2643671d9d8SAndrew Thompson  * USB config
2653671d9d8SAndrew Thompson  *
2663671d9d8SAndrew Thompson  * The following desribes usb transfers that could be submitted on USB device.
2673671d9d8SAndrew Thompson  *
2683671d9d8SAndrew Thompson  * Interface 0 on the USB device must present the following endpoints
2693671d9d8SAndrew Thompson  *	1) Interrupt endpoint to receive HCI events
2703671d9d8SAndrew Thompson  *	2) Bulk IN endpoint to receive ACL data
2713671d9d8SAndrew Thompson  *	3) Bulk OUT endpoint to send ACL data
2723671d9d8SAndrew Thompson  *
2733671d9d8SAndrew Thompson  * Interface 1 on the USB device must present the following endpoints
2743671d9d8SAndrew Thompson  *	1) Isochronous IN endpoint to receive SCO data
2753671d9d8SAndrew Thompson  *	2) Isochronous OUT endpoint to send SCO data
2763671d9d8SAndrew Thompson  */
2773671d9d8SAndrew Thompson 
278760bc48eSAndrew Thompson static const struct usb_config		ubt_config[UBT_N_TRANSFER] =
2793671d9d8SAndrew Thompson {
2803671d9d8SAndrew Thompson 	/*
2813671d9d8SAndrew Thompson 	 * Interface #0
2823671d9d8SAndrew Thompson  	 */
2833671d9d8SAndrew Thompson 
2843671d9d8SAndrew Thompson 	/* Outgoing bulk transfer - ACL packets */
2853671d9d8SAndrew Thompson 	[UBT_IF_0_BULK_DT_WR] = {
2863671d9d8SAndrew Thompson 		.type =		UE_BULK,
2873671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
2883671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
2893671d9d8SAndrew Thompson 		.if_index = 	0,
2903671d9d8SAndrew Thompson 		.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
2913671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
2923671d9d8SAndrew Thompson 		.callback =	&ubt_bulk_write_callback,
2933671d9d8SAndrew Thompson 	},
2943671d9d8SAndrew Thompson 	/* Incoming bulk transfer - ACL packets */
2953671d9d8SAndrew Thompson 	[UBT_IF_0_BULK_DT_RD] = {
2963671d9d8SAndrew Thompson 		.type =		UE_BULK,
2973671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
2983671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
2993671d9d8SAndrew Thompson 		.if_index = 	0,
3003671d9d8SAndrew Thompson 		.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
3013671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
3023671d9d8SAndrew Thompson 		.callback =	&ubt_bulk_read_callback,
3033671d9d8SAndrew Thompson 	},
3043671d9d8SAndrew Thompson 	/* Incoming interrupt transfer - HCI events */
3053671d9d8SAndrew Thompson 	[UBT_IF_0_INTR_DT_RD] = {
3063671d9d8SAndrew Thompson 		.type =		UE_INTERRUPT,
3073671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3083671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3093671d9d8SAndrew Thompson 		.if_index = 	0,
3103671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
3113671d9d8SAndrew Thompson 		.bufsize =	UBT_INTR_BUFFER_SIZE,
3123671d9d8SAndrew Thompson 		.callback =	&ubt_intr_read_callback,
3133671d9d8SAndrew Thompson 	},
3143671d9d8SAndrew Thompson 	/* Outgoing control transfer - HCI commands */
3153671d9d8SAndrew Thompson 	[UBT_IF_0_CTRL_DT_WR] = {
3163671d9d8SAndrew Thompson 		.type =		UE_CONTROL,
3173671d9d8SAndrew Thompson 		.endpoint =	0x00,	/* control pipe */
3183671d9d8SAndrew Thompson 		.direction =	UE_DIR_ANY,
3193671d9d8SAndrew Thompson 		.if_index = 	0,
3203671d9d8SAndrew Thompson 		.bufsize =	UBT_CTRL_BUFFER_SIZE,
3213671d9d8SAndrew Thompson 		.callback =	&ubt_ctrl_write_callback,
3223671d9d8SAndrew Thompson 		.timeout =	5000,	/* 5 seconds */
3233671d9d8SAndrew Thompson 	},
3243671d9d8SAndrew Thompson 
3253671d9d8SAndrew Thompson 	/*
3263671d9d8SAndrew Thompson 	 * Interface #1
3273671d9d8SAndrew Thompson  	 */
3283671d9d8SAndrew Thompson 
3293671d9d8SAndrew Thompson 	/* Incoming isochronous transfer #1 - SCO packets */
3303671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_RD1] = {
3313671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3323671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3333671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3343671d9d8SAndrew Thompson 		.if_index = 	1,
3353671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3363671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3373671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3383671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_read_callback,
3393671d9d8SAndrew Thompson 	},
3403671d9d8SAndrew Thompson 	/* Incoming isochronous transfer #2 - SCO packets */
3413671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_RD2] = {
3423671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3433671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3443671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3453671d9d8SAndrew Thompson 		.if_index = 	1,
3463671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3473671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3483671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3493671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_read_callback,
3503671d9d8SAndrew Thompson 	},
3513671d9d8SAndrew Thompson 	/* Outgoing isochronous transfer #1 - SCO packets */
3523671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_WR1] = {
3533671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3543671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3553671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
3563671d9d8SAndrew Thompson 		.if_index = 	1,
3573671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3583671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3593671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3603671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_write_callback,
3613671d9d8SAndrew Thompson 	},
3623671d9d8SAndrew Thompson 	/* Outgoing isochronous transfer #2 - SCO packets */
3633671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_WR2] = {
3643671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3653671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3663671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
3673671d9d8SAndrew Thompson 		.if_index = 	1,
3683671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3693671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3703671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3713671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_write_callback,
3723671d9d8SAndrew Thompson 	},
3733671d9d8SAndrew Thompson };
3743671d9d8SAndrew Thompson 
3753671d9d8SAndrew Thompson /*
3763671d9d8SAndrew Thompson  * If for some reason device should not be attached then put
3773671d9d8SAndrew Thompson  * VendorID/ProductID pair into the list below. The format is
3783671d9d8SAndrew Thompson  * as follows:
3793671d9d8SAndrew Thompson  *
3803671d9d8SAndrew Thompson  *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
3813671d9d8SAndrew Thompson  *
3823671d9d8SAndrew Thompson  * where VENDOR_ID and PRODUCT_ID are hex numbers.
3833671d9d8SAndrew Thompson  */
3843671d9d8SAndrew Thompson 
385f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_ignore_devs[] =
3863671d9d8SAndrew Thompson {
3873671d9d8SAndrew Thompson 	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
3883671d9d8SAndrew Thompson 	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
3893671d9d8SAndrew Thompson };
3903671d9d8SAndrew Thompson 
3913671d9d8SAndrew Thompson /* List of supported bluetooth devices */
392f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_devs[] =
3933671d9d8SAndrew Thompson {
3943671d9d8SAndrew Thompson 	/* Generic Bluetooth class devices */
3953671d9d8SAndrew Thompson 	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
3963671d9d8SAndrew Thompson 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
3973671d9d8SAndrew Thompson 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
3983671d9d8SAndrew Thompson 
3993671d9d8SAndrew Thompson 	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
4003671d9d8SAndrew Thompson 	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
40170a0e340SGleb Smirnoff 
40270a0e340SGleb Smirnoff 	/* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
40370a0e340SGleb Smirnoff 	{ USB_VENDOR(USB_VENDOR_BROADCOM),
40470a0e340SGleb Smirnoff 	  USB_IF_CSI(UICLASS_VENDOR, 0x01, 0x01) },
4053671d9d8SAndrew Thompson };
4063671d9d8SAndrew Thompson 
4073671d9d8SAndrew Thompson /*
4083671d9d8SAndrew Thompson  * Probe for a USB Bluetooth device.
4093671d9d8SAndrew Thompson  * USB context.
4103671d9d8SAndrew Thompson  */
4113671d9d8SAndrew Thompson 
4123671d9d8SAndrew Thompson static int
4133671d9d8SAndrew Thompson ubt_probe(device_t dev)
4143671d9d8SAndrew Thompson {
415760bc48eSAndrew Thompson 	struct usb_attach_arg	*uaa = device_get_ivars(dev);
416a5db8fd1SAndriy Gapon 	int error;
4173671d9d8SAndrew Thompson 
4183671d9d8SAndrew Thompson 	if (uaa->usb_mode != USB_MODE_HOST)
4193671d9d8SAndrew Thompson 		return (ENXIO);
4203671d9d8SAndrew Thompson 
4213671d9d8SAndrew Thompson 	if (uaa->info.bIfaceIndex != 0)
4223671d9d8SAndrew Thompson 		return (ENXIO);
4233671d9d8SAndrew Thompson 
424a593f6b8SAndrew Thompson 	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
4253671d9d8SAndrew Thompson 			sizeof(ubt_ignore_devs), uaa) == 0)
4263671d9d8SAndrew Thompson 		return (ENXIO);
4273671d9d8SAndrew Thompson 
428a5db8fd1SAndriy Gapon 	error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
429a5db8fd1SAndriy Gapon 	if (error == 0)
430a5db8fd1SAndriy Gapon 		return (BUS_PROBE_GENERIC);
431a5db8fd1SAndriy Gapon 	return (error);
4323671d9d8SAndrew Thompson } /* ubt_probe */
4333671d9d8SAndrew Thompson 
4343671d9d8SAndrew Thompson /*
4353671d9d8SAndrew Thompson  * Attach the device.
4363671d9d8SAndrew Thompson  * USB context.
4373671d9d8SAndrew Thompson  */
4383671d9d8SAndrew Thompson 
4393671d9d8SAndrew Thompson static int
4403671d9d8SAndrew Thompson ubt_attach(device_t dev)
4413671d9d8SAndrew Thompson {
442760bc48eSAndrew Thompson 	struct usb_attach_arg		*uaa = device_get_ivars(dev);
4433671d9d8SAndrew Thompson 	struct ubt_softc		*sc = device_get_softc(dev);
444760bc48eSAndrew Thompson 	struct usb_endpoint_descriptor	*ed;
445760bc48eSAndrew Thompson 	struct usb_interface_descriptor *id;
44612b16d85SHans Petter Selasky 	struct usb_interface		*iface;
4473671d9d8SAndrew Thompson 	uint16_t			wMaxPacketSize;
4483671d9d8SAndrew Thompson 	uint8_t				alt_index, i, j;
4493671d9d8SAndrew Thompson 	uint8_t				iface_index[2] = { 0, 1 };
4503671d9d8SAndrew Thompson 
451a593f6b8SAndrew Thompson 	device_set_usb_desc(dev);
4523671d9d8SAndrew Thompson 
4533671d9d8SAndrew Thompson 	sc->sc_dev = dev;
4543671d9d8SAndrew Thompson 	sc->sc_debug = NG_UBT_WARN_LEVEL;
4553671d9d8SAndrew Thompson 
4563671d9d8SAndrew Thompson 	/*
4573671d9d8SAndrew Thompson 	 * Create Netgraph node
4583671d9d8SAndrew Thompson 	 */
4593671d9d8SAndrew Thompson 
4603671d9d8SAndrew Thompson 	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
4613671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not create Netgraph node\n");
4623671d9d8SAndrew Thompson 		return (ENXIO);
4633671d9d8SAndrew Thompson 	}
4643671d9d8SAndrew Thompson 
4653671d9d8SAndrew Thompson 	/* Name Netgraph node */
4663671d9d8SAndrew Thompson 	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
4673671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not name Netgraph node\n");
4683671d9d8SAndrew Thompson 		NG_NODE_UNREF(sc->sc_node);
4693671d9d8SAndrew Thompson 		return (ENXIO);
4703671d9d8SAndrew Thompson 	}
4713671d9d8SAndrew Thompson 	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
4723671d9d8SAndrew Thompson 	NG_NODE_FORCE_WRITER(sc->sc_node);
4733671d9d8SAndrew Thompson 
4743671d9d8SAndrew Thompson 	/*
4753671d9d8SAndrew Thompson 	 * Initialize device softc structure
4763671d9d8SAndrew Thompson 	 */
4773671d9d8SAndrew Thompson 
4783671d9d8SAndrew Thompson 	/* initialize locks */
4793671d9d8SAndrew Thompson 	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
4803671d9d8SAndrew Thompson 	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
4813671d9d8SAndrew Thompson 
4823671d9d8SAndrew Thompson 	/* initialize packet queues */
4833671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
4843671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
4853671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
4863671d9d8SAndrew Thompson 
4873671d9d8SAndrew Thompson 	/* initialize glue task */
4883671d9d8SAndrew Thompson 	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
4893671d9d8SAndrew Thompson 
4903671d9d8SAndrew Thompson 	/*
4913671d9d8SAndrew Thompson 	 * Configure Bluetooth USB device. Discover all required USB
4923671d9d8SAndrew Thompson 	 * interfaces and endpoints.
4933671d9d8SAndrew Thompson 	 *
4943671d9d8SAndrew Thompson 	 * USB device must present two interfaces:
4953671d9d8SAndrew Thompson 	 * 1) Interface 0 that has 3 endpoints
4963671d9d8SAndrew Thompson 	 *	1) Interrupt endpoint to receive HCI events
4973671d9d8SAndrew Thompson 	 *	2) Bulk IN endpoint to receive ACL data
4983671d9d8SAndrew Thompson 	 *	3) Bulk OUT endpoint to send ACL data
4993671d9d8SAndrew Thompson 	 *
5003671d9d8SAndrew Thompson 	 * 2) Interface 1 then has 2 endpoints
5013671d9d8SAndrew Thompson 	 *	1) Isochronous IN endpoint to receive SCO data
5023671d9d8SAndrew Thompson  	 *	2) Isochronous OUT endpoint to send SCO data
5033671d9d8SAndrew Thompson 	 *
5043671d9d8SAndrew Thompson 	 * Interface 1 (with isochronous endpoints) has several alternate
5053671d9d8SAndrew Thompson 	 * configurations with different packet size.
5063671d9d8SAndrew Thompson 	 */
5073671d9d8SAndrew Thompson 
5083671d9d8SAndrew Thompson 	/*
5093671d9d8SAndrew Thompson 	 * For interface #1 search alternate settings, and find
5103671d9d8SAndrew Thompson 	 * the descriptor with the largest wMaxPacketSize
5113671d9d8SAndrew Thompson 	 */
5123671d9d8SAndrew Thompson 
5133671d9d8SAndrew Thompson 	wMaxPacketSize = 0;
5143671d9d8SAndrew Thompson 	alt_index = 0;
5153671d9d8SAndrew Thompson 	i = 0;
5163671d9d8SAndrew Thompson 	j = 0;
5173671d9d8SAndrew Thompson 	ed = NULL;
5183671d9d8SAndrew Thompson 
5193671d9d8SAndrew Thompson 	/*
5203671d9d8SAndrew Thompson 	 * Search through all the descriptors looking for the largest
5213671d9d8SAndrew Thompson 	 * packet size:
5223671d9d8SAndrew Thompson 	 */
523a593f6b8SAndrew Thompson 	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
524a593f6b8SAndrew Thompson 	    usbd_get_config_descriptor(uaa->device),
525760bc48eSAndrew Thompson 	    (struct usb_descriptor *)ed))) {
5263671d9d8SAndrew Thompson 
5273671d9d8SAndrew Thompson 		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
5283671d9d8SAndrew Thompson 		    (ed->bLength >= sizeof(*id))) {
529760bc48eSAndrew Thompson 			id = (struct usb_interface_descriptor *)ed;
5303671d9d8SAndrew Thompson 			i = id->bInterfaceNumber;
5313671d9d8SAndrew Thompson 			j = id->bAlternateSetting;
5323671d9d8SAndrew Thompson 		}
5333671d9d8SAndrew Thompson 
5343671d9d8SAndrew Thompson 		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
5353671d9d8SAndrew Thompson 		    (ed->bLength >= sizeof(*ed)) &&
5363671d9d8SAndrew Thompson 		    (i == 1)) {
5373671d9d8SAndrew Thompson 			uint16_t temp;
5383671d9d8SAndrew Thompson 
5393671d9d8SAndrew Thompson 			temp = UGETW(ed->wMaxPacketSize);
5403671d9d8SAndrew Thompson 			if (temp > wMaxPacketSize) {
5413671d9d8SAndrew Thompson 				wMaxPacketSize = temp;
5423671d9d8SAndrew Thompson 				alt_index = j;
5433671d9d8SAndrew Thompson 			}
5443671d9d8SAndrew Thompson 		}
5453671d9d8SAndrew Thompson 	}
5463671d9d8SAndrew Thompson 
5473671d9d8SAndrew Thompson 	/* Set alt configuration on interface #1 only if we found it */
5483671d9d8SAndrew Thompson 	if (wMaxPacketSize > 0 &&
549a593f6b8SAndrew Thompson 	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
5503671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not set alternate setting %d " \
5513671d9d8SAndrew Thompson 			"for interface 1!\n", alt_index);
5523671d9d8SAndrew Thompson 		goto detach;
5533671d9d8SAndrew Thompson 	}
5543671d9d8SAndrew Thompson 
5553671d9d8SAndrew Thompson 	/* Setup transfers for both interfaces */
556a593f6b8SAndrew Thompson 	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
5573671d9d8SAndrew Thompson 			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
5583671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not allocate transfers\n");
5593671d9d8SAndrew Thompson 		goto detach;
5603671d9d8SAndrew Thompson 	}
5613671d9d8SAndrew Thompson 
56212b16d85SHans Petter Selasky 	/* Claim all interfaces belonging to the Bluetooth part */
56312b16d85SHans Petter Selasky 	for (i = 1;; i++) {
56412b16d85SHans Petter Selasky 		iface = usbd_get_iface(uaa->device, i);
56512b16d85SHans Petter Selasky 		if (iface == NULL)
56612b16d85SHans Petter Selasky 			break;
56712b16d85SHans Petter Selasky 		id = usbd_get_interface_descriptor(iface);
5683671d9d8SAndrew Thompson 
56912b16d85SHans Petter Selasky 		if ((id != NULL) &&
57012b16d85SHans Petter Selasky 		    (id->bInterfaceClass == UICLASS_WIRELESS) &&
57112b16d85SHans Petter Selasky 		    (id->bInterfaceSubClass == UISUBCLASS_RF) &&
57212b16d85SHans Petter Selasky 		    (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) {
57312b16d85SHans Petter Selasky 			usbd_set_parent_iface(uaa->device, i,
57412b16d85SHans Petter Selasky 			    uaa->info.bIfaceIndex);
57512b16d85SHans Petter Selasky 		}
57612b16d85SHans Petter Selasky 	}
5773671d9d8SAndrew Thompson 	return (0); /* success */
5783671d9d8SAndrew Thompson 
5793671d9d8SAndrew Thompson detach:
5803671d9d8SAndrew Thompson 	ubt_detach(dev);
5813671d9d8SAndrew Thompson 
5823671d9d8SAndrew Thompson 	return (ENXIO);
5833671d9d8SAndrew Thompson } /* ubt_attach */
5843671d9d8SAndrew Thompson 
5853671d9d8SAndrew Thompson /*
5863671d9d8SAndrew Thompson  * Detach the device.
5873671d9d8SAndrew Thompson  * USB context.
5883671d9d8SAndrew Thompson  */
5893671d9d8SAndrew Thompson 
5903671d9d8SAndrew Thompson int
5913671d9d8SAndrew Thompson ubt_detach(device_t dev)
5923671d9d8SAndrew Thompson {
5933671d9d8SAndrew Thompson 	struct ubt_softc	*sc = device_get_softc(dev);
5943671d9d8SAndrew Thompson 	node_p			node = sc->sc_node;
5953671d9d8SAndrew Thompson 
5963671d9d8SAndrew Thompson 	/* Destroy Netgraph node */
5973671d9d8SAndrew Thompson 	if (node != NULL) {
5983671d9d8SAndrew Thompson 		sc->sc_node = NULL;
5993671d9d8SAndrew Thompson 		NG_NODE_REALLY_DIE(node);
6003671d9d8SAndrew Thompson 		ng_rmnode_self(node);
6013671d9d8SAndrew Thompson 	}
6023671d9d8SAndrew Thompson 
6033671d9d8SAndrew Thompson 	/* Make sure ubt_task in gone */
6043671d9d8SAndrew Thompson 	taskqueue_drain(taskqueue_swi, &sc->sc_task);
6053671d9d8SAndrew Thompson 
6063671d9d8SAndrew Thompson 	/* Free USB transfers, if any */
607a593f6b8SAndrew Thompson 	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
6083671d9d8SAndrew Thompson 
6093671d9d8SAndrew Thompson 	/* Destroy queues */
6103671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
6113671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
6123671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
6133671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
6143671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
6153671d9d8SAndrew Thompson 
6163671d9d8SAndrew Thompson 	mtx_destroy(&sc->sc_if_mtx);
6173671d9d8SAndrew Thompson 	mtx_destroy(&sc->sc_ng_mtx);
6183671d9d8SAndrew Thompson 
6193671d9d8SAndrew Thompson 	return (0);
6203671d9d8SAndrew Thompson } /* ubt_detach */
6213671d9d8SAndrew Thompson 
6223671d9d8SAndrew Thompson /*
6233671d9d8SAndrew Thompson  * Called when outgoing control request (HCI command) has completed, i.e.
6243671d9d8SAndrew Thompson  * HCI command was sent to the device.
6253671d9d8SAndrew Thompson  * USB context.
6263671d9d8SAndrew Thompson  */
6273671d9d8SAndrew Thompson 
6283671d9d8SAndrew Thompson static void
629ed6d949aSAndrew Thompson ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
6303671d9d8SAndrew Thompson {
631ed6d949aSAndrew Thompson 	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
632760bc48eSAndrew Thompson 	struct usb_device_request	req;
6333671d9d8SAndrew Thompson 	struct mbuf			*m;
634ed6d949aSAndrew Thompson 	struct usb_page_cache		*pc;
635ed6d949aSAndrew Thompson 	int				actlen;
636ed6d949aSAndrew Thompson 
637ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
6383671d9d8SAndrew Thompson 
6393671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
6403671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
641ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
642ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
6433671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
6443671d9d8SAndrew Thompson 		/* FALLTHROUGH */
6453671d9d8SAndrew Thompson 
6463671d9d8SAndrew Thompson 	case USB_ST_SETUP:
6473671d9d8SAndrew Thompson send_next:
6483671d9d8SAndrew Thompson 		/* Get next command mbuf, if any */
6493671d9d8SAndrew Thompson 		UBT_NG_LOCK(sc);
6503671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
6513671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
6523671d9d8SAndrew Thompson 
6533671d9d8SAndrew Thompson 		if (m == NULL) {
6543671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI command queue is empty\n");
6553671d9d8SAndrew Thompson 			break;	/* transfer complete */
6563671d9d8SAndrew Thompson 		}
6573671d9d8SAndrew Thompson 
6583671d9d8SAndrew Thompson 		/* Initialize a USB control request and then schedule it */
6593671d9d8SAndrew Thompson 		bzero(&req, sizeof(req));
6603671d9d8SAndrew Thompson 		req.bmRequestType = UBT_HCI_REQUEST;
6613671d9d8SAndrew Thompson 		USETW(req.wLength, m->m_pkthdr.len);
6623671d9d8SAndrew Thompson 
6633671d9d8SAndrew Thompson 		UBT_INFO(sc, "Sending control request, " \
6643671d9d8SAndrew Thompson 			"bmRequestType=0x%02x, wLength=%d\n",
6653671d9d8SAndrew Thompson 			req.bmRequestType, UGETW(req.wLength));
6663671d9d8SAndrew Thompson 
667ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
668ed6d949aSAndrew Thompson 		usbd_copy_in(pc, 0, &req, sizeof(req));
669ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 1);
670ed6d949aSAndrew Thompson 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
6713671d9d8SAndrew Thompson 
672ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
673ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
674ed6d949aSAndrew Thompson 		usbd_xfer_set_frames(xfer, 2);
6753671d9d8SAndrew Thompson 
6763671d9d8SAndrew Thompson 		NG_FREE_M(m);
6773671d9d8SAndrew Thompson 
678a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
6793671d9d8SAndrew Thompson 		break;
6803671d9d8SAndrew Thompson 
6813671d9d8SAndrew Thompson 	default: /* Error */
682ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
6833671d9d8SAndrew Thompson 			UBT_WARN(sc, "control transfer failed: %s\n",
684ed6d949aSAndrew Thompson 				usbd_errstr(error));
6853671d9d8SAndrew Thompson 
6863671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
6873671d9d8SAndrew Thompson 			goto send_next;
6883671d9d8SAndrew Thompson 		}
6893671d9d8SAndrew Thompson 
6903671d9d8SAndrew Thompson 		/* transfer cancelled */
6913671d9d8SAndrew Thompson 		break;
6923671d9d8SAndrew Thompson 	}
6933671d9d8SAndrew Thompson } /* ubt_ctrl_write_callback */
6943671d9d8SAndrew Thompson 
6953671d9d8SAndrew Thompson /*
6963671d9d8SAndrew Thompson  * Called when incoming interrupt transfer (HCI event) has completed, i.e.
6973671d9d8SAndrew Thompson  * HCI event was received from the device.
6983671d9d8SAndrew Thompson  * USB context.
6993671d9d8SAndrew Thompson  */
7003671d9d8SAndrew Thompson 
7013671d9d8SAndrew Thompson static void
702ed6d949aSAndrew Thompson ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
7033671d9d8SAndrew Thompson {
704ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
7053671d9d8SAndrew Thompson 	struct mbuf		*m;
7063671d9d8SAndrew Thompson 	ng_hci_event_pkt_t	*hdr;
707ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
708ed6d949aSAndrew Thompson 	int			actlen;
709ed6d949aSAndrew Thompson 
710ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
7113671d9d8SAndrew Thompson 
7123671d9d8SAndrew Thompson 	m = NULL;
7133671d9d8SAndrew Thompson 
7143671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
7153671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
7163671d9d8SAndrew Thompson 		/* Allocate a new mbuf */
717eb1b1807SGleb Smirnoff 		MGETHDR(m, M_NOWAIT, MT_DATA);
7183671d9d8SAndrew Thompson 		if (m == NULL) {
7193671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7203671d9d8SAndrew Thompson 			goto submit_next;
7213671d9d8SAndrew Thompson 		}
7223671d9d8SAndrew Thompson 
723eb1b1807SGleb Smirnoff 		MCLGET(m, M_NOWAIT);
7243671d9d8SAndrew Thompson 		if (!(m->m_flags & M_EXT)) {
7253671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7263671d9d8SAndrew Thompson 			goto submit_next;
7273671d9d8SAndrew Thompson 		}
7283671d9d8SAndrew Thompson 
7293671d9d8SAndrew Thompson 		/* Add HCI packet type */
7303671d9d8SAndrew Thompson 		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
7313671d9d8SAndrew Thompson 		m->m_pkthdr.len = m->m_len = 1;
7323671d9d8SAndrew Thompson 
733ed6d949aSAndrew Thompson 		if (actlen > MCLBYTES - 1)
734ed6d949aSAndrew Thompson 			actlen = MCLBYTES - 1;
7353671d9d8SAndrew Thompson 
736ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
737ed6d949aSAndrew Thompson 		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
738ed6d949aSAndrew Thompson 		m->m_pkthdr.len += actlen;
739ed6d949aSAndrew Thompson 		m->m_len += actlen;
7403671d9d8SAndrew Thompson 
7413671d9d8SAndrew Thompson 		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
742ed6d949aSAndrew Thompson 			actlen);
7433671d9d8SAndrew Thompson 
7443671d9d8SAndrew Thompson 		/* Validate packet and send it up the stack */
7456d917491SHans Petter Selasky 		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
7463671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI event packet is too short\n");
7473671d9d8SAndrew Thompson 
7483671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7493671d9d8SAndrew Thompson 			goto submit_next;
7503671d9d8SAndrew Thompson 		}
7513671d9d8SAndrew Thompson 
7523671d9d8SAndrew Thompson 		hdr = mtod(m, ng_hci_event_pkt_t *);
7533671d9d8SAndrew Thompson 		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
7543671d9d8SAndrew Thompson 			UBT_ERR(sc, "Invalid HCI event packet size, " \
7553671d9d8SAndrew Thompson 				"length=%d, pktlen=%d\n",
7563671d9d8SAndrew Thompson 				hdr->length, m->m_pkthdr.len);
7573671d9d8SAndrew Thompson 
7583671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7593671d9d8SAndrew Thompson 			goto submit_next;
7603671d9d8SAndrew Thompson 		}
7613671d9d8SAndrew Thompson 
7623671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
7633671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len, hdr->length);
7643671d9d8SAndrew Thompson 
7653671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
7663671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
7673671d9d8SAndrew Thompson 
7683671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
7693671d9d8SAndrew Thompson 		/* m == NULL at this point */
7703671d9d8SAndrew Thompson 		/* FALLTHROUGH */
7713671d9d8SAndrew Thompson 
7723671d9d8SAndrew Thompson 	case USB_ST_SETUP:
7733671d9d8SAndrew Thompson submit_next:
7743671d9d8SAndrew Thompson 		NG_FREE_M(m); /* checks for m != NULL */
7753671d9d8SAndrew Thompson 
776ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
777a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
7783671d9d8SAndrew Thompson 		break;
7793671d9d8SAndrew Thompson 
7803671d9d8SAndrew Thompson 	default: /* Error */
781ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
7823671d9d8SAndrew Thompson 			UBT_WARN(sc, "interrupt transfer failed: %s\n",
783ed6d949aSAndrew Thompson 				usbd_errstr(error));
7843671d9d8SAndrew Thompson 
7853671d9d8SAndrew Thompson 			/* Try to clear stall first */
786ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
7873671d9d8SAndrew Thompson 			goto submit_next;
7883671d9d8SAndrew Thompson 		}
7893671d9d8SAndrew Thompson 			/* transfer cancelled */
7903671d9d8SAndrew Thompson 		break;
7913671d9d8SAndrew Thompson 	}
7923671d9d8SAndrew Thompson } /* ubt_intr_read_callback */
7933671d9d8SAndrew Thompson 
7943671d9d8SAndrew Thompson /*
7953671d9d8SAndrew Thompson  * Called when incoming bulk transfer (ACL packet) has completed, i.e.
7963671d9d8SAndrew Thompson  * ACL packet was received from the device.
7973671d9d8SAndrew Thompson  * USB context.
7983671d9d8SAndrew Thompson  */
7993671d9d8SAndrew Thompson 
8003671d9d8SAndrew Thompson static void
801ed6d949aSAndrew Thompson ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
8023671d9d8SAndrew Thompson {
803ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
8043671d9d8SAndrew Thompson 	struct mbuf		*m;
8053671d9d8SAndrew Thompson 	ng_hci_acldata_pkt_t	*hdr;
806ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
8076d917491SHans Petter Selasky 	int len;
808ed6d949aSAndrew Thompson 	int actlen;
809ed6d949aSAndrew Thompson 
810ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
8113671d9d8SAndrew Thompson 
8123671d9d8SAndrew Thompson 	m = NULL;
8133671d9d8SAndrew Thompson 
8143671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
8153671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
8163671d9d8SAndrew Thompson 		/* Allocate new mbuf */
817eb1b1807SGleb Smirnoff 		MGETHDR(m, M_NOWAIT, MT_DATA);
8183671d9d8SAndrew Thompson 		if (m == NULL) {
8193671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8203671d9d8SAndrew Thompson 			goto submit_next;
8213671d9d8SAndrew Thompson 		}
8223671d9d8SAndrew Thompson 
823eb1b1807SGleb Smirnoff 		MCLGET(m, M_NOWAIT);
8243671d9d8SAndrew Thompson 		if (!(m->m_flags & M_EXT)) {
8253671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8263671d9d8SAndrew Thompson 			goto submit_next;
8273671d9d8SAndrew Thompson 		}
8283671d9d8SAndrew Thompson 
8293671d9d8SAndrew Thompson 		/* Add HCI packet type */
8303671d9d8SAndrew Thompson 		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
8313671d9d8SAndrew Thompson 		m->m_pkthdr.len = m->m_len = 1;
8323671d9d8SAndrew Thompson 
833ed6d949aSAndrew Thompson 		if (actlen > MCLBYTES - 1)
834ed6d949aSAndrew Thompson 			actlen = MCLBYTES - 1;
8353671d9d8SAndrew Thompson 
836ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
837ed6d949aSAndrew Thompson 		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
838ed6d949aSAndrew Thompson 		m->m_pkthdr.len += actlen;
839ed6d949aSAndrew Thompson 		m->m_len += actlen;
8403671d9d8SAndrew Thompson 
8413671d9d8SAndrew Thompson 		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
842ed6d949aSAndrew Thompson 			actlen);
8433671d9d8SAndrew Thompson 
8443671d9d8SAndrew Thompson 		/* Validate packet and send it up the stack */
8456d917491SHans Petter Selasky 		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
8463671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI ACL packet is too short\n");
8473671d9d8SAndrew Thompson 
8483671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8493671d9d8SAndrew Thompson 			goto submit_next;
8503671d9d8SAndrew Thompson 		}
8513671d9d8SAndrew Thompson 
8523671d9d8SAndrew Thompson 		hdr = mtod(m, ng_hci_acldata_pkt_t *);
8533671d9d8SAndrew Thompson 		len = le16toh(hdr->length);
8546d917491SHans Petter Selasky 		if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
8553671d9d8SAndrew Thompson 			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
8563671d9d8SAndrew Thompson 				"pktlen=%d\n", len, m->m_pkthdr.len);
8573671d9d8SAndrew Thompson 
8583671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8593671d9d8SAndrew Thompson 			goto submit_next;
8603671d9d8SAndrew Thompson 		}
8613671d9d8SAndrew Thompson 
8623671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
8633671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len, len);
8643671d9d8SAndrew Thompson 
8653671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
8663671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
8673671d9d8SAndrew Thompson 
8683671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
8693671d9d8SAndrew Thompson 		/* m == NULL at this point */
8703671d9d8SAndrew Thompson 		/* FALLTHOUGH */
8713671d9d8SAndrew Thompson 
8723671d9d8SAndrew Thompson 	case USB_ST_SETUP:
8733671d9d8SAndrew Thompson submit_next:
8743671d9d8SAndrew Thompson 		NG_FREE_M(m); /* checks for m != NULL */
8753671d9d8SAndrew Thompson 
876ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
877a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
8783671d9d8SAndrew Thompson 		break;
8793671d9d8SAndrew Thompson 
8803671d9d8SAndrew Thompson 	default: /* Error */
881ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
8823671d9d8SAndrew Thompson 			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
883ed6d949aSAndrew Thompson 				usbd_errstr(error));
8843671d9d8SAndrew Thompson 
8853671d9d8SAndrew Thompson 			/* Try to clear stall first */
886ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
8873671d9d8SAndrew Thompson 			goto submit_next;
8883671d9d8SAndrew Thompson 		}
8893671d9d8SAndrew Thompson 			/* transfer cancelled */
8903671d9d8SAndrew Thompson 		break;
8913671d9d8SAndrew Thompson 	}
8923671d9d8SAndrew Thompson } /* ubt_bulk_read_callback */
8933671d9d8SAndrew Thompson 
8943671d9d8SAndrew Thompson /*
8953671d9d8SAndrew Thompson  * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
8963671d9d8SAndrew Thompson  * ACL packet was sent to the device.
8973671d9d8SAndrew Thompson  * USB context.
8983671d9d8SAndrew Thompson  */
8993671d9d8SAndrew Thompson 
9003671d9d8SAndrew Thompson static void
901ed6d949aSAndrew Thompson ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
9023671d9d8SAndrew Thompson {
903ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
9043671d9d8SAndrew Thompson 	struct mbuf		*m;
905ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
906ed6d949aSAndrew Thompson 	int			actlen;
907ed6d949aSAndrew Thompson 
908ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
9093671d9d8SAndrew Thompson 
9103671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9113671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
912ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
913ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
9143671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
9153671d9d8SAndrew Thompson 		/* FALLTHROUGH */
9163671d9d8SAndrew Thompson 
9173671d9d8SAndrew Thompson 	case USB_ST_SETUP:
9183671d9d8SAndrew Thompson send_next:
9193671d9d8SAndrew Thompson 		/* Get next mbuf, if any */
9203671d9d8SAndrew Thompson 		UBT_NG_LOCK(sc);
9213671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
9223671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
9233671d9d8SAndrew Thompson 
9243671d9d8SAndrew Thompson 		if (m == NULL) {
9253671d9d8SAndrew Thompson 			UBT_INFO(sc, "ACL data queue is empty\n");
9263671d9d8SAndrew Thompson 			break; /* transfer completed */
9273671d9d8SAndrew Thompson 		}
9283671d9d8SAndrew Thompson 
9293671d9d8SAndrew Thompson 		/*
9303671d9d8SAndrew Thompson 		 * Copy ACL data frame back to a linear USB transfer buffer
9313671d9d8SAndrew Thompson 		 * and schedule transfer
9323671d9d8SAndrew Thompson 		 */
9333671d9d8SAndrew Thompson 
934ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
935ed6d949aSAndrew Thompson 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
936ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
9373671d9d8SAndrew Thompson 
9383671d9d8SAndrew Thompson 		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
9393671d9d8SAndrew Thompson 			m->m_pkthdr.len);
9403671d9d8SAndrew Thompson 
9413671d9d8SAndrew Thompson 		NG_FREE_M(m);
9423671d9d8SAndrew Thompson 
943a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
9443671d9d8SAndrew Thompson 		break;
9453671d9d8SAndrew Thompson 
9463671d9d8SAndrew Thompson 	default: /* Error */
947ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
9483671d9d8SAndrew Thompson 			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
949ed6d949aSAndrew Thompson 				usbd_errstr(error));
9503671d9d8SAndrew Thompson 
9513671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
9523671d9d8SAndrew Thompson 
9533671d9d8SAndrew Thompson 			/* try to clear stall first */
954ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
9553671d9d8SAndrew Thompson 			goto send_next;
9563671d9d8SAndrew Thompson 		}
9573671d9d8SAndrew Thompson 			/* transfer cancelled */
9583671d9d8SAndrew Thompson 		break;
9593671d9d8SAndrew Thompson 	}
9603671d9d8SAndrew Thompson } /* ubt_bulk_write_callback */
9613671d9d8SAndrew Thompson 
9623671d9d8SAndrew Thompson /*
9633671d9d8SAndrew Thompson  * Called when incoming isoc transfer (SCO packet) has completed, i.e.
9643671d9d8SAndrew Thompson  * SCO packet was received from the device.
9653671d9d8SAndrew Thompson  * USB context.
9663671d9d8SAndrew Thompson  */
9673671d9d8SAndrew Thompson 
9683671d9d8SAndrew Thompson static void
969ed6d949aSAndrew Thompson ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
9703671d9d8SAndrew Thompson {
971ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
9723671d9d8SAndrew Thompson 	int			n;
973ed6d949aSAndrew Thompson 	int actlen, nframes;
974ed6d949aSAndrew Thompson 
975ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
9763671d9d8SAndrew Thompson 
9773671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9783671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
979ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++)
9803671d9d8SAndrew Thompson 			if (ubt_isoc_read_one_frame(xfer, n) < 0)
9813671d9d8SAndrew Thompson 				break;
9823671d9d8SAndrew Thompson 		/* FALLTHROUGH */
9833671d9d8SAndrew Thompson 
9843671d9d8SAndrew Thompson 	case USB_ST_SETUP:
9853671d9d8SAndrew Thompson read_next:
986ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++)
987ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, n,
988ed6d949aSAndrew Thompson 			    usbd_xfer_max_framelen(xfer));
9893671d9d8SAndrew Thompson 
990a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
9913671d9d8SAndrew Thompson 		break;
9923671d9d8SAndrew Thompson 
9933671d9d8SAndrew Thompson 	default: /* Error */
994ed6d949aSAndrew Thompson                 if (error != USB_ERR_CANCELLED) {
9953671d9d8SAndrew Thompson                         UBT_STAT_IERROR(sc);
9963671d9d8SAndrew Thompson                         goto read_next;
9973671d9d8SAndrew Thompson                 }
9983671d9d8SAndrew Thompson 
9993671d9d8SAndrew Thompson 		/* transfer cancelled */
10003671d9d8SAndrew Thompson 		break;
10013671d9d8SAndrew Thompson 	}
10023671d9d8SAndrew Thompson } /* ubt_isoc_read_callback */
10033671d9d8SAndrew Thompson 
10043671d9d8SAndrew Thompson /*
10053671d9d8SAndrew Thompson  * Helper function. Called from ubt_isoc_read_callback() to read
10063671d9d8SAndrew Thompson  * SCO data from one frame.
10073671d9d8SAndrew Thompson  * USB context.
10083671d9d8SAndrew Thompson  */
10093671d9d8SAndrew Thompson 
10103671d9d8SAndrew Thompson static int
1011760bc48eSAndrew Thompson ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
10123671d9d8SAndrew Thompson {
1013ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1014ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
10153671d9d8SAndrew Thompson 	struct mbuf		*m;
1016ed6d949aSAndrew Thompson 	int			len, want, got, total;
10173671d9d8SAndrew Thompson 
10183671d9d8SAndrew Thompson 	/* Get existing SCO reassembly buffer */
1019ed6d949aSAndrew Thompson 	pc = usbd_xfer_get_frame(xfer, 0);
10203671d9d8SAndrew Thompson 	m = sc->sc_isoc_in_buffer;
10218f9e0ef9SAndrew Thompson 	total = usbd_xfer_frame_len(xfer, frame_no);
10223671d9d8SAndrew Thompson 
10233671d9d8SAndrew Thompson 	/* While we have data in the frame */
1024ed6d949aSAndrew Thompson 	while (total > 0) {
10253671d9d8SAndrew Thompson 		if (m == NULL) {
10263671d9d8SAndrew Thompson 			/* Start new reassembly buffer */
1027eb1b1807SGleb Smirnoff 			MGETHDR(m, M_NOWAIT, MT_DATA);
10283671d9d8SAndrew Thompson 			if (m == NULL) {
10293671d9d8SAndrew Thompson 				UBT_STAT_IERROR(sc);
10303671d9d8SAndrew Thompson 				return (-1);	/* XXX out of sync! */
10313671d9d8SAndrew Thompson 			}
10323671d9d8SAndrew Thompson 
1033eb1b1807SGleb Smirnoff 			MCLGET(m, M_NOWAIT);
10343671d9d8SAndrew Thompson 			if (!(m->m_flags & M_EXT)) {
10353671d9d8SAndrew Thompson 				UBT_STAT_IERROR(sc);
10363671d9d8SAndrew Thompson 				NG_FREE_M(m);
10373671d9d8SAndrew Thompson 				return (-1);	/* XXX out of sync! */
10383671d9d8SAndrew Thompson 			}
10393671d9d8SAndrew Thompson 
10403671d9d8SAndrew Thompson 			/* Expect SCO header */
10413671d9d8SAndrew Thompson 			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
10423671d9d8SAndrew Thompson 			m->m_pkthdr.len = m->m_len = got = 1;
10433671d9d8SAndrew Thompson 			want = sizeof(ng_hci_scodata_pkt_t);
10443671d9d8SAndrew Thompson 		} else {
10453671d9d8SAndrew Thompson 			/*
10463671d9d8SAndrew Thompson 			 * Check if we have SCO header and if so
10473671d9d8SAndrew Thompson 			 * adjust amount of data we want
10483671d9d8SAndrew Thompson 			 */
10493671d9d8SAndrew Thompson 			got = m->m_pkthdr.len;
10503671d9d8SAndrew Thompson 			want = sizeof(ng_hci_scodata_pkt_t);
10513671d9d8SAndrew Thompson 
10523671d9d8SAndrew Thompson 			if (got >= want)
10533671d9d8SAndrew Thompson 				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
10543671d9d8SAndrew Thompson 		}
10553671d9d8SAndrew Thompson 
10563671d9d8SAndrew Thompson 		/* Append frame data to the SCO reassembly buffer */
1057ed6d949aSAndrew Thompson 		len = total;
10583671d9d8SAndrew Thompson 		if (got + len > want)
10593671d9d8SAndrew Thompson 			len = want - got;
10603671d9d8SAndrew Thompson 
1061ed6d949aSAndrew Thompson 		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
10623671d9d8SAndrew Thompson 			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
10633671d9d8SAndrew Thompson 
10643671d9d8SAndrew Thompson 		m->m_pkthdr.len += len;
10653671d9d8SAndrew Thompson 		m->m_len += len;
1066ed6d949aSAndrew Thompson 		total -= len;
10673671d9d8SAndrew Thompson 
10683671d9d8SAndrew Thompson 		/* Check if we got everything we wanted, if not - continue */
10693671d9d8SAndrew Thompson 		if (got != want)
10703671d9d8SAndrew Thompson 			continue;
10713671d9d8SAndrew Thompson 
10723671d9d8SAndrew Thompson 		/* If we got here then we got complete SCO frame */
10733671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
10743671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len,
10753671d9d8SAndrew Thompson 			mtod(m, ng_hci_scodata_pkt_t *)->length);
10763671d9d8SAndrew Thompson 
10773671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
10783671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
10793671d9d8SAndrew Thompson 
10803671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
10813671d9d8SAndrew Thompson 		/* m == NULL at this point */
10823671d9d8SAndrew Thompson 	}
10833671d9d8SAndrew Thompson 
10843671d9d8SAndrew Thompson 	/* Put SCO reassembly buffer back */
10853671d9d8SAndrew Thompson 	sc->sc_isoc_in_buffer = m;
10863671d9d8SAndrew Thompson 
10873671d9d8SAndrew Thompson 	return (0);
10883671d9d8SAndrew Thompson } /* ubt_isoc_read_one_frame */
10893671d9d8SAndrew Thompson 
10903671d9d8SAndrew Thompson /*
10913671d9d8SAndrew Thompson  * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
10923671d9d8SAndrew Thompson  * SCO packet was sent to the device.
10933671d9d8SAndrew Thompson  * USB context.
10943671d9d8SAndrew Thompson  */
10953671d9d8SAndrew Thompson 
10963671d9d8SAndrew Thompson static void
1097ed6d949aSAndrew Thompson ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
10983671d9d8SAndrew Thompson {
1099ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1100ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
11013671d9d8SAndrew Thompson 	struct mbuf		*m;
11023671d9d8SAndrew Thompson 	int			n, space, offset;
1103ed6d949aSAndrew Thompson 	int			actlen, nframes;
1104ed6d949aSAndrew Thompson 
1105ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1106ed6d949aSAndrew Thompson 	pc = usbd_xfer_get_frame(xfer, 0);
11073671d9d8SAndrew Thompson 
11083671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
11093671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
1110ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1111ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
11123671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
11133671d9d8SAndrew Thompson 		/* FALLTHROUGH */
11143671d9d8SAndrew Thompson 
11153671d9d8SAndrew Thompson 	case USB_ST_SETUP:
11163671d9d8SAndrew Thompson send_next:
11173671d9d8SAndrew Thompson 		offset = 0;
1118ed6d949aSAndrew Thompson 		space = usbd_xfer_max_framelen(xfer) * nframes;
11193671d9d8SAndrew Thompson 		m = NULL;
11203671d9d8SAndrew Thompson 
11213671d9d8SAndrew Thompson 		while (space > 0) {
11223671d9d8SAndrew Thompson 			if (m == NULL) {
11233671d9d8SAndrew Thompson 				UBT_NG_LOCK(sc);
11243671d9d8SAndrew Thompson 				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
11253671d9d8SAndrew Thompson 				UBT_NG_UNLOCK(sc);
11263671d9d8SAndrew Thompson 
11273671d9d8SAndrew Thompson 				if (m == NULL)
11283671d9d8SAndrew Thompson 					break;
11293671d9d8SAndrew Thompson 			}
11303671d9d8SAndrew Thompson 
11313671d9d8SAndrew Thompson 			n = min(space, m->m_pkthdr.len);
11323671d9d8SAndrew Thompson 			if (n > 0) {
1133ed6d949aSAndrew Thompson 				usbd_m_copy_in(pc, offset, m,0, n);
11343671d9d8SAndrew Thompson 				m_adj(m, n);
11353671d9d8SAndrew Thompson 
11363671d9d8SAndrew Thompson 				offset += n;
11373671d9d8SAndrew Thompson 				space -= n;
11383671d9d8SAndrew Thompson 			}
11393671d9d8SAndrew Thompson 
11403671d9d8SAndrew Thompson 			if (m->m_pkthdr.len == 0)
11413671d9d8SAndrew Thompson 				NG_FREE_M(m); /* sets m = NULL */
11423671d9d8SAndrew Thompson 		}
11433671d9d8SAndrew Thompson 
11443671d9d8SAndrew Thompson 		/* Put whatever is left from mbuf back on queue */
11453671d9d8SAndrew Thompson 		if (m != NULL) {
11463671d9d8SAndrew Thompson 			UBT_NG_LOCK(sc);
11473671d9d8SAndrew Thompson 			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
11483671d9d8SAndrew Thompson 			UBT_NG_UNLOCK(sc);
11493671d9d8SAndrew Thompson 		}
11503671d9d8SAndrew Thompson 
11513671d9d8SAndrew Thompson 		/*
11523671d9d8SAndrew Thompson 		 * Calculate sizes for isoc frames.
11533671d9d8SAndrew Thompson 		 * Note that offset could be 0 at this point (i.e. we have
11543671d9d8SAndrew Thompson 		 * nothing to send). That is fine, as we have isoc. transfers
11553671d9d8SAndrew Thompson 		 * going in both directions all the time. In this case it
11563671d9d8SAndrew Thompson 		 * would be just empty isoc. transfer.
11573671d9d8SAndrew Thompson 		 */
11583671d9d8SAndrew Thompson 
1159ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++) {
1160ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, n,
1161ed6d949aSAndrew Thompson 			    min(offset, usbd_xfer_max_framelen(xfer)));
11628f9e0ef9SAndrew Thompson 			offset -= usbd_xfer_frame_len(xfer, n);
11633671d9d8SAndrew Thompson 		}
11643671d9d8SAndrew Thompson 
1165a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
11663671d9d8SAndrew Thompson 		break;
11673671d9d8SAndrew Thompson 
11683671d9d8SAndrew Thompson 	default: /* Error */
1169ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
11703671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
11713671d9d8SAndrew Thompson 			goto send_next;
11723671d9d8SAndrew Thompson 		}
11733671d9d8SAndrew Thompson 
11743671d9d8SAndrew Thompson 		/* transfer cancelled */
11753671d9d8SAndrew Thompson 		break;
11763671d9d8SAndrew Thompson 	}
11773671d9d8SAndrew Thompson }
11783671d9d8SAndrew Thompson 
11793671d9d8SAndrew Thompson /*
11803671d9d8SAndrew Thompson  * Utility function to forward provided mbuf upstream (i.e. up the stack).
11813671d9d8SAndrew Thompson  * Modifies value of the mbuf pointer (sets it to NULL).
11823671d9d8SAndrew Thompson  * Save to call from any context.
11833671d9d8SAndrew Thompson  */
11843671d9d8SAndrew Thompson 
11853671d9d8SAndrew Thompson static int
11863671d9d8SAndrew Thompson ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
11873671d9d8SAndrew Thompson {
11883671d9d8SAndrew Thompson 	hook_p	hook;
11893671d9d8SAndrew Thompson 	int	error;
11903671d9d8SAndrew Thompson 
11913671d9d8SAndrew Thompson 	/*
11923671d9d8SAndrew Thompson 	 * Close the race with Netgraph hook newhook/disconnect methods.
11933671d9d8SAndrew Thompson 	 * Save the hook pointer atomically. Two cases are possible:
11943671d9d8SAndrew Thompson 	 *
11953671d9d8SAndrew Thompson 	 * 1) The hook pointer is NULL. It means disconnect method got
11963671d9d8SAndrew Thompson 	 *    there first. In this case we are done.
11973671d9d8SAndrew Thompson 	 *
11983671d9d8SAndrew Thompson 	 * 2) The hook pointer is not NULL. It means that hook pointer
11993671d9d8SAndrew Thompson 	 *    could be either in valid or invalid (i.e. in the process
12003671d9d8SAndrew Thompson 	 *    of disconnect) state. In any case grab an extra reference
12013671d9d8SAndrew Thompson 	 *    to protect the hook pointer.
12023671d9d8SAndrew Thompson 	 *
12033671d9d8SAndrew Thompson 	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
12043671d9d8SAndrew Thompson 	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
12053671d9d8SAndrew Thompson 	 */
12063671d9d8SAndrew Thompson 
12073671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
12083671d9d8SAndrew Thompson 	if ((hook = sc->sc_hook) != NULL)
12093671d9d8SAndrew Thompson 		NG_HOOK_REF(hook);
12103671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
12113671d9d8SAndrew Thompson 
12123671d9d8SAndrew Thompson 	if (hook == NULL) {
12133671d9d8SAndrew Thompson 		NG_FREE_M(*m);
12143671d9d8SAndrew Thompson 		return (ENETDOWN);
12153671d9d8SAndrew Thompson 	}
12163671d9d8SAndrew Thompson 
12173671d9d8SAndrew Thompson 	NG_SEND_DATA_ONLY(error, hook, *m);
12183671d9d8SAndrew Thompson 	NG_HOOK_UNREF(hook);
12193671d9d8SAndrew Thompson 
12203671d9d8SAndrew Thompson 	if (error != 0)
12213671d9d8SAndrew Thompson 		UBT_STAT_IERROR(sc);
12223671d9d8SAndrew Thompson 
12233671d9d8SAndrew Thompson 	return (error);
12243671d9d8SAndrew Thompson } /* ubt_fwd_mbuf_up */
12253671d9d8SAndrew Thompson 
12263671d9d8SAndrew Thompson /****************************************************************************
12273671d9d8SAndrew Thompson  ****************************************************************************
12283671d9d8SAndrew Thompson  **                                 Glue
12293671d9d8SAndrew Thompson  ****************************************************************************
12303671d9d8SAndrew Thompson  ****************************************************************************/
12313671d9d8SAndrew Thompson 
12323671d9d8SAndrew Thompson /*
12333671d9d8SAndrew Thompson  * Schedule glue task. Should be called with sc_ng_mtx held.
12343671d9d8SAndrew Thompson  * Netgraph context.
12353671d9d8SAndrew Thompson  */
12363671d9d8SAndrew Thompson 
12373671d9d8SAndrew Thompson static void
12383671d9d8SAndrew Thompson ubt_task_schedule(ubt_softc_p sc, int action)
12393671d9d8SAndrew Thompson {
12403671d9d8SAndrew Thompson 	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
12413671d9d8SAndrew Thompson 
12423671d9d8SAndrew Thompson 	/*
12433671d9d8SAndrew Thompson 	 * Try to handle corner case when "start all" and "stop all"
12443671d9d8SAndrew Thompson 	 * actions can both be set before task is executed.
12453671d9d8SAndrew Thompson 	 *
12463671d9d8SAndrew Thompson 	 * The rules are
12473671d9d8SAndrew Thompson 	 *
12483671d9d8SAndrew Thompson 	 * sc_task_flags	action		new sc_task_flags
12493671d9d8SAndrew Thompson 	 * ------------------------------------------------------
12503671d9d8SAndrew Thompson 	 * 0			start		start
12513671d9d8SAndrew Thompson 	 * 0			stop		stop
12523671d9d8SAndrew Thompson 	 * start		start		start
12533671d9d8SAndrew Thompson 	 * start		stop		stop
12543671d9d8SAndrew Thompson 	 * stop			start		stop|start
12553671d9d8SAndrew Thompson 	 * stop			stop		stop
12563671d9d8SAndrew Thompson 	 * stop|start		start		stop|start
12573671d9d8SAndrew Thompson 	 * stop|start		stop		stop
12583671d9d8SAndrew Thompson 	 */
12593671d9d8SAndrew Thompson 
12603671d9d8SAndrew Thompson 	if (action != 0) {
12613671d9d8SAndrew Thompson 		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
12623671d9d8SAndrew Thompson 			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
12633671d9d8SAndrew Thompson 
12643671d9d8SAndrew Thompson 		sc->sc_task_flags |= action;
12653671d9d8SAndrew Thompson 	}
12663671d9d8SAndrew Thompson 
12673671d9d8SAndrew Thompson 	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
12683671d9d8SAndrew Thompson 		return;
12693671d9d8SAndrew Thompson 
12703671d9d8SAndrew Thompson 	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
12713671d9d8SAndrew Thompson 		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
12723671d9d8SAndrew Thompson 		return;
12733671d9d8SAndrew Thompson 	}
12743671d9d8SAndrew Thompson 
12753671d9d8SAndrew Thompson 	/* XXX: i think this should never happen */
12763671d9d8SAndrew Thompson } /* ubt_task_schedule */
12773671d9d8SAndrew Thompson 
12783671d9d8SAndrew Thompson /*
12793671d9d8SAndrew Thompson  * Glue task. Examines sc_task_flags and does things depending on it.
12803671d9d8SAndrew Thompson  * Taskqueue context.
12813671d9d8SAndrew Thompson  */
12823671d9d8SAndrew Thompson 
12833671d9d8SAndrew Thompson static void
12843671d9d8SAndrew Thompson ubt_task(void *context, int pending)
12853671d9d8SAndrew Thompson {
12863671d9d8SAndrew Thompson 	ubt_softc_p	sc = context;
12873671d9d8SAndrew Thompson 	int		task_flags, i;
12883671d9d8SAndrew Thompson 
12893671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
12903671d9d8SAndrew Thompson 	task_flags = sc->sc_task_flags;
12913671d9d8SAndrew Thompson 	sc->sc_task_flags = 0;
12923671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
12933671d9d8SAndrew Thompson 
12943671d9d8SAndrew Thompson 	/*
12953671d9d8SAndrew Thompson 	 * Stop all USB transfers synchronously.
12963671d9d8SAndrew Thompson 	 * Stop interface #0 and #1 transfers at the same time and in the
1297a593f6b8SAndrew Thompson 	 * same loop. usbd_transfer_drain() will do appropriate locking.
12983671d9d8SAndrew Thompson 	 */
12993671d9d8SAndrew Thompson 
13003671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_STOP_ALL)
13013671d9d8SAndrew Thompson 		for (i = 0; i < UBT_N_TRANSFER; i ++)
1302a593f6b8SAndrew Thompson 			usbd_transfer_drain(sc->sc_xfer[i]);
13033671d9d8SAndrew Thompson 
13043671d9d8SAndrew Thompson 	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
13053671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_ALL) {
13063671d9d8SAndrew Thompson 		/*
13073671d9d8SAndrew Thompson 		 * Interface #0
13083671d9d8SAndrew Thompson 		 */
13093671d9d8SAndrew Thompson 
13103671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
13113671d9d8SAndrew Thompson 
13123671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
13133671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
13143671d9d8SAndrew Thompson 
13153671d9d8SAndrew Thompson 		/*
13163671d9d8SAndrew Thompson 		 * Interface #1
13173671d9d8SAndrew Thompson 		 * Start both read and write isoc. transfers by default.
13183671d9d8SAndrew Thompson 		 * Get them going all the time even if we have nothing
13193671d9d8SAndrew Thompson 		 * to send to avoid any delays.
13203671d9d8SAndrew Thompson 		 */
13213671d9d8SAndrew Thompson 
13223671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
13233671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
13243671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
13253671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
13263671d9d8SAndrew Thompson 
13273671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
13283671d9d8SAndrew Thompson 	}
13293671d9d8SAndrew Thompson 
13303671d9d8SAndrew Thompson  	/* Start outgoing control transfer */
13313671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_CTRL) {
13323671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
13333671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
13343671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
13353671d9d8SAndrew Thompson 	}
13363671d9d8SAndrew Thompson 
13373671d9d8SAndrew Thompson 	/* Start outgoing bulk transfer */
13383671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_BULK) {
13393671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
13403671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
13413671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
13423671d9d8SAndrew Thompson 	}
13433671d9d8SAndrew Thompson } /* ubt_task */
13443671d9d8SAndrew Thompson 
13453671d9d8SAndrew Thompson /****************************************************************************
13463671d9d8SAndrew Thompson  ****************************************************************************
13473671d9d8SAndrew Thompson  **                        Netgraph specific
13483671d9d8SAndrew Thompson  ****************************************************************************
13493671d9d8SAndrew Thompson  ****************************************************************************/
13503671d9d8SAndrew Thompson 
13513671d9d8SAndrew Thompson /*
13523671d9d8SAndrew Thompson  * Netgraph node constructor. Do not allow to create node of this type.
13533671d9d8SAndrew Thompson  * Netgraph context.
13543671d9d8SAndrew Thompson  */
13553671d9d8SAndrew Thompson 
13563671d9d8SAndrew Thompson static int
13573671d9d8SAndrew Thompson ng_ubt_constructor(node_p node)
13583671d9d8SAndrew Thompson {
13593671d9d8SAndrew Thompson 	return (EINVAL);
13603671d9d8SAndrew Thompson } /* ng_ubt_constructor */
13613671d9d8SAndrew Thompson 
13623671d9d8SAndrew Thompson /*
13633671d9d8SAndrew Thompson  * Netgraph node destructor. Destroy node only when device has been detached.
13643671d9d8SAndrew Thompson  * Netgraph context.
13653671d9d8SAndrew Thompson  */
13663671d9d8SAndrew Thompson 
13673671d9d8SAndrew Thompson static int
13683671d9d8SAndrew Thompson ng_ubt_shutdown(node_p node)
13693671d9d8SAndrew Thompson {
13703671d9d8SAndrew Thompson 	if (node->nd_flags & NGF_REALLY_DIE) {
13713671d9d8SAndrew Thompson 		/*
13723671d9d8SAndrew Thompson                  * We came here because the USB device is being
13733671d9d8SAndrew Thompson 		 * detached, so stop being persistant.
13743671d9d8SAndrew Thompson                  */
13753671d9d8SAndrew Thompson 		NG_NODE_SET_PRIVATE(node, NULL);
13763671d9d8SAndrew Thompson 		NG_NODE_UNREF(node);
13773671d9d8SAndrew Thompson 	} else
13783671d9d8SAndrew Thompson 		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
13793671d9d8SAndrew Thompson 
13803671d9d8SAndrew Thompson 	return (0);
13813671d9d8SAndrew Thompson } /* ng_ubt_shutdown */
13823671d9d8SAndrew Thompson 
13833671d9d8SAndrew Thompson /*
13843671d9d8SAndrew Thompson  * Create new hook. There can only be one.
13853671d9d8SAndrew Thompson  * Netgraph context.
13863671d9d8SAndrew Thompson  */
13873671d9d8SAndrew Thompson 
13883671d9d8SAndrew Thompson static int
13893671d9d8SAndrew Thompson ng_ubt_newhook(node_p node, hook_p hook, char const *name)
13903671d9d8SAndrew Thompson {
13913671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
13923671d9d8SAndrew Thompson 
13933671d9d8SAndrew Thompson 	if (strcmp(name, NG_UBT_HOOK) != 0)
13943671d9d8SAndrew Thompson 		return (EINVAL);
13953671d9d8SAndrew Thompson 
13963671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
13973671d9d8SAndrew Thompson 	if (sc->sc_hook != NULL) {
13983671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
13993671d9d8SAndrew Thompson 
14003671d9d8SAndrew Thompson 		return (EISCONN);
14013671d9d8SAndrew Thompson 	}
14023671d9d8SAndrew Thompson 
14033671d9d8SAndrew Thompson 	sc->sc_hook = hook;
14043671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
14053671d9d8SAndrew Thompson 
14063671d9d8SAndrew Thompson 	return (0);
14073671d9d8SAndrew Thompson } /* ng_ubt_newhook */
14083671d9d8SAndrew Thompson 
14093671d9d8SAndrew Thompson /*
14103671d9d8SAndrew Thompson  * Connect hook. Start incoming USB transfers.
14113671d9d8SAndrew Thompson  * Netgraph context.
14123671d9d8SAndrew Thompson  */
14133671d9d8SAndrew Thompson 
14143671d9d8SAndrew Thompson static int
14153671d9d8SAndrew Thompson ng_ubt_connect(hook_p hook)
14163671d9d8SAndrew Thompson {
14173671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
14183671d9d8SAndrew Thompson 
14193671d9d8SAndrew Thompson 	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
14203671d9d8SAndrew Thompson 
14213671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
14223671d9d8SAndrew Thompson 	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
14233671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
14243671d9d8SAndrew Thompson 
14253671d9d8SAndrew Thompson 	return (0);
14263671d9d8SAndrew Thompson } /* ng_ubt_connect */
14273671d9d8SAndrew Thompson 
14283671d9d8SAndrew Thompson /*
14293671d9d8SAndrew Thompson  * Disconnect hook.
14303671d9d8SAndrew Thompson  * Netgraph context.
14313671d9d8SAndrew Thompson  */
14323671d9d8SAndrew Thompson 
14333671d9d8SAndrew Thompson static int
14343671d9d8SAndrew Thompson ng_ubt_disconnect(hook_p hook)
14353671d9d8SAndrew Thompson {
14363671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
14373671d9d8SAndrew Thompson 
14383671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
14393671d9d8SAndrew Thompson 
14403671d9d8SAndrew Thompson 	if (hook != sc->sc_hook) {
14413671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
14423671d9d8SAndrew Thompson 
14433671d9d8SAndrew Thompson 		return (EINVAL);
14443671d9d8SAndrew Thompson 	}
14453671d9d8SAndrew Thompson 
14463671d9d8SAndrew Thompson 	sc->sc_hook = NULL;
14473671d9d8SAndrew Thompson 
14483671d9d8SAndrew Thompson 	/* Kick off task to stop all USB xfers */
14493671d9d8SAndrew Thompson 	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
14503671d9d8SAndrew Thompson 
14513671d9d8SAndrew Thompson 	/* Drain queues */
14523671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
14533671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
14543671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
14553671d9d8SAndrew Thompson 
14563671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
14573671d9d8SAndrew Thompson 
14583671d9d8SAndrew Thompson 	return (0);
14593671d9d8SAndrew Thompson } /* ng_ubt_disconnect */
14603671d9d8SAndrew Thompson 
14613671d9d8SAndrew Thompson /*
14623671d9d8SAndrew Thompson  * Process control message.
14633671d9d8SAndrew Thompson  * Netgraph context.
14643671d9d8SAndrew Thompson  */
14653671d9d8SAndrew Thompson 
14663671d9d8SAndrew Thompson static int
14673671d9d8SAndrew Thompson ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
14683671d9d8SAndrew Thompson {
14693671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
14703671d9d8SAndrew Thompson 	struct ng_mesg		*msg, *rsp = NULL;
14713671d9d8SAndrew Thompson 	struct ng_bt_mbufq	*q;
14723671d9d8SAndrew Thompson 	int			error = 0, queue, qlen;
14733671d9d8SAndrew Thompson 
14743671d9d8SAndrew Thompson 	NGI_GET_MSG(item, msg);
14753671d9d8SAndrew Thompson 
14763671d9d8SAndrew Thompson 	switch (msg->header.typecookie) {
14773671d9d8SAndrew Thompson 	case NGM_GENERIC_COOKIE:
14783671d9d8SAndrew Thompson 		switch (msg->header.cmd) {
14793671d9d8SAndrew Thompson 		case NGM_TEXT_STATUS:
14803671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
14813671d9d8SAndrew Thompson 			if (rsp == NULL) {
14823671d9d8SAndrew Thompson 				error = ENOMEM;
14833671d9d8SAndrew Thompson 				break;
14843671d9d8SAndrew Thompson 			}
14853671d9d8SAndrew Thompson 
14863671d9d8SAndrew Thompson 			snprintf(rsp->data, NG_TEXTRESPONSE,
14873671d9d8SAndrew Thompson 				"Hook: %s\n" \
14883671d9d8SAndrew Thompson 				"Task flags: %#x\n" \
14893671d9d8SAndrew Thompson 				"Debug: %d\n" \
14903671d9d8SAndrew Thompson 				"CMD queue: [have:%d,max:%d]\n" \
14913671d9d8SAndrew Thompson 				"ACL queue: [have:%d,max:%d]\n" \
14923671d9d8SAndrew Thompson 				"SCO queue: [have:%d,max:%d]",
14933671d9d8SAndrew Thompson 				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
14943671d9d8SAndrew Thompson 				sc->sc_task_flags,
14953671d9d8SAndrew Thompson 				sc->sc_debug,
14963671d9d8SAndrew Thompson 				sc->sc_cmdq.len,
14973671d9d8SAndrew Thompson 				sc->sc_cmdq.maxlen,
14983671d9d8SAndrew Thompson 				sc->sc_aclq.len,
14993671d9d8SAndrew Thompson 				sc->sc_aclq.maxlen,
15003671d9d8SAndrew Thompson 				sc->sc_scoq.len,
15013671d9d8SAndrew Thompson 				sc->sc_scoq.maxlen);
15023671d9d8SAndrew Thompson 			break;
15033671d9d8SAndrew Thompson 
15043671d9d8SAndrew Thompson 		default:
15053671d9d8SAndrew Thompson 			error = EINVAL;
15063671d9d8SAndrew Thompson 			break;
15073671d9d8SAndrew Thompson 		}
15083671d9d8SAndrew Thompson 		break;
15093671d9d8SAndrew Thompson 
15103671d9d8SAndrew Thompson 	case NGM_UBT_COOKIE:
15113671d9d8SAndrew Thompson 		switch (msg->header.cmd) {
15123671d9d8SAndrew Thompson 		case NGM_UBT_NODE_SET_DEBUG:
15133671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
15143671d9d8SAndrew Thompson 				error = EMSGSIZE;
15153671d9d8SAndrew Thompson 				break;
15163671d9d8SAndrew Thompson 			}
15173671d9d8SAndrew Thompson 
15183671d9d8SAndrew Thompson 			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
15193671d9d8SAndrew Thompson 			break;
15203671d9d8SAndrew Thompson 
15213671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_DEBUG:
15223671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
15233671d9d8SAndrew Thompson 			    M_NOWAIT);
15243671d9d8SAndrew Thompson 			if (rsp == NULL) {
15253671d9d8SAndrew Thompson 				error = ENOMEM;
15263671d9d8SAndrew Thompson 				break;
15273671d9d8SAndrew Thompson 			}
15283671d9d8SAndrew Thompson 
15293671d9d8SAndrew Thompson 			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
15303671d9d8SAndrew Thompson 			break;
15313671d9d8SAndrew Thompson 
15323671d9d8SAndrew Thompson 		case NGM_UBT_NODE_SET_QLEN:
15333671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
15343671d9d8SAndrew Thompson 				error = EMSGSIZE;
15353671d9d8SAndrew Thompson 				break;
15363671d9d8SAndrew Thompson 			}
15373671d9d8SAndrew Thompson 
15383671d9d8SAndrew Thompson 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
15393671d9d8SAndrew Thompson 			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
15403671d9d8SAndrew Thompson 
15413671d9d8SAndrew Thompson 			switch (queue) {
15423671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_CMD:
15433671d9d8SAndrew Thompson 				q = &sc->sc_cmdq;
15443671d9d8SAndrew Thompson 				break;
15453671d9d8SAndrew Thompson 
15463671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_ACL:
15473671d9d8SAndrew Thompson 				q = &sc->sc_aclq;
15483671d9d8SAndrew Thompson 				break;
15493671d9d8SAndrew Thompson 
15503671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_SCO:
15513671d9d8SAndrew Thompson 				q = &sc->sc_scoq;
15523671d9d8SAndrew Thompson 				break;
15533671d9d8SAndrew Thompson 
15543671d9d8SAndrew Thompson 			default:
15553671d9d8SAndrew Thompson 				error = EINVAL;
15563671d9d8SAndrew Thompson 				goto done;
15573671d9d8SAndrew Thompson 				/* NOT REACHED */
15583671d9d8SAndrew Thompson 			}
15593671d9d8SAndrew Thompson 
15603671d9d8SAndrew Thompson 			q->maxlen = qlen;
15613671d9d8SAndrew Thompson 			break;
15623671d9d8SAndrew Thompson 
15633671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_QLEN:
15643671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
15653671d9d8SAndrew Thompson 				error = EMSGSIZE;
15663671d9d8SAndrew Thompson 				break;
15673671d9d8SAndrew Thompson 			}
15683671d9d8SAndrew Thompson 
15693671d9d8SAndrew Thompson 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
15703671d9d8SAndrew Thompson 
15713671d9d8SAndrew Thompson 			switch (queue) {
15723671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_CMD:
15733671d9d8SAndrew Thompson 				q = &sc->sc_cmdq;
15743671d9d8SAndrew Thompson 				break;
15753671d9d8SAndrew Thompson 
15763671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_ACL:
15773671d9d8SAndrew Thompson 				q = &sc->sc_aclq;
15783671d9d8SAndrew Thompson 				break;
15793671d9d8SAndrew Thompson 
15803671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_SCO:
15813671d9d8SAndrew Thompson 				q = &sc->sc_scoq;
15823671d9d8SAndrew Thompson 				break;
15833671d9d8SAndrew Thompson 
15843671d9d8SAndrew Thompson 			default:
15853671d9d8SAndrew Thompson 				error = EINVAL;
15863671d9d8SAndrew Thompson 				goto done;
15873671d9d8SAndrew Thompson 				/* NOT REACHED */
15883671d9d8SAndrew Thompson 			}
15893671d9d8SAndrew Thompson 
15903671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
15913671d9d8SAndrew Thompson 				M_NOWAIT);
15923671d9d8SAndrew Thompson 			if (rsp == NULL) {
15933671d9d8SAndrew Thompson 				error = ENOMEM;
15943671d9d8SAndrew Thompson 				break;
15953671d9d8SAndrew Thompson 			}
15963671d9d8SAndrew Thompson 
15973671d9d8SAndrew Thompson 			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
15983671d9d8SAndrew Thompson 			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
15993671d9d8SAndrew Thompson 			break;
16003671d9d8SAndrew Thompson 
16013671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_STAT:
16023671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
16033671d9d8SAndrew Thompson 			    M_NOWAIT);
16043671d9d8SAndrew Thompson 			if (rsp == NULL) {
16053671d9d8SAndrew Thompson 				error = ENOMEM;
16063671d9d8SAndrew Thompson 				break;
16073671d9d8SAndrew Thompson 			}
16083671d9d8SAndrew Thompson 
16093671d9d8SAndrew Thompson 			bcopy(&sc->sc_stat, rsp->data,
16103671d9d8SAndrew Thompson 				sizeof(ng_ubt_node_stat_ep));
16113671d9d8SAndrew Thompson 			break;
16123671d9d8SAndrew Thompson 
16133671d9d8SAndrew Thompson 		case NGM_UBT_NODE_RESET_STAT:
16143671d9d8SAndrew Thompson 			UBT_STAT_RESET(sc);
16153671d9d8SAndrew Thompson 			break;
16163671d9d8SAndrew Thompson 
16173671d9d8SAndrew Thompson 		default:
16183671d9d8SAndrew Thompson 			error = EINVAL;
16193671d9d8SAndrew Thompson 			break;
16203671d9d8SAndrew Thompson 		}
16213671d9d8SAndrew Thompson 		break;
16223671d9d8SAndrew Thompson 
16233671d9d8SAndrew Thompson 	default:
16243671d9d8SAndrew Thompson 		error = EINVAL;
16253671d9d8SAndrew Thompson 		break;
16263671d9d8SAndrew Thompson 	}
16273671d9d8SAndrew Thompson done:
16283671d9d8SAndrew Thompson 	NG_RESPOND_MSG(error, node, item, rsp);
16293671d9d8SAndrew Thompson 	NG_FREE_MSG(msg);
16303671d9d8SAndrew Thompson 
16313671d9d8SAndrew Thompson 	return (error);
16323671d9d8SAndrew Thompson } /* ng_ubt_rcvmsg */
16333671d9d8SAndrew Thompson 
16343671d9d8SAndrew Thompson /*
16353671d9d8SAndrew Thompson  * Process data.
16363671d9d8SAndrew Thompson  * Netgraph context.
16373671d9d8SAndrew Thompson  */
16383671d9d8SAndrew Thompson 
16393671d9d8SAndrew Thompson static int
16403671d9d8SAndrew Thompson ng_ubt_rcvdata(hook_p hook, item_p item)
16413671d9d8SAndrew Thompson {
16423671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
16433671d9d8SAndrew Thompson 	struct mbuf		*m;
16443671d9d8SAndrew Thompson 	struct ng_bt_mbufq	*q;
16453671d9d8SAndrew Thompson 	int			action, error = 0;
16463671d9d8SAndrew Thompson 
16473671d9d8SAndrew Thompson 	if (hook != sc->sc_hook) {
16483671d9d8SAndrew Thompson 		error = EINVAL;
16493671d9d8SAndrew Thompson 		goto done;
16503671d9d8SAndrew Thompson 	}
16513671d9d8SAndrew Thompson 
16523671d9d8SAndrew Thompson 	/* Deatch mbuf and get HCI frame type */
16533671d9d8SAndrew Thompson 	NGI_GET_M(item, m);
16543671d9d8SAndrew Thompson 
16553671d9d8SAndrew Thompson 	/*
16563671d9d8SAndrew Thompson 	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
16573671d9d8SAndrew Thompson 	 * 2 bytes connection handle and at least 1 byte of length.
16583671d9d8SAndrew Thompson 	 * Panic on data frame that has size smaller than 4 bytes (it
16593671d9d8SAndrew Thompson 	 * should not happen)
16603671d9d8SAndrew Thompson 	 */
16613671d9d8SAndrew Thompson 
16623671d9d8SAndrew Thompson 	if (m->m_pkthdr.len < 4)
16633671d9d8SAndrew Thompson 		panic("HCI frame size is too small! pktlen=%d\n",
16643671d9d8SAndrew Thompson 			m->m_pkthdr.len);
16653671d9d8SAndrew Thompson 
16663671d9d8SAndrew Thompson 	/* Process HCI frame */
16673671d9d8SAndrew Thompson 	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
16683671d9d8SAndrew Thompson 	case NG_HCI_CMD_PKT:
16696d917491SHans Petter Selasky 		if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
16703671d9d8SAndrew Thompson 			panic("HCI command frame size is too big! " \
16713671d9d8SAndrew Thompson 				"buffer size=%zd, packet len=%d\n",
16723671d9d8SAndrew Thompson 				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
16733671d9d8SAndrew Thompson 
16743671d9d8SAndrew Thompson 		q = &sc->sc_cmdq;
16753671d9d8SAndrew Thompson 		action = UBT_FLAG_T_START_CTRL;
16763671d9d8SAndrew Thompson 		break;
16773671d9d8SAndrew Thompson 
16783671d9d8SAndrew Thompson 	case NG_HCI_ACL_DATA_PKT:
16793671d9d8SAndrew Thompson 		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
16803671d9d8SAndrew Thompson 			panic("ACL data frame size is too big! " \
16813671d9d8SAndrew Thompson 				"buffer size=%d, packet len=%d\n",
16823671d9d8SAndrew Thompson 				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
16833671d9d8SAndrew Thompson 
16843671d9d8SAndrew Thompson 		q = &sc->sc_aclq;
16853671d9d8SAndrew Thompson 		action = UBT_FLAG_T_START_BULK;
16863671d9d8SAndrew Thompson 		break;
16873671d9d8SAndrew Thompson 
16883671d9d8SAndrew Thompson 	case NG_HCI_SCO_DATA_PKT:
16893671d9d8SAndrew Thompson 		q = &sc->sc_scoq;
16903671d9d8SAndrew Thompson 		action = 0;
16913671d9d8SAndrew Thompson 		break;
16923671d9d8SAndrew Thompson 
16933671d9d8SAndrew Thompson 	default:
16943671d9d8SAndrew Thompson 		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
16953671d9d8SAndrew Thompson 			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
16963671d9d8SAndrew Thompson 
16973671d9d8SAndrew Thompson 		NG_FREE_M(m);
16983671d9d8SAndrew Thompson 		error = EINVAL;
16993671d9d8SAndrew Thompson 		goto done;
17003671d9d8SAndrew Thompson 		/* NOT REACHED */
17013671d9d8SAndrew Thompson 	}
17023671d9d8SAndrew Thompson 
17033671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
17043671d9d8SAndrew Thompson 	if (NG_BT_MBUFQ_FULL(q)) {
17053671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DROP(q);
17063671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
17073671d9d8SAndrew Thompson 
17083671d9d8SAndrew Thompson 		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
17093671d9d8SAndrew Thompson 			*mtod(m, uint8_t *), m->m_pkthdr.len);
17103671d9d8SAndrew Thompson 
17113671d9d8SAndrew Thompson 		NG_FREE_M(m);
17123671d9d8SAndrew Thompson 	} else {
17133671d9d8SAndrew Thompson 		/* Loose HCI packet type, enqueue mbuf and kick off task */
17143671d9d8SAndrew Thompson 		m_adj(m, sizeof(uint8_t));
17153671d9d8SAndrew Thompson 		NG_BT_MBUFQ_ENQUEUE(q, m);
17163671d9d8SAndrew Thompson 		ubt_task_schedule(sc, action);
17173671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
17183671d9d8SAndrew Thompson 	}
17193671d9d8SAndrew Thompson done:
17203671d9d8SAndrew Thompson 	NG_FREE_ITEM(item);
17213671d9d8SAndrew Thompson 
17223671d9d8SAndrew Thompson 	return (error);
17233671d9d8SAndrew Thompson } /* ng_ubt_rcvdata */
17243671d9d8SAndrew Thompson 
17253671d9d8SAndrew Thompson /****************************************************************************
17263671d9d8SAndrew Thompson  ****************************************************************************
17273671d9d8SAndrew Thompson  **                              Module
17283671d9d8SAndrew Thompson  ****************************************************************************
17293671d9d8SAndrew Thompson  ****************************************************************************/
17303671d9d8SAndrew Thompson 
17313671d9d8SAndrew Thompson /*
17323671d9d8SAndrew Thompson  * Load/Unload the driver module
17333671d9d8SAndrew Thompson  */
17343671d9d8SAndrew Thompson 
17353671d9d8SAndrew Thompson static int
17363671d9d8SAndrew Thompson ubt_modevent(module_t mod, int event, void *data)
17373671d9d8SAndrew Thompson {
17383671d9d8SAndrew Thompson 	int	error;
17393671d9d8SAndrew Thompson 
17403671d9d8SAndrew Thompson 	switch (event) {
17413671d9d8SAndrew Thompson 	case MOD_LOAD:
17423671d9d8SAndrew Thompson 		error = ng_newtype(&typestruct);
17433671d9d8SAndrew Thompson 		if (error != 0)
17443671d9d8SAndrew Thompson 			printf("%s: Could not register Netgraph node type, " \
17453671d9d8SAndrew Thompson 				"error=%d\n", NG_UBT_NODE_TYPE, error);
17463671d9d8SAndrew Thompson 		break;
17473671d9d8SAndrew Thompson 
17483671d9d8SAndrew Thompson 	case MOD_UNLOAD:
17493671d9d8SAndrew Thompson 		error = ng_rmtype(&typestruct);
17503671d9d8SAndrew Thompson 		break;
17513671d9d8SAndrew Thompson 
17523671d9d8SAndrew Thompson 	default:
17533671d9d8SAndrew Thompson 		error = EOPNOTSUPP;
17543671d9d8SAndrew Thompson 		break;
17553671d9d8SAndrew Thompson 	}
17563671d9d8SAndrew Thompson 
17573671d9d8SAndrew Thompson 	return (error);
17583671d9d8SAndrew Thompson } /* ubt_modevent */
17593671d9d8SAndrew Thompson 
17603671d9d8SAndrew Thompson static devclass_t	ubt_devclass;
17613671d9d8SAndrew Thompson 
17623671d9d8SAndrew Thompson static device_method_t	ubt_methods[] =
17633671d9d8SAndrew Thompson {
17643671d9d8SAndrew Thompson 	DEVMETHOD(device_probe,	ubt_probe),
17653671d9d8SAndrew Thompson 	DEVMETHOD(device_attach, ubt_attach),
17663671d9d8SAndrew Thompson 	DEVMETHOD(device_detach, ubt_detach),
17673671d9d8SAndrew Thompson 	{ 0, 0 }
17683671d9d8SAndrew Thompson };
17693671d9d8SAndrew Thompson 
17703671d9d8SAndrew Thompson static driver_t		ubt_driver =
17713671d9d8SAndrew Thompson {
17723671d9d8SAndrew Thompson 	.name =	   "ubt",
17733671d9d8SAndrew Thompson 	.methods = ubt_methods,
17743671d9d8SAndrew Thompson 	.size =	   sizeof(struct ubt_softc),
17753671d9d8SAndrew Thompson };
17763671d9d8SAndrew Thompson 
17773671d9d8SAndrew Thompson DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
17783671d9d8SAndrew Thompson MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
17793671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
17803671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
17813671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
17823671d9d8SAndrew Thompson 
1783