1184610Salfred/*
2184610Salfred * ng_ubt.c
3184610Salfred */
4184610Salfred
5184610Salfred/*-
6187494Semax * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7184610Salfred * All rights reserved.
8184610Salfred *
9184610Salfred * Redistribution and use in source and binary forms, with or without
10184610Salfred * modification, are permitted provided that the following conditions
11184610Salfred * are met:
12184610Salfred * 1. Redistributions of source code must retain the above copyright
13184610Salfred *    notice, this list of conditions and the following disclaimer.
14184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
15184610Salfred *    notice, this list of conditions and the following disclaimer in the
16184610Salfred *    documentation and/or other materials provided with the distribution.
17184610Salfred *
18184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28184610Salfred * SUCH DAMAGE.
29184610Salfred *
30184610Salfred * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
31184610Salfred * $FreeBSD$
32184610Salfred */
33184610Salfred
34187494Semax/*
35187494Semax * NOTE: ng_ubt2 driver has a split personality. On one side it is
36187741Semax * a USB device driver and on the other it is a Netgraph node. This
37187494Semax * driver will *NOT* create traditional /dev/ enties, only Netgraph
38187494Semax * node.
39187494Semax *
40187741Semax * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
41187494Semax *
42187741Semax * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
43187741Semax *    by USB for any USB request going over device's interface #0 and #1,
44187741Semax *    i.e. interrupt, control, bulk and isoc. transfers.
45187494Semax *
46187741Semax * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
47187741Semax *    and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
48187741Semax *    pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
49187741Semax *    think of it as a spin lock.
50187494Semax *
51187494Semax * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
52187494Semax *
53187494Semax * 1) USB context. This is where all the USB related stuff happens. All
54187741Semax *    callbacks run in this context. All callbacks are called (by USB) with
55187494Semax *    appropriate interface lock held. It is (generally) allowed to grab
56187494Semax *    any additional locks.
57187494Semax *
58187494Semax * 2) Netgraph context. This is where all the Netgraph related stuff happens.
59187494Semax *    Since we mark node as WRITER, the Netgraph node will be "locked" (from
60187494Semax *    Netgraph point of view). Any variable that is only modified from the
61187494Semax *    Netgraph context does not require any additonal locking. It is generally
62187741Semax *    *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
63187741Semax *    grab any lock in the Netgraph context that could cause de-scheduling of
64187741Semax *    the Netgraph thread for significant amount of time. In fact, the only
65187741Semax *    lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
66187741Semax *    Also make sure that any code that is called from the Netgraph context
67187741Semax *    follows the rule above.
68187494Semax *
69187741Semax * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
70187741Semax *    NOT allowed to grab any lock that could cause de-scheduling in the
71187741Semax *    Netgraph context, and, USB requires us to grab interface lock before
72187741Semax *    doing things with transfers, it is safer to transition from the Netgraph
73187741Semax *    context to the Taskqueue context before we can call into USB subsystem.
74187494Semax *
75187494Semax * So, to put everything together, the rules are as follows.
76187494Semax *	It is OK to call from the USB context or the Taskqueue context into
77187494Semax * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
78187494Semax * it is allowed to call into the Netgraph context with locks held.
79187494Semax *	Is it *NOT* OK to call from the Netgraph context into the USB context,
80187741Semax * because USB requires us to grab interface locks, and, it is safer to
81187741Semax * avoid it. So, to make things safer we set task flags to indicate which
82187741Semax * actions we want to perform and schedule ubt_task which would run in the
83187741Semax * Taskqueue context.
84187494Semax *	Is is OK to call from the Taskqueue context into the USB context,
85187494Semax * and, ubt_task does just that (i.e. grabs appropriate interface locks
86187741Semax * before calling into USB).
87187741Semax *	Access to the outgoing queues, task flags and hook pointer is
88187741Semax * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
89187741Semax * sc_ng_mtx should really be a spin lock (and it is very likely to an
90189002Sed * equivalent of spin lock due to adaptive nature of FreeBSD mutexes).
91187741Semax *	All USB callbacks accept softc pointer as a private data. USB ensures
92187741Semax * that this pointer is valid.
93187494Semax */
94187494Semax
95194677Sthompsa#include <sys/stdint.h>
96194677Sthompsa#include <sys/stddef.h>
97194677Sthompsa#include <sys/param.h>
98194677Sthompsa#include <sys/queue.h>
99194677Sthompsa#include <sys/types.h>
100194677Sthompsa#include <sys/systm.h>
101194677Sthompsa#include <sys/kernel.h>
102194677Sthompsa#include <sys/bus.h>
103194677Sthompsa#include <sys/module.h>
104194677Sthompsa#include <sys/lock.h>
105194677Sthompsa#include <sys/mutex.h>
106194677Sthompsa#include <sys/condvar.h>
107194677Sthompsa#include <sys/sysctl.h>
108194677Sthompsa#include <sys/sx.h>
109194677Sthompsa#include <sys/unistd.h>
110194677Sthompsa#include <sys/callout.h>
111194677Sthompsa#include <sys/malloc.h>
112194677Sthompsa#include <sys/priv.h>
113194677Sthompsa
114188746Sthompsa#include "usbdevs.h"
115188942Sthompsa#include <dev/usb/usb.h>
116194677Sthompsa#include <dev/usb/usbdi.h>
117194677Sthompsa#include <dev/usb/usbdi_util.h>
118184610Salfred
119194228Sthompsa#define	USB_DEBUG_VAR usb_debug
120188942Sthompsa#include <dev/usb/usb_debug.h>
121188942Sthompsa#include <dev/usb/usb_busdma.h>
122184610Salfred
123184610Salfred#include <sys/mbuf.h>
124187494Semax#include <sys/taskqueue.h>
125184610Salfred
126184610Salfred#include <netgraph/ng_message.h>
127184610Salfred#include <netgraph/netgraph.h>
128184610Salfred#include <netgraph/ng_parse.h>
129184610Salfred#include <netgraph/bluetooth/include/ng_bluetooth.h>
130184610Salfred#include <netgraph/bluetooth/include/ng_hci.h>
131184610Salfred#include <netgraph/bluetooth/include/ng_ubt.h>
132192909Sthompsa#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>
133184610Salfred
134187494Semaxstatic int		ubt_modevent(module_t, int, void *);
135187494Semaxstatic device_probe_t	ubt_probe;
136187494Semaxstatic device_attach_t	ubt_attach;
137187494Semaxstatic device_detach_t	ubt_detach;
138184610Salfred
139187741Semaxstatic void		ubt_task_schedule(ubt_softc_p, int);
140187494Semaxstatic task_fn_t	ubt_task;
141184610Salfred
142194228Sthompsa#define	ubt_xfer_start(sc, i)	usbd_transfer_start((sc)->sc_xfer[(i)])
143187741Semax
144187494Semax/* Netgraph methods */
145187494Semaxstatic ng_constructor_t	ng_ubt_constructor;
146187494Semaxstatic ng_shutdown_t	ng_ubt_shutdown;
147187494Semaxstatic ng_newhook_t	ng_ubt_newhook;
148187494Semaxstatic ng_connect_t	ng_ubt_connect;
149187494Semaxstatic ng_disconnect_t	ng_ubt_disconnect;
150187494Semaxstatic ng_rcvmsg_t	ng_ubt_rcvmsg;
151187494Semaxstatic ng_rcvdata_t	ng_ubt_rcvdata;
152184610Salfred
153184610Salfred/* Queue length */
154187494Semaxstatic const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
155184610Salfred{
156187494Semax	{ "queue", &ng_parse_int32_type, },
157187494Semax	{ "qlen",  &ng_parse_int32_type, },
158187494Semax	{ NULL, }
159184610Salfred};
160187494Semaxstatic const struct ng_parse_type		ng_ubt_node_qlen_type =
161187494Semax{
162184610Salfred	&ng_parse_struct_type,
163184610Salfred	&ng_ubt_node_qlen_type_fields
164184610Salfred};
165184610Salfred
166184610Salfred/* Stat info */
167187494Semaxstatic const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
168184610Salfred{
169187494Semax	{ "pckts_recv", &ng_parse_uint32_type, },
170187494Semax	{ "bytes_recv", &ng_parse_uint32_type, },
171187494Semax	{ "pckts_sent", &ng_parse_uint32_type, },
172187494Semax	{ "bytes_sent", &ng_parse_uint32_type, },
173187494Semax	{ "oerrors",    &ng_parse_uint32_type, },
174187494Semax	{ "ierrors",    &ng_parse_uint32_type, },
175187494Semax	{ NULL, }
176184610Salfred};
177187494Semaxstatic const struct ng_parse_type		ng_ubt_node_stat_type =
178187494Semax{
179184610Salfred	&ng_parse_struct_type,
180184610Salfred	&ng_ubt_node_stat_type_fields
181184610Salfred};
182184610Salfred
183184610Salfred/* Netgraph node command list */
184187494Semaxstatic const struct ng_cmdlist			ng_ubt_cmdlist[] =
185187494Semax{
186184610Salfred	{
187184610Salfred		NGM_UBT_COOKIE,
188184610Salfred		NGM_UBT_NODE_SET_DEBUG,
189184610Salfred		"set_debug",
190184610Salfred		&ng_parse_uint16_type,
191184610Salfred		NULL
192184610Salfred	},
193184610Salfred	{
194184610Salfred		NGM_UBT_COOKIE,
195184610Salfred		NGM_UBT_NODE_GET_DEBUG,
196184610Salfred		"get_debug",
197184610Salfred		NULL,
198184610Salfred		&ng_parse_uint16_type
199184610Salfred	},
200184610Salfred	{
201184610Salfred		NGM_UBT_COOKIE,
202184610Salfred		NGM_UBT_NODE_SET_QLEN,
203184610Salfred		"set_qlen",
204184610Salfred		&ng_ubt_node_qlen_type,
205184610Salfred		NULL
206184610Salfred	},
207184610Salfred	{
208184610Salfred		NGM_UBT_COOKIE,
209184610Salfred		NGM_UBT_NODE_GET_QLEN,
210184610Salfred		"get_qlen",
211184610Salfred		&ng_ubt_node_qlen_type,
212184610Salfred		&ng_ubt_node_qlen_type
213184610Salfred	},
214184610Salfred	{
215184610Salfred		NGM_UBT_COOKIE,
216184610Salfred		NGM_UBT_NODE_GET_STAT,
217184610Salfred		"get_stat",
218184610Salfred		NULL,
219184610Salfred		&ng_ubt_node_stat_type
220184610Salfred	},
221184610Salfred	{
222184610Salfred		NGM_UBT_COOKIE,
223184610Salfred		NGM_UBT_NODE_RESET_STAT,
224184610Salfred		"reset_stat",
225184610Salfred		NULL,
226184610Salfred		NULL
227184610Salfred	},
228187494Semax	{ 0, }
229184610Salfred};
230184610Salfred
231184610Salfred/* Netgraph node type */
232187494Semaxstatic struct ng_type	typestruct =
233187494Semax{
234187494Semax	.version = 	NG_ABI_VERSION,
235187494Semax	.name =		NG_UBT_NODE_TYPE,
236187494Semax	.constructor =	ng_ubt_constructor,
237187494Semax	.rcvmsg =	ng_ubt_rcvmsg,
238187494Semax	.shutdown =	ng_ubt_shutdown,
239187494Semax	.newhook =	ng_ubt_newhook,
240187494Semax	.connect =	ng_ubt_connect,
241187494Semax	.rcvdata =	ng_ubt_rcvdata,
242187494Semax	.disconnect =	ng_ubt_disconnect,
243187494Semax	.cmdlist =	ng_ubt_cmdlist
244184610Salfred};
245184610Salfred
246187494Semax/****************************************************************************
247187494Semax ****************************************************************************
248187494Semax **                              USB specific
249187494Semax ****************************************************************************
250187494Semax ****************************************************************************/
251187494Semax
252184610Salfred/* USB methods */
253193045Sthompsastatic usb_callback_t	ubt_ctrl_write_callback;
254193045Sthompsastatic usb_callback_t	ubt_intr_read_callback;
255193045Sthompsastatic usb_callback_t	ubt_bulk_read_callback;
256193045Sthompsastatic usb_callback_t	ubt_bulk_write_callback;
257193045Sthompsastatic usb_callback_t	ubt_isoc_read_callback;
258193045Sthompsastatic usb_callback_t	ubt_isoc_write_callback;
259184610Salfred
260187741Semaxstatic int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
261192984Sthompsastatic int		ubt_isoc_read_one_frame(struct usb_xfer *, int);
262184610Salfred
263187494Semax/*
264187494Semax * USB config
265187494Semax *
266187494Semax * The following desribes usb transfers that could be submitted on USB device.
267187494Semax *
268187494Semax * Interface 0 on the USB device must present the following endpoints
269187494Semax *	1) Interrupt endpoint to receive HCI events
270187494Semax *	2) Bulk IN endpoint to receive ACL data
271187494Semax *	3) Bulk OUT endpoint to send ACL data
272187494Semax *
273187494Semax * Interface 1 on the USB device must present the following endpoints
274187494Semax *	1) Isochronous IN endpoint to receive SCO data
275187494Semax *	2) Isochronous OUT endpoint to send SCO data
276187494Semax */
277184610Salfred
278192984Sthompsastatic const struct usb_config		ubt_config[UBT_N_TRANSFER] =
279187494Semax{
280187494Semax	/*
281187494Semax	 * Interface #0
282187494Semax 	 */
283184610Salfred
284187494Semax	/* Outgoing bulk transfer - ACL packets */
285187494Semax	[UBT_IF_0_BULK_DT_WR] = {
286187494Semax		.type =		UE_BULK,
287187494Semax		.endpoint =	UE_ADDR_ANY,
288187494Semax		.direction =	UE_DIR_OUT,
289187741Semax		.if_index = 	0,
290190734Sthompsa		.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
291190734Sthompsa		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
292190734Sthompsa		.callback =	&ubt_bulk_write_callback,
293184610Salfred	},
294187494Semax	/* Incoming bulk transfer - ACL packets */
295187494Semax	[UBT_IF_0_BULK_DT_RD] = {
296187494Semax		.type =		UE_BULK,
297187494Semax		.endpoint =	UE_ADDR_ANY,
298187494Semax		.direction =	UE_DIR_IN,
299187741Semax		.if_index = 	0,
300190734Sthompsa		.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
301190734Sthompsa		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
302190734Sthompsa		.callback =	&ubt_bulk_read_callback,
303184610Salfred	},
304187494Semax	/* Incoming interrupt transfer - HCI events */
305187494Semax	[UBT_IF_0_INTR_DT_RD] = {
306187494Semax		.type =		UE_INTERRUPT,
307187494Semax		.endpoint =	UE_ADDR_ANY,
308187494Semax		.direction =	UE_DIR_IN,
309187741Semax		.if_index = 	0,
310190734Sthompsa		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
311190734Sthompsa		.bufsize =	UBT_INTR_BUFFER_SIZE,
312190734Sthompsa		.callback =	&ubt_intr_read_callback,
313184610Salfred	},
314187494Semax	/* Outgoing control transfer - HCI commands */
315187494Semax	[UBT_IF_0_CTRL_DT_WR] = {
316187494Semax		.type =		UE_CONTROL,
317187494Semax		.endpoint =	0x00,	/* control pipe */
318187494Semax		.direction =	UE_DIR_ANY,
319187741Semax		.if_index = 	0,
320190734Sthompsa		.bufsize =	UBT_CTRL_BUFFER_SIZE,
321190734Sthompsa		.callback =	&ubt_ctrl_write_callback,
322190734Sthompsa		.timeout =	5000,	/* 5 seconds */
323184610Salfred	},
324184610Salfred
325187494Semax	/*
326187494Semax	 * Interface #1
327187494Semax 	 */
328184610Salfred
329187494Semax	/* Incoming isochronous transfer #1 - SCO packets */
330187494Semax	[UBT_IF_1_ISOC_DT_RD1] = {
331187494Semax		.type =		UE_ISOCHRONOUS,
332187494Semax		.endpoint =	UE_ADDR_ANY,
333187494Semax		.direction =	UE_DIR_IN,
334187741Semax		.if_index = 	1,
335190734Sthompsa		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
336190734Sthompsa		.frames =	UBT_ISOC_NFRAMES,
337190734Sthompsa		.flags =	{ .short_xfer_ok = 1, },
338190734Sthompsa		.callback =	&ubt_isoc_read_callback,
339184610Salfred	},
340187494Semax	/* Incoming isochronous transfer #2 - SCO packets */
341187494Semax	[UBT_IF_1_ISOC_DT_RD2] = {
342187494Semax		.type =		UE_ISOCHRONOUS,
343187494Semax		.endpoint =	UE_ADDR_ANY,
344187494Semax		.direction =	UE_DIR_IN,
345187741Semax		.if_index = 	1,
346190734Sthompsa		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
347190734Sthompsa		.frames =	UBT_ISOC_NFRAMES,
348190734Sthompsa		.flags =	{ .short_xfer_ok = 1, },
349190734Sthompsa		.callback =	&ubt_isoc_read_callback,
350184610Salfred	},
351187494Semax	/* Outgoing isochronous transfer #1 - SCO packets */
352187494Semax	[UBT_IF_1_ISOC_DT_WR1] = {
353187494Semax		.type =		UE_ISOCHRONOUS,
354187494Semax		.endpoint =	UE_ADDR_ANY,
355187494Semax		.direction =	UE_DIR_OUT,
356187741Semax		.if_index = 	1,
357190734Sthompsa		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
358190734Sthompsa		.frames =	UBT_ISOC_NFRAMES,
359190734Sthompsa		.flags =	{ .short_xfer_ok = 1, },
360190734Sthompsa		.callback =	&ubt_isoc_write_callback,
361184610Salfred	},
362187494Semax	/* Outgoing isochronous transfer #2 - SCO packets */
363187494Semax	[UBT_IF_1_ISOC_DT_WR2] = {
364187494Semax		.type =		UE_ISOCHRONOUS,
365187494Semax		.endpoint =	UE_ADDR_ANY,
366187494Semax		.direction =	UE_DIR_OUT,
367187741Semax		.if_index = 	1,
368190734Sthompsa		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
369190734Sthompsa		.frames =	UBT_ISOC_NFRAMES,
370190734Sthompsa		.flags =	{ .short_xfer_ok = 1, },
371190734Sthompsa		.callback =	&ubt_isoc_write_callback,
372184610Salfred	},
373184610Salfred};
374184610Salfred
375184610Salfred/*
376184610Salfred * If for some reason device should not be attached then put
377184610Salfred * VendorID/ProductID pair into the list below. The format is
378184610Salfred * as follows:
379184610Salfred *
380187494Semax *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
381184610Salfred *
382184610Salfred * where VENDOR_ID and PRODUCT_ID are hex numbers.
383184610Salfred */
384187741Semax
385223486Shselaskystatic const STRUCT_USB_HOST_ID ubt_ignore_devs[] =
386187741Semax{
387184610Salfred	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
388187494Semax	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
389249178Sadrian
390249178Sadrian	/* Atheros 3011 with sflash firmware */
391249178Sadrian	{ USB_VPI(0x0cf3, 0x3002, 0) },
392249178Sadrian	{ USB_VPI(0x0cf3, 0xe019, 0) },
393249178Sadrian	{ USB_VPI(0x13d3, 0x3304, 0) },
394249178Sadrian	{ USB_VPI(0x0930, 0x0215, 0) },
395249178Sadrian	{ USB_VPI(0x0489, 0xe03d, 0) },
396249178Sadrian	{ USB_VPI(0x0489, 0xe027, 0) },
397249178Sadrian
398249178Sadrian	/* Atheros AR9285 Malbec with sflash firmware */
399249178Sadrian	{ USB_VPI(0x03f0, 0x311d, 0) },
400249178Sadrian
401249178Sadrian	/* Atheros 3012 with sflash firmware */
402249178Sadrian	{ USB_VPI(0x0cf3, 0x3004, 0) },
403249178Sadrian	{ USB_VPI(0x0cf3, 0x311d, 0) },
404249178Sadrian	{ USB_VPI(0x13d3, 0x3375, 0) },
405249178Sadrian	{ USB_VPI(0x04ca, 0x3005, 0) },
406249178Sadrian	{ USB_VPI(0x04ca, 0x3006, 0) },
407249178Sadrian	{ USB_VPI(0x04ca, 0x3008, 0) },
408249178Sadrian	{ USB_VPI(0x13d3, 0x3362, 0) },
409249178Sadrian	{ USB_VPI(0x0cf3, 0xe004, 0) },
410249178Sadrian	{ USB_VPI(0x0930, 0x0219, 0) },
411249178Sadrian	{ USB_VPI(0x0489, 0xe057, 0) },
412249178Sadrian	{ USB_VPI(0x13d3, 0x3393, 0) },
413249178Sadrian	{ USB_VPI(0x0489, 0xe04e, 0) },
414249178Sadrian	{ USB_VPI(0x0489, 0xe056, 0) },
415249178Sadrian
416249178Sadrian	/* Atheros AR5BBU12 with sflash firmware */
417249178Sadrian	{ USB_VPI(0x0489, 0xe02c, 0) },
418249178Sadrian
419249178Sadrian	/* Atheros AR5BBU12 with sflash firmware */
420249178Sadrian	{ USB_VPI(0x0489, 0xe03c, 0) },
421249178Sadrian	{ USB_VPI(0x0489, 0xe036, 0) },
422184610Salfred};
423184610Salfred
424184610Salfred/* List of supported bluetooth devices */
425223486Shselaskystatic const STRUCT_USB_HOST_ID ubt_devs[] =
426187741Semax{
427187494Semax	/* Generic Bluetooth class devices */
428187494Semax	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
429187494Semax	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
430187494Semax	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
431184610Salfred
432184610Salfred	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
433187494Semax	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
434244704Sglebius
435244704Sglebius	/* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
436244704Sglebius	{ USB_VENDOR(USB_VENDOR_BROADCOM),
437244714Srakuco	  USB_IFACE_CLASS(UICLASS_VENDOR),
438244714Srakuco	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
439244714Srakuco	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
440255090Shselasky
441255090Shselasky	/* Apple-specific (Broadcom) devices */
442255090Shselasky	{ USB_VENDOR(USB_VENDOR_APPLE),
443255090Shselasky	  USB_IFACE_CLASS(UICLASS_VENDOR),
444255090Shselasky	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
445255090Shselasky	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
446255090Shselasky
447255090Shselasky	/* Foxconn - Hon Hai */
448255090Shselasky	{ USB_VENDOR(USB_VENDOR_FOXCONN),
449255090Shselasky	  USB_IFACE_CLASS(UICLASS_VENDOR),
450255090Shselasky	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
451255090Shselasky	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
452255090Shselasky
453255090Shselasky	/* MediaTek MT76x0E */
454255090Shselasky	{ USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },
455255090Shselasky
456255090Shselasky	/* Broadcom SoftSailing reporting vendor specific */
457255090Shselasky	{ USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },
458255090Shselasky
459255090Shselasky	/* Apple MacBookPro 7,1 */
460255090Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },
461255090Shselasky
462255090Shselasky	/* Apple iMac11,1 */
463255090Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },
464255090Shselasky
465255090Shselasky	/* Apple MacBookPro6,2 */
466255090Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },
467255090Shselasky
468255090Shselasky	/* Apple MacBookAir3,1, MacBookAir3,2 */
469255090Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },
470255090Shselasky
471255090Shselasky	/* Apple MacBookAir4,1 */
472255090Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },
473255090Shselasky
474255090Shselasky	/* MacBookAir6,1 */
475255090Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },
476255090Shselasky
477255090Shselasky	/* Apple MacBookPro8,2 */
478255090Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },
479255090Shselasky
480255090Shselasky	/* Apple MacMini5,1 */
481255090Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },
482255090Shselasky
483255090Shselasky	/* Bluetooth Ultraport Module from IBM */
484255090Shselasky	{ USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },
485255090Shselasky
486255090Shselasky	/* ALPS Modules with non-standard ID */
487255090Shselasky	{ USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },
488255090Shselasky	{ USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },
489255090Shselasky
490255090Shselasky	{ USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },
491255090Shselasky
492255090Shselasky	/* Canyon CN-BTU1 with HID interfaces */
493255090Shselasky	{ USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },
494255090Shselasky
495255090Shselasky	/* Broadcom BCM20702A0 */
496255090Shselasky	{ USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },
497255128Seadler	{ USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },
498255090Shselasky	{ USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },
499255090Shselasky	{ USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },
500255090Shselasky	{ USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
501184610Salfred};
502184610Salfred
503184610Salfred/*
504187494Semax * Probe for a USB Bluetooth device.
505187494Semax * USB context.
506184610Salfred */
507184610Salfred
508184610Salfredstatic int
509184610Salfredubt_probe(device_t dev)
510184610Salfred{
511192984Sthompsa	struct usb_attach_arg	*uaa = device_get_ivars(dev);
512222055Savg	int error;
513184610Salfred
514192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
515184610Salfred		return (ENXIO);
516187494Semax
517187494Semax	if (uaa->info.bIfaceIndex != 0)
518184610Salfred		return (ENXIO);
519187494Semax
520194228Sthompsa	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
521187494Semax			sizeof(ubt_ignore_devs), uaa) == 0)
522184610Salfred		return (ENXIO);
523187494Semax
524222055Savg	error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
525222055Savg	if (error == 0)
526222055Savg		return (BUS_PROBE_GENERIC);
527222055Savg	return (error);
528187494Semax} /* ubt_probe */
529184610Salfred
530184610Salfred/*
531187494Semax * Attach the device.
532187494Semax * USB context.
533184610Salfred */
534184610Salfred
535184610Salfredstatic int
536184610Salfredubt_attach(device_t dev)
537184610Salfred{
538192984Sthompsa	struct usb_attach_arg		*uaa = device_get_ivars(dev);
539187494Semax	struct ubt_softc		*sc = device_get_softc(dev);
540192984Sthompsa	struct usb_endpoint_descriptor	*ed;
541192984Sthompsa	struct usb_interface_descriptor *id;
542241078Shselasky	struct usb_interface		*iface;
543187494Semax	uint16_t			wMaxPacketSize;
544187741Semax	uint8_t				alt_index, i, j;
545187741Semax	uint8_t				iface_index[2] = { 0, 1 };
546184610Salfred
547194228Sthompsa	device_set_usb_desc(dev);
548184610Salfred
549187741Semax	sc->sc_dev = dev;
550187741Semax	sc->sc_debug = NG_UBT_WARN_LEVEL;
551253347Srodrigc
552187494Semax	/*
553187494Semax	 * Create Netgraph node
554187494Semax	 */
555187494Semax
556187494Semax	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
557187741Semax		UBT_ALERT(sc, "could not create Netgraph node\n");
558187494Semax		return (ENXIO);
559187494Semax	}
560187494Semax
561187494Semax	/* Name Netgraph node */
562187741Semax	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
563187741Semax		UBT_ALERT(sc, "could not name Netgraph node\n");
564187494Semax		NG_NODE_UNREF(sc->sc_node);
565187494Semax		return (ENXIO);
566187494Semax	}
567187494Semax	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
568187494Semax	NG_NODE_FORCE_WRITER(sc->sc_node);
569187494Semax
570184610Salfred	/*
571184610Salfred	 * Initialize device softc structure
572184610Salfred	 */
573184610Salfred
574187494Semax	/* initialize locks */
575187741Semax	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
576187741Semax	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
577187494Semax
578187494Semax	/* initialize packet queues */
579184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
580184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
581187494Semax	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
582184610Salfred
583187494Semax	/* initialize glue task */
584187741Semax	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
585184610Salfred
586184610Salfred	/*
587184610Salfred	 * Configure Bluetooth USB device. Discover all required USB
588184610Salfred	 * interfaces and endpoints.
589184610Salfred	 *
590184610Salfred	 * USB device must present two interfaces:
591184610Salfred	 * 1) Interface 0 that has 3 endpoints
592184610Salfred	 *	1) Interrupt endpoint to receive HCI events
593184610Salfred	 *	2) Bulk IN endpoint to receive ACL data
594184610Salfred	 *	3) Bulk OUT endpoint to send ACL data
595184610Salfred	 *
596184610Salfred	 * 2) Interface 1 then has 2 endpoints
597184610Salfred	 *	1) Isochronous IN endpoint to receive SCO data
598184610Salfred 	 *	2) Isochronous OUT endpoint to send SCO data
599184610Salfred	 *
600184610Salfred	 * Interface 1 (with isochronous endpoints) has several alternate
601184610Salfred	 * configurations with different packet size.
602184610Salfred	 */
603184610Salfred
604184610Salfred	/*
605187741Semax	 * For interface #1 search alternate settings, and find
606187741Semax	 * the descriptor with the largest wMaxPacketSize
607184610Salfred	 */
608184610Salfred
609184610Salfred	wMaxPacketSize = 0;
610187494Semax	alt_index = 0;
611184610Salfred	i = 0;
612184610Salfred	j = 0;
613190728Sthompsa	ed = NULL;
614187494Semax
615190728Sthompsa	/*
616190728Sthompsa	 * Search through all the descriptors looking for the largest
617190728Sthompsa	 * packet size:
618190728Sthompsa	 */
619194228Sthompsa	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
620194228Sthompsa	    usbd_get_config_descriptor(uaa->device),
621192984Sthompsa	    (struct usb_descriptor *)ed))) {
622184610Salfred
623190728Sthompsa		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
624190728Sthompsa		    (ed->bLength >= sizeof(*id))) {
625192984Sthompsa			id = (struct usb_interface_descriptor *)ed;
626190728Sthompsa			i = id->bInterfaceNumber;
627190728Sthompsa			j = id->bAlternateSetting;
628184610Salfred		}
629187494Semax
630190728Sthompsa		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
631190728Sthompsa		    (ed->bLength >= sizeof(*ed)) &&
632190728Sthompsa		    (i == 1)) {
633190728Sthompsa			uint16_t temp;
634190728Sthompsa
635190728Sthompsa			temp = UGETW(ed->wMaxPacketSize);
636190728Sthompsa			if (temp > wMaxPacketSize) {
637190728Sthompsa				wMaxPacketSize = temp;
638190728Sthompsa				alt_index = j;
639190728Sthompsa			}
640184610Salfred		}
641184610Salfred	}
642184610Salfred
643187741Semax	/* Set alt configuration on interface #1 only if we found it */
644187494Semax	if (wMaxPacketSize > 0 &&
645194228Sthompsa	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
646187741Semax		UBT_ALERT(sc, "could not set alternate setting %d " \
647187494Semax			"for interface 1!\n", alt_index);
648184610Salfred		goto detach;
649184610Salfred	}
650184610Salfred
651187741Semax	/* Setup transfers for both interfaces */
652194228Sthompsa	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
653187741Semax			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
654187741Semax		UBT_ALERT(sc, "could not allocate transfers\n");
655184610Salfred		goto detach;
656184610Salfred	}
657184610Salfred
658241078Shselasky	/* Claim all interfaces belonging to the Bluetooth part */
659241078Shselasky	for (i = 1;; i++) {
660241078Shselasky		iface = usbd_get_iface(uaa->device, i);
661241078Shselasky		if (iface == NULL)
662241078Shselasky			break;
663241078Shselasky		id = usbd_get_interface_descriptor(iface);
664184610Salfred
665241078Shselasky		if ((id != NULL) &&
666241078Shselasky		    (id->bInterfaceClass == UICLASS_WIRELESS) &&
667241078Shselasky		    (id->bInterfaceSubClass == UISUBCLASS_RF) &&
668241078Shselasky		    (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) {
669241078Shselasky			usbd_set_parent_iface(uaa->device, i,
670241078Shselasky			    uaa->info.bIfaceIndex);
671241078Shselasky		}
672241078Shselasky	}
673187494Semax	return (0); /* success */
674184610Salfred
675184610Salfreddetach:
676184610Salfred	ubt_detach(dev);
677184610Salfred
678184610Salfred	return (ENXIO);
679187494Semax} /* ubt_attach */
680184610Salfred
681184610Salfred/*
682187494Semax * Detach the device.
683187494Semax * USB context.
684184610Salfred */
685184610Salfred
686184610Salfredint
687184610Salfredubt_detach(device_t dev)
688184610Salfred{
689187494Semax	struct ubt_softc	*sc = device_get_softc(dev);
690187494Semax	node_p			node = sc->sc_node;
691184610Salfred
692187494Semax	/* Destroy Netgraph node */
693187494Semax	if (node != NULL) {
694187494Semax		sc->sc_node = NULL;
695187494Semax		NG_NODE_REALLY_DIE(node);
696187494Semax		ng_rmnode_self(node);
697184610Salfred	}
698184610Salfred
699187741Semax	/* Make sure ubt_task in gone */
700187741Semax	taskqueue_drain(taskqueue_swi, &sc->sc_task);
701187741Semax
702187494Semax	/* Free USB transfers, if any */
703194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
704184610Salfred
705187494Semax	/* Destroy queues */
706187741Semax	UBT_NG_LOCK(sc);
707184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
708184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
709184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
710187741Semax	UBT_NG_UNLOCK(sc);
711184610Salfred
712187741Semax	mtx_destroy(&sc->sc_if_mtx);
713187741Semax	mtx_destroy(&sc->sc_ng_mtx);
714187494Semax
715184610Salfred	return (0);
716187494Semax} /* ubt_detach */
717184610Salfred
718187494Semax/*
719187494Semax * Called when outgoing control request (HCI command) has completed, i.e.
720187494Semax * HCI command was sent to the device.
721187494Semax * USB context.
722187494Semax */
723187494Semax
724184610Salfredstatic void
725194677Sthompsaubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
726184610Salfred{
727194677Sthompsa	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
728192984Sthompsa	struct usb_device_request	req;
729187494Semax	struct mbuf			*m;
730194677Sthompsa	struct usb_page_cache		*pc;
731194677Sthompsa	int				actlen;
732184610Salfred
733194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
734194677Sthompsa
735184610Salfred	switch (USB_GET_STATE(xfer)) {
736184610Salfred	case USB_ST_TRANSFERRED:
737194677Sthompsa		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
738194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
739187741Semax		UBT_STAT_PCKTS_SENT(sc);
740187494Semax		/* FALLTHROUGH */
741184610Salfred
742184610Salfred	case USB_ST_SETUP:
743187494Semaxsend_next:
744187494Semax		/* Get next command mbuf, if any */
745187741Semax		UBT_NG_LOCK(sc);
746184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
747187741Semax		UBT_NG_UNLOCK(sc);
748184610Salfred
749184610Salfred		if (m == NULL) {
750187494Semax			UBT_INFO(sc, "HCI command queue is empty\n");
751187741Semax			break;	/* transfer complete */
752184610Salfred		}
753184610Salfred
754187494Semax		/* Initialize a USB control request and then schedule it */
755184610Salfred		bzero(&req, sizeof(req));
756184610Salfred		req.bmRequestType = UBT_HCI_REQUEST;
757184610Salfred		USETW(req.wLength, m->m_pkthdr.len);
758184610Salfred
759187494Semax		UBT_INFO(sc, "Sending control request, " \
760187494Semax			"bmRequestType=0x%02x, wLength=%d\n",
761187494Semax			req.bmRequestType, UGETW(req.wLength));
762184610Salfred
763194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
764194677Sthompsa		usbd_copy_in(pc, 0, &req, sizeof(req));
765194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 1);
766194677Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
767184610Salfred
768194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
769194677Sthompsa		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
770194677Sthompsa		usbd_xfer_set_frames(xfer, 2);
771184610Salfred
772184610Salfred		NG_FREE_M(m);
773184610Salfred
774194228Sthompsa		usbd_transfer_submit(xfer);
775187494Semax		break;
776184610Salfred
777187494Semax	default: /* Error */
778194677Sthompsa		if (error != USB_ERR_CANCELLED) {
779187494Semax			UBT_WARN(sc, "control transfer failed: %s\n",
780194677Sthompsa				usbd_errstr(error));
781187494Semax
782187494Semax			UBT_STAT_OERROR(sc);
783187494Semax			goto send_next;
784184610Salfred		}
785187494Semax
786187741Semax		/* transfer cancelled */
787187494Semax		break;
788184610Salfred	}
789187494Semax} /* ubt_ctrl_write_callback */
790184610Salfred
791187494Semax/*
792187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e.
793187494Semax * HCI event was received from the device.
794187494Semax * USB context.
795187494Semax */
796187494Semax
797184610Salfredstatic void
798194677Sthompsaubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
799184610Salfred{
800194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
801187494Semax	struct mbuf		*m;
802187494Semax	ng_hci_event_pkt_t	*hdr;
803194677Sthompsa	struct usb_page_cache	*pc;
804194677Sthompsa	int			actlen;
805184610Salfred
806194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
807194677Sthompsa
808187494Semax	m = NULL;
809187494Semax
810184610Salfred	switch (USB_GET_STATE(xfer)) {
811184610Salfred	case USB_ST_TRANSFERRED:
812187494Semax		/* Allocate a new mbuf */
813243882Sglebius		MGETHDR(m, M_NOWAIT, MT_DATA);
814184610Salfred		if (m == NULL) {
815187494Semax			UBT_STAT_IERROR(sc);
816187494Semax			goto submit_next;
817184610Salfred		}
818187494Semax
819243882Sglebius		MCLGET(m, M_NOWAIT);
820184610Salfred		if (!(m->m_flags & M_EXT)) {
821187494Semax			UBT_STAT_IERROR(sc);
822187494Semax			goto submit_next;
823184610Salfred		}
824187494Semax
825187494Semax		/* Add HCI packet type */
826187494Semax		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
827187494Semax		m->m_pkthdr.len = m->m_len = 1;
828187494Semax
829194677Sthompsa		if (actlen > MCLBYTES - 1)
830194677Sthompsa			actlen = MCLBYTES - 1;
831187494Semax
832194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
833194677Sthompsa		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
834194677Sthompsa		m->m_pkthdr.len += actlen;
835194677Sthompsa		m->m_len += actlen;
836187494Semax
837187494Semax		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
838194677Sthompsa			actlen);
839187494Semax
840187494Semax		/* Validate packet and send it up the stack */
841233774Shselasky		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
842187494Semax			UBT_INFO(sc, "HCI event packet is too short\n");
843187494Semax
844187494Semax			UBT_STAT_IERROR(sc);
845187494Semax			goto submit_next;
846184610Salfred		}
847184610Salfred
848187494Semax		hdr = mtod(m, ng_hci_event_pkt_t *);
849187494Semax		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
850187494Semax			UBT_ERR(sc, "Invalid HCI event packet size, " \
851187494Semax				"length=%d, pktlen=%d\n",
852187494Semax				hdr->length, m->m_pkthdr.len);
853184610Salfred
854187494Semax			UBT_STAT_IERROR(sc);
855187494Semax			goto submit_next;
856184610Salfred		}
857184610Salfred
858187494Semax		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
859187494Semax			"length=%d\n", m->m_pkthdr.len, hdr->length);
860184610Salfred
861187494Semax		UBT_STAT_PCKTS_RECV(sc);
862187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
863184610Salfred
864187741Semax		ubt_fwd_mbuf_up(sc, &m);
865187494Semax		/* m == NULL at this point */
866187494Semax		/* FALLTHROUGH */
867184610Salfred
868184610Salfred	case USB_ST_SETUP:
869187494Semaxsubmit_next:
870187494Semax		NG_FREE_M(m); /* checks for m != NULL */
871184610Salfred
872194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
873194228Sthompsa		usbd_transfer_submit(xfer);
874187494Semax		break;
875184610Salfred
876187494Semax	default: /* Error */
877194677Sthompsa		if (error != USB_ERR_CANCELLED) {
878187494Semax			UBT_WARN(sc, "interrupt transfer failed: %s\n",
879194677Sthompsa				usbd_errstr(error));
880184610Salfred
881187494Semax			/* Try to clear stall first */
882194677Sthompsa			usbd_xfer_set_stall(xfer);
883187741Semax			goto submit_next;
884187741Semax		}
885187741Semax			/* transfer cancelled */
886187494Semax		break;
887184610Salfred	}
888187494Semax} /* ubt_intr_read_callback */
889184610Salfred
890187494Semax/*
891187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e.
892187494Semax * ACL packet was received from the device.
893187494Semax * USB context.
894187494Semax */
895187494Semax
896184610Salfredstatic void
897194677Sthompsaubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
898184610Salfred{
899194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
900187494Semax	struct mbuf		*m;
901187494Semax	ng_hci_acldata_pkt_t	*hdr;
902194677Sthompsa	struct usb_page_cache	*pc;
903233774Shselasky	int len;
904233774Shselasky	int actlen;
905184610Salfred
906194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
907194677Sthompsa
908187494Semax	m = NULL;
909184610Salfred
910184610Salfred	switch (USB_GET_STATE(xfer)) {
911184610Salfred	case USB_ST_TRANSFERRED:
912187494Semax		/* Allocate new mbuf */
913243882Sglebius		MGETHDR(m, M_NOWAIT, MT_DATA);
914184610Salfred		if (m == NULL) {
915187494Semax			UBT_STAT_IERROR(sc);
916187494Semax			goto submit_next;
917184610Salfred		}
918187494Semax
919243882Sglebius		MCLGET(m, M_NOWAIT);
920184610Salfred		if (!(m->m_flags & M_EXT)) {
921187494Semax			UBT_STAT_IERROR(sc);
922187494Semax			goto submit_next;
923184610Salfred		}
924184610Salfred
925187494Semax		/* Add HCI packet type */
926187494Semax		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
927187494Semax		m->m_pkthdr.len = m->m_len = 1;
928184610Salfred
929194677Sthompsa		if (actlen > MCLBYTES - 1)
930194677Sthompsa			actlen = MCLBYTES - 1;
931184610Salfred
932194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
933194677Sthompsa		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
934194677Sthompsa		m->m_pkthdr.len += actlen;
935194677Sthompsa		m->m_len += actlen;
936184610Salfred
937187494Semax		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
938194677Sthompsa			actlen);
939184610Salfred
940187494Semax		/* Validate packet and send it up the stack */
941233774Shselasky		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
942187494Semax			UBT_INFO(sc, "HCI ACL packet is too short\n");
943184610Salfred
944187494Semax			UBT_STAT_IERROR(sc);
945187494Semax			goto submit_next;
946184610Salfred		}
947184610Salfred
948187494Semax		hdr = mtod(m, ng_hci_acldata_pkt_t *);
949187494Semax		len = le16toh(hdr->length);
950233774Shselasky		if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
951187494Semax			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
952187494Semax				"pktlen=%d\n", len, m->m_pkthdr.len);
953184610Salfred
954187494Semax			UBT_STAT_IERROR(sc);
955187494Semax			goto submit_next;
956184610Salfred		}
957184610Salfred
958187494Semax		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
959187494Semax			"length=%d\n", m->m_pkthdr.len, len);
960184610Salfred
961187494Semax		UBT_STAT_PCKTS_RECV(sc);
962187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
963184610Salfred
964187741Semax		ubt_fwd_mbuf_up(sc, &m);
965187494Semax		/* m == NULL at this point */
966187494Semax		/* FALLTHOUGH */
967184610Salfred
968187494Semax	case USB_ST_SETUP:
969187494Semaxsubmit_next:
970187494Semax		NG_FREE_M(m); /* checks for m != NULL */
971184610Salfred
972194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
973194228Sthompsa		usbd_transfer_submit(xfer);
974187494Semax		break;
975184610Salfred
976187494Semax	default: /* Error */
977194677Sthompsa		if (error != USB_ERR_CANCELLED) {
978187494Semax			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
979194677Sthompsa				usbd_errstr(error));
980184610Salfred
981187494Semax			/* Try to clear stall first */
982194677Sthompsa			usbd_xfer_set_stall(xfer);
983187741Semax			goto submit_next;
984187741Semax		}
985187741Semax			/* transfer cancelled */
986187494Semax		break;
987187494Semax	}
988187494Semax} /* ubt_bulk_read_callback */
989184610Salfred
990187494Semax/*
991187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
992187494Semax * ACL packet was sent to the device.
993187494Semax * USB context.
994187494Semax */
995184610Salfred
996184610Salfredstatic void
997194677Sthompsaubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
998184610Salfred{
999194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1000187494Semax	struct mbuf		*m;
1001194677Sthompsa	struct usb_page_cache	*pc;
1002194677Sthompsa	int			actlen;
1003184610Salfred
1004194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1005194677Sthompsa
1006184610Salfred	switch (USB_GET_STATE(xfer)) {
1007184610Salfred	case USB_ST_TRANSFERRED:
1008194677Sthompsa		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
1009194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
1010187741Semax		UBT_STAT_PCKTS_SENT(sc);
1011187494Semax		/* FALLTHROUGH */
1012184610Salfred
1013187494Semax	case USB_ST_SETUP:
1014187741Semaxsend_next:
1015187494Semax		/* Get next mbuf, if any */
1016187741Semax		UBT_NG_LOCK(sc);
1017184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
1018187741Semax		UBT_NG_UNLOCK(sc);
1019184610Salfred
1020184610Salfred		if (m == NULL) {
1021187494Semax			UBT_INFO(sc, "ACL data queue is empty\n");
1022187741Semax			break; /* transfer completed */
1023184610Salfred		}
1024187494Semax
1025184610Salfred		/*
1026187494Semax		 * Copy ACL data frame back to a linear USB transfer buffer
1027187494Semax		 * and schedule transfer
1028184610Salfred		 */
1029184610Salfred
1030194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
1031194677Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
1032194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
1033184610Salfred
1034187494Semax		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
1035187494Semax			m->m_pkthdr.len);
1036184610Salfred
1037184610Salfred		NG_FREE_M(m);
1038184610Salfred
1039194228Sthompsa		usbd_transfer_submit(xfer);
1040187494Semax		break;
1041184610Salfred
1042187494Semax	default: /* Error */
1043194677Sthompsa		if (error != USB_ERR_CANCELLED) {
1044187494Semax			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
1045194677Sthompsa				usbd_errstr(error));
1046184610Salfred
1047187494Semax			UBT_STAT_OERROR(sc);
1048184610Salfred
1049184610Salfred			/* try to clear stall first */
1050194677Sthompsa			usbd_xfer_set_stall(xfer);
1051187741Semax			goto send_next;
1052187741Semax		}
1053187741Semax			/* transfer cancelled */
1054187494Semax		break;
1055184610Salfred	}
1056187494Semax} /* ubt_bulk_write_callback */
1057184610Salfred
1058187494Semax/*
1059187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e.
1060187494Semax * SCO packet was received from the device.
1061187494Semax * USB context.
1062187494Semax */
1063187494Semax
1064184610Salfredstatic void
1065194677Sthompsaubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
1066184610Salfred{
1067194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1068187494Semax	int			n;
1069194677Sthompsa	int actlen, nframes;
1070184610Salfred
1071194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1072194677Sthompsa
1073187494Semax	switch (USB_GET_STATE(xfer)) {
1074187494Semax	case USB_ST_TRANSFERRED:
1075194677Sthompsa		for (n = 0; n < nframes; n ++)
1076187494Semax			if (ubt_isoc_read_one_frame(xfer, n) < 0)
1077187494Semax				break;
1078187494Semax		/* FALLTHROUGH */
1079184610Salfred
1080187494Semax	case USB_ST_SETUP:
1081187494Semaxread_next:
1082194677Sthompsa		for (n = 0; n < nframes; n ++)
1083194677Sthompsa			usbd_xfer_set_frame_len(xfer, n,
1084194677Sthompsa			    usbd_xfer_max_framelen(xfer));
1085184610Salfred
1086194228Sthompsa		usbd_transfer_submit(xfer);
1087187494Semax		break;
1088184610Salfred
1089187494Semax	default: /* Error */
1090194677Sthompsa                if (error != USB_ERR_CANCELLED) {
1091187494Semax                        UBT_STAT_IERROR(sc);
1092187494Semax                        goto read_next;
1093187494Semax                }
1094184610Salfred
1095187741Semax		/* transfer cancelled */
1096187494Semax		break;
1097187494Semax	}
1098187494Semax} /* ubt_isoc_read_callback */
1099184610Salfred
1100187494Semax/*
1101187494Semax * Helper function. Called from ubt_isoc_read_callback() to read
1102187494Semax * SCO data from one frame.
1103187494Semax * USB context.
1104187494Semax */
1105184610Salfred
1106187494Semaxstatic int
1107192984Sthompsaubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
1108187494Semax{
1109194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1110194677Sthompsa	struct usb_page_cache	*pc;
1111187494Semax	struct mbuf		*m;
1112194677Sthompsa	int			len, want, got, total;
1113184610Salfred
1114187494Semax	/* Get existing SCO reassembly buffer */
1115194677Sthompsa	pc = usbd_xfer_get_frame(xfer, 0);
1116187494Semax	m = sc->sc_isoc_in_buffer;
1117194682Sthompsa	total = usbd_xfer_frame_len(xfer, frame_no);
1118184610Salfred
1119187494Semax	/* While we have data in the frame */
1120194677Sthompsa	while (total > 0) {
1121187494Semax		if (m == NULL) {
1122187494Semax			/* Start new reassembly buffer */
1123243882Sglebius			MGETHDR(m, M_NOWAIT, MT_DATA);
1124187494Semax			if (m == NULL) {
1125187494Semax				UBT_STAT_IERROR(sc);
1126187494Semax				return (-1);	/* XXX out of sync! */
1127187494Semax			}
1128184610Salfred
1129243882Sglebius			MCLGET(m, M_NOWAIT);
1130187494Semax			if (!(m->m_flags & M_EXT)) {
1131187494Semax				UBT_STAT_IERROR(sc);
1132187494Semax				NG_FREE_M(m);
1133187494Semax				return (-1);	/* XXX out of sync! */
1134184610Salfred			}
1135184610Salfred
1136187494Semax			/* Expect SCO header */
1137187494Semax			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
1138187494Semax			m->m_pkthdr.len = m->m_len = got = 1;
1139187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
1140187494Semax		} else {
1141187494Semax			/*
1142187494Semax			 * Check if we have SCO header and if so
1143187494Semax			 * adjust amount of data we want
1144187494Semax			 */
1145187494Semax			got = m->m_pkthdr.len;
1146187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
1147184610Salfred
1148187494Semax			if (got >= want)
1149187494Semax				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
1150184610Salfred		}
1151184610Salfred
1152187494Semax		/* Append frame data to the SCO reassembly buffer */
1153194677Sthompsa		len = total;
1154187494Semax		if (got + len > want)
1155187494Semax			len = want - got;
1156184610Salfred
1157194677Sthompsa		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
1158187494Semax			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
1159184610Salfred
1160187494Semax		m->m_pkthdr.len += len;
1161187494Semax		m->m_len += len;
1162194677Sthompsa		total -= len;
1163184610Salfred
1164187494Semax		/* Check if we got everything we wanted, if not - continue */
1165187494Semax		if (got != want)
1166187494Semax			continue;
1167184610Salfred
1168187494Semax		/* If we got here then we got complete SCO frame */
1169187494Semax		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
1170187494Semax			"length=%d\n", m->m_pkthdr.len,
1171187494Semax			mtod(m, ng_hci_scodata_pkt_t *)->length);
1172184610Salfred
1173187494Semax		UBT_STAT_PCKTS_RECV(sc);
1174187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
1175184610Salfred
1176187741Semax		ubt_fwd_mbuf_up(sc, &m);
1177187494Semax		/* m == NULL at this point */
1178187494Semax	}
1179184610Salfred
1180187494Semax	/* Put SCO reassembly buffer back */
1181187494Semax	sc->sc_isoc_in_buffer = m;
1182184610Salfred
1183187494Semax	return (0);
1184187494Semax} /* ubt_isoc_read_one_frame */
1185184610Salfred
1186187494Semax/*
1187187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
1188187494Semax * SCO packet was sent to the device.
1189187494Semax * USB context.
1190187494Semax */
1191184610Salfred
1192184610Salfredstatic void
1193194677Sthompsaubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
1194184610Salfred{
1195194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1196194677Sthompsa	struct usb_page_cache	*pc;
1197187494Semax	struct mbuf		*m;
1198187494Semax	int			n, space, offset;
1199194677Sthompsa	int			actlen, nframes;
1200184610Salfred
1201194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1202194677Sthompsa	pc = usbd_xfer_get_frame(xfer, 0);
1203194677Sthompsa
1204184610Salfred	switch (USB_GET_STATE(xfer)) {
1205184610Salfred	case USB_ST_TRANSFERRED:
1206194677Sthompsa		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1207194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
1208187741Semax		UBT_STAT_PCKTS_SENT(sc);
1209187494Semax		/* FALLTHROUGH */
1210184610Salfred
1211184610Salfred	case USB_ST_SETUP:
1212187494Semaxsend_next:
1213184610Salfred		offset = 0;
1214194677Sthompsa		space = usbd_xfer_max_framelen(xfer) * nframes;
1215187494Semax		m = NULL;
1216184610Salfred
1217187494Semax		while (space > 0) {
1218187494Semax			if (m == NULL) {
1219187741Semax				UBT_NG_LOCK(sc);
1220187494Semax				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
1221187741Semax				UBT_NG_UNLOCK(sc);
1222184610Salfred
1223187494Semax				if (m == NULL)
1224187494Semax					break;
1225187494Semax			}
1226184610Salfred
1227187494Semax			n = min(space, m->m_pkthdr.len);
1228187494Semax			if (n > 0) {
1229194677Sthompsa				usbd_m_copy_in(pc, offset, m,0, n);
1230187494Semax				m_adj(m, n);
1231184610Salfred
1232187494Semax				offset += n;
1233187494Semax				space -= n;
1234187494Semax			}
1235184610Salfred
1236187494Semax			if (m->m_pkthdr.len == 0)
1237187494Semax				NG_FREE_M(m); /* sets m = NULL */
1238187494Semax		}
1239184610Salfred
1240187494Semax		/* Put whatever is left from mbuf back on queue */
1241187494Semax		if (m != NULL) {
1242187741Semax			UBT_NG_LOCK(sc);
1243187494Semax			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
1244187741Semax			UBT_NG_UNLOCK(sc);
1245184610Salfred		}
1246184610Salfred
1247187494Semax		/*
1248187494Semax		 * Calculate sizes for isoc frames.
1249187494Semax		 * Note that offset could be 0 at this point (i.e. we have
1250187494Semax		 * nothing to send). That is fine, as we have isoc. transfers
1251187494Semax		 * going in both directions all the time. In this case it
1252187494Semax		 * would be just empty isoc. transfer.
1253187494Semax		 */
1254187494Semax
1255194677Sthompsa		for (n = 0; n < nframes; n ++) {
1256194677Sthompsa			usbd_xfer_set_frame_len(xfer, n,
1257194677Sthompsa			    min(offset, usbd_xfer_max_framelen(xfer)));
1258194682Sthompsa			offset -= usbd_xfer_frame_len(xfer, n);
1259187494Semax		}
1260187494Semax
1261194228Sthompsa		usbd_transfer_submit(xfer);
1262187494Semax		break;
1263184610Salfred
1264187494Semax	default: /* Error */
1265194677Sthompsa		if (error != USB_ERR_CANCELLED) {
1266187494Semax			UBT_STAT_OERROR(sc);
1267187494Semax			goto send_next;
1268184610Salfred		}
1269187494Semax
1270187741Semax		/* transfer cancelled */
1271187494Semax		break;
1272184610Salfred	}
1273184610Salfred}
1274184610Salfred
1275187741Semax/*
1276187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack).
1277187741Semax * Modifies value of the mbuf pointer (sets it to NULL).
1278187741Semax * Save to call from any context.
1279187741Semax */
1280187741Semax
1281187741Semaxstatic int
1282187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
1283187741Semax{
1284187741Semax	hook_p	hook;
1285187741Semax	int	error;
1286187741Semax
1287187741Semax	/*
1288187741Semax	 * Close the race with Netgraph hook newhook/disconnect methods.
1289187741Semax	 * Save the hook pointer atomically. Two cases are possible:
1290187741Semax	 *
1291187741Semax	 * 1) The hook pointer is NULL. It means disconnect method got
1292187741Semax	 *    there first. In this case we are done.
1293187741Semax	 *
1294187741Semax	 * 2) The hook pointer is not NULL. It means that hook pointer
1295187741Semax	 *    could be either in valid or invalid (i.e. in the process
1296187741Semax	 *    of disconnect) state. In any case grab an extra reference
1297187741Semax	 *    to protect the hook pointer.
1298187741Semax	 *
1299187741Semax	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
1300187741Semax	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
1301187741Semax	 */
1302187741Semax
1303187741Semax	UBT_NG_LOCK(sc);
1304187741Semax	if ((hook = sc->sc_hook) != NULL)
1305187741Semax		NG_HOOK_REF(hook);
1306187741Semax	UBT_NG_UNLOCK(sc);
1307187741Semax
1308187741Semax	if (hook == NULL) {
1309187741Semax		NG_FREE_M(*m);
1310187741Semax		return (ENETDOWN);
1311187741Semax	}
1312187741Semax
1313187741Semax	NG_SEND_DATA_ONLY(error, hook, *m);
1314187741Semax	NG_HOOK_UNREF(hook);
1315187741Semax
1316187741Semax	if (error != 0)
1317187741Semax		UBT_STAT_IERROR(sc);
1318187741Semax
1319187741Semax	return (error);
1320187741Semax} /* ubt_fwd_mbuf_up */
1321187741Semax
1322184610Salfred/****************************************************************************
1323184610Salfred ****************************************************************************
1324187494Semax **                                 Glue
1325184610Salfred ****************************************************************************
1326184610Salfred ****************************************************************************/
1327184610Salfred
1328184610Salfred/*
1329187741Semax * Schedule glue task. Should be called with sc_ng_mtx held.
1330187494Semax * Netgraph context.
1331184610Salfred */
1332184610Salfred
1333187741Semaxstatic void
1334187494Semaxubt_task_schedule(ubt_softc_p sc, int action)
1335184610Salfred{
1336187741Semax	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
1337184610Salfred
1338187741Semax	/*
1339187741Semax	 * Try to handle corner case when "start all" and "stop all"
1340187741Semax	 * actions can both be set before task is executed.
1341187741Semax	 *
1342187741Semax	 * The rules are
1343187741Semax	 *
1344187741Semax	 * sc_task_flags	action		new sc_task_flags
1345187741Semax	 * ------------------------------------------------------
1346187741Semax	 * 0			start		start
1347187741Semax	 * 0			stop		stop
1348187741Semax	 * start		start		start
1349187741Semax	 * start		stop		stop
1350187741Semax	 * stop			start		stop|start
1351187741Semax	 * stop			stop		stop
1352187741Semax	 * stop|start		start		stop|start
1353187741Semax	 * stop|start		stop		stop
1354187741Semax	 */
1355187494Semax
1356187741Semax	if (action != 0) {
1357187741Semax		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
1358187494Semax			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
1359187494Semax
1360187494Semax		sc->sc_task_flags |= action;
1361187494Semax	}
1362187494Semax
1363187494Semax	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
1364187741Semax		return;
1365187494Semax
1366187494Semax	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
1367187494Semax		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
1368187741Semax		return;
1369187494Semax	}
1370187494Semax
1371187494Semax	/* XXX: i think this should never happen */
1372187494Semax} /* ubt_task_schedule */
1373187494Semax
1374184610Salfred/*
1375187494Semax * Glue task. Examines sc_task_flags and does things depending on it.
1376187494Semax * Taskqueue context.
1377184610Salfred */
1378184610Salfred
1379187494Semaxstatic void
1380187494Semaxubt_task(void *context, int pending)
1381184610Salfred{
1382187741Semax	ubt_softc_p	sc = context;
1383187741Semax	int		task_flags, i;
1384184610Salfred
1385187741Semax	UBT_NG_LOCK(sc);
1386187494Semax	task_flags = sc->sc_task_flags;
1387187494Semax	sc->sc_task_flags = 0;
1388187741Semax	UBT_NG_UNLOCK(sc);
1389187494Semax
1390187741Semax	/*
1391187741Semax	 * Stop all USB transfers synchronously.
1392187741Semax	 * Stop interface #0 and #1 transfers at the same time and in the
1393194228Sthompsa	 * same loop. usbd_transfer_drain() will do appropriate locking.
1394187741Semax	 */
1395187494Semax
1396187741Semax	if (task_flags & UBT_FLAG_T_STOP_ALL)
1397187741Semax		for (i = 0; i < UBT_N_TRANSFER; i ++)
1398194228Sthompsa			usbd_transfer_drain(sc->sc_xfer[i]);
1399187494Semax
1400187741Semax	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
1401187494Semax	if (task_flags & UBT_FLAG_T_START_ALL) {
1402187494Semax		/*
1403187494Semax		 * Interface #0
1404187494Semax		 */
1405187494Semax
1406187741Semax		mtx_lock(&sc->sc_if_mtx);
1407187741Semax
1408187494Semax		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
1409187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
1410187494Semax
1411187494Semax		/*
1412187494Semax		 * Interface #1
1413187494Semax		 * Start both read and write isoc. transfers by default.
1414187494Semax		 * Get them going all the time even if we have nothing
1415187494Semax		 * to send to avoid any delays.
1416187494Semax		 */
1417187494Semax
1418187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
1419187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
1420187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
1421187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
1422187741Semax
1423187741Semax		mtx_unlock(&sc->sc_if_mtx);
1424184610Salfred	}
1425187494Semax
1426187494Semax 	/* Start outgoing control transfer */
1427187494Semax	if (task_flags & UBT_FLAG_T_START_CTRL) {
1428187741Semax		mtx_lock(&sc->sc_if_mtx);
1429187494Semax		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
1430187741Semax		mtx_unlock(&sc->sc_if_mtx);
1431184610Salfred	}
1432184610Salfred
1433187494Semax	/* Start outgoing bulk transfer */
1434187494Semax	if (task_flags & UBT_FLAG_T_START_BULK) {
1435187741Semax		mtx_lock(&sc->sc_if_mtx);
1436187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
1437187741Semax		mtx_unlock(&sc->sc_if_mtx);
1438184610Salfred	}
1439187494Semax} /* ubt_task */
1440187494Semax
1441187494Semax/****************************************************************************
1442187494Semax ****************************************************************************
1443187494Semax **                        Netgraph specific
1444187494Semax ****************************************************************************
1445187494Semax ****************************************************************************/
1446184610Salfred
1447187494Semax/*
1448187494Semax * Netgraph node constructor. Do not allow to create node of this type.
1449187494Semax * Netgraph context.
1450187494Semax */
1451184610Salfred
1452187494Semaxstatic int
1453187494Semaxng_ubt_constructor(node_p node)
1454187494Semax{
1455187494Semax	return (EINVAL);
1456187494Semax} /* ng_ubt_constructor */
1457184610Salfred
1458184610Salfred/*
1459187494Semax * Netgraph node destructor. Destroy node only when device has been detached.
1460187494Semax * Netgraph context.
1461184610Salfred */
1462184610Salfred
1463184610Salfredstatic int
1464187494Semaxng_ubt_shutdown(node_p node)
1465184610Salfred{
1466187494Semax	if (node->nd_flags & NGF_REALLY_DIE) {
1467187494Semax		/*
1468187494Semax                 * We came here because the USB device is being
1469187494Semax		 * detached, so stop being persistant.
1470187494Semax                 */
1471187494Semax		NG_NODE_SET_PRIVATE(node, NULL);
1472187494Semax		NG_NODE_UNREF(node);
1473187494Semax	} else
1474187494Semax		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
1475184610Salfred
1476187494Semax	return (0);
1477187494Semax} /* ng_ubt_shutdown */
1478184610Salfred
1479187494Semax/*
1480187494Semax * Create new hook. There can only be one.
1481187494Semax * Netgraph context.
1482187494Semax */
1483184610Salfred
1484187494Semaxstatic int
1485187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name)
1486187494Semax{
1487187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1488184610Salfred
1489187494Semax	if (strcmp(name, NG_UBT_HOOK) != 0)
1490187494Semax		return (EINVAL);
1491184610Salfred
1492187741Semax	UBT_NG_LOCK(sc);
1493187741Semax	if (sc->sc_hook != NULL) {
1494187741Semax		UBT_NG_UNLOCK(sc);
1495187741Semax
1496187494Semax		return (EISCONN);
1497187741Semax	}
1498184610Salfred
1499187494Semax	sc->sc_hook = hook;
1500187741Semax	UBT_NG_UNLOCK(sc);
1501184610Salfred
1502187494Semax	return (0);
1503187494Semax} /* ng_ubt_newhook */
1504184610Salfred
1505187494Semax/*
1506187494Semax * Connect hook. Start incoming USB transfers.
1507187494Semax * Netgraph context.
1508187494Semax */
1509184610Salfred
1510187494Semaxstatic int
1511187494Semaxng_ubt_connect(hook_p hook)
1512187494Semax{
1513187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1514184610Salfred
1515187494Semax	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
1516184610Salfred
1517187741Semax	UBT_NG_LOCK(sc);
1518187494Semax	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
1519187741Semax	UBT_NG_UNLOCK(sc);
1520184610Salfred
1521184610Salfred	return (0);
1522187494Semax} /* ng_ubt_connect */
1523184610Salfred
1524184610Salfred/*
1525187494Semax * Disconnect hook.
1526187494Semax * Netgraph context.
1527184610Salfred */
1528184610Salfred
1529184610Salfredstatic int
1530184610Salfredng_ubt_disconnect(hook_p hook)
1531184610Salfred{
1532187741Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1533184610Salfred
1534187741Semax	UBT_NG_LOCK(sc);
1535184610Salfred
1536187741Semax	if (hook != sc->sc_hook) {
1537187741Semax		UBT_NG_UNLOCK(sc);
1538184610Salfred
1539187494Semax		return (EINVAL);
1540187741Semax	}
1541184610Salfred
1542187494Semax	sc->sc_hook = NULL;
1543184610Salfred
1544187741Semax	/* Kick off task to stop all USB xfers */
1545187741Semax	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
1546184610Salfred
1547187494Semax	/* Drain queues */
1548187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
1549187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
1550187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
1551184610Salfred
1552187741Semax	UBT_NG_UNLOCK(sc);
1553184610Salfred
1554187494Semax	return (0);
1555187494Semax} /* ng_ubt_disconnect */
1556187494Semax
1557184610Salfred/*
1558187494Semax * Process control message.
1559187494Semax * Netgraph context.
1560184610Salfred */
1561184610Salfred
1562184610Salfredstatic int
1563184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
1564184610Salfred{
1565187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1566187494Semax	struct ng_mesg		*msg, *rsp = NULL;
1567187494Semax	struct ng_bt_mbufq	*q;
1568187494Semax	int			error = 0, queue, qlen;
1569184610Salfred
1570184610Salfred	NGI_GET_MSG(item, msg);
1571184610Salfred
1572184610Salfred	switch (msg->header.typecookie) {
1573184610Salfred	case NGM_GENERIC_COOKIE:
1574184610Salfred		switch (msg->header.cmd) {
1575184610Salfred		case NGM_TEXT_STATUS:
1576184610Salfred			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
1577187494Semax			if (rsp == NULL) {
1578184610Salfred				error = ENOMEM;
1579187494Semax				break;
1580187494Semax			}
1581187494Semax
1582187494Semax			snprintf(rsp->data, NG_TEXTRESPONSE,
1583187494Semax				"Hook: %s\n" \
1584187494Semax				"Task flags: %#x\n" \
1585187494Semax				"Debug: %d\n" \
1586187494Semax				"CMD queue: [have:%d,max:%d]\n" \
1587187494Semax				"ACL queue: [have:%d,max:%d]\n" \
1588187494Semax				"SCO queue: [have:%d,max:%d]",
1589187741Semax				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
1590187494Semax				sc->sc_task_flags,
1591187494Semax				sc->sc_debug,
1592187494Semax				sc->sc_cmdq.len,
1593187494Semax				sc->sc_cmdq.maxlen,
1594187494Semax				sc->sc_aclq.len,
1595187494Semax				sc->sc_aclq.maxlen,
1596187494Semax				sc->sc_scoq.len,
1597187494Semax				sc->sc_scoq.maxlen);
1598184610Salfred			break;
1599184610Salfred
1600184610Salfred		default:
1601184610Salfred			error = EINVAL;
1602184610Salfred			break;
1603184610Salfred		}
1604184610Salfred		break;
1605184610Salfred
1606184610Salfred	case NGM_UBT_COOKIE:
1607184610Salfred		switch (msg->header.cmd) {
1608184610Salfred		case NGM_UBT_NODE_SET_DEBUG:
1609187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
1610184610Salfred				error = EMSGSIZE;
1611187494Semax				break;
1612187494Semax			}
1613187494Semax
1614187494Semax			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
1615184610Salfred			break;
1616184610Salfred
1617184610Salfred		case NGM_UBT_NODE_GET_DEBUG:
1618184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
1619184610Salfred			    M_NOWAIT);
1620187494Semax			if (rsp == NULL) {
1621184610Salfred				error = ENOMEM;
1622187494Semax				break;
1623187494Semax			}
1624187494Semax
1625187494Semax			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
1626184610Salfred			break;
1627184610Salfred
1628184610Salfred		case NGM_UBT_NODE_SET_QLEN:
1629187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1630184610Salfred				error = EMSGSIZE;
1631187494Semax				break;
1632187494Semax			}
1633184610Salfred
1634187494Semax			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1635187494Semax			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
1636184610Salfred
1637187494Semax			switch (queue) {
1638187494Semax			case NGM_UBT_NODE_QUEUE_CMD:
1639187494Semax				q = &sc->sc_cmdq;
1640187494Semax				break;
1641184610Salfred
1642187494Semax			case NGM_UBT_NODE_QUEUE_ACL:
1643187494Semax				q = &sc->sc_aclq;
1644187494Semax				break;
1645184610Salfred
1646187494Semax			case NGM_UBT_NODE_QUEUE_SCO:
1647187494Semax				q = &sc->sc_scoq;
1648187494Semax				break;
1649184610Salfred
1650187494Semax			default:
1651187494Semax				error = EINVAL;
1652187494Semax				goto done;
1653187494Semax				/* NOT REACHED */
1654184610Salfred			}
1655187494Semax
1656187494Semax			q->maxlen = qlen;
1657184610Salfred			break;
1658184610Salfred
1659184610Salfred		case NGM_UBT_NODE_GET_QLEN:
1660184610Salfred			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1661184610Salfred				error = EMSGSIZE;
1662184610Salfred				break;
1663184610Salfred			}
1664187494Semax
1665184610Salfred			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1666187494Semax
1667184610Salfred			switch (queue) {
1668184610Salfred			case NGM_UBT_NODE_QUEUE_CMD:
1669184610Salfred				q = &sc->sc_cmdq;
1670184610Salfred				break;
1671184610Salfred
1672184610Salfred			case NGM_UBT_NODE_QUEUE_ACL:
1673184610Salfred				q = &sc->sc_aclq;
1674184610Salfred				break;
1675184610Salfred
1676184610Salfred			case NGM_UBT_NODE_QUEUE_SCO:
1677184610Salfred				q = &sc->sc_scoq;
1678184610Salfred				break;
1679184610Salfred
1680184610Salfred			default:
1681184610Salfred				error = EINVAL;
1682187494Semax				goto done;
1683187494Semax				/* NOT REACHED */
1684187494Semax			}
1685187494Semax
1686187494Semax			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
1687187494Semax				M_NOWAIT);
1688187494Semax			if (rsp == NULL) {
1689187494Semax				error = ENOMEM;
1690184610Salfred				break;
1691184610Salfred			}
1692184610Salfred
1693187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
1694187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
1695184610Salfred			break;
1696184610Salfred
1697184610Salfred		case NGM_UBT_NODE_GET_STAT:
1698184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
1699184610Salfred			    M_NOWAIT);
1700187494Semax			if (rsp == NULL) {
1701184610Salfred				error = ENOMEM;
1702187494Semax				break;
1703184610Salfred			}
1704187494Semax
1705187494Semax			bcopy(&sc->sc_stat, rsp->data,
1706187494Semax				sizeof(ng_ubt_node_stat_ep));
1707184610Salfred			break;
1708184610Salfred
1709184610Salfred		case NGM_UBT_NODE_RESET_STAT:
1710187494Semax			UBT_STAT_RESET(sc);
1711184610Salfred			break;
1712184610Salfred
1713184610Salfred		default:
1714184610Salfred			error = EINVAL;
1715184610Salfred			break;
1716184610Salfred		}
1717184610Salfred		break;
1718184610Salfred
1719184610Salfred	default:
1720184610Salfred		error = EINVAL;
1721184610Salfred		break;
1722184610Salfred	}
1723187494Semaxdone:
1724184610Salfred	NG_RESPOND_MSG(error, node, item, rsp);
1725184610Salfred	NG_FREE_MSG(msg);
1726184610Salfred
1727184610Salfred	return (error);
1728187494Semax} /* ng_ubt_rcvmsg */
1729184610Salfred
1730184610Salfred/*
1731187494Semax * Process data.
1732187494Semax * Netgraph context.
1733184610Salfred */
1734184610Salfred
1735184610Salfredstatic int
1736184610Salfredng_ubt_rcvdata(hook_p hook, item_p item)
1737184610Salfred{
1738187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1739187494Semax	struct mbuf		*m;
1740187494Semax	struct ng_bt_mbufq	*q;
1741187494Semax	int			action, error = 0;
1742184610Salfred
1743184610Salfred	if (hook != sc->sc_hook) {
1744184610Salfred		error = EINVAL;
1745184610Salfred		goto done;
1746184610Salfred	}
1747187494Semax
1748187494Semax	/* Deatch mbuf and get HCI frame type */
1749184610Salfred	NGI_GET_M(item, m);
1750184610Salfred
1751187494Semax	/*
1752187494Semax	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
1753187494Semax	 * 2 bytes connection handle and at least 1 byte of length.
1754187494Semax	 * Panic on data frame that has size smaller than 4 bytes (it
1755187494Semax	 * should not happen)
1756187494Semax	 */
1757187494Semax
1758187494Semax	if (m->m_pkthdr.len < 4)
1759187494Semax		panic("HCI frame size is too small! pktlen=%d\n",
1760187494Semax			m->m_pkthdr.len);
1761187494Semax
1762187494Semax	/* Process HCI frame */
1763184610Salfred	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
1764184610Salfred	case NG_HCI_CMD_PKT:
1765233774Shselasky		if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
1766187494Semax			panic("HCI command frame size is too big! " \
1767187494Semax				"buffer size=%zd, packet len=%d\n",
1768187494Semax				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
1769187494Semax
1770184610Salfred		q = &sc->sc_cmdq;
1771187494Semax		action = UBT_FLAG_T_START_CTRL;
1772184610Salfred		break;
1773184610Salfred
1774184610Salfred	case NG_HCI_ACL_DATA_PKT:
1775187494Semax		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
1776187494Semax			panic("ACL data frame size is too big! " \
1777187494Semax				"buffer size=%d, packet len=%d\n",
1778187494Semax				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
1779187494Semax
1780184610Salfred		q = &sc->sc_aclq;
1781187494Semax		action = UBT_FLAG_T_START_BULK;
1782184610Salfred		break;
1783184610Salfred
1784184610Salfred	case NG_HCI_SCO_DATA_PKT:
1785184610Salfred		q = &sc->sc_scoq;
1786187494Semax		action = 0;
1787184610Salfred		break;
1788184610Salfred
1789184610Salfred	default:
1790187494Semax		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
1791187494Semax			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
1792184610Salfred
1793184610Salfred		NG_FREE_M(m);
1794184610Salfred		error = EINVAL;
1795184610Salfred		goto done;
1796187494Semax		/* NOT REACHED */
1797184610Salfred	}
1798184610Salfred
1799187741Semax	UBT_NG_LOCK(sc);
1800184610Salfred	if (NG_BT_MBUFQ_FULL(q)) {
1801187494Semax		NG_BT_MBUFQ_DROP(q);
1802187741Semax		UBT_NG_UNLOCK(sc);
1803187494Semax
1804187494Semax		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
1805187494Semax			*mtod(m, uint8_t *), m->m_pkthdr.len);
1806187494Semax
1807184610Salfred		NG_FREE_M(m);
1808184610Salfred	} else {
1809187494Semax		/* Loose HCI packet type, enqueue mbuf and kick off task */
1810187494Semax		m_adj(m, sizeof(uint8_t));
1811184610Salfred		NG_BT_MBUFQ_ENQUEUE(q, m);
1812187494Semax		ubt_task_schedule(sc, action);
1813187741Semax		UBT_NG_UNLOCK(sc);
1814184610Salfred	}
1815184610Salfreddone:
1816184610Salfred	NG_FREE_ITEM(item);
1817184610Salfred
1818187494Semax	return (error);
1819187494Semax} /* ng_ubt_rcvdata */
1820187494Semax
1821187494Semax/****************************************************************************
1822187494Semax ****************************************************************************
1823187494Semax **                              Module
1824187494Semax ****************************************************************************
1825187494Semax ****************************************************************************/
1826187494Semax
1827187494Semax/*
1828187494Semax * Load/Unload the driver module
1829187494Semax */
1830187494Semax
1831187494Semaxstatic int
1832187494Semaxubt_modevent(module_t mod, int event, void *data)
1833187494Semax{
1834187494Semax	int	error;
1835187494Semax
1836187494Semax	switch (event) {
1837187494Semax	case MOD_LOAD:
1838187494Semax		error = ng_newtype(&typestruct);
1839187494Semax		if (error != 0)
1840187494Semax			printf("%s: Could not register Netgraph node type, " \
1841187494Semax				"error=%d\n", NG_UBT_NODE_TYPE, error);
1842187494Semax		break;
1843187494Semax
1844187494Semax	case MOD_UNLOAD:
1845187494Semax		error = ng_rmtype(&typestruct);
1846187494Semax		break;
1847187494Semax
1848187494Semax	default:
1849187494Semax		error = EOPNOTSUPP;
1850187494Semax		break;
1851184610Salfred	}
1852187494Semax
1853184610Salfred	return (error);
1854187494Semax} /* ubt_modevent */
1855187494Semax
1856187494Semaxstatic devclass_t	ubt_devclass;
1857187494Semax
1858187494Semaxstatic device_method_t	ubt_methods[] =
1859187494Semax{
1860187494Semax	DEVMETHOD(device_probe,	ubt_probe),
1861187494Semax	DEVMETHOD(device_attach, ubt_attach),
1862187494Semax	DEVMETHOD(device_detach, ubt_detach),
1863244715Srakuco	DEVMETHOD_END
1864187494Semax};
1865187494Semax
1866187494Semaxstatic driver_t		ubt_driver =
1867187494Semax{
1868187494Semax	.name =	   "ubt",
1869187494Semax	.methods = ubt_methods,
1870187494Semax	.size =	   sizeof(struct ubt_softc),
1871187494Semax};
1872187494Semax
1873189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
1874187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
1875187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
1876187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
1877188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
1878187494Semax
1879