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) },
389184610Salfred};
390184610Salfred
391184610Salfred/* List of supported bluetooth devices */
392223486Shselaskystatic const STRUCT_USB_HOST_ID ubt_devs[] =
393187741Semax{
394187494Semax	/* Generic Bluetooth class devices */
395187494Semax	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
396187494Semax	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
397187494Semax	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
398184610Salfred
399184610Salfred	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
400187494Semax	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
401244956Shselasky
402244956Shselasky	/* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
403244956Shselasky	{ USB_VENDOR(USB_VENDOR_BROADCOM),
404244956Shselasky	  USB_IFACE_CLASS(UICLASS_VENDOR),
405244956Shselasky	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
406244956Shselasky	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
407255345Shselasky
408255345Shselasky	/* Apple-specific (Broadcom) devices */
409255345Shselasky	{ USB_VENDOR(USB_VENDOR_APPLE),
410255345Shselasky	  USB_IFACE_CLASS(UICLASS_VENDOR),
411255345Shselasky	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
412255345Shselasky	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
413255345Shselasky
414255345Shselasky	/* Foxconn - Hon Hai */
415255345Shselasky	{ USB_VENDOR(USB_VENDOR_FOXCONN),
416255345Shselasky	  USB_IFACE_CLASS(UICLASS_VENDOR),
417255345Shselasky	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
418255345Shselasky	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
419255345Shselasky
420255345Shselasky	/* MediaTek MT76x0E */
421255345Shselasky	{ USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },
422255345Shselasky
423255345Shselasky	/* Broadcom SoftSailing reporting vendor specific */
424255345Shselasky	{ USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },
425255345Shselasky
426255345Shselasky	/* Apple MacBookPro 7,1 */
427255345Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },
428255345Shselasky
429255345Shselasky	/* Apple iMac11,1 */
430255345Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },
431255345Shselasky
432255345Shselasky	/* Apple MacBookPro6,2 */
433255345Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },
434255345Shselasky
435255345Shselasky	/* Apple MacBookAir3,1, MacBookAir3,2 */
436255345Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },
437255345Shselasky
438255345Shselasky	/* Apple MacBookAir4,1 */
439255345Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },
440255345Shselasky
441255345Shselasky	/* MacBookAir6,1 */
442255345Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },
443255345Shselasky
444255345Shselasky	/* Apple MacBookPro8,2 */
445255345Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },
446255345Shselasky
447255345Shselasky	/* Apple MacMini5,1 */
448255345Shselasky	{ USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },
449255345Shselasky
450255345Shselasky	/* Bluetooth Ultraport Module from IBM */
451255345Shselasky	{ USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },
452255345Shselasky
453255345Shselasky	/* ALPS Modules with non-standard ID */
454255345Shselasky	{ USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },
455255345Shselasky	{ USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },
456255345Shselasky
457255345Shselasky	{ USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },
458255345Shselasky
459255345Shselasky	/* Canyon CN-BTU1 with HID interfaces */
460255345Shselasky	{ USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },
461255345Shselasky
462255345Shselasky	/* Broadcom BCM20702A0 */
463255345Shselasky	{ USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },
464255345Shselasky	{ USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },
465255345Shselasky	{ USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },
466255345Shselasky	{ USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },
467255345Shselasky	{ USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
468184610Salfred};
469184610Salfred
470184610Salfred/*
471187494Semax * Probe for a USB Bluetooth device.
472187494Semax * USB context.
473184610Salfred */
474184610Salfred
475184610Salfredstatic int
476184610Salfredubt_probe(device_t dev)
477184610Salfred{
478192984Sthompsa	struct usb_attach_arg	*uaa = device_get_ivars(dev);
479222055Savg	int error;
480184610Salfred
481192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
482184610Salfred		return (ENXIO);
483187494Semax
484187494Semax	if (uaa->info.bIfaceIndex != 0)
485184610Salfred		return (ENXIO);
486187494Semax
487194228Sthompsa	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
488187494Semax			sizeof(ubt_ignore_devs), uaa) == 0)
489184610Salfred		return (ENXIO);
490187494Semax
491222055Savg	error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
492222055Savg	if (error == 0)
493222055Savg		return (BUS_PROBE_GENERIC);
494222055Savg	return (error);
495187494Semax} /* ubt_probe */
496184610Salfred
497184610Salfred/*
498187494Semax * Attach the device.
499187494Semax * USB context.
500184610Salfred */
501184610Salfred
502184610Salfredstatic int
503184610Salfredubt_attach(device_t dev)
504184610Salfred{
505192984Sthompsa	struct usb_attach_arg		*uaa = device_get_ivars(dev);
506187494Semax	struct ubt_softc		*sc = device_get_softc(dev);
507192984Sthompsa	struct usb_endpoint_descriptor	*ed;
508192984Sthompsa	struct usb_interface_descriptor *id;
509187494Semax	uint16_t			wMaxPacketSize;
510187741Semax	uint8_t				alt_index, i, j;
511187741Semax	uint8_t				iface_index[2] = { 0, 1 };
512184610Salfred
513194228Sthompsa	device_set_usb_desc(dev);
514184610Salfred
515187741Semax	sc->sc_dev = dev;
516187741Semax	sc->sc_debug = NG_UBT_WARN_LEVEL;
517184610Salfred
518187494Semax	/*
519187494Semax	 * Create Netgraph node
520187494Semax	 */
521187494Semax
522187494Semax	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
523187741Semax		UBT_ALERT(sc, "could not create Netgraph node\n");
524187494Semax		return (ENXIO);
525187494Semax	}
526187494Semax
527187494Semax	/* Name Netgraph node */
528187741Semax	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
529187741Semax		UBT_ALERT(sc, "could not name Netgraph node\n");
530187494Semax		NG_NODE_UNREF(sc->sc_node);
531187494Semax		return (ENXIO);
532187494Semax	}
533187494Semax	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
534187494Semax	NG_NODE_FORCE_WRITER(sc->sc_node);
535187494Semax
536184610Salfred	/*
537184610Salfred	 * Initialize device softc structure
538184610Salfred	 */
539184610Salfred
540187494Semax	/* initialize locks */
541187741Semax	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
542187741Semax	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
543187494Semax
544187494Semax	/* initialize packet queues */
545184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
546184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
547187494Semax	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
548184610Salfred
549187494Semax	/* initialize glue task */
550187741Semax	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
551184610Salfred
552184610Salfred	/*
553184610Salfred	 * Configure Bluetooth USB device. Discover all required USB
554184610Salfred	 * interfaces and endpoints.
555184610Salfred	 *
556184610Salfred	 * USB device must present two interfaces:
557184610Salfred	 * 1) Interface 0 that has 3 endpoints
558184610Salfred	 *	1) Interrupt endpoint to receive HCI events
559184610Salfred	 *	2) Bulk IN endpoint to receive ACL data
560184610Salfred	 *	3) Bulk OUT endpoint to send ACL data
561184610Salfred	 *
562184610Salfred	 * 2) Interface 1 then has 2 endpoints
563184610Salfred	 *	1) Isochronous IN endpoint to receive SCO data
564184610Salfred 	 *	2) Isochronous OUT endpoint to send SCO data
565184610Salfred	 *
566184610Salfred	 * Interface 1 (with isochronous endpoints) has several alternate
567184610Salfred	 * configurations with different packet size.
568184610Salfred	 */
569184610Salfred
570184610Salfred	/*
571187741Semax	 * For interface #1 search alternate settings, and find
572187741Semax	 * the descriptor with the largest wMaxPacketSize
573184610Salfred	 */
574184610Salfred
575184610Salfred	wMaxPacketSize = 0;
576187494Semax	alt_index = 0;
577184610Salfred	i = 0;
578184610Salfred	j = 0;
579190728Sthompsa	ed = NULL;
580187494Semax
581190728Sthompsa	/*
582190728Sthompsa	 * Search through all the descriptors looking for the largest
583190728Sthompsa	 * packet size:
584190728Sthompsa	 */
585194228Sthompsa	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
586194228Sthompsa	    usbd_get_config_descriptor(uaa->device),
587192984Sthompsa	    (struct usb_descriptor *)ed))) {
588184610Salfred
589190728Sthompsa		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
590190728Sthompsa		    (ed->bLength >= sizeof(*id))) {
591192984Sthompsa			id = (struct usb_interface_descriptor *)ed;
592190728Sthompsa			i = id->bInterfaceNumber;
593190728Sthompsa			j = id->bAlternateSetting;
594184610Salfred		}
595187494Semax
596190728Sthompsa		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
597190728Sthompsa		    (ed->bLength >= sizeof(*ed)) &&
598190728Sthompsa		    (i == 1)) {
599190728Sthompsa			uint16_t temp;
600190728Sthompsa
601190728Sthompsa			temp = UGETW(ed->wMaxPacketSize);
602190728Sthompsa			if (temp > wMaxPacketSize) {
603190728Sthompsa				wMaxPacketSize = temp;
604190728Sthompsa				alt_index = j;
605190728Sthompsa			}
606184610Salfred		}
607184610Salfred	}
608184610Salfred
609187741Semax	/* Set alt configuration on interface #1 only if we found it */
610187494Semax	if (wMaxPacketSize > 0 &&
611194228Sthompsa	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
612187741Semax		UBT_ALERT(sc, "could not set alternate setting %d " \
613187494Semax			"for interface 1!\n", alt_index);
614184610Salfred		goto detach;
615184610Salfred	}
616184610Salfred
617187741Semax	/* Setup transfers for both interfaces */
618194228Sthompsa	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
619187741Semax			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
620187741Semax		UBT_ALERT(sc, "could not allocate transfers\n");
621184610Salfred		goto detach;
622184610Salfred	}
623184610Salfred
624187494Semax	/* Claim all interfaces on the device */
625194228Sthompsa	for (i = 1; usbd_get_iface(uaa->device, i) != NULL; i ++)
626194228Sthompsa		usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
627184610Salfred
628187494Semax	return (0); /* success */
629184610Salfred
630184610Salfreddetach:
631184610Salfred	ubt_detach(dev);
632184610Salfred
633184610Salfred	return (ENXIO);
634187494Semax} /* ubt_attach */
635184610Salfred
636184610Salfred/*
637187494Semax * Detach the device.
638187494Semax * USB context.
639184610Salfred */
640184610Salfred
641184610Salfredint
642184610Salfredubt_detach(device_t dev)
643184610Salfred{
644187494Semax	struct ubt_softc	*sc = device_get_softc(dev);
645187494Semax	node_p			node = sc->sc_node;
646184610Salfred
647187494Semax	/* Destroy Netgraph node */
648187494Semax	if (node != NULL) {
649187494Semax		sc->sc_node = NULL;
650187494Semax		NG_NODE_REALLY_DIE(node);
651187494Semax		ng_rmnode_self(node);
652184610Salfred	}
653184610Salfred
654187741Semax	/* Make sure ubt_task in gone */
655187741Semax	taskqueue_drain(taskqueue_swi, &sc->sc_task);
656187741Semax
657187494Semax	/* Free USB transfers, if any */
658194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
659184610Salfred
660187494Semax	/* Destroy queues */
661187741Semax	UBT_NG_LOCK(sc);
662184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
663184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
664184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
665187741Semax	UBT_NG_UNLOCK(sc);
666184610Salfred
667187741Semax	mtx_destroy(&sc->sc_if_mtx);
668187741Semax	mtx_destroy(&sc->sc_ng_mtx);
669187494Semax
670184610Salfred	return (0);
671187494Semax} /* ubt_detach */
672184610Salfred
673187494Semax/*
674187494Semax * Called when outgoing control request (HCI command) has completed, i.e.
675187494Semax * HCI command was sent to the device.
676187494Semax * USB context.
677187494Semax */
678187494Semax
679184610Salfredstatic void
680194677Sthompsaubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
681184610Salfred{
682194677Sthompsa	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
683192984Sthompsa	struct usb_device_request	req;
684187494Semax	struct mbuf			*m;
685194677Sthompsa	struct usb_page_cache		*pc;
686194677Sthompsa	int				actlen;
687184610Salfred
688194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
689194677Sthompsa
690184610Salfred	switch (USB_GET_STATE(xfer)) {
691184610Salfred	case USB_ST_TRANSFERRED:
692194677Sthompsa		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
693194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
694187741Semax		UBT_STAT_PCKTS_SENT(sc);
695187494Semax		/* FALLTHROUGH */
696184610Salfred
697184610Salfred	case USB_ST_SETUP:
698187494Semaxsend_next:
699187494Semax		/* Get next command mbuf, if any */
700187741Semax		UBT_NG_LOCK(sc);
701184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
702187741Semax		UBT_NG_UNLOCK(sc);
703184610Salfred
704184610Salfred		if (m == NULL) {
705187494Semax			UBT_INFO(sc, "HCI command queue is empty\n");
706187741Semax			break;	/* transfer complete */
707184610Salfred		}
708184610Salfred
709187494Semax		/* Initialize a USB control request and then schedule it */
710184610Salfred		bzero(&req, sizeof(req));
711184610Salfred		req.bmRequestType = UBT_HCI_REQUEST;
712184610Salfred		USETW(req.wLength, m->m_pkthdr.len);
713184610Salfred
714187494Semax		UBT_INFO(sc, "Sending control request, " \
715187494Semax			"bmRequestType=0x%02x, wLength=%d\n",
716187494Semax			req.bmRequestType, UGETW(req.wLength));
717184610Salfred
718194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
719194677Sthompsa		usbd_copy_in(pc, 0, &req, sizeof(req));
720194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 1);
721194677Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
722184610Salfred
723194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
724194677Sthompsa		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
725194677Sthompsa		usbd_xfer_set_frames(xfer, 2);
726184610Salfred
727184610Salfred		NG_FREE_M(m);
728184610Salfred
729194228Sthompsa		usbd_transfer_submit(xfer);
730187494Semax		break;
731184610Salfred
732187494Semax	default: /* Error */
733194677Sthompsa		if (error != USB_ERR_CANCELLED) {
734187494Semax			UBT_WARN(sc, "control transfer failed: %s\n",
735194677Sthompsa				usbd_errstr(error));
736187494Semax
737187494Semax			UBT_STAT_OERROR(sc);
738187494Semax			goto send_next;
739184610Salfred		}
740187494Semax
741187741Semax		/* transfer cancelled */
742187494Semax		break;
743184610Salfred	}
744187494Semax} /* ubt_ctrl_write_callback */
745184610Salfred
746187494Semax/*
747187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e.
748187494Semax * HCI event was received from the device.
749187494Semax * USB context.
750187494Semax */
751187494Semax
752184610Salfredstatic void
753194677Sthompsaubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
754184610Salfred{
755194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
756187494Semax	struct mbuf		*m;
757187494Semax	ng_hci_event_pkt_t	*hdr;
758194677Sthompsa	struct usb_page_cache	*pc;
759194677Sthompsa	int			actlen;
760184610Salfred
761194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
762194677Sthompsa
763187494Semax	m = NULL;
764187494Semax
765184610Salfred	switch (USB_GET_STATE(xfer)) {
766184610Salfred	case USB_ST_TRANSFERRED:
767187494Semax		/* Allocate a new mbuf */
768184610Salfred		MGETHDR(m, M_DONTWAIT, MT_DATA);
769184610Salfred		if (m == NULL) {
770187494Semax			UBT_STAT_IERROR(sc);
771187494Semax			goto submit_next;
772184610Salfred		}
773187494Semax
774184610Salfred		MCLGET(m, M_DONTWAIT);
775184610Salfred		if (!(m->m_flags & M_EXT)) {
776187494Semax			UBT_STAT_IERROR(sc);
777187494Semax			goto submit_next;
778184610Salfred		}
779187494Semax
780187494Semax		/* Add HCI packet type */
781187494Semax		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
782187494Semax		m->m_pkthdr.len = m->m_len = 1;
783187494Semax
784194677Sthompsa		if (actlen > MCLBYTES - 1)
785194677Sthompsa			actlen = MCLBYTES - 1;
786187494Semax
787194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
788194677Sthompsa		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
789194677Sthompsa		m->m_pkthdr.len += actlen;
790194677Sthompsa		m->m_len += actlen;
791187494Semax
792187494Semax		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
793194677Sthompsa			actlen);
794187494Semax
795187494Semax		/* Validate packet and send it up the stack */
796235000Shselasky		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
797187494Semax			UBT_INFO(sc, "HCI event packet is too short\n");
798187494Semax
799187494Semax			UBT_STAT_IERROR(sc);
800187494Semax			goto submit_next;
801184610Salfred		}
802184610Salfred
803187494Semax		hdr = mtod(m, ng_hci_event_pkt_t *);
804187494Semax		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
805187494Semax			UBT_ERR(sc, "Invalid HCI event packet size, " \
806187494Semax				"length=%d, pktlen=%d\n",
807187494Semax				hdr->length, m->m_pkthdr.len);
808184610Salfred
809187494Semax			UBT_STAT_IERROR(sc);
810187494Semax			goto submit_next;
811184610Salfred		}
812184610Salfred
813187494Semax		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
814187494Semax			"length=%d\n", m->m_pkthdr.len, hdr->length);
815184610Salfred
816187494Semax		UBT_STAT_PCKTS_RECV(sc);
817187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
818184610Salfred
819187741Semax		ubt_fwd_mbuf_up(sc, &m);
820187494Semax		/* m == NULL at this point */
821187494Semax		/* FALLTHROUGH */
822184610Salfred
823184610Salfred	case USB_ST_SETUP:
824187494Semaxsubmit_next:
825187494Semax		NG_FREE_M(m); /* checks for m != NULL */
826184610Salfred
827194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
828194228Sthompsa		usbd_transfer_submit(xfer);
829187494Semax		break;
830184610Salfred
831187494Semax	default: /* Error */
832194677Sthompsa		if (error != USB_ERR_CANCELLED) {
833187494Semax			UBT_WARN(sc, "interrupt transfer failed: %s\n",
834194677Sthompsa				usbd_errstr(error));
835184610Salfred
836187494Semax			/* Try to clear stall first */
837194677Sthompsa			usbd_xfer_set_stall(xfer);
838187741Semax			goto submit_next;
839187741Semax		}
840187741Semax			/* transfer cancelled */
841187494Semax		break;
842184610Salfred	}
843187494Semax} /* ubt_intr_read_callback */
844184610Salfred
845187494Semax/*
846187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e.
847187494Semax * ACL packet was received from the device.
848187494Semax * USB context.
849187494Semax */
850187494Semax
851184610Salfredstatic void
852194677Sthompsaubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
853184610Salfred{
854194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
855187494Semax	struct mbuf		*m;
856187494Semax	ng_hci_acldata_pkt_t	*hdr;
857194677Sthompsa	struct usb_page_cache	*pc;
858235000Shselasky	int len;
859235000Shselasky	int actlen;
860184610Salfred
861194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
862194677Sthompsa
863187494Semax	m = NULL;
864184610Salfred
865184610Salfred	switch (USB_GET_STATE(xfer)) {
866184610Salfred	case USB_ST_TRANSFERRED:
867187494Semax		/* Allocate new mbuf */
868184610Salfred		MGETHDR(m, M_DONTWAIT, MT_DATA);
869184610Salfred		if (m == NULL) {
870187494Semax			UBT_STAT_IERROR(sc);
871187494Semax			goto submit_next;
872184610Salfred		}
873187494Semax
874184610Salfred		MCLGET(m, M_DONTWAIT);
875184610Salfred		if (!(m->m_flags & M_EXT)) {
876187494Semax			UBT_STAT_IERROR(sc);
877187494Semax			goto submit_next;
878184610Salfred		}
879184610Salfred
880187494Semax		/* Add HCI packet type */
881187494Semax		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
882187494Semax		m->m_pkthdr.len = m->m_len = 1;
883184610Salfred
884194677Sthompsa		if (actlen > MCLBYTES - 1)
885194677Sthompsa			actlen = MCLBYTES - 1;
886184610Salfred
887194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
888194677Sthompsa		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
889194677Sthompsa		m->m_pkthdr.len += actlen;
890194677Sthompsa		m->m_len += actlen;
891184610Salfred
892187494Semax		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
893194677Sthompsa			actlen);
894184610Salfred
895187494Semax		/* Validate packet and send it up the stack */
896235000Shselasky		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
897187494Semax			UBT_INFO(sc, "HCI ACL packet is too short\n");
898184610Salfred
899187494Semax			UBT_STAT_IERROR(sc);
900187494Semax			goto submit_next;
901184610Salfred		}
902184610Salfred
903187494Semax		hdr = mtod(m, ng_hci_acldata_pkt_t *);
904187494Semax		len = le16toh(hdr->length);
905235000Shselasky		if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
906187494Semax			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
907187494Semax				"pktlen=%d\n", len, m->m_pkthdr.len);
908184610Salfred
909187494Semax			UBT_STAT_IERROR(sc);
910187494Semax			goto submit_next;
911184610Salfred		}
912184610Salfred
913187494Semax		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
914187494Semax			"length=%d\n", m->m_pkthdr.len, len);
915184610Salfred
916187494Semax		UBT_STAT_PCKTS_RECV(sc);
917187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
918184610Salfred
919187741Semax		ubt_fwd_mbuf_up(sc, &m);
920187494Semax		/* m == NULL at this point */
921187494Semax		/* FALLTHOUGH */
922184610Salfred
923187494Semax	case USB_ST_SETUP:
924187494Semaxsubmit_next:
925187494Semax		NG_FREE_M(m); /* checks for m != NULL */
926184610Salfred
927194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
928194228Sthompsa		usbd_transfer_submit(xfer);
929187494Semax		break;
930184610Salfred
931187494Semax	default: /* Error */
932194677Sthompsa		if (error != USB_ERR_CANCELLED) {
933187494Semax			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
934194677Sthompsa				usbd_errstr(error));
935184610Salfred
936187494Semax			/* Try to clear stall first */
937194677Sthompsa			usbd_xfer_set_stall(xfer);
938187741Semax			goto submit_next;
939187741Semax		}
940187741Semax			/* transfer cancelled */
941187494Semax		break;
942187494Semax	}
943187494Semax} /* ubt_bulk_read_callback */
944184610Salfred
945187494Semax/*
946187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
947187494Semax * ACL packet was sent to the device.
948187494Semax * USB context.
949187494Semax */
950184610Salfred
951184610Salfredstatic void
952194677Sthompsaubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
953184610Salfred{
954194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
955187494Semax	struct mbuf		*m;
956194677Sthompsa	struct usb_page_cache	*pc;
957194677Sthompsa	int			actlen;
958184610Salfred
959194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
960194677Sthompsa
961184610Salfred	switch (USB_GET_STATE(xfer)) {
962184610Salfred	case USB_ST_TRANSFERRED:
963194677Sthompsa		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
964194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
965187741Semax		UBT_STAT_PCKTS_SENT(sc);
966187494Semax		/* FALLTHROUGH */
967184610Salfred
968187494Semax	case USB_ST_SETUP:
969187741Semaxsend_next:
970187494Semax		/* Get next mbuf, if any */
971187741Semax		UBT_NG_LOCK(sc);
972184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
973187741Semax		UBT_NG_UNLOCK(sc);
974184610Salfred
975184610Salfred		if (m == NULL) {
976187494Semax			UBT_INFO(sc, "ACL data queue is empty\n");
977187741Semax			break; /* transfer completed */
978184610Salfred		}
979187494Semax
980184610Salfred		/*
981187494Semax		 * Copy ACL data frame back to a linear USB transfer buffer
982187494Semax		 * and schedule transfer
983184610Salfred		 */
984184610Salfred
985194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
986194677Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
987194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
988184610Salfred
989187494Semax		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
990187494Semax			m->m_pkthdr.len);
991184610Salfred
992184610Salfred		NG_FREE_M(m);
993184610Salfred
994194228Sthompsa		usbd_transfer_submit(xfer);
995187494Semax		break;
996184610Salfred
997187494Semax	default: /* Error */
998194677Sthompsa		if (error != USB_ERR_CANCELLED) {
999187494Semax			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
1000194677Sthompsa				usbd_errstr(error));
1001184610Salfred
1002187494Semax			UBT_STAT_OERROR(sc);
1003184610Salfred
1004184610Salfred			/* try to clear stall first */
1005194677Sthompsa			usbd_xfer_set_stall(xfer);
1006187741Semax			goto send_next;
1007187741Semax		}
1008187741Semax			/* transfer cancelled */
1009187494Semax		break;
1010184610Salfred	}
1011187494Semax} /* ubt_bulk_write_callback */
1012184610Salfred
1013187494Semax/*
1014187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e.
1015187494Semax * SCO packet was received from the device.
1016187494Semax * USB context.
1017187494Semax */
1018187494Semax
1019184610Salfredstatic void
1020194677Sthompsaubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
1021184610Salfred{
1022194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1023187494Semax	int			n;
1024194677Sthompsa	int actlen, nframes;
1025184610Salfred
1026194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1027194677Sthompsa
1028187494Semax	switch (USB_GET_STATE(xfer)) {
1029187494Semax	case USB_ST_TRANSFERRED:
1030194677Sthompsa		for (n = 0; n < nframes; n ++)
1031187494Semax			if (ubt_isoc_read_one_frame(xfer, n) < 0)
1032187494Semax				break;
1033187494Semax		/* FALLTHROUGH */
1034184610Salfred
1035187494Semax	case USB_ST_SETUP:
1036187494Semaxread_next:
1037194677Sthompsa		for (n = 0; n < nframes; n ++)
1038194677Sthompsa			usbd_xfer_set_frame_len(xfer, n,
1039194677Sthompsa			    usbd_xfer_max_framelen(xfer));
1040184610Salfred
1041194228Sthompsa		usbd_transfer_submit(xfer);
1042187494Semax		break;
1043184610Salfred
1044187494Semax	default: /* Error */
1045194677Sthompsa                if (error != USB_ERR_CANCELLED) {
1046187494Semax                        UBT_STAT_IERROR(sc);
1047187494Semax                        goto read_next;
1048187494Semax                }
1049184610Salfred
1050187741Semax		/* transfer cancelled */
1051187494Semax		break;
1052187494Semax	}
1053187494Semax} /* ubt_isoc_read_callback */
1054184610Salfred
1055187494Semax/*
1056187494Semax * Helper function. Called from ubt_isoc_read_callback() to read
1057187494Semax * SCO data from one frame.
1058187494Semax * USB context.
1059187494Semax */
1060184610Salfred
1061187494Semaxstatic int
1062192984Sthompsaubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
1063187494Semax{
1064194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1065194677Sthompsa	struct usb_page_cache	*pc;
1066187494Semax	struct mbuf		*m;
1067194677Sthompsa	int			len, want, got, total;
1068184610Salfred
1069187494Semax	/* Get existing SCO reassembly buffer */
1070194677Sthompsa	pc = usbd_xfer_get_frame(xfer, 0);
1071187494Semax	m = sc->sc_isoc_in_buffer;
1072194682Sthompsa	total = usbd_xfer_frame_len(xfer, frame_no);
1073184610Salfred
1074187494Semax	/* While we have data in the frame */
1075194677Sthompsa	while (total > 0) {
1076187494Semax		if (m == NULL) {
1077187494Semax			/* Start new reassembly buffer */
1078187494Semax			MGETHDR(m, M_DONTWAIT, MT_DATA);
1079187494Semax			if (m == NULL) {
1080187494Semax				UBT_STAT_IERROR(sc);
1081187494Semax				return (-1);	/* XXX out of sync! */
1082187494Semax			}
1083184610Salfred
1084187494Semax			MCLGET(m, M_DONTWAIT);
1085187494Semax			if (!(m->m_flags & M_EXT)) {
1086187494Semax				UBT_STAT_IERROR(sc);
1087187494Semax				NG_FREE_M(m);
1088187494Semax				return (-1);	/* XXX out of sync! */
1089184610Salfred			}
1090184610Salfred
1091187494Semax			/* Expect SCO header */
1092187494Semax			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
1093187494Semax			m->m_pkthdr.len = m->m_len = got = 1;
1094187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
1095187494Semax		} else {
1096187494Semax			/*
1097187494Semax			 * Check if we have SCO header and if so
1098187494Semax			 * adjust amount of data we want
1099187494Semax			 */
1100187494Semax			got = m->m_pkthdr.len;
1101187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
1102184610Salfred
1103187494Semax			if (got >= want)
1104187494Semax				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
1105184610Salfred		}
1106184610Salfred
1107187494Semax		/* Append frame data to the SCO reassembly buffer */
1108194677Sthompsa		len = total;
1109187494Semax		if (got + len > want)
1110187494Semax			len = want - got;
1111184610Salfred
1112194677Sthompsa		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
1113187494Semax			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
1114184610Salfred
1115187494Semax		m->m_pkthdr.len += len;
1116187494Semax		m->m_len += len;
1117194677Sthompsa		total -= len;
1118184610Salfred
1119187494Semax		/* Check if we got everything we wanted, if not - continue */
1120187494Semax		if (got != want)
1121187494Semax			continue;
1122184610Salfred
1123187494Semax		/* If we got here then we got complete SCO frame */
1124187494Semax		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
1125187494Semax			"length=%d\n", m->m_pkthdr.len,
1126187494Semax			mtod(m, ng_hci_scodata_pkt_t *)->length);
1127184610Salfred
1128187494Semax		UBT_STAT_PCKTS_RECV(sc);
1129187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
1130184610Salfred
1131187741Semax		ubt_fwd_mbuf_up(sc, &m);
1132187494Semax		/* m == NULL at this point */
1133187494Semax	}
1134184610Salfred
1135187494Semax	/* Put SCO reassembly buffer back */
1136187494Semax	sc->sc_isoc_in_buffer = m;
1137184610Salfred
1138187494Semax	return (0);
1139187494Semax} /* ubt_isoc_read_one_frame */
1140184610Salfred
1141187494Semax/*
1142187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
1143187494Semax * SCO packet was sent to the device.
1144187494Semax * USB context.
1145187494Semax */
1146184610Salfred
1147184610Salfredstatic void
1148194677Sthompsaubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
1149184610Salfred{
1150194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1151194677Sthompsa	struct usb_page_cache	*pc;
1152187494Semax	struct mbuf		*m;
1153187494Semax	int			n, space, offset;
1154194677Sthompsa	int			actlen, nframes;
1155184610Salfred
1156194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1157194677Sthompsa	pc = usbd_xfer_get_frame(xfer, 0);
1158194677Sthompsa
1159184610Salfred	switch (USB_GET_STATE(xfer)) {
1160184610Salfred	case USB_ST_TRANSFERRED:
1161194677Sthompsa		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1162194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
1163187741Semax		UBT_STAT_PCKTS_SENT(sc);
1164187494Semax		/* FALLTHROUGH */
1165184610Salfred
1166184610Salfred	case USB_ST_SETUP:
1167187494Semaxsend_next:
1168184610Salfred		offset = 0;
1169194677Sthompsa		space = usbd_xfer_max_framelen(xfer) * nframes;
1170187494Semax		m = NULL;
1171184610Salfred
1172187494Semax		while (space > 0) {
1173187494Semax			if (m == NULL) {
1174187741Semax				UBT_NG_LOCK(sc);
1175187494Semax				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
1176187741Semax				UBT_NG_UNLOCK(sc);
1177184610Salfred
1178187494Semax				if (m == NULL)
1179187494Semax					break;
1180187494Semax			}
1181184610Salfred
1182187494Semax			n = min(space, m->m_pkthdr.len);
1183187494Semax			if (n > 0) {
1184194677Sthompsa				usbd_m_copy_in(pc, offset, m,0, n);
1185187494Semax				m_adj(m, n);
1186184610Salfred
1187187494Semax				offset += n;
1188187494Semax				space -= n;
1189187494Semax			}
1190184610Salfred
1191187494Semax			if (m->m_pkthdr.len == 0)
1192187494Semax				NG_FREE_M(m); /* sets m = NULL */
1193187494Semax		}
1194184610Salfred
1195187494Semax		/* Put whatever is left from mbuf back on queue */
1196187494Semax		if (m != NULL) {
1197187741Semax			UBT_NG_LOCK(sc);
1198187494Semax			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
1199187741Semax			UBT_NG_UNLOCK(sc);
1200184610Salfred		}
1201184610Salfred
1202187494Semax		/*
1203187494Semax		 * Calculate sizes for isoc frames.
1204187494Semax		 * Note that offset could be 0 at this point (i.e. we have
1205187494Semax		 * nothing to send). That is fine, as we have isoc. transfers
1206187494Semax		 * going in both directions all the time. In this case it
1207187494Semax		 * would be just empty isoc. transfer.
1208187494Semax		 */
1209187494Semax
1210194677Sthompsa		for (n = 0; n < nframes; n ++) {
1211194677Sthompsa			usbd_xfer_set_frame_len(xfer, n,
1212194677Sthompsa			    min(offset, usbd_xfer_max_framelen(xfer)));
1213194682Sthompsa			offset -= usbd_xfer_frame_len(xfer, n);
1214187494Semax		}
1215187494Semax
1216194228Sthompsa		usbd_transfer_submit(xfer);
1217187494Semax		break;
1218184610Salfred
1219187494Semax	default: /* Error */
1220194677Sthompsa		if (error != USB_ERR_CANCELLED) {
1221187494Semax			UBT_STAT_OERROR(sc);
1222187494Semax			goto send_next;
1223184610Salfred		}
1224187494Semax
1225187741Semax		/* transfer cancelled */
1226187494Semax		break;
1227184610Salfred	}
1228184610Salfred}
1229184610Salfred
1230187741Semax/*
1231187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack).
1232187741Semax * Modifies value of the mbuf pointer (sets it to NULL).
1233187741Semax * Save to call from any context.
1234187741Semax */
1235187741Semax
1236187741Semaxstatic int
1237187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
1238187741Semax{
1239187741Semax	hook_p	hook;
1240187741Semax	int	error;
1241187741Semax
1242187741Semax	/*
1243187741Semax	 * Close the race with Netgraph hook newhook/disconnect methods.
1244187741Semax	 * Save the hook pointer atomically. Two cases are possible:
1245187741Semax	 *
1246187741Semax	 * 1) The hook pointer is NULL. It means disconnect method got
1247187741Semax	 *    there first. In this case we are done.
1248187741Semax	 *
1249187741Semax	 * 2) The hook pointer is not NULL. It means that hook pointer
1250187741Semax	 *    could be either in valid or invalid (i.e. in the process
1251187741Semax	 *    of disconnect) state. In any case grab an extra reference
1252187741Semax	 *    to protect the hook pointer.
1253187741Semax	 *
1254187741Semax	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
1255187741Semax	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
1256187741Semax	 */
1257187741Semax
1258187741Semax	UBT_NG_LOCK(sc);
1259187741Semax	if ((hook = sc->sc_hook) != NULL)
1260187741Semax		NG_HOOK_REF(hook);
1261187741Semax	UBT_NG_UNLOCK(sc);
1262187741Semax
1263187741Semax	if (hook == NULL) {
1264187741Semax		NG_FREE_M(*m);
1265187741Semax		return (ENETDOWN);
1266187741Semax	}
1267187741Semax
1268187741Semax	NG_SEND_DATA_ONLY(error, hook, *m);
1269187741Semax	NG_HOOK_UNREF(hook);
1270187741Semax
1271187741Semax	if (error != 0)
1272187741Semax		UBT_STAT_IERROR(sc);
1273187741Semax
1274187741Semax	return (error);
1275187741Semax} /* ubt_fwd_mbuf_up */
1276187741Semax
1277184610Salfred/****************************************************************************
1278184610Salfred ****************************************************************************
1279187494Semax **                                 Glue
1280184610Salfred ****************************************************************************
1281184610Salfred ****************************************************************************/
1282184610Salfred
1283184610Salfred/*
1284187741Semax * Schedule glue task. Should be called with sc_ng_mtx held.
1285187494Semax * Netgraph context.
1286184610Salfred */
1287184610Salfred
1288187741Semaxstatic void
1289187494Semaxubt_task_schedule(ubt_softc_p sc, int action)
1290184610Salfred{
1291187741Semax	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
1292184610Salfred
1293187741Semax	/*
1294187741Semax	 * Try to handle corner case when "start all" and "stop all"
1295187741Semax	 * actions can both be set before task is executed.
1296187741Semax	 *
1297187741Semax	 * The rules are
1298187741Semax	 *
1299187741Semax	 * sc_task_flags	action		new sc_task_flags
1300187741Semax	 * ------------------------------------------------------
1301187741Semax	 * 0			start		start
1302187741Semax	 * 0			stop		stop
1303187741Semax	 * start		start		start
1304187741Semax	 * start		stop		stop
1305187741Semax	 * stop			start		stop|start
1306187741Semax	 * stop			stop		stop
1307187741Semax	 * stop|start		start		stop|start
1308187741Semax	 * stop|start		stop		stop
1309187741Semax	 */
1310187494Semax
1311187741Semax	if (action != 0) {
1312187741Semax		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
1313187494Semax			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
1314187494Semax
1315187494Semax		sc->sc_task_flags |= action;
1316187494Semax	}
1317187494Semax
1318187494Semax	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
1319187741Semax		return;
1320187494Semax
1321187494Semax	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
1322187494Semax		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
1323187741Semax		return;
1324187494Semax	}
1325187494Semax
1326187494Semax	/* XXX: i think this should never happen */
1327187494Semax} /* ubt_task_schedule */
1328187494Semax
1329184610Salfred/*
1330187494Semax * Glue task. Examines sc_task_flags and does things depending on it.
1331187494Semax * Taskqueue context.
1332184610Salfred */
1333184610Salfred
1334187494Semaxstatic void
1335187494Semaxubt_task(void *context, int pending)
1336184610Salfred{
1337187741Semax	ubt_softc_p	sc = context;
1338187741Semax	int		task_flags, i;
1339184610Salfred
1340187741Semax	UBT_NG_LOCK(sc);
1341187494Semax	task_flags = sc->sc_task_flags;
1342187494Semax	sc->sc_task_flags = 0;
1343187741Semax	UBT_NG_UNLOCK(sc);
1344187494Semax
1345187741Semax	/*
1346187741Semax	 * Stop all USB transfers synchronously.
1347187741Semax	 * Stop interface #0 and #1 transfers at the same time and in the
1348194228Sthompsa	 * same loop. usbd_transfer_drain() will do appropriate locking.
1349187741Semax	 */
1350187494Semax
1351187741Semax	if (task_flags & UBT_FLAG_T_STOP_ALL)
1352187741Semax		for (i = 0; i < UBT_N_TRANSFER; i ++)
1353194228Sthompsa			usbd_transfer_drain(sc->sc_xfer[i]);
1354187494Semax
1355187741Semax	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
1356187494Semax	if (task_flags & UBT_FLAG_T_START_ALL) {
1357187494Semax		/*
1358187494Semax		 * Interface #0
1359187494Semax		 */
1360187494Semax
1361187741Semax		mtx_lock(&sc->sc_if_mtx);
1362187741Semax
1363187494Semax		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
1364187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
1365187494Semax
1366187494Semax		/*
1367187494Semax		 * Interface #1
1368187494Semax		 * Start both read and write isoc. transfers by default.
1369187494Semax		 * Get them going all the time even if we have nothing
1370187494Semax		 * to send to avoid any delays.
1371187494Semax		 */
1372187494Semax
1373187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
1374187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
1375187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
1376187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
1377187741Semax
1378187741Semax		mtx_unlock(&sc->sc_if_mtx);
1379184610Salfred	}
1380187494Semax
1381187494Semax 	/* Start outgoing control transfer */
1382187494Semax	if (task_flags & UBT_FLAG_T_START_CTRL) {
1383187741Semax		mtx_lock(&sc->sc_if_mtx);
1384187494Semax		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
1385187741Semax		mtx_unlock(&sc->sc_if_mtx);
1386184610Salfred	}
1387184610Salfred
1388187494Semax	/* Start outgoing bulk transfer */
1389187494Semax	if (task_flags & UBT_FLAG_T_START_BULK) {
1390187741Semax		mtx_lock(&sc->sc_if_mtx);
1391187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
1392187741Semax		mtx_unlock(&sc->sc_if_mtx);
1393184610Salfred	}
1394187494Semax} /* ubt_task */
1395187494Semax
1396187494Semax/****************************************************************************
1397187494Semax ****************************************************************************
1398187494Semax **                        Netgraph specific
1399187494Semax ****************************************************************************
1400187494Semax ****************************************************************************/
1401184610Salfred
1402187494Semax/*
1403187494Semax * Netgraph node constructor. Do not allow to create node of this type.
1404187494Semax * Netgraph context.
1405187494Semax */
1406184610Salfred
1407187494Semaxstatic int
1408187494Semaxng_ubt_constructor(node_p node)
1409187494Semax{
1410187494Semax	return (EINVAL);
1411187494Semax} /* ng_ubt_constructor */
1412184610Salfred
1413184610Salfred/*
1414187494Semax * Netgraph node destructor. Destroy node only when device has been detached.
1415187494Semax * Netgraph context.
1416184610Salfred */
1417184610Salfred
1418184610Salfredstatic int
1419187494Semaxng_ubt_shutdown(node_p node)
1420184610Salfred{
1421187494Semax	if (node->nd_flags & NGF_REALLY_DIE) {
1422187494Semax		/*
1423187494Semax                 * We came here because the USB device is being
1424187494Semax		 * detached, so stop being persistant.
1425187494Semax                 */
1426187494Semax		NG_NODE_SET_PRIVATE(node, NULL);
1427187494Semax		NG_NODE_UNREF(node);
1428187494Semax	} else
1429187494Semax		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
1430184610Salfred
1431187494Semax	return (0);
1432187494Semax} /* ng_ubt_shutdown */
1433184610Salfred
1434187494Semax/*
1435187494Semax * Create new hook. There can only be one.
1436187494Semax * Netgraph context.
1437187494Semax */
1438184610Salfred
1439187494Semaxstatic int
1440187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name)
1441187494Semax{
1442187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1443184610Salfred
1444187494Semax	if (strcmp(name, NG_UBT_HOOK) != 0)
1445187494Semax		return (EINVAL);
1446184610Salfred
1447187741Semax	UBT_NG_LOCK(sc);
1448187741Semax	if (sc->sc_hook != NULL) {
1449187741Semax		UBT_NG_UNLOCK(sc);
1450187741Semax
1451187494Semax		return (EISCONN);
1452187741Semax	}
1453184610Salfred
1454187494Semax	sc->sc_hook = hook;
1455187741Semax	UBT_NG_UNLOCK(sc);
1456184610Salfred
1457187494Semax	return (0);
1458187494Semax} /* ng_ubt_newhook */
1459184610Salfred
1460187494Semax/*
1461187494Semax * Connect hook. Start incoming USB transfers.
1462187494Semax * Netgraph context.
1463187494Semax */
1464184610Salfred
1465187494Semaxstatic int
1466187494Semaxng_ubt_connect(hook_p hook)
1467187494Semax{
1468187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1469184610Salfred
1470187494Semax	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
1471184610Salfred
1472187741Semax	UBT_NG_LOCK(sc);
1473187494Semax	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
1474187741Semax	UBT_NG_UNLOCK(sc);
1475184610Salfred
1476184610Salfred	return (0);
1477187494Semax} /* ng_ubt_connect */
1478184610Salfred
1479184610Salfred/*
1480187494Semax * Disconnect hook.
1481187494Semax * Netgraph context.
1482184610Salfred */
1483184610Salfred
1484184610Salfredstatic int
1485184610Salfredng_ubt_disconnect(hook_p hook)
1486184610Salfred{
1487187741Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1488184610Salfred
1489187741Semax	UBT_NG_LOCK(sc);
1490184610Salfred
1491187741Semax	if (hook != sc->sc_hook) {
1492187741Semax		UBT_NG_UNLOCK(sc);
1493184610Salfred
1494187494Semax		return (EINVAL);
1495187741Semax	}
1496184610Salfred
1497187494Semax	sc->sc_hook = NULL;
1498184610Salfred
1499187741Semax	/* Kick off task to stop all USB xfers */
1500187741Semax	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
1501184610Salfred
1502187494Semax	/* Drain queues */
1503187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
1504187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
1505187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
1506184610Salfred
1507187741Semax	UBT_NG_UNLOCK(sc);
1508184610Salfred
1509187494Semax	return (0);
1510187494Semax} /* ng_ubt_disconnect */
1511187494Semax
1512184610Salfred/*
1513187494Semax * Process control message.
1514187494Semax * Netgraph context.
1515184610Salfred */
1516184610Salfred
1517184610Salfredstatic int
1518184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
1519184610Salfred{
1520187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1521187494Semax	struct ng_mesg		*msg, *rsp = NULL;
1522187494Semax	struct ng_bt_mbufq	*q;
1523187494Semax	int			error = 0, queue, qlen;
1524184610Salfred
1525184610Salfred	NGI_GET_MSG(item, msg);
1526184610Salfred
1527184610Salfred	switch (msg->header.typecookie) {
1528184610Salfred	case NGM_GENERIC_COOKIE:
1529184610Salfred		switch (msg->header.cmd) {
1530184610Salfred		case NGM_TEXT_STATUS:
1531184610Salfred			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
1532187494Semax			if (rsp == NULL) {
1533184610Salfred				error = ENOMEM;
1534187494Semax				break;
1535187494Semax			}
1536187494Semax
1537187494Semax			snprintf(rsp->data, NG_TEXTRESPONSE,
1538187494Semax				"Hook: %s\n" \
1539187494Semax				"Task flags: %#x\n" \
1540187494Semax				"Debug: %d\n" \
1541187494Semax				"CMD queue: [have:%d,max:%d]\n" \
1542187494Semax				"ACL queue: [have:%d,max:%d]\n" \
1543187494Semax				"SCO queue: [have:%d,max:%d]",
1544187741Semax				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
1545187494Semax				sc->sc_task_flags,
1546187494Semax				sc->sc_debug,
1547187494Semax				sc->sc_cmdq.len,
1548187494Semax				sc->sc_cmdq.maxlen,
1549187494Semax				sc->sc_aclq.len,
1550187494Semax				sc->sc_aclq.maxlen,
1551187494Semax				sc->sc_scoq.len,
1552187494Semax				sc->sc_scoq.maxlen);
1553184610Salfred			break;
1554184610Salfred
1555184610Salfred		default:
1556184610Salfred			error = EINVAL;
1557184610Salfred			break;
1558184610Salfred		}
1559184610Salfred		break;
1560184610Salfred
1561184610Salfred	case NGM_UBT_COOKIE:
1562184610Salfred		switch (msg->header.cmd) {
1563184610Salfred		case NGM_UBT_NODE_SET_DEBUG:
1564187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
1565184610Salfred				error = EMSGSIZE;
1566187494Semax				break;
1567187494Semax			}
1568187494Semax
1569187494Semax			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
1570184610Salfred			break;
1571184610Salfred
1572184610Salfred		case NGM_UBT_NODE_GET_DEBUG:
1573184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
1574184610Salfred			    M_NOWAIT);
1575187494Semax			if (rsp == NULL) {
1576184610Salfred				error = ENOMEM;
1577187494Semax				break;
1578187494Semax			}
1579187494Semax
1580187494Semax			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
1581184610Salfred			break;
1582184610Salfred
1583184610Salfred		case NGM_UBT_NODE_SET_QLEN:
1584187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1585184610Salfred				error = EMSGSIZE;
1586187494Semax				break;
1587187494Semax			}
1588184610Salfred
1589187494Semax			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1590187494Semax			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
1591184610Salfred
1592187494Semax			switch (queue) {
1593187494Semax			case NGM_UBT_NODE_QUEUE_CMD:
1594187494Semax				q = &sc->sc_cmdq;
1595187494Semax				break;
1596184610Salfred
1597187494Semax			case NGM_UBT_NODE_QUEUE_ACL:
1598187494Semax				q = &sc->sc_aclq;
1599187494Semax				break;
1600184610Salfred
1601187494Semax			case NGM_UBT_NODE_QUEUE_SCO:
1602187494Semax				q = &sc->sc_scoq;
1603187494Semax				break;
1604184610Salfred
1605187494Semax			default:
1606187494Semax				error = EINVAL;
1607187494Semax				goto done;
1608187494Semax				/* NOT REACHED */
1609184610Salfred			}
1610187494Semax
1611187494Semax			q->maxlen = qlen;
1612184610Salfred			break;
1613184610Salfred
1614184610Salfred		case NGM_UBT_NODE_GET_QLEN:
1615184610Salfred			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1616184610Salfred				error = EMSGSIZE;
1617184610Salfred				break;
1618184610Salfred			}
1619187494Semax
1620184610Salfred			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1621187494Semax
1622184610Salfred			switch (queue) {
1623184610Salfred			case NGM_UBT_NODE_QUEUE_CMD:
1624184610Salfred				q = &sc->sc_cmdq;
1625184610Salfred				break;
1626184610Salfred
1627184610Salfred			case NGM_UBT_NODE_QUEUE_ACL:
1628184610Salfred				q = &sc->sc_aclq;
1629184610Salfred				break;
1630184610Salfred
1631184610Salfred			case NGM_UBT_NODE_QUEUE_SCO:
1632184610Salfred				q = &sc->sc_scoq;
1633184610Salfred				break;
1634184610Salfred
1635184610Salfred			default:
1636184610Salfred				error = EINVAL;
1637187494Semax				goto done;
1638187494Semax				/* NOT REACHED */
1639187494Semax			}
1640187494Semax
1641187494Semax			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
1642187494Semax				M_NOWAIT);
1643187494Semax			if (rsp == NULL) {
1644187494Semax				error = ENOMEM;
1645184610Salfred				break;
1646184610Salfred			}
1647184610Salfred
1648187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
1649187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
1650184610Salfred			break;
1651184610Salfred
1652184610Salfred		case NGM_UBT_NODE_GET_STAT:
1653184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
1654184610Salfred			    M_NOWAIT);
1655187494Semax			if (rsp == NULL) {
1656184610Salfred				error = ENOMEM;
1657187494Semax				break;
1658184610Salfred			}
1659187494Semax
1660187494Semax			bcopy(&sc->sc_stat, rsp->data,
1661187494Semax				sizeof(ng_ubt_node_stat_ep));
1662184610Salfred			break;
1663184610Salfred
1664184610Salfred		case NGM_UBT_NODE_RESET_STAT:
1665187494Semax			UBT_STAT_RESET(sc);
1666184610Salfred			break;
1667184610Salfred
1668184610Salfred		default:
1669184610Salfred			error = EINVAL;
1670184610Salfred			break;
1671184610Salfred		}
1672184610Salfred		break;
1673184610Salfred
1674184610Salfred	default:
1675184610Salfred		error = EINVAL;
1676184610Salfred		break;
1677184610Salfred	}
1678187494Semaxdone:
1679184610Salfred	NG_RESPOND_MSG(error, node, item, rsp);
1680184610Salfred	NG_FREE_MSG(msg);
1681184610Salfred
1682184610Salfred	return (error);
1683187494Semax} /* ng_ubt_rcvmsg */
1684184610Salfred
1685184610Salfred/*
1686187494Semax * Process data.
1687187494Semax * Netgraph context.
1688184610Salfred */
1689184610Salfred
1690184610Salfredstatic int
1691184610Salfredng_ubt_rcvdata(hook_p hook, item_p item)
1692184610Salfred{
1693187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1694187494Semax	struct mbuf		*m;
1695187494Semax	struct ng_bt_mbufq	*q;
1696187494Semax	int			action, error = 0;
1697184610Salfred
1698184610Salfred	if (hook != sc->sc_hook) {
1699184610Salfred		error = EINVAL;
1700184610Salfred		goto done;
1701184610Salfred	}
1702187494Semax
1703187494Semax	/* Deatch mbuf and get HCI frame type */
1704184610Salfred	NGI_GET_M(item, m);
1705184610Salfred
1706187494Semax	/*
1707187494Semax	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
1708187494Semax	 * 2 bytes connection handle and at least 1 byte of length.
1709187494Semax	 * Panic on data frame that has size smaller than 4 bytes (it
1710187494Semax	 * should not happen)
1711187494Semax	 */
1712187494Semax
1713187494Semax	if (m->m_pkthdr.len < 4)
1714187494Semax		panic("HCI frame size is too small! pktlen=%d\n",
1715187494Semax			m->m_pkthdr.len);
1716187494Semax
1717187494Semax	/* Process HCI frame */
1718184610Salfred	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
1719184610Salfred	case NG_HCI_CMD_PKT:
1720235000Shselasky		if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
1721187494Semax			panic("HCI command frame size is too big! " \
1722187494Semax				"buffer size=%zd, packet len=%d\n",
1723187494Semax				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
1724187494Semax
1725184610Salfred		q = &sc->sc_cmdq;
1726187494Semax		action = UBT_FLAG_T_START_CTRL;
1727184610Salfred		break;
1728184610Salfred
1729184610Salfred	case NG_HCI_ACL_DATA_PKT:
1730187494Semax		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
1731187494Semax			panic("ACL data frame size is too big! " \
1732187494Semax				"buffer size=%d, packet len=%d\n",
1733187494Semax				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
1734187494Semax
1735184610Salfred		q = &sc->sc_aclq;
1736187494Semax		action = UBT_FLAG_T_START_BULK;
1737184610Salfred		break;
1738184610Salfred
1739184610Salfred	case NG_HCI_SCO_DATA_PKT:
1740184610Salfred		q = &sc->sc_scoq;
1741187494Semax		action = 0;
1742184610Salfred		break;
1743184610Salfred
1744184610Salfred	default:
1745187494Semax		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
1746187494Semax			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
1747184610Salfred
1748184610Salfred		NG_FREE_M(m);
1749184610Salfred		error = EINVAL;
1750184610Salfred		goto done;
1751187494Semax		/* NOT REACHED */
1752184610Salfred	}
1753184610Salfred
1754187741Semax	UBT_NG_LOCK(sc);
1755184610Salfred	if (NG_BT_MBUFQ_FULL(q)) {
1756187494Semax		NG_BT_MBUFQ_DROP(q);
1757187741Semax		UBT_NG_UNLOCK(sc);
1758187494Semax
1759187494Semax		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
1760187494Semax			*mtod(m, uint8_t *), m->m_pkthdr.len);
1761187494Semax
1762184610Salfred		NG_FREE_M(m);
1763184610Salfred	} else {
1764187494Semax		/* Loose HCI packet type, enqueue mbuf and kick off task */
1765187494Semax		m_adj(m, sizeof(uint8_t));
1766184610Salfred		NG_BT_MBUFQ_ENQUEUE(q, m);
1767187494Semax		ubt_task_schedule(sc, action);
1768187741Semax		UBT_NG_UNLOCK(sc);
1769184610Salfred	}
1770184610Salfreddone:
1771184610Salfred	NG_FREE_ITEM(item);
1772184610Salfred
1773187494Semax	return (error);
1774187494Semax} /* ng_ubt_rcvdata */
1775187494Semax
1776187494Semax/****************************************************************************
1777187494Semax ****************************************************************************
1778187494Semax **                              Module
1779187494Semax ****************************************************************************
1780187494Semax ****************************************************************************/
1781187494Semax
1782187494Semax/*
1783187494Semax * Load/Unload the driver module
1784187494Semax */
1785187494Semax
1786187494Semaxstatic int
1787187494Semaxubt_modevent(module_t mod, int event, void *data)
1788187494Semax{
1789187494Semax	int	error;
1790187494Semax
1791187494Semax	switch (event) {
1792187494Semax	case MOD_LOAD:
1793187494Semax		error = ng_newtype(&typestruct);
1794187494Semax		if (error != 0)
1795187494Semax			printf("%s: Could not register Netgraph node type, " \
1796187494Semax				"error=%d\n", NG_UBT_NODE_TYPE, error);
1797187494Semax		break;
1798187494Semax
1799187494Semax	case MOD_UNLOAD:
1800187494Semax		error = ng_rmtype(&typestruct);
1801187494Semax		break;
1802187494Semax
1803187494Semax	default:
1804187494Semax		error = EOPNOTSUPP;
1805187494Semax		break;
1806184610Salfred	}
1807187494Semax
1808184610Salfred	return (error);
1809187494Semax} /* ubt_modevent */
1810187494Semax
1811187494Semaxstatic devclass_t	ubt_devclass;
1812187494Semax
1813187494Semaxstatic device_method_t	ubt_methods[] =
1814187494Semax{
1815187494Semax	DEVMETHOD(device_probe,	ubt_probe),
1816187494Semax	DEVMETHOD(device_attach, ubt_attach),
1817187494Semax	DEVMETHOD(device_detach, ubt_detach),
1818244956Shselasky	DEVMETHOD_END
1819187494Semax};
1820187494Semax
1821187494Semaxstatic driver_t		ubt_driver =
1822187494Semax{
1823187494Semax	.name =	   "ubt",
1824187494Semax	.methods = ubt_methods,
1825187494Semax	.size =	   sizeof(struct ubt_softc),
1826187494Semax};
1827187494Semax
1828189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
1829187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
1830187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
1831187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
1832188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
1833187494Semax
1834