ng_ubt.c revision 244956
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: stable/9/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c 244956 2013-01-02 07:33:19Z hselasky $
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) },
407184610Salfred};
408184610Salfred
409184610Salfred/*
410187494Semax * Probe for a USB Bluetooth device.
411187494Semax * USB context.
412184610Salfred */
413184610Salfred
414184610Salfredstatic int
415184610Salfredubt_probe(device_t dev)
416184610Salfred{
417192984Sthompsa	struct usb_attach_arg	*uaa = device_get_ivars(dev);
418222055Savg	int error;
419184610Salfred
420192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
421184610Salfred		return (ENXIO);
422187494Semax
423187494Semax	if (uaa->info.bIfaceIndex != 0)
424184610Salfred		return (ENXIO);
425187494Semax
426194228Sthompsa	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
427187494Semax			sizeof(ubt_ignore_devs), uaa) == 0)
428184610Salfred		return (ENXIO);
429187494Semax
430222055Savg	error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
431222055Savg	if (error == 0)
432222055Savg		return (BUS_PROBE_GENERIC);
433222055Savg	return (error);
434187494Semax} /* ubt_probe */
435184610Salfred
436184610Salfred/*
437187494Semax * Attach the device.
438187494Semax * USB context.
439184610Salfred */
440184610Salfred
441184610Salfredstatic int
442184610Salfredubt_attach(device_t dev)
443184610Salfred{
444192984Sthompsa	struct usb_attach_arg		*uaa = device_get_ivars(dev);
445187494Semax	struct ubt_softc		*sc = device_get_softc(dev);
446192984Sthompsa	struct usb_endpoint_descriptor	*ed;
447192984Sthompsa	struct usb_interface_descriptor *id;
448187494Semax	uint16_t			wMaxPacketSize;
449187741Semax	uint8_t				alt_index, i, j;
450187741Semax	uint8_t				iface_index[2] = { 0, 1 };
451184610Salfred
452194228Sthompsa	device_set_usb_desc(dev);
453184610Salfred
454187741Semax	sc->sc_dev = dev;
455187741Semax	sc->sc_debug = NG_UBT_WARN_LEVEL;
456184610Salfred
457187494Semax	/*
458187494Semax	 * Create Netgraph node
459187494Semax	 */
460187494Semax
461187494Semax	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
462187741Semax		UBT_ALERT(sc, "could not create Netgraph node\n");
463187494Semax		return (ENXIO);
464187494Semax	}
465187494Semax
466187494Semax	/* Name Netgraph node */
467187741Semax	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
468187741Semax		UBT_ALERT(sc, "could not name Netgraph node\n");
469187494Semax		NG_NODE_UNREF(sc->sc_node);
470187494Semax		return (ENXIO);
471187494Semax	}
472187494Semax	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
473187494Semax	NG_NODE_FORCE_WRITER(sc->sc_node);
474187494Semax
475184610Salfred	/*
476184610Salfred	 * Initialize device softc structure
477184610Salfred	 */
478184610Salfred
479187494Semax	/* initialize locks */
480187741Semax	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
481187741Semax	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
482187494Semax
483187494Semax	/* initialize packet queues */
484184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
485184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
486187494Semax	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
487184610Salfred
488187494Semax	/* initialize glue task */
489187741Semax	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
490184610Salfred
491184610Salfred	/*
492184610Salfred	 * Configure Bluetooth USB device. Discover all required USB
493184610Salfred	 * interfaces and endpoints.
494184610Salfred	 *
495184610Salfred	 * USB device must present two interfaces:
496184610Salfred	 * 1) Interface 0 that has 3 endpoints
497184610Salfred	 *	1) Interrupt endpoint to receive HCI events
498184610Salfred	 *	2) Bulk IN endpoint to receive ACL data
499184610Salfred	 *	3) Bulk OUT endpoint to send ACL data
500184610Salfred	 *
501184610Salfred	 * 2) Interface 1 then has 2 endpoints
502184610Salfred	 *	1) Isochronous IN endpoint to receive SCO data
503184610Salfred 	 *	2) Isochronous OUT endpoint to send SCO data
504184610Salfred	 *
505184610Salfred	 * Interface 1 (with isochronous endpoints) has several alternate
506184610Salfred	 * configurations with different packet size.
507184610Salfred	 */
508184610Salfred
509184610Salfred	/*
510187741Semax	 * For interface #1 search alternate settings, and find
511187741Semax	 * the descriptor with the largest wMaxPacketSize
512184610Salfred	 */
513184610Salfred
514184610Salfred	wMaxPacketSize = 0;
515187494Semax	alt_index = 0;
516184610Salfred	i = 0;
517184610Salfred	j = 0;
518190728Sthompsa	ed = NULL;
519187494Semax
520190728Sthompsa	/*
521190728Sthompsa	 * Search through all the descriptors looking for the largest
522190728Sthompsa	 * packet size:
523190728Sthompsa	 */
524194228Sthompsa	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
525194228Sthompsa	    usbd_get_config_descriptor(uaa->device),
526192984Sthompsa	    (struct usb_descriptor *)ed))) {
527184610Salfred
528190728Sthompsa		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
529190728Sthompsa		    (ed->bLength >= sizeof(*id))) {
530192984Sthompsa			id = (struct usb_interface_descriptor *)ed;
531190728Sthompsa			i = id->bInterfaceNumber;
532190728Sthompsa			j = id->bAlternateSetting;
533184610Salfred		}
534187494Semax
535190728Sthompsa		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
536190728Sthompsa		    (ed->bLength >= sizeof(*ed)) &&
537190728Sthompsa		    (i == 1)) {
538190728Sthompsa			uint16_t temp;
539190728Sthompsa
540190728Sthompsa			temp = UGETW(ed->wMaxPacketSize);
541190728Sthompsa			if (temp > wMaxPacketSize) {
542190728Sthompsa				wMaxPacketSize = temp;
543190728Sthompsa				alt_index = j;
544190728Sthompsa			}
545184610Salfred		}
546184610Salfred	}
547184610Salfred
548187741Semax	/* Set alt configuration on interface #1 only if we found it */
549187494Semax	if (wMaxPacketSize > 0 &&
550194228Sthompsa	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
551187741Semax		UBT_ALERT(sc, "could not set alternate setting %d " \
552187494Semax			"for interface 1!\n", alt_index);
553184610Salfred		goto detach;
554184610Salfred	}
555184610Salfred
556187741Semax	/* Setup transfers for both interfaces */
557194228Sthompsa	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
558187741Semax			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
559187741Semax		UBT_ALERT(sc, "could not allocate transfers\n");
560184610Salfred		goto detach;
561184610Salfred	}
562184610Salfred
563187494Semax	/* Claim all interfaces on the device */
564194228Sthompsa	for (i = 1; usbd_get_iface(uaa->device, i) != NULL; i ++)
565194228Sthompsa		usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
566184610Salfred
567187494Semax	return (0); /* success */
568184610Salfred
569184610Salfreddetach:
570184610Salfred	ubt_detach(dev);
571184610Salfred
572184610Salfred	return (ENXIO);
573187494Semax} /* ubt_attach */
574184610Salfred
575184610Salfred/*
576187494Semax * Detach the device.
577187494Semax * USB context.
578184610Salfred */
579184610Salfred
580184610Salfredint
581184610Salfredubt_detach(device_t dev)
582184610Salfred{
583187494Semax	struct ubt_softc	*sc = device_get_softc(dev);
584187494Semax	node_p			node = sc->sc_node;
585184610Salfred
586187494Semax	/* Destroy Netgraph node */
587187494Semax	if (node != NULL) {
588187494Semax		sc->sc_node = NULL;
589187494Semax		NG_NODE_REALLY_DIE(node);
590187494Semax		ng_rmnode_self(node);
591184610Salfred	}
592184610Salfred
593187741Semax	/* Make sure ubt_task in gone */
594187741Semax	taskqueue_drain(taskqueue_swi, &sc->sc_task);
595187741Semax
596187494Semax	/* Free USB transfers, if any */
597194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
598184610Salfred
599187494Semax	/* Destroy queues */
600187741Semax	UBT_NG_LOCK(sc);
601184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
602184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
603184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
604187741Semax	UBT_NG_UNLOCK(sc);
605184610Salfred
606187741Semax	mtx_destroy(&sc->sc_if_mtx);
607187741Semax	mtx_destroy(&sc->sc_ng_mtx);
608187494Semax
609184610Salfred	return (0);
610187494Semax} /* ubt_detach */
611184610Salfred
612187494Semax/*
613187494Semax * Called when outgoing control request (HCI command) has completed, i.e.
614187494Semax * HCI command was sent to the device.
615187494Semax * USB context.
616187494Semax */
617187494Semax
618184610Salfredstatic void
619194677Sthompsaubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
620184610Salfred{
621194677Sthompsa	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
622192984Sthompsa	struct usb_device_request	req;
623187494Semax	struct mbuf			*m;
624194677Sthompsa	struct usb_page_cache		*pc;
625194677Sthompsa	int				actlen;
626184610Salfred
627194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
628194677Sthompsa
629184610Salfred	switch (USB_GET_STATE(xfer)) {
630184610Salfred	case USB_ST_TRANSFERRED:
631194677Sthompsa		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
632194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
633187741Semax		UBT_STAT_PCKTS_SENT(sc);
634187494Semax		/* FALLTHROUGH */
635184610Salfred
636184610Salfred	case USB_ST_SETUP:
637187494Semaxsend_next:
638187494Semax		/* Get next command mbuf, if any */
639187741Semax		UBT_NG_LOCK(sc);
640184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
641187741Semax		UBT_NG_UNLOCK(sc);
642184610Salfred
643184610Salfred		if (m == NULL) {
644187494Semax			UBT_INFO(sc, "HCI command queue is empty\n");
645187741Semax			break;	/* transfer complete */
646184610Salfred		}
647184610Salfred
648187494Semax		/* Initialize a USB control request and then schedule it */
649184610Salfred		bzero(&req, sizeof(req));
650184610Salfred		req.bmRequestType = UBT_HCI_REQUEST;
651184610Salfred		USETW(req.wLength, m->m_pkthdr.len);
652184610Salfred
653187494Semax		UBT_INFO(sc, "Sending control request, " \
654187494Semax			"bmRequestType=0x%02x, wLength=%d\n",
655187494Semax			req.bmRequestType, UGETW(req.wLength));
656184610Salfred
657194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
658194677Sthompsa		usbd_copy_in(pc, 0, &req, sizeof(req));
659194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 1);
660194677Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
661184610Salfred
662194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
663194677Sthompsa		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
664194677Sthompsa		usbd_xfer_set_frames(xfer, 2);
665184610Salfred
666184610Salfred		NG_FREE_M(m);
667184610Salfred
668194228Sthompsa		usbd_transfer_submit(xfer);
669187494Semax		break;
670184610Salfred
671187494Semax	default: /* Error */
672194677Sthompsa		if (error != USB_ERR_CANCELLED) {
673187494Semax			UBT_WARN(sc, "control transfer failed: %s\n",
674194677Sthompsa				usbd_errstr(error));
675187494Semax
676187494Semax			UBT_STAT_OERROR(sc);
677187494Semax			goto send_next;
678184610Salfred		}
679187494Semax
680187741Semax		/* transfer cancelled */
681187494Semax		break;
682184610Salfred	}
683187494Semax} /* ubt_ctrl_write_callback */
684184610Salfred
685187494Semax/*
686187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e.
687187494Semax * HCI event was received from the device.
688187494Semax * USB context.
689187494Semax */
690187494Semax
691184610Salfredstatic void
692194677Sthompsaubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
693184610Salfred{
694194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
695187494Semax	struct mbuf		*m;
696187494Semax	ng_hci_event_pkt_t	*hdr;
697194677Sthompsa	struct usb_page_cache	*pc;
698194677Sthompsa	int			actlen;
699184610Salfred
700194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
701194677Sthompsa
702187494Semax	m = NULL;
703187494Semax
704184610Salfred	switch (USB_GET_STATE(xfer)) {
705184610Salfred	case USB_ST_TRANSFERRED:
706187494Semax		/* Allocate a new mbuf */
707184610Salfred		MGETHDR(m, M_DONTWAIT, MT_DATA);
708184610Salfred		if (m == NULL) {
709187494Semax			UBT_STAT_IERROR(sc);
710187494Semax			goto submit_next;
711184610Salfred		}
712187494Semax
713184610Salfred		MCLGET(m, M_DONTWAIT);
714184610Salfred		if (!(m->m_flags & M_EXT)) {
715187494Semax			UBT_STAT_IERROR(sc);
716187494Semax			goto submit_next;
717184610Salfred		}
718187494Semax
719187494Semax		/* Add HCI packet type */
720187494Semax		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
721187494Semax		m->m_pkthdr.len = m->m_len = 1;
722187494Semax
723194677Sthompsa		if (actlen > MCLBYTES - 1)
724194677Sthompsa			actlen = MCLBYTES - 1;
725187494Semax
726194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
727194677Sthompsa		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
728194677Sthompsa		m->m_pkthdr.len += actlen;
729194677Sthompsa		m->m_len += actlen;
730187494Semax
731187494Semax		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
732194677Sthompsa			actlen);
733187494Semax
734187494Semax		/* Validate packet and send it up the stack */
735235000Shselasky		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
736187494Semax			UBT_INFO(sc, "HCI event packet is too short\n");
737187494Semax
738187494Semax			UBT_STAT_IERROR(sc);
739187494Semax			goto submit_next;
740184610Salfred		}
741184610Salfred
742187494Semax		hdr = mtod(m, ng_hci_event_pkt_t *);
743187494Semax		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
744187494Semax			UBT_ERR(sc, "Invalid HCI event packet size, " \
745187494Semax				"length=%d, pktlen=%d\n",
746187494Semax				hdr->length, m->m_pkthdr.len);
747184610Salfred
748187494Semax			UBT_STAT_IERROR(sc);
749187494Semax			goto submit_next;
750184610Salfred		}
751184610Salfred
752187494Semax		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
753187494Semax			"length=%d\n", m->m_pkthdr.len, hdr->length);
754184610Salfred
755187494Semax		UBT_STAT_PCKTS_RECV(sc);
756187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
757184610Salfred
758187741Semax		ubt_fwd_mbuf_up(sc, &m);
759187494Semax		/* m == NULL at this point */
760187494Semax		/* FALLTHROUGH */
761184610Salfred
762184610Salfred	case USB_ST_SETUP:
763187494Semaxsubmit_next:
764187494Semax		NG_FREE_M(m); /* checks for m != NULL */
765184610Salfred
766194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
767194228Sthompsa		usbd_transfer_submit(xfer);
768187494Semax		break;
769184610Salfred
770187494Semax	default: /* Error */
771194677Sthompsa		if (error != USB_ERR_CANCELLED) {
772187494Semax			UBT_WARN(sc, "interrupt transfer failed: %s\n",
773194677Sthompsa				usbd_errstr(error));
774184610Salfred
775187494Semax			/* Try to clear stall first */
776194677Sthompsa			usbd_xfer_set_stall(xfer);
777187741Semax			goto submit_next;
778187741Semax		}
779187741Semax			/* transfer cancelled */
780187494Semax		break;
781184610Salfred	}
782187494Semax} /* ubt_intr_read_callback */
783184610Salfred
784187494Semax/*
785187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e.
786187494Semax * ACL packet was received from the device.
787187494Semax * USB context.
788187494Semax */
789187494Semax
790184610Salfredstatic void
791194677Sthompsaubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
792184610Salfred{
793194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
794187494Semax	struct mbuf		*m;
795187494Semax	ng_hci_acldata_pkt_t	*hdr;
796194677Sthompsa	struct usb_page_cache	*pc;
797235000Shselasky	int len;
798235000Shselasky	int actlen;
799184610Salfred
800194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
801194677Sthompsa
802187494Semax	m = NULL;
803184610Salfred
804184610Salfred	switch (USB_GET_STATE(xfer)) {
805184610Salfred	case USB_ST_TRANSFERRED:
806187494Semax		/* Allocate new mbuf */
807184610Salfred		MGETHDR(m, M_DONTWAIT, MT_DATA);
808184610Salfred		if (m == NULL) {
809187494Semax			UBT_STAT_IERROR(sc);
810187494Semax			goto submit_next;
811184610Salfred		}
812187494Semax
813184610Salfred		MCLGET(m, M_DONTWAIT);
814184610Salfred		if (!(m->m_flags & M_EXT)) {
815187494Semax			UBT_STAT_IERROR(sc);
816187494Semax			goto submit_next;
817184610Salfred		}
818184610Salfred
819187494Semax		/* Add HCI packet type */
820187494Semax		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
821187494Semax		m->m_pkthdr.len = m->m_len = 1;
822184610Salfred
823194677Sthompsa		if (actlen > MCLBYTES - 1)
824194677Sthompsa			actlen = MCLBYTES - 1;
825184610Salfred
826194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
827194677Sthompsa		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
828194677Sthompsa		m->m_pkthdr.len += actlen;
829194677Sthompsa		m->m_len += actlen;
830184610Salfred
831187494Semax		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
832194677Sthompsa			actlen);
833184610Salfred
834187494Semax		/* Validate packet and send it up the stack */
835235000Shselasky		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
836187494Semax			UBT_INFO(sc, "HCI ACL packet is too short\n");
837184610Salfred
838187494Semax			UBT_STAT_IERROR(sc);
839187494Semax			goto submit_next;
840184610Salfred		}
841184610Salfred
842187494Semax		hdr = mtod(m, ng_hci_acldata_pkt_t *);
843187494Semax		len = le16toh(hdr->length);
844235000Shselasky		if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
845187494Semax			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
846187494Semax				"pktlen=%d\n", len, m->m_pkthdr.len);
847184610Salfred
848187494Semax			UBT_STAT_IERROR(sc);
849187494Semax			goto submit_next;
850184610Salfred		}
851184610Salfred
852187494Semax		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
853187494Semax			"length=%d\n", m->m_pkthdr.len, len);
854184610Salfred
855187494Semax		UBT_STAT_PCKTS_RECV(sc);
856187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
857184610Salfred
858187741Semax		ubt_fwd_mbuf_up(sc, &m);
859187494Semax		/* m == NULL at this point */
860187494Semax		/* FALLTHOUGH */
861184610Salfred
862187494Semax	case USB_ST_SETUP:
863187494Semaxsubmit_next:
864187494Semax		NG_FREE_M(m); /* checks for m != NULL */
865184610Salfred
866194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
867194228Sthompsa		usbd_transfer_submit(xfer);
868187494Semax		break;
869184610Salfred
870187494Semax	default: /* Error */
871194677Sthompsa		if (error != USB_ERR_CANCELLED) {
872187494Semax			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
873194677Sthompsa				usbd_errstr(error));
874184610Salfred
875187494Semax			/* Try to clear stall first */
876194677Sthompsa			usbd_xfer_set_stall(xfer);
877187741Semax			goto submit_next;
878187741Semax		}
879187741Semax			/* transfer cancelled */
880187494Semax		break;
881187494Semax	}
882187494Semax} /* ubt_bulk_read_callback */
883184610Salfred
884187494Semax/*
885187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
886187494Semax * ACL packet was sent to the device.
887187494Semax * USB context.
888187494Semax */
889184610Salfred
890184610Salfredstatic void
891194677Sthompsaubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
892184610Salfred{
893194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
894187494Semax	struct mbuf		*m;
895194677Sthompsa	struct usb_page_cache	*pc;
896194677Sthompsa	int			actlen;
897184610Salfred
898194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
899194677Sthompsa
900184610Salfred	switch (USB_GET_STATE(xfer)) {
901184610Salfred	case USB_ST_TRANSFERRED:
902194677Sthompsa		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
903194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
904187741Semax		UBT_STAT_PCKTS_SENT(sc);
905187494Semax		/* FALLTHROUGH */
906184610Salfred
907187494Semax	case USB_ST_SETUP:
908187741Semaxsend_next:
909187494Semax		/* Get next mbuf, if any */
910187741Semax		UBT_NG_LOCK(sc);
911184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
912187741Semax		UBT_NG_UNLOCK(sc);
913184610Salfred
914184610Salfred		if (m == NULL) {
915187494Semax			UBT_INFO(sc, "ACL data queue is empty\n");
916187741Semax			break; /* transfer completed */
917184610Salfred		}
918187494Semax
919184610Salfred		/*
920187494Semax		 * Copy ACL data frame back to a linear USB transfer buffer
921187494Semax		 * and schedule transfer
922184610Salfred		 */
923184610Salfred
924194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
925194677Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
926194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
927184610Salfred
928187494Semax		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
929187494Semax			m->m_pkthdr.len);
930184610Salfred
931184610Salfred		NG_FREE_M(m);
932184610Salfred
933194228Sthompsa		usbd_transfer_submit(xfer);
934187494Semax		break;
935184610Salfred
936187494Semax	default: /* Error */
937194677Sthompsa		if (error != USB_ERR_CANCELLED) {
938187494Semax			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
939194677Sthompsa				usbd_errstr(error));
940184610Salfred
941187494Semax			UBT_STAT_OERROR(sc);
942184610Salfred
943184610Salfred			/* try to clear stall first */
944194677Sthompsa			usbd_xfer_set_stall(xfer);
945187741Semax			goto send_next;
946187741Semax		}
947187741Semax			/* transfer cancelled */
948187494Semax		break;
949184610Salfred	}
950187494Semax} /* ubt_bulk_write_callback */
951184610Salfred
952187494Semax/*
953187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e.
954187494Semax * SCO packet was received from the device.
955187494Semax * USB context.
956187494Semax */
957187494Semax
958184610Salfredstatic void
959194677Sthompsaubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
960184610Salfred{
961194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
962187494Semax	int			n;
963194677Sthompsa	int actlen, nframes;
964184610Salfred
965194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
966194677Sthompsa
967187494Semax	switch (USB_GET_STATE(xfer)) {
968187494Semax	case USB_ST_TRANSFERRED:
969194677Sthompsa		for (n = 0; n < nframes; n ++)
970187494Semax			if (ubt_isoc_read_one_frame(xfer, n) < 0)
971187494Semax				break;
972187494Semax		/* FALLTHROUGH */
973184610Salfred
974187494Semax	case USB_ST_SETUP:
975187494Semaxread_next:
976194677Sthompsa		for (n = 0; n < nframes; n ++)
977194677Sthompsa			usbd_xfer_set_frame_len(xfer, n,
978194677Sthompsa			    usbd_xfer_max_framelen(xfer));
979184610Salfred
980194228Sthompsa		usbd_transfer_submit(xfer);
981187494Semax		break;
982184610Salfred
983187494Semax	default: /* Error */
984194677Sthompsa                if (error != USB_ERR_CANCELLED) {
985187494Semax                        UBT_STAT_IERROR(sc);
986187494Semax                        goto read_next;
987187494Semax                }
988184610Salfred
989187741Semax		/* transfer cancelled */
990187494Semax		break;
991187494Semax	}
992187494Semax} /* ubt_isoc_read_callback */
993184610Salfred
994187494Semax/*
995187494Semax * Helper function. Called from ubt_isoc_read_callback() to read
996187494Semax * SCO data from one frame.
997187494Semax * USB context.
998187494Semax */
999184610Salfred
1000187494Semaxstatic int
1001192984Sthompsaubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
1002187494Semax{
1003194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1004194677Sthompsa	struct usb_page_cache	*pc;
1005187494Semax	struct mbuf		*m;
1006194677Sthompsa	int			len, want, got, total;
1007184610Salfred
1008187494Semax	/* Get existing SCO reassembly buffer */
1009194677Sthompsa	pc = usbd_xfer_get_frame(xfer, 0);
1010187494Semax	m = sc->sc_isoc_in_buffer;
1011194682Sthompsa	total = usbd_xfer_frame_len(xfer, frame_no);
1012184610Salfred
1013187494Semax	/* While we have data in the frame */
1014194677Sthompsa	while (total > 0) {
1015187494Semax		if (m == NULL) {
1016187494Semax			/* Start new reassembly buffer */
1017187494Semax			MGETHDR(m, M_DONTWAIT, MT_DATA);
1018187494Semax			if (m == NULL) {
1019187494Semax				UBT_STAT_IERROR(sc);
1020187494Semax				return (-1);	/* XXX out of sync! */
1021187494Semax			}
1022184610Salfred
1023187494Semax			MCLGET(m, M_DONTWAIT);
1024187494Semax			if (!(m->m_flags & M_EXT)) {
1025187494Semax				UBT_STAT_IERROR(sc);
1026187494Semax				NG_FREE_M(m);
1027187494Semax				return (-1);	/* XXX out of sync! */
1028184610Salfred			}
1029184610Salfred
1030187494Semax			/* Expect SCO header */
1031187494Semax			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
1032187494Semax			m->m_pkthdr.len = m->m_len = got = 1;
1033187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
1034187494Semax		} else {
1035187494Semax			/*
1036187494Semax			 * Check if we have SCO header and if so
1037187494Semax			 * adjust amount of data we want
1038187494Semax			 */
1039187494Semax			got = m->m_pkthdr.len;
1040187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
1041184610Salfred
1042187494Semax			if (got >= want)
1043187494Semax				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
1044184610Salfred		}
1045184610Salfred
1046187494Semax		/* Append frame data to the SCO reassembly buffer */
1047194677Sthompsa		len = total;
1048187494Semax		if (got + len > want)
1049187494Semax			len = want - got;
1050184610Salfred
1051194677Sthompsa		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
1052187494Semax			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
1053184610Salfred
1054187494Semax		m->m_pkthdr.len += len;
1055187494Semax		m->m_len += len;
1056194677Sthompsa		total -= len;
1057184610Salfred
1058187494Semax		/* Check if we got everything we wanted, if not - continue */
1059187494Semax		if (got != want)
1060187494Semax			continue;
1061184610Salfred
1062187494Semax		/* If we got here then we got complete SCO frame */
1063187494Semax		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
1064187494Semax			"length=%d\n", m->m_pkthdr.len,
1065187494Semax			mtod(m, ng_hci_scodata_pkt_t *)->length);
1066184610Salfred
1067187494Semax		UBT_STAT_PCKTS_RECV(sc);
1068187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
1069184610Salfred
1070187741Semax		ubt_fwd_mbuf_up(sc, &m);
1071187494Semax		/* m == NULL at this point */
1072187494Semax	}
1073184610Salfred
1074187494Semax	/* Put SCO reassembly buffer back */
1075187494Semax	sc->sc_isoc_in_buffer = m;
1076184610Salfred
1077187494Semax	return (0);
1078187494Semax} /* ubt_isoc_read_one_frame */
1079184610Salfred
1080187494Semax/*
1081187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
1082187494Semax * SCO packet was sent to the device.
1083187494Semax * USB context.
1084187494Semax */
1085184610Salfred
1086184610Salfredstatic void
1087194677Sthompsaubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
1088184610Salfred{
1089194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1090194677Sthompsa	struct usb_page_cache	*pc;
1091187494Semax	struct mbuf		*m;
1092187494Semax	int			n, space, offset;
1093194677Sthompsa	int			actlen, nframes;
1094184610Salfred
1095194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1096194677Sthompsa	pc = usbd_xfer_get_frame(xfer, 0);
1097194677Sthompsa
1098184610Salfred	switch (USB_GET_STATE(xfer)) {
1099184610Salfred	case USB_ST_TRANSFERRED:
1100194677Sthompsa		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1101194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
1102187741Semax		UBT_STAT_PCKTS_SENT(sc);
1103187494Semax		/* FALLTHROUGH */
1104184610Salfred
1105184610Salfred	case USB_ST_SETUP:
1106187494Semaxsend_next:
1107184610Salfred		offset = 0;
1108194677Sthompsa		space = usbd_xfer_max_framelen(xfer) * nframes;
1109187494Semax		m = NULL;
1110184610Salfred
1111187494Semax		while (space > 0) {
1112187494Semax			if (m == NULL) {
1113187741Semax				UBT_NG_LOCK(sc);
1114187494Semax				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
1115187741Semax				UBT_NG_UNLOCK(sc);
1116184610Salfred
1117187494Semax				if (m == NULL)
1118187494Semax					break;
1119187494Semax			}
1120184610Salfred
1121187494Semax			n = min(space, m->m_pkthdr.len);
1122187494Semax			if (n > 0) {
1123194677Sthompsa				usbd_m_copy_in(pc, offset, m,0, n);
1124187494Semax				m_adj(m, n);
1125184610Salfred
1126187494Semax				offset += n;
1127187494Semax				space -= n;
1128187494Semax			}
1129184610Salfred
1130187494Semax			if (m->m_pkthdr.len == 0)
1131187494Semax				NG_FREE_M(m); /* sets m = NULL */
1132187494Semax		}
1133184610Salfred
1134187494Semax		/* Put whatever is left from mbuf back on queue */
1135187494Semax		if (m != NULL) {
1136187741Semax			UBT_NG_LOCK(sc);
1137187494Semax			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
1138187741Semax			UBT_NG_UNLOCK(sc);
1139184610Salfred		}
1140184610Salfred
1141187494Semax		/*
1142187494Semax		 * Calculate sizes for isoc frames.
1143187494Semax		 * Note that offset could be 0 at this point (i.e. we have
1144187494Semax		 * nothing to send). That is fine, as we have isoc. transfers
1145187494Semax		 * going in both directions all the time. In this case it
1146187494Semax		 * would be just empty isoc. transfer.
1147187494Semax		 */
1148187494Semax
1149194677Sthompsa		for (n = 0; n < nframes; n ++) {
1150194677Sthompsa			usbd_xfer_set_frame_len(xfer, n,
1151194677Sthompsa			    min(offset, usbd_xfer_max_framelen(xfer)));
1152194682Sthompsa			offset -= usbd_xfer_frame_len(xfer, n);
1153187494Semax		}
1154187494Semax
1155194228Sthompsa		usbd_transfer_submit(xfer);
1156187494Semax		break;
1157184610Salfred
1158187494Semax	default: /* Error */
1159194677Sthompsa		if (error != USB_ERR_CANCELLED) {
1160187494Semax			UBT_STAT_OERROR(sc);
1161187494Semax			goto send_next;
1162184610Salfred		}
1163187494Semax
1164187741Semax		/* transfer cancelled */
1165187494Semax		break;
1166184610Salfred	}
1167184610Salfred}
1168184610Salfred
1169187741Semax/*
1170187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack).
1171187741Semax * Modifies value of the mbuf pointer (sets it to NULL).
1172187741Semax * Save to call from any context.
1173187741Semax */
1174187741Semax
1175187741Semaxstatic int
1176187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
1177187741Semax{
1178187741Semax	hook_p	hook;
1179187741Semax	int	error;
1180187741Semax
1181187741Semax	/*
1182187741Semax	 * Close the race with Netgraph hook newhook/disconnect methods.
1183187741Semax	 * Save the hook pointer atomically. Two cases are possible:
1184187741Semax	 *
1185187741Semax	 * 1) The hook pointer is NULL. It means disconnect method got
1186187741Semax	 *    there first. In this case we are done.
1187187741Semax	 *
1188187741Semax	 * 2) The hook pointer is not NULL. It means that hook pointer
1189187741Semax	 *    could be either in valid or invalid (i.e. in the process
1190187741Semax	 *    of disconnect) state. In any case grab an extra reference
1191187741Semax	 *    to protect the hook pointer.
1192187741Semax	 *
1193187741Semax	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
1194187741Semax	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
1195187741Semax	 */
1196187741Semax
1197187741Semax	UBT_NG_LOCK(sc);
1198187741Semax	if ((hook = sc->sc_hook) != NULL)
1199187741Semax		NG_HOOK_REF(hook);
1200187741Semax	UBT_NG_UNLOCK(sc);
1201187741Semax
1202187741Semax	if (hook == NULL) {
1203187741Semax		NG_FREE_M(*m);
1204187741Semax		return (ENETDOWN);
1205187741Semax	}
1206187741Semax
1207187741Semax	NG_SEND_DATA_ONLY(error, hook, *m);
1208187741Semax	NG_HOOK_UNREF(hook);
1209187741Semax
1210187741Semax	if (error != 0)
1211187741Semax		UBT_STAT_IERROR(sc);
1212187741Semax
1213187741Semax	return (error);
1214187741Semax} /* ubt_fwd_mbuf_up */
1215187741Semax
1216184610Salfred/****************************************************************************
1217184610Salfred ****************************************************************************
1218187494Semax **                                 Glue
1219184610Salfred ****************************************************************************
1220184610Salfred ****************************************************************************/
1221184610Salfred
1222184610Salfred/*
1223187741Semax * Schedule glue task. Should be called with sc_ng_mtx held.
1224187494Semax * Netgraph context.
1225184610Salfred */
1226184610Salfred
1227187741Semaxstatic void
1228187494Semaxubt_task_schedule(ubt_softc_p sc, int action)
1229184610Salfred{
1230187741Semax	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
1231184610Salfred
1232187741Semax	/*
1233187741Semax	 * Try to handle corner case when "start all" and "stop all"
1234187741Semax	 * actions can both be set before task is executed.
1235187741Semax	 *
1236187741Semax	 * The rules are
1237187741Semax	 *
1238187741Semax	 * sc_task_flags	action		new sc_task_flags
1239187741Semax	 * ------------------------------------------------------
1240187741Semax	 * 0			start		start
1241187741Semax	 * 0			stop		stop
1242187741Semax	 * start		start		start
1243187741Semax	 * start		stop		stop
1244187741Semax	 * stop			start		stop|start
1245187741Semax	 * stop			stop		stop
1246187741Semax	 * stop|start		start		stop|start
1247187741Semax	 * stop|start		stop		stop
1248187741Semax	 */
1249187494Semax
1250187741Semax	if (action != 0) {
1251187741Semax		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
1252187494Semax			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
1253187494Semax
1254187494Semax		sc->sc_task_flags |= action;
1255187494Semax	}
1256187494Semax
1257187494Semax	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
1258187741Semax		return;
1259187494Semax
1260187494Semax	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
1261187494Semax		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
1262187741Semax		return;
1263187494Semax	}
1264187494Semax
1265187494Semax	/* XXX: i think this should never happen */
1266187494Semax} /* ubt_task_schedule */
1267187494Semax
1268184610Salfred/*
1269187494Semax * Glue task. Examines sc_task_flags and does things depending on it.
1270187494Semax * Taskqueue context.
1271184610Salfred */
1272184610Salfred
1273187494Semaxstatic void
1274187494Semaxubt_task(void *context, int pending)
1275184610Salfred{
1276187741Semax	ubt_softc_p	sc = context;
1277187741Semax	int		task_flags, i;
1278184610Salfred
1279187741Semax	UBT_NG_LOCK(sc);
1280187494Semax	task_flags = sc->sc_task_flags;
1281187494Semax	sc->sc_task_flags = 0;
1282187741Semax	UBT_NG_UNLOCK(sc);
1283187494Semax
1284187741Semax	/*
1285187741Semax	 * Stop all USB transfers synchronously.
1286187741Semax	 * Stop interface #0 and #1 transfers at the same time and in the
1287194228Sthompsa	 * same loop. usbd_transfer_drain() will do appropriate locking.
1288187741Semax	 */
1289187494Semax
1290187741Semax	if (task_flags & UBT_FLAG_T_STOP_ALL)
1291187741Semax		for (i = 0; i < UBT_N_TRANSFER; i ++)
1292194228Sthompsa			usbd_transfer_drain(sc->sc_xfer[i]);
1293187494Semax
1294187741Semax	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
1295187494Semax	if (task_flags & UBT_FLAG_T_START_ALL) {
1296187494Semax		/*
1297187494Semax		 * Interface #0
1298187494Semax		 */
1299187494Semax
1300187741Semax		mtx_lock(&sc->sc_if_mtx);
1301187741Semax
1302187494Semax		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
1303187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
1304187494Semax
1305187494Semax		/*
1306187494Semax		 * Interface #1
1307187494Semax		 * Start both read and write isoc. transfers by default.
1308187494Semax		 * Get them going all the time even if we have nothing
1309187494Semax		 * to send to avoid any delays.
1310187494Semax		 */
1311187494Semax
1312187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
1313187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
1314187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
1315187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
1316187741Semax
1317187741Semax		mtx_unlock(&sc->sc_if_mtx);
1318184610Salfred	}
1319187494Semax
1320187494Semax 	/* Start outgoing control transfer */
1321187494Semax	if (task_flags & UBT_FLAG_T_START_CTRL) {
1322187741Semax		mtx_lock(&sc->sc_if_mtx);
1323187494Semax		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
1324187741Semax		mtx_unlock(&sc->sc_if_mtx);
1325184610Salfred	}
1326184610Salfred
1327187494Semax	/* Start outgoing bulk transfer */
1328187494Semax	if (task_flags & UBT_FLAG_T_START_BULK) {
1329187741Semax		mtx_lock(&sc->sc_if_mtx);
1330187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
1331187741Semax		mtx_unlock(&sc->sc_if_mtx);
1332184610Salfred	}
1333187494Semax} /* ubt_task */
1334187494Semax
1335187494Semax/****************************************************************************
1336187494Semax ****************************************************************************
1337187494Semax **                        Netgraph specific
1338187494Semax ****************************************************************************
1339187494Semax ****************************************************************************/
1340184610Salfred
1341187494Semax/*
1342187494Semax * Netgraph node constructor. Do not allow to create node of this type.
1343187494Semax * Netgraph context.
1344187494Semax */
1345184610Salfred
1346187494Semaxstatic int
1347187494Semaxng_ubt_constructor(node_p node)
1348187494Semax{
1349187494Semax	return (EINVAL);
1350187494Semax} /* ng_ubt_constructor */
1351184610Salfred
1352184610Salfred/*
1353187494Semax * Netgraph node destructor. Destroy node only when device has been detached.
1354187494Semax * Netgraph context.
1355184610Salfred */
1356184610Salfred
1357184610Salfredstatic int
1358187494Semaxng_ubt_shutdown(node_p node)
1359184610Salfred{
1360187494Semax	if (node->nd_flags & NGF_REALLY_DIE) {
1361187494Semax		/*
1362187494Semax                 * We came here because the USB device is being
1363187494Semax		 * detached, so stop being persistant.
1364187494Semax                 */
1365187494Semax		NG_NODE_SET_PRIVATE(node, NULL);
1366187494Semax		NG_NODE_UNREF(node);
1367187494Semax	} else
1368187494Semax		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
1369184610Salfred
1370187494Semax	return (0);
1371187494Semax} /* ng_ubt_shutdown */
1372184610Salfred
1373187494Semax/*
1374187494Semax * Create new hook. There can only be one.
1375187494Semax * Netgraph context.
1376187494Semax */
1377184610Salfred
1378187494Semaxstatic int
1379187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name)
1380187494Semax{
1381187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1382184610Salfred
1383187494Semax	if (strcmp(name, NG_UBT_HOOK) != 0)
1384187494Semax		return (EINVAL);
1385184610Salfred
1386187741Semax	UBT_NG_LOCK(sc);
1387187741Semax	if (sc->sc_hook != NULL) {
1388187741Semax		UBT_NG_UNLOCK(sc);
1389187741Semax
1390187494Semax		return (EISCONN);
1391187741Semax	}
1392184610Salfred
1393187494Semax	sc->sc_hook = hook;
1394187741Semax	UBT_NG_UNLOCK(sc);
1395184610Salfred
1396187494Semax	return (0);
1397187494Semax} /* ng_ubt_newhook */
1398184610Salfred
1399187494Semax/*
1400187494Semax * Connect hook. Start incoming USB transfers.
1401187494Semax * Netgraph context.
1402187494Semax */
1403184610Salfred
1404187494Semaxstatic int
1405187494Semaxng_ubt_connect(hook_p hook)
1406187494Semax{
1407187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1408184610Salfred
1409187494Semax	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
1410184610Salfred
1411187741Semax	UBT_NG_LOCK(sc);
1412187494Semax	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
1413187741Semax	UBT_NG_UNLOCK(sc);
1414184610Salfred
1415184610Salfred	return (0);
1416187494Semax} /* ng_ubt_connect */
1417184610Salfred
1418184610Salfred/*
1419187494Semax * Disconnect hook.
1420187494Semax * Netgraph context.
1421184610Salfred */
1422184610Salfred
1423184610Salfredstatic int
1424184610Salfredng_ubt_disconnect(hook_p hook)
1425184610Salfred{
1426187741Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1427184610Salfred
1428187741Semax	UBT_NG_LOCK(sc);
1429184610Salfred
1430187741Semax	if (hook != sc->sc_hook) {
1431187741Semax		UBT_NG_UNLOCK(sc);
1432184610Salfred
1433187494Semax		return (EINVAL);
1434187741Semax	}
1435184610Salfred
1436187494Semax	sc->sc_hook = NULL;
1437184610Salfred
1438187741Semax	/* Kick off task to stop all USB xfers */
1439187741Semax	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
1440184610Salfred
1441187494Semax	/* Drain queues */
1442187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
1443187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
1444187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
1445184610Salfred
1446187741Semax	UBT_NG_UNLOCK(sc);
1447184610Salfred
1448187494Semax	return (0);
1449187494Semax} /* ng_ubt_disconnect */
1450187494Semax
1451184610Salfred/*
1452187494Semax * Process control message.
1453187494Semax * Netgraph context.
1454184610Salfred */
1455184610Salfred
1456184610Salfredstatic int
1457184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
1458184610Salfred{
1459187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1460187494Semax	struct ng_mesg		*msg, *rsp = NULL;
1461187494Semax	struct ng_bt_mbufq	*q;
1462187494Semax	int			error = 0, queue, qlen;
1463184610Salfred
1464184610Salfred	NGI_GET_MSG(item, msg);
1465184610Salfred
1466184610Salfred	switch (msg->header.typecookie) {
1467184610Salfred	case NGM_GENERIC_COOKIE:
1468184610Salfred		switch (msg->header.cmd) {
1469184610Salfred		case NGM_TEXT_STATUS:
1470184610Salfred			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
1471187494Semax			if (rsp == NULL) {
1472184610Salfred				error = ENOMEM;
1473187494Semax				break;
1474187494Semax			}
1475187494Semax
1476187494Semax			snprintf(rsp->data, NG_TEXTRESPONSE,
1477187494Semax				"Hook: %s\n" \
1478187494Semax				"Task flags: %#x\n" \
1479187494Semax				"Debug: %d\n" \
1480187494Semax				"CMD queue: [have:%d,max:%d]\n" \
1481187494Semax				"ACL queue: [have:%d,max:%d]\n" \
1482187494Semax				"SCO queue: [have:%d,max:%d]",
1483187741Semax				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
1484187494Semax				sc->sc_task_flags,
1485187494Semax				sc->sc_debug,
1486187494Semax				sc->sc_cmdq.len,
1487187494Semax				sc->sc_cmdq.maxlen,
1488187494Semax				sc->sc_aclq.len,
1489187494Semax				sc->sc_aclq.maxlen,
1490187494Semax				sc->sc_scoq.len,
1491187494Semax				sc->sc_scoq.maxlen);
1492184610Salfred			break;
1493184610Salfred
1494184610Salfred		default:
1495184610Salfred			error = EINVAL;
1496184610Salfred			break;
1497184610Salfred		}
1498184610Salfred		break;
1499184610Salfred
1500184610Salfred	case NGM_UBT_COOKIE:
1501184610Salfred		switch (msg->header.cmd) {
1502184610Salfred		case NGM_UBT_NODE_SET_DEBUG:
1503187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
1504184610Salfred				error = EMSGSIZE;
1505187494Semax				break;
1506187494Semax			}
1507187494Semax
1508187494Semax			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
1509184610Salfred			break;
1510184610Salfred
1511184610Salfred		case NGM_UBT_NODE_GET_DEBUG:
1512184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
1513184610Salfred			    M_NOWAIT);
1514187494Semax			if (rsp == NULL) {
1515184610Salfred				error = ENOMEM;
1516187494Semax				break;
1517187494Semax			}
1518187494Semax
1519187494Semax			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
1520184610Salfred			break;
1521184610Salfred
1522184610Salfred		case NGM_UBT_NODE_SET_QLEN:
1523187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1524184610Salfred				error = EMSGSIZE;
1525187494Semax				break;
1526187494Semax			}
1527184610Salfred
1528187494Semax			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1529187494Semax			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
1530184610Salfred
1531187494Semax			switch (queue) {
1532187494Semax			case NGM_UBT_NODE_QUEUE_CMD:
1533187494Semax				q = &sc->sc_cmdq;
1534187494Semax				break;
1535184610Salfred
1536187494Semax			case NGM_UBT_NODE_QUEUE_ACL:
1537187494Semax				q = &sc->sc_aclq;
1538187494Semax				break;
1539184610Salfred
1540187494Semax			case NGM_UBT_NODE_QUEUE_SCO:
1541187494Semax				q = &sc->sc_scoq;
1542187494Semax				break;
1543184610Salfred
1544187494Semax			default:
1545187494Semax				error = EINVAL;
1546187494Semax				goto done;
1547187494Semax				/* NOT REACHED */
1548184610Salfred			}
1549187494Semax
1550187494Semax			q->maxlen = qlen;
1551184610Salfred			break;
1552184610Salfred
1553184610Salfred		case NGM_UBT_NODE_GET_QLEN:
1554184610Salfred			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1555184610Salfred				error = EMSGSIZE;
1556184610Salfred				break;
1557184610Salfred			}
1558187494Semax
1559184610Salfred			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1560187494Semax
1561184610Salfred			switch (queue) {
1562184610Salfred			case NGM_UBT_NODE_QUEUE_CMD:
1563184610Salfred				q = &sc->sc_cmdq;
1564184610Salfred				break;
1565184610Salfred
1566184610Salfred			case NGM_UBT_NODE_QUEUE_ACL:
1567184610Salfred				q = &sc->sc_aclq;
1568184610Salfred				break;
1569184610Salfred
1570184610Salfred			case NGM_UBT_NODE_QUEUE_SCO:
1571184610Salfred				q = &sc->sc_scoq;
1572184610Salfred				break;
1573184610Salfred
1574184610Salfred			default:
1575184610Salfred				error = EINVAL;
1576187494Semax				goto done;
1577187494Semax				/* NOT REACHED */
1578187494Semax			}
1579187494Semax
1580187494Semax			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
1581187494Semax				M_NOWAIT);
1582187494Semax			if (rsp == NULL) {
1583187494Semax				error = ENOMEM;
1584184610Salfred				break;
1585184610Salfred			}
1586184610Salfred
1587187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
1588187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
1589184610Salfred			break;
1590184610Salfred
1591184610Salfred		case NGM_UBT_NODE_GET_STAT:
1592184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
1593184610Salfred			    M_NOWAIT);
1594187494Semax			if (rsp == NULL) {
1595184610Salfred				error = ENOMEM;
1596187494Semax				break;
1597184610Salfred			}
1598187494Semax
1599187494Semax			bcopy(&sc->sc_stat, rsp->data,
1600187494Semax				sizeof(ng_ubt_node_stat_ep));
1601184610Salfred			break;
1602184610Salfred
1603184610Salfred		case NGM_UBT_NODE_RESET_STAT:
1604187494Semax			UBT_STAT_RESET(sc);
1605184610Salfred			break;
1606184610Salfred
1607184610Salfred		default:
1608184610Salfred			error = EINVAL;
1609184610Salfred			break;
1610184610Salfred		}
1611184610Salfred		break;
1612184610Salfred
1613184610Salfred	default:
1614184610Salfred		error = EINVAL;
1615184610Salfred		break;
1616184610Salfred	}
1617187494Semaxdone:
1618184610Salfred	NG_RESPOND_MSG(error, node, item, rsp);
1619184610Salfred	NG_FREE_MSG(msg);
1620184610Salfred
1621184610Salfred	return (error);
1622187494Semax} /* ng_ubt_rcvmsg */
1623184610Salfred
1624184610Salfred/*
1625187494Semax * Process data.
1626187494Semax * Netgraph context.
1627184610Salfred */
1628184610Salfred
1629184610Salfredstatic int
1630184610Salfredng_ubt_rcvdata(hook_p hook, item_p item)
1631184610Salfred{
1632187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1633187494Semax	struct mbuf		*m;
1634187494Semax	struct ng_bt_mbufq	*q;
1635187494Semax	int			action, error = 0;
1636184610Salfred
1637184610Salfred	if (hook != sc->sc_hook) {
1638184610Salfred		error = EINVAL;
1639184610Salfred		goto done;
1640184610Salfred	}
1641187494Semax
1642187494Semax	/* Deatch mbuf and get HCI frame type */
1643184610Salfred	NGI_GET_M(item, m);
1644184610Salfred
1645187494Semax	/*
1646187494Semax	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
1647187494Semax	 * 2 bytes connection handle and at least 1 byte of length.
1648187494Semax	 * Panic on data frame that has size smaller than 4 bytes (it
1649187494Semax	 * should not happen)
1650187494Semax	 */
1651187494Semax
1652187494Semax	if (m->m_pkthdr.len < 4)
1653187494Semax		panic("HCI frame size is too small! pktlen=%d\n",
1654187494Semax			m->m_pkthdr.len);
1655187494Semax
1656187494Semax	/* Process HCI frame */
1657184610Salfred	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
1658184610Salfred	case NG_HCI_CMD_PKT:
1659235000Shselasky		if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
1660187494Semax			panic("HCI command frame size is too big! " \
1661187494Semax				"buffer size=%zd, packet len=%d\n",
1662187494Semax				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
1663187494Semax
1664184610Salfred		q = &sc->sc_cmdq;
1665187494Semax		action = UBT_FLAG_T_START_CTRL;
1666184610Salfred		break;
1667184610Salfred
1668184610Salfred	case NG_HCI_ACL_DATA_PKT:
1669187494Semax		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
1670187494Semax			panic("ACL data frame size is too big! " \
1671187494Semax				"buffer size=%d, packet len=%d\n",
1672187494Semax				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
1673187494Semax
1674184610Salfred		q = &sc->sc_aclq;
1675187494Semax		action = UBT_FLAG_T_START_BULK;
1676184610Salfred		break;
1677184610Salfred
1678184610Salfred	case NG_HCI_SCO_DATA_PKT:
1679184610Salfred		q = &sc->sc_scoq;
1680187494Semax		action = 0;
1681184610Salfred		break;
1682184610Salfred
1683184610Salfred	default:
1684187494Semax		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
1685187494Semax			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
1686184610Salfred
1687184610Salfred		NG_FREE_M(m);
1688184610Salfred		error = EINVAL;
1689184610Salfred		goto done;
1690187494Semax		/* NOT REACHED */
1691184610Salfred	}
1692184610Salfred
1693187741Semax	UBT_NG_LOCK(sc);
1694184610Salfred	if (NG_BT_MBUFQ_FULL(q)) {
1695187494Semax		NG_BT_MBUFQ_DROP(q);
1696187741Semax		UBT_NG_UNLOCK(sc);
1697187494Semax
1698187494Semax		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
1699187494Semax			*mtod(m, uint8_t *), m->m_pkthdr.len);
1700187494Semax
1701184610Salfred		NG_FREE_M(m);
1702184610Salfred	} else {
1703187494Semax		/* Loose HCI packet type, enqueue mbuf and kick off task */
1704187494Semax		m_adj(m, sizeof(uint8_t));
1705184610Salfred		NG_BT_MBUFQ_ENQUEUE(q, m);
1706187494Semax		ubt_task_schedule(sc, action);
1707187741Semax		UBT_NG_UNLOCK(sc);
1708184610Salfred	}
1709184610Salfreddone:
1710184610Salfred	NG_FREE_ITEM(item);
1711184610Salfred
1712187494Semax	return (error);
1713187494Semax} /* ng_ubt_rcvdata */
1714187494Semax
1715187494Semax/****************************************************************************
1716187494Semax ****************************************************************************
1717187494Semax **                              Module
1718187494Semax ****************************************************************************
1719187494Semax ****************************************************************************/
1720187494Semax
1721187494Semax/*
1722187494Semax * Load/Unload the driver module
1723187494Semax */
1724187494Semax
1725187494Semaxstatic int
1726187494Semaxubt_modevent(module_t mod, int event, void *data)
1727187494Semax{
1728187494Semax	int	error;
1729187494Semax
1730187494Semax	switch (event) {
1731187494Semax	case MOD_LOAD:
1732187494Semax		error = ng_newtype(&typestruct);
1733187494Semax		if (error != 0)
1734187494Semax			printf("%s: Could not register Netgraph node type, " \
1735187494Semax				"error=%d\n", NG_UBT_NODE_TYPE, error);
1736187494Semax		break;
1737187494Semax
1738187494Semax	case MOD_UNLOAD:
1739187494Semax		error = ng_rmtype(&typestruct);
1740187494Semax		break;
1741187494Semax
1742187494Semax	default:
1743187494Semax		error = EOPNOTSUPP;
1744187494Semax		break;
1745184610Salfred	}
1746187494Semax
1747184610Salfred	return (error);
1748187494Semax} /* ubt_modevent */
1749187494Semax
1750187494Semaxstatic devclass_t	ubt_devclass;
1751187494Semax
1752187494Semaxstatic device_method_t	ubt_methods[] =
1753187494Semax{
1754187494Semax	DEVMETHOD(device_probe,	ubt_probe),
1755187494Semax	DEVMETHOD(device_attach, ubt_attach),
1756187494Semax	DEVMETHOD(device_detach, ubt_detach),
1757244956Shselasky	DEVMETHOD_END
1758187494Semax};
1759187494Semax
1760187494Semaxstatic driver_t		ubt_driver =
1761187494Semax{
1762187494Semax	.name =	   "ubt",
1763187494Semax	.methods = ubt_methods,
1764187494Semax	.size =	   sizeof(struct ubt_softc),
1765187494Semax};
1766187494Semax
1767189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
1768187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
1769187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
1770187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
1771188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
1772187494Semax
1773