xref: /dflybsd-src/sys/netgraph7/bluetooth/drivers/ubt/ng_ubt.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
1b06ebda0SMatthew Dillon /*
2b06ebda0SMatthew Dillon  * ng_ubt.c
3b06ebda0SMatthew Dillon  */
4b06ebda0SMatthew Dillon 
5b06ebda0SMatthew Dillon /*-
6d2902f79SSascha Wildner  * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7b06ebda0SMatthew Dillon  * All rights reserved.
8b06ebda0SMatthew Dillon  *
9b06ebda0SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
10b06ebda0SMatthew Dillon  * modification, are permitted provided that the following conditions
11b06ebda0SMatthew Dillon  * are met:
12b06ebda0SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
13b06ebda0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
14b06ebda0SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
15b06ebda0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
16b06ebda0SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
17b06ebda0SMatthew Dillon  *
18b06ebda0SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19b06ebda0SMatthew Dillon  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20b06ebda0SMatthew Dillon  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21b06ebda0SMatthew Dillon  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22b06ebda0SMatthew Dillon  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23b06ebda0SMatthew Dillon  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24b06ebda0SMatthew Dillon  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25b06ebda0SMatthew Dillon  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26b06ebda0SMatthew Dillon  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27b06ebda0SMatthew Dillon  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28b06ebda0SMatthew Dillon  * SUCH DAMAGE.
29b06ebda0SMatthew Dillon  *
30b06ebda0SMatthew Dillon  * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
31d2902f79SSascha Wildner  * $FreeBSD: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c 260482 2014-01-09 15:31:44Z adrian $
32b06ebda0SMatthew Dillon  */
33b06ebda0SMatthew Dillon 
34b06ebda0SMatthew Dillon /*
35d2902f79SSascha Wildner  * NOTE: ng_ubt2 driver has a split personality. On one side it is
36d2902f79SSascha Wildner  * a USB device driver and on the other it is a Netgraph node. This
37d2902f79SSascha Wildner  * driver will *NOT* create traditional /dev/ enties, only Netgraph
38d2902f79SSascha Wildner  * node.
39d2902f79SSascha Wildner  *
40d2902f79SSascha Wildner  * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
41d2902f79SSascha Wildner  *
42d2902f79SSascha Wildner  * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
43d2902f79SSascha Wildner  *    by USB for any USB request going over device's interface #0 and #1,
44d2902f79SSascha Wildner  *    i.e. interrupt, control, bulk and isoc. transfers.
45d2902f79SSascha Wildner  *
46d2902f79SSascha Wildner  * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
47d2902f79SSascha Wildner  *    and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
48d2902f79SSascha Wildner  *    pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
49d2902f79SSascha Wildner  *    think of it as a spin lock.
50d2902f79SSascha Wildner  *
51d2902f79SSascha Wildner  * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
52d2902f79SSascha Wildner  *
53d2902f79SSascha Wildner  * 1) USB context. This is where all the USB related stuff happens. All
54d2902f79SSascha Wildner  *    callbacks run in this context. All callbacks are called (by USB) with
55d2902f79SSascha Wildner  *    appropriate interface lock held. It is (generally) allowed to grab
56d2902f79SSascha Wildner  *    any additional locks.
57d2902f79SSascha Wildner  *
58d2902f79SSascha Wildner  * 2) Netgraph context. This is where all the Netgraph related stuff happens.
59d2902f79SSascha Wildner  *    Since we mark node as WRITER, the Netgraph node will be "locked" (from
60d2902f79SSascha Wildner  *    Netgraph point of view). Any variable that is only modified from the
61d2902f79SSascha Wildner  *    Netgraph context does not require any additonal locking. It is generally
62d2902f79SSascha Wildner  *    *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
63d2902f79SSascha Wildner  *    grab any lock in the Netgraph context that could cause de-scheduling of
64d2902f79SSascha Wildner  *    the Netgraph thread for significant amount of time. In fact, the only
65d2902f79SSascha Wildner  *    lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
66d2902f79SSascha Wildner  *    Also make sure that any code that is called from the Netgraph context
67d2902f79SSascha Wildner  *    follows the rule above.
68d2902f79SSascha Wildner  *
69d2902f79SSascha Wildner  * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
70d2902f79SSascha Wildner  *    NOT allowed to grab any lock that could cause de-scheduling in the
71d2902f79SSascha Wildner  *    Netgraph context, and, USB requires us to grab interface lock before
72d2902f79SSascha Wildner  *    doing things with transfers, it is safer to transition from the Netgraph
73d2902f79SSascha Wildner  *    context to the Taskqueue context before we can call into USB subsystem.
74d2902f79SSascha Wildner  *
75d2902f79SSascha Wildner  * So, to put everything together, the rules are as follows.
76d2902f79SSascha Wildner  *	It is OK to call from the USB context or the Taskqueue context into
77d2902f79SSascha Wildner  * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
78d2902f79SSascha Wildner  * it is allowed to call into the Netgraph context with locks held.
79d2902f79SSascha Wildner  *	Is it *NOT* OK to call from the Netgraph context into the USB context,
80d2902f79SSascha Wildner  * because USB requires us to grab interface locks, and, it is safer to
81d2902f79SSascha Wildner  * avoid it. So, to make things safer we set task flags to indicate which
82d2902f79SSascha Wildner  * actions we want to perform and schedule ubt_task which would run in the
83d2902f79SSascha Wildner  * Taskqueue context.
84d2902f79SSascha Wildner  *	Is is OK to call from the Taskqueue context into the USB context,
85d2902f79SSascha Wildner  * and, ubt_task does just that (i.e. grabs appropriate interface locks
86d2902f79SSascha Wildner  * before calling into USB).
87d2902f79SSascha Wildner  *	Access to the outgoing queues, task flags and hook pointer is
88d2902f79SSascha Wildner  * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
89d2902f79SSascha Wildner  * sc_ng_mtx should really be a spin lock (and it is very likely to an
90d2902f79SSascha Wildner  * equivalent of spin lock due to adaptive nature of FreeBSD mutexes).
91d2902f79SSascha Wildner  *	All USB callbacks accept softc pointer as a private data. USB ensures
92d2902f79SSascha Wildner  * that this pointer is valid.
93b06ebda0SMatthew Dillon  */
94b06ebda0SMatthew Dillon 
95d2902f79SSascha Wildner #include <sys/stdint.h>
96d2902f79SSascha Wildner #include <sys/param.h>
97d2902f79SSascha Wildner #include <sys/queue.h>
98d2902f79SSascha Wildner #include <sys/types.h>
99d2902f79SSascha Wildner #include <sys/systm.h>
100d2902f79SSascha Wildner #include <sys/kernel.h>
101d2902f79SSascha Wildner #include <sys/bus.h>
102d2902f79SSascha Wildner #include <sys/module.h>
103d2902f79SSascha Wildner #include <sys/lock.h>
104d2902f79SSascha Wildner #include <sys/condvar.h>
105d2902f79SSascha Wildner #include <sys/sysctl.h>
106d2902f79SSascha Wildner #include <sys/unistd.h>
107d2902f79SSascha Wildner #include <sys/callout.h>
108d2902f79SSascha Wildner #include <sys/malloc.h>
109*2b3f93eaSMatthew Dillon #include <sys/caps.h>
110d2902f79SSascha Wildner 
111d2902f79SSascha Wildner #include "usbdevs.h"
112d2902f79SSascha Wildner #include <bus/u4b/usb.h>
113d2902f79SSascha Wildner #include <bus/u4b/usbdi.h>
114d2902f79SSascha Wildner #include <bus/u4b/usbdi_util.h>
115d2902f79SSascha Wildner 
116d2902f79SSascha Wildner #define	USB_DEBUG_VAR usb_debug
117d2902f79SSascha Wildner #include <bus/u4b/usb_debug.h>
118d2902f79SSascha Wildner #include <bus/u4b/usb_busdma.h>
119d2902f79SSascha Wildner 
120d2902f79SSascha Wildner #include <sys/mbuf.h>
121d2902f79SSascha Wildner #include <sys/taskqueue.h>
122d2902f79SSascha Wildner 
123d2902f79SSascha Wildner #include <netgraph7/ng_message.h>
124d2902f79SSascha Wildner #include <netgraph7/netgraph.h>
125d2902f79SSascha Wildner #include <netgraph7/ng_parse.h>
126d2902f79SSascha Wildner #include <netgraph7/bluetooth/include/ng_bluetooth.h>
127d2902f79SSascha Wildner #include <netgraph7/bluetooth/include/ng_hci.h>
128d2902f79SSascha Wildner #include <netgraph7/bluetooth/include/ng_ubt.h>
129d2902f79SSascha Wildner #include <netgraph7/bluetooth/drivers/ubt/ng_ubt_var.h>
130d2902f79SSascha Wildner 
131d2902f79SSascha Wildner static int		ubt_modevent(module_t, int, void *);
132d2902f79SSascha Wildner static device_probe_t	ubt_probe;
133b06ebda0SMatthew Dillon static device_attach_t	ubt_attach;
134b06ebda0SMatthew Dillon static device_detach_t	ubt_detach;
135b06ebda0SMatthew Dillon 
136d2902f79SSascha Wildner static void		ubt_task_schedule(ubt_softc_p, int);
137d2902f79SSascha Wildner static task_fn_t	ubt_task;
138b06ebda0SMatthew Dillon 
139d2902f79SSascha Wildner #define	ubt_xfer_start(sc, i)	usbd_transfer_start((sc)->sc_xfer[(i)])
140b06ebda0SMatthew Dillon 
141d2902f79SSascha Wildner /* Netgraph methods */
142b06ebda0SMatthew Dillon static ng_constructor_t	ng_ubt_constructor;
143b06ebda0SMatthew Dillon static ng_shutdown_t	ng_ubt_shutdown;
144b06ebda0SMatthew Dillon static ng_newhook_t	ng_ubt_newhook;
145b06ebda0SMatthew Dillon static ng_connect_t	ng_ubt_connect;
146b06ebda0SMatthew Dillon static ng_disconnect_t	ng_ubt_disconnect;
147b06ebda0SMatthew Dillon static ng_rcvmsg_t	ng_ubt_rcvmsg;
148b06ebda0SMatthew Dillon static ng_rcvdata_t	ng_ubt_rcvdata;
149b06ebda0SMatthew Dillon 
150b06ebda0SMatthew Dillon /* Queue length */
151b06ebda0SMatthew Dillon static const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
152b06ebda0SMatthew Dillon {
153b06ebda0SMatthew Dillon 	{ "queue", &ng_parse_int32_type, },
154b06ebda0SMatthew Dillon 	{ "qlen",  &ng_parse_int32_type, },
155b06ebda0SMatthew Dillon 	{ NULL, }
156b06ebda0SMatthew Dillon };
157d2902f79SSascha Wildner static const struct ng_parse_type		ng_ubt_node_qlen_type =
158d2902f79SSascha Wildner {
159b06ebda0SMatthew Dillon 	&ng_parse_struct_type,
160b06ebda0SMatthew Dillon 	&ng_ubt_node_qlen_type_fields
161b06ebda0SMatthew Dillon };
162b06ebda0SMatthew Dillon 
163b06ebda0SMatthew Dillon /* Stat info */
164b06ebda0SMatthew Dillon static const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
165b06ebda0SMatthew Dillon {
166b06ebda0SMatthew Dillon 	{ "pckts_recv", &ng_parse_uint32_type, },
167b06ebda0SMatthew Dillon 	{ "bytes_recv", &ng_parse_uint32_type, },
168b06ebda0SMatthew Dillon 	{ "pckts_sent", &ng_parse_uint32_type, },
169b06ebda0SMatthew Dillon 	{ "bytes_sent", &ng_parse_uint32_type, },
170b06ebda0SMatthew Dillon 	{ "oerrors",    &ng_parse_uint32_type, },
171b06ebda0SMatthew Dillon 	{ "ierrors",    &ng_parse_uint32_type, },
172b06ebda0SMatthew Dillon 	{ NULL, }
173b06ebda0SMatthew Dillon };
174d2902f79SSascha Wildner static const struct ng_parse_type		ng_ubt_node_stat_type =
175d2902f79SSascha Wildner {
176b06ebda0SMatthew Dillon 	&ng_parse_struct_type,
177b06ebda0SMatthew Dillon 	&ng_ubt_node_stat_type_fields
178b06ebda0SMatthew Dillon };
179b06ebda0SMatthew Dillon 
180b06ebda0SMatthew Dillon /* Netgraph node command list */
181d2902f79SSascha Wildner static const struct ng_cmdlist			ng_ubt_cmdlist[] =
182d2902f79SSascha Wildner {
183b06ebda0SMatthew Dillon 	{
184b06ebda0SMatthew Dillon 		NGM_UBT_COOKIE,
185b06ebda0SMatthew Dillon 		NGM_UBT_NODE_SET_DEBUG,
186b06ebda0SMatthew Dillon 		"set_debug",
187b06ebda0SMatthew Dillon 		&ng_parse_uint16_type,
188b06ebda0SMatthew Dillon 		NULL
189b06ebda0SMatthew Dillon 	},
190b06ebda0SMatthew Dillon 	{
191b06ebda0SMatthew Dillon 		NGM_UBT_COOKIE,
192b06ebda0SMatthew Dillon 		NGM_UBT_NODE_GET_DEBUG,
193b06ebda0SMatthew Dillon 		"get_debug",
194b06ebda0SMatthew Dillon 		NULL,
195b06ebda0SMatthew Dillon 		&ng_parse_uint16_type
196b06ebda0SMatthew Dillon 	},
197b06ebda0SMatthew Dillon 	{
198b06ebda0SMatthew Dillon 		NGM_UBT_COOKIE,
199b06ebda0SMatthew Dillon 		NGM_UBT_NODE_SET_QLEN,
200b06ebda0SMatthew Dillon 		"set_qlen",
201b06ebda0SMatthew Dillon 		&ng_ubt_node_qlen_type,
202b06ebda0SMatthew Dillon 		NULL
203b06ebda0SMatthew Dillon 	},
204b06ebda0SMatthew Dillon 	{
205b06ebda0SMatthew Dillon 		NGM_UBT_COOKIE,
206b06ebda0SMatthew Dillon 		NGM_UBT_NODE_GET_QLEN,
207b06ebda0SMatthew Dillon 		"get_qlen",
208b06ebda0SMatthew Dillon 		&ng_ubt_node_qlen_type,
209b06ebda0SMatthew Dillon 		&ng_ubt_node_qlen_type
210b06ebda0SMatthew Dillon 	},
211b06ebda0SMatthew Dillon 	{
212b06ebda0SMatthew Dillon 		NGM_UBT_COOKIE,
213b06ebda0SMatthew Dillon 		NGM_UBT_NODE_GET_STAT,
214b06ebda0SMatthew Dillon 		"get_stat",
215b06ebda0SMatthew Dillon 		NULL,
216b06ebda0SMatthew Dillon 		&ng_ubt_node_stat_type
217b06ebda0SMatthew Dillon 	},
218b06ebda0SMatthew Dillon 	{
219b06ebda0SMatthew Dillon 		NGM_UBT_COOKIE,
220b06ebda0SMatthew Dillon 		NGM_UBT_NODE_RESET_STAT,
221b06ebda0SMatthew Dillon 		"reset_stat",
222b06ebda0SMatthew Dillon 		NULL,
223b06ebda0SMatthew Dillon 		NULL
224b06ebda0SMatthew Dillon 	},
225b06ebda0SMatthew Dillon 	{ 0, }
226b06ebda0SMatthew Dillon };
227b06ebda0SMatthew Dillon 
228b06ebda0SMatthew Dillon /* Netgraph node type */
229d2902f79SSascha Wildner static struct ng_type	typestruct =
230d2902f79SSascha Wildner {
231b06ebda0SMatthew Dillon 	.version = 	NG_ABI_VERSION,
232b06ebda0SMatthew Dillon 	.name =		NG_UBT_NODE_TYPE,
233b06ebda0SMatthew Dillon 	.constructor =	ng_ubt_constructor,
234b06ebda0SMatthew Dillon 	.rcvmsg =	ng_ubt_rcvmsg,
235b06ebda0SMatthew Dillon 	.shutdown =	ng_ubt_shutdown,
236b06ebda0SMatthew Dillon 	.newhook =	ng_ubt_newhook,
237b06ebda0SMatthew Dillon 	.connect =	ng_ubt_connect,
238b06ebda0SMatthew Dillon 	.rcvdata =	ng_ubt_rcvdata,
239b06ebda0SMatthew Dillon 	.disconnect =	ng_ubt_disconnect,
240b06ebda0SMatthew Dillon 	.cmdlist =	ng_ubt_cmdlist
241b06ebda0SMatthew Dillon };
242b06ebda0SMatthew Dillon 
243b06ebda0SMatthew Dillon /****************************************************************************
244b06ebda0SMatthew Dillon  ****************************************************************************
245b06ebda0SMatthew Dillon  **                              USB specific
246b06ebda0SMatthew Dillon  ****************************************************************************
247b06ebda0SMatthew Dillon  ****************************************************************************/
248b06ebda0SMatthew Dillon 
249d2902f79SSascha Wildner /* USB methods */
250d2902f79SSascha Wildner static usb_callback_t	ubt_ctrl_write_callback;
251d2902f79SSascha Wildner static usb_callback_t	ubt_intr_read_callback;
252d2902f79SSascha Wildner static usb_callback_t	ubt_bulk_read_callback;
253d2902f79SSascha Wildner static usb_callback_t	ubt_bulk_write_callback;
254d2902f79SSascha Wildner static usb_callback_t	ubt_isoc_read_callback;
255d2902f79SSascha Wildner static usb_callback_t	ubt_isoc_write_callback;
256b06ebda0SMatthew Dillon 
257d2902f79SSascha Wildner static int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
258d2902f79SSascha Wildner static int		ubt_isoc_read_one_frame(struct usb_xfer *, int);
259b06ebda0SMatthew Dillon 
260b06ebda0SMatthew Dillon /*
261d2902f79SSascha Wildner  * USB config
262d2902f79SSascha Wildner  *
263d2902f79SSascha Wildner  * The following desribes usb transfers that could be submitted on USB device.
264d2902f79SSascha Wildner  *
265d2902f79SSascha Wildner  * Interface 0 on the USB device must present the following endpoints
266d2902f79SSascha Wildner  *	1) Interrupt endpoint to receive HCI events
267d2902f79SSascha Wildner  *	2) Bulk IN endpoint to receive ACL data
268d2902f79SSascha Wildner  *	3) Bulk OUT endpoint to send ACL data
269d2902f79SSascha Wildner  *
270d2902f79SSascha Wildner  * Interface 1 on the USB device must present the following endpoints
271d2902f79SSascha Wildner  *	1) Isochronous IN endpoint to receive SCO data
272d2902f79SSascha Wildner  *	2) Isochronous OUT endpoint to send SCO data
273b06ebda0SMatthew Dillon  */
274b06ebda0SMatthew Dillon 
275d2902f79SSascha Wildner static const struct usb_config		ubt_config[UBT_N_TRANSFER] =
276b06ebda0SMatthew Dillon {
277b06ebda0SMatthew Dillon 	/*
278d2902f79SSascha Wildner 	 * Interface #0
279d2902f79SSascha Wildner  	 */
280d2902f79SSascha Wildner 
281d2902f79SSascha Wildner 	/* Outgoing bulk transfer - ACL packets */
282d2902f79SSascha Wildner 	[UBT_IF_0_BULK_DT_WR] = {
283d2902f79SSascha Wildner 		.type =		UE_BULK,
284d2902f79SSascha Wildner 		.endpoint =	UE_ADDR_ANY,
285d2902f79SSascha Wildner 		.direction =	UE_DIR_OUT,
286d2902f79SSascha Wildner 		.if_index = 	0,
287d2902f79SSascha Wildner 		.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
288d2902f79SSascha Wildner 		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
289d2902f79SSascha Wildner 		.callback =	&ubt_bulk_write_callback,
290d2902f79SSascha Wildner 	},
291d2902f79SSascha Wildner 	/* Incoming bulk transfer - ACL packets */
292d2902f79SSascha Wildner 	[UBT_IF_0_BULK_DT_RD] = {
293d2902f79SSascha Wildner 		.type =		UE_BULK,
294d2902f79SSascha Wildner 		.endpoint =	UE_ADDR_ANY,
295d2902f79SSascha Wildner 		.direction =	UE_DIR_IN,
296d2902f79SSascha Wildner 		.if_index = 	0,
297d2902f79SSascha Wildner 		.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
298d2902f79SSascha Wildner 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
299d2902f79SSascha Wildner 		.callback =	&ubt_bulk_read_callback,
300d2902f79SSascha Wildner 	},
301d2902f79SSascha Wildner 	/* Incoming interrupt transfer - HCI events */
302d2902f79SSascha Wildner 	[UBT_IF_0_INTR_DT_RD] = {
303d2902f79SSascha Wildner 		.type =		UE_INTERRUPT,
304d2902f79SSascha Wildner 		.endpoint =	UE_ADDR_ANY,
305d2902f79SSascha Wildner 		.direction =	UE_DIR_IN,
306d2902f79SSascha Wildner 		.if_index = 	0,
307d2902f79SSascha Wildner 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
308d2902f79SSascha Wildner 		.bufsize =	UBT_INTR_BUFFER_SIZE,
309d2902f79SSascha Wildner 		.callback =	&ubt_intr_read_callback,
310d2902f79SSascha Wildner 	},
311d2902f79SSascha Wildner 	/* Outgoing control transfer - HCI commands */
312d2902f79SSascha Wildner 	[UBT_IF_0_CTRL_DT_WR] = {
313d2902f79SSascha Wildner 		.type =		UE_CONTROL,
314d2902f79SSascha Wildner 		.endpoint =	0x00,	/* control pipe */
315d2902f79SSascha Wildner 		.direction =	UE_DIR_ANY,
316d2902f79SSascha Wildner 		.if_index = 	0,
317d2902f79SSascha Wildner 		.bufsize =	UBT_CTRL_BUFFER_SIZE,
318d2902f79SSascha Wildner 		.callback =	&ubt_ctrl_write_callback,
319d2902f79SSascha Wildner 		.timeout =	5000,	/* 5 seconds */
320d2902f79SSascha Wildner 	},
321d2902f79SSascha Wildner 
322d2902f79SSascha Wildner 	/*
323d2902f79SSascha Wildner 	 * Interface #1
324d2902f79SSascha Wildner  	 */
325d2902f79SSascha Wildner 
326d2902f79SSascha Wildner 	/* Incoming isochronous transfer #1 - SCO packets */
327d2902f79SSascha Wildner 	[UBT_IF_1_ISOC_DT_RD1] = {
328d2902f79SSascha Wildner 		.type =		UE_ISOCHRONOUS,
329d2902f79SSascha Wildner 		.endpoint =	UE_ADDR_ANY,
330d2902f79SSascha Wildner 		.direction =	UE_DIR_IN,
331d2902f79SSascha Wildner 		.if_index = 	1,
332d2902f79SSascha Wildner 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
333d2902f79SSascha Wildner 		.frames =	UBT_ISOC_NFRAMES,
334d2902f79SSascha Wildner 		.flags =	{ .short_xfer_ok = 1, },
335d2902f79SSascha Wildner 		.callback =	&ubt_isoc_read_callback,
336d2902f79SSascha Wildner 	},
337d2902f79SSascha Wildner 	/* Incoming isochronous transfer #2 - SCO packets */
338d2902f79SSascha Wildner 	[UBT_IF_1_ISOC_DT_RD2] = {
339d2902f79SSascha Wildner 		.type =		UE_ISOCHRONOUS,
340d2902f79SSascha Wildner 		.endpoint =	UE_ADDR_ANY,
341d2902f79SSascha Wildner 		.direction =	UE_DIR_IN,
342d2902f79SSascha Wildner 		.if_index = 	1,
343d2902f79SSascha Wildner 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
344d2902f79SSascha Wildner 		.frames =	UBT_ISOC_NFRAMES,
345d2902f79SSascha Wildner 		.flags =	{ .short_xfer_ok = 1, },
346d2902f79SSascha Wildner 		.callback =	&ubt_isoc_read_callback,
347d2902f79SSascha Wildner 	},
348d2902f79SSascha Wildner 	/* Outgoing isochronous transfer #1 - SCO packets */
349d2902f79SSascha Wildner 	[UBT_IF_1_ISOC_DT_WR1] = {
350d2902f79SSascha Wildner 		.type =		UE_ISOCHRONOUS,
351d2902f79SSascha Wildner 		.endpoint =	UE_ADDR_ANY,
352d2902f79SSascha Wildner 		.direction =	UE_DIR_OUT,
353d2902f79SSascha Wildner 		.if_index = 	1,
354d2902f79SSascha Wildner 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
355d2902f79SSascha Wildner 		.frames =	UBT_ISOC_NFRAMES,
356d2902f79SSascha Wildner 		.flags =	{ .short_xfer_ok = 1, },
357d2902f79SSascha Wildner 		.callback =	&ubt_isoc_write_callback,
358d2902f79SSascha Wildner 	},
359d2902f79SSascha Wildner 	/* Outgoing isochronous transfer #2 - SCO packets */
360d2902f79SSascha Wildner 	[UBT_IF_1_ISOC_DT_WR2] = {
361d2902f79SSascha Wildner 		.type =		UE_ISOCHRONOUS,
362d2902f79SSascha Wildner 		.endpoint =	UE_ADDR_ANY,
363d2902f79SSascha Wildner 		.direction =	UE_DIR_OUT,
364d2902f79SSascha Wildner 		.if_index = 	1,
365d2902f79SSascha Wildner 		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
366d2902f79SSascha Wildner 		.frames =	UBT_ISOC_NFRAMES,
367d2902f79SSascha Wildner 		.flags =	{ .short_xfer_ok = 1, },
368d2902f79SSascha Wildner 		.callback =	&ubt_isoc_write_callback,
369d2902f79SSascha Wildner 	},
370d2902f79SSascha Wildner };
371d2902f79SSascha Wildner 
372d2902f79SSascha Wildner /*
373b06ebda0SMatthew Dillon  * If for some reason device should not be attached then put
374b06ebda0SMatthew Dillon  * VendorID/ProductID pair into the list below. The format is
375b06ebda0SMatthew Dillon  * as follows:
376b06ebda0SMatthew Dillon  *
377d2902f79SSascha Wildner  *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
378b06ebda0SMatthew Dillon  *
379b06ebda0SMatthew Dillon  * where VENDOR_ID and PRODUCT_ID are hex numbers.
380b06ebda0SMatthew Dillon  */
381b06ebda0SMatthew Dillon 
382d2902f79SSascha Wildner static const STRUCT_USB_HOST_ID ubt_ignore_devs[] =
383d2902f79SSascha Wildner {
384d2902f79SSascha Wildner 	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
385d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
386d2902f79SSascha Wildner 
387d2902f79SSascha Wildner 	/* Atheros 3011 with sflash firmware */
388d2902f79SSascha Wildner 	{ USB_VPI(0x0cf3, 0x3002, 0) },
389d2902f79SSascha Wildner 	{ USB_VPI(0x0cf3, 0xe019, 0) },
390d2902f79SSascha Wildner 	{ USB_VPI(0x13d3, 0x3304, 0) },
391d2902f79SSascha Wildner 	{ USB_VPI(0x0930, 0x0215, 0) },
392d2902f79SSascha Wildner 	{ USB_VPI(0x0489, 0xe03d, 0) },
393d2902f79SSascha Wildner 	{ USB_VPI(0x0489, 0xe027, 0) },
394d2902f79SSascha Wildner 
395d2902f79SSascha Wildner 	/* Atheros AR9285 Malbec with sflash firmware */
396d2902f79SSascha Wildner 	{ USB_VPI(0x03f0, 0x311d, 0) },
397d2902f79SSascha Wildner 
398d2902f79SSascha Wildner 	/* Atheros 3012 with sflash firmware */
399d2902f79SSascha Wildner 	{ USB_VPI(0x0cf3, 0x3004, 0), USB_DEV_BCD_LTEQ(1) },
400d2902f79SSascha Wildner 	{ USB_VPI(0x0cf3, 0x311d, 0), USB_DEV_BCD_LTEQ(1) },
401d2902f79SSascha Wildner 	{ USB_VPI(0x13d3, 0x3375, 0), USB_DEV_BCD_LTEQ(1) },
402d2902f79SSascha Wildner 	{ USB_VPI(0x04ca, 0x3005, 0), USB_DEV_BCD_LTEQ(1) },
403d2902f79SSascha Wildner 	{ USB_VPI(0x04ca, 0x3006, 0), USB_DEV_BCD_LTEQ(1) },
404d2902f79SSascha Wildner 	{ USB_VPI(0x04ca, 0x3008, 0), USB_DEV_BCD_LTEQ(1) },
405d2902f79SSascha Wildner 	{ USB_VPI(0x13d3, 0x3362, 0), USB_DEV_BCD_LTEQ(1) },
406d2902f79SSascha Wildner 	{ USB_VPI(0x0cf3, 0xe004, 0), USB_DEV_BCD_LTEQ(1) },
407d2902f79SSascha Wildner 	{ USB_VPI(0x0930, 0x0219, 0), USB_DEV_BCD_LTEQ(1) },
408d2902f79SSascha Wildner 	{ USB_VPI(0x0489, 0xe057, 0), USB_DEV_BCD_LTEQ(1) },
409d2902f79SSascha Wildner 	{ USB_VPI(0x13d3, 0x3393, 0), USB_DEV_BCD_LTEQ(1) },
410d2902f79SSascha Wildner 	{ USB_VPI(0x0489, 0xe04e, 0), USB_DEV_BCD_LTEQ(1) },
411d2902f79SSascha Wildner 	{ USB_VPI(0x0489, 0xe056, 0), USB_DEV_BCD_LTEQ(1) },
412d2902f79SSascha Wildner 
413d2902f79SSascha Wildner 	/* Atheros AR5BBU12 with sflash firmware */
414d2902f79SSascha Wildner 	{ USB_VPI(0x0489, 0xe02c, 0), USB_DEV_BCD_LTEQ(1) },
415d2902f79SSascha Wildner 
416d2902f79SSascha Wildner 	/* Atheros AR5BBU12 with sflash firmware */
417d2902f79SSascha Wildner 	{ USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) },
418d2902f79SSascha Wildner 	{ USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) },
419d2902f79SSascha Wildner };
420d2902f79SSascha Wildner 
421d2902f79SSascha Wildner /* List of supported bluetooth devices */
422d2902f79SSascha Wildner static const STRUCT_USB_HOST_ID ubt_devs[] =
423d2902f79SSascha Wildner {
424d2902f79SSascha Wildner 	/* Generic Bluetooth class devices */
425d2902f79SSascha Wildner 	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
426d2902f79SSascha Wildner 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
427d2902f79SSascha Wildner 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
428d2902f79SSascha Wildner 
429d2902f79SSascha Wildner 	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
430d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
431d2902f79SSascha Wildner 
432d2902f79SSascha Wildner 	/* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
433d2902f79SSascha Wildner 	{ USB_VENDOR(USB_VENDOR_BROADCOM),
434d2902f79SSascha Wildner 	  USB_IFACE_CLASS(UICLASS_VENDOR),
435d2902f79SSascha Wildner 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
436d2902f79SSascha Wildner 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
437d2902f79SSascha Wildner 
438d2902f79SSascha Wildner 	/* Apple-specific (Broadcom) devices */
439d2902f79SSascha Wildner 	{ USB_VENDOR(USB_VENDOR_APPLE),
440d2902f79SSascha Wildner 	  USB_IFACE_CLASS(UICLASS_VENDOR),
441d2902f79SSascha Wildner 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
442d2902f79SSascha Wildner 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
443d2902f79SSascha Wildner 
444d2902f79SSascha Wildner 	/* Foxconn - Hon Hai */
445d2902f79SSascha Wildner 	{ USB_VENDOR(USB_VENDOR_FOXCONN),
446d2902f79SSascha Wildner 	  USB_IFACE_CLASS(UICLASS_VENDOR),
447d2902f79SSascha Wildner 	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
448d2902f79SSascha Wildner 	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
449d2902f79SSascha Wildner 
450d2902f79SSascha Wildner 	/* MediaTek MT76x0E */
451d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },
452d2902f79SSascha Wildner 
453d2902f79SSascha Wildner 	/* Broadcom SoftSailing reporting vendor specific */
454d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },
455d2902f79SSascha Wildner 
456d2902f79SSascha Wildner 	/* Apple MacBookPro 7,1 */
457d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },
458d2902f79SSascha Wildner 
459d2902f79SSascha Wildner 	/* Apple iMac11,1 */
460d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },
461d2902f79SSascha Wildner 
462d2902f79SSascha Wildner 	/* Apple MacBookPro6,2 */
463d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },
464d2902f79SSascha Wildner 
465d2902f79SSascha Wildner 	/* Apple MacBookAir3,1, MacBookAir3,2 */
466d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },
467d2902f79SSascha Wildner 
468d2902f79SSascha Wildner 	/* Apple MacBookAir4,1 */
469d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },
470d2902f79SSascha Wildner 
471d2902f79SSascha Wildner 	/* MacBookAir6,1 */
472d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },
473d2902f79SSascha Wildner 
474d2902f79SSascha Wildner 	/* Apple MacBookPro8,2 */
475d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },
476d2902f79SSascha Wildner 
477d2902f79SSascha Wildner 	/* Apple MacMini5,1 */
478d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },
479d2902f79SSascha Wildner 
480d2902f79SSascha Wildner 	/* Bluetooth Ultraport Module from IBM */
481d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },
482d2902f79SSascha Wildner 
483d2902f79SSascha Wildner 	/* ALPS Modules with non-standard ID */
484d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },
485d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },
486d2902f79SSascha Wildner 
487d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },
488d2902f79SSascha Wildner 
489d2902f79SSascha Wildner 	/* Canyon CN-BTU1 with HID interfaces */
490d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },
491d2902f79SSascha Wildner 
492d2902f79SSascha Wildner 	/* Broadcom BCM20702A0 */
493d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },
494d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },
495d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },
496d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },
497d2902f79SSascha Wildner 	{ USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
498b06ebda0SMatthew Dillon };
499b06ebda0SMatthew Dillon 
500b06ebda0SMatthew Dillon /*
501d2902f79SSascha Wildner  * Probe for a USB Bluetooth device.
502d2902f79SSascha Wildner  * USB context.
503b06ebda0SMatthew Dillon  */
504b06ebda0SMatthew Dillon 
505b06ebda0SMatthew Dillon static int
ubt_probe(device_t dev)506d2902f79SSascha Wildner ubt_probe(device_t dev)
507b06ebda0SMatthew Dillon {
508d2902f79SSascha Wildner 	struct usb_attach_arg	*uaa = device_get_ivars(dev);
509d2902f79SSascha Wildner 	int error;
510b06ebda0SMatthew Dillon 
511d2902f79SSascha Wildner 	if (uaa->usb_mode != USB_MODE_HOST)
512d2902f79SSascha Wildner 		return (ENXIO);
513d2902f79SSascha Wildner 
514d2902f79SSascha Wildner 	if (uaa->info.bIfaceIndex != 0)
515d2902f79SSascha Wildner 		return (ENXIO);
516d2902f79SSascha Wildner 
517d2902f79SSascha Wildner 	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
518d2902f79SSascha Wildner 			sizeof(ubt_ignore_devs), uaa) == 0)
519d2902f79SSascha Wildner 		return (ENXIO);
520d2902f79SSascha Wildner 
521d2902f79SSascha Wildner 	error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
522d2902f79SSascha Wildner 	if (error == 0)
523d2902f79SSascha Wildner 		return (BUS_PROBE_GENERIC);
524d2902f79SSascha Wildner 	return (error);
525d2902f79SSascha Wildner } /* ubt_probe */
526d2902f79SSascha Wildner 
527d2902f79SSascha Wildner /*
528d2902f79SSascha Wildner  * Attach the device.
529d2902f79SSascha Wildner  * USB context.
530d2902f79SSascha Wildner  */
531d2902f79SSascha Wildner 
532d2902f79SSascha Wildner static int
ubt_attach(device_t dev)533d2902f79SSascha Wildner ubt_attach(device_t dev)
534d2902f79SSascha Wildner {
535d2902f79SSascha Wildner 	struct usb_attach_arg		*uaa = device_get_ivars(dev);
536d2902f79SSascha Wildner 	struct ubt_softc		*sc = device_get_softc(dev);
537d2902f79SSascha Wildner 	struct usb_endpoint_descriptor	*ed;
538d2902f79SSascha Wildner 	struct usb_interface_descriptor *id;
539d2902f79SSascha Wildner 	struct usb_interface		*iface;
540d2902f79SSascha Wildner 	uint16_t			wMaxPacketSize;
541d2902f79SSascha Wildner 	uint8_t				alt_index, i, j;
542d2902f79SSascha Wildner 	uint8_t				iface_index[2] = { 0, 1 };
543d2902f79SSascha Wildner 
544d2902f79SSascha Wildner 	device_set_usb_desc(dev);
545d2902f79SSascha Wildner 
546d2902f79SSascha Wildner 	sc->sc_dev = dev;
547d2902f79SSascha Wildner 	sc->sc_debug = NG_UBT_WARN_LEVEL;
548d2902f79SSascha Wildner 
549d2902f79SSascha Wildner 	/*
550d2902f79SSascha Wildner 	 * Create Netgraph node
551d2902f79SSascha Wildner 	 */
552d2902f79SSascha Wildner 
553d2902f79SSascha Wildner 	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
554d2902f79SSascha Wildner 		UBT_ALERT(sc, "could not create Netgraph node\n");
555d2902f79SSascha Wildner 		return (ENXIO);
556d2902f79SSascha Wildner 	}
557d2902f79SSascha Wildner 
558d2902f79SSascha Wildner 	/* Name Netgraph node */
559d2902f79SSascha Wildner 	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
560d2902f79SSascha Wildner 		UBT_ALERT(sc, "could not name Netgraph node\n");
561d2902f79SSascha Wildner 		NG_NODE_UNREF(sc->sc_node);
562d2902f79SSascha Wildner 		return (ENXIO);
563d2902f79SSascha Wildner 	}
564d2902f79SSascha Wildner 	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
565d2902f79SSascha Wildner 	NG_NODE_FORCE_WRITER(sc->sc_node);
566b06ebda0SMatthew Dillon 
567b06ebda0SMatthew Dillon 	/*
568b06ebda0SMatthew Dillon 	 * Initialize device softc structure
569b06ebda0SMatthew Dillon 	 */
570b06ebda0SMatthew Dillon 
571d2902f79SSascha Wildner 	/* initialize locks */
572d2902f79SSascha Wildner 	lockinit(&sc->sc_ng_lock, "ubt ng", 0, 0);
573d2902f79SSascha Wildner 	lockinit(&sc->sc_if_lock, "ubt if", 0, LK_CANRECURSE);
574b06ebda0SMatthew Dillon 
575d2902f79SSascha Wildner 	/* initialize packet queues */
576b06ebda0SMatthew Dillon 	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
577b06ebda0SMatthew Dillon 	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
578b06ebda0SMatthew Dillon 	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
579b06ebda0SMatthew Dillon 
580d2902f79SSascha Wildner 	/* initialize glue task */
581d2902f79SSascha Wildner 	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
582b06ebda0SMatthew Dillon 
583b06ebda0SMatthew Dillon 	/*
584d2902f79SSascha Wildner 	 * Configure Bluetooth USB device. Discover all required USB
585d2902f79SSascha Wildner 	 * interfaces and endpoints.
586b06ebda0SMatthew Dillon 	 *
587b06ebda0SMatthew Dillon 	 * USB device must present two interfaces:
588b06ebda0SMatthew Dillon 	 * 1) Interface 0 that has 3 endpoints
589b06ebda0SMatthew Dillon 	 *	1) Interrupt endpoint to receive HCI events
590b06ebda0SMatthew Dillon 	 *	2) Bulk IN endpoint to receive ACL data
591b06ebda0SMatthew Dillon 	 *	3) Bulk OUT endpoint to send ACL data
592b06ebda0SMatthew Dillon 	 *
593b06ebda0SMatthew Dillon 	 * 2) Interface 1 then has 2 endpoints
594b06ebda0SMatthew Dillon 	 *	1) Isochronous IN endpoint to receive SCO data
595b06ebda0SMatthew Dillon  	 *	2) Isochronous OUT endpoint to send SCO data
596b06ebda0SMatthew Dillon 	 *
597b06ebda0SMatthew Dillon 	 * Interface 1 (with isochronous endpoints) has several alternate
598b06ebda0SMatthew Dillon 	 * configurations with different packet size.
599b06ebda0SMatthew Dillon 	 */
600b06ebda0SMatthew Dillon 
601b06ebda0SMatthew Dillon 	/*
602d2902f79SSascha Wildner 	 * For interface #1 search alternate settings, and find
603d2902f79SSascha Wildner 	 * the descriptor with the largest wMaxPacketSize
604b06ebda0SMatthew Dillon 	 */
605b06ebda0SMatthew Dillon 
606d2902f79SSascha Wildner 	wMaxPacketSize = 0;
607d2902f79SSascha Wildner 	alt_index = 0;
608d2902f79SSascha Wildner 	i = 0;
609d2902f79SSascha Wildner 	j = 0;
610d2902f79SSascha Wildner 	ed = NULL;
611d2902f79SSascha Wildner 
612d2902f79SSascha Wildner 	/*
613d2902f79SSascha Wildner 	 * Search through all the descriptors looking for the largest
614d2902f79SSascha Wildner 	 * packet size:
615d2902f79SSascha Wildner 	 */
616d2902f79SSascha Wildner 	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
617d2902f79SSascha Wildner 	    usbd_get_config_descriptor(uaa->device),
618d2902f79SSascha Wildner 	    (struct usb_descriptor *)ed))) {
619d2902f79SSascha Wildner 
620d2902f79SSascha Wildner 		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
621d2902f79SSascha Wildner 		    (ed->bLength >= sizeof(*id))) {
622d2902f79SSascha Wildner 			id = (struct usb_interface_descriptor *)ed;
623d2902f79SSascha Wildner 			i = id->bInterfaceNumber;
624d2902f79SSascha Wildner 			j = id->bAlternateSetting;
625b06ebda0SMatthew Dillon 		}
626b06ebda0SMatthew Dillon 
627d2902f79SSascha Wildner 		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
628d2902f79SSascha Wildner 		    (ed->bLength >= sizeof(*ed)) &&
629d2902f79SSascha Wildner 		    (i == 1)) {
630d2902f79SSascha Wildner 			uint16_t temp;
631d2902f79SSascha Wildner 
632d2902f79SSascha Wildner 			temp = UGETW(ed->wMaxPacketSize);
633d2902f79SSascha Wildner 			if (temp > wMaxPacketSize) {
634d2902f79SSascha Wildner 				wMaxPacketSize = temp;
635d2902f79SSascha Wildner 				alt_index = j;
636d2902f79SSascha Wildner 			}
637d2902f79SSascha Wildner 		}
638b06ebda0SMatthew Dillon 	}
639b06ebda0SMatthew Dillon 
640d2902f79SSascha Wildner 	/* Set alt configuration on interface #1 only if we found it */
641d2902f79SSascha Wildner 	if (wMaxPacketSize > 0 &&
642d2902f79SSascha Wildner 	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
643d2902f79SSascha Wildner 		UBT_ALERT(sc, "could not set alternate setting %d " \
644d2902f79SSascha Wildner 			"for interface 1!\n", alt_index);
645d2902f79SSascha Wildner 		goto detach;
646b06ebda0SMatthew Dillon 	}
647b06ebda0SMatthew Dillon 
648d2902f79SSascha Wildner 	/* Setup transfers for both interfaces */
649d2902f79SSascha Wildner 	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
650d2902f79SSascha Wildner 			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_lock)) {
651d2902f79SSascha Wildner 		UBT_ALERT(sc, "could not allocate transfers\n");
652d2902f79SSascha Wildner 		goto detach;
653d2902f79SSascha Wildner 	}
654d2902f79SSascha Wildner 
655d2902f79SSascha Wildner 	/* Claim all interfaces belonging to the Bluetooth part */
656d2902f79SSascha Wildner 	for (i = 1;; i++) {
657d2902f79SSascha Wildner 		iface = usbd_get_iface(uaa->device, i);
658d2902f79SSascha Wildner 		if (iface == NULL)
659b06ebda0SMatthew Dillon 			break;
660d2902f79SSascha Wildner 		id = usbd_get_interface_descriptor(iface);
661b06ebda0SMatthew Dillon 
662d2902f79SSascha Wildner 		if ((id != NULL) &&
663d2902f79SSascha Wildner 		    (id->bInterfaceClass == UICLASS_WIRELESS) &&
664d2902f79SSascha Wildner 		    (id->bInterfaceSubClass == UISUBCLASS_RF) &&
665d2902f79SSascha Wildner 		    (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) {
666d2902f79SSascha Wildner 			usbd_set_parent_iface(uaa->device, i,
667d2902f79SSascha Wildner 			    uaa->info.bIfaceIndex);
668b06ebda0SMatthew Dillon 		}
669b06ebda0SMatthew Dillon 	}
670d2902f79SSascha Wildner 	return (0); /* success */
671b06ebda0SMatthew Dillon 
672d2902f79SSascha Wildner detach:
673d2902f79SSascha Wildner 	ubt_detach(dev);
674b06ebda0SMatthew Dillon 
675d2902f79SSascha Wildner 	return (ENXIO);
676b06ebda0SMatthew Dillon } /* ubt_attach */
677b06ebda0SMatthew Dillon 
678b06ebda0SMatthew Dillon /*
679d2902f79SSascha Wildner  * Detach the device.
680d2902f79SSascha Wildner  * USB context.
681b06ebda0SMatthew Dillon  */
682b06ebda0SMatthew Dillon 
683d2902f79SSascha Wildner int
ubt_detach(device_t dev)684d2902f79SSascha Wildner ubt_detach(device_t dev)
685b06ebda0SMatthew Dillon {
686d2902f79SSascha Wildner 	struct ubt_softc	*sc = device_get_softc(dev);
687d2902f79SSascha Wildner 	node_p			node = sc->sc_node;
688b06ebda0SMatthew Dillon 
689b06ebda0SMatthew Dillon 	/* Destroy Netgraph node */
690d2902f79SSascha Wildner 	if (node != NULL) {
691b06ebda0SMatthew Dillon 		sc->sc_node = NULL;
692d2902f79SSascha Wildner 		NG_NODE_REALLY_DIE(node);
693d2902f79SSascha Wildner 		ng_rmnode_self(node);
694b06ebda0SMatthew Dillon 	}
695b06ebda0SMatthew Dillon 
696d2902f79SSascha Wildner 	/* Make sure ubt_task in gone */
697d2902f79SSascha Wildner 	taskqueue_drain(taskqueue_swi, &sc->sc_task);
698b06ebda0SMatthew Dillon 
699d2902f79SSascha Wildner 	/* Free USB transfers, if any */
700d2902f79SSascha Wildner 	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
701b06ebda0SMatthew Dillon 
702b06ebda0SMatthew Dillon 	/* Destroy queues */
703d2902f79SSascha Wildner 	UBT_NG_LOCK(sc);
704d2902f79SSascha Wildner 	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
705d2902f79SSascha Wildner 	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
706d2902f79SSascha Wildner 	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
707d2902f79SSascha Wildner 	UBT_NG_UNLOCK(sc);
708b06ebda0SMatthew Dillon 
709d2902f79SSascha Wildner 	lockuninit(&sc->sc_if_lock);
710d2902f79SSascha Wildner 	lockuninit(&sc->sc_ng_lock);
711b06ebda0SMatthew Dillon 
712b06ebda0SMatthew Dillon 	return (0);
713b06ebda0SMatthew Dillon } /* ubt_detach */
714b06ebda0SMatthew Dillon 
715b06ebda0SMatthew Dillon /*
716d2902f79SSascha Wildner  * Called when outgoing control request (HCI command) has completed, i.e.
717d2902f79SSascha Wildner  * HCI command was sent to the device.
718d2902f79SSascha Wildner  * USB context.
719b06ebda0SMatthew Dillon  */
720b06ebda0SMatthew Dillon 
721d2902f79SSascha Wildner static void
ubt_ctrl_write_callback(struct usb_xfer * xfer,usb_error_t error)722d2902f79SSascha Wildner ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
723b06ebda0SMatthew Dillon {
724d2902f79SSascha Wildner 	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
725d2902f79SSascha Wildner 	struct usb_device_request	req;
726d2902f79SSascha Wildner 	struct mbuf			*m;
727d2902f79SSascha Wildner 	struct usb_page_cache		*pc;
728d2902f79SSascha Wildner 	int				actlen;
729b06ebda0SMatthew Dillon 
730d2902f79SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
731b06ebda0SMatthew Dillon 
732d2902f79SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
733d2902f79SSascha Wildner 	case USB_ST_TRANSFERRED:
734d2902f79SSascha Wildner 		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
735d2902f79SSascha Wildner 		UBT_STAT_BYTES_SENT(sc, actlen);
736d2902f79SSascha Wildner 		UBT_STAT_PCKTS_SENT(sc);
737d2902f79SSascha Wildner 		/* FALLTHROUGH */
738d2902f79SSascha Wildner 
739d2902f79SSascha Wildner 	case USB_ST_SETUP:
740d2902f79SSascha Wildner send_next:
741d2902f79SSascha Wildner 		/* Get next command mbuf, if any */
742d2902f79SSascha Wildner 		UBT_NG_LOCK(sc);
743b06ebda0SMatthew Dillon 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
744d2902f79SSascha Wildner 		UBT_NG_UNLOCK(sc);
745d2902f79SSascha Wildner 
746b06ebda0SMatthew Dillon 		if (m == NULL) {
747d2902f79SSascha Wildner 			UBT_INFO(sc, "HCI command queue is empty\n");
748d2902f79SSascha Wildner 			break;	/* transfer complete */
749b06ebda0SMatthew Dillon 		}
750b06ebda0SMatthew Dillon 
751b06ebda0SMatthew Dillon 		/* Initialize a USB control request and then schedule it */
752b06ebda0SMatthew Dillon 		bzero(&req, sizeof(req));
753b06ebda0SMatthew Dillon 		req.bmRequestType = UBT_HCI_REQUEST;
754b06ebda0SMatthew Dillon 		USETW(req.wLength, m->m_pkthdr.len);
755b06ebda0SMatthew Dillon 
756d2902f79SSascha Wildner 		UBT_INFO(sc, "Sending control request, " \
757d2902f79SSascha Wildner 			"bmRequestType=0x%02x, wLength=%d\n",
758d2902f79SSascha Wildner 			req.bmRequestType, UGETW(req.wLength));
759b06ebda0SMatthew Dillon 
760d2902f79SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
761d2902f79SSascha Wildner 		usbd_copy_in(pc, 0, &req, sizeof(req));
762d2902f79SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 1);
763d2902f79SSascha Wildner 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
764b06ebda0SMatthew Dillon 
765d2902f79SSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
766d2902f79SSascha Wildner 		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
767d2902f79SSascha Wildner 		usbd_xfer_set_frames(xfer, 2);
768b06ebda0SMatthew Dillon 
769b06ebda0SMatthew Dillon 		NG_FREE_M(m);
770b06ebda0SMatthew Dillon 
771d2902f79SSascha Wildner 		usbd_transfer_submit(xfer);
772d2902f79SSascha Wildner 		break;
773d2902f79SSascha Wildner 
774d2902f79SSascha Wildner 	default: /* Error */
775d2902f79SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
776d2902f79SSascha Wildner 			UBT_WARN(sc, "control transfer failed: %s\n",
777d2902f79SSascha Wildner 				usbd_errstr(error));
778d2902f79SSascha Wildner 
779d2902f79SSascha Wildner 			UBT_STAT_OERROR(sc);
780d2902f79SSascha Wildner 			goto send_next;
781d2902f79SSascha Wildner 		}
782d2902f79SSascha Wildner 
783d2902f79SSascha Wildner 		/* transfer cancelled */
784d2902f79SSascha Wildner 		break;
785d2902f79SSascha Wildner 	}
786d2902f79SSascha Wildner } /* ubt_ctrl_write_callback */
787b06ebda0SMatthew Dillon 
788b06ebda0SMatthew Dillon /*
789d2902f79SSascha Wildner  * Called when incoming interrupt transfer (HCI event) has completed, i.e.
790d2902f79SSascha Wildner  * HCI event was received from the device.
791d2902f79SSascha Wildner  * USB context.
792b06ebda0SMatthew Dillon  */
793b06ebda0SMatthew Dillon 
794b06ebda0SMatthew Dillon static void
ubt_intr_read_callback(struct usb_xfer * xfer,usb_error_t error)795d2902f79SSascha Wildner ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
796b06ebda0SMatthew Dillon {
797d2902f79SSascha Wildner 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
798d2902f79SSascha Wildner 	struct mbuf		*m;
799d2902f79SSascha Wildner 	ng_hci_event_pkt_t	*hdr;
800d2902f79SSascha Wildner 	struct usb_page_cache	*pc;
801d2902f79SSascha Wildner 	int			actlen;
802b06ebda0SMatthew Dillon 
803d2902f79SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
804b06ebda0SMatthew Dillon 
805d2902f79SSascha Wildner 	m = NULL;
806b06ebda0SMatthew Dillon 
807d2902f79SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
808d2902f79SSascha Wildner 	case USB_ST_TRANSFERRED:
809d2902f79SSascha Wildner 		/* Allocate a new mbuf */
810b5523eacSSascha Wildner 		MGETHDR(m, M_NOWAIT, MT_DATA);
811d2902f79SSascha Wildner 		if (m == NULL) {
812d2902f79SSascha Wildner 			UBT_STAT_IERROR(sc);
813d2902f79SSascha Wildner 			goto submit_next;
814d2902f79SSascha Wildner 		}
815b06ebda0SMatthew Dillon 
816b5523eacSSascha Wildner 		MCLGET(m, M_NOWAIT);
817b06ebda0SMatthew Dillon 		if (!(m->m_flags & M_EXT)) {
818d2902f79SSascha Wildner 			UBT_STAT_IERROR(sc);
819d2902f79SSascha Wildner 			goto submit_next;
820b06ebda0SMatthew Dillon 		}
821b06ebda0SMatthew Dillon 
822d2902f79SSascha Wildner 		/* Add HCI packet type */
823d2902f79SSascha Wildner 		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
824b06ebda0SMatthew Dillon 		m->m_pkthdr.len = m->m_len = 1;
825b06ebda0SMatthew Dillon 
826d2902f79SSascha Wildner 		if (actlen > MCLBYTES - 1)
827d2902f79SSascha Wildner 			actlen = MCLBYTES - 1;
828b06ebda0SMatthew Dillon 
829d2902f79SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
830d2902f79SSascha Wildner 		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
831d2902f79SSascha Wildner 		m->m_pkthdr.len += actlen;
832d2902f79SSascha Wildner 		m->m_len += actlen;
833b06ebda0SMatthew Dillon 
834d2902f79SSascha Wildner 		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
835d2902f79SSascha Wildner 			actlen);
836b06ebda0SMatthew Dillon 
837d2902f79SSascha Wildner 		/* Validate packet and send it up the stack */
838d2902f79SSascha Wildner 		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
839d2902f79SSascha Wildner 			UBT_INFO(sc, "HCI event packet is too short\n");
840b06ebda0SMatthew Dillon 
841d2902f79SSascha Wildner 			UBT_STAT_IERROR(sc);
842d2902f79SSascha Wildner 			goto submit_next;
843b06ebda0SMatthew Dillon 		}
844b06ebda0SMatthew Dillon 
845b06ebda0SMatthew Dillon 		hdr = mtod(m, ng_hci_event_pkt_t *);
846d2902f79SSascha Wildner 		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
847d2902f79SSascha Wildner 			UBT_ERR(sc, "Invalid HCI event packet size, " \
848d2902f79SSascha Wildner 				"length=%d, pktlen=%d\n",
849d2902f79SSascha Wildner 				hdr->length, m->m_pkthdr.len);
850b06ebda0SMatthew Dillon 
851d2902f79SSascha Wildner 			UBT_STAT_IERROR(sc);
852d2902f79SSascha Wildner 			goto submit_next;
853b06ebda0SMatthew Dillon 		}
854b06ebda0SMatthew Dillon 
855d2902f79SSascha Wildner 		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
856d2902f79SSascha Wildner 			"length=%d\n", m->m_pkthdr.len, hdr->length);
857b06ebda0SMatthew Dillon 
858d2902f79SSascha Wildner 		UBT_STAT_PCKTS_RECV(sc);
859d2902f79SSascha Wildner 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
860d2902f79SSascha Wildner 
861d2902f79SSascha Wildner 		ubt_fwd_mbuf_up(sc, &m);
862d2902f79SSascha Wildner 		/* m == NULL at this point */
863d2902f79SSascha Wildner 		/* FALLTHROUGH */
864d2902f79SSascha Wildner 
865d2902f79SSascha Wildner 	case USB_ST_SETUP:
866d2902f79SSascha Wildner submit_next:
867d2902f79SSascha Wildner 		NG_FREE_M(m); /* checks for m != NULL */
868d2902f79SSascha Wildner 
869d2902f79SSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
870d2902f79SSascha Wildner 		usbd_transfer_submit(xfer);
871d2902f79SSascha Wildner 		break;
872d2902f79SSascha Wildner 
873d2902f79SSascha Wildner 	default: /* Error */
874d2902f79SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
875d2902f79SSascha Wildner 			UBT_WARN(sc, "interrupt transfer failed: %s\n",
876d2902f79SSascha Wildner 				usbd_errstr(error));
877d2902f79SSascha Wildner 
878d2902f79SSascha Wildner 			/* Try to clear stall first */
879d2902f79SSascha Wildner 			usbd_xfer_set_stall(xfer);
880d2902f79SSascha Wildner 			goto submit_next;
881b06ebda0SMatthew Dillon 		}
882d2902f79SSascha Wildner 			/* transfer cancelled */
883d2902f79SSascha Wildner 		break;
884b06ebda0SMatthew Dillon 	}
885d2902f79SSascha Wildner } /* ubt_intr_read_callback */
886b06ebda0SMatthew Dillon 
887b06ebda0SMatthew Dillon /*
888d2902f79SSascha Wildner  * Called when incoming bulk transfer (ACL packet) has completed, i.e.
889d2902f79SSascha Wildner  * ACL packet was received from the device.
890d2902f79SSascha Wildner  * USB context.
891b06ebda0SMatthew Dillon  */
892b06ebda0SMatthew Dillon 
893d2902f79SSascha Wildner static void
ubt_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)894d2902f79SSascha Wildner ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
895b06ebda0SMatthew Dillon {
896d2902f79SSascha Wildner 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
897d2902f79SSascha Wildner 	struct mbuf		*m;
898d2902f79SSascha Wildner 	ng_hci_acldata_pkt_t	*hdr;
899d2902f79SSascha Wildner 	struct usb_page_cache	*pc;
900d2902f79SSascha Wildner 	int len;
901d2902f79SSascha Wildner 	int actlen;
902b06ebda0SMatthew Dillon 
903d2902f79SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
904b06ebda0SMatthew Dillon 
905d2902f79SSascha Wildner 	m = NULL;
906d2902f79SSascha Wildner 
907d2902f79SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
908d2902f79SSascha Wildner 	case USB_ST_TRANSFERRED:
909d2902f79SSascha Wildner 		/* Allocate new mbuf */
910b5523eacSSascha Wildner 		MGETHDR(m, M_NOWAIT, MT_DATA);
911d2902f79SSascha Wildner 		if (m == NULL) {
912d2902f79SSascha Wildner 			UBT_STAT_IERROR(sc);
913d2902f79SSascha Wildner 			goto submit_next;
914d2902f79SSascha Wildner 		}
915b06ebda0SMatthew Dillon 
916b5523eacSSascha Wildner 		MCLGET(m, M_NOWAIT);
917b06ebda0SMatthew Dillon 		if (!(m->m_flags & M_EXT)) {
918d2902f79SSascha Wildner 			UBT_STAT_IERROR(sc);
919d2902f79SSascha Wildner 			goto submit_next;
920b06ebda0SMatthew Dillon 		}
921b06ebda0SMatthew Dillon 
922d2902f79SSascha Wildner 		/* Add HCI packet type */
923d2902f79SSascha Wildner 		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
924b06ebda0SMatthew Dillon 		m->m_pkthdr.len = m->m_len = 1;
925b06ebda0SMatthew Dillon 
926d2902f79SSascha Wildner 		if (actlen > MCLBYTES - 1)
927d2902f79SSascha Wildner 			actlen = MCLBYTES - 1;
928b06ebda0SMatthew Dillon 
929d2902f79SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
930d2902f79SSascha Wildner 		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
931d2902f79SSascha Wildner 		m->m_pkthdr.len += actlen;
932d2902f79SSascha Wildner 		m->m_len += actlen;
933b06ebda0SMatthew Dillon 
934d2902f79SSascha Wildner 		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
935d2902f79SSascha Wildner 			actlen);
936b06ebda0SMatthew Dillon 
937d2902f79SSascha Wildner 		/* Validate packet and send it up the stack */
938d2902f79SSascha Wildner 		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
939d2902f79SSascha Wildner 			UBT_INFO(sc, "HCI ACL packet is too short\n");
940b06ebda0SMatthew Dillon 
941d2902f79SSascha Wildner 			UBT_STAT_IERROR(sc);
942d2902f79SSascha Wildner 			goto submit_next;
943b06ebda0SMatthew Dillon 		}
944b06ebda0SMatthew Dillon 
945b06ebda0SMatthew Dillon 		hdr = mtod(m, ng_hci_acldata_pkt_t *);
946b06ebda0SMatthew Dillon 		len = le16toh(hdr->length);
947d2902f79SSascha Wildner 		if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
948d2902f79SSascha Wildner 			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
949d2902f79SSascha Wildner 				"pktlen=%d\n", len, m->m_pkthdr.len);
950b06ebda0SMatthew Dillon 
951d2902f79SSascha Wildner 			UBT_STAT_IERROR(sc);
952d2902f79SSascha Wildner 			goto submit_next;
953b06ebda0SMatthew Dillon 		}
954d2902f79SSascha Wildner 
955d2902f79SSascha Wildner 		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
956d2902f79SSascha Wildner 			"length=%d\n", m->m_pkthdr.len, len);
957d2902f79SSascha Wildner 
958d2902f79SSascha Wildner 		UBT_STAT_PCKTS_RECV(sc);
959d2902f79SSascha Wildner 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
960d2902f79SSascha Wildner 
961d2902f79SSascha Wildner 		ubt_fwd_mbuf_up(sc, &m);
962d2902f79SSascha Wildner 		/* m == NULL at this point */
963d2902f79SSascha Wildner 		/* FALLTHOUGH */
964d2902f79SSascha Wildner 
965d2902f79SSascha Wildner 	case USB_ST_SETUP:
966d2902f79SSascha Wildner submit_next:
967d2902f79SSascha Wildner 		NG_FREE_M(m); /* checks for m != NULL */
968d2902f79SSascha Wildner 
969d2902f79SSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
970d2902f79SSascha Wildner 		usbd_transfer_submit(xfer);
971d2902f79SSascha Wildner 		break;
972d2902f79SSascha Wildner 
973d2902f79SSascha Wildner 	default: /* Error */
974d2902f79SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
975d2902f79SSascha Wildner 			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
976d2902f79SSascha Wildner 				usbd_errstr(error));
977d2902f79SSascha Wildner 
978d2902f79SSascha Wildner 			/* Try to clear stall first */
979d2902f79SSascha Wildner 			usbd_xfer_set_stall(xfer);
980d2902f79SSascha Wildner 			goto submit_next;
981d2902f79SSascha Wildner 		}
982d2902f79SSascha Wildner 			/* transfer cancelled */
983d2902f79SSascha Wildner 		break;
984d2902f79SSascha Wildner 	}
985d2902f79SSascha Wildner } /* ubt_bulk_read_callback */
986b06ebda0SMatthew Dillon 
987b06ebda0SMatthew Dillon /*
988d2902f79SSascha Wildner  * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
989d2902f79SSascha Wildner  * ACL packet was sent to the device.
990d2902f79SSascha Wildner  * USB context.
991b06ebda0SMatthew Dillon  */
992b06ebda0SMatthew Dillon 
993d2902f79SSascha Wildner static void
ubt_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)994d2902f79SSascha Wildner ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
995b06ebda0SMatthew Dillon {
996d2902f79SSascha Wildner 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
997d2902f79SSascha Wildner 	struct mbuf		*m;
998d2902f79SSascha Wildner 	struct usb_page_cache	*pc;
999d2902f79SSascha Wildner 	int			actlen;
1000b06ebda0SMatthew Dillon 
1001d2902f79SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1002b06ebda0SMatthew Dillon 
1003d2902f79SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
1004d2902f79SSascha Wildner 	case USB_ST_TRANSFERRED:
1005d2902f79SSascha Wildner 		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
1006d2902f79SSascha Wildner 		UBT_STAT_BYTES_SENT(sc, actlen);
1007d2902f79SSascha Wildner 		UBT_STAT_PCKTS_SENT(sc);
1008d2902f79SSascha Wildner 		/* FALLTHROUGH */
1009d2902f79SSascha Wildner 
1010d2902f79SSascha Wildner 	case USB_ST_SETUP:
1011d2902f79SSascha Wildner send_next:
1012d2902f79SSascha Wildner 		/* Get next mbuf, if any */
1013d2902f79SSascha Wildner 		UBT_NG_LOCK(sc);
1014b06ebda0SMatthew Dillon 		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
1015d2902f79SSascha Wildner 		UBT_NG_UNLOCK(sc);
1016b06ebda0SMatthew Dillon 
1017d2902f79SSascha Wildner 		if (m == NULL) {
1018d2902f79SSascha Wildner 			UBT_INFO(sc, "ACL data queue is empty\n");
1019d2902f79SSascha Wildner 			break; /* transfer completed */
1020b06ebda0SMatthew Dillon 		}
1021b06ebda0SMatthew Dillon 
1022b06ebda0SMatthew Dillon 		/*
1023d2902f79SSascha Wildner 		 * Copy ACL data frame back to a linear USB transfer buffer
1024d2902f79SSascha Wildner 		 * and schedule transfer
1025b06ebda0SMatthew Dillon 		 */
1026b06ebda0SMatthew Dillon 
1027d2902f79SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
1028d2902f79SSascha Wildner 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
1029d2902f79SSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
1030d2902f79SSascha Wildner 
1031d2902f79SSascha Wildner 		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
1032b06ebda0SMatthew Dillon 			m->m_pkthdr.len);
1033b06ebda0SMatthew Dillon 
1034b06ebda0SMatthew Dillon 		NG_FREE_M(m);
1035b06ebda0SMatthew Dillon 
1036d2902f79SSascha Wildner 		usbd_transfer_submit(xfer);
1037d2902f79SSascha Wildner 		break;
1038d2902f79SSascha Wildner 
1039d2902f79SSascha Wildner 	default: /* Error */
1040d2902f79SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
1041d2902f79SSascha Wildner 			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
1042d2902f79SSascha Wildner 				usbd_errstr(error));
1043d2902f79SSascha Wildner 
1044d2902f79SSascha Wildner 			UBT_STAT_OERROR(sc);
1045d2902f79SSascha Wildner 
1046d2902f79SSascha Wildner 			/* try to clear stall first */
1047d2902f79SSascha Wildner 			usbd_xfer_set_stall(xfer);
1048d2902f79SSascha Wildner 			goto send_next;
1049d2902f79SSascha Wildner 		}
1050d2902f79SSascha Wildner 			/* transfer cancelled */
1051d2902f79SSascha Wildner 		break;
1052d2902f79SSascha Wildner 	}
1053d2902f79SSascha Wildner } /* ubt_bulk_write_callback */
1054b06ebda0SMatthew Dillon 
1055b06ebda0SMatthew Dillon /*
1056d2902f79SSascha Wildner  * Called when incoming isoc transfer (SCO packet) has completed, i.e.
1057d2902f79SSascha Wildner  * SCO packet was received from the device.
1058d2902f79SSascha Wildner  * USB context.
1059b06ebda0SMatthew Dillon  */
1060b06ebda0SMatthew Dillon 
1061b06ebda0SMatthew Dillon static void
ubt_isoc_read_callback(struct usb_xfer * xfer,usb_error_t error)1062d2902f79SSascha Wildner ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
1063b06ebda0SMatthew Dillon {
1064d2902f79SSascha Wildner 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1065d2902f79SSascha Wildner 	int			n;
1066d2902f79SSascha Wildner 	int actlen, nframes;
1067b06ebda0SMatthew Dillon 
1068d2902f79SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1069b06ebda0SMatthew Dillon 
1070d2902f79SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
1071d2902f79SSascha Wildner 	case USB_ST_TRANSFERRED:
1072d2902f79SSascha Wildner 		for (n = 0; n < nframes; n ++)
1073d2902f79SSascha Wildner 			if (ubt_isoc_read_one_frame(xfer, n) < 0)
1074d2902f79SSascha Wildner 				break;
1075d2902f79SSascha Wildner 		/* FALLTHROUGH */
1076b06ebda0SMatthew Dillon 
1077d2902f79SSascha Wildner 	case USB_ST_SETUP:
1078d2902f79SSascha Wildner read_next:
1079d2902f79SSascha Wildner 		for (n = 0; n < nframes; n ++)
1080d2902f79SSascha Wildner 			usbd_xfer_set_frame_len(xfer, n,
1081d2902f79SSascha Wildner 			    usbd_xfer_max_framelen(xfer));
1082b06ebda0SMatthew Dillon 
1083d2902f79SSascha Wildner 		usbd_transfer_submit(xfer);
1084d2902f79SSascha Wildner 		break;
1085b06ebda0SMatthew Dillon 
1086d2902f79SSascha Wildner 	default: /* Error */
1087d2902f79SSascha Wildner                 if (error != USB_ERR_CANCELLED) {
1088d2902f79SSascha Wildner                         UBT_STAT_IERROR(sc);
1089d2902f79SSascha Wildner                         goto read_next;
1090b06ebda0SMatthew Dillon                 }
1091b06ebda0SMatthew Dillon 
1092d2902f79SSascha Wildner 		/* transfer cancelled */
1093d2902f79SSascha Wildner 		break;
1094b06ebda0SMatthew Dillon 	}
1095d2902f79SSascha Wildner } /* ubt_isoc_read_callback */
1096b06ebda0SMatthew Dillon 
1097b06ebda0SMatthew Dillon /*
1098d2902f79SSascha Wildner  * Helper function. Called from ubt_isoc_read_callback() to read
1099d2902f79SSascha Wildner  * SCO data from one frame.
1100d2902f79SSascha Wildner  * USB context.
1101b06ebda0SMatthew Dillon  */
1102b06ebda0SMatthew Dillon 
1103d2902f79SSascha Wildner static int
ubt_isoc_read_one_frame(struct usb_xfer * xfer,int frame_no)1104d2902f79SSascha Wildner ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
1105b06ebda0SMatthew Dillon {
1106d2902f79SSascha Wildner 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1107d2902f79SSascha Wildner 	struct usb_page_cache	*pc;
1108d2902f79SSascha Wildner 	struct mbuf		*m;
1109d2902f79SSascha Wildner 	int			len, want, got, total;
1110b06ebda0SMatthew Dillon 
1111d2902f79SSascha Wildner 	/* Get existing SCO reassembly buffer */
1112d2902f79SSascha Wildner 	pc = usbd_xfer_get_frame(xfer, 0);
1113d2902f79SSascha Wildner 	m = sc->sc_isoc_in_buffer;
1114d2902f79SSascha Wildner 	total = usbd_xfer_frame_len(xfer, frame_no);
1115b06ebda0SMatthew Dillon 
1116d2902f79SSascha Wildner 	/* While we have data in the frame */
1117d2902f79SSascha Wildner 	while (total > 0) {
1118d2902f79SSascha Wildner 		if (m == NULL) {
1119d2902f79SSascha Wildner 			/* Start new reassembly buffer */
1120b5523eacSSascha Wildner 			MGETHDR(m, M_NOWAIT, MT_DATA);
1121b06ebda0SMatthew Dillon 			if (m == NULL) {
1122d2902f79SSascha Wildner 				UBT_STAT_IERROR(sc);
1123d2902f79SSascha Wildner 				return (-1);	/* XXX out of sync! */
1124b06ebda0SMatthew Dillon 			}
1125b06ebda0SMatthew Dillon 
1126b5523eacSSascha Wildner 			MCLGET(m, M_NOWAIT);
1127d2902f79SSascha Wildner 			if (!(m->m_flags & M_EXT)) {
1128d2902f79SSascha Wildner 				UBT_STAT_IERROR(sc);
1129b06ebda0SMatthew Dillon 				NG_FREE_M(m);
1130d2902f79SSascha Wildner 				return (-1);	/* XXX out of sync! */
1131b06ebda0SMatthew Dillon 			}
1132d2902f79SSascha Wildner 
1133d2902f79SSascha Wildner 			/* Expect SCO header */
1134d2902f79SSascha Wildner 			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
1135d2902f79SSascha Wildner 			m->m_pkthdr.len = m->m_len = got = 1;
1136d2902f79SSascha Wildner 			want = sizeof(ng_hci_scodata_pkt_t);
1137d2902f79SSascha Wildner 		} else {
1138d2902f79SSascha Wildner 			/*
1139d2902f79SSascha Wildner 			 * Check if we have SCO header and if so
1140d2902f79SSascha Wildner 			 * adjust amount of data we want
1141d2902f79SSascha Wildner 			 */
1142d2902f79SSascha Wildner 			got = m->m_pkthdr.len;
1143d2902f79SSascha Wildner 			want = sizeof(ng_hci_scodata_pkt_t);
1144d2902f79SSascha Wildner 
1145d2902f79SSascha Wildner 			if (got >= want)
1146d2902f79SSascha Wildner 				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
1147d2902f79SSascha Wildner 		}
1148d2902f79SSascha Wildner 
1149d2902f79SSascha Wildner 		/* Append frame data to the SCO reassembly buffer */
1150d2902f79SSascha Wildner 		len = total;
1151d2902f79SSascha Wildner 		if (got + len > want)
1152d2902f79SSascha Wildner 			len = want - got;
1153d2902f79SSascha Wildner 
1154d2902f79SSascha Wildner 		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
1155d2902f79SSascha Wildner 			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
1156d2902f79SSascha Wildner 
1157d2902f79SSascha Wildner 		m->m_pkthdr.len += len;
1158d2902f79SSascha Wildner 		m->m_len += len;
1159d2902f79SSascha Wildner 		total -= len;
1160d2902f79SSascha Wildner 
1161d2902f79SSascha Wildner 		/* Check if we got everything we wanted, if not - continue */
1162d2902f79SSascha Wildner 		if (got != want)
1163d2902f79SSascha Wildner 			continue;
1164d2902f79SSascha Wildner 
1165d2902f79SSascha Wildner 		/* If we got here then we got complete SCO frame */
1166d2902f79SSascha Wildner 		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
1167d2902f79SSascha Wildner 			"length=%d\n", m->m_pkthdr.len,
1168d2902f79SSascha Wildner 			mtod(m, ng_hci_scodata_pkt_t *)->length);
1169d2902f79SSascha Wildner 
1170d2902f79SSascha Wildner 		UBT_STAT_PCKTS_RECV(sc);
1171d2902f79SSascha Wildner 		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
1172d2902f79SSascha Wildner 
1173d2902f79SSascha Wildner 		ubt_fwd_mbuf_up(sc, &m);
1174d2902f79SSascha Wildner 		/* m == NULL at this point */
1175d2902f79SSascha Wildner 	}
1176d2902f79SSascha Wildner 
1177d2902f79SSascha Wildner 	/* Put SCO reassembly buffer back */
1178d2902f79SSascha Wildner 	sc->sc_isoc_in_buffer = m;
1179d2902f79SSascha Wildner 
1180d2902f79SSascha Wildner 	return (0);
1181d2902f79SSascha Wildner } /* ubt_isoc_read_one_frame */
1182b06ebda0SMatthew Dillon 
1183b06ebda0SMatthew Dillon /*
1184d2902f79SSascha Wildner  * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
1185d2902f79SSascha Wildner  * SCO packet was sent to the device.
1186d2902f79SSascha Wildner  * USB context.
1187b06ebda0SMatthew Dillon  */
1188b06ebda0SMatthew Dillon 
1189d2902f79SSascha Wildner static void
ubt_isoc_write_callback(struct usb_xfer * xfer,usb_error_t error)1190d2902f79SSascha Wildner ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
1191b06ebda0SMatthew Dillon {
1192d2902f79SSascha Wildner 	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1193d2902f79SSascha Wildner 	struct usb_page_cache	*pc;
1194d2902f79SSascha Wildner 	struct mbuf		*m;
1195d2902f79SSascha Wildner 	int			n, space, offset;
1196d2902f79SSascha Wildner 	int			actlen, nframes;
1197b06ebda0SMatthew Dillon 
1198d2902f79SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1199d2902f79SSascha Wildner 	pc = usbd_xfer_get_frame(xfer, 0);
1200b06ebda0SMatthew Dillon 
1201d2902f79SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
1202d2902f79SSascha Wildner 	case USB_ST_TRANSFERRED:
1203d2902f79SSascha Wildner 		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1204d2902f79SSascha Wildner 		UBT_STAT_BYTES_SENT(sc, actlen);
1205d2902f79SSascha Wildner 		UBT_STAT_PCKTS_SENT(sc);
1206d2902f79SSascha Wildner 		/* FALLTHROUGH */
1207d2902f79SSascha Wildner 
1208d2902f79SSascha Wildner 	case USB_ST_SETUP:
1209d2902f79SSascha Wildner send_next:
1210d2902f79SSascha Wildner 		offset = 0;
1211d2902f79SSascha Wildner 		space = usbd_xfer_max_framelen(xfer) * nframes;
1212d2902f79SSascha Wildner 		m = NULL;
1213d2902f79SSascha Wildner 
1214d2902f79SSascha Wildner 		while (space > 0) {
1215b06ebda0SMatthew Dillon 			if (m == NULL) {
1216d2902f79SSascha Wildner 				UBT_NG_LOCK(sc);
1217d2902f79SSascha Wildner 				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
1218d2902f79SSascha Wildner 				UBT_NG_UNLOCK(sc);
1219b06ebda0SMatthew Dillon 
1220d2902f79SSascha Wildner 				if (m == NULL)
1221d2902f79SSascha Wildner 					break;
1222b06ebda0SMatthew Dillon 			}
1223b06ebda0SMatthew Dillon 
1224d2902f79SSascha Wildner 			n = min(space, m->m_pkthdr.len);
1225d2902f79SSascha Wildner 			if (n > 0) {
1226d2902f79SSascha Wildner 				usbd_m_copy_in(pc, offset, m,0, n);
1227d2902f79SSascha Wildner 				m_adj(m, n);
1228b06ebda0SMatthew Dillon 
1229d2902f79SSascha Wildner 				offset += n;
1230d2902f79SSascha Wildner 				space -= n;
1231b06ebda0SMatthew Dillon 			}
1232b06ebda0SMatthew Dillon 
1233d2902f79SSascha Wildner 			if (m->m_pkthdr.len == 0)
1234d2902f79SSascha Wildner 				NG_FREE_M(m); /* sets m = NULL */
1235b06ebda0SMatthew Dillon 		}
1236b06ebda0SMatthew Dillon 
1237d2902f79SSascha Wildner 		/* Put whatever is left from mbuf back on queue */
1238d2902f79SSascha Wildner 		if (m != NULL) {
1239d2902f79SSascha Wildner 			UBT_NG_LOCK(sc);
1240d2902f79SSascha Wildner 			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
1241d2902f79SSascha Wildner 			UBT_NG_UNLOCK(sc);
1242b06ebda0SMatthew Dillon 		}
1243b06ebda0SMatthew Dillon 
1244b06ebda0SMatthew Dillon 		/*
1245d2902f79SSascha Wildner 		 * Calculate sizes for isoc frames.
1246d2902f79SSascha Wildner 		 * Note that offset could be 0 at this point (i.e. we have
1247d2902f79SSascha Wildner 		 * nothing to send). That is fine, as we have isoc. transfers
1248d2902f79SSascha Wildner 		 * going in both directions all the time. In this case it
1249d2902f79SSascha Wildner 		 * would be just empty isoc. transfer.
1250d2902f79SSascha Wildner 		 */
1251d2902f79SSascha Wildner 
1252d2902f79SSascha Wildner 		for (n = 0; n < nframes; n ++) {
1253d2902f79SSascha Wildner 			usbd_xfer_set_frame_len(xfer, n,
1254d2902f79SSascha Wildner 			    min(offset, usbd_xfer_max_framelen(xfer)));
1255d2902f79SSascha Wildner 			offset -= usbd_xfer_frame_len(xfer, n);
1256d2902f79SSascha Wildner 		}
1257d2902f79SSascha Wildner 
1258d2902f79SSascha Wildner 		usbd_transfer_submit(xfer);
1259d2902f79SSascha Wildner 		break;
1260d2902f79SSascha Wildner 
1261d2902f79SSascha Wildner 	default: /* Error */
1262d2902f79SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
1263d2902f79SSascha Wildner 			UBT_STAT_OERROR(sc);
1264d2902f79SSascha Wildner 			goto send_next;
1265d2902f79SSascha Wildner 		}
1266d2902f79SSascha Wildner 
1267d2902f79SSascha Wildner 		/* transfer cancelled */
1268d2902f79SSascha Wildner 		break;
1269d2902f79SSascha Wildner 	}
1270d2902f79SSascha Wildner }
1271d2902f79SSascha Wildner 
1272d2902f79SSascha Wildner /*
1273d2902f79SSascha Wildner  * Utility function to forward provided mbuf upstream (i.e. up the stack).
1274d2902f79SSascha Wildner  * Modifies value of the mbuf pointer (sets it to NULL).
1275d2902f79SSascha Wildner  * Save to call from any context.
1276d2902f79SSascha Wildner  */
1277d2902f79SSascha Wildner 
1278d2902f79SSascha Wildner static int
ubt_fwd_mbuf_up(ubt_softc_p sc,struct mbuf ** m)1279d2902f79SSascha Wildner ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
1280d2902f79SSascha Wildner {
1281d2902f79SSascha Wildner 	hook_p	hook;
1282d2902f79SSascha Wildner 	int	error;
1283d2902f79SSascha Wildner 
1284d2902f79SSascha Wildner 	/*
1285d2902f79SSascha Wildner 	 * Close the race with Netgraph hook newhook/disconnect methods.
1286d2902f79SSascha Wildner 	 * Save the hook pointer atomically. Two cases are possible:
1287d2902f79SSascha Wildner 	 *
1288d2902f79SSascha Wildner 	 * 1) The hook pointer is NULL. It means disconnect method got
1289d2902f79SSascha Wildner 	 *    there first. In this case we are done.
1290d2902f79SSascha Wildner 	 *
1291d2902f79SSascha Wildner 	 * 2) The hook pointer is not NULL. It means that hook pointer
1292d2902f79SSascha Wildner 	 *    could be either in valid or invalid (i.e. in the process
1293d2902f79SSascha Wildner 	 *    of disconnect) state. In any case grab an extra reference
1294d2902f79SSascha Wildner 	 *    to protect the hook pointer.
1295d2902f79SSascha Wildner 	 *
1296d2902f79SSascha Wildner 	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
1297d2902f79SSascha Wildner 	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
1298d2902f79SSascha Wildner 	 */
1299d2902f79SSascha Wildner 
1300d2902f79SSascha Wildner 	UBT_NG_LOCK(sc);
1301d2902f79SSascha Wildner 	if ((hook = sc->sc_hook) != NULL)
1302d2902f79SSascha Wildner 		NG_HOOK_REF(hook);
1303d2902f79SSascha Wildner 	UBT_NG_UNLOCK(sc);
1304d2902f79SSascha Wildner 
1305d2902f79SSascha Wildner 	if (hook == NULL) {
1306d2902f79SSascha Wildner 		NG_FREE_M(*m);
1307d2902f79SSascha Wildner 		return (ENETDOWN);
1308d2902f79SSascha Wildner 	}
1309d2902f79SSascha Wildner 
1310d2902f79SSascha Wildner 	NG_SEND_DATA_ONLY(error, hook, *m);
1311d2902f79SSascha Wildner 	NG_HOOK_UNREF(hook);
1312d2902f79SSascha Wildner 
1313d2902f79SSascha Wildner 	if (error != 0)
1314d2902f79SSascha Wildner 		UBT_STAT_IERROR(sc);
1315d2902f79SSascha Wildner 
1316d2902f79SSascha Wildner 	return (error);
1317d2902f79SSascha Wildner } /* ubt_fwd_mbuf_up */
1318d2902f79SSascha Wildner 
1319d2902f79SSascha Wildner /****************************************************************************
1320d2902f79SSascha Wildner  ****************************************************************************
1321d2902f79SSascha Wildner  **                                 Glue
1322d2902f79SSascha Wildner  ****************************************************************************
1323d2902f79SSascha Wildner  ****************************************************************************/
1324d2902f79SSascha Wildner 
1325d2902f79SSascha Wildner /*
1326d2902f79SSascha Wildner  * Schedule glue task. Should be called with sc_ng_mtx held.
1327d2902f79SSascha Wildner  * Netgraph context.
1328b06ebda0SMatthew Dillon  */
1329b06ebda0SMatthew Dillon 
1330b06ebda0SMatthew Dillon static void
ubt_task_schedule(ubt_softc_p sc,int action)1331d2902f79SSascha Wildner ubt_task_schedule(ubt_softc_p sc, int action)
1332b06ebda0SMatthew Dillon {
1333d2902f79SSascha Wildner 	KKASSERT(lockowned(&sc->sc_ng_lock) != 0);
1334b06ebda0SMatthew Dillon 
1335b06ebda0SMatthew Dillon 	/*
1336d2902f79SSascha Wildner 	 * Try to handle corner case when "start all" and "stop all"
1337d2902f79SSascha Wildner 	 * actions can both be set before task is executed.
1338d2902f79SSascha Wildner 	 *
1339d2902f79SSascha Wildner 	 * The rules are
1340d2902f79SSascha Wildner 	 *
1341d2902f79SSascha Wildner 	 * sc_task_flags	action		new sc_task_flags
1342d2902f79SSascha Wildner 	 * ------------------------------------------------------
1343d2902f79SSascha Wildner 	 * 0			start		start
1344d2902f79SSascha Wildner 	 * 0			stop		stop
1345d2902f79SSascha Wildner 	 * start		start		start
1346d2902f79SSascha Wildner 	 * start		stop		stop
1347d2902f79SSascha Wildner 	 * stop			start		stop|start
1348d2902f79SSascha Wildner 	 * stop			stop		stop
1349d2902f79SSascha Wildner 	 * stop|start		start		stop|start
1350d2902f79SSascha Wildner 	 * stop|start		stop		stop
1351d2902f79SSascha Wildner 	 */
1352d2902f79SSascha Wildner 
1353d2902f79SSascha Wildner 	if (action != 0) {
1354d2902f79SSascha Wildner 		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
1355d2902f79SSascha Wildner 			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
1356d2902f79SSascha Wildner 
1357d2902f79SSascha Wildner 		sc->sc_task_flags |= action;
1358d2902f79SSascha Wildner 	}
1359d2902f79SSascha Wildner 
1360d2902f79SSascha Wildner 	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
1361d2902f79SSascha Wildner 		return;
1362d2902f79SSascha Wildner 
1363d2902f79SSascha Wildner 	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
1364d2902f79SSascha Wildner 		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
1365d2902f79SSascha Wildner 		return;
1366d2902f79SSascha Wildner 	}
1367d2902f79SSascha Wildner 
1368d2902f79SSascha Wildner 	/* XXX: i think this should never happen */
1369d2902f79SSascha Wildner } /* ubt_task_schedule */
1370d2902f79SSascha Wildner 
1371d2902f79SSascha Wildner /*
1372d2902f79SSascha Wildner  * Glue task. Examines sc_task_flags and does things depending on it.
1373d2902f79SSascha Wildner  * Taskqueue context.
1374b06ebda0SMatthew Dillon  */
1375b06ebda0SMatthew Dillon 
1376b06ebda0SMatthew Dillon static void
ubt_task(void * context,int pending)1377d2902f79SSascha Wildner ubt_task(void *context, int pending)
1378b06ebda0SMatthew Dillon {
1379d2902f79SSascha Wildner 	ubt_softc_p	sc = context;
1380d2902f79SSascha Wildner 	int		task_flags, i;
1381b06ebda0SMatthew Dillon 
1382d2902f79SSascha Wildner 	UBT_NG_LOCK(sc);
1383d2902f79SSascha Wildner 	task_flags = sc->sc_task_flags;
1384d2902f79SSascha Wildner 	sc->sc_task_flags = 0;
1385d2902f79SSascha Wildner 	UBT_NG_UNLOCK(sc);
1386b06ebda0SMatthew Dillon 
1387d2902f79SSascha Wildner 	/*
1388d2902f79SSascha Wildner 	 * Stop all USB transfers synchronously.
1389d2902f79SSascha Wildner 	 * Stop interface #0 and #1 transfers at the same time and in the
1390d2902f79SSascha Wildner 	 * same loop. usbd_transfer_drain() will do appropriate locking.
1391d2902f79SSascha Wildner 	 */
1392b06ebda0SMatthew Dillon 
1393d2902f79SSascha Wildner 	if (task_flags & UBT_FLAG_T_STOP_ALL)
1394d2902f79SSascha Wildner 		for (i = 0; i < UBT_N_TRANSFER; i ++)
1395d2902f79SSascha Wildner 			usbd_transfer_drain(sc->sc_xfer[i]);
1396d2902f79SSascha Wildner 
1397d2902f79SSascha Wildner 	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
1398d2902f79SSascha Wildner 	if (task_flags & UBT_FLAG_T_START_ALL) {
1399d2902f79SSascha Wildner 		/*
1400d2902f79SSascha Wildner 		 * Interface #0
1401d2902f79SSascha Wildner 		 */
1402d2902f79SSascha Wildner 
1403d2902f79SSascha Wildner 		lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
1404d2902f79SSascha Wildner 
1405d2902f79SSascha Wildner 		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
1406d2902f79SSascha Wildner 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
1407d2902f79SSascha Wildner 
1408d2902f79SSascha Wildner 		/*
1409d2902f79SSascha Wildner 		 * Interface #1
1410d2902f79SSascha Wildner 		 * Start both read and write isoc. transfers by default.
1411d2902f79SSascha Wildner 		 * Get them going all the time even if we have nothing
1412d2902f79SSascha Wildner 		 * to send to avoid any delays.
1413d2902f79SSascha Wildner 		 */
1414d2902f79SSascha Wildner 
1415d2902f79SSascha Wildner 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
1416d2902f79SSascha Wildner 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
1417d2902f79SSascha Wildner 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
1418d2902f79SSascha Wildner 		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
1419d2902f79SSascha Wildner 
1420d2902f79SSascha Wildner 		lockmgr(&sc->sc_if_lock, LK_RELEASE);
1421d2902f79SSascha Wildner 	}
1422d2902f79SSascha Wildner 
1423d2902f79SSascha Wildner  	/* Start outgoing control transfer */
1424d2902f79SSascha Wildner 	if (task_flags & UBT_FLAG_T_START_CTRL) {
1425d2902f79SSascha Wildner 		lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
1426d2902f79SSascha Wildner 		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
1427d2902f79SSascha Wildner 		lockmgr(&sc->sc_if_lock, LK_RELEASE);
1428d2902f79SSascha Wildner 	}
1429d2902f79SSascha Wildner 
1430d2902f79SSascha Wildner 	/* Start outgoing bulk transfer */
1431d2902f79SSascha Wildner 	if (task_flags & UBT_FLAG_T_START_BULK) {
1432d2902f79SSascha Wildner 		lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
1433d2902f79SSascha Wildner 		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
1434d2902f79SSascha Wildner 		lockmgr(&sc->sc_if_lock, LK_RELEASE);
1435d2902f79SSascha Wildner 	}
1436d2902f79SSascha Wildner } /* ubt_task */
1437b06ebda0SMatthew Dillon 
1438b06ebda0SMatthew Dillon /****************************************************************************
1439b06ebda0SMatthew Dillon  ****************************************************************************
1440b06ebda0SMatthew Dillon  **                        Netgraph specific
1441b06ebda0SMatthew Dillon  ****************************************************************************
1442b06ebda0SMatthew Dillon  ****************************************************************************/
1443b06ebda0SMatthew Dillon 
1444b06ebda0SMatthew Dillon /*
1445b06ebda0SMatthew Dillon  * Netgraph node constructor. Do not allow to create node of this type.
1446d2902f79SSascha Wildner  * Netgraph context.
1447b06ebda0SMatthew Dillon  */
1448b06ebda0SMatthew Dillon 
1449b06ebda0SMatthew Dillon static int
ng_ubt_constructor(node_p node)1450b06ebda0SMatthew Dillon ng_ubt_constructor(node_p node)
1451b06ebda0SMatthew Dillon {
1452b06ebda0SMatthew Dillon 	return (EINVAL);
1453b06ebda0SMatthew Dillon } /* ng_ubt_constructor */
1454b06ebda0SMatthew Dillon 
1455b06ebda0SMatthew Dillon /*
1456d2902f79SSascha Wildner  * Netgraph node destructor. Destroy node only when device has been detached.
1457d2902f79SSascha Wildner  * Netgraph context.
1458b06ebda0SMatthew Dillon  */
1459b06ebda0SMatthew Dillon 
1460b06ebda0SMatthew Dillon static int
ng_ubt_shutdown(node_p node)1461b06ebda0SMatthew Dillon ng_ubt_shutdown(node_p node)
1462b06ebda0SMatthew Dillon {
1463d2902f79SSascha Wildner 	if (node->nd_flags & NGF_REALLY_DIE) {
1464d2902f79SSascha Wildner 		/*
1465d2902f79SSascha Wildner                  * We came here because the USB device is being
1466d2902f79SSascha Wildner 		 * detached, so stop being persistant.
1467d2902f79SSascha Wildner                  */
1468b06ebda0SMatthew Dillon 		NG_NODE_SET_PRIVATE(node, NULL);
1469b06ebda0SMatthew Dillon 		NG_NODE_UNREF(node);
1470d2902f79SSascha Wildner 	} else
1471d2902f79SSascha Wildner 		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
1472b06ebda0SMatthew Dillon 
1473b06ebda0SMatthew Dillon 	return (0);
1474b06ebda0SMatthew Dillon } /* ng_ubt_shutdown */
1475b06ebda0SMatthew Dillon 
1476b06ebda0SMatthew Dillon /*
1477b06ebda0SMatthew Dillon  * Create new hook. There can only be one.
1478d2902f79SSascha Wildner  * Netgraph context.
1479b06ebda0SMatthew Dillon  */
1480b06ebda0SMatthew Dillon 
1481b06ebda0SMatthew Dillon static int
ng_ubt_newhook(node_p node,hook_p hook,char const * name)1482b06ebda0SMatthew Dillon ng_ubt_newhook(node_p node, hook_p hook, char const *name)
1483b06ebda0SMatthew Dillon {
1484d2902f79SSascha Wildner 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1485b06ebda0SMatthew Dillon 
1486b06ebda0SMatthew Dillon 	if (strcmp(name, NG_UBT_HOOK) != 0)
1487b06ebda0SMatthew Dillon 		return (EINVAL);
1488b06ebda0SMatthew Dillon 
1489d2902f79SSascha Wildner 	UBT_NG_LOCK(sc);
1490d2902f79SSascha Wildner 	if (sc->sc_hook != NULL) {
1491d2902f79SSascha Wildner 		UBT_NG_UNLOCK(sc);
1492d2902f79SSascha Wildner 
1493b06ebda0SMatthew Dillon 		return (EISCONN);
1494d2902f79SSascha Wildner 	}
1495b06ebda0SMatthew Dillon 
1496b06ebda0SMatthew Dillon 	sc->sc_hook = hook;
1497d2902f79SSascha Wildner 	UBT_NG_UNLOCK(sc);
1498b06ebda0SMatthew Dillon 
1499b06ebda0SMatthew Dillon 	return (0);
1500b06ebda0SMatthew Dillon } /* ng_ubt_newhook */
1501b06ebda0SMatthew Dillon 
1502b06ebda0SMatthew Dillon /*
1503d2902f79SSascha Wildner  * Connect hook. Start incoming USB transfers.
1504d2902f79SSascha Wildner  * Netgraph context.
1505b06ebda0SMatthew Dillon  */
1506b06ebda0SMatthew Dillon 
1507b06ebda0SMatthew Dillon static int
ng_ubt_connect(hook_p hook)1508b06ebda0SMatthew Dillon ng_ubt_connect(hook_p hook)
1509b06ebda0SMatthew Dillon {
1510d2902f79SSascha Wildner 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1511b06ebda0SMatthew Dillon 
1512b06ebda0SMatthew Dillon 	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
1513b06ebda0SMatthew Dillon 
1514d2902f79SSascha Wildner 	UBT_NG_LOCK(sc);
1515d2902f79SSascha Wildner 	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
1516d2902f79SSascha Wildner 	UBT_NG_UNLOCK(sc);
1517b06ebda0SMatthew Dillon 
1518b06ebda0SMatthew Dillon 	return (0);
1519b06ebda0SMatthew Dillon } /* ng_ubt_connect */
1520b06ebda0SMatthew Dillon 
1521b06ebda0SMatthew Dillon /*
1522d2902f79SSascha Wildner  * Disconnect hook.
1523d2902f79SSascha Wildner  * Netgraph context.
1524b06ebda0SMatthew Dillon  */
1525b06ebda0SMatthew Dillon 
1526b06ebda0SMatthew Dillon static int
ng_ubt_disconnect(hook_p hook)1527b06ebda0SMatthew Dillon ng_ubt_disconnect(hook_p hook)
1528b06ebda0SMatthew Dillon {
1529d2902f79SSascha Wildner 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1530b06ebda0SMatthew Dillon 
1531d2902f79SSascha Wildner 	UBT_NG_LOCK(sc);
1532d2902f79SSascha Wildner 
1533d2902f79SSascha Wildner 	if (hook != sc->sc_hook) {
1534d2902f79SSascha Wildner 		UBT_NG_UNLOCK(sc);
1535d2902f79SSascha Wildner 
1536b06ebda0SMatthew Dillon 		return (EINVAL);
1537b06ebda0SMatthew Dillon 	}
1538b06ebda0SMatthew Dillon 
1539d2902f79SSascha Wildner 	sc->sc_hook = NULL;
1540d2902f79SSascha Wildner 
1541d2902f79SSascha Wildner 	/* Kick off task to stop all USB xfers */
1542d2902f79SSascha Wildner 	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
1543d2902f79SSascha Wildner 
1544d2902f79SSascha Wildner 	/* Drain queues */
1545d2902f79SSascha Wildner 	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
1546d2902f79SSascha Wildner 	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
1547d2902f79SSascha Wildner 	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
1548d2902f79SSascha Wildner 
1549d2902f79SSascha Wildner 	UBT_NG_UNLOCK(sc);
1550d2902f79SSascha Wildner 
1551b06ebda0SMatthew Dillon 	return (0);
1552b06ebda0SMatthew Dillon } /* ng_ubt_disconnect */
1553b06ebda0SMatthew Dillon 
1554b06ebda0SMatthew Dillon /*
1555d2902f79SSascha Wildner  * Process control message.
1556d2902f79SSascha Wildner  * Netgraph context.
1557b06ebda0SMatthew Dillon  */
1558b06ebda0SMatthew Dillon 
1559b06ebda0SMatthew Dillon static int
ng_ubt_rcvmsg(node_p node,item_p item,hook_p lasthook)1560b06ebda0SMatthew Dillon ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
1561b06ebda0SMatthew Dillon {
1562d2902f79SSascha Wildner 	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1563d2902f79SSascha Wildner 	struct ng_mesg		*msg, *rsp = NULL;
1564d2902f79SSascha Wildner 	struct ng_bt_mbufq	*q;
1565b06ebda0SMatthew Dillon 	int			error = 0, queue, qlen;
1566b06ebda0SMatthew Dillon 
1567b06ebda0SMatthew Dillon 	NGI_GET_MSG(item, msg);
1568b06ebda0SMatthew Dillon 
1569b06ebda0SMatthew Dillon 	switch (msg->header.typecookie) {
1570b06ebda0SMatthew Dillon 	case NGM_GENERIC_COOKIE:
1571b06ebda0SMatthew Dillon 		switch (msg->header.cmd) {
1572b06ebda0SMatthew Dillon 		case NGM_TEXT_STATUS:
15735a975a3dSMatthew Dillon 			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_WAITOK | M_NULLOK);
1574d2902f79SSascha Wildner 			if (rsp == NULL) {
1575b06ebda0SMatthew Dillon 				error = ENOMEM;
1576d2902f79SSascha Wildner 				break;
1577d2902f79SSascha Wildner 			}
1578d2902f79SSascha Wildner 
1579a62226e4SSascha Wildner 			ksnprintf(rsp->data, NG_TEXTRESPONSE,
1580b06ebda0SMatthew Dillon 				"Hook: %s\n" \
1581d2902f79SSascha Wildner 				"Task flags: %#x\n" \
1582b06ebda0SMatthew Dillon 				"Debug: %d\n" \
1583b06ebda0SMatthew Dillon 				"CMD queue: [have:%d,max:%d]\n" \
1584b06ebda0SMatthew Dillon 				"ACL queue: [have:%d,max:%d]\n" \
1585b06ebda0SMatthew Dillon 				"SCO queue: [have:%d,max:%d]",
1586b06ebda0SMatthew Dillon 				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
1587d2902f79SSascha Wildner 				sc->sc_task_flags,
1588b06ebda0SMatthew Dillon 				sc->sc_debug,
1589d2902f79SSascha Wildner 				sc->sc_cmdq.len,
1590b06ebda0SMatthew Dillon 				sc->sc_cmdq.maxlen,
1591d2902f79SSascha Wildner 				sc->sc_aclq.len,
1592b06ebda0SMatthew Dillon 				sc->sc_aclq.maxlen,
1593d2902f79SSascha Wildner 				sc->sc_scoq.len,
1594b06ebda0SMatthew Dillon 				sc->sc_scoq.maxlen);
1595b06ebda0SMatthew Dillon 			break;
1596b06ebda0SMatthew Dillon 
1597b06ebda0SMatthew Dillon 		default:
1598b06ebda0SMatthew Dillon 			error = EINVAL;
1599b06ebda0SMatthew Dillon 			break;
1600b06ebda0SMatthew Dillon 		}
1601b06ebda0SMatthew Dillon 		break;
1602b06ebda0SMatthew Dillon 
1603b06ebda0SMatthew Dillon 	case NGM_UBT_COOKIE:
1604b06ebda0SMatthew Dillon 		switch (msg->header.cmd) {
1605b06ebda0SMatthew Dillon 		case NGM_UBT_NODE_SET_DEBUG:
1606d2902f79SSascha Wildner 			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
1607b06ebda0SMatthew Dillon 				error = EMSGSIZE;
1608d2902f79SSascha Wildner 				break;
1609d2902f79SSascha Wildner 			}
1610d2902f79SSascha Wildner 
1611d2902f79SSascha Wildner 			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
1612b06ebda0SMatthew Dillon 			break;
1613b06ebda0SMatthew Dillon 
1614b06ebda0SMatthew Dillon 		case NGM_UBT_NODE_GET_DEBUG:
1615b06ebda0SMatthew Dillon 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
16165a975a3dSMatthew Dillon 			    M_WAITOK | M_NULLOK);
1617d2902f79SSascha Wildner 			if (rsp == NULL) {
1618b06ebda0SMatthew Dillon 				error = ENOMEM;
1619d2902f79SSascha Wildner 				break;
1620d2902f79SSascha Wildner 			}
1621d2902f79SSascha Wildner 
1622d2902f79SSascha Wildner 			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
1623b06ebda0SMatthew Dillon 			break;
1624b06ebda0SMatthew Dillon 
1625b06ebda0SMatthew Dillon 		case NGM_UBT_NODE_SET_QLEN:
1626d2902f79SSascha Wildner 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1627b06ebda0SMatthew Dillon 				error = EMSGSIZE;
1628b06ebda0SMatthew Dillon 				break;
1629b06ebda0SMatthew Dillon 			}
1630b06ebda0SMatthew Dillon 
1631d2902f79SSascha Wildner 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1632d2902f79SSascha Wildner 			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
1633d2902f79SSascha Wildner 
1634b06ebda0SMatthew Dillon 			switch (queue) {
1635b06ebda0SMatthew Dillon 			case NGM_UBT_NODE_QUEUE_CMD:
1636b06ebda0SMatthew Dillon 				q = &sc->sc_cmdq;
1637b06ebda0SMatthew Dillon 				break;
1638b06ebda0SMatthew Dillon 
1639b06ebda0SMatthew Dillon 			case NGM_UBT_NODE_QUEUE_ACL:
1640b06ebda0SMatthew Dillon 				q = &sc->sc_aclq;
1641b06ebda0SMatthew Dillon 				break;
1642b06ebda0SMatthew Dillon 
1643b06ebda0SMatthew Dillon 			case NGM_UBT_NODE_QUEUE_SCO:
1644b06ebda0SMatthew Dillon 				q = &sc->sc_scoq;
1645b06ebda0SMatthew Dillon 				break;
1646b06ebda0SMatthew Dillon 
1647b06ebda0SMatthew Dillon 			default:
1648b06ebda0SMatthew Dillon 				error = EINVAL;
1649d2902f79SSascha Wildner 				goto done;
1650d2902f79SSascha Wildner 				/* NOT REACHED */
1651b06ebda0SMatthew Dillon 			}
1652b06ebda0SMatthew Dillon 
1653b06ebda0SMatthew Dillon 			q->maxlen = qlen;
1654b06ebda0SMatthew Dillon 			break;
1655b06ebda0SMatthew Dillon 
1656b06ebda0SMatthew Dillon 		case NGM_UBT_NODE_GET_QLEN:
1657b06ebda0SMatthew Dillon 			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1658b06ebda0SMatthew Dillon 				error = EMSGSIZE;
1659b06ebda0SMatthew Dillon 				break;
1660b06ebda0SMatthew Dillon 			}
1661b06ebda0SMatthew Dillon 
1662b06ebda0SMatthew Dillon 			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1663d2902f79SSascha Wildner 
1664b06ebda0SMatthew Dillon 			switch (queue) {
1665b06ebda0SMatthew Dillon 			case NGM_UBT_NODE_QUEUE_CMD:
1666b06ebda0SMatthew Dillon 				q = &sc->sc_cmdq;
1667b06ebda0SMatthew Dillon 				break;
1668b06ebda0SMatthew Dillon 
1669b06ebda0SMatthew Dillon 			case NGM_UBT_NODE_QUEUE_ACL:
1670b06ebda0SMatthew Dillon 				q = &sc->sc_aclq;
1671b06ebda0SMatthew Dillon 				break;
1672b06ebda0SMatthew Dillon 
1673b06ebda0SMatthew Dillon 			case NGM_UBT_NODE_QUEUE_SCO:
1674b06ebda0SMatthew Dillon 				q = &sc->sc_scoq;
1675b06ebda0SMatthew Dillon 				break;
1676b06ebda0SMatthew Dillon 
1677b06ebda0SMatthew Dillon 			default:
1678b06ebda0SMatthew Dillon 				error = EINVAL;
1679d2902f79SSascha Wildner 				goto done;
1680d2902f79SSascha Wildner 				/* NOT REACHED */
1681b06ebda0SMatthew Dillon 			}
1682b06ebda0SMatthew Dillon 
1683d2902f79SSascha Wildner 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
1684d2902f79SSascha Wildner 				M_WAITOK | M_NULLOK);
1685b06ebda0SMatthew Dillon 			if (rsp == NULL) {
1686b06ebda0SMatthew Dillon 				error = ENOMEM;
1687b06ebda0SMatthew Dillon 				break;
1688b06ebda0SMatthew Dillon 			}
1689b06ebda0SMatthew Dillon 
1690d2902f79SSascha Wildner 			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
1691d2902f79SSascha Wildner 			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
1692b06ebda0SMatthew Dillon 			break;
1693b06ebda0SMatthew Dillon 
1694b06ebda0SMatthew Dillon 		case NGM_UBT_NODE_GET_STAT:
1695b06ebda0SMatthew Dillon 			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
16965a975a3dSMatthew Dillon 			    M_WAITOK | M_NULLOK);
1697d2902f79SSascha Wildner 			if (rsp == NULL) {
1698b06ebda0SMatthew Dillon 				error = ENOMEM;
1699d2902f79SSascha Wildner 				break;
1700d2902f79SSascha Wildner 			}
1701d2902f79SSascha Wildner 
1702b06ebda0SMatthew Dillon 			bcopy(&sc->sc_stat, rsp->data,
1703b06ebda0SMatthew Dillon 				sizeof(ng_ubt_node_stat_ep));
1704b06ebda0SMatthew Dillon 			break;
1705b06ebda0SMatthew Dillon 
1706b06ebda0SMatthew Dillon 		case NGM_UBT_NODE_RESET_STAT:
1707d2902f79SSascha Wildner 			UBT_STAT_RESET(sc);
1708b06ebda0SMatthew Dillon 			break;
1709b06ebda0SMatthew Dillon 
1710b06ebda0SMatthew Dillon 		default:
1711b06ebda0SMatthew Dillon 			error = EINVAL;
1712b06ebda0SMatthew Dillon 			break;
1713b06ebda0SMatthew Dillon 		}
1714b06ebda0SMatthew Dillon 		break;
1715b06ebda0SMatthew Dillon 
1716b06ebda0SMatthew Dillon 	default:
1717b06ebda0SMatthew Dillon 		error = EINVAL;
1718b06ebda0SMatthew Dillon 		break;
1719b06ebda0SMatthew Dillon 	}
1720d2902f79SSascha Wildner done:
1721b06ebda0SMatthew Dillon 	NG_RESPOND_MSG(error, node, item, rsp);
1722b06ebda0SMatthew Dillon 	NG_FREE_MSG(msg);
1723b06ebda0SMatthew Dillon 
1724b06ebda0SMatthew Dillon 	return (error);
1725b06ebda0SMatthew Dillon } /* ng_ubt_rcvmsg */
1726b06ebda0SMatthew Dillon 
1727b06ebda0SMatthew Dillon /*
1728d2902f79SSascha Wildner  * Process data.
1729d2902f79SSascha Wildner  * Netgraph context.
1730b06ebda0SMatthew Dillon  */
1731b06ebda0SMatthew Dillon 
1732b06ebda0SMatthew Dillon static int
ng_ubt_rcvdata(hook_p hook,item_p item)1733b06ebda0SMatthew Dillon ng_ubt_rcvdata(hook_p hook, item_p item)
1734b06ebda0SMatthew Dillon {
1735d2902f79SSascha Wildner 	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1736d2902f79SSascha Wildner 	struct mbuf		*m;
1737d2902f79SSascha Wildner 	struct ng_bt_mbufq	*q;
1738d2902f79SSascha Wildner 	int			action, error = 0;
1739b06ebda0SMatthew Dillon 
1740b06ebda0SMatthew Dillon 	if (hook != sc->sc_hook) {
1741b06ebda0SMatthew Dillon 		error = EINVAL;
1742b06ebda0SMatthew Dillon 		goto done;
1743b06ebda0SMatthew Dillon 	}
1744b06ebda0SMatthew Dillon 
1745b06ebda0SMatthew Dillon 	/* Deatch mbuf and get HCI frame type */
1746b06ebda0SMatthew Dillon 	NGI_GET_M(item, m);
1747b06ebda0SMatthew Dillon 
1748d2902f79SSascha Wildner 	/*
1749d2902f79SSascha Wildner 	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
1750d2902f79SSascha Wildner 	 * 2 bytes connection handle and at least 1 byte of length.
1751d2902f79SSascha Wildner 	 * Panic on data frame that has size smaller than 4 bytes (it
1752d2902f79SSascha Wildner 	 * should not happen)
1753d2902f79SSascha Wildner 	 */
1754d2902f79SSascha Wildner 
1755d2902f79SSascha Wildner 	if (m->m_pkthdr.len < 4)
1756d2902f79SSascha Wildner 		panic("HCI frame size is too small! pktlen=%d\n",
1757d2902f79SSascha Wildner 			m->m_pkthdr.len);
1758d2902f79SSascha Wildner 
1759b06ebda0SMatthew Dillon 	/* Process HCI frame */
1760d2902f79SSascha Wildner 	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
1761b06ebda0SMatthew Dillon 	case NG_HCI_CMD_PKT:
1762d2902f79SSascha Wildner 		if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
1763d2902f79SSascha Wildner 			panic("HCI command frame size is too big! " \
1764d2902f79SSascha Wildner 				"buffer size=%zd, packet len=%d\n",
1765d2902f79SSascha Wildner 				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
1766d2902f79SSascha Wildner 
1767b06ebda0SMatthew Dillon 		q = &sc->sc_cmdq;
1768d2902f79SSascha Wildner 		action = UBT_FLAG_T_START_CTRL;
1769b06ebda0SMatthew Dillon 		break;
1770b06ebda0SMatthew Dillon 
1771b06ebda0SMatthew Dillon 	case NG_HCI_ACL_DATA_PKT:
1772d2902f79SSascha Wildner 		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
1773d2902f79SSascha Wildner 			panic("ACL data frame size is too big! " \
1774d2902f79SSascha Wildner 				"buffer size=%d, packet len=%d\n",
1775d2902f79SSascha Wildner 				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
1776d2902f79SSascha Wildner 
1777b06ebda0SMatthew Dillon 		q = &sc->sc_aclq;
1778d2902f79SSascha Wildner 		action = UBT_FLAG_T_START_BULK;
1779b06ebda0SMatthew Dillon 		break;
1780b06ebda0SMatthew Dillon 
1781b06ebda0SMatthew Dillon 	case NG_HCI_SCO_DATA_PKT:
1782b06ebda0SMatthew Dillon 		q = &sc->sc_scoq;
1783d2902f79SSascha Wildner 		action = 0;
1784b06ebda0SMatthew Dillon 		break;
1785b06ebda0SMatthew Dillon 
1786b06ebda0SMatthew Dillon 	default:
1787d2902f79SSascha Wildner 		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
1788d2902f79SSascha Wildner 			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
1789b06ebda0SMatthew Dillon 
1790b06ebda0SMatthew Dillon 		NG_FREE_M(m);
1791b06ebda0SMatthew Dillon 		error = EINVAL;
1792b06ebda0SMatthew Dillon 		goto done;
1793b06ebda0SMatthew Dillon 		/* NOT REACHED */
1794b06ebda0SMatthew Dillon 	}
1795b06ebda0SMatthew Dillon 
1796d2902f79SSascha Wildner 	UBT_NG_LOCK(sc);
1797b06ebda0SMatthew Dillon 	if (NG_BT_MBUFQ_FULL(q)) {
1798d2902f79SSascha Wildner 		NG_BT_MBUFQ_DROP(q);
1799d2902f79SSascha Wildner 		UBT_NG_UNLOCK(sc);
1800d2902f79SSascha Wildner 
1801d2902f79SSascha Wildner 		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
1802d2902f79SSascha Wildner 			*mtod(m, uint8_t *), m->m_pkthdr.len);
1803b06ebda0SMatthew Dillon 
1804b06ebda0SMatthew Dillon 		NG_FREE_M(m);
1805d2902f79SSascha Wildner 	} else {
1806d2902f79SSascha Wildner 		/* Loose HCI packet type, enqueue mbuf and kick off task */
1807d2902f79SSascha Wildner 		m_adj(m, sizeof(uint8_t));
1808b06ebda0SMatthew Dillon 		NG_BT_MBUFQ_ENQUEUE(q, m);
1809d2902f79SSascha Wildner 		ubt_task_schedule(sc, action);
1810d2902f79SSascha Wildner 		UBT_NG_UNLOCK(sc);
1811d2902f79SSascha Wildner 	}
1812b06ebda0SMatthew Dillon done:
1813b06ebda0SMatthew Dillon 	NG_FREE_ITEM(item);
1814b06ebda0SMatthew Dillon 
1815b06ebda0SMatthew Dillon 	return (error);
1816b06ebda0SMatthew Dillon } /* ng_ubt_rcvdata */
1817b06ebda0SMatthew Dillon 
1818d2902f79SSascha Wildner /****************************************************************************
1819d2902f79SSascha Wildner  ****************************************************************************
1820d2902f79SSascha Wildner  **                              Module
1821d2902f79SSascha Wildner  ****************************************************************************
1822d2902f79SSascha Wildner  ****************************************************************************/
1823d2902f79SSascha Wildner 
1824d2902f79SSascha Wildner /*
1825d2902f79SSascha Wildner  * Load/Unload the driver module
1826d2902f79SSascha Wildner  */
1827d2902f79SSascha Wildner 
1828d2902f79SSascha Wildner static int
ubt_modevent(module_t mod,int event,void * data)1829d2902f79SSascha Wildner ubt_modevent(module_t mod, int event, void *data)
1830d2902f79SSascha Wildner {
1831d2902f79SSascha Wildner 	int	error;
1832d2902f79SSascha Wildner 
1833d2902f79SSascha Wildner 	switch (event) {
1834d2902f79SSascha Wildner 	case MOD_LOAD:
1835d2902f79SSascha Wildner 		error = ng_newtype(&typestruct);
1836d2902f79SSascha Wildner 		if (error != 0)
1837a62226e4SSascha Wildner 			kprintf("%s: Could not register Netgraph node type, " \
1838d2902f79SSascha Wildner 				"error=%d\n", NG_UBT_NODE_TYPE, error);
1839d2902f79SSascha Wildner 		break;
1840d2902f79SSascha Wildner 
1841d2902f79SSascha Wildner 	case MOD_UNLOAD:
1842d2902f79SSascha Wildner 		error = ng_rmtype(&typestruct);
1843d2902f79SSascha Wildner 		break;
1844d2902f79SSascha Wildner 
1845d2902f79SSascha Wildner 	default:
1846d2902f79SSascha Wildner 		error = EOPNOTSUPP;
1847d2902f79SSascha Wildner 		break;
1848d2902f79SSascha Wildner 	}
1849d2902f79SSascha Wildner 
1850d2902f79SSascha Wildner 	return (error);
1851d2902f79SSascha Wildner } /* ubt_modevent */
1852d2902f79SSascha Wildner 
1853d2902f79SSascha Wildner static devclass_t	ubt_devclass;
1854d2902f79SSascha Wildner 
1855d2902f79SSascha Wildner static device_method_t	ubt_methods[] =
1856d2902f79SSascha Wildner {
1857d2902f79SSascha Wildner 	DEVMETHOD(device_probe,	ubt_probe),
1858d2902f79SSascha Wildner 	DEVMETHOD(device_attach, ubt_attach),
1859d2902f79SSascha Wildner 	DEVMETHOD(device_detach, ubt_detach),
1860d2902f79SSascha Wildner 
1861d2902f79SSascha Wildner 	DEVMETHOD_END
1862d2902f79SSascha Wildner };
1863d2902f79SSascha Wildner 
1864d2902f79SSascha Wildner static driver_t		ubt_driver =
1865d2902f79SSascha Wildner {
1866d2902f79SSascha Wildner 	.name =	   "ubt",
1867d2902f79SSascha Wildner 	.methods = ubt_methods,
1868d2902f79SSascha Wildner 	.size =	   sizeof(struct ubt_softc),
1869d2902f79SSascha Wildner };
1870d2902f79SSascha Wildner 
1871d2902f79SSascha Wildner DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, NULL);
1872d2902f79SSascha Wildner MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
1873d2902f79SSascha Wildner MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
1874d2902f79SSascha Wildner MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
1875d2902f79SSascha Wildner MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
1876d2902f79SSascha Wildner 
1877