ng_ubt.c revision 190728
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: head/sys/dev/usb/bluetooth/ng_ubt.c 190728 2009-04-05 18:19:30Z thompsa $
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
95188746Sthompsa#include "usbdevs.h"
96188942Sthompsa#include <dev/usb/usb.h>
97188942Sthompsa#include <dev/usb/usb_mfunc.h>
98188942Sthompsa#include <dev/usb/usb_error.h>
99184610Salfred
100184610Salfred#define	USB_DEBUG_VAR usb2_debug
101184610Salfred
102188942Sthompsa#include <dev/usb/usb_core.h>
103188942Sthompsa#include <dev/usb/usb_debug.h>
104188942Sthompsa#include <dev/usb/usb_parse.h>
105188942Sthompsa#include <dev/usb/usb_lookup.h>
106188942Sthompsa#include <dev/usb/usb_util.h>
107188942Sthompsa#include <dev/usb/usb_busdma.h>
108188942Sthompsa#include <dev/usb/usb_process.h>
109188942Sthompsa#include <dev/usb/usb_transfer.h>
110184610Salfred
111184610Salfred#include <sys/mbuf.h>
112187494Semax#include <sys/taskqueue.h>
113184610Salfred
114184610Salfred#include <netgraph/ng_message.h>
115184610Salfred#include <netgraph/netgraph.h>
116184610Salfred#include <netgraph/ng_parse.h>
117184610Salfred#include <netgraph/bluetooth/include/ng_bluetooth.h>
118184610Salfred#include <netgraph/bluetooth/include/ng_hci.h>
119184610Salfred#include <netgraph/bluetooth/include/ng_ubt.h>
120184610Salfred
121188942Sthompsa#include <dev/usb/bluetooth/ng_ubt_var.h>
122184610Salfred
123187494Semaxstatic int		ubt_modevent(module_t, int, void *);
124187494Semaxstatic device_probe_t	ubt_probe;
125187494Semaxstatic device_attach_t	ubt_attach;
126187494Semaxstatic device_detach_t	ubt_detach;
127184610Salfred
128187741Semaxstatic void		ubt_task_schedule(ubt_softc_p, int);
129187494Semaxstatic task_fn_t	ubt_task;
130184610Salfred
131187741Semax#define	ubt_xfer_start(sc, i)	usb2_transfer_start((sc)->sc_xfer[(i)])
132187741Semax
133187494Semax/* Netgraph methods */
134187494Semaxstatic ng_constructor_t	ng_ubt_constructor;
135187494Semaxstatic ng_shutdown_t	ng_ubt_shutdown;
136187494Semaxstatic ng_newhook_t	ng_ubt_newhook;
137187494Semaxstatic ng_connect_t	ng_ubt_connect;
138187494Semaxstatic ng_disconnect_t	ng_ubt_disconnect;
139187494Semaxstatic ng_rcvmsg_t	ng_ubt_rcvmsg;
140187494Semaxstatic ng_rcvdata_t	ng_ubt_rcvdata;
141184610Salfred
142184610Salfred/* Queue length */
143187494Semaxstatic const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
144184610Salfred{
145187494Semax	{ "queue", &ng_parse_int32_type, },
146187494Semax	{ "qlen",  &ng_parse_int32_type, },
147187494Semax	{ NULL, }
148184610Salfred};
149187494Semaxstatic const struct ng_parse_type		ng_ubt_node_qlen_type =
150187494Semax{
151184610Salfred	&ng_parse_struct_type,
152184610Salfred	&ng_ubt_node_qlen_type_fields
153184610Salfred};
154184610Salfred
155184610Salfred/* Stat info */
156187494Semaxstatic const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
157184610Salfred{
158187494Semax	{ "pckts_recv", &ng_parse_uint32_type, },
159187494Semax	{ "bytes_recv", &ng_parse_uint32_type, },
160187494Semax	{ "pckts_sent", &ng_parse_uint32_type, },
161187494Semax	{ "bytes_sent", &ng_parse_uint32_type, },
162187494Semax	{ "oerrors",    &ng_parse_uint32_type, },
163187494Semax	{ "ierrors",    &ng_parse_uint32_type, },
164187494Semax	{ NULL, }
165184610Salfred};
166187494Semaxstatic const struct ng_parse_type		ng_ubt_node_stat_type =
167187494Semax{
168184610Salfred	&ng_parse_struct_type,
169184610Salfred	&ng_ubt_node_stat_type_fields
170184610Salfred};
171184610Salfred
172184610Salfred/* Netgraph node command list */
173187494Semaxstatic const struct ng_cmdlist			ng_ubt_cmdlist[] =
174187494Semax{
175184610Salfred	{
176184610Salfred		NGM_UBT_COOKIE,
177184610Salfred		NGM_UBT_NODE_SET_DEBUG,
178184610Salfred		"set_debug",
179184610Salfred		&ng_parse_uint16_type,
180184610Salfred		NULL
181184610Salfred	},
182184610Salfred	{
183184610Salfred		NGM_UBT_COOKIE,
184184610Salfred		NGM_UBT_NODE_GET_DEBUG,
185184610Salfred		"get_debug",
186184610Salfred		NULL,
187184610Salfred		&ng_parse_uint16_type
188184610Salfred	},
189184610Salfred	{
190184610Salfred		NGM_UBT_COOKIE,
191184610Salfred		NGM_UBT_NODE_SET_QLEN,
192184610Salfred		"set_qlen",
193184610Salfred		&ng_ubt_node_qlen_type,
194184610Salfred		NULL
195184610Salfred	},
196184610Salfred	{
197184610Salfred		NGM_UBT_COOKIE,
198184610Salfred		NGM_UBT_NODE_GET_QLEN,
199184610Salfred		"get_qlen",
200184610Salfred		&ng_ubt_node_qlen_type,
201184610Salfred		&ng_ubt_node_qlen_type
202184610Salfred	},
203184610Salfred	{
204184610Salfred		NGM_UBT_COOKIE,
205184610Salfred		NGM_UBT_NODE_GET_STAT,
206184610Salfred		"get_stat",
207184610Salfred		NULL,
208184610Salfred		&ng_ubt_node_stat_type
209184610Salfred	},
210184610Salfred	{
211184610Salfred		NGM_UBT_COOKIE,
212184610Salfred		NGM_UBT_NODE_RESET_STAT,
213184610Salfred		"reset_stat",
214184610Salfred		NULL,
215184610Salfred		NULL
216184610Salfred	},
217187494Semax	{ 0, }
218184610Salfred};
219184610Salfred
220184610Salfred/* Netgraph node type */
221187494Semaxstatic struct ng_type	typestruct =
222187494Semax{
223187494Semax	.version = 	NG_ABI_VERSION,
224187494Semax	.name =		NG_UBT_NODE_TYPE,
225187494Semax	.constructor =	ng_ubt_constructor,
226187494Semax	.rcvmsg =	ng_ubt_rcvmsg,
227187494Semax	.shutdown =	ng_ubt_shutdown,
228187494Semax	.newhook =	ng_ubt_newhook,
229187494Semax	.connect =	ng_ubt_connect,
230187494Semax	.rcvdata =	ng_ubt_rcvdata,
231187494Semax	.disconnect =	ng_ubt_disconnect,
232187494Semax	.cmdlist =	ng_ubt_cmdlist
233184610Salfred};
234184610Salfred
235187494Semax/****************************************************************************
236187494Semax ****************************************************************************
237187494Semax **                              USB specific
238187494Semax ****************************************************************************
239187494Semax ****************************************************************************/
240187494Semax
241184610Salfred/* USB methods */
242187494Semaxstatic usb2_callback_t	ubt_ctrl_write_callback;
243187494Semaxstatic usb2_callback_t	ubt_intr_read_callback;
244187494Semaxstatic usb2_callback_t	ubt_bulk_read_callback;
245187494Semaxstatic usb2_callback_t	ubt_bulk_write_callback;
246187494Semaxstatic usb2_callback_t	ubt_isoc_read_callback;
247187494Semaxstatic usb2_callback_t	ubt_isoc_write_callback;
248184610Salfred
249187741Semaxstatic int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
250187741Semaxstatic int		ubt_isoc_read_one_frame(struct usb2_xfer *, int);
251184610Salfred
252187494Semax/*
253187494Semax * USB config
254187494Semax *
255187494Semax * The following desribes usb transfers that could be submitted on USB device.
256187494Semax *
257187494Semax * Interface 0 on the USB device must present the following endpoints
258187494Semax *	1) Interrupt endpoint to receive HCI events
259187494Semax *	2) Bulk IN endpoint to receive ACL data
260187494Semax *	3) Bulk OUT endpoint to send ACL data
261187494Semax *
262187494Semax * Interface 1 on the USB device must present the following endpoints
263187494Semax *	1) Isochronous IN endpoint to receive SCO data
264187494Semax *	2) Isochronous OUT endpoint to send SCO data
265187494Semax */
266184610Salfred
267187494Semaxstatic const struct usb2_config		ubt_config[UBT_N_TRANSFER] =
268187494Semax{
269187494Semax	/*
270187494Semax	 * Interface #0
271187494Semax 	 */
272184610Salfred
273187494Semax	/* Outgoing bulk transfer - ACL packets */
274187494Semax	[UBT_IF_0_BULK_DT_WR] = {
275187494Semax		.type =		UE_BULK,
276187494Semax		.endpoint =	UE_ADDR_ANY,
277187494Semax		.direction =	UE_DIR_OUT,
278187741Semax		.if_index = 	0,
279187494Semax		.mh.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
280187741Semax		.mh.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
281187494Semax		.mh.callback =	&ubt_bulk_write_callback,
282184610Salfred	},
283187494Semax	/* Incoming bulk transfer - ACL packets */
284187494Semax	[UBT_IF_0_BULK_DT_RD] = {
285187494Semax		.type =		UE_BULK,
286187494Semax		.endpoint =	UE_ADDR_ANY,
287187494Semax		.direction =	UE_DIR_IN,
288187741Semax		.if_index = 	0,
289187494Semax		.mh.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
290187494Semax		.mh.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
291187494Semax		.mh.callback =	&ubt_bulk_read_callback,
292184610Salfred	},
293187494Semax	/* Incoming interrupt transfer - HCI events */
294187494Semax	[UBT_IF_0_INTR_DT_RD] = {
295187494Semax		.type =		UE_INTERRUPT,
296187494Semax		.endpoint =	UE_ADDR_ANY,
297187494Semax		.direction =	UE_DIR_IN,
298187741Semax		.if_index = 	0,
299187494Semax		.mh.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
300187494Semax		.mh.bufsize =	UBT_INTR_BUFFER_SIZE,
301187494Semax		.mh.callback =	&ubt_intr_read_callback,
302184610Salfred	},
303187494Semax	/* Outgoing control transfer - HCI commands */
304187494Semax	[UBT_IF_0_CTRL_DT_WR] = {
305187494Semax		.type =		UE_CONTROL,
306187494Semax		.endpoint =	0x00,	/* control pipe */
307187494Semax		.direction =	UE_DIR_ANY,
308187741Semax		.if_index = 	0,
309187494Semax		.mh.bufsize =	UBT_CTRL_BUFFER_SIZE,
310187494Semax		.mh.callback =	&ubt_ctrl_write_callback,
311187494Semax		.mh.timeout =	5000,	/* 5 seconds */
312184610Salfred	},
313184610Salfred
314187494Semax	/*
315187494Semax	 * Interface #1
316187494Semax 	 */
317184610Salfred
318187494Semax	/* Incoming isochronous transfer #1 - SCO packets */
319187494Semax	[UBT_IF_1_ISOC_DT_RD1] = {
320187494Semax		.type =		UE_ISOCHRONOUS,
321187494Semax		.endpoint =	UE_ADDR_ANY,
322187494Semax		.direction =	UE_DIR_IN,
323187741Semax		.if_index = 	1,
324187494Semax		.mh.bufsize =	0,	/* use "wMaxPacketSize * frames" */
325187494Semax		.mh.frames =	UBT_ISOC_NFRAMES,
326187494Semax		.mh.flags =	{ .short_xfer_ok = 1, },
327187494Semax		.mh.callback =	&ubt_isoc_read_callback,
328184610Salfred	},
329187494Semax	/* Incoming isochronous transfer #2 - SCO packets */
330187494Semax	[UBT_IF_1_ISOC_DT_RD2] = {
331187494Semax		.type =		UE_ISOCHRONOUS,
332187494Semax		.endpoint =	UE_ADDR_ANY,
333187494Semax		.direction =	UE_DIR_IN,
334187741Semax		.if_index = 	1,
335187494Semax		.mh.bufsize =	0,	/* use "wMaxPacketSize * frames" */
336187494Semax		.mh.frames =	UBT_ISOC_NFRAMES,
337187494Semax		.mh.flags =	{ .short_xfer_ok = 1, },
338187494Semax		.mh.callback =	&ubt_isoc_read_callback,
339184610Salfred	},
340187494Semax	/* Outgoing isochronous transfer #1 - SCO packets */
341187494Semax	[UBT_IF_1_ISOC_DT_WR1] = {
342187494Semax		.type =		UE_ISOCHRONOUS,
343187494Semax		.endpoint =	UE_ADDR_ANY,
344187494Semax		.direction =	UE_DIR_OUT,
345187741Semax		.if_index = 	1,
346187494Semax		.mh.bufsize =	0,	/* use "wMaxPacketSize * frames" */
347187494Semax		.mh.frames =	UBT_ISOC_NFRAMES,
348187494Semax		.mh.flags =	{ .short_xfer_ok = 1, },
349187494Semax		.mh.callback =	&ubt_isoc_write_callback,
350184610Salfred	},
351187494Semax	/* Outgoing isochronous transfer #2 - SCO packets */
352187494Semax	[UBT_IF_1_ISOC_DT_WR2] = {
353187494Semax		.type =		UE_ISOCHRONOUS,
354187494Semax		.endpoint =	UE_ADDR_ANY,
355187494Semax		.direction =	UE_DIR_OUT,
356187741Semax		.if_index = 	1,
357187494Semax		.mh.bufsize =	0,	/* use "wMaxPacketSize * frames" */
358187494Semax		.mh.frames =	UBT_ISOC_NFRAMES,
359187494Semax		.mh.flags =	{ .short_xfer_ok = 1, },
360187494Semax		.mh.callback =	&ubt_isoc_write_callback,
361184610Salfred	},
362184610Salfred};
363184610Salfred
364184610Salfred/*
365184610Salfred * If for some reason device should not be attached then put
366184610Salfred * VendorID/ProductID pair into the list below. The format is
367184610Salfred * as follows:
368184610Salfred *
369187494Semax *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
370184610Salfred *
371184610Salfred * where VENDOR_ID and PRODUCT_ID are hex numbers.
372184610Salfred */
373187741Semax
374187741Semaxstatic const struct usb2_device_id ubt_ignore_devs[] =
375187741Semax{
376184610Salfred	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
377187494Semax	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
378184610Salfred};
379184610Salfred
380184610Salfred/* List of supported bluetooth devices */
381187741Semaxstatic const struct usb2_device_id ubt_devs[] =
382187741Semax{
383187494Semax	/* Generic Bluetooth class devices */
384187494Semax	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
385187494Semax	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
386187494Semax	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
387184610Salfred
388184610Salfred	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
389187494Semax	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
390184610Salfred};
391184610Salfred
392184610Salfred/*
393187494Semax * Probe for a USB Bluetooth device.
394187494Semax * USB context.
395184610Salfred */
396184610Salfred
397184610Salfredstatic int
398184610Salfredubt_probe(device_t dev)
399184610Salfred{
400187494Semax	struct usb2_attach_arg	*uaa = device_get_ivars(dev);
401184610Salfred
402187494Semax	if (uaa->usb2_mode != USB_MODE_HOST)
403184610Salfred		return (ENXIO);
404187494Semax
405187494Semax	if (uaa->info.bIfaceIndex != 0)
406184610Salfred		return (ENXIO);
407187494Semax
408187865Semax	if (uaa->use_generic == 0)
409187865Semax		return (ENXIO);
410187865Semax
411184610Salfred	if (usb2_lookup_id_by_uaa(ubt_ignore_devs,
412187494Semax			sizeof(ubt_ignore_devs), uaa) == 0)
413184610Salfred		return (ENXIO);
414187494Semax
415184610Salfred	return (usb2_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa));
416187494Semax} /* ubt_probe */
417184610Salfred
418184610Salfred/*
419187494Semax * Attach the device.
420187494Semax * USB context.
421184610Salfred */
422184610Salfred
423184610Salfredstatic int
424184610Salfredubt_attach(device_t dev)
425184610Salfred{
426187494Semax	struct usb2_attach_arg		*uaa = device_get_ivars(dev);
427187494Semax	struct ubt_softc		*sc = device_get_softc(dev);
428187494Semax	struct usb2_endpoint_descriptor	*ed;
429190728Sthompsa	struct usb2_interface_descriptor *id;
430187494Semax	uint16_t			wMaxPacketSize;
431187741Semax	uint8_t				alt_index, i, j;
432187741Semax	uint8_t				iface_index[2] = { 0, 1 };
433184610Salfred
434184610Salfred	device_set_usb2_desc(dev);
435184610Salfred
436187741Semax	sc->sc_dev = dev;
437187741Semax	sc->sc_debug = NG_UBT_WARN_LEVEL;
438184610Salfred
439187494Semax	/*
440187494Semax	 * Create Netgraph node
441187494Semax	 */
442187494Semax
443187494Semax	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
444187741Semax		UBT_ALERT(sc, "could not create Netgraph node\n");
445187494Semax		return (ENXIO);
446187494Semax	}
447187494Semax
448187494Semax	/* Name Netgraph node */
449187741Semax	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
450187741Semax		UBT_ALERT(sc, "could not name Netgraph node\n");
451187494Semax		NG_NODE_UNREF(sc->sc_node);
452187494Semax		return (ENXIO);
453187494Semax	}
454187494Semax	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
455187494Semax	NG_NODE_FORCE_WRITER(sc->sc_node);
456187494Semax
457184610Salfred	/*
458184610Salfred	 * Initialize device softc structure
459184610Salfred	 */
460184610Salfred
461187494Semax	/* initialize locks */
462187741Semax	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
463187741Semax	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
464187494Semax
465187494Semax	/* initialize packet queues */
466184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
467184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
468187494Semax	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
469184610Salfred
470187494Semax	/* initialize glue task */
471187741Semax	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
472184610Salfred
473184610Salfred	/*
474184610Salfred	 * Configure Bluetooth USB device. Discover all required USB
475184610Salfred	 * interfaces and endpoints.
476184610Salfred	 *
477184610Salfred	 * USB device must present two interfaces:
478184610Salfred	 * 1) Interface 0 that has 3 endpoints
479184610Salfred	 *	1) Interrupt endpoint to receive HCI events
480184610Salfred	 *	2) Bulk IN endpoint to receive ACL data
481184610Salfred	 *	3) Bulk OUT endpoint to send ACL data
482184610Salfred	 *
483184610Salfred	 * 2) Interface 1 then has 2 endpoints
484184610Salfred	 *	1) Isochronous IN endpoint to receive SCO data
485184610Salfred 	 *	2) Isochronous OUT endpoint to send SCO data
486184610Salfred	 *
487184610Salfred	 * Interface 1 (with isochronous endpoints) has several alternate
488184610Salfred	 * configurations with different packet size.
489184610Salfred	 */
490184610Salfred
491184610Salfred	/*
492187741Semax	 * For interface #1 search alternate settings, and find
493187741Semax	 * the descriptor with the largest wMaxPacketSize
494184610Salfred	 */
495184610Salfred
496184610Salfred	wMaxPacketSize = 0;
497187494Semax	alt_index = 0;
498184610Salfred	i = 0;
499184610Salfred	j = 0;
500190728Sthompsa	ed = NULL;
501187494Semax
502190728Sthompsa	/*
503190728Sthompsa	 * Search through all the descriptors looking for the largest
504190728Sthompsa	 * packet size:
505190728Sthompsa	 */
506190728Sthompsa	while ((ed = (struct usb2_endpoint_descriptor *)usb2_desc_foreach(
507190728Sthompsa	    usb2_get_config_descriptor(uaa->device),
508190728Sthompsa	    (struct usb2_descriptor *)ed))) {
509184610Salfred
510190728Sthompsa		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
511190728Sthompsa		    (ed->bLength >= sizeof(*id))) {
512190728Sthompsa			id = (struct usb2_interface_descriptor *)ed;
513190728Sthompsa			i = id->bInterfaceNumber;
514190728Sthompsa			j = id->bAlternateSetting;
515184610Salfred		}
516187494Semax
517190728Sthompsa		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
518190728Sthompsa		    (ed->bLength >= sizeof(*ed)) &&
519190728Sthompsa		    (i == 1)) {
520190728Sthompsa			uint16_t temp;
521190728Sthompsa
522190728Sthompsa			temp = UGETW(ed->wMaxPacketSize);
523190728Sthompsa			if (temp > wMaxPacketSize) {
524190728Sthompsa				wMaxPacketSize = temp;
525190728Sthompsa				alt_index = j;
526190728Sthompsa			}
527184610Salfred		}
528184610Salfred	}
529184610Salfred
530187741Semax	/* Set alt configuration on interface #1 only if we found it */
531187494Semax	if (wMaxPacketSize > 0 &&
532187494Semax	    usb2_set_alt_interface_index(uaa->device, 1, alt_index)) {
533187741Semax		UBT_ALERT(sc, "could not set alternate setting %d " \
534187494Semax			"for interface 1!\n", alt_index);
535184610Salfred		goto detach;
536184610Salfred	}
537184610Salfred
538187741Semax	/* Setup transfers for both interfaces */
539187741Semax	if (usb2_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
540187741Semax			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
541187741Semax		UBT_ALERT(sc, "could not allocate transfers\n");
542184610Salfred		goto detach;
543184610Salfred	}
544184610Salfred
545187494Semax	/* Claim all interfaces on the device */
546187494Semax	for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++)
547184610Salfred		usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
548184610Salfred
549187494Semax	return (0); /* success */
550184610Salfred
551184610Salfreddetach:
552184610Salfred	ubt_detach(dev);
553184610Salfred
554184610Salfred	return (ENXIO);
555187494Semax} /* ubt_attach */
556184610Salfred
557184610Salfred/*
558187494Semax * Detach the device.
559187494Semax * USB context.
560184610Salfred */
561184610Salfred
562184610Salfredint
563184610Salfredubt_detach(device_t dev)
564184610Salfred{
565187494Semax	struct ubt_softc	*sc = device_get_softc(dev);
566187494Semax	node_p			node = sc->sc_node;
567184610Salfred
568187494Semax	/* Destroy Netgraph node */
569187494Semax	if (node != NULL) {
570187494Semax		sc->sc_node = NULL;
571187494Semax		NG_NODE_REALLY_DIE(node);
572187494Semax		ng_rmnode_self(node);
573184610Salfred	}
574184610Salfred
575187741Semax	/* Make sure ubt_task in gone */
576187741Semax	taskqueue_drain(taskqueue_swi, &sc->sc_task);
577187741Semax
578187494Semax	/* Free USB transfers, if any */
579187494Semax	usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
580184610Salfred
581187494Semax	/* Destroy queues */
582187741Semax	UBT_NG_LOCK(sc);
583184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
584184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
585184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
586187741Semax	UBT_NG_UNLOCK(sc);
587184610Salfred
588187741Semax	mtx_destroy(&sc->sc_if_mtx);
589187741Semax	mtx_destroy(&sc->sc_ng_mtx);
590187494Semax
591184610Salfred	return (0);
592187494Semax} /* ubt_detach */
593184610Salfred
594187494Semax/*
595187494Semax * Called when outgoing control request (HCI command) has completed, i.e.
596187494Semax * HCI command was sent to the device.
597187494Semax * USB context.
598187494Semax */
599187494Semax
600184610Salfredstatic void
601184610Salfredubt_ctrl_write_callback(struct usb2_xfer *xfer)
602184610Salfred{
603187741Semax	struct ubt_softc		*sc = xfer->priv_sc;
604187494Semax	struct usb2_device_request	req;
605187494Semax	struct mbuf			*m;
606184610Salfred
607184610Salfred	switch (USB_GET_STATE(xfer)) {
608184610Salfred	case USB_ST_TRANSFERRED:
609187741Semax		UBT_INFO(sc, "sent %d bytes to control pipe\n", xfer->actlen);
610187741Semax		UBT_STAT_BYTES_SENT(sc, xfer->actlen);
611187741Semax		UBT_STAT_PCKTS_SENT(sc);
612187494Semax		/* FALLTHROUGH */
613184610Salfred
614184610Salfred	case USB_ST_SETUP:
615187494Semaxsend_next:
616187494Semax		/* Get next command mbuf, if any */
617187741Semax		UBT_NG_LOCK(sc);
618184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
619187741Semax		UBT_NG_UNLOCK(sc);
620184610Salfred
621184610Salfred		if (m == NULL) {
622187494Semax			UBT_INFO(sc, "HCI command queue is empty\n");
623187741Semax			break;	/* transfer complete */
624184610Salfred		}
625184610Salfred
626187494Semax		/* Initialize a USB control request and then schedule it */
627184610Salfred		bzero(&req, sizeof(req));
628184610Salfred		req.bmRequestType = UBT_HCI_REQUEST;
629184610Salfred		USETW(req.wLength, m->m_pkthdr.len);
630184610Salfred
631187494Semax		UBT_INFO(sc, "Sending control request, " \
632187494Semax			"bmRequestType=0x%02x, wLength=%d\n",
633187494Semax			req.bmRequestType, UGETW(req.wLength));
634184610Salfred
635184610Salfred		usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
636184610Salfred		usb2_m_copy_in(xfer->frbuffers + 1, 0, m, 0, m->m_pkthdr.len);
637184610Salfred
638184610Salfred		xfer->frlengths[0] = sizeof(req);
639184610Salfred		xfer->frlengths[1] = m->m_pkthdr.len;
640187494Semax		xfer->nframes = 2;
641184610Salfred
642184610Salfred		NG_FREE_M(m);
643184610Salfred
644184610Salfred		usb2_start_hardware(xfer);
645187494Semax		break;
646184610Salfred
647187494Semax	default: /* Error */
648187494Semax		if (xfer->error != USB_ERR_CANCELLED) {
649187494Semax			UBT_WARN(sc, "control transfer failed: %s\n",
650187494Semax				usb2_errstr(xfer->error));
651187494Semax
652187494Semax			UBT_STAT_OERROR(sc);
653187494Semax			goto send_next;
654184610Salfred		}
655187494Semax
656187741Semax		/* transfer cancelled */
657187494Semax		break;
658184610Salfred	}
659187494Semax} /* ubt_ctrl_write_callback */
660184610Salfred
661187494Semax/*
662187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e.
663187494Semax * HCI event was received from the device.
664187494Semax * USB context.
665187494Semax */
666187494Semax
667184610Salfredstatic void
668184610Salfredubt_intr_read_callback(struct usb2_xfer *xfer)
669184610Salfred{
670187741Semax	struct ubt_softc	*sc = xfer->priv_sc;
671187494Semax	struct mbuf		*m;
672187494Semax	ng_hci_event_pkt_t	*hdr;
673184610Salfred
674187494Semax	m = NULL;
675187494Semax
676184610Salfred	switch (USB_GET_STATE(xfer)) {
677184610Salfred	case USB_ST_TRANSFERRED:
678187494Semax		/* Allocate a new mbuf */
679184610Salfred		MGETHDR(m, M_DONTWAIT, MT_DATA);
680184610Salfred		if (m == NULL) {
681187494Semax			UBT_STAT_IERROR(sc);
682187494Semax			goto submit_next;
683184610Salfred		}
684187494Semax
685184610Salfred		MCLGET(m, M_DONTWAIT);
686184610Salfred		if (!(m->m_flags & M_EXT)) {
687187494Semax			UBT_STAT_IERROR(sc);
688187494Semax			goto submit_next;
689184610Salfred		}
690187494Semax
691187494Semax		/* Add HCI packet type */
692187494Semax		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
693187494Semax		m->m_pkthdr.len = m->m_len = 1;
694187494Semax
695187494Semax		if (xfer->actlen > MCLBYTES - 1)
696187494Semax			xfer->actlen = MCLBYTES - 1;
697187494Semax
698187494Semax		usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
699187494Semax			xfer->actlen);
700187494Semax		m->m_pkthdr.len += xfer->actlen;
701187494Semax		m->m_len += xfer->actlen;
702187494Semax
703187494Semax		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
704187494Semax			xfer->actlen);
705187494Semax
706187494Semax		/* Validate packet and send it up the stack */
707187494Semax		if (m->m_pkthdr.len < sizeof(*hdr)) {
708187494Semax			UBT_INFO(sc, "HCI event packet is too short\n");
709187494Semax
710187494Semax			UBT_STAT_IERROR(sc);
711187494Semax			goto submit_next;
712184610Salfred		}
713184610Salfred
714187494Semax		hdr = mtod(m, ng_hci_event_pkt_t *);
715187494Semax		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
716187494Semax			UBT_ERR(sc, "Invalid HCI event packet size, " \
717187494Semax				"length=%d, pktlen=%d\n",
718187494Semax				hdr->length, m->m_pkthdr.len);
719184610Salfred
720187494Semax			UBT_STAT_IERROR(sc);
721187494Semax			goto submit_next;
722184610Salfred		}
723184610Salfred
724187494Semax		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
725187494Semax			"length=%d\n", m->m_pkthdr.len, hdr->length);
726184610Salfred
727187494Semax		UBT_STAT_PCKTS_RECV(sc);
728187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
729184610Salfred
730187741Semax		ubt_fwd_mbuf_up(sc, &m);
731187494Semax		/* m == NULL at this point */
732187494Semax		/* FALLTHROUGH */
733184610Salfred
734184610Salfred	case USB_ST_SETUP:
735187494Semaxsubmit_next:
736187494Semax		NG_FREE_M(m); /* checks for m != NULL */
737184610Salfred
738187741Semax		xfer->frlengths[0] = xfer->max_data_length;
739187741Semax		usb2_start_hardware(xfer);
740187494Semax		break;
741184610Salfred
742187494Semax	default: /* Error */
743187494Semax		if (xfer->error != USB_ERR_CANCELLED) {
744187494Semax			UBT_WARN(sc, "interrupt transfer failed: %s\n",
745187494Semax				usb2_errstr(xfer->error));
746184610Salfred
747187494Semax			/* Try to clear stall first */
748187741Semax			xfer->flags.stall_pipe = 1;
749187741Semax			goto submit_next;
750187741Semax		}
751187741Semax			/* transfer cancelled */
752187494Semax		break;
753184610Salfred	}
754187494Semax} /* ubt_intr_read_callback */
755184610Salfred
756187494Semax/*
757187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e.
758187494Semax * ACL packet was received from the device.
759187494Semax * USB context.
760187494Semax */
761187494Semax
762184610Salfredstatic void
763187494Semaxubt_bulk_read_callback(struct usb2_xfer *xfer)
764184610Salfred{
765187741Semax	struct ubt_softc	*sc = xfer->priv_sc;
766187494Semax	struct mbuf		*m;
767187494Semax	ng_hci_acldata_pkt_t	*hdr;
768187494Semax	uint16_t		len;
769184610Salfred
770187494Semax	m = NULL;
771184610Salfred
772184610Salfred	switch (USB_GET_STATE(xfer)) {
773184610Salfred	case USB_ST_TRANSFERRED:
774187494Semax		/* Allocate new mbuf */
775184610Salfred		MGETHDR(m, M_DONTWAIT, MT_DATA);
776184610Salfred		if (m == NULL) {
777187494Semax			UBT_STAT_IERROR(sc);
778187494Semax			goto submit_next;
779184610Salfred		}
780187494Semax
781184610Salfred		MCLGET(m, M_DONTWAIT);
782184610Salfred		if (!(m->m_flags & M_EXT)) {
783187494Semax			UBT_STAT_IERROR(sc);
784187494Semax			goto submit_next;
785184610Salfred		}
786184610Salfred
787187494Semax		/* Add HCI packet type */
788187494Semax		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
789187494Semax		m->m_pkthdr.len = m->m_len = 1;
790184610Salfred
791187494Semax		if (xfer->actlen > MCLBYTES - 1)
792187494Semax			xfer->actlen = MCLBYTES - 1;
793184610Salfred
794187494Semax		usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
795187494Semax			xfer->actlen);
796184610Salfred		m->m_pkthdr.len += xfer->actlen;
797184610Salfred		m->m_len += xfer->actlen;
798184610Salfred
799187494Semax		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
800187494Semax			xfer->actlen);
801184610Salfred
802187494Semax		/* Validate packet and send it up the stack */
803187494Semax		if (m->m_pkthdr.len < sizeof(*hdr)) {
804187494Semax			UBT_INFO(sc, "HCI ACL packet is too short\n");
805184610Salfred
806187494Semax			UBT_STAT_IERROR(sc);
807187494Semax			goto submit_next;
808184610Salfred		}
809184610Salfred
810187494Semax		hdr = mtod(m, ng_hci_acldata_pkt_t *);
811187494Semax		len = le16toh(hdr->length);
812187494Semax		if (len != (m->m_pkthdr.len - sizeof(*hdr))) {
813187494Semax			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
814187494Semax				"pktlen=%d\n", len, m->m_pkthdr.len);
815184610Salfred
816187494Semax			UBT_STAT_IERROR(sc);
817187494Semax			goto submit_next;
818184610Salfred		}
819184610Salfred
820187494Semax		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
821187494Semax			"length=%d\n", m->m_pkthdr.len, len);
822184610Salfred
823187494Semax		UBT_STAT_PCKTS_RECV(sc);
824187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
825184610Salfred
826187741Semax		ubt_fwd_mbuf_up(sc, &m);
827187494Semax		/* m == NULL at this point */
828187494Semax		/* FALLTHOUGH */
829184610Salfred
830187494Semax	case USB_ST_SETUP:
831187494Semaxsubmit_next:
832187494Semax		NG_FREE_M(m); /* checks for m != NULL */
833184610Salfred
834187741Semax		xfer->frlengths[0] = xfer->max_data_length;
835187741Semax		usb2_start_hardware(xfer);
836187494Semax		break;
837184610Salfred
838187494Semax	default: /* Error */
839187494Semax		if (xfer->error != USB_ERR_CANCELLED) {
840187494Semax			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
841187494Semax				usb2_errstr(xfer->error));
842184610Salfred
843187494Semax			/* Try to clear stall first */
844187741Semax			xfer->flags.stall_pipe = 1;
845187741Semax			goto submit_next;
846187741Semax		}
847187741Semax			/* transfer cancelled */
848187494Semax		break;
849187494Semax	}
850187494Semax} /* ubt_bulk_read_callback */
851184610Salfred
852187494Semax/*
853187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
854187494Semax * ACL packet was sent to the device.
855187494Semax * USB context.
856187494Semax */
857184610Salfred
858184610Salfredstatic void
859184610Salfredubt_bulk_write_callback(struct usb2_xfer *xfer)
860184610Salfred{
861187741Semax	struct ubt_softc	*sc = xfer->priv_sc;
862187494Semax	struct mbuf		*m;
863184610Salfred
864184610Salfred	switch (USB_GET_STATE(xfer)) {
865184610Salfred	case USB_ST_TRANSFERRED:
866187741Semax		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", xfer->actlen);
867187741Semax		UBT_STAT_BYTES_SENT(sc, xfer->actlen);
868187741Semax		UBT_STAT_PCKTS_SENT(sc);
869187494Semax		/* FALLTHROUGH */
870184610Salfred
871187494Semax	case USB_ST_SETUP:
872187741Semaxsend_next:
873187494Semax		/* Get next mbuf, if any */
874187741Semax		UBT_NG_LOCK(sc);
875184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
876187741Semax		UBT_NG_UNLOCK(sc);
877184610Salfred
878184610Salfred		if (m == NULL) {
879187494Semax			UBT_INFO(sc, "ACL data queue is empty\n");
880187741Semax			break; /* transfer completed */
881184610Salfred		}
882187494Semax
883184610Salfred		/*
884187494Semax		 * Copy ACL data frame back to a linear USB transfer buffer
885187494Semax		 * and schedule transfer
886184610Salfred		 */
887184610Salfred
888184610Salfred		usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len);
889187494Semax		xfer->frlengths[0] = m->m_pkthdr.len;
890184610Salfred
891187494Semax		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
892187494Semax			m->m_pkthdr.len);
893184610Salfred
894184610Salfred		NG_FREE_M(m);
895184610Salfred
896184610Salfred		usb2_start_hardware(xfer);
897187494Semax		break;
898184610Salfred
899187494Semax	default: /* Error */
900184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
901187494Semax			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
902187494Semax				usb2_errstr(xfer->error));
903184610Salfred
904187494Semax			UBT_STAT_OERROR(sc);
905184610Salfred
906184610Salfred			/* try to clear stall first */
907187741Semax			xfer->flags.stall_pipe = 1;
908187741Semax			goto send_next;
909187741Semax		}
910187741Semax			/* transfer cancelled */
911187494Semax		break;
912184610Salfred	}
913187494Semax} /* ubt_bulk_write_callback */
914184610Salfred
915187494Semax/*
916187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e.
917187494Semax * SCO packet was received from the device.
918187494Semax * USB context.
919187494Semax */
920187494Semax
921184610Salfredstatic void
922184610Salfredubt_isoc_read_callback(struct usb2_xfer *xfer)
923184610Salfred{
924187741Semax	struct ubt_softc	*sc = xfer->priv_sc;
925187494Semax	int			n;
926184610Salfred
927187494Semax	switch (USB_GET_STATE(xfer)) {
928187494Semax	case USB_ST_TRANSFERRED:
929187494Semax		for (n = 0; n < xfer->nframes; n ++)
930187494Semax			if (ubt_isoc_read_one_frame(xfer, n) < 0)
931187494Semax				break;
932187494Semax		/* FALLTHROUGH */
933184610Salfred
934187494Semax	case USB_ST_SETUP:
935187494Semaxread_next:
936187494Semax		for (n = 0; n < xfer->nframes; n ++)
937187494Semax			xfer->frlengths[n] = xfer->max_frame_size;
938184610Salfred
939187494Semax		usb2_start_hardware(xfer);
940187494Semax		break;
941184610Salfred
942187494Semax	default: /* Error */
943187494Semax                if (xfer->error != USB_ERR_CANCELLED) {
944187494Semax                        UBT_STAT_IERROR(sc);
945187494Semax                        goto read_next;
946187494Semax                }
947184610Salfred
948187741Semax		/* transfer cancelled */
949187494Semax		break;
950187494Semax	}
951187494Semax} /* ubt_isoc_read_callback */
952184610Salfred
953187494Semax/*
954187494Semax * Helper function. Called from ubt_isoc_read_callback() to read
955187494Semax * SCO data from one frame.
956187494Semax * USB context.
957187494Semax */
958184610Salfred
959187494Semaxstatic int
960187494Semaxubt_isoc_read_one_frame(struct usb2_xfer *xfer, int frame_no)
961187494Semax{
962187494Semax	struct ubt_softc	*sc = xfer->priv_sc;
963187494Semax	struct mbuf		*m;
964187741Semax	int			len, want, got;
965184610Salfred
966187494Semax	/* Get existing SCO reassembly buffer */
967187494Semax	m = sc->sc_isoc_in_buffer;
968187494Semax	sc->sc_isoc_in_buffer = NULL;
969184610Salfred
970187494Semax	/* While we have data in the frame */
971187494Semax	while ((len = xfer->frlengths[frame_no]) > 0) {
972187494Semax		if (m == NULL) {
973187494Semax			/* Start new reassembly buffer */
974187494Semax			MGETHDR(m, M_DONTWAIT, MT_DATA);
975187494Semax			if (m == NULL) {
976187494Semax				UBT_STAT_IERROR(sc);
977187494Semax				return (-1);	/* XXX out of sync! */
978187494Semax			}
979184610Salfred
980187494Semax			MCLGET(m, M_DONTWAIT);
981187494Semax			if (!(m->m_flags & M_EXT)) {
982187494Semax				UBT_STAT_IERROR(sc);
983187494Semax				NG_FREE_M(m);
984187494Semax				return (-1);	/* XXX out of sync! */
985184610Salfred			}
986184610Salfred
987187494Semax			/* Expect SCO header */
988187494Semax			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
989187494Semax			m->m_pkthdr.len = m->m_len = got = 1;
990187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
991187494Semax		} else {
992187494Semax			/*
993187494Semax			 * Check if we have SCO header and if so
994187494Semax			 * adjust amount of data we want
995187494Semax			 */
996187494Semax			got = m->m_pkthdr.len;
997187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
998184610Salfred
999187494Semax			if (got >= want)
1000187494Semax				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
1001184610Salfred		}
1002184610Salfred
1003187494Semax		/* Append frame data to the SCO reassembly buffer */
1004187494Semax		if (got + len > want)
1005187494Semax			len = want - got;
1006184610Salfred
1007187494Semax		usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size,
1008187494Semax			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
1009184610Salfred
1010187494Semax		m->m_pkthdr.len += len;
1011187494Semax		m->m_len += len;
1012187494Semax		xfer->frlengths[frame_no] -= len;
1013184610Salfred
1014187494Semax		/* Check if we got everything we wanted, if not - continue */
1015187494Semax		if (got != want)
1016187494Semax			continue;
1017184610Salfred
1018187494Semax		/* If we got here then we got complete SCO frame */
1019187494Semax		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
1020187494Semax			"length=%d\n", m->m_pkthdr.len,
1021187494Semax			mtod(m, ng_hci_scodata_pkt_t *)->length);
1022184610Salfred
1023187494Semax		UBT_STAT_PCKTS_RECV(sc);
1024187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
1025184610Salfred
1026187741Semax		ubt_fwd_mbuf_up(sc, &m);
1027187494Semax		/* m == NULL at this point */
1028187494Semax	}
1029184610Salfred
1030187494Semax	/* Put SCO reassembly buffer back */
1031187494Semax	sc->sc_isoc_in_buffer = m;
1032184610Salfred
1033187494Semax	return (0);
1034187494Semax} /* ubt_isoc_read_one_frame */
1035184610Salfred
1036187494Semax/*
1037187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
1038187494Semax * SCO packet was sent to the device.
1039187494Semax * USB context.
1040187494Semax */
1041184610Salfred
1042184610Salfredstatic void
1043184610Salfredubt_isoc_write_callback(struct usb2_xfer *xfer)
1044184610Salfred{
1045187741Semax	struct ubt_softc	*sc = xfer->priv_sc;
1046187494Semax	struct mbuf		*m;
1047187494Semax	int			n, space, offset;
1048184610Salfred
1049184610Salfred	switch (USB_GET_STATE(xfer)) {
1050184610Salfred	case USB_ST_TRANSFERRED:
1051187741Semax		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", xfer->actlen);
1052187741Semax		UBT_STAT_BYTES_SENT(sc, xfer->actlen);
1053187741Semax		UBT_STAT_PCKTS_SENT(sc);
1054187494Semax		/* FALLTHROUGH */
1055184610Salfred
1056184610Salfred	case USB_ST_SETUP:
1057187494Semaxsend_next:
1058184610Salfred		offset = 0;
1059187494Semax		space = xfer->max_frame_size * xfer->nframes;
1060187494Semax		m = NULL;
1061184610Salfred
1062187494Semax		while (space > 0) {
1063187494Semax			if (m == NULL) {
1064187741Semax				UBT_NG_LOCK(sc);
1065187494Semax				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
1066187741Semax				UBT_NG_UNLOCK(sc);
1067184610Salfred
1068187494Semax				if (m == NULL)
1069187494Semax					break;
1070187494Semax			}
1071184610Salfred
1072187494Semax			n = min(space, m->m_pkthdr.len);
1073187494Semax			if (n > 0) {
1074187494Semax				usb2_m_copy_in(xfer->frbuffers, offset, m,0, n);
1075187494Semax				m_adj(m, n);
1076184610Salfred
1077187494Semax				offset += n;
1078187494Semax				space -= n;
1079187494Semax			}
1080184610Salfred
1081187494Semax			if (m->m_pkthdr.len == 0)
1082187494Semax				NG_FREE_M(m); /* sets m = NULL */
1083187494Semax		}
1084184610Salfred
1085187494Semax		/* Put whatever is left from mbuf back on queue */
1086187494Semax		if (m != NULL) {
1087187741Semax			UBT_NG_LOCK(sc);
1088187494Semax			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
1089187741Semax			UBT_NG_UNLOCK(sc);
1090184610Salfred		}
1091184610Salfred
1092187494Semax		/*
1093187494Semax		 * Calculate sizes for isoc frames.
1094187494Semax		 * Note that offset could be 0 at this point (i.e. we have
1095187494Semax		 * nothing to send). That is fine, as we have isoc. transfers
1096187494Semax		 * going in both directions all the time. In this case it
1097187494Semax		 * would be just empty isoc. transfer.
1098187494Semax		 */
1099187494Semax
1100187494Semax		for (n = 0; n < xfer->nframes; n ++) {
1101187494Semax			xfer->frlengths[n] = min(offset, xfer->max_frame_size);
1102187494Semax			offset -= xfer->frlengths[n];
1103187494Semax		}
1104187494Semax
1105184610Salfred		usb2_start_hardware(xfer);
1106187494Semax		break;
1107184610Salfred
1108187494Semax	default: /* Error */
1109187494Semax		if (xfer->error != USB_ERR_CANCELLED) {
1110187494Semax			UBT_STAT_OERROR(sc);
1111187494Semax			goto send_next;
1112184610Salfred		}
1113187494Semax
1114187741Semax		/* transfer cancelled */
1115187494Semax		break;
1116184610Salfred	}
1117184610Salfred}
1118184610Salfred
1119187741Semax/*
1120187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack).
1121187741Semax * Modifies value of the mbuf pointer (sets it to NULL).
1122187741Semax * Save to call from any context.
1123187741Semax */
1124187741Semax
1125187741Semaxstatic int
1126187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
1127187741Semax{
1128187741Semax	hook_p	hook;
1129187741Semax	int	error;
1130187741Semax
1131187741Semax	/*
1132187741Semax	 * Close the race with Netgraph hook newhook/disconnect methods.
1133187741Semax	 * Save the hook pointer atomically. Two cases are possible:
1134187741Semax	 *
1135187741Semax	 * 1) The hook pointer is NULL. It means disconnect method got
1136187741Semax	 *    there first. In this case we are done.
1137187741Semax	 *
1138187741Semax	 * 2) The hook pointer is not NULL. It means that hook pointer
1139187741Semax	 *    could be either in valid or invalid (i.e. in the process
1140187741Semax	 *    of disconnect) state. In any case grab an extra reference
1141187741Semax	 *    to protect the hook pointer.
1142187741Semax	 *
1143187741Semax	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
1144187741Semax	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
1145187741Semax	 */
1146187741Semax
1147187741Semax	UBT_NG_LOCK(sc);
1148187741Semax	if ((hook = sc->sc_hook) != NULL)
1149187741Semax		NG_HOOK_REF(hook);
1150187741Semax	UBT_NG_UNLOCK(sc);
1151187741Semax
1152187741Semax	if (hook == NULL) {
1153187741Semax		NG_FREE_M(*m);
1154187741Semax		return (ENETDOWN);
1155187741Semax	}
1156187741Semax
1157187741Semax	NG_SEND_DATA_ONLY(error, hook, *m);
1158187741Semax	NG_HOOK_UNREF(hook);
1159187741Semax
1160187741Semax	if (error != 0)
1161187741Semax		UBT_STAT_IERROR(sc);
1162187741Semax
1163187741Semax	return (error);
1164187741Semax} /* ubt_fwd_mbuf_up */
1165187741Semax
1166184610Salfred/****************************************************************************
1167184610Salfred ****************************************************************************
1168187494Semax **                                 Glue
1169184610Salfred ****************************************************************************
1170184610Salfred ****************************************************************************/
1171184610Salfred
1172184610Salfred/*
1173187741Semax * Schedule glue task. Should be called with sc_ng_mtx held.
1174187494Semax * Netgraph context.
1175184610Salfred */
1176184610Salfred
1177187741Semaxstatic void
1178187494Semaxubt_task_schedule(ubt_softc_p sc, int action)
1179184610Salfred{
1180187741Semax	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
1181184610Salfred
1182187741Semax	/*
1183187741Semax	 * Try to handle corner case when "start all" and "stop all"
1184187741Semax	 * actions can both be set before task is executed.
1185187741Semax	 *
1186187741Semax	 * The rules are
1187187741Semax	 *
1188187741Semax	 * sc_task_flags	action		new sc_task_flags
1189187741Semax	 * ------------------------------------------------------
1190187741Semax	 * 0			start		start
1191187741Semax	 * 0			stop		stop
1192187741Semax	 * start		start		start
1193187741Semax	 * start		stop		stop
1194187741Semax	 * stop			start		stop|start
1195187741Semax	 * stop			stop		stop
1196187741Semax	 * stop|start		start		stop|start
1197187741Semax	 * stop|start		stop		stop
1198187741Semax	 */
1199187494Semax
1200187741Semax	if (action != 0) {
1201187741Semax		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
1202187494Semax			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
1203187494Semax
1204187494Semax		sc->sc_task_flags |= action;
1205187494Semax	}
1206187494Semax
1207187494Semax	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
1208187741Semax		return;
1209187494Semax
1210187494Semax	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
1211187494Semax		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
1212187741Semax		return;
1213187494Semax	}
1214187494Semax
1215187494Semax	/* XXX: i think this should never happen */
1216187494Semax} /* ubt_task_schedule */
1217187494Semax
1218184610Salfred/*
1219187494Semax * Glue task. Examines sc_task_flags and does things depending on it.
1220187494Semax * Taskqueue context.
1221184610Salfred */
1222184610Salfred
1223187494Semaxstatic void
1224187494Semaxubt_task(void *context, int pending)
1225184610Salfred{
1226187741Semax	ubt_softc_p	sc = context;
1227187741Semax	int		task_flags, i;
1228184610Salfred
1229187741Semax	UBT_NG_LOCK(sc);
1230187494Semax	task_flags = sc->sc_task_flags;
1231187494Semax	sc->sc_task_flags = 0;
1232187741Semax	UBT_NG_UNLOCK(sc);
1233187494Semax
1234187741Semax	/*
1235187741Semax	 * Stop all USB transfers synchronously.
1236187741Semax	 * Stop interface #0 and #1 transfers at the same time and in the
1237187741Semax	 * same loop. usb2_transfer_drain() will do appropriate locking.
1238187741Semax	 */
1239187494Semax
1240187741Semax	if (task_flags & UBT_FLAG_T_STOP_ALL)
1241187741Semax		for (i = 0; i < UBT_N_TRANSFER; i ++)
1242187741Semax			usb2_transfer_drain(sc->sc_xfer[i]);
1243187494Semax
1244187741Semax	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
1245187494Semax	if (task_flags & UBT_FLAG_T_START_ALL) {
1246187494Semax		/*
1247187494Semax		 * Interface #0
1248187494Semax		 */
1249187494Semax
1250187741Semax		mtx_lock(&sc->sc_if_mtx);
1251187741Semax
1252187494Semax		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
1253187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
1254187494Semax
1255187494Semax		/*
1256187494Semax		 * Interface #1
1257187494Semax		 * Start both read and write isoc. transfers by default.
1258187494Semax		 * Get them going all the time even if we have nothing
1259187494Semax		 * to send to avoid any delays.
1260187494Semax		 */
1261187494Semax
1262187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
1263187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
1264187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
1265187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
1266187741Semax
1267187741Semax		mtx_unlock(&sc->sc_if_mtx);
1268184610Salfred	}
1269187494Semax
1270187494Semax 	/* Start outgoing control transfer */
1271187494Semax	if (task_flags & UBT_FLAG_T_START_CTRL) {
1272187741Semax		mtx_lock(&sc->sc_if_mtx);
1273187494Semax		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
1274187741Semax		mtx_unlock(&sc->sc_if_mtx);
1275184610Salfred	}
1276184610Salfred
1277187494Semax	/* Start outgoing bulk transfer */
1278187494Semax	if (task_flags & UBT_FLAG_T_START_BULK) {
1279187741Semax		mtx_lock(&sc->sc_if_mtx);
1280187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
1281187741Semax		mtx_unlock(&sc->sc_if_mtx);
1282184610Salfred	}
1283187494Semax} /* ubt_task */
1284187494Semax
1285187494Semax/****************************************************************************
1286187494Semax ****************************************************************************
1287187494Semax **                        Netgraph specific
1288187494Semax ****************************************************************************
1289187494Semax ****************************************************************************/
1290184610Salfred
1291187494Semax/*
1292187494Semax * Netgraph node constructor. Do not allow to create node of this type.
1293187494Semax * Netgraph context.
1294187494Semax */
1295184610Salfred
1296187494Semaxstatic int
1297187494Semaxng_ubt_constructor(node_p node)
1298187494Semax{
1299187494Semax	return (EINVAL);
1300187494Semax} /* ng_ubt_constructor */
1301184610Salfred
1302184610Salfred/*
1303187494Semax * Netgraph node destructor. Destroy node only when device has been detached.
1304187494Semax * Netgraph context.
1305184610Salfred */
1306184610Salfred
1307184610Salfredstatic int
1308187494Semaxng_ubt_shutdown(node_p node)
1309184610Salfred{
1310187494Semax	if (node->nd_flags & NGF_REALLY_DIE) {
1311187494Semax		/*
1312187494Semax                 * We came here because the USB device is being
1313187494Semax		 * detached, so stop being persistant.
1314187494Semax                 */
1315187494Semax		NG_NODE_SET_PRIVATE(node, NULL);
1316187494Semax		NG_NODE_UNREF(node);
1317187494Semax	} else
1318187494Semax		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
1319184610Salfred
1320187494Semax	return (0);
1321187494Semax} /* ng_ubt_shutdown */
1322184610Salfred
1323187494Semax/*
1324187494Semax * Create new hook. There can only be one.
1325187494Semax * Netgraph context.
1326187494Semax */
1327184610Salfred
1328187494Semaxstatic int
1329187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name)
1330187494Semax{
1331187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1332184610Salfred
1333187494Semax	if (strcmp(name, NG_UBT_HOOK) != 0)
1334187494Semax		return (EINVAL);
1335184610Salfred
1336187741Semax	UBT_NG_LOCK(sc);
1337187741Semax	if (sc->sc_hook != NULL) {
1338187741Semax		UBT_NG_UNLOCK(sc);
1339187741Semax
1340187494Semax		return (EISCONN);
1341187741Semax	}
1342184610Salfred
1343187494Semax	sc->sc_hook = hook;
1344187741Semax	UBT_NG_UNLOCK(sc);
1345184610Salfred
1346187494Semax	return (0);
1347187494Semax} /* ng_ubt_newhook */
1348184610Salfred
1349187494Semax/*
1350187494Semax * Connect hook. Start incoming USB transfers.
1351187494Semax * Netgraph context.
1352187494Semax */
1353184610Salfred
1354187494Semaxstatic int
1355187494Semaxng_ubt_connect(hook_p hook)
1356187494Semax{
1357187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1358184610Salfred
1359187494Semax	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
1360184610Salfred
1361187741Semax	UBT_NG_LOCK(sc);
1362187494Semax	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
1363187741Semax	UBT_NG_UNLOCK(sc);
1364184610Salfred
1365184610Salfred	return (0);
1366187494Semax} /* ng_ubt_connect */
1367184610Salfred
1368184610Salfred/*
1369187494Semax * Disconnect hook.
1370187494Semax * Netgraph context.
1371184610Salfred */
1372184610Salfred
1373184610Salfredstatic int
1374184610Salfredng_ubt_disconnect(hook_p hook)
1375184610Salfred{
1376187741Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1377184610Salfred
1378187741Semax	UBT_NG_LOCK(sc);
1379184610Salfred
1380187741Semax	if (hook != sc->sc_hook) {
1381187741Semax		UBT_NG_UNLOCK(sc);
1382184610Salfred
1383187494Semax		return (EINVAL);
1384187741Semax	}
1385184610Salfred
1386187494Semax	sc->sc_hook = NULL;
1387184610Salfred
1388187741Semax	/* Kick off task to stop all USB xfers */
1389187741Semax	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
1390184610Salfred
1391187494Semax	/* Drain queues */
1392187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
1393187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
1394187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
1395184610Salfred
1396187741Semax	UBT_NG_UNLOCK(sc);
1397184610Salfred
1398187494Semax	return (0);
1399187494Semax} /* ng_ubt_disconnect */
1400187494Semax
1401184610Salfred/*
1402187494Semax * Process control message.
1403187494Semax * Netgraph context.
1404184610Salfred */
1405184610Salfred
1406184610Salfredstatic int
1407184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
1408184610Salfred{
1409187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1410187494Semax	struct ng_mesg		*msg, *rsp = NULL;
1411187494Semax	struct ng_bt_mbufq	*q;
1412187494Semax	int			error = 0, queue, qlen;
1413184610Salfred
1414184610Salfred	NGI_GET_MSG(item, msg);
1415184610Salfred
1416184610Salfred	switch (msg->header.typecookie) {
1417184610Salfred	case NGM_GENERIC_COOKIE:
1418184610Salfred		switch (msg->header.cmd) {
1419184610Salfred		case NGM_TEXT_STATUS:
1420184610Salfred			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
1421187494Semax			if (rsp == NULL) {
1422184610Salfred				error = ENOMEM;
1423187494Semax				break;
1424187494Semax			}
1425187494Semax
1426187494Semax			snprintf(rsp->data, NG_TEXTRESPONSE,
1427187494Semax				"Hook: %s\n" \
1428187494Semax				"Task flags: %#x\n" \
1429187494Semax				"Debug: %d\n" \
1430187494Semax				"CMD queue: [have:%d,max:%d]\n" \
1431187494Semax				"ACL queue: [have:%d,max:%d]\n" \
1432187494Semax				"SCO queue: [have:%d,max:%d]",
1433187741Semax				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
1434187494Semax				sc->sc_task_flags,
1435187494Semax				sc->sc_debug,
1436187494Semax				sc->sc_cmdq.len,
1437187494Semax				sc->sc_cmdq.maxlen,
1438187494Semax				sc->sc_aclq.len,
1439187494Semax				sc->sc_aclq.maxlen,
1440187494Semax				sc->sc_scoq.len,
1441187494Semax				sc->sc_scoq.maxlen);
1442184610Salfred			break;
1443184610Salfred
1444184610Salfred		default:
1445184610Salfred			error = EINVAL;
1446184610Salfred			break;
1447184610Salfred		}
1448184610Salfred		break;
1449184610Salfred
1450184610Salfred	case NGM_UBT_COOKIE:
1451184610Salfred		switch (msg->header.cmd) {
1452184610Salfred		case NGM_UBT_NODE_SET_DEBUG:
1453187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
1454184610Salfred				error = EMSGSIZE;
1455187494Semax				break;
1456187494Semax			}
1457187494Semax
1458187494Semax			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
1459184610Salfred			break;
1460184610Salfred
1461184610Salfred		case NGM_UBT_NODE_GET_DEBUG:
1462184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
1463184610Salfred			    M_NOWAIT);
1464187494Semax			if (rsp == NULL) {
1465184610Salfred				error = ENOMEM;
1466187494Semax				break;
1467187494Semax			}
1468187494Semax
1469187494Semax			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
1470184610Salfred			break;
1471184610Salfred
1472184610Salfred		case NGM_UBT_NODE_SET_QLEN:
1473187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1474184610Salfred				error = EMSGSIZE;
1475187494Semax				break;
1476187494Semax			}
1477184610Salfred
1478187494Semax			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1479187494Semax			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
1480184610Salfred
1481187494Semax			switch (queue) {
1482187494Semax			case NGM_UBT_NODE_QUEUE_CMD:
1483187494Semax				q = &sc->sc_cmdq;
1484187494Semax				break;
1485184610Salfred
1486187494Semax			case NGM_UBT_NODE_QUEUE_ACL:
1487187494Semax				q = &sc->sc_aclq;
1488187494Semax				break;
1489184610Salfred
1490187494Semax			case NGM_UBT_NODE_QUEUE_SCO:
1491187494Semax				q = &sc->sc_scoq;
1492187494Semax				break;
1493184610Salfred
1494187494Semax			default:
1495187494Semax				error = EINVAL;
1496187494Semax				goto done;
1497187494Semax				/* NOT REACHED */
1498184610Salfred			}
1499187494Semax
1500187494Semax			q->maxlen = qlen;
1501184610Salfred			break;
1502184610Salfred
1503184610Salfred		case NGM_UBT_NODE_GET_QLEN:
1504184610Salfred			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1505184610Salfred				error = EMSGSIZE;
1506184610Salfred				break;
1507184610Salfred			}
1508187494Semax
1509184610Salfred			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1510187494Semax
1511184610Salfred			switch (queue) {
1512184610Salfred			case NGM_UBT_NODE_QUEUE_CMD:
1513184610Salfred				q = &sc->sc_cmdq;
1514184610Salfred				break;
1515184610Salfred
1516184610Salfred			case NGM_UBT_NODE_QUEUE_ACL:
1517184610Salfred				q = &sc->sc_aclq;
1518184610Salfred				break;
1519184610Salfred
1520184610Salfred			case NGM_UBT_NODE_QUEUE_SCO:
1521184610Salfred				q = &sc->sc_scoq;
1522184610Salfred				break;
1523184610Salfred
1524184610Salfred			default:
1525184610Salfred				error = EINVAL;
1526187494Semax				goto done;
1527187494Semax				/* NOT REACHED */
1528187494Semax			}
1529187494Semax
1530187494Semax			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
1531187494Semax				M_NOWAIT);
1532187494Semax			if (rsp == NULL) {
1533187494Semax				error = ENOMEM;
1534184610Salfred				break;
1535184610Salfred			}
1536184610Salfred
1537187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
1538187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
1539184610Salfred			break;
1540184610Salfred
1541184610Salfred		case NGM_UBT_NODE_GET_STAT:
1542184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
1543184610Salfred			    M_NOWAIT);
1544187494Semax			if (rsp == NULL) {
1545184610Salfred				error = ENOMEM;
1546187494Semax				break;
1547184610Salfred			}
1548187494Semax
1549187494Semax			bcopy(&sc->sc_stat, rsp->data,
1550187494Semax				sizeof(ng_ubt_node_stat_ep));
1551184610Salfred			break;
1552184610Salfred
1553184610Salfred		case NGM_UBT_NODE_RESET_STAT:
1554187494Semax			UBT_STAT_RESET(sc);
1555184610Salfred			break;
1556184610Salfred
1557184610Salfred		default:
1558184610Salfred			error = EINVAL;
1559184610Salfred			break;
1560184610Salfred		}
1561184610Salfred		break;
1562184610Salfred
1563184610Salfred	default:
1564184610Salfred		error = EINVAL;
1565184610Salfred		break;
1566184610Salfred	}
1567187494Semaxdone:
1568184610Salfred	NG_RESPOND_MSG(error, node, item, rsp);
1569184610Salfred	NG_FREE_MSG(msg);
1570184610Salfred
1571184610Salfred	return (error);
1572187494Semax} /* ng_ubt_rcvmsg */
1573184610Salfred
1574184610Salfred/*
1575187494Semax * Process data.
1576187494Semax * Netgraph context.
1577184610Salfred */
1578184610Salfred
1579184610Salfredstatic int
1580184610Salfredng_ubt_rcvdata(hook_p hook, item_p item)
1581184610Salfred{
1582187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1583187494Semax	struct mbuf		*m;
1584187494Semax	struct ng_bt_mbufq	*q;
1585187494Semax	int			action, error = 0;
1586184610Salfred
1587184610Salfred	if (hook != sc->sc_hook) {
1588184610Salfred		error = EINVAL;
1589184610Salfred		goto done;
1590184610Salfred	}
1591187494Semax
1592187494Semax	/* Deatch mbuf and get HCI frame type */
1593184610Salfred	NGI_GET_M(item, m);
1594184610Salfred
1595187494Semax	/*
1596187494Semax	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
1597187494Semax	 * 2 bytes connection handle and at least 1 byte of length.
1598187494Semax	 * Panic on data frame that has size smaller than 4 bytes (it
1599187494Semax	 * should not happen)
1600187494Semax	 */
1601187494Semax
1602187494Semax	if (m->m_pkthdr.len < 4)
1603187494Semax		panic("HCI frame size is too small! pktlen=%d\n",
1604187494Semax			m->m_pkthdr.len);
1605187494Semax
1606187494Semax	/* Process HCI frame */
1607184610Salfred	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
1608184610Salfred	case NG_HCI_CMD_PKT:
1609187494Semax		if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE)
1610187494Semax			panic("HCI command frame size is too big! " \
1611187494Semax				"buffer size=%zd, packet len=%d\n",
1612187494Semax				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
1613187494Semax
1614184610Salfred		q = &sc->sc_cmdq;
1615187494Semax		action = UBT_FLAG_T_START_CTRL;
1616184610Salfred		break;
1617184610Salfred
1618184610Salfred	case NG_HCI_ACL_DATA_PKT:
1619187494Semax		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
1620187494Semax			panic("ACL data frame size is too big! " \
1621187494Semax				"buffer size=%d, packet len=%d\n",
1622187494Semax				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
1623187494Semax
1624184610Salfred		q = &sc->sc_aclq;
1625187494Semax		action = UBT_FLAG_T_START_BULK;
1626184610Salfred		break;
1627184610Salfred
1628184610Salfred	case NG_HCI_SCO_DATA_PKT:
1629184610Salfred		q = &sc->sc_scoq;
1630187494Semax		action = 0;
1631184610Salfred		break;
1632184610Salfred
1633184610Salfred	default:
1634187494Semax		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
1635187494Semax			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
1636184610Salfred
1637184610Salfred		NG_FREE_M(m);
1638184610Salfred		error = EINVAL;
1639184610Salfred		goto done;
1640187494Semax		/* NOT REACHED */
1641184610Salfred	}
1642184610Salfred
1643187741Semax	UBT_NG_LOCK(sc);
1644184610Salfred	if (NG_BT_MBUFQ_FULL(q)) {
1645187494Semax		NG_BT_MBUFQ_DROP(q);
1646187741Semax		UBT_NG_UNLOCK(sc);
1647187494Semax
1648187494Semax		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
1649187494Semax			*mtod(m, uint8_t *), m->m_pkthdr.len);
1650187494Semax
1651184610Salfred		NG_FREE_M(m);
1652184610Salfred	} else {
1653187494Semax		/* Loose HCI packet type, enqueue mbuf and kick off task */
1654187494Semax		m_adj(m, sizeof(uint8_t));
1655184610Salfred		NG_BT_MBUFQ_ENQUEUE(q, m);
1656187494Semax		ubt_task_schedule(sc, action);
1657187741Semax		UBT_NG_UNLOCK(sc);
1658184610Salfred	}
1659184610Salfreddone:
1660184610Salfred	NG_FREE_ITEM(item);
1661184610Salfred
1662187494Semax	return (error);
1663187494Semax} /* ng_ubt_rcvdata */
1664187494Semax
1665187494Semax/****************************************************************************
1666187494Semax ****************************************************************************
1667187494Semax **                              Module
1668187494Semax ****************************************************************************
1669187494Semax ****************************************************************************/
1670187494Semax
1671187494Semax/*
1672187494Semax * Load/Unload the driver module
1673187494Semax */
1674187494Semax
1675187494Semaxstatic int
1676187494Semaxubt_modevent(module_t mod, int event, void *data)
1677187494Semax{
1678187494Semax	int	error;
1679187494Semax
1680187494Semax	switch (event) {
1681187494Semax	case MOD_LOAD:
1682187494Semax		error = ng_newtype(&typestruct);
1683187494Semax		if (error != 0)
1684187494Semax			printf("%s: Could not register Netgraph node type, " \
1685187494Semax				"error=%d\n", NG_UBT_NODE_TYPE, error);
1686187494Semax		break;
1687187494Semax
1688187494Semax	case MOD_UNLOAD:
1689187494Semax		error = ng_rmtype(&typestruct);
1690187494Semax		break;
1691187494Semax
1692187494Semax	default:
1693187494Semax		error = EOPNOTSUPP;
1694187494Semax		break;
1695184610Salfred	}
1696187494Semax
1697184610Salfred	return (error);
1698187494Semax} /* ubt_modevent */
1699187494Semax
1700187494Semaxstatic devclass_t	ubt_devclass;
1701187494Semax
1702187494Semaxstatic device_method_t	ubt_methods[] =
1703187494Semax{
1704187494Semax	DEVMETHOD(device_probe,	ubt_probe),
1705187494Semax	DEVMETHOD(device_attach, ubt_attach),
1706187494Semax	DEVMETHOD(device_detach, ubt_detach),
1707187494Semax	{ 0, 0 }
1708187494Semax};
1709187494Semax
1710187494Semaxstatic driver_t		ubt_driver =
1711187494Semax{
1712187494Semax	.name =	   "ubt",
1713187494Semax	.methods = ubt_methods,
1714187494Semax	.size =	   sizeof(struct ubt_softc),
1715187494Semax};
1716187494Semax
1717189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
1718187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
1719187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
1720187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
1721188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
1722187494Semax
1723