xref: /freebsd/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c (revision 24ae172a50352ad4fd22989477f29ecca5aed6e3)
13671d9d8SAndrew Thompson /*
23671d9d8SAndrew Thompson  * ng_ubt.c
33671d9d8SAndrew Thompson  */
43671d9d8SAndrew Thompson 
53671d9d8SAndrew Thompson /*-
64d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
7fe267a55SPedro F. Giffuni  *
83671d9d8SAndrew Thompson  * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
93671d9d8SAndrew Thompson  * All rights reserved.
103671d9d8SAndrew Thompson  *
113671d9d8SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
123671d9d8SAndrew Thompson  * modification, are permitted provided that the following conditions
133671d9d8SAndrew Thompson  * are met:
143671d9d8SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
153671d9d8SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
163671d9d8SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
173671d9d8SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
183671d9d8SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
193671d9d8SAndrew Thompson  *
203671d9d8SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
213671d9d8SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
223671d9d8SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
233671d9d8SAndrew Thompson  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
243671d9d8SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
253671d9d8SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
263671d9d8SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
273671d9d8SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
283671d9d8SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
293671d9d8SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
303671d9d8SAndrew Thompson  * SUCH DAMAGE.
313671d9d8SAndrew Thompson  *
323671d9d8SAndrew Thompson  * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
333671d9d8SAndrew Thompson  */
343671d9d8SAndrew Thompson 
353671d9d8SAndrew Thompson /*
363671d9d8SAndrew Thompson  * NOTE: ng_ubt2 driver has a split personality. On one side it is
373671d9d8SAndrew Thompson  * a USB device driver and on the other it is a Netgraph node. This
383671d9d8SAndrew Thompson  * driver will *NOT* create traditional /dev/ enties, only Netgraph
393671d9d8SAndrew Thompson  * node.
403671d9d8SAndrew Thompson  *
413671d9d8SAndrew Thompson  * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
423671d9d8SAndrew Thompson  *
433671d9d8SAndrew Thompson  * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
443671d9d8SAndrew Thompson  *    by USB for any USB request going over device's interface #0 and #1,
453671d9d8SAndrew Thompson  *    i.e. interrupt, control, bulk and isoc. transfers.
463671d9d8SAndrew Thompson  *
473671d9d8SAndrew Thompson  * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
483671d9d8SAndrew Thompson  *    and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
493671d9d8SAndrew Thompson  *    pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
503671d9d8SAndrew Thompson  *    think of it as a spin lock.
513671d9d8SAndrew Thompson  *
523671d9d8SAndrew Thompson  * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
533671d9d8SAndrew Thompson  *
543671d9d8SAndrew Thompson  * 1) USB context. This is where all the USB related stuff happens. All
553671d9d8SAndrew Thompson  *    callbacks run in this context. All callbacks are called (by USB) with
563671d9d8SAndrew Thompson  *    appropriate interface lock held. It is (generally) allowed to grab
573671d9d8SAndrew Thompson  *    any additional locks.
583671d9d8SAndrew Thompson  *
593671d9d8SAndrew Thompson  * 2) Netgraph context. This is where all the Netgraph related stuff happens.
603671d9d8SAndrew Thompson  *    Since we mark node as WRITER, the Netgraph node will be "locked" (from
613671d9d8SAndrew Thompson  *    Netgraph point of view). Any variable that is only modified from the
62053359b7SPedro F. Giffuni  *    Netgraph context does not require any additional locking. It is generally
633671d9d8SAndrew Thompson  *    *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
643671d9d8SAndrew Thompson  *    grab any lock in the Netgraph context that could cause de-scheduling of
653671d9d8SAndrew Thompson  *    the Netgraph thread for significant amount of time. In fact, the only
663671d9d8SAndrew Thompson  *    lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
673671d9d8SAndrew Thompson  *    Also make sure that any code that is called from the Netgraph context
683671d9d8SAndrew Thompson  *    follows the rule above.
693671d9d8SAndrew Thompson  *
703671d9d8SAndrew Thompson  * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
713671d9d8SAndrew Thompson  *    NOT allowed to grab any lock that could cause de-scheduling in the
723671d9d8SAndrew Thompson  *    Netgraph context, and, USB requires us to grab interface lock before
733671d9d8SAndrew Thompson  *    doing things with transfers, it is safer to transition from the Netgraph
743671d9d8SAndrew Thompson  *    context to the Taskqueue context before we can call into USB subsystem.
753671d9d8SAndrew Thompson  *
763671d9d8SAndrew Thompson  * So, to put everything together, the rules are as follows.
773671d9d8SAndrew Thompson  *	It is OK to call from the USB context or the Taskqueue context into
783671d9d8SAndrew Thompson  * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
793671d9d8SAndrew Thompson  * it is allowed to call into the Netgraph context with locks held.
803671d9d8SAndrew Thompson  *	Is it *NOT* OK to call from the Netgraph context into the USB context,
813671d9d8SAndrew Thompson  * because USB requires us to grab interface locks, and, it is safer to
823671d9d8SAndrew Thompson  * avoid it. So, to make things safer we set task flags to indicate which
833671d9d8SAndrew Thompson  * actions we want to perform and schedule ubt_task which would run in the
843671d9d8SAndrew Thompson  * Taskqueue context.
853671d9d8SAndrew Thompson  *	Is is OK to call from the Taskqueue context into the USB context,
863671d9d8SAndrew Thompson  * and, ubt_task does just that (i.e. grabs appropriate interface locks
873671d9d8SAndrew Thompson  * before calling into USB).
883671d9d8SAndrew Thompson  *	Access to the outgoing queues, task flags and hook pointer is
893671d9d8SAndrew Thompson  * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
903671d9d8SAndrew Thompson  * sc_ng_mtx should really be a spin lock (and it is very likely to an
913671d9d8SAndrew Thompson  * equivalent of spin lock due to adaptive nature of FreeBSD mutexes).
923671d9d8SAndrew Thompson  *	All USB callbacks accept softc pointer as a private data. USB ensures
933671d9d8SAndrew Thompson  * that this pointer is valid.
943671d9d8SAndrew Thompson  */
953671d9d8SAndrew Thompson 
96ed6d949aSAndrew Thompson #include <sys/stdint.h>
97ed6d949aSAndrew Thompson #include <sys/stddef.h>
98ed6d949aSAndrew Thompson #include <sys/param.h>
99ed6d949aSAndrew Thompson #include <sys/queue.h>
100ed6d949aSAndrew Thompson #include <sys/types.h>
101ed6d949aSAndrew Thompson #include <sys/systm.h>
102ed6d949aSAndrew Thompson #include <sys/kernel.h>
103ed6d949aSAndrew Thompson #include <sys/bus.h>
104ed6d949aSAndrew Thompson #include <sys/module.h>
105ed6d949aSAndrew Thompson #include <sys/lock.h>
106ed6d949aSAndrew Thompson #include <sys/mutex.h>
107ed6d949aSAndrew Thompson #include <sys/condvar.h>
108ed6d949aSAndrew Thompson #include <sys/sysctl.h>
109ed6d949aSAndrew Thompson #include <sys/sx.h>
110ed6d949aSAndrew Thompson #include <sys/unistd.h>
111ed6d949aSAndrew Thompson #include <sys/callout.h>
112ed6d949aSAndrew Thompson #include <sys/malloc.h>
113ed6d949aSAndrew Thompson #include <sys/priv.h>
114ed6d949aSAndrew Thompson 
1153671d9d8SAndrew Thompson #include "usbdevs.h"
1163671d9d8SAndrew Thompson #include <dev/usb/usb.h>
117ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
118ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
1193671d9d8SAndrew Thompson 
120a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_debug
1213671d9d8SAndrew Thompson #include <dev/usb/usb_debug.h>
1223671d9d8SAndrew Thompson #include <dev/usb/usb_busdma.h>
1233671d9d8SAndrew Thompson 
1243671d9d8SAndrew Thompson #include <sys/mbuf.h>
1253671d9d8SAndrew Thompson #include <sys/taskqueue.h>
1263671d9d8SAndrew Thompson 
1273671d9d8SAndrew Thompson #include <netgraph/ng_message.h>
1283671d9d8SAndrew Thompson #include <netgraph/netgraph.h>
1293671d9d8SAndrew Thompson #include <netgraph/ng_parse.h>
1303671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h>
1313671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_hci.h>
1323671d9d8SAndrew Thompson #include <netgraph/bluetooth/include/ng_ubt.h>
13306079f14SAndrew Thompson #include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>
1343671d9d8SAndrew Thompson 
1353671d9d8SAndrew Thompson static int		ubt_modevent(module_t, int, void *);
1363671d9d8SAndrew Thompson static device_probe_t	ubt_probe;
1373671d9d8SAndrew Thompson static device_attach_t	ubt_attach;
1383671d9d8SAndrew Thompson static device_detach_t	ubt_detach;
1393671d9d8SAndrew Thompson 
1403671d9d8SAndrew Thompson static void		ubt_task_schedule(ubt_softc_p, int);
1413671d9d8SAndrew Thompson static task_fn_t	ubt_task;
1423671d9d8SAndrew Thompson 
143a593f6b8SAndrew Thompson #define	ubt_xfer_start(sc, i)	usbd_transfer_start((sc)->sc_xfer[(i)])
1443671d9d8SAndrew Thompson 
1453671d9d8SAndrew Thompson /* Netgraph methods */
1463671d9d8SAndrew Thompson static ng_constructor_t	ng_ubt_constructor;
1473671d9d8SAndrew Thompson static ng_shutdown_t	ng_ubt_shutdown;
1483671d9d8SAndrew Thompson static ng_newhook_t	ng_ubt_newhook;
1493671d9d8SAndrew Thompson static ng_connect_t	ng_ubt_connect;
1503671d9d8SAndrew Thompson static ng_disconnect_t	ng_ubt_disconnect;
1513671d9d8SAndrew Thompson static ng_rcvmsg_t	ng_ubt_rcvmsg;
1523671d9d8SAndrew Thompson static ng_rcvdata_t	ng_ubt_rcvdata;
1533671d9d8SAndrew Thompson 
15467cbbf19SHans Petter Selasky static int ng_usb_isoc_enable = 1;
15567cbbf19SHans Petter Selasky 
15603f03934SHans Petter Selasky SYSCTL_INT(_net_bluetooth, OID_AUTO, usb_isoc_enable, CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
15767cbbf19SHans Petter Selasky     &ng_usb_isoc_enable, 0, "enable isochronous transfers");
15867cbbf19SHans Petter Selasky 
1593671d9d8SAndrew Thompson /* Queue length */
1603671d9d8SAndrew Thompson static const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
1613671d9d8SAndrew Thompson {
1623671d9d8SAndrew Thompson 	{ "queue", &ng_parse_int32_type, },
1633671d9d8SAndrew Thompson 	{ "qlen",  &ng_parse_int32_type, },
1643671d9d8SAndrew Thompson 	{ NULL, }
1653671d9d8SAndrew Thompson };
1663671d9d8SAndrew Thompson static const struct ng_parse_type		ng_ubt_node_qlen_type =
1673671d9d8SAndrew Thompson {
1683671d9d8SAndrew Thompson 	&ng_parse_struct_type,
1693671d9d8SAndrew Thompson 	&ng_ubt_node_qlen_type_fields
1703671d9d8SAndrew Thompson };
1713671d9d8SAndrew Thompson 
1723671d9d8SAndrew Thompson /* Stat info */
1733671d9d8SAndrew Thompson static const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
1743671d9d8SAndrew Thompson {
1753671d9d8SAndrew Thompson 	{ "pckts_recv", &ng_parse_uint32_type, },
1763671d9d8SAndrew Thompson 	{ "bytes_recv", &ng_parse_uint32_type, },
1773671d9d8SAndrew Thompson 	{ "pckts_sent", &ng_parse_uint32_type, },
1783671d9d8SAndrew Thompson 	{ "bytes_sent", &ng_parse_uint32_type, },
1793671d9d8SAndrew Thompson 	{ "oerrors",    &ng_parse_uint32_type, },
1803671d9d8SAndrew Thompson 	{ "ierrors",    &ng_parse_uint32_type, },
1813671d9d8SAndrew Thompson 	{ NULL, }
1823671d9d8SAndrew Thompson };
1833671d9d8SAndrew Thompson static const struct ng_parse_type		ng_ubt_node_stat_type =
1843671d9d8SAndrew Thompson {
1853671d9d8SAndrew Thompson 	&ng_parse_struct_type,
1863671d9d8SAndrew Thompson 	&ng_ubt_node_stat_type_fields
1873671d9d8SAndrew Thompson };
1883671d9d8SAndrew Thompson 
1893671d9d8SAndrew Thompson /* Netgraph node command list */
1903671d9d8SAndrew Thompson static const struct ng_cmdlist			ng_ubt_cmdlist[] =
1913671d9d8SAndrew Thompson {
1923671d9d8SAndrew Thompson 	{
1933671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
1943671d9d8SAndrew Thompson 		NGM_UBT_NODE_SET_DEBUG,
1953671d9d8SAndrew Thompson 		"set_debug",
1963671d9d8SAndrew Thompson 		&ng_parse_uint16_type,
1973671d9d8SAndrew Thompson 		NULL
1983671d9d8SAndrew Thompson 	},
1993671d9d8SAndrew Thompson 	{
2003671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2013671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_DEBUG,
2023671d9d8SAndrew Thompson 		"get_debug",
2033671d9d8SAndrew Thompson 		NULL,
2043671d9d8SAndrew Thompson 		&ng_parse_uint16_type
2053671d9d8SAndrew Thompson 	},
2063671d9d8SAndrew Thompson 	{
2073671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2083671d9d8SAndrew Thompson 		NGM_UBT_NODE_SET_QLEN,
2093671d9d8SAndrew Thompson 		"set_qlen",
2103671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type,
2113671d9d8SAndrew Thompson 		NULL
2123671d9d8SAndrew Thompson 	},
2133671d9d8SAndrew Thompson 	{
2143671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2153671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_QLEN,
2163671d9d8SAndrew Thompson 		"get_qlen",
2173671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type,
2183671d9d8SAndrew Thompson 		&ng_ubt_node_qlen_type
2193671d9d8SAndrew Thompson 	},
2203671d9d8SAndrew Thompson 	{
2213671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2223671d9d8SAndrew Thompson 		NGM_UBT_NODE_GET_STAT,
2233671d9d8SAndrew Thompson 		"get_stat",
2243671d9d8SAndrew Thompson 		NULL,
2253671d9d8SAndrew Thompson 		&ng_ubt_node_stat_type
2263671d9d8SAndrew Thompson 	},
2273671d9d8SAndrew Thompson 	{
2283671d9d8SAndrew Thompson 		NGM_UBT_COOKIE,
2293671d9d8SAndrew Thompson 		NGM_UBT_NODE_RESET_STAT,
2303671d9d8SAndrew Thompson 		"reset_stat",
2313671d9d8SAndrew Thompson 		NULL,
2323671d9d8SAndrew Thompson 		NULL
2333671d9d8SAndrew Thompson 	},
2343671d9d8SAndrew Thompson 	{ 0, }
2353671d9d8SAndrew Thompson };
2363671d9d8SAndrew Thompson 
2373671d9d8SAndrew Thompson /* Netgraph node type */
2383671d9d8SAndrew Thompson static struct ng_type	typestruct =
2393671d9d8SAndrew Thompson {
2403671d9d8SAndrew Thompson 	.version = 	NG_ABI_VERSION,
2413671d9d8SAndrew Thompson 	.name =		NG_UBT_NODE_TYPE,
2423671d9d8SAndrew Thompson 	.constructor =	ng_ubt_constructor,
2433671d9d8SAndrew Thompson 	.rcvmsg =	ng_ubt_rcvmsg,
2443671d9d8SAndrew Thompson 	.shutdown =	ng_ubt_shutdown,
2453671d9d8SAndrew Thompson 	.newhook =	ng_ubt_newhook,
2463671d9d8SAndrew Thompson 	.connect =	ng_ubt_connect,
2473671d9d8SAndrew Thompson 	.rcvdata =	ng_ubt_rcvdata,
2483671d9d8SAndrew Thompson 	.disconnect =	ng_ubt_disconnect,
2493671d9d8SAndrew Thompson 	.cmdlist =	ng_ubt_cmdlist
2503671d9d8SAndrew Thompson };
2513671d9d8SAndrew Thompson 
2523671d9d8SAndrew Thompson /****************************************************************************
2533671d9d8SAndrew Thompson  ****************************************************************************
2543671d9d8SAndrew Thompson  **                              USB specific
2553671d9d8SAndrew Thompson  ****************************************************************************
2563671d9d8SAndrew Thompson  ****************************************************************************/
2573671d9d8SAndrew Thompson 
2583671d9d8SAndrew Thompson /* USB methods */
2593544d43bSVladimir Kondratyev static usb_callback_t	ubt_probe_intr_callback;
260e0a69b51SAndrew Thompson static usb_callback_t	ubt_ctrl_write_callback;
261e0a69b51SAndrew Thompson static usb_callback_t	ubt_intr_read_callback;
262e0a69b51SAndrew Thompson static usb_callback_t	ubt_bulk_read_callback;
263e0a69b51SAndrew Thompson static usb_callback_t	ubt_bulk_write_callback;
264e0a69b51SAndrew Thompson static usb_callback_t	ubt_isoc_read_callback;
265e0a69b51SAndrew Thompson static usb_callback_t	ubt_isoc_write_callback;
2663671d9d8SAndrew Thompson 
2673671d9d8SAndrew Thompson static int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
268760bc48eSAndrew Thompson static int		ubt_isoc_read_one_frame(struct usb_xfer *, int);
2693671d9d8SAndrew Thompson 
2703671d9d8SAndrew Thompson /*
2713671d9d8SAndrew Thompson  * USB config
2723671d9d8SAndrew Thompson  *
2733671d9d8SAndrew Thompson  * The following desribes usb transfers that could be submitted on USB device.
2743671d9d8SAndrew Thompson  *
2753671d9d8SAndrew Thompson  * Interface 0 on the USB device must present the following endpoints
2763671d9d8SAndrew Thompson  *	1) Interrupt endpoint to receive HCI events
2773671d9d8SAndrew Thompson  *	2) Bulk IN endpoint to receive ACL data
2783671d9d8SAndrew Thompson  *	3) Bulk OUT endpoint to send ACL data
2793671d9d8SAndrew Thompson  *
2803671d9d8SAndrew Thompson  * Interface 1 on the USB device must present the following endpoints
2813671d9d8SAndrew Thompson  *	1) Isochronous IN endpoint to receive SCO data
2823671d9d8SAndrew Thompson  *	2) Isochronous OUT endpoint to send SCO data
2833671d9d8SAndrew Thompson  */
2843671d9d8SAndrew Thompson 
285760bc48eSAndrew Thompson static const struct usb_config		ubt_config[UBT_N_TRANSFER] =
2863671d9d8SAndrew Thompson {
2873671d9d8SAndrew Thompson 	/*
2883671d9d8SAndrew Thompson 	 * Interface #0
2893671d9d8SAndrew Thompson  	 */
2903671d9d8SAndrew Thompson 
2913671d9d8SAndrew Thompson 	/* Outgoing bulk transfer - ACL packets */
2923671d9d8SAndrew Thompson 	[UBT_IF_0_BULK_DT_WR] = {
2933671d9d8SAndrew Thompson 		.type =		UE_BULK,
2943671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
2953671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
2963671d9d8SAndrew Thompson 		.if_index = 	0,
2973671d9d8SAndrew Thompson 		.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
2983671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
2993671d9d8SAndrew Thompson 		.callback =	&ubt_bulk_write_callback,
3003671d9d8SAndrew Thompson 	},
3013671d9d8SAndrew Thompson 	/* Incoming bulk transfer - ACL packets */
3023671d9d8SAndrew Thompson 	[UBT_IF_0_BULK_DT_RD] = {
3033671d9d8SAndrew Thompson 		.type =		UE_BULK,
3043671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3053671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3063671d9d8SAndrew Thompson 		.if_index = 	0,
3073671d9d8SAndrew Thompson 		.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
3083671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
3093671d9d8SAndrew Thompson 		.callback =	&ubt_bulk_read_callback,
3103671d9d8SAndrew Thompson 	},
3113671d9d8SAndrew Thompson 	/* Incoming interrupt transfer - HCI events */
3123671d9d8SAndrew Thompson 	[UBT_IF_0_INTR_DT_RD] = {
3133671d9d8SAndrew Thompson 		.type =		UE_INTERRUPT,
3143671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3153671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3163671d9d8SAndrew Thompson 		.if_index = 	0,
3173671d9d8SAndrew Thompson 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
3183671d9d8SAndrew Thompson 		.bufsize =	UBT_INTR_BUFFER_SIZE,
3193671d9d8SAndrew Thompson 		.callback =	&ubt_intr_read_callback,
3203671d9d8SAndrew Thompson 	},
3213671d9d8SAndrew Thompson 	/* Outgoing control transfer - HCI commands */
3223671d9d8SAndrew Thompson 	[UBT_IF_0_CTRL_DT_WR] = {
3233671d9d8SAndrew Thompson 		.type =		UE_CONTROL,
3243671d9d8SAndrew Thompson 		.endpoint =	0x00,	/* control pipe */
3253671d9d8SAndrew Thompson 		.direction =	UE_DIR_ANY,
3263671d9d8SAndrew Thompson 		.if_index = 	0,
3273671d9d8SAndrew Thompson 		.bufsize =	UBT_CTRL_BUFFER_SIZE,
3283671d9d8SAndrew Thompson 		.callback =	&ubt_ctrl_write_callback,
3293671d9d8SAndrew Thompson 		.timeout =	5000,	/* 5 seconds */
3303671d9d8SAndrew Thompson 	},
3313671d9d8SAndrew Thompson 
3323671d9d8SAndrew Thompson 	/*
3333671d9d8SAndrew Thompson 	 * Interface #1
3343671d9d8SAndrew Thompson  	 */
3353671d9d8SAndrew Thompson 
3363671d9d8SAndrew Thompson 	/* Incoming isochronous transfer #1 - SCO packets */
3373671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_RD1] = {
3383671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3393671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3403671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3413671d9d8SAndrew Thompson 		.if_index = 	1,
3423671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3433671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3443671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3453671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_read_callback,
3463671d9d8SAndrew Thompson 	},
3473671d9d8SAndrew Thompson 	/* Incoming isochronous transfer #2 - SCO packets */
3483671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_RD2] = {
3493671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3503671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3513671d9d8SAndrew Thompson 		.direction =	UE_DIR_IN,
3523671d9d8SAndrew Thompson 		.if_index = 	1,
3533671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3543671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3553671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3563671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_read_callback,
3573671d9d8SAndrew Thompson 	},
3583671d9d8SAndrew Thompson 	/* Outgoing isochronous transfer #1 - SCO packets */
3593671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_WR1] = {
3603671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3613671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3623671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
3633671d9d8SAndrew Thompson 		.if_index = 	1,
3643671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3653671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3663671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3673671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_write_callback,
3683671d9d8SAndrew Thompson 	},
3693671d9d8SAndrew Thompson 	/* Outgoing isochronous transfer #2 - SCO packets */
3703671d9d8SAndrew Thompson 	[UBT_IF_1_ISOC_DT_WR2] = {
3713671d9d8SAndrew Thompson 		.type =		UE_ISOCHRONOUS,
3723671d9d8SAndrew Thompson 		.endpoint =	UE_ADDR_ANY,
3733671d9d8SAndrew Thompson 		.direction =	UE_DIR_OUT,
3743671d9d8SAndrew Thompson 		.if_index = 	1,
3753671d9d8SAndrew Thompson 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
3763671d9d8SAndrew Thompson 		.frames =	UBT_ISOC_NFRAMES,
3773671d9d8SAndrew Thompson 		.flags =	{ .short_xfer_ok = 1, },
3783671d9d8SAndrew Thompson 		.callback =	&ubt_isoc_write_callback,
3793671d9d8SAndrew Thompson 	},
3803671d9d8SAndrew Thompson };
3813671d9d8SAndrew Thompson 
3823671d9d8SAndrew Thompson /*
3833671d9d8SAndrew Thompson  * If for some reason device should not be attached then put
3843671d9d8SAndrew Thompson  * VendorID/ProductID pair into the list below. The format is
3853671d9d8SAndrew Thompson  * as follows:
3863671d9d8SAndrew Thompson  *
3873671d9d8SAndrew Thompson  *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
3883671d9d8SAndrew Thompson  *
3893671d9d8SAndrew Thompson  * where VENDOR_ID and PRODUCT_ID are hex numbers.
3903671d9d8SAndrew Thompson  */
3913671d9d8SAndrew Thompson 
392f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_ignore_devs[] =
3933671d9d8SAndrew Thompson {
3943671d9d8SAndrew Thompson 	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
3953671d9d8SAndrew Thompson 	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
396c36c780dSAdrian Chadd 
397c36c780dSAdrian Chadd 	/* Atheros 3011 with sflash firmware */
398c36c780dSAdrian Chadd 	{ USB_VPI(0x0cf3, 0x3002, 0) },
399c36c780dSAdrian Chadd 	{ USB_VPI(0x0cf3, 0xe019, 0) },
400c36c780dSAdrian Chadd 	{ USB_VPI(0x13d3, 0x3304, 0) },
401c36c780dSAdrian Chadd 	{ USB_VPI(0x0930, 0x0215, 0) },
402c36c780dSAdrian Chadd 	{ USB_VPI(0x0489, 0xe03d, 0) },
403c36c780dSAdrian Chadd 	{ USB_VPI(0x0489, 0xe027, 0) },
404c36c780dSAdrian Chadd 
405c36c780dSAdrian Chadd 	/* Atheros AR9285 Malbec with sflash firmware */
406c36c780dSAdrian Chadd 	{ USB_VPI(0x03f0, 0x311d, 0) },
407c36c780dSAdrian Chadd 
408c36c780dSAdrian Chadd 	/* Atheros 3012 with sflash firmware */
4090818ec92SAdrian Chadd 	{ USB_VPI(0x0cf3, 0x3004, 0), USB_DEV_BCD_LTEQ(1) },
4100818ec92SAdrian Chadd 	{ USB_VPI(0x0cf3, 0x311d, 0), USB_DEV_BCD_LTEQ(1) },
4110818ec92SAdrian Chadd 	{ USB_VPI(0x13d3, 0x3375, 0), USB_DEV_BCD_LTEQ(1) },
4120818ec92SAdrian Chadd 	{ USB_VPI(0x04ca, 0x3005, 0), USB_DEV_BCD_LTEQ(1) },
4130818ec92SAdrian Chadd 	{ USB_VPI(0x04ca, 0x3006, 0), USB_DEV_BCD_LTEQ(1) },
4140818ec92SAdrian Chadd 	{ USB_VPI(0x04ca, 0x3008, 0), USB_DEV_BCD_LTEQ(1) },
4150818ec92SAdrian Chadd 	{ USB_VPI(0x13d3, 0x3362, 0), USB_DEV_BCD_LTEQ(1) },
4160818ec92SAdrian Chadd 	{ USB_VPI(0x0cf3, 0xe004, 0), USB_DEV_BCD_LTEQ(1) },
4170818ec92SAdrian Chadd 	{ USB_VPI(0x0930, 0x0219, 0), USB_DEV_BCD_LTEQ(1) },
4180818ec92SAdrian Chadd 	{ USB_VPI(0x0489, 0xe057, 0), USB_DEV_BCD_LTEQ(1) },
4190818ec92SAdrian Chadd 	{ USB_VPI(0x13d3, 0x3393, 0), USB_DEV_BCD_LTEQ(1) },
4200818ec92SAdrian Chadd 	{ USB_VPI(0x0489, 0xe04e, 0), USB_DEV_BCD_LTEQ(1) },
4210818ec92SAdrian Chadd 	{ USB_VPI(0x0489, 0xe056, 0), USB_DEV_BCD_LTEQ(1) },
422c36c780dSAdrian Chadd 
423c36c780dSAdrian Chadd 	/* Atheros AR5BBU12 with sflash firmware */
4240818ec92SAdrian Chadd 	{ USB_VPI(0x0489, 0xe02c, 0), USB_DEV_BCD_LTEQ(1) },
425c36c780dSAdrian Chadd 
426c36c780dSAdrian Chadd 	/* Atheros AR5BBU12 with sflash firmware */
4270818ec92SAdrian Chadd 	{ USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) },
4280818ec92SAdrian Chadd 	{ USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) },
4293544d43bSVladimir Kondratyev 
430788a171cSVladimir Kondratyev 	/* Intel Wireless controllers are handled in ng_ubt_intel.c */
431788a171cSVladimir Kondratyev 	{ USB_VPI(USB_VENDOR_INTEL2, 0x07dc, 0) },
432788a171cSVladimir Kondratyev 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0a2a, 0) },
433788a171cSVladimir Kondratyev 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0aa7, 0) },
4343544d43bSVladimir Kondratyev 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0a2b, 0) },
4353544d43bSVladimir Kondratyev 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0aaa, 0) },
4363544d43bSVladimir Kondratyev 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0025, 0) },
4373544d43bSVladimir Kondratyev 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0026, 0) },
4383544d43bSVladimir Kondratyev 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0029, 0) },
439b25ba58aSMark Johnston 
440b25ba58aSMark Johnston 	/*
441b25ba58aSMark Johnston 	 * Some Intel controllers are not yet supported by ng_ubt_intel and
442b25ba58aSMark Johnston 	 * should be ignored.
443b25ba58aSMark Johnston 	 */
444b25ba58aSMark Johnston 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0032, 0) },
445b25ba58aSMark Johnston 	{ USB_VPI(USB_VENDOR_INTEL2, 0x0033, 0) },
4468b21c469SKevin Lo 
4478b21c469SKevin Lo 	/* MediaTek MT7925 */
4488b21c469SKevin Lo 	{ USB_VPI(USB_VENDOR_AZUREWAVE, 0x3602, 0) },
4498b21c469SKevin Lo 	{ USB_VPI(USB_VENDOR_AZUREWAVE, 0x3604, 0) },
4503671d9d8SAndrew Thompson };
4513671d9d8SAndrew Thompson 
4523671d9d8SAndrew Thompson /* List of supported bluetooth devices */
453f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ubt_devs[] =
4543671d9d8SAndrew Thompson {
4553671d9d8SAndrew Thompson 	/* Generic Bluetooth class devices */
4563671d9d8SAndrew Thompson 	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
4573671d9d8SAndrew Thompson 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
4583671d9d8SAndrew Thompson 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
4593671d9d8SAndrew Thompson 
4603671d9d8SAndrew Thompson 	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
4613671d9d8SAndrew Thompson 	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
46270a0e340SGleb Smirnoff 
46370a0e340SGleb Smirnoff 	/* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
46470a0e340SGleb Smirnoff 	{ USB_VENDOR(USB_VENDOR_BROADCOM),
465ac76cdc6SRaphael Kubo da Costa 	  USB_IFACE_CLASS(UICLASS_VENDOR),
466ac76cdc6SRaphael Kubo da Costa 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
467ac76cdc6SRaphael Kubo da Costa 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
46833f4aa11SHans Petter Selasky 
46933f4aa11SHans Petter Selasky 	/* Apple-specific (Broadcom) devices */
47033f4aa11SHans Petter Selasky 	{ USB_VENDOR(USB_VENDOR_APPLE),
47133f4aa11SHans Petter Selasky 	  USB_IFACE_CLASS(UICLASS_VENDOR),
47233f4aa11SHans Petter Selasky 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
47333f4aa11SHans Petter Selasky 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
47433f4aa11SHans Petter Selasky 
47533f4aa11SHans Petter Selasky 	/* Foxconn - Hon Hai */
47633f4aa11SHans Petter Selasky 	{ USB_VENDOR(USB_VENDOR_FOXCONN),
47733f4aa11SHans Petter Selasky 	  USB_IFACE_CLASS(UICLASS_VENDOR),
47833f4aa11SHans Petter Selasky 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
47933f4aa11SHans Petter Selasky 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
48033f4aa11SHans Petter Selasky 
48133f4aa11SHans Petter Selasky 	/* MediaTek MT76x0E */
48233f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },
48333f4aa11SHans Petter Selasky 
48433f4aa11SHans Petter Selasky 	/* Broadcom SoftSailing reporting vendor specific */
48533f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },
48633f4aa11SHans Petter Selasky 
48733f4aa11SHans Petter Selasky 	/* Apple MacBookPro 7,1 */
48833f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },
48933f4aa11SHans Petter Selasky 
49033f4aa11SHans Petter Selasky 	/* Apple iMac11,1 */
49133f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },
49233f4aa11SHans Petter Selasky 
49333f4aa11SHans Petter Selasky 	/* Apple MacBookPro6,2 */
49433f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },
49533f4aa11SHans Petter Selasky 
49633f4aa11SHans Petter Selasky 	/* Apple MacBookAir3,1, MacBookAir3,2 */
49733f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },
49833f4aa11SHans Petter Selasky 
49933f4aa11SHans Petter Selasky 	/* Apple MacBookAir4,1 */
50033f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },
50133f4aa11SHans Petter Selasky 
50233f4aa11SHans Petter Selasky 	/* MacBookAir6,1 */
50333f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },
50433f4aa11SHans Petter Selasky 
50533f4aa11SHans Petter Selasky 	/* Apple MacBookPro8,2 */
50633f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },
50733f4aa11SHans Petter Selasky 
50833f4aa11SHans Petter Selasky 	/* Apple MacMini5,1 */
50933f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },
51033f4aa11SHans Petter Selasky 
51133f4aa11SHans Petter Selasky 	/* Bluetooth Ultraport Module from IBM */
51233f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },
51333f4aa11SHans Petter Selasky 
51433f4aa11SHans Petter Selasky 	/* ALPS Modules with non-standard ID */
51533f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },
51633f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },
51733f4aa11SHans Petter Selasky 
51833f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },
51933f4aa11SHans Petter Selasky 
52033f4aa11SHans Petter Selasky 	/* Canyon CN-BTU1 with HID interfaces */
52133f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },
52233f4aa11SHans Petter Selasky 
52333f4aa11SHans Petter Selasky 	/* Broadcom BCM20702A0 */
52433f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },
525c6721651SEitan Adler 	{ USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },
52633f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },
52733f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },
52833f4aa11SHans Petter Selasky 	{ USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
5293a3992fbSMark Johnston 	{ USB_VPI(USB_VENDOR_BELKIN, 0x065a, 0) },
5303671d9d8SAndrew Thompson };
5313671d9d8SAndrew Thompson 
5323671d9d8SAndrew Thompson /*
5333544d43bSVladimir Kondratyev  * Does a synchronous (waits for completion event) execution of HCI command.
5343544d43bSVladimir Kondratyev  * Size of both command and response buffers are passed in length field of
5353544d43bSVladimir Kondratyev  * corresponding structures in "Parameter Total Length" format i.e.
5363544d43bSVladimir Kondratyev  * not including HCI packet headers.
5370b23c50aSVladimir Kondratyev  * Expected event code must be placed into "Event code" of the response buffer.
5383544d43bSVladimir Kondratyev  *
5393544d43bSVladimir Kondratyev  * Must not be used after USB transfers have been configured in attach routine.
5403544d43bSVladimir Kondratyev  */
5413544d43bSVladimir Kondratyev 
5423544d43bSVladimir Kondratyev usb_error_t
ubt_do_hci_request(struct usb_device * udev,struct ubt_hci_cmd * cmd,void * evt,usb_timeout_t timeout)5433544d43bSVladimir Kondratyev ubt_do_hci_request(struct usb_device *udev, struct ubt_hci_cmd *cmd,
5443544d43bSVladimir Kondratyev     void *evt, usb_timeout_t timeout)
5453544d43bSVladimir Kondratyev {
5463544d43bSVladimir Kondratyev 	static const struct usb_config ubt_probe_config = {
5473544d43bSVladimir Kondratyev 		.type = UE_INTERRUPT,
5483544d43bSVladimir Kondratyev 		.endpoint = UE_ADDR_ANY,
5493544d43bSVladimir Kondratyev 		.direction = UE_DIR_IN,
5503544d43bSVladimir Kondratyev 		.flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
5513544d43bSVladimir Kondratyev 		.bufsize = UBT_INTR_BUFFER_SIZE,
5523544d43bSVladimir Kondratyev 		.callback = &ubt_probe_intr_callback,
5533544d43bSVladimir Kondratyev 	};
5543544d43bSVladimir Kondratyev 	struct usb_device_request req;
5553544d43bSVladimir Kondratyev 	struct usb_xfer *xfer[1];
5563544d43bSVladimir Kondratyev 	struct mtx mtx;
5573544d43bSVladimir Kondratyev 	usb_error_t error = USB_ERR_NORMAL_COMPLETION;
5583544d43bSVladimir Kondratyev 	uint8_t iface_index = 0;
5593544d43bSVladimir Kondratyev 
5603544d43bSVladimir Kondratyev 	/* Initialize a USB control request and then do it */
5613544d43bSVladimir Kondratyev 	bzero(&req, sizeof(req));
5623544d43bSVladimir Kondratyev 	req.bmRequestType = UBT_HCI_REQUEST;
5633544d43bSVladimir Kondratyev 	req.wIndex[0] = iface_index;
5643544d43bSVladimir Kondratyev 	USETW(req.wLength, UBT_HCI_CMD_SIZE(cmd));
5653544d43bSVladimir Kondratyev 
5663544d43bSVladimir Kondratyev 	error = usbd_do_request(udev, NULL, &req, cmd);
5673544d43bSVladimir Kondratyev 	if (error != USB_ERR_NORMAL_COMPLETION) {
5683544d43bSVladimir Kondratyev 		printf("ng_ubt: usbd_do_request error=%s\n",
5693544d43bSVladimir Kondratyev 			usbd_errstr(error));
5703544d43bSVladimir Kondratyev 		return (error);
5713544d43bSVladimir Kondratyev 	}
5723544d43bSVladimir Kondratyev 
5733544d43bSVladimir Kondratyev 	if (evt == NULL)
5743544d43bSVladimir Kondratyev 		return (USB_ERR_NORMAL_COMPLETION);
5753544d43bSVladimir Kondratyev 
5760b23c50aSVladimir Kondratyev 	/* Save operation code if we expect completion event in response */
5770b23c50aSVladimir Kondratyev 	if(((struct ubt_hci_event *)evt)->header.event ==
5780b23c50aSVladimir Kondratyev 	    NG_HCI_EVENT_COMMAND_COMPL)
5790b23c50aSVladimir Kondratyev 		((struct ubt_hci_event_command_compl *)evt)->opcode =
5800b23c50aSVladimir Kondratyev 		  cmd->opcode;
5810b23c50aSVladimir Kondratyev 
5823544d43bSVladimir Kondratyev 	/* Initialize INTR endpoint xfer and wait for response */
583443127c5SXin LI 	mtx_init(&mtx, "ubt pb", NULL, MTX_DEF | MTX_NEW);
5843544d43bSVladimir Kondratyev 
5853544d43bSVladimir Kondratyev 	error = usbd_transfer_setup(udev, &iface_index, xfer,
5863544d43bSVladimir Kondratyev 	    &ubt_probe_config, 1, evt, &mtx);
5873544d43bSVladimir Kondratyev 	if (error == USB_ERR_NORMAL_COMPLETION) {
5883544d43bSVladimir Kondratyev 		mtx_lock(&mtx);
5893544d43bSVladimir Kondratyev 		usbd_transfer_start(*xfer);
5903544d43bSVladimir Kondratyev 
5913544d43bSVladimir Kondratyev 		if (msleep_sbt(evt, &mtx, 0, "ubt pb", SBT_1MS * timeout,
5923544d43bSVladimir Kondratyev 				0, C_HARDCLOCK) == EWOULDBLOCK) {
5933544d43bSVladimir Kondratyev 			printf("ng_ubt: HCI command 0x%04x timed out\n",
5943544d43bSVladimir Kondratyev 				le16toh(cmd->opcode));
5953544d43bSVladimir Kondratyev 			error = USB_ERR_TIMEOUT;
5963544d43bSVladimir Kondratyev 		}
5973544d43bSVladimir Kondratyev 
5983544d43bSVladimir Kondratyev 		usbd_transfer_stop(*xfer);
5993544d43bSVladimir Kondratyev 		mtx_unlock(&mtx);
6003544d43bSVladimir Kondratyev 
6013544d43bSVladimir Kondratyev 		usbd_transfer_unsetup(xfer, 1);
6023544d43bSVladimir Kondratyev 	} else
6033544d43bSVladimir Kondratyev 		printf("ng_ubt: usbd_transfer_setup error=%s\n",
6043544d43bSVladimir Kondratyev 			usbd_errstr(error));
6053544d43bSVladimir Kondratyev 
6063544d43bSVladimir Kondratyev 	mtx_destroy(&mtx);
6073544d43bSVladimir Kondratyev 
6083544d43bSVladimir Kondratyev 	return (error);
6093544d43bSVladimir Kondratyev }
6103544d43bSVladimir Kondratyev 
6113544d43bSVladimir Kondratyev /*
6123671d9d8SAndrew Thompson  * Probe for a USB Bluetooth device.
6133671d9d8SAndrew Thompson  * USB context.
6143671d9d8SAndrew Thompson  */
6153671d9d8SAndrew Thompson 
6163671d9d8SAndrew Thompson static int
ubt_probe(device_t dev)6173671d9d8SAndrew Thompson ubt_probe(device_t dev)
6183671d9d8SAndrew Thompson {
619760bc48eSAndrew Thompson 	struct usb_attach_arg	*uaa = device_get_ivars(dev);
6209d28e15eSHans Petter Selasky 	const struct usb_device_id *id;
6213671d9d8SAndrew Thompson 
6223671d9d8SAndrew Thompson 	if (uaa->usb_mode != USB_MODE_HOST)
6233671d9d8SAndrew Thompson 		return (ENXIO);
6243671d9d8SAndrew Thompson 
625a593f6b8SAndrew Thompson 	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
6263671d9d8SAndrew Thompson 			sizeof(ubt_ignore_devs), uaa) == 0)
6273671d9d8SAndrew Thompson 		return (ENXIO);
628*24ae172aSVladimir Kondratyev 	if (usbd_lookup_id_by_uaa(ubt_rtl_devs,
629*24ae172aSVladimir Kondratyev 			ubt_rtl_devs_sizeof, uaa) == 0)
630*24ae172aSVladimir Kondratyev 		return (ENXIO);
6313671d9d8SAndrew Thompson 
6329d28e15eSHans Petter Selasky 	id = usbd_lookup_id_by_info(ubt_devs,
6339d28e15eSHans Petter Selasky 	    sizeof(ubt_devs), &uaa->info);
6349d28e15eSHans Petter Selasky 	if (id == NULL)
6359d28e15eSHans Petter Selasky 		return (ENXIO);
6369d28e15eSHans Petter Selasky 
6379d28e15eSHans Petter Selasky 	if (uaa->info.bIfaceIndex != 0) {
6389d28e15eSHans Petter Selasky 		/* make sure we are matching the interface */
6399d28e15eSHans Petter Selasky 		if (id->match_flag_int_class &&
6409d28e15eSHans Petter Selasky 		    id->match_flag_int_subclass &&
6419d28e15eSHans Petter Selasky 		    id->match_flag_int_protocol)
642a5db8fd1SAndriy Gapon 			return (BUS_PROBE_GENERIC);
6439d28e15eSHans Petter Selasky 		else
6449d28e15eSHans Petter Selasky 			return (ENXIO);
6459d28e15eSHans Petter Selasky 	} else {
6469d28e15eSHans Petter Selasky 		return (BUS_PROBE_GENERIC);
6479d28e15eSHans Petter Selasky 	}
6483671d9d8SAndrew Thompson } /* ubt_probe */
6493671d9d8SAndrew Thompson 
6503671d9d8SAndrew Thompson /*
6513671d9d8SAndrew Thompson  * Attach the device.
6523671d9d8SAndrew Thompson  * USB context.
6533671d9d8SAndrew Thompson  */
6543671d9d8SAndrew Thompson 
6553671d9d8SAndrew Thompson static int
ubt_attach(device_t dev)6563671d9d8SAndrew Thompson ubt_attach(device_t dev)
6573671d9d8SAndrew Thompson {
658760bc48eSAndrew Thompson 	struct usb_attach_arg		*uaa = device_get_ivars(dev);
6593671d9d8SAndrew Thompson 	struct ubt_softc		*sc = device_get_softc(dev);
660760bc48eSAndrew Thompson 	struct usb_endpoint_descriptor	*ed;
661760bc48eSAndrew Thompson 	struct usb_interface_descriptor *id;
6629d28e15eSHans Petter Selasky 	struct usb_interface		*iface[2];
6634ac6682cSHans Petter Selasky 	uint32_t			wMaxPacketSize;
6643671d9d8SAndrew Thompson 	uint8_t				alt_index, i, j;
6659d28e15eSHans Petter Selasky 	uint8_t				iface_index[2];
6663671d9d8SAndrew Thompson 
667a593f6b8SAndrew Thompson 	device_set_usb_desc(dev);
6683671d9d8SAndrew Thompson 
6699d28e15eSHans Petter Selasky 	iface_index[0] = uaa->info.bIfaceIndex;
6709d28e15eSHans Petter Selasky 	iface_index[1] = uaa->info.bIfaceIndex + 1;
6719d28e15eSHans Petter Selasky 
6729d28e15eSHans Petter Selasky 	iface[0] = usbd_get_iface(uaa->device, iface_index[0]);
6739d28e15eSHans Petter Selasky 	iface[1] = usbd_get_iface(uaa->device, iface_index[1]);
6749d28e15eSHans Petter Selasky 
6753671d9d8SAndrew Thompson 	sc->sc_dev = dev;
6763671d9d8SAndrew Thompson 	sc->sc_debug = NG_UBT_WARN_LEVEL;
67767272c5aSCraig Rodrigues 
6783671d9d8SAndrew Thompson 	/*
6799d28e15eSHans Petter Selasky 	 * Sanity checks.
6809d28e15eSHans Petter Selasky 	 */
6819d28e15eSHans Petter Selasky 
6829d28e15eSHans Petter Selasky 	if (iface[0] == NULL || iface[1] == NULL ||
6839d28e15eSHans Petter Selasky 	    iface[0]->idesc == NULL || iface[1]->idesc == NULL) {
6849d28e15eSHans Petter Selasky 		UBT_ALERT(sc, "could not get two interfaces\n");
6859d28e15eSHans Petter Selasky 		return (ENXIO);
6869d28e15eSHans Petter Selasky 	}
6879d28e15eSHans Petter Selasky 
6889d28e15eSHans Petter Selasky 	/*
6893671d9d8SAndrew Thompson 	 * Create Netgraph node
6903671d9d8SAndrew Thompson 	 */
6913671d9d8SAndrew Thompson 
6923671d9d8SAndrew Thompson 	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
6933671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not create Netgraph node\n");
6943671d9d8SAndrew Thompson 		return (ENXIO);
6953671d9d8SAndrew Thompson 	}
6963671d9d8SAndrew Thompson 
6973671d9d8SAndrew Thompson 	/* Name Netgraph node */
6983671d9d8SAndrew Thompson 	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
6993671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not name Netgraph node\n");
7003671d9d8SAndrew Thompson 		NG_NODE_UNREF(sc->sc_node);
7013671d9d8SAndrew Thompson 		return (ENXIO);
7023671d9d8SAndrew Thompson 	}
7033671d9d8SAndrew Thompson 	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
7043671d9d8SAndrew Thompson 	NG_NODE_FORCE_WRITER(sc->sc_node);
7053671d9d8SAndrew Thompson 
7063671d9d8SAndrew Thompson 	/*
7073671d9d8SAndrew Thompson 	 * Initialize device softc structure
7083671d9d8SAndrew Thompson 	 */
7093671d9d8SAndrew Thompson 
7103671d9d8SAndrew Thompson 	/* initialize locks */
7113671d9d8SAndrew Thompson 	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
7123671d9d8SAndrew Thompson 	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
7133671d9d8SAndrew Thompson 
7143671d9d8SAndrew Thompson 	/* initialize packet queues */
7153671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
7163671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
7173671d9d8SAndrew Thompson 	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
7183671d9d8SAndrew Thompson 
7193671d9d8SAndrew Thompson 	/* initialize glue task */
7203671d9d8SAndrew Thompson 	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
7213671d9d8SAndrew Thompson 
7223671d9d8SAndrew Thompson 	/*
7233671d9d8SAndrew Thompson 	 * Configure Bluetooth USB device. Discover all required USB
7243671d9d8SAndrew Thompson 	 * interfaces and endpoints.
7253671d9d8SAndrew Thompson 	 *
7263671d9d8SAndrew Thompson 	 * USB device must present two interfaces:
7273671d9d8SAndrew Thompson 	 * 1) Interface 0 that has 3 endpoints
7283671d9d8SAndrew Thompson 	 *	1) Interrupt endpoint to receive HCI events
7293671d9d8SAndrew Thompson 	 *	2) Bulk IN endpoint to receive ACL data
7303671d9d8SAndrew Thompson 	 *	3) Bulk OUT endpoint to send ACL data
7313671d9d8SAndrew Thompson 	 *
7323671d9d8SAndrew Thompson 	 * 2) Interface 1 then has 2 endpoints
7333671d9d8SAndrew Thompson 	 *	1) Isochronous IN endpoint to receive SCO data
7343671d9d8SAndrew Thompson  	 *	2) Isochronous OUT endpoint to send SCO data
7353671d9d8SAndrew Thompson 	 *
7363671d9d8SAndrew Thompson 	 * Interface 1 (with isochronous endpoints) has several alternate
7373671d9d8SAndrew Thompson 	 * configurations with different packet size.
7383671d9d8SAndrew Thompson 	 */
7393671d9d8SAndrew Thompson 
7403671d9d8SAndrew Thompson 	/*
7413671d9d8SAndrew Thompson 	 * For interface #1 search alternate settings, and find
7423671d9d8SAndrew Thompson 	 * the descriptor with the largest wMaxPacketSize
7433671d9d8SAndrew Thompson 	 */
7443671d9d8SAndrew Thompson 
7453671d9d8SAndrew Thompson 	wMaxPacketSize = 0;
7463671d9d8SAndrew Thompson 	alt_index = 0;
7473671d9d8SAndrew Thompson 	i = 0;
7483671d9d8SAndrew Thompson 	j = 0;
7493671d9d8SAndrew Thompson 	ed = NULL;
7503671d9d8SAndrew Thompson 
7513671d9d8SAndrew Thompson 	/*
7523671d9d8SAndrew Thompson 	 * Search through all the descriptors looking for the largest
7533671d9d8SAndrew Thompson 	 * packet size:
7543671d9d8SAndrew Thompson 	 */
755a593f6b8SAndrew Thompson 	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
756a593f6b8SAndrew Thompson 	    usbd_get_config_descriptor(uaa->device),
757760bc48eSAndrew Thompson 	    (struct usb_descriptor *)ed))) {
7583671d9d8SAndrew Thompson 		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
7593671d9d8SAndrew Thompson 		    (ed->bLength >= sizeof(*id))) {
760760bc48eSAndrew Thompson 			id = (struct usb_interface_descriptor *)ed;
7619d28e15eSHans Petter Selasky 			i = (id->bInterfaceNumber == iface[1]->idesc->bInterfaceNumber);
7623671d9d8SAndrew Thompson 			j = id->bAlternateSetting;
7633671d9d8SAndrew Thompson 		}
7643671d9d8SAndrew Thompson 
7653671d9d8SAndrew Thompson 		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
7663671d9d8SAndrew Thompson 		    (ed->bLength >= sizeof(*ed)) &&
7679d28e15eSHans Petter Selasky 		    (i != 0)) {
7684ac6682cSHans Petter Selasky 			uint32_t temp;
7693671d9d8SAndrew Thompson 
7704ac6682cSHans Petter Selasky 			temp = usbd_get_max_frame_length(
7714ac6682cSHans Petter Selasky 			    ed, NULL, usbd_get_speed(uaa->device));
7723671d9d8SAndrew Thompson 			if (temp > wMaxPacketSize) {
7733671d9d8SAndrew Thompson 				wMaxPacketSize = temp;
7743671d9d8SAndrew Thompson 				alt_index = j;
7753671d9d8SAndrew Thompson 			}
7763671d9d8SAndrew Thompson 		}
7773671d9d8SAndrew Thompson 	}
7783671d9d8SAndrew Thompson 
7793671d9d8SAndrew Thompson 	/* Set alt configuration on interface #1 only if we found it */
7803671d9d8SAndrew Thompson 	if (wMaxPacketSize > 0 &&
7819d28e15eSHans Petter Selasky 	    usbd_set_alt_interface_index(uaa->device, iface_index[1], alt_index)) {
7823671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not set alternate setting %d " \
7833671d9d8SAndrew Thompson 			"for interface 1!\n", alt_index);
7843671d9d8SAndrew Thompson 		goto detach;
7853671d9d8SAndrew Thompson 	}
7863671d9d8SAndrew Thompson 
7873671d9d8SAndrew Thompson 	/* Setup transfers for both interfaces */
78867cbbf19SHans Petter Selasky 	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer, ubt_config,
78967cbbf19SHans Petter Selasky 			ng_usb_isoc_enable ? UBT_N_TRANSFER : UBT_IF_1_ISOC_DT_RD1,
79067cbbf19SHans Petter Selasky 			sc, &sc->sc_if_mtx)) {
7913671d9d8SAndrew Thompson 		UBT_ALERT(sc, "could not allocate transfers\n");
7923671d9d8SAndrew Thompson 		goto detach;
7933671d9d8SAndrew Thompson 	}
7943671d9d8SAndrew Thompson 
7959d28e15eSHans Petter Selasky 	/* Claim second interface belonging to the Bluetooth part */
7969d28e15eSHans Petter Selasky 	usbd_set_parent_iface(uaa->device, iface_index[1], uaa->info.bIfaceIndex);
7973671d9d8SAndrew Thompson 
7983671d9d8SAndrew Thompson 	return (0); /* success */
7993671d9d8SAndrew Thompson 
8003671d9d8SAndrew Thompson detach:
8013671d9d8SAndrew Thompson 	ubt_detach(dev);
8023671d9d8SAndrew Thompson 
8033671d9d8SAndrew Thompson 	return (ENXIO);
8043671d9d8SAndrew Thompson } /* ubt_attach */
8053671d9d8SAndrew Thompson 
8063671d9d8SAndrew Thompson /*
8073671d9d8SAndrew Thompson  * Detach the device.
8083671d9d8SAndrew Thompson  * USB context.
8093671d9d8SAndrew Thompson  */
8103671d9d8SAndrew Thompson 
8113671d9d8SAndrew Thompson int
ubt_detach(device_t dev)8123671d9d8SAndrew Thompson ubt_detach(device_t dev)
8133671d9d8SAndrew Thompson {
8143671d9d8SAndrew Thompson 	struct ubt_softc	*sc = device_get_softc(dev);
8153671d9d8SAndrew Thompson 	node_p			node = sc->sc_node;
8163671d9d8SAndrew Thompson 
8173671d9d8SAndrew Thompson 	/* Destroy Netgraph node */
8183671d9d8SAndrew Thompson 	if (node != NULL) {
8193671d9d8SAndrew Thompson 		sc->sc_node = NULL;
8203671d9d8SAndrew Thompson 		NG_NODE_REALLY_DIE(node);
8213671d9d8SAndrew Thompson 		ng_rmnode_self(node);
8223671d9d8SAndrew Thompson 	}
8233671d9d8SAndrew Thompson 
8243671d9d8SAndrew Thompson 	/* Make sure ubt_task in gone */
8253671d9d8SAndrew Thompson 	taskqueue_drain(taskqueue_swi, &sc->sc_task);
8263671d9d8SAndrew Thompson 
8273671d9d8SAndrew Thompson 	/* Free USB transfers, if any */
828a593f6b8SAndrew Thompson 	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
8293671d9d8SAndrew Thompson 
8303671d9d8SAndrew Thompson 	/* Destroy queues */
8313671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
8323671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
8333671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
8343671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
8353671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
8363671d9d8SAndrew Thompson 
8373671d9d8SAndrew Thompson 	mtx_destroy(&sc->sc_if_mtx);
8383671d9d8SAndrew Thompson 	mtx_destroy(&sc->sc_ng_mtx);
8393671d9d8SAndrew Thompson 
8403671d9d8SAndrew Thompson 	return (0);
8413671d9d8SAndrew Thompson } /* ubt_detach */
8423671d9d8SAndrew Thompson 
8433671d9d8SAndrew Thompson /*
8443544d43bSVladimir Kondratyev  * Called when incoming interrupt transfer (HCI event) has completed, i.e.
8453544d43bSVladimir Kondratyev  * HCI event was received from the device during device probe stage.
8463544d43bSVladimir Kondratyev  * USB context.
8473544d43bSVladimir Kondratyev  */
8483544d43bSVladimir Kondratyev 
8493544d43bSVladimir Kondratyev static void
ubt_probe_intr_callback(struct usb_xfer * xfer,usb_error_t error)8503544d43bSVladimir Kondratyev ubt_probe_intr_callback(struct usb_xfer *xfer, usb_error_t error)
8513544d43bSVladimir Kondratyev {
8523544d43bSVladimir Kondratyev 	struct ubt_hci_event	*evt = usbd_xfer_softc(xfer);
8533544d43bSVladimir Kondratyev 	struct usb_page_cache	*pc;
8543544d43bSVladimir Kondratyev 	int			actlen;
8550b23c50aSVladimir Kondratyev 	struct ubt_hci_evhdr	evhdr;
8560b23c50aSVladimir Kondratyev 	uint16_t		opcode;
8573544d43bSVladimir Kondratyev 
8583544d43bSVladimir Kondratyev 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
8593544d43bSVladimir Kondratyev 
8603544d43bSVladimir Kondratyev 	switch (USB_GET_STATE(xfer)) {
8613544d43bSVladimir Kondratyev 	case USB_ST_TRANSFERRED:
8623544d43bSVladimir Kondratyev 		if (actlen > UBT_HCI_EVENT_SIZE(evt))
8633544d43bSVladimir Kondratyev 			actlen = UBT_HCI_EVENT_SIZE(evt);
8640b23c50aSVladimir Kondratyev 		if (actlen < sizeof(evhdr))
8650b23c50aSVladimir Kondratyev 			goto submit_next;
8663544d43bSVladimir Kondratyev 		pc = usbd_xfer_get_frame(xfer, 0);
8670b23c50aSVladimir Kondratyev 		usbd_copy_out(pc, 0, &evhdr, sizeof(evhdr));
8680b23c50aSVladimir Kondratyev 		/* Check for expected event code */
8690b23c50aSVladimir Kondratyev 		if (evt->header.event != 0 &&
8700b23c50aSVladimir Kondratyev 		    (evt->header.event != evhdr.event))
8710b23c50aSVladimir Kondratyev 			goto submit_next;
8720b23c50aSVladimir Kondratyev 		/* For completion events check operation code as well */
8730b23c50aSVladimir Kondratyev 		if (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL) {
8740b23c50aSVladimir Kondratyev 			if (actlen < sizeof(struct ubt_hci_event_command_compl))
8750b23c50aSVladimir Kondratyev 				goto submit_next;
8760b23c50aSVladimir Kondratyev 			usbd_copy_out(pc,
8770b23c50aSVladimir Kondratyev 			    offsetof(struct ubt_hci_event_command_compl, opcode),
8780b23c50aSVladimir Kondratyev 			    &opcode, sizeof(opcode));
8790b23c50aSVladimir Kondratyev 			if (opcode !=
8800b23c50aSVladimir Kondratyev 			    ((struct ubt_hci_event_command_compl *)evt)->opcode)
8810b23c50aSVladimir Kondratyev 				goto submit_next;
8820b23c50aSVladimir Kondratyev 		}
8833544d43bSVladimir Kondratyev 		usbd_copy_out(pc, 0, evt, actlen);
8843544d43bSVladimir Kondratyev 		/* OneShot mode */
8853544d43bSVladimir Kondratyev 		wakeup(evt);
8863544d43bSVladimir Kondratyev 		break;
8873544d43bSVladimir Kondratyev 
8883544d43bSVladimir Kondratyev         case USB_ST_SETUP:
8893544d43bSVladimir Kondratyev submit_next:
8903544d43bSVladimir Kondratyev 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
8913544d43bSVladimir Kondratyev 		usbd_transfer_submit(xfer);
8923544d43bSVladimir Kondratyev 		break;
8933544d43bSVladimir Kondratyev 
8943544d43bSVladimir Kondratyev 	default:
8953544d43bSVladimir Kondratyev 		if (error != USB_ERR_CANCELLED) {
8963544d43bSVladimir Kondratyev 			printf("ng_ubt: interrupt transfer failed: %s\n",
8973544d43bSVladimir Kondratyev 				usbd_errstr(error));
89852489f2aSVladimir Kondratyev 			/* Try clear stall first */
89952489f2aSVladimir Kondratyev 			usbd_xfer_set_stall(xfer);
9003544d43bSVladimir Kondratyev 			goto submit_next;
9013544d43bSVladimir Kondratyev 		}
9023544d43bSVladimir Kondratyev 		break;
9033544d43bSVladimir Kondratyev 	}
9043544d43bSVladimir Kondratyev } /* ubt_probe_intr_callback */
9053544d43bSVladimir Kondratyev 
9063544d43bSVladimir Kondratyev /*
9073671d9d8SAndrew Thompson  * Called when outgoing control request (HCI command) has completed, i.e.
9083671d9d8SAndrew Thompson  * HCI command was sent to the device.
9093671d9d8SAndrew Thompson  * USB context.
9103671d9d8SAndrew Thompson  */
9113671d9d8SAndrew Thompson 
9123671d9d8SAndrew Thompson static void
ubt_ctrl_write_callback(struct usb_xfer * xfer,usb_error_t error)913ed6d949aSAndrew Thompson ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
9143671d9d8SAndrew Thompson {
915ed6d949aSAndrew Thompson 	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
916760bc48eSAndrew Thompson 	struct usb_device_request	req;
9173671d9d8SAndrew Thompson 	struct mbuf			*m;
918ed6d949aSAndrew Thompson 	struct usb_page_cache		*pc;
919ed6d949aSAndrew Thompson 	int				actlen;
920ed6d949aSAndrew Thompson 
921ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
9223671d9d8SAndrew Thompson 
9233671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9243671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
925ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
926ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
9273671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
9283671d9d8SAndrew Thompson 		/* FALLTHROUGH */
9293671d9d8SAndrew Thompson 
9303671d9d8SAndrew Thompson 	case USB_ST_SETUP:
9313671d9d8SAndrew Thompson send_next:
9323671d9d8SAndrew Thompson 		/* Get next command mbuf, if any */
9333671d9d8SAndrew Thompson 		UBT_NG_LOCK(sc);
9343671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
9353671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
9363671d9d8SAndrew Thompson 
9373671d9d8SAndrew Thompson 		if (m == NULL) {
9383671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI command queue is empty\n");
9393671d9d8SAndrew Thompson 			break;	/* transfer complete */
9403671d9d8SAndrew Thompson 		}
9413671d9d8SAndrew Thompson 
9423671d9d8SAndrew Thompson 		/* Initialize a USB control request and then schedule it */
9433671d9d8SAndrew Thompson 		bzero(&req, sizeof(req));
9443671d9d8SAndrew Thompson 		req.bmRequestType = UBT_HCI_REQUEST;
9453671d9d8SAndrew Thompson 		USETW(req.wLength, m->m_pkthdr.len);
9463671d9d8SAndrew Thompson 
9473671d9d8SAndrew Thompson 		UBT_INFO(sc, "Sending control request, " \
9483671d9d8SAndrew Thompson 			"bmRequestType=0x%02x, wLength=%d\n",
9493671d9d8SAndrew Thompson 			req.bmRequestType, UGETW(req.wLength));
9503671d9d8SAndrew Thompson 
951ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
952ed6d949aSAndrew Thompson 		usbd_copy_in(pc, 0, &req, sizeof(req));
953ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 1);
954ed6d949aSAndrew Thompson 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
9553671d9d8SAndrew Thompson 
956ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
957ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
958ed6d949aSAndrew Thompson 		usbd_xfer_set_frames(xfer, 2);
9593671d9d8SAndrew Thompson 
9603671d9d8SAndrew Thompson 		NG_FREE_M(m);
9613671d9d8SAndrew Thompson 
962a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
9633671d9d8SAndrew Thompson 		break;
9643671d9d8SAndrew Thompson 
9653671d9d8SAndrew Thompson 	default: /* Error */
966ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
9673671d9d8SAndrew Thompson 			UBT_WARN(sc, "control transfer failed: %s\n",
968ed6d949aSAndrew Thompson 				usbd_errstr(error));
9693671d9d8SAndrew Thompson 
9703671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
9713671d9d8SAndrew Thompson 			goto send_next;
9723671d9d8SAndrew Thompson 		}
9733671d9d8SAndrew Thompson 
9743671d9d8SAndrew Thompson 		/* transfer cancelled */
9753671d9d8SAndrew Thompson 		break;
9763671d9d8SAndrew Thompson 	}
9773671d9d8SAndrew Thompson } /* ubt_ctrl_write_callback */
9783671d9d8SAndrew Thompson 
9793671d9d8SAndrew Thompson /*
9803671d9d8SAndrew Thompson  * Called when incoming interrupt transfer (HCI event) has completed, i.e.
9813671d9d8SAndrew Thompson  * HCI event was received from the device.
9823671d9d8SAndrew Thompson  * USB context.
9833671d9d8SAndrew Thompson  */
9843671d9d8SAndrew Thompson 
9853671d9d8SAndrew Thompson static void
ubt_intr_read_callback(struct usb_xfer * xfer,usb_error_t error)986ed6d949aSAndrew Thompson ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
9873671d9d8SAndrew Thompson {
988ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
9893671d9d8SAndrew Thompson 	struct mbuf		*m;
9903671d9d8SAndrew Thompson 	ng_hci_event_pkt_t	*hdr;
991ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
992ed6d949aSAndrew Thompson 	int			actlen;
993ed6d949aSAndrew Thompson 
994ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
9953671d9d8SAndrew Thompson 
9963671d9d8SAndrew Thompson 	m = NULL;
9973671d9d8SAndrew Thompson 
9983671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9993671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
10003671d9d8SAndrew Thompson 		/* Allocate a new mbuf */
1001eb1b1807SGleb Smirnoff 		MGETHDR(m, M_NOWAIT, MT_DATA);
10023671d9d8SAndrew Thompson 		if (m == NULL) {
10033671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
10043671d9d8SAndrew Thompson 			goto submit_next;
10053671d9d8SAndrew Thompson 		}
10063671d9d8SAndrew Thompson 
10072a8c860fSRobert Watson 		if (!(MCLGET(m, M_NOWAIT))) {
10083671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
10093671d9d8SAndrew Thompson 			goto submit_next;
10103671d9d8SAndrew Thompson 		}
10113671d9d8SAndrew Thompson 
10123671d9d8SAndrew Thompson 		/* Add HCI packet type */
10133671d9d8SAndrew Thompson 		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
10143671d9d8SAndrew Thompson 		m->m_pkthdr.len = m->m_len = 1;
10153671d9d8SAndrew Thompson 
1016ed6d949aSAndrew Thompson 		if (actlen > MCLBYTES - 1)
1017ed6d949aSAndrew Thompson 			actlen = MCLBYTES - 1;
10183671d9d8SAndrew Thompson 
1019ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1020ed6d949aSAndrew Thompson 		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
1021ed6d949aSAndrew Thompson 		m->m_pkthdr.len += actlen;
1022ed6d949aSAndrew Thompson 		m->m_len += actlen;
10233671d9d8SAndrew Thompson 
10243671d9d8SAndrew Thompson 		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
1025ed6d949aSAndrew Thompson 			actlen);
10263671d9d8SAndrew Thompson 
10273671d9d8SAndrew Thompson 		/* Validate packet and send it up the stack */
10286d917491SHans Petter Selasky 		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
10293671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI event packet is too short\n");
10303671d9d8SAndrew Thompson 
10313671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
10323671d9d8SAndrew Thompson 			goto submit_next;
10333671d9d8SAndrew Thompson 		}
10343671d9d8SAndrew Thompson 
10353671d9d8SAndrew Thompson 		hdr = mtod(m, ng_hci_event_pkt_t *);
10363671d9d8SAndrew Thompson 		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
10373671d9d8SAndrew Thompson 			UBT_ERR(sc, "Invalid HCI event packet size, " \
10383671d9d8SAndrew Thompson 				"length=%d, pktlen=%d\n",
10393671d9d8SAndrew Thompson 				hdr->length, m->m_pkthdr.len);
10403671d9d8SAndrew Thompson 
10413671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
10423671d9d8SAndrew Thompson 			goto submit_next;
10433671d9d8SAndrew Thompson 		}
10443671d9d8SAndrew Thompson 
10453671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
10463671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len, hdr->length);
10473671d9d8SAndrew Thompson 
10483671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
10493671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
10503671d9d8SAndrew Thompson 
10513671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
10523671d9d8SAndrew Thompson 		/* m == NULL at this point */
10533671d9d8SAndrew Thompson 		/* FALLTHROUGH */
10543671d9d8SAndrew Thompson 
10553671d9d8SAndrew Thompson 	case USB_ST_SETUP:
10563671d9d8SAndrew Thompson submit_next:
10573671d9d8SAndrew Thompson 		NG_FREE_M(m); /* checks for m != NULL */
10583671d9d8SAndrew Thompson 
1059ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1060a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
10613671d9d8SAndrew Thompson 		break;
10623671d9d8SAndrew Thompson 
10633671d9d8SAndrew Thompson 	default: /* Error */
1064ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
10653671d9d8SAndrew Thompson 			UBT_WARN(sc, "interrupt transfer failed: %s\n",
1066ed6d949aSAndrew Thompson 				usbd_errstr(error));
10673671d9d8SAndrew Thompson 
10683671d9d8SAndrew Thompson 			/* Try to clear stall first */
1069ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
10703671d9d8SAndrew Thompson 			goto submit_next;
10713671d9d8SAndrew Thompson 		}
10723671d9d8SAndrew Thompson 			/* transfer cancelled */
10733671d9d8SAndrew Thompson 		break;
10743671d9d8SAndrew Thompson 	}
10753671d9d8SAndrew Thompson } /* ubt_intr_read_callback */
10763671d9d8SAndrew Thompson 
10773671d9d8SAndrew Thompson /*
10783671d9d8SAndrew Thompson  * Called when incoming bulk transfer (ACL packet) has completed, i.e.
10793671d9d8SAndrew Thompson  * ACL packet was received from the device.
10803671d9d8SAndrew Thompson  * USB context.
10813671d9d8SAndrew Thompson  */
10823671d9d8SAndrew Thompson 
10833671d9d8SAndrew Thompson static void
ubt_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)1084ed6d949aSAndrew Thompson ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
10853671d9d8SAndrew Thompson {
1086ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
10873671d9d8SAndrew Thompson 	struct mbuf		*m;
10883671d9d8SAndrew Thompson 	ng_hci_acldata_pkt_t	*hdr;
1089ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
10906d917491SHans Petter Selasky 	int len;
1091ed6d949aSAndrew Thompson 	int actlen;
1092ed6d949aSAndrew Thompson 
1093ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
10943671d9d8SAndrew Thompson 
10953671d9d8SAndrew Thompson 	m = NULL;
10963671d9d8SAndrew Thompson 
10973671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
10983671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
10993671d9d8SAndrew Thompson 		/* Allocate new mbuf */
1100eb1b1807SGleb Smirnoff 		MGETHDR(m, M_NOWAIT, MT_DATA);
11013671d9d8SAndrew Thompson 		if (m == NULL) {
11023671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
11033671d9d8SAndrew Thompson 			goto submit_next;
11043671d9d8SAndrew Thompson 		}
11053671d9d8SAndrew Thompson 
11062a8c860fSRobert Watson 		if (!(MCLGET(m, M_NOWAIT))) {
11073671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
11083671d9d8SAndrew Thompson 			goto submit_next;
11093671d9d8SAndrew Thompson 		}
11103671d9d8SAndrew Thompson 
11113671d9d8SAndrew Thompson 		/* Add HCI packet type */
11123671d9d8SAndrew Thompson 		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
11133671d9d8SAndrew Thompson 		m->m_pkthdr.len = m->m_len = 1;
11143671d9d8SAndrew Thompson 
1115ed6d949aSAndrew Thompson 		if (actlen > MCLBYTES - 1)
1116ed6d949aSAndrew Thompson 			actlen = MCLBYTES - 1;
11173671d9d8SAndrew Thompson 
1118ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1119ed6d949aSAndrew Thompson 		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
1120ed6d949aSAndrew Thompson 		m->m_pkthdr.len += actlen;
1121ed6d949aSAndrew Thompson 		m->m_len += actlen;
11223671d9d8SAndrew Thompson 
11233671d9d8SAndrew Thompson 		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
1124ed6d949aSAndrew Thompson 			actlen);
11253671d9d8SAndrew Thompson 
11263671d9d8SAndrew Thompson 		/* Validate packet and send it up the stack */
11276d917491SHans Petter Selasky 		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
11283671d9d8SAndrew Thompson 			UBT_INFO(sc, "HCI ACL packet is too short\n");
11293671d9d8SAndrew Thompson 
11303671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
11313671d9d8SAndrew Thompson 			goto submit_next;
11323671d9d8SAndrew Thompson 		}
11333671d9d8SAndrew Thompson 
11343671d9d8SAndrew Thompson 		hdr = mtod(m, ng_hci_acldata_pkt_t *);
11353671d9d8SAndrew Thompson 		len = le16toh(hdr->length);
11366d917491SHans Petter Selasky 		if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
11373671d9d8SAndrew Thompson 			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
11383671d9d8SAndrew Thompson 				"pktlen=%d\n", len, m->m_pkthdr.len);
11393671d9d8SAndrew Thompson 
11403671d9d8SAndrew Thompson 			UBT_STAT_IERROR(sc);
11413671d9d8SAndrew Thompson 			goto submit_next;
11423671d9d8SAndrew Thompson 		}
11433671d9d8SAndrew Thompson 
11443671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
11453671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len, len);
11463671d9d8SAndrew Thompson 
11473671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
11483671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
11493671d9d8SAndrew Thompson 
11503671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
11513671d9d8SAndrew Thompson 		/* m == NULL at this point */
11523671d9d8SAndrew Thompson 		/* FALLTHOUGH */
11533671d9d8SAndrew Thompson 
11543671d9d8SAndrew Thompson 	case USB_ST_SETUP:
11553671d9d8SAndrew Thompson submit_next:
11563671d9d8SAndrew Thompson 		NG_FREE_M(m); /* checks for m != NULL */
11573671d9d8SAndrew Thompson 
1158ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1159a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
11603671d9d8SAndrew Thompson 		break;
11613671d9d8SAndrew Thompson 
11623671d9d8SAndrew Thompson 	default: /* Error */
1163ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
11643671d9d8SAndrew Thompson 			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
1165ed6d949aSAndrew Thompson 				usbd_errstr(error));
11663671d9d8SAndrew Thompson 
11673671d9d8SAndrew Thompson 			/* Try to clear stall first */
1168ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
11693671d9d8SAndrew Thompson 			goto submit_next;
11703671d9d8SAndrew Thompson 		}
11713671d9d8SAndrew Thompson 			/* transfer cancelled */
11723671d9d8SAndrew Thompson 		break;
11733671d9d8SAndrew Thompson 	}
11743671d9d8SAndrew Thompson } /* ubt_bulk_read_callback */
11753671d9d8SAndrew Thompson 
11763671d9d8SAndrew Thompson /*
11773671d9d8SAndrew Thompson  * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
11783671d9d8SAndrew Thompson  * ACL packet was sent to the device.
11793671d9d8SAndrew Thompson  * USB context.
11803671d9d8SAndrew Thompson  */
11813671d9d8SAndrew Thompson 
11823671d9d8SAndrew Thompson static void
ubt_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)1183ed6d949aSAndrew Thompson ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
11843671d9d8SAndrew Thompson {
1185ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
11863671d9d8SAndrew Thompson 	struct mbuf		*m;
1187ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
1188ed6d949aSAndrew Thompson 	int			actlen;
1189ed6d949aSAndrew Thompson 
1190ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
11913671d9d8SAndrew Thompson 
11923671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
11933671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
1194ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
1195ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
11963671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
11973671d9d8SAndrew Thompson 		/* FALLTHROUGH */
11983671d9d8SAndrew Thompson 
11993671d9d8SAndrew Thompson 	case USB_ST_SETUP:
12003671d9d8SAndrew Thompson send_next:
12013671d9d8SAndrew Thompson 		/* Get next mbuf, if any */
12023671d9d8SAndrew Thompson 		UBT_NG_LOCK(sc);
12033671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
12043671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
12053671d9d8SAndrew Thompson 
12063671d9d8SAndrew Thompson 		if (m == NULL) {
12073671d9d8SAndrew Thompson 			UBT_INFO(sc, "ACL data queue is empty\n");
12083671d9d8SAndrew Thompson 			break; /* transfer completed */
12093671d9d8SAndrew Thompson 		}
12103671d9d8SAndrew Thompson 
12113671d9d8SAndrew Thompson 		/*
12123671d9d8SAndrew Thompson 		 * Copy ACL data frame back to a linear USB transfer buffer
12133671d9d8SAndrew Thompson 		 * and schedule transfer
12143671d9d8SAndrew Thompson 		 */
12153671d9d8SAndrew Thompson 
1216ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1217ed6d949aSAndrew Thompson 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
1218ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
12193671d9d8SAndrew Thompson 
12203671d9d8SAndrew Thompson 		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
12213671d9d8SAndrew Thompson 			m->m_pkthdr.len);
12223671d9d8SAndrew Thompson 
12233671d9d8SAndrew Thompson 		NG_FREE_M(m);
12243671d9d8SAndrew Thompson 
1225a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
12263671d9d8SAndrew Thompson 		break;
12273671d9d8SAndrew Thompson 
12283671d9d8SAndrew Thompson 	default: /* Error */
1229ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
12303671d9d8SAndrew Thompson 			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
1231ed6d949aSAndrew Thompson 				usbd_errstr(error));
12323671d9d8SAndrew Thompson 
12333671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
12343671d9d8SAndrew Thompson 
12353671d9d8SAndrew Thompson 			/* try to clear stall first */
1236ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
12373671d9d8SAndrew Thompson 			goto send_next;
12383671d9d8SAndrew Thompson 		}
12393671d9d8SAndrew Thompson 			/* transfer cancelled */
12403671d9d8SAndrew Thompson 		break;
12413671d9d8SAndrew Thompson 	}
12423671d9d8SAndrew Thompson } /* ubt_bulk_write_callback */
12433671d9d8SAndrew Thompson 
12443671d9d8SAndrew Thompson /*
12453671d9d8SAndrew Thompson  * Called when incoming isoc transfer (SCO packet) has completed, i.e.
12463671d9d8SAndrew Thompson  * SCO packet was received from the device.
12473671d9d8SAndrew Thompson  * USB context.
12483671d9d8SAndrew Thompson  */
12493671d9d8SAndrew Thompson 
12503671d9d8SAndrew Thompson static void
ubt_isoc_read_callback(struct usb_xfer * xfer,usb_error_t error)1251ed6d949aSAndrew Thompson ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
12523671d9d8SAndrew Thompson {
1253ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
12543671d9d8SAndrew Thompson 	int			n;
1255ed6d949aSAndrew Thompson 	int actlen, nframes;
1256ed6d949aSAndrew Thompson 
1257ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
12583671d9d8SAndrew Thompson 
12593671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
12603671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
1261ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++)
12623671d9d8SAndrew Thompson 			if (ubt_isoc_read_one_frame(xfer, n) < 0)
12633671d9d8SAndrew Thompson 				break;
12643671d9d8SAndrew Thompson 		/* FALLTHROUGH */
12653671d9d8SAndrew Thompson 
12663671d9d8SAndrew Thompson 	case USB_ST_SETUP:
12673671d9d8SAndrew Thompson read_next:
1268ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++)
1269ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, n,
1270ed6d949aSAndrew Thompson 			    usbd_xfer_max_framelen(xfer));
12713671d9d8SAndrew Thompson 
1272a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
12733671d9d8SAndrew Thompson 		break;
12743671d9d8SAndrew Thompson 
12753671d9d8SAndrew Thompson 	default: /* Error */
1276ed6d949aSAndrew Thompson                 if (error != USB_ERR_CANCELLED) {
12773671d9d8SAndrew Thompson                         UBT_STAT_IERROR(sc);
12783671d9d8SAndrew Thompson                         goto read_next;
12793671d9d8SAndrew Thompson                 }
12803671d9d8SAndrew Thompson 
12813671d9d8SAndrew Thompson 		/* transfer cancelled */
12823671d9d8SAndrew Thompson 		break;
12833671d9d8SAndrew Thompson 	}
12843671d9d8SAndrew Thompson } /* ubt_isoc_read_callback */
12853671d9d8SAndrew Thompson 
12863671d9d8SAndrew Thompson /*
12873671d9d8SAndrew Thompson  * Helper function. Called from ubt_isoc_read_callback() to read
12883671d9d8SAndrew Thompson  * SCO data from one frame.
12893671d9d8SAndrew Thompson  * USB context.
12903671d9d8SAndrew Thompson  */
12913671d9d8SAndrew Thompson 
12923671d9d8SAndrew Thompson static int
ubt_isoc_read_one_frame(struct usb_xfer * xfer,int frame_no)1293760bc48eSAndrew Thompson ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
12943671d9d8SAndrew Thompson {
1295ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1296ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
12973671d9d8SAndrew Thompson 	struct mbuf		*m;
1298ed6d949aSAndrew Thompson 	int			len, want, got, total;
12993671d9d8SAndrew Thompson 
13003671d9d8SAndrew Thompson 	/* Get existing SCO reassembly buffer */
1301ed6d949aSAndrew Thompson 	pc = usbd_xfer_get_frame(xfer, 0);
13023671d9d8SAndrew Thompson 	m = sc->sc_isoc_in_buffer;
13038f9e0ef9SAndrew Thompson 	total = usbd_xfer_frame_len(xfer, frame_no);
13043671d9d8SAndrew Thompson 
13053671d9d8SAndrew Thompson 	/* While we have data in the frame */
1306ed6d949aSAndrew Thompson 	while (total > 0) {
13073671d9d8SAndrew Thompson 		if (m == NULL) {
13083671d9d8SAndrew Thompson 			/* Start new reassembly buffer */
1309eb1b1807SGleb Smirnoff 			MGETHDR(m, M_NOWAIT, MT_DATA);
13103671d9d8SAndrew Thompson 			if (m == NULL) {
13113671d9d8SAndrew Thompson 				UBT_STAT_IERROR(sc);
13123671d9d8SAndrew Thompson 				return (-1);	/* XXX out of sync! */
13133671d9d8SAndrew Thompson 			}
13143671d9d8SAndrew Thompson 
13152a8c860fSRobert Watson 			if (!(MCLGET(m, M_NOWAIT))) {
13163671d9d8SAndrew Thompson 				UBT_STAT_IERROR(sc);
13173671d9d8SAndrew Thompson 				NG_FREE_M(m);
13183671d9d8SAndrew Thompson 				return (-1);	/* XXX out of sync! */
13193671d9d8SAndrew Thompson 			}
13203671d9d8SAndrew Thompson 
13213671d9d8SAndrew Thompson 			/* Expect SCO header */
13223671d9d8SAndrew Thompson 			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
13233671d9d8SAndrew Thompson 			m->m_pkthdr.len = m->m_len = got = 1;
13243671d9d8SAndrew Thompson 			want = sizeof(ng_hci_scodata_pkt_t);
13253671d9d8SAndrew Thompson 		} else {
13263671d9d8SAndrew Thompson 			/*
13273671d9d8SAndrew Thompson 			 * Check if we have SCO header and if so
13283671d9d8SAndrew Thompson 			 * adjust amount of data we want
13293671d9d8SAndrew Thompson 			 */
13303671d9d8SAndrew Thompson 			got = m->m_pkthdr.len;
13313671d9d8SAndrew Thompson 			want = sizeof(ng_hci_scodata_pkt_t);
13323671d9d8SAndrew Thompson 
13333671d9d8SAndrew Thompson 			if (got >= want)
13343671d9d8SAndrew Thompson 				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
13353671d9d8SAndrew Thompson 		}
13363671d9d8SAndrew Thompson 
13373671d9d8SAndrew Thompson 		/* Append frame data to the SCO reassembly buffer */
1338ed6d949aSAndrew Thompson 		len = total;
13393671d9d8SAndrew Thompson 		if (got + len > want)
13403671d9d8SAndrew Thompson 			len = want - got;
13413671d9d8SAndrew Thompson 
1342ed6d949aSAndrew Thompson 		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
13433671d9d8SAndrew Thompson 			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
13443671d9d8SAndrew Thompson 
13453671d9d8SAndrew Thompson 		m->m_pkthdr.len += len;
13463671d9d8SAndrew Thompson 		m->m_len += len;
1347ed6d949aSAndrew Thompson 		total -= len;
13483671d9d8SAndrew Thompson 
13493671d9d8SAndrew Thompson 		/* Check if we got everything we wanted, if not - continue */
13503671d9d8SAndrew Thompson 		if (got != want)
13513671d9d8SAndrew Thompson 			continue;
13523671d9d8SAndrew Thompson 
13533671d9d8SAndrew Thompson 		/* If we got here then we got complete SCO frame */
13543671d9d8SAndrew Thompson 		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
13553671d9d8SAndrew Thompson 			"length=%d\n", m->m_pkthdr.len,
13563671d9d8SAndrew Thompson 			mtod(m, ng_hci_scodata_pkt_t *)->length);
13573671d9d8SAndrew Thompson 
13583671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_RECV(sc);
13593671d9d8SAndrew Thompson 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
13603671d9d8SAndrew Thompson 
13613671d9d8SAndrew Thompson 		ubt_fwd_mbuf_up(sc, &m);
13623671d9d8SAndrew Thompson 		/* m == NULL at this point */
13633671d9d8SAndrew Thompson 	}
13643671d9d8SAndrew Thompson 
13653671d9d8SAndrew Thompson 	/* Put SCO reassembly buffer back */
13663671d9d8SAndrew Thompson 	sc->sc_isoc_in_buffer = m;
13673671d9d8SAndrew Thompson 
13683671d9d8SAndrew Thompson 	return (0);
13693671d9d8SAndrew Thompson } /* ubt_isoc_read_one_frame */
13703671d9d8SAndrew Thompson 
13713671d9d8SAndrew Thompson /*
13723671d9d8SAndrew Thompson  * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
13733671d9d8SAndrew Thompson  * SCO packet was sent to the device.
13743671d9d8SAndrew Thompson  * USB context.
13753671d9d8SAndrew Thompson  */
13763671d9d8SAndrew Thompson 
13773671d9d8SAndrew Thompson static void
ubt_isoc_write_callback(struct usb_xfer * xfer,usb_error_t error)1378ed6d949aSAndrew Thompson ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
13793671d9d8SAndrew Thompson {
1380ed6d949aSAndrew Thompson 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1381ed6d949aSAndrew Thompson 	struct usb_page_cache	*pc;
13823671d9d8SAndrew Thompson 	struct mbuf		*m;
13833671d9d8SAndrew Thompson 	int			n, space, offset;
1384ed6d949aSAndrew Thompson 	int			actlen, nframes;
1385ed6d949aSAndrew Thompson 
1386ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1387ed6d949aSAndrew Thompson 	pc = usbd_xfer_get_frame(xfer, 0);
13883671d9d8SAndrew Thompson 
13893671d9d8SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
13903671d9d8SAndrew Thompson 	case USB_ST_TRANSFERRED:
1391ed6d949aSAndrew Thompson 		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1392ed6d949aSAndrew Thompson 		UBT_STAT_BYTES_SENT(sc, actlen);
13933671d9d8SAndrew Thompson 		UBT_STAT_PCKTS_SENT(sc);
13943671d9d8SAndrew Thompson 		/* FALLTHROUGH */
13953671d9d8SAndrew Thompson 
13963671d9d8SAndrew Thompson 	case USB_ST_SETUP:
13973671d9d8SAndrew Thompson send_next:
13983671d9d8SAndrew Thompson 		offset = 0;
1399ed6d949aSAndrew Thompson 		space = usbd_xfer_max_framelen(xfer) * nframes;
14003671d9d8SAndrew Thompson 		m = NULL;
14013671d9d8SAndrew Thompson 
14023671d9d8SAndrew Thompson 		while (space > 0) {
14033671d9d8SAndrew Thompson 			if (m == NULL) {
14043671d9d8SAndrew Thompson 				UBT_NG_LOCK(sc);
14053671d9d8SAndrew Thompson 				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
14063671d9d8SAndrew Thompson 				UBT_NG_UNLOCK(sc);
14073671d9d8SAndrew Thompson 
14083671d9d8SAndrew Thompson 				if (m == NULL)
14093671d9d8SAndrew Thompson 					break;
14103671d9d8SAndrew Thompson 			}
14113671d9d8SAndrew Thompson 
14123671d9d8SAndrew Thompson 			n = min(space, m->m_pkthdr.len);
14133671d9d8SAndrew Thompson 			if (n > 0) {
1414ed6d949aSAndrew Thompson 				usbd_m_copy_in(pc, offset, m,0, n);
14153671d9d8SAndrew Thompson 				m_adj(m, n);
14163671d9d8SAndrew Thompson 
14173671d9d8SAndrew Thompson 				offset += n;
14183671d9d8SAndrew Thompson 				space -= n;
14193671d9d8SAndrew Thompson 			}
14203671d9d8SAndrew Thompson 
14213671d9d8SAndrew Thompson 			if (m->m_pkthdr.len == 0)
14223671d9d8SAndrew Thompson 				NG_FREE_M(m); /* sets m = NULL */
14233671d9d8SAndrew Thompson 		}
14243671d9d8SAndrew Thompson 
14253671d9d8SAndrew Thompson 		/* Put whatever is left from mbuf back on queue */
14263671d9d8SAndrew Thompson 		if (m != NULL) {
14273671d9d8SAndrew Thompson 			UBT_NG_LOCK(sc);
14283671d9d8SAndrew Thompson 			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
14293671d9d8SAndrew Thompson 			UBT_NG_UNLOCK(sc);
14303671d9d8SAndrew Thompson 		}
14313671d9d8SAndrew Thompson 
14323671d9d8SAndrew Thompson 		/*
14333671d9d8SAndrew Thompson 		 * Calculate sizes for isoc frames.
14343671d9d8SAndrew Thompson 		 * Note that offset could be 0 at this point (i.e. we have
14353671d9d8SAndrew Thompson 		 * nothing to send). That is fine, as we have isoc. transfers
14363671d9d8SAndrew Thompson 		 * going in both directions all the time. In this case it
14373671d9d8SAndrew Thompson 		 * would be just empty isoc. transfer.
14383671d9d8SAndrew Thompson 		 */
14393671d9d8SAndrew Thompson 
1440ed6d949aSAndrew Thompson 		for (n = 0; n < nframes; n ++) {
1441ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, n,
1442ed6d949aSAndrew Thompson 			    min(offset, usbd_xfer_max_framelen(xfer)));
14438f9e0ef9SAndrew Thompson 			offset -= usbd_xfer_frame_len(xfer, n);
14443671d9d8SAndrew Thompson 		}
14453671d9d8SAndrew Thompson 
1446a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
14473671d9d8SAndrew Thompson 		break;
14483671d9d8SAndrew Thompson 
14493671d9d8SAndrew Thompson 	default: /* Error */
1450ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
14513671d9d8SAndrew Thompson 			UBT_STAT_OERROR(sc);
14523671d9d8SAndrew Thompson 			goto send_next;
14533671d9d8SAndrew Thompson 		}
14543671d9d8SAndrew Thompson 
14553671d9d8SAndrew Thompson 		/* transfer cancelled */
14563671d9d8SAndrew Thompson 		break;
14573671d9d8SAndrew Thompson 	}
14583671d9d8SAndrew Thompson }
14593671d9d8SAndrew Thompson 
14603671d9d8SAndrew Thompson /*
14613671d9d8SAndrew Thompson  * Utility function to forward provided mbuf upstream (i.e. up the stack).
14623671d9d8SAndrew Thompson  * Modifies value of the mbuf pointer (sets it to NULL).
14633671d9d8SAndrew Thompson  * Save to call from any context.
14643671d9d8SAndrew Thompson  */
14653671d9d8SAndrew Thompson 
14663671d9d8SAndrew Thompson static int
ubt_fwd_mbuf_up(ubt_softc_p sc,struct mbuf ** m)14673671d9d8SAndrew Thompson ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
14683671d9d8SAndrew Thompson {
14693671d9d8SAndrew Thompson 	hook_p	hook;
14703671d9d8SAndrew Thompson 	int	error;
14713671d9d8SAndrew Thompson 
14723671d9d8SAndrew Thompson 	/*
14733671d9d8SAndrew Thompson 	 * Close the race with Netgraph hook newhook/disconnect methods.
14743671d9d8SAndrew Thompson 	 * Save the hook pointer atomically. Two cases are possible:
14753671d9d8SAndrew Thompson 	 *
14763671d9d8SAndrew Thompson 	 * 1) The hook pointer is NULL. It means disconnect method got
14773671d9d8SAndrew Thompson 	 *    there first. In this case we are done.
14783671d9d8SAndrew Thompson 	 *
14793671d9d8SAndrew Thompson 	 * 2) The hook pointer is not NULL. It means that hook pointer
14803671d9d8SAndrew Thompson 	 *    could be either in valid or invalid (i.e. in the process
14813671d9d8SAndrew Thompson 	 *    of disconnect) state. In any case grab an extra reference
14823671d9d8SAndrew Thompson 	 *    to protect the hook pointer.
14833671d9d8SAndrew Thompson 	 *
14843671d9d8SAndrew Thompson 	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
14853671d9d8SAndrew Thompson 	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
14863671d9d8SAndrew Thompson 	 */
14873671d9d8SAndrew Thompson 
14883671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
14893671d9d8SAndrew Thompson 	if ((hook = sc->sc_hook) != NULL)
14903671d9d8SAndrew Thompson 		NG_HOOK_REF(hook);
14913671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
14923671d9d8SAndrew Thompson 
14933671d9d8SAndrew Thompson 	if (hook == NULL) {
14943671d9d8SAndrew Thompson 		NG_FREE_M(*m);
14953671d9d8SAndrew Thompson 		return (ENETDOWN);
14963671d9d8SAndrew Thompson 	}
14973671d9d8SAndrew Thompson 
14983671d9d8SAndrew Thompson 	NG_SEND_DATA_ONLY(error, hook, *m);
14993671d9d8SAndrew Thompson 	NG_HOOK_UNREF(hook);
15003671d9d8SAndrew Thompson 
15013671d9d8SAndrew Thompson 	if (error != 0)
15023671d9d8SAndrew Thompson 		UBT_STAT_IERROR(sc);
15033671d9d8SAndrew Thompson 
15043671d9d8SAndrew Thompson 	return (error);
15053671d9d8SAndrew Thompson } /* ubt_fwd_mbuf_up */
15063671d9d8SAndrew Thompson 
15073671d9d8SAndrew Thompson /****************************************************************************
15083671d9d8SAndrew Thompson  ****************************************************************************
15093671d9d8SAndrew Thompson  **                                 Glue
15103671d9d8SAndrew Thompson  ****************************************************************************
15113671d9d8SAndrew Thompson  ****************************************************************************/
15123671d9d8SAndrew Thompson 
15133671d9d8SAndrew Thompson /*
15143671d9d8SAndrew Thompson  * Schedule glue task. Should be called with sc_ng_mtx held.
15153671d9d8SAndrew Thompson  * Netgraph context.
15163671d9d8SAndrew Thompson  */
15173671d9d8SAndrew Thompson 
15183671d9d8SAndrew Thompson static void
ubt_task_schedule(ubt_softc_p sc,int action)15193671d9d8SAndrew Thompson ubt_task_schedule(ubt_softc_p sc, int action)
15203671d9d8SAndrew Thompson {
15213671d9d8SAndrew Thompson 	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
15223671d9d8SAndrew Thompson 
15233671d9d8SAndrew Thompson 	/*
15243671d9d8SAndrew Thompson 	 * Try to handle corner case when "start all" and "stop all"
15253671d9d8SAndrew Thompson 	 * actions can both be set before task is executed.
15263671d9d8SAndrew Thompson 	 *
15273671d9d8SAndrew Thompson 	 * The rules are
15283671d9d8SAndrew Thompson 	 *
15293671d9d8SAndrew Thompson 	 * sc_task_flags	action		new sc_task_flags
15303671d9d8SAndrew Thompson 	 * ------------------------------------------------------
15313671d9d8SAndrew Thompson 	 * 0			start		start
15323671d9d8SAndrew Thompson 	 * 0			stop		stop
15333671d9d8SAndrew Thompson 	 * start		start		start
15343671d9d8SAndrew Thompson 	 * start		stop		stop
15353671d9d8SAndrew Thompson 	 * stop			start		stop|start
15363671d9d8SAndrew Thompson 	 * stop			stop		stop
15373671d9d8SAndrew Thompson 	 * stop|start		start		stop|start
15383671d9d8SAndrew Thompson 	 * stop|start		stop		stop
15393671d9d8SAndrew Thompson 	 */
15403671d9d8SAndrew Thompson 
15413671d9d8SAndrew Thompson 	if (action != 0) {
15423671d9d8SAndrew Thompson 		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
15433671d9d8SAndrew Thompson 			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
15443671d9d8SAndrew Thompson 
15453671d9d8SAndrew Thompson 		sc->sc_task_flags |= action;
15463671d9d8SAndrew Thompson 	}
15473671d9d8SAndrew Thompson 
15483671d9d8SAndrew Thompson 	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
15493671d9d8SAndrew Thompson 		return;
15503671d9d8SAndrew Thompson 
15513671d9d8SAndrew Thompson 	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
15523671d9d8SAndrew Thompson 		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
15533671d9d8SAndrew Thompson 		return;
15543671d9d8SAndrew Thompson 	}
15553671d9d8SAndrew Thompson 
15563671d9d8SAndrew Thompson 	/* XXX: i think this should never happen */
15573671d9d8SAndrew Thompson } /* ubt_task_schedule */
15583671d9d8SAndrew Thompson 
15593671d9d8SAndrew Thompson /*
15603671d9d8SAndrew Thompson  * Glue task. Examines sc_task_flags and does things depending on it.
15613671d9d8SAndrew Thompson  * Taskqueue context.
15623671d9d8SAndrew Thompson  */
15633671d9d8SAndrew Thompson 
15643671d9d8SAndrew Thompson static void
ubt_task(void * context,int pending)15653671d9d8SAndrew Thompson ubt_task(void *context, int pending)
15663671d9d8SAndrew Thompson {
15673671d9d8SAndrew Thompson 	ubt_softc_p	sc = context;
15683671d9d8SAndrew Thompson 	int		task_flags, i;
15693671d9d8SAndrew Thompson 
15703671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
15713671d9d8SAndrew Thompson 	task_flags = sc->sc_task_flags;
15723671d9d8SAndrew Thompson 	sc->sc_task_flags = 0;
15733671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
15743671d9d8SAndrew Thompson 
15753671d9d8SAndrew Thompson 	/*
15763671d9d8SAndrew Thompson 	 * Stop all USB transfers synchronously.
15773671d9d8SAndrew Thompson 	 * Stop interface #0 and #1 transfers at the same time and in the
1578a593f6b8SAndrew Thompson 	 * same loop. usbd_transfer_drain() will do appropriate locking.
15793671d9d8SAndrew Thompson 	 */
15803671d9d8SAndrew Thompson 
15813671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_STOP_ALL)
15823671d9d8SAndrew Thompson 		for (i = 0; i < UBT_N_TRANSFER; i ++)
1583a593f6b8SAndrew Thompson 			usbd_transfer_drain(sc->sc_xfer[i]);
15843671d9d8SAndrew Thompson 
15853671d9d8SAndrew Thompson 	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
15863671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_ALL) {
15873671d9d8SAndrew Thompson 		/*
15883671d9d8SAndrew Thompson 		 * Interface #0
15893671d9d8SAndrew Thompson 		 */
15903671d9d8SAndrew Thompson 
15913671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
15923671d9d8SAndrew Thompson 
15933671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
15943671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
15953671d9d8SAndrew Thompson 
15963671d9d8SAndrew Thompson 		/*
15973671d9d8SAndrew Thompson 		 * Interface #1
15983671d9d8SAndrew Thompson 		 * Start both read and write isoc. transfers by default.
15993671d9d8SAndrew Thompson 		 * Get them going all the time even if we have nothing
16003671d9d8SAndrew Thompson 		 * to send to avoid any delays.
16013671d9d8SAndrew Thompson 		 */
16023671d9d8SAndrew Thompson 
16033671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
16043671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
16053671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
16063671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
16073671d9d8SAndrew Thompson 
16083671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
16093671d9d8SAndrew Thompson 	}
16103671d9d8SAndrew Thompson 
16113671d9d8SAndrew Thompson  	/* Start outgoing control transfer */
16123671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_CTRL) {
16133671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
16143671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
16153671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
16163671d9d8SAndrew Thompson 	}
16173671d9d8SAndrew Thompson 
16183671d9d8SAndrew Thompson 	/* Start outgoing bulk transfer */
16193671d9d8SAndrew Thompson 	if (task_flags & UBT_FLAG_T_START_BULK) {
16203671d9d8SAndrew Thompson 		mtx_lock(&sc->sc_if_mtx);
16213671d9d8SAndrew Thompson 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
16223671d9d8SAndrew Thompson 		mtx_unlock(&sc->sc_if_mtx);
16233671d9d8SAndrew Thompson 	}
16243671d9d8SAndrew Thompson } /* ubt_task */
16253671d9d8SAndrew Thompson 
16263671d9d8SAndrew Thompson /****************************************************************************
16273671d9d8SAndrew Thompson  ****************************************************************************
16283671d9d8SAndrew Thompson  **                        Netgraph specific
16293671d9d8SAndrew Thompson  ****************************************************************************
16303671d9d8SAndrew Thompson  ****************************************************************************/
16313671d9d8SAndrew Thompson 
16323671d9d8SAndrew Thompson /*
16333671d9d8SAndrew Thompson  * Netgraph node constructor. Do not allow to create node of this type.
16343671d9d8SAndrew Thompson  * Netgraph context.
16353671d9d8SAndrew Thompson  */
16363671d9d8SAndrew Thompson 
16373671d9d8SAndrew Thompson static int
ng_ubt_constructor(node_p node)16383671d9d8SAndrew Thompson ng_ubt_constructor(node_p node)
16393671d9d8SAndrew Thompson {
16403671d9d8SAndrew Thompson 	return (EINVAL);
16413671d9d8SAndrew Thompson } /* ng_ubt_constructor */
16423671d9d8SAndrew Thompson 
16433671d9d8SAndrew Thompson /*
16443671d9d8SAndrew Thompson  * Netgraph node destructor. Destroy node only when device has been detached.
16453671d9d8SAndrew Thompson  * Netgraph context.
16463671d9d8SAndrew Thompson  */
16473671d9d8SAndrew Thompson 
16483671d9d8SAndrew Thompson static int
ng_ubt_shutdown(node_p node)16493671d9d8SAndrew Thompson ng_ubt_shutdown(node_p node)
16503671d9d8SAndrew Thompson {
16513671d9d8SAndrew Thompson 	if (node->nd_flags & NGF_REALLY_DIE) {
16523671d9d8SAndrew Thompson 		/*
16533671d9d8SAndrew Thompson                  * We came here because the USB device is being
1654053359b7SPedro F. Giffuni 		 * detached, so stop being persistent.
16553671d9d8SAndrew Thompson                  */
16563671d9d8SAndrew Thompson 		NG_NODE_SET_PRIVATE(node, NULL);
16573671d9d8SAndrew Thompson 		NG_NODE_UNREF(node);
16583671d9d8SAndrew Thompson 	} else
16593671d9d8SAndrew Thompson 		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
16603671d9d8SAndrew Thompson 
16613671d9d8SAndrew Thompson 	return (0);
16623671d9d8SAndrew Thompson } /* ng_ubt_shutdown */
16633671d9d8SAndrew Thompson 
16643671d9d8SAndrew Thompson /*
16653671d9d8SAndrew Thompson  * Create new hook. There can only be one.
16663671d9d8SAndrew Thompson  * Netgraph context.
16673671d9d8SAndrew Thompson  */
16683671d9d8SAndrew Thompson 
16693671d9d8SAndrew Thompson static int
ng_ubt_newhook(node_p node,hook_p hook,char const * name)16703671d9d8SAndrew Thompson ng_ubt_newhook(node_p node, hook_p hook, char const *name)
16713671d9d8SAndrew Thompson {
16723671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
16733671d9d8SAndrew Thompson 
16743671d9d8SAndrew Thompson 	if (strcmp(name, NG_UBT_HOOK) != 0)
16753671d9d8SAndrew Thompson 		return (EINVAL);
16763671d9d8SAndrew Thompson 
16773671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
16783671d9d8SAndrew Thompson 	if (sc->sc_hook != NULL) {
16793671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
16803671d9d8SAndrew Thompson 
16813671d9d8SAndrew Thompson 		return (EISCONN);
16823671d9d8SAndrew Thompson 	}
16833671d9d8SAndrew Thompson 
16843671d9d8SAndrew Thompson 	sc->sc_hook = hook;
16853671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
16863671d9d8SAndrew Thompson 
16873671d9d8SAndrew Thompson 	return (0);
16883671d9d8SAndrew Thompson } /* ng_ubt_newhook */
16893671d9d8SAndrew Thompson 
16903671d9d8SAndrew Thompson /*
16913671d9d8SAndrew Thompson  * Connect hook. Start incoming USB transfers.
16923671d9d8SAndrew Thompson  * Netgraph context.
16933671d9d8SAndrew Thompson  */
16943671d9d8SAndrew Thompson 
16953671d9d8SAndrew Thompson static int
ng_ubt_connect(hook_p hook)16963671d9d8SAndrew Thompson ng_ubt_connect(hook_p hook)
16973671d9d8SAndrew Thompson {
16983671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
16993671d9d8SAndrew Thompson 
17003671d9d8SAndrew Thompson 	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
17013671d9d8SAndrew Thompson 
17023671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
17033671d9d8SAndrew Thompson 	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
17043671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
17053671d9d8SAndrew Thompson 
17063671d9d8SAndrew Thompson 	return (0);
17073671d9d8SAndrew Thompson } /* ng_ubt_connect */
17083671d9d8SAndrew Thompson 
17093671d9d8SAndrew Thompson /*
17103671d9d8SAndrew Thompson  * Disconnect hook.
17113671d9d8SAndrew Thompson  * Netgraph context.
17123671d9d8SAndrew Thompson  */
17133671d9d8SAndrew Thompson 
17143671d9d8SAndrew Thompson static int
ng_ubt_disconnect(hook_p hook)17153671d9d8SAndrew Thompson ng_ubt_disconnect(hook_p hook)
17163671d9d8SAndrew Thompson {
17173671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
17183671d9d8SAndrew Thompson 
17193671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
17203671d9d8SAndrew Thompson 
17213671d9d8SAndrew Thompson 	if (hook != sc->sc_hook) {
17223671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
17233671d9d8SAndrew Thompson 
17243671d9d8SAndrew Thompson 		return (EINVAL);
17253671d9d8SAndrew Thompson 	}
17263671d9d8SAndrew Thompson 
17273671d9d8SAndrew Thompson 	sc->sc_hook = NULL;
17283671d9d8SAndrew Thompson 
17293671d9d8SAndrew Thompson 	/* Kick off task to stop all USB xfers */
17303671d9d8SAndrew Thompson 	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
17313671d9d8SAndrew Thompson 
17323671d9d8SAndrew Thompson 	/* Drain queues */
17333671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
17343671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
17353671d9d8SAndrew Thompson 	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
17363671d9d8SAndrew Thompson 
17373671d9d8SAndrew Thompson 	UBT_NG_UNLOCK(sc);
17383671d9d8SAndrew Thompson 
17393671d9d8SAndrew Thompson 	return (0);
17403671d9d8SAndrew Thompson } /* ng_ubt_disconnect */
17413671d9d8SAndrew Thompson 
17423671d9d8SAndrew Thompson /*
17433671d9d8SAndrew Thompson  * Process control message.
17443671d9d8SAndrew Thompson  * Netgraph context.
17453671d9d8SAndrew Thompson  */
17463671d9d8SAndrew Thompson 
17473671d9d8SAndrew Thompson static int
ng_ubt_rcvmsg(node_p node,item_p item,hook_p lasthook)17483671d9d8SAndrew Thompson ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
17493671d9d8SAndrew Thompson {
17503671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
17513671d9d8SAndrew Thompson 	struct ng_mesg		*msg, *rsp = NULL;
17523671d9d8SAndrew Thompson 	struct ng_bt_mbufq	*q;
17533671d9d8SAndrew Thompson 	int			error = 0, queue, qlen;
17543671d9d8SAndrew Thompson 
17553671d9d8SAndrew Thompson 	NGI_GET_MSG(item, msg);
17563671d9d8SAndrew Thompson 
17573671d9d8SAndrew Thompson 	switch (msg->header.typecookie) {
17583671d9d8SAndrew Thompson 	case NGM_GENERIC_COOKIE:
17593671d9d8SAndrew Thompson 		switch (msg->header.cmd) {
17603671d9d8SAndrew Thompson 		case NGM_TEXT_STATUS:
17613671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
17623671d9d8SAndrew Thompson 			if (rsp == NULL) {
17633671d9d8SAndrew Thompson 				error = ENOMEM;
17643671d9d8SAndrew Thompson 				break;
17653671d9d8SAndrew Thompson 			}
17663671d9d8SAndrew Thompson 
17673671d9d8SAndrew Thompson 			snprintf(rsp->data, NG_TEXTRESPONSE,
17683671d9d8SAndrew Thompson 				"Hook: %s\n" \
17693671d9d8SAndrew Thompson 				"Task flags: %#x\n" \
17703671d9d8SAndrew Thompson 				"Debug: %d\n" \
17713671d9d8SAndrew Thompson 				"CMD queue: [have:%d,max:%d]\n" \
17723671d9d8SAndrew Thompson 				"ACL queue: [have:%d,max:%d]\n" \
17733671d9d8SAndrew Thompson 				"SCO queue: [have:%d,max:%d]",
17743671d9d8SAndrew Thompson 				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
17753671d9d8SAndrew Thompson 				sc->sc_task_flags,
17763671d9d8SAndrew Thompson 				sc->sc_debug,
17773671d9d8SAndrew Thompson 				sc->sc_cmdq.len,
17783671d9d8SAndrew Thompson 				sc->sc_cmdq.maxlen,
17793671d9d8SAndrew Thompson 				sc->sc_aclq.len,
17803671d9d8SAndrew Thompson 				sc->sc_aclq.maxlen,
17813671d9d8SAndrew Thompson 				sc->sc_scoq.len,
17823671d9d8SAndrew Thompson 				sc->sc_scoq.maxlen);
17833671d9d8SAndrew Thompson 			break;
17843671d9d8SAndrew Thompson 
17853671d9d8SAndrew Thompson 		default:
17863671d9d8SAndrew Thompson 			error = EINVAL;
17873671d9d8SAndrew Thompson 			break;
17883671d9d8SAndrew Thompson 		}
17893671d9d8SAndrew Thompson 		break;
17903671d9d8SAndrew Thompson 
17913671d9d8SAndrew Thompson 	case NGM_UBT_COOKIE:
17923671d9d8SAndrew Thompson 		switch (msg->header.cmd) {
17933671d9d8SAndrew Thompson 		case NGM_UBT_NODE_SET_DEBUG:
17943671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
17953671d9d8SAndrew Thompson 				error = EMSGSIZE;
17963671d9d8SAndrew Thompson 				break;
17973671d9d8SAndrew Thompson 			}
17983671d9d8SAndrew Thompson 
17993671d9d8SAndrew Thompson 			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
18003671d9d8SAndrew Thompson 			break;
18013671d9d8SAndrew Thompson 
18023671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_DEBUG:
18033671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
18043671d9d8SAndrew Thompson 			    M_NOWAIT);
18053671d9d8SAndrew Thompson 			if (rsp == NULL) {
18063671d9d8SAndrew Thompson 				error = ENOMEM;
18073671d9d8SAndrew Thompson 				break;
18083671d9d8SAndrew Thompson 			}
18093671d9d8SAndrew Thompson 
18103671d9d8SAndrew Thompson 			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
18113671d9d8SAndrew Thompson 			break;
18123671d9d8SAndrew Thompson 
18133671d9d8SAndrew Thompson 		case NGM_UBT_NODE_SET_QLEN:
18143671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
18153671d9d8SAndrew Thompson 				error = EMSGSIZE;
18163671d9d8SAndrew Thompson 				break;
18173671d9d8SAndrew Thompson 			}
18183671d9d8SAndrew Thompson 
18193671d9d8SAndrew Thompson 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
18203671d9d8SAndrew Thompson 			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
18213671d9d8SAndrew Thompson 
18223671d9d8SAndrew Thompson 			switch (queue) {
18233671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_CMD:
18243671d9d8SAndrew Thompson 				q = &sc->sc_cmdq;
18253671d9d8SAndrew Thompson 				break;
18263671d9d8SAndrew Thompson 
18273671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_ACL:
18283671d9d8SAndrew Thompson 				q = &sc->sc_aclq;
18293671d9d8SAndrew Thompson 				break;
18303671d9d8SAndrew Thompson 
18313671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_SCO:
18323671d9d8SAndrew Thompson 				q = &sc->sc_scoq;
18333671d9d8SAndrew Thompson 				break;
18343671d9d8SAndrew Thompson 
18353671d9d8SAndrew Thompson 			default:
18363671d9d8SAndrew Thompson 				error = EINVAL;
18373671d9d8SAndrew Thompson 				goto done;
18383671d9d8SAndrew Thompson 				/* NOT REACHED */
18393671d9d8SAndrew Thompson 			}
18403671d9d8SAndrew Thompson 
18413671d9d8SAndrew Thompson 			q->maxlen = qlen;
18423671d9d8SAndrew Thompson 			break;
18433671d9d8SAndrew Thompson 
18443671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_QLEN:
18453671d9d8SAndrew Thompson 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
18463671d9d8SAndrew Thompson 				error = EMSGSIZE;
18473671d9d8SAndrew Thompson 				break;
18483671d9d8SAndrew Thompson 			}
18493671d9d8SAndrew Thompson 
18503671d9d8SAndrew Thompson 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
18513671d9d8SAndrew Thompson 
18523671d9d8SAndrew Thompson 			switch (queue) {
18533671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_CMD:
18543671d9d8SAndrew Thompson 				q = &sc->sc_cmdq;
18553671d9d8SAndrew Thompson 				break;
18563671d9d8SAndrew Thompson 
18573671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_ACL:
18583671d9d8SAndrew Thompson 				q = &sc->sc_aclq;
18593671d9d8SAndrew Thompson 				break;
18603671d9d8SAndrew Thompson 
18613671d9d8SAndrew Thompson 			case NGM_UBT_NODE_QUEUE_SCO:
18623671d9d8SAndrew Thompson 				q = &sc->sc_scoq;
18633671d9d8SAndrew Thompson 				break;
18643671d9d8SAndrew Thompson 
18653671d9d8SAndrew Thompson 			default:
18663671d9d8SAndrew Thompson 				error = EINVAL;
18673671d9d8SAndrew Thompson 				goto done;
18683671d9d8SAndrew Thompson 				/* NOT REACHED */
18693671d9d8SAndrew Thompson 			}
18703671d9d8SAndrew Thompson 
18713671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
18723671d9d8SAndrew Thompson 				M_NOWAIT);
18733671d9d8SAndrew Thompson 			if (rsp == NULL) {
18743671d9d8SAndrew Thompson 				error = ENOMEM;
18753671d9d8SAndrew Thompson 				break;
18763671d9d8SAndrew Thompson 			}
18773671d9d8SAndrew Thompson 
18783671d9d8SAndrew Thompson 			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
18793671d9d8SAndrew Thompson 			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
18803671d9d8SAndrew Thompson 			break;
18813671d9d8SAndrew Thompson 
18823671d9d8SAndrew Thompson 		case NGM_UBT_NODE_GET_STAT:
18833671d9d8SAndrew Thompson 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
18843671d9d8SAndrew Thompson 			    M_NOWAIT);
18853671d9d8SAndrew Thompson 			if (rsp == NULL) {
18863671d9d8SAndrew Thompson 				error = ENOMEM;
18873671d9d8SAndrew Thompson 				break;
18883671d9d8SAndrew Thompson 			}
18893671d9d8SAndrew Thompson 
18903671d9d8SAndrew Thompson 			bcopy(&sc->sc_stat, rsp->data,
18913671d9d8SAndrew Thompson 				sizeof(ng_ubt_node_stat_ep));
18923671d9d8SAndrew Thompson 			break;
18933671d9d8SAndrew Thompson 
18943671d9d8SAndrew Thompson 		case NGM_UBT_NODE_RESET_STAT:
18953671d9d8SAndrew Thompson 			UBT_STAT_RESET(sc);
18963671d9d8SAndrew Thompson 			break;
18973671d9d8SAndrew Thompson 
18983671d9d8SAndrew Thompson 		default:
18993671d9d8SAndrew Thompson 			error = EINVAL;
19003671d9d8SAndrew Thompson 			break;
19013671d9d8SAndrew Thompson 		}
19023671d9d8SAndrew Thompson 		break;
19033671d9d8SAndrew Thompson 
19043671d9d8SAndrew Thompson 	default:
19053671d9d8SAndrew Thompson 		error = EINVAL;
19063671d9d8SAndrew Thompson 		break;
19073671d9d8SAndrew Thompson 	}
19083671d9d8SAndrew Thompson done:
19093671d9d8SAndrew Thompson 	NG_RESPOND_MSG(error, node, item, rsp);
19103671d9d8SAndrew Thompson 	NG_FREE_MSG(msg);
19113671d9d8SAndrew Thompson 
19123671d9d8SAndrew Thompson 	return (error);
19133671d9d8SAndrew Thompson } /* ng_ubt_rcvmsg */
19143671d9d8SAndrew Thompson 
19153671d9d8SAndrew Thompson /*
19163671d9d8SAndrew Thompson  * Process data.
19173671d9d8SAndrew Thompson  * Netgraph context.
19183671d9d8SAndrew Thompson  */
19193671d9d8SAndrew Thompson 
19203671d9d8SAndrew Thompson static int
ng_ubt_rcvdata(hook_p hook,item_p item)19213671d9d8SAndrew Thompson ng_ubt_rcvdata(hook_p hook, item_p item)
19223671d9d8SAndrew Thompson {
19233671d9d8SAndrew Thompson 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
19243671d9d8SAndrew Thompson 	struct mbuf		*m;
19253671d9d8SAndrew Thompson 	struct ng_bt_mbufq	*q;
19263671d9d8SAndrew Thompson 	int			action, error = 0;
19273671d9d8SAndrew Thompson 
19283671d9d8SAndrew Thompson 	if (hook != sc->sc_hook) {
19293671d9d8SAndrew Thompson 		error = EINVAL;
19303671d9d8SAndrew Thompson 		goto done;
19313671d9d8SAndrew Thompson 	}
19323671d9d8SAndrew Thompson 
19333671d9d8SAndrew Thompson 	/* Deatch mbuf and get HCI frame type */
19343671d9d8SAndrew Thompson 	NGI_GET_M(item, m);
19353671d9d8SAndrew Thompson 
19363671d9d8SAndrew Thompson 	/*
19373671d9d8SAndrew Thompson 	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
19383671d9d8SAndrew Thompson 	 * 2 bytes connection handle and at least 1 byte of length.
19393671d9d8SAndrew Thompson 	 * Panic on data frame that has size smaller than 4 bytes (it
19403671d9d8SAndrew Thompson 	 * should not happen)
19413671d9d8SAndrew Thompson 	 */
19423671d9d8SAndrew Thompson 
19433671d9d8SAndrew Thompson 	if (m->m_pkthdr.len < 4)
19443671d9d8SAndrew Thompson 		panic("HCI frame size is too small! pktlen=%d\n",
19453671d9d8SAndrew Thompson 			m->m_pkthdr.len);
19463671d9d8SAndrew Thompson 
19473671d9d8SAndrew Thompson 	/* Process HCI frame */
19483671d9d8SAndrew Thompson 	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
19493671d9d8SAndrew Thompson 	case NG_HCI_CMD_PKT:
19506d917491SHans Petter Selasky 		if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
19513671d9d8SAndrew Thompson 			panic("HCI command frame size is too big! " \
19523671d9d8SAndrew Thompson 				"buffer size=%zd, packet len=%d\n",
19533671d9d8SAndrew Thompson 				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
19543671d9d8SAndrew Thompson 
19553671d9d8SAndrew Thompson 		q = &sc->sc_cmdq;
19563671d9d8SAndrew Thompson 		action = UBT_FLAG_T_START_CTRL;
19573671d9d8SAndrew Thompson 		break;
19583671d9d8SAndrew Thompson 
19593671d9d8SAndrew Thompson 	case NG_HCI_ACL_DATA_PKT:
19603671d9d8SAndrew Thompson 		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
19613671d9d8SAndrew Thompson 			panic("ACL data frame size is too big! " \
19623671d9d8SAndrew Thompson 				"buffer size=%d, packet len=%d\n",
19633671d9d8SAndrew Thompson 				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
19643671d9d8SAndrew Thompson 
19653671d9d8SAndrew Thompson 		q = &sc->sc_aclq;
19663671d9d8SAndrew Thompson 		action = UBT_FLAG_T_START_BULK;
19673671d9d8SAndrew Thompson 		break;
19683671d9d8SAndrew Thompson 
19693671d9d8SAndrew Thompson 	case NG_HCI_SCO_DATA_PKT:
19703671d9d8SAndrew Thompson 		q = &sc->sc_scoq;
19713671d9d8SAndrew Thompson 		action = 0;
19723671d9d8SAndrew Thompson 		break;
19733671d9d8SAndrew Thompson 
19743671d9d8SAndrew Thompson 	default:
19753671d9d8SAndrew Thompson 		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
19763671d9d8SAndrew Thompson 			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
19773671d9d8SAndrew Thompson 
19783671d9d8SAndrew Thompson 		NG_FREE_M(m);
19793671d9d8SAndrew Thompson 		error = EINVAL;
19803671d9d8SAndrew Thompson 		goto done;
19813671d9d8SAndrew Thompson 		/* NOT REACHED */
19823671d9d8SAndrew Thompson 	}
19833671d9d8SAndrew Thompson 
19843671d9d8SAndrew Thompson 	UBT_NG_LOCK(sc);
19853671d9d8SAndrew Thompson 	if (NG_BT_MBUFQ_FULL(q)) {
19863671d9d8SAndrew Thompson 		NG_BT_MBUFQ_DROP(q);
19873671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
19883671d9d8SAndrew Thompson 
19893671d9d8SAndrew Thompson 		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
19903671d9d8SAndrew Thompson 			*mtod(m, uint8_t *), m->m_pkthdr.len);
19913671d9d8SAndrew Thompson 
19923671d9d8SAndrew Thompson 		NG_FREE_M(m);
19933671d9d8SAndrew Thompson 	} else {
19943671d9d8SAndrew Thompson 		/* Loose HCI packet type, enqueue mbuf and kick off task */
19953671d9d8SAndrew Thompson 		m_adj(m, sizeof(uint8_t));
19963671d9d8SAndrew Thompson 		NG_BT_MBUFQ_ENQUEUE(q, m);
19973671d9d8SAndrew Thompson 		ubt_task_schedule(sc, action);
19983671d9d8SAndrew Thompson 		UBT_NG_UNLOCK(sc);
19993671d9d8SAndrew Thompson 	}
20003671d9d8SAndrew Thompson done:
20013671d9d8SAndrew Thompson 	NG_FREE_ITEM(item);
20023671d9d8SAndrew Thompson 
20033671d9d8SAndrew Thompson 	return (error);
20043671d9d8SAndrew Thompson } /* ng_ubt_rcvdata */
20053671d9d8SAndrew Thompson 
20063671d9d8SAndrew Thompson /****************************************************************************
20073671d9d8SAndrew Thompson  ****************************************************************************
20083671d9d8SAndrew Thompson  **                              Module
20093671d9d8SAndrew Thompson  ****************************************************************************
20103671d9d8SAndrew Thompson  ****************************************************************************/
20113671d9d8SAndrew Thompson 
20123671d9d8SAndrew Thompson /*
20133671d9d8SAndrew Thompson  * Load/Unload the driver module
20143671d9d8SAndrew Thompson  */
20153671d9d8SAndrew Thompson 
20163671d9d8SAndrew Thompson static int
ubt_modevent(module_t mod,int event,void * data)20173671d9d8SAndrew Thompson ubt_modevent(module_t mod, int event, void *data)
20183671d9d8SAndrew Thompson {
20193671d9d8SAndrew Thompson 	int	error;
20203671d9d8SAndrew Thompson 
20213671d9d8SAndrew Thompson 	switch (event) {
20223671d9d8SAndrew Thompson 	case MOD_LOAD:
20233671d9d8SAndrew Thompson 		error = ng_newtype(&typestruct);
20243671d9d8SAndrew Thompson 		if (error != 0)
20253671d9d8SAndrew Thompson 			printf("%s: Could not register Netgraph node type, " \
20263671d9d8SAndrew Thompson 				"error=%d\n", NG_UBT_NODE_TYPE, error);
20273671d9d8SAndrew Thompson 		break;
20283671d9d8SAndrew Thompson 
20293671d9d8SAndrew Thompson 	case MOD_UNLOAD:
20303671d9d8SAndrew Thompson 		error = ng_rmtype(&typestruct);
20313671d9d8SAndrew Thompson 		break;
20323671d9d8SAndrew Thompson 
20333671d9d8SAndrew Thompson 	default:
20343671d9d8SAndrew Thompson 		error = EOPNOTSUPP;
20353671d9d8SAndrew Thompson 		break;
20363671d9d8SAndrew Thompson 	}
20373671d9d8SAndrew Thompson 
20383671d9d8SAndrew Thompson 	return (error);
20393671d9d8SAndrew Thompson } /* ubt_modevent */
20403671d9d8SAndrew Thompson 
20413671d9d8SAndrew Thompson static device_method_t	ubt_methods[] =
20423671d9d8SAndrew Thompson {
20433671d9d8SAndrew Thompson 	DEVMETHOD(device_probe,	ubt_probe),
20443671d9d8SAndrew Thompson 	DEVMETHOD(device_attach, ubt_attach),
20453671d9d8SAndrew Thompson 	DEVMETHOD(device_detach, ubt_detach),
2046b42a2049SRaphael Kubo da Costa 	DEVMETHOD_END
20473671d9d8SAndrew Thompson };
20483671d9d8SAndrew Thompson 
20493544d43bSVladimir Kondratyev driver_t		ubt_driver =
20503671d9d8SAndrew Thompson {
20513671d9d8SAndrew Thompson 	.name =	   "ubt",
20523671d9d8SAndrew Thompson 	.methods = ubt_methods,
20533671d9d8SAndrew Thompson 	.size =	   sizeof(struct ubt_softc),
20543671d9d8SAndrew Thompson };
20553671d9d8SAndrew Thompson 
2056d83185d9SJohn Baldwin DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_modevent, 0);
20573671d9d8SAndrew Thompson MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
20583671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
20593671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
20608fa95293SHans Petter Selasky MODULE_DEPEND(ng_ubt, ng_bluetooth, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
20613671d9d8SAndrew Thompson MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
2062f809f280SWarner Losh USB_PNP_HOST_INFO(ubt_devs);
2063