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 
953671d9d8SAndrew Thompson #include "usbdevs.h"
963671d9d8SAndrew Thompson #include <dev/usb/usb.h>
973671d9d8SAndrew Thompson #include <dev/usb/usb_mfunc.h>
983671d9d8SAndrew Thompson #include <dev/usb/usb_error.h>
993671d9d8SAndrew Thompson 
1003671d9d8SAndrew Thompson #define	USB_DEBUG_VAR usb2_debug
1013671d9d8SAndrew Thompson 
1023671d9d8SAndrew Thompson #include <dev/usb/usb_core.h>
1033671d9d8SAndrew Thompson #include <dev/usb/usb_debug.h>
1043671d9d8SAndrew Thompson #include <dev/usb/usb_parse.h>
1053671d9d8SAndrew Thompson #include <dev/usb/usb_lookup.h>
1063671d9d8SAndrew Thompson #include <dev/usb/usb_util.h>
1073671d9d8SAndrew Thompson #include <dev/usb/usb_busdma.h>
1083671d9d8SAndrew Thompson #include <dev/usb/usb_process.h>
1093671d9d8SAndrew Thompson #include <dev/usb/usb_transfer.h>
1103671d9d8SAndrew Thompson 
1113671d9d8SAndrew Thompson #include <sys/mbuf.h>
1123671d9d8SAndrew Thompson #include <sys/taskqueue.h>
1133671d9d8SAndrew Thompson 
1143671d9d8SAndrew Thompson #include <netgraph/ng_message.h>
1153671d9d8SAndrew Thompson #include <netgraph/netgraph.h>
1163671d9d8SAndrew Thompson #include <netgraph/ng_parse.h>
1173671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h>
1183671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_hci.h>
1193671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_ubt.h>
1203671d9d8SAndrew Thompson 
1213671d9d8SAndrew Thompson #include <dev/usb/bluetooth/ng_ubt_var.h>
1223671d9d8SAndrew Thompson 
1233671d9d8SAndrew Thompson static int		ubt_modevent(module_t, int, void *);
1243671d9d8SAndrew Thompson static device_probe_t	ubt_probe;
1253671d9d8SAndrew Thompson static device_attach_t	ubt_attach;
1263671d9d8SAndrew Thompson static device_detach_t	ubt_detach;
1273671d9d8SAndrew Thompson 
1283671d9d8SAndrew Thompson static void		ubt_task_schedule(ubt_softc_p, int);
1293671d9d8SAndrew Thompson static task_fn_t	ubt_task;
1303671d9d8SAndrew Thompson 
1313671d9d8SAndrew Thompson #define	ubt_xfer_start(sc, i)	usb2_transfer_start((sc)->sc_xfer[(i)])
1323671d9d8SAndrew Thompson 
1333671d9d8SAndrew Thompson /* Netgraph methods */
1343671d9d8SAndrew Thompson static ng_constructor_t	ng_ubt_constructor;
1353671d9d8SAndrew Thompson static ng_shutdown_t	ng_ubt_shutdown;
1363671d9d8SAndrew Thompson static ng_newhook_t	ng_ubt_newhook;
1373671d9d8SAndrew Thompson static ng_connect_t	ng_ubt_connect;
1383671d9d8SAndrew Thompson static ng_disconnect_t	ng_ubt_disconnect;
1393671d9d8SAndrew Thompson static ng_rcvmsg_t	ng_ubt_rcvmsg;
1403671d9d8SAndrew Thompson static ng_rcvdata_t	ng_ubt_rcvdata;
1413671d9d8SAndrew Thompson 
1423671d9d8SAndrew Thompson /* Queue length */
1433671d9d8SAndrew Thompson static const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
1443671d9d8SAndrew Thompson {
1453671d9d8SAndrew Thompson 	{ "queue", &ng_parse_int32_type, },
1463671d9d8SAndrew Thompson 	{ "qlen",  &ng_parse_int32_type, },
1473671d9d8SAndrew Thompson 	{ NULL, }
1483671d9d8SAndrew Thompson };
1493671d9d8SAndrew Thompson static const struct ng_parse_type		ng_ubt_node_qlen_type =
1503671d9d8SAndrew Thompson {
1513671d9d8SAndrew Thompson 	&ng_parse_struct_type,
1523671d9d8SAndrew Thompson 	&ng_ubt_node_qlen_type_fields
1533671d9d8SAndrew Thompson };
1543671d9d8SAndrew Thompson 
1553671d9d8SAndrew Thompson /* Stat info */
1563671d9d8SAndrew Thompson static const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
1573671d9d8SAndrew Thompson {
1583671d9d8SAndrew Thompson 	{ "pckts_recv", &ng_parse_uint32_type, },
1593671d9d8SAndrew Thompson 	{ "bytes_recv", &ng_parse_uint32_type, },
1603671d9d8SAndrew Thompson 	{ "pckts_sent", &ng_parse_uint32_type, },
1613671d9d8SAndrew Thompson 	{ "bytes_sent", &ng_parse_uint32_type, },
1623671d9d8SAndrew Thompson 	{ "oerrors",    &ng_parse_uint32_type, },
1633671d9d8SAndrew Thompson 	{ "ierrors",    &ng_parse_uint32_type, },
1643671d9d8SAndrew Thompson 	{ NULL, }
1653671d9d8SAndrew Thompson };
1663671d9d8SAndrew Thompson static const struct ng_parse_type		ng_ubt_node_stat_type =
1673671d9d8SAndrew Thompson {
1683671d9d8SAndrew Thompson 	&ng_parse_struct_type,
1693671d9d8SAndrew Thompson 	&ng_ubt_node_stat_type_fields
1703671d9d8SAndrew Thompson };
1713671d9d8SAndrew Thompson 
1723671d9d8SAndrew Thompson /* Netgraph node command list */
1733671d9d8SAndrew Thompson static const struct ng_cmdlist			ng_ubt_cmdlist[] =
1743671d9d8SAndrew Thompson {
1753671d9d8SAndrew Thompson 	{
1763671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1773671d9d8SAndrew Thompson 		NGM_UBT_NODE_SET_DEBUG,
1783671d9d8SAndrew Thompson 		"set_debug",
1793671d9d8SAndrew Thompson 		&ng_parse_uint16_type,
1803671d9d8SAndrew Thompson 		NULL
1813671d9d8SAndrew Thompson 	},
1823671d9d8SAndrew Thompson 	{
1833671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1843671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_DEBUG,
1853671d9d8SAndrew Thompson 		"get_debug",
1863671d9d8SAndrew Thompson 		NULL,
1873671d9d8SAndrew Thompson 		&ng_parse_uint16_type
1883671d9d8SAndrew Thompson 	},
1893671d9d8SAndrew Thompson 	{
1903671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1913671d9d8SAndrew Thompson 		NGM_UBT_NODE_SET_QLEN,
1923671d9d8SAndrew Thompson 		"set_qlen",
1933671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type,
1943671d9d8SAndrew Thompson 		NULL
1953671d9d8SAndrew Thompson 	},
1963671d9d8SAndrew Thompson 	{
1973671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1983671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_QLEN,
1993671d9d8SAndrew Thompson 		"get_qlen",
2003671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type,
2013671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type
2023671d9d8SAndrew Thompson 	},
2033671d9d8SAndrew Thompson 	{
2043671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2053671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_STAT,
2063671d9d8SAndrew Thompson 		"get_stat",
2073671d9d8SAndrew Thompson 		NULL,
2083671d9d8SAndrew Thompson 		&ng_ubt_node_stat_type
2093671d9d8SAndrew Thompson 	},
2103671d9d8SAndrew Thompson 	{
2113671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2123671d9d8SAndrew Thompson 		NGM_UBT_NODE_RESET_STAT,
2133671d9d8SAndrew Thompson 		"reset_stat",
2143671d9d8SAndrew Thompson 		NULL,
2153671d9d8SAndrew Thompson 		NULL
2163671d9d8SAndrew Thompson 	},
2173671d9d8SAndrew Thompson 	{ 0, }
2183671d9d8SAndrew Thompson };
2193671d9d8SAndrew Thompson 
2203671d9d8SAndrew Thompson /* Netgraph node type */
2213671d9d8SAndrew Thompson static struct ng_type	typestruct =
2223671d9d8SAndrew Thompson {
2233671d9d8SAndrew Thompson 	.version = 	NG_ABI_VERSION,
2243671d9d8SAndrew Thompson 	.name =		NG_UBT_NODE_TYPE,
2253671d9d8SAndrew Thompson 	.constructor =	ng_ubt_constructor,
2263671d9d8SAndrew Thompson 	.rcvmsg =	ng_ubt_rcvmsg,
2273671d9d8SAndrew Thompson 	.shutdown =	ng_ubt_shutdown,
2283671d9d8SAndrew Thompson 	.newhook =	ng_ubt_newhook,
2293671d9d8SAndrew Thompson 	.connect =	ng_ubt_connect,
2303671d9d8SAndrew Thompson 	.rcvdata =	ng_ubt_rcvdata,
2313671d9d8SAndrew Thompson 	.disconnect =	ng_ubt_disconnect,
2323671d9d8SAndrew Thompson 	.cmdlist =	ng_ubt_cmdlist
2333671d9d8SAndrew Thompson };
2343671d9d8SAndrew Thompson 
2353671d9d8SAndrew Thompson /****************************************************************************
2363671d9d8SAndrew Thompson  ****************************************************************************
2373671d9d8SAndrew Thompson  **                              USB specific
2383671d9d8SAndrew Thompson  ****************************************************************************
2393671d9d8SAndrew Thompson  ****************************************************************************/
2403671d9d8SAndrew Thompson 
2413671d9d8SAndrew Thompson /* USB methods */
2423671d9d8SAndrew Thompson static usb2_callback_t	ubt_ctrl_write_callback;
2433671d9d8SAndrew Thompson static usb2_callback_t	ubt_intr_read_callback;
2443671d9d8SAndrew Thompson static usb2_callback_t	ubt_bulk_read_callback;
2453671d9d8SAndrew Thompson static usb2_callback_t	ubt_bulk_write_callback;
2463671d9d8SAndrew Thompson static usb2_callback_t	ubt_isoc_read_callback;
2473671d9d8SAndrew Thompson static usb2_callback_t	ubt_isoc_write_callback;
2483671d9d8SAndrew Thompson 
2493671d9d8SAndrew Thompson static int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
2503671d9d8SAndrew Thompson static int		ubt_isoc_read_one_frame(struct usb2_xfer *, int);
2513671d9d8SAndrew Thompson 
2523671d9d8SAndrew Thompson /*
2533671d9d8SAndrew Thompson  * USB config
2543671d9d8SAndrew Thompson  *
2553671d9d8SAndrew Thompson  * The following desribes usb transfers that could be submitted on USB device.
2563671d9d8SAndrew Thompson  *
2573671d9d8SAndrew Thompson  * Interface 0 on the USB device must present the following endpoints
2583671d9d8SAndrew Thompson  *	1) Interrupt endpoint to receive HCI events
2593671d9d8SAndrew Thompson  *	2) Bulk IN endpoint to receive ACL data
2603671d9d8SAndrew Thompson  *	3) Bulk OUT endpoint to send ACL data
2613671d9d8SAndrew Thompson  *
2623671d9d8SAndrew Thompson  * Interface 1 on the USB device must present the following endpoints
2633671d9d8SAndrew Thompson  *	1) Isochronous IN endpoint to receive SCO data
2643671d9d8SAndrew Thompson  *	2) Isochronous OUT endpoint to send SCO data
2653671d9d8SAndrew Thompson  */
2663671d9d8SAndrew Thompson 
2673671d9d8SAndrew Thompson static const struct usb2_config		ubt_config[UBT_N_TRANSFER] =
2683671d9d8SAndrew Thompson {
2693671d9d8SAndrew Thompson 	/*
2703671d9d8SAndrew Thompson 	 * Interface #0
2713671d9d8SAndrew Thompson  	 */
2723671d9d8SAndrew Thompson 
2733671d9d8SAndrew Thompson 	/* Outgoing bulk transfer - ACL packets */
2743671d9d8SAndrew Thompson 	[UBT_IF_0_BULK_DT_WR] = {
2753671d9d8SAndrew Thompson 		.type =		UE_BULK,
2763671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
2773671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
2783671d9d8SAndrew Thompson 		.if_index = 	0,
2793671d9d8SAndrew Thompson 		.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
2803671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
2813671d9d8SAndrew Thompson 		.callback =	&ubt_bulk_write_callback,
2823671d9d8SAndrew Thompson 	},
2833671d9d8SAndrew Thompson 	/* Incoming bulk transfer - ACL packets */
2843671d9d8SAndrew Thompson 	[UBT_IF_0_BULK_DT_RD] = {
2853671d9d8SAndrew Thompson 		.type =		UE_BULK,
2863671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
2873671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
2883671d9d8SAndrew Thompson 		.if_index = 	0,
2893671d9d8SAndrew Thompson 		.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
2903671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
2913671d9d8SAndrew Thompson 		.callback =	&ubt_bulk_read_callback,
2923671d9d8SAndrew Thompson 	},
2933671d9d8SAndrew Thompson 	/* Incoming interrupt transfer - HCI events */
2943671d9d8SAndrew Thompson 	[UBT_IF_0_INTR_DT_RD] = {
2953671d9d8SAndrew Thompson 		.type =		UE_INTERRUPT,
2963671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
2973671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
2983671d9d8SAndrew Thompson 		.if_index = 	0,
2993671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
3003671d9d8SAndrew Thompson 		.bufsize =	UBT_INTR_BUFFER_SIZE,
3013671d9d8SAndrew Thompson 		.callback =	&ubt_intr_read_callback,
3023671d9d8SAndrew Thompson 	},
3033671d9d8SAndrew Thompson 	/* Outgoing control transfer - HCI commands */
3043671d9d8SAndrew Thompson 	[UBT_IF_0_CTRL_DT_WR] = {
3053671d9d8SAndrew Thompson 		.type =		UE_CONTROL,
3063671d9d8SAndrew Thompson 		.endpoint =	0x00,	/* control pipe */
3073671d9d8SAndrew Thompson 		.direction =	UE_DIR_ANY,
3083671d9d8SAndrew Thompson 		.if_index = 	0,
3093671d9d8SAndrew Thompson 		.bufsize =	UBT_CTRL_BUFFER_SIZE,
3103671d9d8SAndrew Thompson 		.callback =	&ubt_ctrl_write_callback,
3113671d9d8SAndrew Thompson 		.timeout =	5000,	/* 5 seconds */
3123671d9d8SAndrew Thompson 	},
3133671d9d8SAndrew Thompson 
3143671d9d8SAndrew Thompson 	/*
3153671d9d8SAndrew Thompson 	 * Interface #1
3163671d9d8SAndrew Thompson  	 */
3173671d9d8SAndrew Thompson 
3183671d9d8SAndrew Thompson 	/* Incoming isochronous transfer #1 - SCO packets */
3193671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_RD1] = {
3203671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3213671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3223671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3233671d9d8SAndrew Thompson 		.if_index = 	1,
3243671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3253671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3263671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3273671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_read_callback,
3283671d9d8SAndrew Thompson 	},
3293671d9d8SAndrew Thompson 	/* Incoming isochronous transfer #2 - SCO packets */
3303671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_RD2] = {
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 	/* Outgoing isochronous transfer #1 - SCO packets */
3413671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_WR1] = {
3423671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3433671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3443671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
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_write_callback,
3503671d9d8SAndrew Thompson 	},
3513671d9d8SAndrew Thompson 	/* Outgoing isochronous transfer #2 - SCO packets */
3523671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_WR2] = {
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 };
3633671d9d8SAndrew Thompson 
3643671d9d8SAndrew Thompson /*
3653671d9d8SAndrew Thompson  * If for some reason device should not be attached then put
3663671d9d8SAndrew Thompson  * VendorID/ProductID pair into the list below. The format is
3673671d9d8SAndrew Thompson  * as follows:
3683671d9d8SAndrew Thompson  *
3693671d9d8SAndrew Thompson  *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
3703671d9d8SAndrew Thompson  *
3713671d9d8SAndrew Thompson  * where VENDOR_ID and PRODUCT_ID are hex numbers.
3723671d9d8SAndrew Thompson  */
3733671d9d8SAndrew Thompson 
3743671d9d8SAndrew Thompson static const struct usb2_device_id ubt_ignore_devs[] =
3753671d9d8SAndrew Thompson {
3763671d9d8SAndrew Thompson 	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
3773671d9d8SAndrew Thompson 	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
3783671d9d8SAndrew Thompson };
3793671d9d8SAndrew Thompson 
3803671d9d8SAndrew Thompson /* List of supported bluetooth devices */
3813671d9d8SAndrew Thompson static const struct usb2_device_id ubt_devs[] =
3823671d9d8SAndrew Thompson {
3833671d9d8SAndrew Thompson 	/* Generic Bluetooth class devices */
3843671d9d8SAndrew Thompson 	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
3853671d9d8SAndrew Thompson 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
3863671d9d8SAndrew Thompson 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
3873671d9d8SAndrew Thompson 
3883671d9d8SAndrew Thompson 	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
3893671d9d8SAndrew Thompson 	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
3903671d9d8SAndrew Thompson };
3913671d9d8SAndrew Thompson 
3923671d9d8SAndrew Thompson /*
3933671d9d8SAndrew Thompson  * Probe for a USB Bluetooth device.
3943671d9d8SAndrew Thompson  * USB context.
3953671d9d8SAndrew Thompson  */
3963671d9d8SAndrew Thompson 
3973671d9d8SAndrew Thompson static int
3983671d9d8SAndrew Thompson ubt_probe(device_t dev)
3993671d9d8SAndrew Thompson {
4003671d9d8SAndrew Thompson 	struct usb2_attach_arg	*uaa = device_get_ivars(dev);
4013671d9d8SAndrew Thompson 
4023671d9d8SAndrew Thompson 	if (uaa->usb_mode != USB_MODE_HOST)
4033671d9d8SAndrew Thompson 		return (ENXIO);
4043671d9d8SAndrew Thompson 
4053671d9d8SAndrew Thompson 	if (uaa->info.bIfaceIndex != 0)
4063671d9d8SAndrew Thompson 		return (ENXIO);
4073671d9d8SAndrew Thompson 
4083671d9d8SAndrew Thompson 	if (uaa->use_generic == 0)
4093671d9d8SAndrew Thompson 		return (ENXIO);
4103671d9d8SAndrew Thompson 
4113671d9d8SAndrew Thompson 	if (usb2_lookup_id_by_uaa(ubt_ignore_devs,
4123671d9d8SAndrew Thompson 			sizeof(ubt_ignore_devs), uaa) == 0)
4133671d9d8SAndrew Thompson 		return (ENXIO);
4143671d9d8SAndrew Thompson 
4153671d9d8SAndrew Thompson 	return (usb2_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa));
4163671d9d8SAndrew Thompson } /* ubt_probe */
4173671d9d8SAndrew Thompson 
4183671d9d8SAndrew Thompson /*
4193671d9d8SAndrew Thompson  * Attach the device.
4203671d9d8SAndrew Thompson  * USB context.
4213671d9d8SAndrew Thompson  */
4223671d9d8SAndrew Thompson 
4233671d9d8SAndrew Thompson static int
4243671d9d8SAndrew Thompson ubt_attach(device_t dev)
4253671d9d8SAndrew Thompson {
4263671d9d8SAndrew Thompson 	struct usb2_attach_arg		*uaa = device_get_ivars(dev);
4273671d9d8SAndrew Thompson 	struct ubt_softc		*sc = device_get_softc(dev);
4283671d9d8SAndrew Thompson 	struct usb2_endpoint_descriptor	*ed;
4293671d9d8SAndrew Thompson 	struct usb2_interface_descriptor *id;
4303671d9d8SAndrew Thompson 	uint16_t			wMaxPacketSize;
4313671d9d8SAndrew Thompson 	uint8_t				alt_index, i, j;
4323671d9d8SAndrew Thompson 	uint8_t				iface_index[2] = { 0, 1 };
4333671d9d8SAndrew Thompson 
4343671d9d8SAndrew Thompson 	device_set_usb2_desc(dev);
4353671d9d8SAndrew Thompson 
4363671d9d8SAndrew Thompson 	sc->sc_dev = dev;
4373671d9d8SAndrew Thompson 	sc->sc_debug = NG_UBT_WARN_LEVEL;
4383671d9d8SAndrew Thompson 
4393671d9d8SAndrew Thompson 	/*
4403671d9d8SAndrew Thompson 	 * Create Netgraph node
4413671d9d8SAndrew Thompson 	 */
4423671d9d8SAndrew Thompson 
4433671d9d8SAndrew Thompson 	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
4443671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not create Netgraph node\n");
4453671d9d8SAndrew Thompson 		return (ENXIO);
4463671d9d8SAndrew Thompson 	}
4473671d9d8SAndrew Thompson 
4483671d9d8SAndrew Thompson 	/* Name Netgraph node */
4493671d9d8SAndrew Thompson 	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
4503671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not name Netgraph node\n");
4513671d9d8SAndrew Thompson 		NG_NODE_UNREF(sc->sc_node);
4523671d9d8SAndrew Thompson 		return (ENXIO);
4533671d9d8SAndrew Thompson 	}
4543671d9d8SAndrew Thompson 	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
4553671d9d8SAndrew Thompson 	NG_NODE_FORCE_WRITER(sc->sc_node);
4563671d9d8SAndrew Thompson 
4573671d9d8SAndrew Thompson 	/*
4583671d9d8SAndrew Thompson 	 * Initialize device softc structure
4593671d9d8SAndrew Thompson 	 */
4603671d9d8SAndrew Thompson 
4613671d9d8SAndrew Thompson 	/* initialize locks */
4623671d9d8SAndrew Thompson 	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
4633671d9d8SAndrew Thompson 	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
4643671d9d8SAndrew Thompson 
4653671d9d8SAndrew Thompson 	/* initialize packet queues */
4663671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
4673671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
4683671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
4693671d9d8SAndrew Thompson 
4703671d9d8SAndrew Thompson 	/* initialize glue task */
4713671d9d8SAndrew Thompson 	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
4723671d9d8SAndrew Thompson 
4733671d9d8SAndrew Thompson 	/*
4743671d9d8SAndrew Thompson 	 * Configure Bluetooth USB device. Discover all required USB
4753671d9d8SAndrew Thompson 	 * interfaces and endpoints.
4763671d9d8SAndrew Thompson 	 *
4773671d9d8SAndrew Thompson 	 * USB device must present two interfaces:
4783671d9d8SAndrew Thompson 	 * 1) Interface 0 that has 3 endpoints
4793671d9d8SAndrew Thompson 	 *	1) Interrupt endpoint to receive HCI events
4803671d9d8SAndrew Thompson 	 *	2) Bulk IN endpoint to receive ACL data
4813671d9d8SAndrew Thompson 	 *	3) Bulk OUT endpoint to send ACL data
4823671d9d8SAndrew Thompson 	 *
4833671d9d8SAndrew Thompson 	 * 2) Interface 1 then has 2 endpoints
4843671d9d8SAndrew Thompson 	 *	1) Isochronous IN endpoint to receive SCO data
4853671d9d8SAndrew Thompson  	 *	2) Isochronous OUT endpoint to send SCO data
4863671d9d8SAndrew Thompson 	 *
4873671d9d8SAndrew Thompson 	 * Interface 1 (with isochronous endpoints) has several alternate
4883671d9d8SAndrew Thompson 	 * configurations with different packet size.
4893671d9d8SAndrew Thompson 	 */
4903671d9d8SAndrew Thompson 
4913671d9d8SAndrew Thompson 	/*
4923671d9d8SAndrew Thompson 	 * For interface #1 search alternate settings, and find
4933671d9d8SAndrew Thompson 	 * the descriptor with the largest wMaxPacketSize
4943671d9d8SAndrew Thompson 	 */
4953671d9d8SAndrew Thompson 
4963671d9d8SAndrew Thompson 	wMaxPacketSize = 0;
4973671d9d8SAndrew Thompson 	alt_index = 0;
4983671d9d8SAndrew Thompson 	i = 0;
4993671d9d8SAndrew Thompson 	j = 0;
5003671d9d8SAndrew Thompson 	ed = NULL;
5013671d9d8SAndrew Thompson 
5023671d9d8SAndrew Thompson 	/*
5033671d9d8SAndrew Thompson 	 * Search through all the descriptors looking for the largest
5043671d9d8SAndrew Thompson 	 * packet size:
5053671d9d8SAndrew Thompson 	 */
5063671d9d8SAndrew Thompson 	while ((ed = (struct usb2_endpoint_descriptor *)usb2_desc_foreach(
5073671d9d8SAndrew Thompson 	    usb2_get_config_descriptor(uaa->device),
5083671d9d8SAndrew Thompson 	    (struct usb2_descriptor *)ed))) {
5093671d9d8SAndrew Thompson 
5103671d9d8SAndrew Thompson 		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
5113671d9d8SAndrew Thompson 		    (ed->bLength >= sizeof(*id))) {
5123671d9d8SAndrew Thompson 			id = (struct usb2_interface_descriptor *)ed;
5133671d9d8SAndrew Thompson 			i = id->bInterfaceNumber;
5143671d9d8SAndrew Thompson 			j = id->bAlternateSetting;
5153671d9d8SAndrew Thompson 		}
5163671d9d8SAndrew Thompson 
5173671d9d8SAndrew Thompson 		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
5183671d9d8SAndrew Thompson 		    (ed->bLength >= sizeof(*ed)) &&
5193671d9d8SAndrew Thompson 		    (i == 1)) {
5203671d9d8SAndrew Thompson 			uint16_t temp;
5213671d9d8SAndrew Thompson 
5223671d9d8SAndrew Thompson 			temp = UGETW(ed->wMaxPacketSize);
5233671d9d8SAndrew Thompson 			if (temp > wMaxPacketSize) {
5243671d9d8SAndrew Thompson 				wMaxPacketSize = temp;
5253671d9d8SAndrew Thompson 				alt_index = j;
5263671d9d8SAndrew Thompson 			}
5273671d9d8SAndrew Thompson 		}
5283671d9d8SAndrew Thompson 	}
5293671d9d8SAndrew Thompson 
5303671d9d8SAndrew Thompson 	/* Set alt configuration on interface #1 only if we found it */
5313671d9d8SAndrew Thompson 	if (wMaxPacketSize > 0 &&
5323671d9d8SAndrew Thompson 	    usb2_set_alt_interface_index(uaa->device, 1, alt_index)) {
5333671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not set alternate setting %d " \
5343671d9d8SAndrew Thompson 			"for interface 1!\n", alt_index);
5353671d9d8SAndrew Thompson 		goto detach;
5363671d9d8SAndrew Thompson 	}
5373671d9d8SAndrew Thompson 
5383671d9d8SAndrew Thompson 	/* Setup transfers for both interfaces */
5393671d9d8SAndrew Thompson 	if (usb2_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
5403671d9d8SAndrew Thompson 			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
5413671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not allocate transfers\n");
5423671d9d8SAndrew Thompson 		goto detach;
5433671d9d8SAndrew Thompson 	}
5443671d9d8SAndrew Thompson 
5453671d9d8SAndrew Thompson 	/* Claim all interfaces on the device */
5463671d9d8SAndrew Thompson 	for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++)
5473671d9d8SAndrew Thompson 		usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
5483671d9d8SAndrew Thompson 
5493671d9d8SAndrew Thompson 	return (0); /* success */
5503671d9d8SAndrew Thompson 
5513671d9d8SAndrew Thompson detach:
5523671d9d8SAndrew Thompson 	ubt_detach(dev);
5533671d9d8SAndrew Thompson 
5543671d9d8SAndrew Thompson 	return (ENXIO);
5553671d9d8SAndrew Thompson } /* ubt_attach */
5563671d9d8SAndrew Thompson 
5573671d9d8SAndrew Thompson /*
5583671d9d8SAndrew Thompson  * Detach the device.
5593671d9d8SAndrew Thompson  * USB context.
5603671d9d8SAndrew Thompson  */
5613671d9d8SAndrew Thompson 
5623671d9d8SAndrew Thompson int
5633671d9d8SAndrew Thompson ubt_detach(device_t dev)
5643671d9d8SAndrew Thompson {
5653671d9d8SAndrew Thompson 	struct ubt_softc	*sc = device_get_softc(dev);
5663671d9d8SAndrew Thompson 	node_p			node = sc->sc_node;
5673671d9d8SAndrew Thompson 
5683671d9d8SAndrew Thompson 	/* Destroy Netgraph node */
5693671d9d8SAndrew Thompson 	if (node != NULL) {
5703671d9d8SAndrew Thompson 		sc->sc_node = NULL;
5713671d9d8SAndrew Thompson 		NG_NODE_REALLY_DIE(node);
5723671d9d8SAndrew Thompson 		ng_rmnode_self(node);
5733671d9d8SAndrew Thompson 	}
5743671d9d8SAndrew Thompson 
5753671d9d8SAndrew Thompson 	/* Make sure ubt_task in gone */
5763671d9d8SAndrew Thompson 	taskqueue_drain(taskqueue_swi, &sc->sc_task);
5773671d9d8SAndrew Thompson 
5783671d9d8SAndrew Thompson 	/* Free USB transfers, if any */
5793671d9d8SAndrew Thompson 	usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
5803671d9d8SAndrew Thompson 
5813671d9d8SAndrew Thompson 	/* Destroy queues */
5823671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
5833671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
5843671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
5853671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
5863671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
5873671d9d8SAndrew Thompson 
5883671d9d8SAndrew Thompson 	mtx_destroy(&sc->sc_if_mtx);
5893671d9d8SAndrew Thompson 	mtx_destroy(&sc->sc_ng_mtx);
5903671d9d8SAndrew Thompson 
5913671d9d8SAndrew Thompson 	return (0);
5923671d9d8SAndrew Thompson } /* ubt_detach */
5933671d9d8SAndrew Thompson 
5943671d9d8SAndrew Thompson /*
5953671d9d8SAndrew Thompson  * Called when outgoing control request (HCI command) has completed, i.e.
5963671d9d8SAndrew Thompson  * HCI command was sent to the device.
5973671d9d8SAndrew Thompson  * USB context.
5983671d9d8SAndrew Thompson  */
5993671d9d8SAndrew Thompson 
6003671d9d8SAndrew Thompson static void
6013671d9d8SAndrew Thompson ubt_ctrl_write_callback(struct usb2_xfer *xfer)
6023671d9d8SAndrew Thompson {
6033671d9d8SAndrew Thompson 	struct ubt_softc		*sc = xfer->priv_sc;
6043671d9d8SAndrew Thompson 	struct usb2_device_request	req;
6053671d9d8SAndrew Thompson 	struct mbuf			*m;
6063671d9d8SAndrew Thompson 
6073671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
6083671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
6093671d9d8SAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to control pipe\n", xfer->actlen);
6103671d9d8SAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, xfer->actlen);
6113671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
6123671d9d8SAndrew Thompson 		/* FALLTHROUGH */
6133671d9d8SAndrew Thompson 
6143671d9d8SAndrew Thompson 	case USB_ST_SETUP:
6153671d9d8SAndrew Thompson send_next:
6163671d9d8SAndrew Thompson 		/* Get next command mbuf, if any */
6173671d9d8SAndrew Thompson 		UBT_NG_LOCK(sc);
6183671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
6193671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
6203671d9d8SAndrew Thompson 
6213671d9d8SAndrew Thompson 		if (m == NULL) {
6223671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI command queue is empty\n");
6233671d9d8SAndrew Thompson 			break;	/* transfer complete */
6243671d9d8SAndrew Thompson 		}
6253671d9d8SAndrew Thompson 
6263671d9d8SAndrew Thompson 		/* Initialize a USB control request and then schedule it */
6273671d9d8SAndrew Thompson 		bzero(&req, sizeof(req));
6283671d9d8SAndrew Thompson 		req.bmRequestType = UBT_HCI_REQUEST;
6293671d9d8SAndrew Thompson 		USETW(req.wLength, m->m_pkthdr.len);
6303671d9d8SAndrew Thompson 
6313671d9d8SAndrew Thompson 		UBT_INFO(sc, "Sending control request, " \
6323671d9d8SAndrew Thompson 			"bmRequestType=0x%02x, wLength=%d\n",
6333671d9d8SAndrew Thompson 			req.bmRequestType, UGETW(req.wLength));
6343671d9d8SAndrew Thompson 
6353671d9d8SAndrew Thompson 		usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
6363671d9d8SAndrew Thompson 		usb2_m_copy_in(xfer->frbuffers + 1, 0, m, 0, m->m_pkthdr.len);
6373671d9d8SAndrew Thompson 
6383671d9d8SAndrew Thompson 		xfer->frlengths[0] = sizeof(req);
6393671d9d8SAndrew Thompson 		xfer->frlengths[1] = m->m_pkthdr.len;
6403671d9d8SAndrew Thompson 		xfer->nframes = 2;
6413671d9d8SAndrew Thompson 
6423671d9d8SAndrew Thompson 		NG_FREE_M(m);
6433671d9d8SAndrew Thompson 
6443671d9d8SAndrew Thompson 		usb2_start_hardware(xfer);
6453671d9d8SAndrew Thompson 		break;
6463671d9d8SAndrew Thompson 
6473671d9d8SAndrew Thompson 	default: /* Error */
6483671d9d8SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
6493671d9d8SAndrew Thompson 			UBT_WARN(sc, "control transfer failed: %s\n",
6503671d9d8SAndrew Thompson 				usb2_errstr(xfer->error));
6513671d9d8SAndrew Thompson 
6523671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
6533671d9d8SAndrew Thompson 			goto send_next;
6543671d9d8SAndrew Thompson 		}
6553671d9d8SAndrew Thompson 
6563671d9d8SAndrew Thompson 		/* transfer cancelled */
6573671d9d8SAndrew Thompson 		break;
6583671d9d8SAndrew Thompson 	}
6593671d9d8SAndrew Thompson } /* ubt_ctrl_write_callback */
6603671d9d8SAndrew Thompson 
6613671d9d8SAndrew Thompson /*
6623671d9d8SAndrew Thompson  * Called when incoming interrupt transfer (HCI event) has completed, i.e.
6633671d9d8SAndrew Thompson  * HCI event was received from the device.
6643671d9d8SAndrew Thompson  * USB context.
6653671d9d8SAndrew Thompson  */
6663671d9d8SAndrew Thompson 
6673671d9d8SAndrew Thompson static void
6683671d9d8SAndrew Thompson ubt_intr_read_callback(struct usb2_xfer *xfer)
6693671d9d8SAndrew Thompson {
6703671d9d8SAndrew Thompson 	struct ubt_softc	*sc = xfer->priv_sc;
6713671d9d8SAndrew Thompson 	struct mbuf		*m;
6723671d9d8SAndrew Thompson 	ng_hci_event_pkt_t	*hdr;
6733671d9d8SAndrew Thompson 
6743671d9d8SAndrew Thompson 	m = NULL;
6753671d9d8SAndrew Thompson 
6763671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
6773671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
6783671d9d8SAndrew Thompson 		/* Allocate a new mbuf */
6793671d9d8SAndrew Thompson 		MGETHDR(m, M_DONTWAIT, MT_DATA);
6803671d9d8SAndrew Thompson 		if (m == NULL) {
6813671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
6823671d9d8SAndrew Thompson 			goto submit_next;
6833671d9d8SAndrew Thompson 		}
6843671d9d8SAndrew Thompson 
6853671d9d8SAndrew Thompson 		MCLGET(m, M_DONTWAIT);
6863671d9d8SAndrew Thompson 		if (!(m->m_flags & M_EXT)) {
6873671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
6883671d9d8SAndrew Thompson 			goto submit_next;
6893671d9d8SAndrew Thompson 		}
6903671d9d8SAndrew Thompson 
6913671d9d8SAndrew Thompson 		/* Add HCI packet type */
6923671d9d8SAndrew Thompson 		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
6933671d9d8SAndrew Thompson 		m->m_pkthdr.len = m->m_len = 1;
6943671d9d8SAndrew Thompson 
6953671d9d8SAndrew Thompson 		if (xfer->actlen > MCLBYTES - 1)
6963671d9d8SAndrew Thompson 			xfer->actlen = MCLBYTES - 1;
6973671d9d8SAndrew Thompson 
6983671d9d8SAndrew Thompson 		usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
6993671d9d8SAndrew Thompson 			xfer->actlen);
7003671d9d8SAndrew Thompson 		m->m_pkthdr.len += xfer->actlen;
7013671d9d8SAndrew Thompson 		m->m_len += xfer->actlen;
7023671d9d8SAndrew Thompson 
7033671d9d8SAndrew Thompson 		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
7043671d9d8SAndrew Thompson 			xfer->actlen);
7053671d9d8SAndrew Thompson 
7063671d9d8SAndrew Thompson 		/* Validate packet and send it up the stack */
7073671d9d8SAndrew Thompson 		if (m->m_pkthdr.len < sizeof(*hdr)) {
7083671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI event packet is too short\n");
7093671d9d8SAndrew Thompson 
7103671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7113671d9d8SAndrew Thompson 			goto submit_next;
7123671d9d8SAndrew Thompson 		}
7133671d9d8SAndrew Thompson 
7143671d9d8SAndrew Thompson 		hdr = mtod(m, ng_hci_event_pkt_t *);
7153671d9d8SAndrew Thompson 		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
7163671d9d8SAndrew Thompson 			UBT_ERR(sc, "Invalid HCI event packet size, " \
7173671d9d8SAndrew Thompson 				"length=%d, pktlen=%d\n",
7183671d9d8SAndrew Thompson 				hdr->length, m->m_pkthdr.len);
7193671d9d8SAndrew Thompson 
7203671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7213671d9d8SAndrew Thompson 			goto submit_next;
7223671d9d8SAndrew Thompson 		}
7233671d9d8SAndrew Thompson 
7243671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
7253671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len, hdr->length);
7263671d9d8SAndrew Thompson 
7273671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
7283671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
7293671d9d8SAndrew Thompson 
7303671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
7313671d9d8SAndrew Thompson 		/* m == NULL at this point */
7323671d9d8SAndrew Thompson 		/* FALLTHROUGH */
7333671d9d8SAndrew Thompson 
7343671d9d8SAndrew Thompson 	case USB_ST_SETUP:
7353671d9d8SAndrew Thompson submit_next:
7363671d9d8SAndrew Thompson 		NG_FREE_M(m); /* checks for m != NULL */
7373671d9d8SAndrew Thompson 
7383671d9d8SAndrew Thompson 		xfer->frlengths[0] = xfer->max_data_length;
7393671d9d8SAndrew Thompson 		usb2_start_hardware(xfer);
7403671d9d8SAndrew Thompson 		break;
7413671d9d8SAndrew Thompson 
7423671d9d8SAndrew Thompson 	default: /* Error */
7433671d9d8SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
7443671d9d8SAndrew Thompson 			UBT_WARN(sc, "interrupt transfer failed: %s\n",
7453671d9d8SAndrew Thompson 				usb2_errstr(xfer->error));
7463671d9d8SAndrew Thompson 
7473671d9d8SAndrew Thompson 			/* Try to clear stall first */
7483671d9d8SAndrew Thompson 			xfer->flags.stall_pipe = 1;
7493671d9d8SAndrew Thompson 			goto submit_next;
7503671d9d8SAndrew Thompson 		}
7513671d9d8SAndrew Thompson 			/* transfer cancelled */
7523671d9d8SAndrew Thompson 		break;
7533671d9d8SAndrew Thompson 	}
7543671d9d8SAndrew Thompson } /* ubt_intr_read_callback */
7553671d9d8SAndrew Thompson 
7563671d9d8SAndrew Thompson /*
7573671d9d8SAndrew Thompson  * Called when incoming bulk transfer (ACL packet) has completed, i.e.
7583671d9d8SAndrew Thompson  * ACL packet was received from the device.
7593671d9d8SAndrew Thompson  * USB context.
7603671d9d8SAndrew Thompson  */
7613671d9d8SAndrew Thompson 
7623671d9d8SAndrew Thompson static void
7633671d9d8SAndrew Thompson ubt_bulk_read_callback(struct usb2_xfer *xfer)
7643671d9d8SAndrew Thompson {
7653671d9d8SAndrew Thompson 	struct ubt_softc	*sc = xfer->priv_sc;
7663671d9d8SAndrew Thompson 	struct mbuf		*m;
7673671d9d8SAndrew Thompson 	ng_hci_acldata_pkt_t	*hdr;
7683671d9d8SAndrew Thompson 	uint16_t		len;
7693671d9d8SAndrew Thompson 
7703671d9d8SAndrew Thompson 	m = NULL;
7713671d9d8SAndrew Thompson 
7723671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
7733671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
7743671d9d8SAndrew Thompson 		/* Allocate new mbuf */
7753671d9d8SAndrew Thompson 		MGETHDR(m, M_DONTWAIT, MT_DATA);
7763671d9d8SAndrew Thompson 		if (m == NULL) {
7773671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7783671d9d8SAndrew Thompson 			goto submit_next;
7793671d9d8SAndrew Thompson 		}
7803671d9d8SAndrew Thompson 
7813671d9d8SAndrew Thompson 		MCLGET(m, M_DONTWAIT);
7823671d9d8SAndrew Thompson 		if (!(m->m_flags & M_EXT)) {
7833671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
7843671d9d8SAndrew Thompson 			goto submit_next;
7853671d9d8SAndrew Thompson 		}
7863671d9d8SAndrew Thompson 
7873671d9d8SAndrew Thompson 		/* Add HCI packet type */
7883671d9d8SAndrew Thompson 		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
7893671d9d8SAndrew Thompson 		m->m_pkthdr.len = m->m_len = 1;
7903671d9d8SAndrew Thompson 
7913671d9d8SAndrew Thompson 		if (xfer->actlen > MCLBYTES - 1)
7923671d9d8SAndrew Thompson 			xfer->actlen = MCLBYTES - 1;
7933671d9d8SAndrew Thompson 
7943671d9d8SAndrew Thompson 		usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
7953671d9d8SAndrew Thompson 			xfer->actlen);
7963671d9d8SAndrew Thompson 		m->m_pkthdr.len += xfer->actlen;
7973671d9d8SAndrew Thompson 		m->m_len += xfer->actlen;
7983671d9d8SAndrew Thompson 
7993671d9d8SAndrew Thompson 		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
8003671d9d8SAndrew Thompson 			xfer->actlen);
8013671d9d8SAndrew Thompson 
8023671d9d8SAndrew Thompson 		/* Validate packet and send it up the stack */
8033671d9d8SAndrew Thompson 		if (m->m_pkthdr.len < sizeof(*hdr)) {
8043671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI ACL packet is too short\n");
8053671d9d8SAndrew Thompson 
8063671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8073671d9d8SAndrew Thompson 			goto submit_next;
8083671d9d8SAndrew Thompson 		}
8093671d9d8SAndrew Thompson 
8103671d9d8SAndrew Thompson 		hdr = mtod(m, ng_hci_acldata_pkt_t *);
8113671d9d8SAndrew Thompson 		len = le16toh(hdr->length);
8123671d9d8SAndrew Thompson 		if (len != (m->m_pkthdr.len - sizeof(*hdr))) {
8133671d9d8SAndrew Thompson 			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
8143671d9d8SAndrew Thompson 				"pktlen=%d\n", len, m->m_pkthdr.len);
8153671d9d8SAndrew Thompson 
8163671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
8173671d9d8SAndrew Thompson 			goto submit_next;
8183671d9d8SAndrew Thompson 		}
8193671d9d8SAndrew Thompson 
8203671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
8213671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len, len);
8223671d9d8SAndrew Thompson 
8233671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
8243671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
8253671d9d8SAndrew Thompson 
8263671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
8273671d9d8SAndrew Thompson 		/* m == NULL at this point */
8283671d9d8SAndrew Thompson 		/* FALLTHOUGH */
8293671d9d8SAndrew Thompson 
8303671d9d8SAndrew Thompson 	case USB_ST_SETUP:
8313671d9d8SAndrew Thompson submit_next:
8323671d9d8SAndrew Thompson 		NG_FREE_M(m); /* checks for m != NULL */
8333671d9d8SAndrew Thompson 
8343671d9d8SAndrew Thompson 		xfer->frlengths[0] = xfer->max_data_length;
8353671d9d8SAndrew Thompson 		usb2_start_hardware(xfer);
8363671d9d8SAndrew Thompson 		break;
8373671d9d8SAndrew Thompson 
8383671d9d8SAndrew Thompson 	default: /* Error */
8393671d9d8SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
8403671d9d8SAndrew Thompson 			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
8413671d9d8SAndrew Thompson 				usb2_errstr(xfer->error));
8423671d9d8SAndrew Thompson 
8433671d9d8SAndrew Thompson 			/* Try to clear stall first */
8443671d9d8SAndrew Thompson 			xfer->flags.stall_pipe = 1;
8453671d9d8SAndrew Thompson 			goto submit_next;
8463671d9d8SAndrew Thompson 		}
8473671d9d8SAndrew Thompson 			/* transfer cancelled */
8483671d9d8SAndrew Thompson 		break;
8493671d9d8SAndrew Thompson 	}
8503671d9d8SAndrew Thompson } /* ubt_bulk_read_callback */
8513671d9d8SAndrew Thompson 
8523671d9d8SAndrew Thompson /*
8533671d9d8SAndrew Thompson  * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
8543671d9d8SAndrew Thompson  * ACL packet was sent to the device.
8553671d9d8SAndrew Thompson  * USB context.
8563671d9d8SAndrew Thompson  */
8573671d9d8SAndrew Thompson 
8583671d9d8SAndrew Thompson static void
8593671d9d8SAndrew Thompson ubt_bulk_write_callback(struct usb2_xfer *xfer)
8603671d9d8SAndrew Thompson {
8613671d9d8SAndrew Thompson 	struct ubt_softc	*sc = xfer->priv_sc;
8623671d9d8SAndrew Thompson 	struct mbuf		*m;
8633671d9d8SAndrew Thompson 
8643671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
8653671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
8663671d9d8SAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", xfer->actlen);
8673671d9d8SAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, xfer->actlen);
8683671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
8693671d9d8SAndrew Thompson 		/* FALLTHROUGH */
8703671d9d8SAndrew Thompson 
8713671d9d8SAndrew Thompson 	case USB_ST_SETUP:
8723671d9d8SAndrew Thompson send_next:
8733671d9d8SAndrew Thompson 		/* Get next mbuf, if any */
8743671d9d8SAndrew Thompson 		UBT_NG_LOCK(sc);
8753671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
8763671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
8773671d9d8SAndrew Thompson 
8783671d9d8SAndrew Thompson 		if (m == NULL) {
8793671d9d8SAndrew Thompson 			UBT_INFO(sc, "ACL data queue is empty\n");
8803671d9d8SAndrew Thompson 			break; /* transfer completed */
8813671d9d8SAndrew Thompson 		}
8823671d9d8SAndrew Thompson 
8833671d9d8SAndrew Thompson 		/*
8843671d9d8SAndrew Thompson 		 * Copy ACL data frame back to a linear USB transfer buffer
8853671d9d8SAndrew Thompson 		 * and schedule transfer
8863671d9d8SAndrew Thompson 		 */
8873671d9d8SAndrew Thompson 
8883671d9d8SAndrew Thompson 		usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len);
8893671d9d8SAndrew Thompson 		xfer->frlengths[0] = m->m_pkthdr.len;
8903671d9d8SAndrew Thompson 
8913671d9d8SAndrew Thompson 		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
8923671d9d8SAndrew Thompson 			m->m_pkthdr.len);
8933671d9d8SAndrew Thompson 
8943671d9d8SAndrew Thompson 		NG_FREE_M(m);
8953671d9d8SAndrew Thompson 
8963671d9d8SAndrew Thompson 		usb2_start_hardware(xfer);
8973671d9d8SAndrew Thompson 		break;
8983671d9d8SAndrew Thompson 
8993671d9d8SAndrew Thompson 	default: /* Error */
9003671d9d8SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
9013671d9d8SAndrew Thompson 			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
9023671d9d8SAndrew Thompson 				usb2_errstr(xfer->error));
9033671d9d8SAndrew Thompson 
9043671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
9053671d9d8SAndrew Thompson 
9063671d9d8SAndrew Thompson 			/* try to clear stall first */
9073671d9d8SAndrew Thompson 			xfer->flags.stall_pipe = 1;
9083671d9d8SAndrew Thompson 			goto send_next;
9093671d9d8SAndrew Thompson 		}
9103671d9d8SAndrew Thompson 			/* transfer cancelled */
9113671d9d8SAndrew Thompson 		break;
9123671d9d8SAndrew Thompson 	}
9133671d9d8SAndrew Thompson } /* ubt_bulk_write_callback */
9143671d9d8SAndrew Thompson 
9153671d9d8SAndrew Thompson /*
9163671d9d8SAndrew Thompson  * Called when incoming isoc transfer (SCO packet) has completed, i.e.
9173671d9d8SAndrew Thompson  * SCO packet was received from the device.
9183671d9d8SAndrew Thompson  * USB context.
9193671d9d8SAndrew Thompson  */
9203671d9d8SAndrew Thompson 
9213671d9d8SAndrew Thompson static void
9223671d9d8SAndrew Thompson ubt_isoc_read_callback(struct usb2_xfer *xfer)
9233671d9d8SAndrew Thompson {
9243671d9d8SAndrew Thompson 	struct ubt_softc	*sc = xfer->priv_sc;
9253671d9d8SAndrew Thompson 	int			n;
9263671d9d8SAndrew Thompson 
9273671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9283671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
9293671d9d8SAndrew Thompson 		for (n = 0; n < xfer->nframes; n ++)
9303671d9d8SAndrew Thompson 			if (ubt_isoc_read_one_frame(xfer, n) < 0)
9313671d9d8SAndrew Thompson 				break;
9323671d9d8SAndrew Thompson 		/* FALLTHROUGH */
9333671d9d8SAndrew Thompson 
9343671d9d8SAndrew Thompson 	case USB_ST_SETUP:
9353671d9d8SAndrew Thompson read_next:
9363671d9d8SAndrew Thompson 		for (n = 0; n < xfer->nframes; n ++)
9373671d9d8SAndrew Thompson 			xfer->frlengths[n] = xfer->max_frame_size;
9383671d9d8SAndrew Thompson 
9393671d9d8SAndrew Thompson 		usb2_start_hardware(xfer);
9403671d9d8SAndrew Thompson 		break;
9413671d9d8SAndrew Thompson 
9423671d9d8SAndrew Thompson 	default: /* Error */
9433671d9d8SAndrew Thompson                 if (xfer->error != USB_ERR_CANCELLED) {
9443671d9d8SAndrew Thompson                         UBT_STAT_IERROR(sc);
9453671d9d8SAndrew Thompson                         goto read_next;
9463671d9d8SAndrew Thompson                 }
9473671d9d8SAndrew Thompson 
9483671d9d8SAndrew Thompson 		/* transfer cancelled */
9493671d9d8SAndrew Thompson 		break;
9503671d9d8SAndrew Thompson 	}
9513671d9d8SAndrew Thompson } /* ubt_isoc_read_callback */
9523671d9d8SAndrew Thompson 
9533671d9d8SAndrew Thompson /*
9543671d9d8SAndrew Thompson  * Helper function. Called from ubt_isoc_read_callback() to read
9553671d9d8SAndrew Thompson  * SCO data from one frame.
9563671d9d8SAndrew Thompson  * USB context.
9573671d9d8SAndrew Thompson  */
9583671d9d8SAndrew Thompson 
9593671d9d8SAndrew Thompson static int
9603671d9d8SAndrew Thompson ubt_isoc_read_one_frame(struct usb2_xfer *xfer, int frame_no)
9613671d9d8SAndrew Thompson {
9623671d9d8SAndrew Thompson 	struct ubt_softc	*sc = xfer->priv_sc;
9633671d9d8SAndrew Thompson 	struct mbuf		*m;
9643671d9d8SAndrew Thompson 	int			len, want, got;
9653671d9d8SAndrew Thompson 
9663671d9d8SAndrew Thompson 	/* Get existing SCO reassembly buffer */
9673671d9d8SAndrew Thompson 	m = sc->sc_isoc_in_buffer;
9683671d9d8SAndrew Thompson 	sc->sc_isoc_in_buffer = NULL;
9693671d9d8SAndrew Thompson 
9703671d9d8SAndrew Thompson 	/* While we have data in the frame */
9713671d9d8SAndrew Thompson 	while ((len = xfer->frlengths[frame_no]) > 0) {
9723671d9d8SAndrew Thompson 		if (m == NULL) {
9733671d9d8SAndrew Thompson 			/* Start new reassembly buffer */
9743671d9d8SAndrew Thompson 			MGETHDR(m, M_DONTWAIT, MT_DATA);
9753671d9d8SAndrew Thompson 			if (m == NULL) {
9763671d9d8SAndrew Thompson 				UBT_STAT_IERROR(sc);
9773671d9d8SAndrew Thompson 				return (-1);	/* XXX out of sync! */
9783671d9d8SAndrew Thompson 			}
9793671d9d8SAndrew Thompson 
9803671d9d8SAndrew Thompson 			MCLGET(m, M_DONTWAIT);
9813671d9d8SAndrew Thompson 			if (!(m->m_flags & M_EXT)) {
9823671d9d8SAndrew Thompson 				UBT_STAT_IERROR(sc);
9833671d9d8SAndrew Thompson 				NG_FREE_M(m);
9843671d9d8SAndrew Thompson 				return (-1);	/* XXX out of sync! */
9853671d9d8SAndrew Thompson 			}
9863671d9d8SAndrew Thompson 
9873671d9d8SAndrew Thompson 			/* Expect SCO header */
9883671d9d8SAndrew Thompson 			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
9893671d9d8SAndrew Thompson 			m->m_pkthdr.len = m->m_len = got = 1;
9903671d9d8SAndrew Thompson 			want = sizeof(ng_hci_scodata_pkt_t);
9913671d9d8SAndrew Thompson 		} else {
9923671d9d8SAndrew Thompson 			/*
9933671d9d8SAndrew Thompson 			 * Check if we have SCO header and if so
9943671d9d8SAndrew Thompson 			 * adjust amount of data we want
9953671d9d8SAndrew Thompson 			 */
9963671d9d8SAndrew Thompson 			got = m->m_pkthdr.len;
9973671d9d8SAndrew Thompson 			want = sizeof(ng_hci_scodata_pkt_t);
9983671d9d8SAndrew Thompson 
9993671d9d8SAndrew Thompson 			if (got >= want)
10003671d9d8SAndrew Thompson 				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
10013671d9d8SAndrew Thompson 		}
10023671d9d8SAndrew Thompson 
10033671d9d8SAndrew Thompson 		/* Append frame data to the SCO reassembly buffer */
10043671d9d8SAndrew Thompson 		if (got + len > want)
10053671d9d8SAndrew Thompson 			len = want - got;
10063671d9d8SAndrew Thompson 
10073671d9d8SAndrew Thompson 		usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size,
10083671d9d8SAndrew Thompson 			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
10093671d9d8SAndrew Thompson 
10103671d9d8SAndrew Thompson 		m->m_pkthdr.len += len;
10113671d9d8SAndrew Thompson 		m->m_len += len;
10123671d9d8SAndrew Thompson 		xfer->frlengths[frame_no] -= len;
10133671d9d8SAndrew Thompson 
10143671d9d8SAndrew Thompson 		/* Check if we got everything we wanted, if not - continue */
10153671d9d8SAndrew Thompson 		if (got != want)
10163671d9d8SAndrew Thompson 			continue;
10173671d9d8SAndrew Thompson 
10183671d9d8SAndrew Thompson 		/* If we got here then we got complete SCO frame */
10193671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
10203671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len,
10213671d9d8SAndrew Thompson 			mtod(m, ng_hci_scodata_pkt_t *)->length);
10223671d9d8SAndrew Thompson 
10233671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
10243671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
10253671d9d8SAndrew Thompson 
10263671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
10273671d9d8SAndrew Thompson 		/* m == NULL at this point */
10283671d9d8SAndrew Thompson 	}
10293671d9d8SAndrew Thompson 
10303671d9d8SAndrew Thompson 	/* Put SCO reassembly buffer back */
10313671d9d8SAndrew Thompson 	sc->sc_isoc_in_buffer = m;
10323671d9d8SAndrew Thompson 
10333671d9d8SAndrew Thompson 	return (0);
10343671d9d8SAndrew Thompson } /* ubt_isoc_read_one_frame */
10353671d9d8SAndrew Thompson 
10363671d9d8SAndrew Thompson /*
10373671d9d8SAndrew Thompson  * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
10383671d9d8SAndrew Thompson  * SCO packet was sent to the device.
10393671d9d8SAndrew Thompson  * USB context.
10403671d9d8SAndrew Thompson  */
10413671d9d8SAndrew Thompson 
10423671d9d8SAndrew Thompson static void
10433671d9d8SAndrew Thompson ubt_isoc_write_callback(struct usb2_xfer *xfer)
10443671d9d8SAndrew Thompson {
10453671d9d8SAndrew Thompson 	struct ubt_softc	*sc = xfer->priv_sc;
10463671d9d8SAndrew Thompson 	struct mbuf		*m;
10473671d9d8SAndrew Thompson 	int			n, space, offset;
10483671d9d8SAndrew Thompson 
10493671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
10503671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
10513671d9d8SAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", xfer->actlen);
10523671d9d8SAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, xfer->actlen);
10533671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
10543671d9d8SAndrew Thompson 		/* FALLTHROUGH */
10553671d9d8SAndrew Thompson 
10563671d9d8SAndrew Thompson 	case USB_ST_SETUP:
10573671d9d8SAndrew Thompson send_next:
10583671d9d8SAndrew Thompson 		offset = 0;
10593671d9d8SAndrew Thompson 		space = xfer->max_frame_size * xfer->nframes;
10603671d9d8SAndrew Thompson 		m = NULL;
10613671d9d8SAndrew Thompson 
10623671d9d8SAndrew Thompson 		while (space > 0) {
10633671d9d8SAndrew Thompson 			if (m == NULL) {
10643671d9d8SAndrew Thompson 				UBT_NG_LOCK(sc);
10653671d9d8SAndrew Thompson 				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
10663671d9d8SAndrew Thompson 				UBT_NG_UNLOCK(sc);
10673671d9d8SAndrew Thompson 
10683671d9d8SAndrew Thompson 				if (m == NULL)
10693671d9d8SAndrew Thompson 					break;
10703671d9d8SAndrew Thompson 			}
10713671d9d8SAndrew Thompson 
10723671d9d8SAndrew Thompson 			n = min(space, m->m_pkthdr.len);
10733671d9d8SAndrew Thompson 			if (n > 0) {
10743671d9d8SAndrew Thompson 				usb2_m_copy_in(xfer->frbuffers, offset, m,0, n);
10753671d9d8SAndrew Thompson 				m_adj(m, n);
10763671d9d8SAndrew Thompson 
10773671d9d8SAndrew Thompson 				offset += n;
10783671d9d8SAndrew Thompson 				space -= n;
10793671d9d8SAndrew Thompson 			}
10803671d9d8SAndrew Thompson 
10813671d9d8SAndrew Thompson 			if (m->m_pkthdr.len == 0)
10823671d9d8SAndrew Thompson 				NG_FREE_M(m); /* sets m = NULL */
10833671d9d8SAndrew Thompson 		}
10843671d9d8SAndrew Thompson 
10853671d9d8SAndrew Thompson 		/* Put whatever is left from mbuf back on queue */
10863671d9d8SAndrew Thompson 		if (m != NULL) {
10873671d9d8SAndrew Thompson 			UBT_NG_LOCK(sc);
10883671d9d8SAndrew Thompson 			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
10893671d9d8SAndrew Thompson 			UBT_NG_UNLOCK(sc);
10903671d9d8SAndrew Thompson 		}
10913671d9d8SAndrew Thompson 
10923671d9d8SAndrew Thompson 		/*
10933671d9d8SAndrew Thompson 		 * Calculate sizes for isoc frames.
10943671d9d8SAndrew Thompson 		 * Note that offset could be 0 at this point (i.e. we have
10953671d9d8SAndrew Thompson 		 * nothing to send). That is fine, as we have isoc. transfers
10963671d9d8SAndrew Thompson 		 * going in both directions all the time. In this case it
10973671d9d8SAndrew Thompson 		 * would be just empty isoc. transfer.
10983671d9d8SAndrew Thompson 		 */
10993671d9d8SAndrew Thompson 
11003671d9d8SAndrew Thompson 		for (n = 0; n < xfer->nframes; n ++) {
11013671d9d8SAndrew Thompson 			xfer->frlengths[n] = min(offset, xfer->max_frame_size);
11023671d9d8SAndrew Thompson 			offset -= xfer->frlengths[n];
11033671d9d8SAndrew Thompson 		}
11043671d9d8SAndrew Thompson 
11053671d9d8SAndrew Thompson 		usb2_start_hardware(xfer);
11063671d9d8SAndrew Thompson 		break;
11073671d9d8SAndrew Thompson 
11083671d9d8SAndrew Thompson 	default: /* Error */
11093671d9d8SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
11103671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
11113671d9d8SAndrew Thompson 			goto send_next;
11123671d9d8SAndrew Thompson 		}
11133671d9d8SAndrew Thompson 
11143671d9d8SAndrew Thompson 		/* transfer cancelled */
11153671d9d8SAndrew Thompson 		break;
11163671d9d8SAndrew Thompson 	}
11173671d9d8SAndrew Thompson }
11183671d9d8SAndrew Thompson 
11193671d9d8SAndrew Thompson /*
11203671d9d8SAndrew Thompson  * Utility function to forward provided mbuf upstream (i.e. up the stack).
11213671d9d8SAndrew Thompson  * Modifies value of the mbuf pointer (sets it to NULL).
11223671d9d8SAndrew Thompson  * Save to call from any context.
11233671d9d8SAndrew Thompson  */
11243671d9d8SAndrew Thompson 
11253671d9d8SAndrew Thompson static int
11263671d9d8SAndrew Thompson ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
11273671d9d8SAndrew Thompson {
11283671d9d8SAndrew Thompson 	hook_p	hook;
11293671d9d8SAndrew Thompson 	int	error;
11303671d9d8SAndrew Thompson 
11313671d9d8SAndrew Thompson 	/*
11323671d9d8SAndrew Thompson 	 * Close the race with Netgraph hook newhook/disconnect methods.
11333671d9d8SAndrew Thompson 	 * Save the hook pointer atomically. Two cases are possible:
11343671d9d8SAndrew Thompson 	 *
11353671d9d8SAndrew Thompson 	 * 1) The hook pointer is NULL. It means disconnect method got
11363671d9d8SAndrew Thompson 	 *    there first. In this case we are done.
11373671d9d8SAndrew Thompson 	 *
11383671d9d8SAndrew Thompson 	 * 2) The hook pointer is not NULL. It means that hook pointer
11393671d9d8SAndrew Thompson 	 *    could be either in valid or invalid (i.e. in the process
11403671d9d8SAndrew Thompson 	 *    of disconnect) state. In any case grab an extra reference
11413671d9d8SAndrew Thompson 	 *    to protect the hook pointer.
11423671d9d8SAndrew Thompson 	 *
11433671d9d8SAndrew Thompson 	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
11443671d9d8SAndrew Thompson 	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
11453671d9d8SAndrew Thompson 	 */
11463671d9d8SAndrew Thompson 
11473671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
11483671d9d8SAndrew Thompson 	if ((hook = sc->sc_hook) != NULL)
11493671d9d8SAndrew Thompson 		NG_HOOK_REF(hook);
11503671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
11513671d9d8SAndrew Thompson 
11523671d9d8SAndrew Thompson 	if (hook == NULL) {
11533671d9d8SAndrew Thompson 		NG_FREE_M(*m);
11543671d9d8SAndrew Thompson 		return (ENETDOWN);
11553671d9d8SAndrew Thompson 	}
11563671d9d8SAndrew Thompson 
11573671d9d8SAndrew Thompson 	NG_SEND_DATA_ONLY(error, hook, *m);
11583671d9d8SAndrew Thompson 	NG_HOOK_UNREF(hook);
11593671d9d8SAndrew Thompson 
11603671d9d8SAndrew Thompson 	if (error != 0)
11613671d9d8SAndrew Thompson 		UBT_STAT_IERROR(sc);
11623671d9d8SAndrew Thompson 
11633671d9d8SAndrew Thompson 	return (error);
11643671d9d8SAndrew Thompson } /* ubt_fwd_mbuf_up */
11653671d9d8SAndrew Thompson 
11663671d9d8SAndrew Thompson /****************************************************************************
11673671d9d8SAndrew Thompson  ****************************************************************************
11683671d9d8SAndrew Thompson  **                                 Glue
11693671d9d8SAndrew Thompson  ****************************************************************************
11703671d9d8SAndrew Thompson  ****************************************************************************/
11713671d9d8SAndrew Thompson 
11723671d9d8SAndrew Thompson /*
11733671d9d8SAndrew Thompson  * Schedule glue task. Should be called with sc_ng_mtx held.
11743671d9d8SAndrew Thompson  * Netgraph context.
11753671d9d8SAndrew Thompson  */
11763671d9d8SAndrew Thompson 
11773671d9d8SAndrew Thompson static void
11783671d9d8SAndrew Thompson ubt_task_schedule(ubt_softc_p sc, int action)
11793671d9d8SAndrew Thompson {
11803671d9d8SAndrew Thompson 	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
11813671d9d8SAndrew Thompson 
11823671d9d8SAndrew Thompson 	/*
11833671d9d8SAndrew Thompson 	 * Try to handle corner case when "start all" and "stop all"
11843671d9d8SAndrew Thompson 	 * actions can both be set before task is executed.
11853671d9d8SAndrew Thompson 	 *
11863671d9d8SAndrew Thompson 	 * The rules are
11873671d9d8SAndrew Thompson 	 *
11883671d9d8SAndrew Thompson 	 * sc_task_flags	action		new sc_task_flags
11893671d9d8SAndrew Thompson 	 * ------------------------------------------------------
11903671d9d8SAndrew Thompson 	 * 0			start		start
11913671d9d8SAndrew Thompson 	 * 0			stop		stop
11923671d9d8SAndrew Thompson 	 * start		start		start
11933671d9d8SAndrew Thompson 	 * start		stop		stop
11943671d9d8SAndrew Thompson 	 * stop			start		stop|start
11953671d9d8SAndrew Thompson 	 * stop			stop		stop
11963671d9d8SAndrew Thompson 	 * stop|start		start		stop|start
11973671d9d8SAndrew Thompson 	 * stop|start		stop		stop
11983671d9d8SAndrew Thompson 	 */
11993671d9d8SAndrew Thompson 
12003671d9d8SAndrew Thompson 	if (action != 0) {
12013671d9d8SAndrew Thompson 		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
12023671d9d8SAndrew Thompson 			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
12033671d9d8SAndrew Thompson 
12043671d9d8SAndrew Thompson 		sc->sc_task_flags |= action;
12053671d9d8SAndrew Thompson 	}
12063671d9d8SAndrew Thompson 
12073671d9d8SAndrew Thompson 	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
12083671d9d8SAndrew Thompson 		return;
12093671d9d8SAndrew Thompson 
12103671d9d8SAndrew Thompson 	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
12113671d9d8SAndrew Thompson 		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
12123671d9d8SAndrew Thompson 		return;
12133671d9d8SAndrew Thompson 	}
12143671d9d8SAndrew Thompson 
12153671d9d8SAndrew Thompson 	/* XXX: i think this should never happen */
12163671d9d8SAndrew Thompson } /* ubt_task_schedule */
12173671d9d8SAndrew Thompson 
12183671d9d8SAndrew Thompson /*
12193671d9d8SAndrew Thompson  * Glue task. Examines sc_task_flags and does things depending on it.
12203671d9d8SAndrew Thompson  * Taskqueue context.
12213671d9d8SAndrew Thompson  */
12223671d9d8SAndrew Thompson 
12233671d9d8SAndrew Thompson static void
12243671d9d8SAndrew Thompson ubt_task(void *context, int pending)
12253671d9d8SAndrew Thompson {
12263671d9d8SAndrew Thompson 	ubt_softc_p	sc = context;
12273671d9d8SAndrew Thompson 	int		task_flags, i;
12283671d9d8SAndrew Thompson 
12293671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
12303671d9d8SAndrew Thompson 	task_flags = sc->sc_task_flags;
12313671d9d8SAndrew Thompson 	sc->sc_task_flags = 0;
12323671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
12333671d9d8SAndrew Thompson 
12343671d9d8SAndrew Thompson 	/*
12353671d9d8SAndrew Thompson 	 * Stop all USB transfers synchronously.
12363671d9d8SAndrew Thompson 	 * Stop interface #0 and #1 transfers at the same time and in the
12373671d9d8SAndrew Thompson 	 * same loop. usb2_transfer_drain() will do appropriate locking.
12383671d9d8SAndrew Thompson 	 */
12393671d9d8SAndrew Thompson 
12403671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_STOP_ALL)
12413671d9d8SAndrew Thompson 		for (i = 0; i < UBT_N_TRANSFER; i ++)
12423671d9d8SAndrew Thompson 			usb2_transfer_drain(sc->sc_xfer[i]);
12433671d9d8SAndrew Thompson 
12443671d9d8SAndrew Thompson 	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
12453671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_ALL) {
12463671d9d8SAndrew Thompson 		/*
12473671d9d8SAndrew Thompson 		 * Interface #0
12483671d9d8SAndrew Thompson 		 */
12493671d9d8SAndrew Thompson 
12503671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
12513671d9d8SAndrew Thompson 
12523671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
12533671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
12543671d9d8SAndrew Thompson 
12553671d9d8SAndrew Thompson 		/*
12563671d9d8SAndrew Thompson 		 * Interface #1
12573671d9d8SAndrew Thompson 		 * Start both read and write isoc. transfers by default.
12583671d9d8SAndrew Thompson 		 * Get them going all the time even if we have nothing
12593671d9d8SAndrew Thompson 		 * to send to avoid any delays.
12603671d9d8SAndrew Thompson 		 */
12613671d9d8SAndrew Thompson 
12623671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
12633671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
12643671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
12653671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
12663671d9d8SAndrew Thompson 
12673671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
12683671d9d8SAndrew Thompson 	}
12693671d9d8SAndrew Thompson 
12703671d9d8SAndrew Thompson  	/* Start outgoing control transfer */
12713671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_CTRL) {
12723671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
12733671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
12743671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
12753671d9d8SAndrew Thompson 	}
12763671d9d8SAndrew Thompson 
12773671d9d8SAndrew Thompson 	/* Start outgoing bulk transfer */
12783671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_BULK) {
12793671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
12803671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
12813671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
12823671d9d8SAndrew Thompson 	}
12833671d9d8SAndrew Thompson } /* ubt_task */
12843671d9d8SAndrew Thompson 
12853671d9d8SAndrew Thompson /****************************************************************************
12863671d9d8SAndrew Thompson  ****************************************************************************
12873671d9d8SAndrew Thompson  **                        Netgraph specific
12883671d9d8SAndrew Thompson  ****************************************************************************
12893671d9d8SAndrew Thompson  ****************************************************************************/
12903671d9d8SAndrew Thompson 
12913671d9d8SAndrew Thompson /*
12923671d9d8SAndrew Thompson  * Netgraph node constructor. Do not allow to create node of this type.
12933671d9d8SAndrew Thompson  * Netgraph context.
12943671d9d8SAndrew Thompson  */
12953671d9d8SAndrew Thompson 
12963671d9d8SAndrew Thompson static int
12973671d9d8SAndrew Thompson ng_ubt_constructor(node_p node)
12983671d9d8SAndrew Thompson {
12993671d9d8SAndrew Thompson 	return (EINVAL);
13003671d9d8SAndrew Thompson } /* ng_ubt_constructor */
13013671d9d8SAndrew Thompson 
13023671d9d8SAndrew Thompson /*
13033671d9d8SAndrew Thompson  * Netgraph node destructor. Destroy node only when device has been detached.
13043671d9d8SAndrew Thompson  * Netgraph context.
13053671d9d8SAndrew Thompson  */
13063671d9d8SAndrew Thompson 
13073671d9d8SAndrew Thompson static int
13083671d9d8SAndrew Thompson ng_ubt_shutdown(node_p node)
13093671d9d8SAndrew Thompson {
13103671d9d8SAndrew Thompson 	if (node->nd_flags & NGF_REALLY_DIE) {
13113671d9d8SAndrew Thompson 		/*
13123671d9d8SAndrew Thompson                  * We came here because the USB device is being
13133671d9d8SAndrew Thompson 		 * detached, so stop being persistant.
13143671d9d8SAndrew Thompson                  */
13153671d9d8SAndrew Thompson 		NG_NODE_SET_PRIVATE(node, NULL);
13163671d9d8SAndrew Thompson 		NG_NODE_UNREF(node);
13173671d9d8SAndrew Thompson 	} else
13183671d9d8SAndrew Thompson 		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
13193671d9d8SAndrew Thompson 
13203671d9d8SAndrew Thompson 	return (0);
13213671d9d8SAndrew Thompson } /* ng_ubt_shutdown */
13223671d9d8SAndrew Thompson 
13233671d9d8SAndrew Thompson /*
13243671d9d8SAndrew Thompson  * Create new hook. There can only be one.
13253671d9d8SAndrew Thompson  * Netgraph context.
13263671d9d8SAndrew Thompson  */
13273671d9d8SAndrew Thompson 
13283671d9d8SAndrew Thompson static int
13293671d9d8SAndrew Thompson ng_ubt_newhook(node_p node, hook_p hook, char const *name)
13303671d9d8SAndrew Thompson {
13313671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
13323671d9d8SAndrew Thompson 
13333671d9d8SAndrew Thompson 	if (strcmp(name, NG_UBT_HOOK) != 0)
13343671d9d8SAndrew Thompson 		return (EINVAL);
13353671d9d8SAndrew Thompson 
13363671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
13373671d9d8SAndrew Thompson 	if (sc->sc_hook != NULL) {
13383671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
13393671d9d8SAndrew Thompson 
13403671d9d8SAndrew Thompson 		return (EISCONN);
13413671d9d8SAndrew Thompson 	}
13423671d9d8SAndrew Thompson 
13433671d9d8SAndrew Thompson 	sc->sc_hook = hook;
13443671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
13453671d9d8SAndrew Thompson 
13463671d9d8SAndrew Thompson 	return (0);
13473671d9d8SAndrew Thompson } /* ng_ubt_newhook */
13483671d9d8SAndrew Thompson 
13493671d9d8SAndrew Thompson /*
13503671d9d8SAndrew Thompson  * Connect hook. Start incoming USB transfers.
13513671d9d8SAndrew Thompson  * Netgraph context.
13523671d9d8SAndrew Thompson  */
13533671d9d8SAndrew Thompson 
13543671d9d8SAndrew Thompson static int
13553671d9d8SAndrew Thompson ng_ubt_connect(hook_p hook)
13563671d9d8SAndrew Thompson {
13573671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
13583671d9d8SAndrew Thompson 
13593671d9d8SAndrew Thompson 	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
13603671d9d8SAndrew Thompson 
13613671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
13623671d9d8SAndrew Thompson 	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
13633671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
13643671d9d8SAndrew Thompson 
13653671d9d8SAndrew Thompson 	return (0);
13663671d9d8SAndrew Thompson } /* ng_ubt_connect */
13673671d9d8SAndrew Thompson 
13683671d9d8SAndrew Thompson /*
13693671d9d8SAndrew Thompson  * Disconnect hook.
13703671d9d8SAndrew Thompson  * Netgraph context.
13713671d9d8SAndrew Thompson  */
13723671d9d8SAndrew Thompson 
13733671d9d8SAndrew Thompson static int
13743671d9d8SAndrew Thompson ng_ubt_disconnect(hook_p hook)
13753671d9d8SAndrew Thompson {
13763671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
13773671d9d8SAndrew Thompson 
13783671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
13793671d9d8SAndrew Thompson 
13803671d9d8SAndrew Thompson 	if (hook != sc->sc_hook) {
13813671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
13823671d9d8SAndrew Thompson 
13833671d9d8SAndrew Thompson 		return (EINVAL);
13843671d9d8SAndrew Thompson 	}
13853671d9d8SAndrew Thompson 
13863671d9d8SAndrew Thompson 	sc->sc_hook = NULL;
13873671d9d8SAndrew Thompson 
13883671d9d8SAndrew Thompson 	/* Kick off task to stop all USB xfers */
13893671d9d8SAndrew Thompson 	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
13903671d9d8SAndrew Thompson 
13913671d9d8SAndrew Thompson 	/* Drain queues */
13923671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
13933671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
13943671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
13953671d9d8SAndrew Thompson 
13963671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
13973671d9d8SAndrew Thompson 
13983671d9d8SAndrew Thompson 	return (0);
13993671d9d8SAndrew Thompson } /* ng_ubt_disconnect */
14003671d9d8SAndrew Thompson 
14013671d9d8SAndrew Thompson /*
14023671d9d8SAndrew Thompson  * Process control message.
14033671d9d8SAndrew Thompson  * Netgraph context.
14043671d9d8SAndrew Thompson  */
14053671d9d8SAndrew Thompson 
14063671d9d8SAndrew Thompson static int
14073671d9d8SAndrew Thompson ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
14083671d9d8SAndrew Thompson {
14093671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
14103671d9d8SAndrew Thompson 	struct ng_mesg		*msg, *rsp = NULL;
14113671d9d8SAndrew Thompson 	struct ng_bt_mbufq	*q;
14123671d9d8SAndrew Thompson 	int			error = 0, queue, qlen;
14133671d9d8SAndrew Thompson 
14143671d9d8SAndrew Thompson 	NGI_GET_MSG(item, msg);
14153671d9d8SAndrew Thompson 
14163671d9d8SAndrew Thompson 	switch (msg->header.typecookie) {
14173671d9d8SAndrew Thompson 	case NGM_GENERIC_COOKIE:
14183671d9d8SAndrew Thompson 		switch (msg->header.cmd) {
14193671d9d8SAndrew Thompson 		case NGM_TEXT_STATUS:
14203671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
14213671d9d8SAndrew Thompson 			if (rsp == NULL) {
14223671d9d8SAndrew Thompson 				error = ENOMEM;
14233671d9d8SAndrew Thompson 				break;
14243671d9d8SAndrew Thompson 			}
14253671d9d8SAndrew Thompson 
14263671d9d8SAndrew Thompson 			snprintf(rsp->data, NG_TEXTRESPONSE,
14273671d9d8SAndrew Thompson 				"Hook: %s\n" \
14283671d9d8SAndrew Thompson 				"Task flags: %#x\n" \
14293671d9d8SAndrew Thompson 				"Debug: %d\n" \
14303671d9d8SAndrew Thompson 				"CMD queue: [have:%d,max:%d]\n" \
14313671d9d8SAndrew Thompson 				"ACL queue: [have:%d,max:%d]\n" \
14323671d9d8SAndrew Thompson 				"SCO queue: [have:%d,max:%d]",
14333671d9d8SAndrew Thompson 				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
14343671d9d8SAndrew Thompson 				sc->sc_task_flags,
14353671d9d8SAndrew Thompson 				sc->sc_debug,
14363671d9d8SAndrew Thompson 				sc->sc_cmdq.len,
14373671d9d8SAndrew Thompson 				sc->sc_cmdq.maxlen,
14383671d9d8SAndrew Thompson 				sc->sc_aclq.len,
14393671d9d8SAndrew Thompson 				sc->sc_aclq.maxlen,
14403671d9d8SAndrew Thompson 				sc->sc_scoq.len,
14413671d9d8SAndrew Thompson 				sc->sc_scoq.maxlen);
14423671d9d8SAndrew Thompson 			break;
14433671d9d8SAndrew Thompson 
14443671d9d8SAndrew Thompson 		default:
14453671d9d8SAndrew Thompson 			error = EINVAL;
14463671d9d8SAndrew Thompson 			break;
14473671d9d8SAndrew Thompson 		}
14483671d9d8SAndrew Thompson 		break;
14493671d9d8SAndrew Thompson 
14503671d9d8SAndrew Thompson 	case NGM_UBT_COOKIE:
14513671d9d8SAndrew Thompson 		switch (msg->header.cmd) {
14523671d9d8SAndrew Thompson 		case NGM_UBT_NODE_SET_DEBUG:
14533671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
14543671d9d8SAndrew Thompson 				error = EMSGSIZE;
14553671d9d8SAndrew Thompson 				break;
14563671d9d8SAndrew Thompson 			}
14573671d9d8SAndrew Thompson 
14583671d9d8SAndrew Thompson 			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
14593671d9d8SAndrew Thompson 			break;
14603671d9d8SAndrew Thompson 
14613671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_DEBUG:
14623671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
14633671d9d8SAndrew Thompson 			    M_NOWAIT);
14643671d9d8SAndrew Thompson 			if (rsp == NULL) {
14653671d9d8SAndrew Thompson 				error = ENOMEM;
14663671d9d8SAndrew Thompson 				break;
14673671d9d8SAndrew Thompson 			}
14683671d9d8SAndrew Thompson 
14693671d9d8SAndrew Thompson 			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
14703671d9d8SAndrew Thompson 			break;
14713671d9d8SAndrew Thompson 
14723671d9d8SAndrew Thompson 		case NGM_UBT_NODE_SET_QLEN:
14733671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
14743671d9d8SAndrew Thompson 				error = EMSGSIZE;
14753671d9d8SAndrew Thompson 				break;
14763671d9d8SAndrew Thompson 			}
14773671d9d8SAndrew Thompson 
14783671d9d8SAndrew Thompson 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
14793671d9d8SAndrew Thompson 			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
14803671d9d8SAndrew Thompson 
14813671d9d8SAndrew Thompson 			switch (queue) {
14823671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_CMD:
14833671d9d8SAndrew Thompson 				q = &sc->sc_cmdq;
14843671d9d8SAndrew Thompson 				break;
14853671d9d8SAndrew Thompson 
14863671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_ACL:
14873671d9d8SAndrew Thompson 				q = &sc->sc_aclq;
14883671d9d8SAndrew Thompson 				break;
14893671d9d8SAndrew Thompson 
14903671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_SCO:
14913671d9d8SAndrew Thompson 				q = &sc->sc_scoq;
14923671d9d8SAndrew Thompson 				break;
14933671d9d8SAndrew Thompson 
14943671d9d8SAndrew Thompson 			default:
14953671d9d8SAndrew Thompson 				error = EINVAL;
14963671d9d8SAndrew Thompson 				goto done;
14973671d9d8SAndrew Thompson 				/* NOT REACHED */
14983671d9d8SAndrew Thompson 			}
14993671d9d8SAndrew Thompson 
15003671d9d8SAndrew Thompson 			q->maxlen = qlen;
15013671d9d8SAndrew Thompson 			break;
15023671d9d8SAndrew Thompson 
15033671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_QLEN:
15043671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
15053671d9d8SAndrew Thompson 				error = EMSGSIZE;
15063671d9d8SAndrew Thompson 				break;
15073671d9d8SAndrew Thompson 			}
15083671d9d8SAndrew Thompson 
15093671d9d8SAndrew Thompson 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
15103671d9d8SAndrew Thompson 
15113671d9d8SAndrew Thompson 			switch (queue) {
15123671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_CMD:
15133671d9d8SAndrew Thompson 				q = &sc->sc_cmdq;
15143671d9d8SAndrew Thompson 				break;
15153671d9d8SAndrew Thompson 
15163671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_ACL:
15173671d9d8SAndrew Thompson 				q = &sc->sc_aclq;
15183671d9d8SAndrew Thompson 				break;
15193671d9d8SAndrew Thompson 
15203671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_SCO:
15213671d9d8SAndrew Thompson 				q = &sc->sc_scoq;
15223671d9d8SAndrew Thompson 				break;
15233671d9d8SAndrew Thompson 
15243671d9d8SAndrew Thompson 			default:
15253671d9d8SAndrew Thompson 				error = EINVAL;
15263671d9d8SAndrew Thompson 				goto done;
15273671d9d8SAndrew Thompson 				/* NOT REACHED */
15283671d9d8SAndrew Thompson 			}
15293671d9d8SAndrew Thompson 
15303671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
15313671d9d8SAndrew Thompson 				M_NOWAIT);
15323671d9d8SAndrew Thompson 			if (rsp == NULL) {
15333671d9d8SAndrew Thompson 				error = ENOMEM;
15343671d9d8SAndrew Thompson 				break;
15353671d9d8SAndrew Thompson 			}
15363671d9d8SAndrew Thompson 
15373671d9d8SAndrew Thompson 			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
15383671d9d8SAndrew Thompson 			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
15393671d9d8SAndrew Thompson 			break;
15403671d9d8SAndrew Thompson 
15413671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_STAT:
15423671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
15433671d9d8SAndrew Thompson 			    M_NOWAIT);
15443671d9d8SAndrew Thompson 			if (rsp == NULL) {
15453671d9d8SAndrew Thompson 				error = ENOMEM;
15463671d9d8SAndrew Thompson 				break;
15473671d9d8SAndrew Thompson 			}
15483671d9d8SAndrew Thompson 
15493671d9d8SAndrew Thompson 			bcopy(&sc->sc_stat, rsp->data,
15503671d9d8SAndrew Thompson 				sizeof(ng_ubt_node_stat_ep));
15513671d9d8SAndrew Thompson 			break;
15523671d9d8SAndrew Thompson 
15533671d9d8SAndrew Thompson 		case NGM_UBT_NODE_RESET_STAT:
15543671d9d8SAndrew Thompson 			UBT_STAT_RESET(sc);
15553671d9d8SAndrew Thompson 			break;
15563671d9d8SAndrew Thompson 
15573671d9d8SAndrew Thompson 		default:
15583671d9d8SAndrew Thompson 			error = EINVAL;
15593671d9d8SAndrew Thompson 			break;
15603671d9d8SAndrew Thompson 		}
15613671d9d8SAndrew Thompson 		break;
15623671d9d8SAndrew Thompson 
15633671d9d8SAndrew Thompson 	default:
15643671d9d8SAndrew Thompson 		error = EINVAL;
15653671d9d8SAndrew Thompson 		break;
15663671d9d8SAndrew Thompson 	}
15673671d9d8SAndrew Thompson done:
15683671d9d8SAndrew Thompson 	NG_RESPOND_MSG(error, node, item, rsp);
15693671d9d8SAndrew Thompson 	NG_FREE_MSG(msg);
15703671d9d8SAndrew Thompson 
15713671d9d8SAndrew Thompson 	return (error);
15723671d9d8SAndrew Thompson } /* ng_ubt_rcvmsg */
15733671d9d8SAndrew Thompson 
15743671d9d8SAndrew Thompson /*
15753671d9d8SAndrew Thompson  * Process data.
15763671d9d8SAndrew Thompson  * Netgraph context.
15773671d9d8SAndrew Thompson  */
15783671d9d8SAndrew Thompson 
15793671d9d8SAndrew Thompson static int
15803671d9d8SAndrew Thompson ng_ubt_rcvdata(hook_p hook, item_p item)
15813671d9d8SAndrew Thompson {
15823671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
15833671d9d8SAndrew Thompson 	struct mbuf		*m;
15843671d9d8SAndrew Thompson 	struct ng_bt_mbufq	*q;
15853671d9d8SAndrew Thompson 	int			action, error = 0;
15863671d9d8SAndrew Thompson 
15873671d9d8SAndrew Thompson 	if (hook != sc->sc_hook) {
15883671d9d8SAndrew Thompson 		error = EINVAL;
15893671d9d8SAndrew Thompson 		goto done;
15903671d9d8SAndrew Thompson 	}
15913671d9d8SAndrew Thompson 
15923671d9d8SAndrew Thompson 	/* Deatch mbuf and get HCI frame type */
15933671d9d8SAndrew Thompson 	NGI_GET_M(item, m);
15943671d9d8SAndrew Thompson 
15953671d9d8SAndrew Thompson 	/*
15963671d9d8SAndrew Thompson 	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
15973671d9d8SAndrew Thompson 	 * 2 bytes connection handle and at least 1 byte of length.
15983671d9d8SAndrew Thompson 	 * Panic on data frame that has size smaller than 4 bytes (it
15993671d9d8SAndrew Thompson 	 * should not happen)
16003671d9d8SAndrew Thompson 	 */
16013671d9d8SAndrew Thompson 
16023671d9d8SAndrew Thompson 	if (m->m_pkthdr.len < 4)
16033671d9d8SAndrew Thompson 		panic("HCI frame size is too small! pktlen=%d\n",
16043671d9d8SAndrew Thompson 			m->m_pkthdr.len);
16053671d9d8SAndrew Thompson 
16063671d9d8SAndrew Thompson 	/* Process HCI frame */
16073671d9d8SAndrew Thompson 	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
16083671d9d8SAndrew Thompson 	case NG_HCI_CMD_PKT:
16093671d9d8SAndrew Thompson 		if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE)
16103671d9d8SAndrew Thompson 			panic("HCI command frame size is too big! " \
16113671d9d8SAndrew Thompson 				"buffer size=%zd, packet len=%d\n",
16123671d9d8SAndrew Thompson 				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
16133671d9d8SAndrew Thompson 
16143671d9d8SAndrew Thompson 		q = &sc->sc_cmdq;
16153671d9d8SAndrew Thompson 		action = UBT_FLAG_T_START_CTRL;
16163671d9d8SAndrew Thompson 		break;
16173671d9d8SAndrew Thompson 
16183671d9d8SAndrew Thompson 	case NG_HCI_ACL_DATA_PKT:
16193671d9d8SAndrew Thompson 		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
16203671d9d8SAndrew Thompson 			panic("ACL data frame size is too big! " \
16213671d9d8SAndrew Thompson 				"buffer size=%d, packet len=%d\n",
16223671d9d8SAndrew Thompson 				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
16233671d9d8SAndrew Thompson 
16243671d9d8SAndrew Thompson 		q = &sc->sc_aclq;
16253671d9d8SAndrew Thompson 		action = UBT_FLAG_T_START_BULK;
16263671d9d8SAndrew Thompson 		break;
16273671d9d8SAndrew Thompson 
16283671d9d8SAndrew Thompson 	case NG_HCI_SCO_DATA_PKT:
16293671d9d8SAndrew Thompson 		q = &sc->sc_scoq;
16303671d9d8SAndrew Thompson 		action = 0;
16313671d9d8SAndrew Thompson 		break;
16323671d9d8SAndrew Thompson 
16333671d9d8SAndrew Thompson 	default:
16343671d9d8SAndrew Thompson 		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
16353671d9d8SAndrew Thompson 			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
16363671d9d8SAndrew Thompson 
16373671d9d8SAndrew Thompson 		NG_FREE_M(m);
16383671d9d8SAndrew Thompson 		error = EINVAL;
16393671d9d8SAndrew Thompson 		goto done;
16403671d9d8SAndrew Thompson 		/* NOT REACHED */
16413671d9d8SAndrew Thompson 	}
16423671d9d8SAndrew Thompson 
16433671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
16443671d9d8SAndrew Thompson 	if (NG_BT_MBUFQ_FULL(q)) {
16453671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DROP(q);
16463671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
16473671d9d8SAndrew Thompson 
16483671d9d8SAndrew Thompson 		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
16493671d9d8SAndrew Thompson 			*mtod(m, uint8_t *), m->m_pkthdr.len);
16503671d9d8SAndrew Thompson 
16513671d9d8SAndrew Thompson 		NG_FREE_M(m);
16523671d9d8SAndrew Thompson 	} else {
16533671d9d8SAndrew Thompson 		/* Loose HCI packet type, enqueue mbuf and kick off task */
16543671d9d8SAndrew Thompson 		m_adj(m, sizeof(uint8_t));
16553671d9d8SAndrew Thompson 		NG_BT_MBUFQ_ENQUEUE(q, m);
16563671d9d8SAndrew Thompson 		ubt_task_schedule(sc, action);
16573671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
16583671d9d8SAndrew Thompson 	}
16593671d9d8SAndrew Thompson done:
16603671d9d8SAndrew Thompson 	NG_FREE_ITEM(item);
16613671d9d8SAndrew Thompson 
16623671d9d8SAndrew Thompson 	return (error);
16633671d9d8SAndrew Thompson } /* ng_ubt_rcvdata */
16643671d9d8SAndrew Thompson 
16653671d9d8SAndrew Thompson /****************************************************************************
16663671d9d8SAndrew Thompson  ****************************************************************************
16673671d9d8SAndrew Thompson  **                              Module
16683671d9d8SAndrew Thompson  ****************************************************************************
16693671d9d8SAndrew Thompson  ****************************************************************************/
16703671d9d8SAndrew Thompson 
16713671d9d8SAndrew Thompson /*
16723671d9d8SAndrew Thompson  * Load/Unload the driver module
16733671d9d8SAndrew Thompson  */
16743671d9d8SAndrew Thompson 
16753671d9d8SAndrew Thompson static int
16763671d9d8SAndrew Thompson ubt_modevent(module_t mod, int event, void *data)
16773671d9d8SAndrew Thompson {
16783671d9d8SAndrew Thompson 	int	error;
16793671d9d8SAndrew Thompson 
16803671d9d8SAndrew Thompson 	switch (event) {
16813671d9d8SAndrew Thompson 	case MOD_LOAD:
16823671d9d8SAndrew Thompson 		error = ng_newtype(&typestruct);
16833671d9d8SAndrew Thompson 		if (error != 0)
16843671d9d8SAndrew Thompson 			printf("%s: Could not register Netgraph node type, " \
16853671d9d8SAndrew Thompson 				"error=%d\n", NG_UBT_NODE_TYPE, error);
16863671d9d8SAndrew Thompson 		break;
16873671d9d8SAndrew Thompson 
16883671d9d8SAndrew Thompson 	case MOD_UNLOAD:
16893671d9d8SAndrew Thompson 		error = ng_rmtype(&typestruct);
16903671d9d8SAndrew Thompson 		break;
16913671d9d8SAndrew Thompson 
16923671d9d8SAndrew Thompson 	default:
16933671d9d8SAndrew Thompson 		error = EOPNOTSUPP;
16943671d9d8SAndrew Thompson 		break;
16953671d9d8SAndrew Thompson 	}
16963671d9d8SAndrew Thompson 
16973671d9d8SAndrew Thompson 	return (error);
16983671d9d8SAndrew Thompson } /* ubt_modevent */
16993671d9d8SAndrew Thompson 
17003671d9d8SAndrew Thompson static devclass_t	ubt_devclass;
17013671d9d8SAndrew Thompson 
17023671d9d8SAndrew Thompson static device_method_t	ubt_methods[] =
17033671d9d8SAndrew Thompson {
17043671d9d8SAndrew Thompson 	DEVMETHOD(device_probe,	ubt_probe),
17053671d9d8SAndrew Thompson 	DEVMETHOD(device_attach, ubt_attach),
17063671d9d8SAndrew Thompson 	DEVMETHOD(device_detach, ubt_detach),
17073671d9d8SAndrew Thompson 	{ 0, 0 }
17083671d9d8SAndrew Thompson };
17093671d9d8SAndrew Thompson 
17103671d9d8SAndrew Thompson static driver_t		ubt_driver =
17113671d9d8SAndrew Thompson {
17123671d9d8SAndrew Thompson 	.name =	   "ubt",
17133671d9d8SAndrew Thompson 	.methods = ubt_methods,
17143671d9d8SAndrew Thompson 	.size =	   sizeof(struct ubt_softc),
17153671d9d8SAndrew Thompson };
17163671d9d8SAndrew Thompson 
17173671d9d8SAndrew Thompson DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
17183671d9d8SAndrew Thompson MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
17193671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
17203671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
17213671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
17223671d9d8SAndrew Thompson 
1723