ng_ubt.c revision 194682
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/netgraph/bluetooth/drivers/ubt/ng_ubt.c 194682 2009-06-23 06:00:31Z 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
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/linker_set.h>
104194677Sthompsa#include <sys/module.h>
105194677Sthompsa#include <sys/lock.h>
106194677Sthompsa#include <sys/mutex.h>
107194677Sthompsa#include <sys/condvar.h>
108194677Sthompsa#include <sys/sysctl.h>
109194677Sthompsa#include <sys/sx.h>
110194677Sthompsa#include <sys/unistd.h>
111194677Sthompsa#include <sys/callout.h>
112194677Sthompsa#include <sys/malloc.h>
113194677Sthompsa#include <sys/priv.h>
114194677Sthompsa
115188746Sthompsa#include "usbdevs.h"
116188942Sthompsa#include <dev/usb/usb.h>
117194677Sthompsa#include <dev/usb/usbdi.h>
118194677Sthompsa#include <dev/usb/usbdi_util.h>
119184610Salfred
120194228Sthompsa#define	USB_DEBUG_VAR usb_debug
121188942Sthompsa#include <dev/usb/usb_debug.h>
122188942Sthompsa#include <dev/usb/usb_busdma.h>
123184610Salfred
124184610Salfred#include <sys/mbuf.h>
125187494Semax#include <sys/taskqueue.h>
126184610Salfred
127184610Salfred#include <netgraph/ng_message.h>
128184610Salfred#include <netgraph/netgraph.h>
129184610Salfred#include <netgraph/ng_parse.h>
130184610Salfred#include <netgraph/bluetooth/include/ng_bluetooth.h>
131184610Salfred#include <netgraph/bluetooth/include/ng_hci.h>
132184610Salfred#include <netgraph/bluetooth/include/ng_ubt.h>
133192909Sthompsa#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>
134184610Salfred
135187494Semaxstatic int		ubt_modevent(module_t, int, void *);
136187494Semaxstatic device_probe_t	ubt_probe;
137187494Semaxstatic device_attach_t	ubt_attach;
138187494Semaxstatic device_detach_t	ubt_detach;
139184610Salfred
140187741Semaxstatic void		ubt_task_schedule(ubt_softc_p, int);
141187494Semaxstatic task_fn_t	ubt_task;
142184610Salfred
143194228Sthompsa#define	ubt_xfer_start(sc, i)	usbd_transfer_start((sc)->sc_xfer[(i)])
144187741Semax
145187494Semax/* Netgraph methods */
146187494Semaxstatic ng_constructor_t	ng_ubt_constructor;
147187494Semaxstatic ng_shutdown_t	ng_ubt_shutdown;
148187494Semaxstatic ng_newhook_t	ng_ubt_newhook;
149187494Semaxstatic ng_connect_t	ng_ubt_connect;
150187494Semaxstatic ng_disconnect_t	ng_ubt_disconnect;
151187494Semaxstatic ng_rcvmsg_t	ng_ubt_rcvmsg;
152187494Semaxstatic ng_rcvdata_t	ng_ubt_rcvdata;
153184610Salfred
154184610Salfred/* Queue length */
155187494Semaxstatic const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
156184610Salfred{
157187494Semax	{ "queue", &ng_parse_int32_type, },
158187494Semax	{ "qlen",  &ng_parse_int32_type, },
159187494Semax	{ NULL, }
160184610Salfred};
161187494Semaxstatic const struct ng_parse_type		ng_ubt_node_qlen_type =
162187494Semax{
163184610Salfred	&ng_parse_struct_type,
164184610Salfred	&ng_ubt_node_qlen_type_fields
165184610Salfred};
166184610Salfred
167184610Salfred/* Stat info */
168187494Semaxstatic const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
169184610Salfred{
170187494Semax	{ "pckts_recv", &ng_parse_uint32_type, },
171187494Semax	{ "bytes_recv", &ng_parse_uint32_type, },
172187494Semax	{ "pckts_sent", &ng_parse_uint32_type, },
173187494Semax	{ "bytes_sent", &ng_parse_uint32_type, },
174187494Semax	{ "oerrors",    &ng_parse_uint32_type, },
175187494Semax	{ "ierrors",    &ng_parse_uint32_type, },
176187494Semax	{ NULL, }
177184610Salfred};
178187494Semaxstatic const struct ng_parse_type		ng_ubt_node_stat_type =
179187494Semax{
180184610Salfred	&ng_parse_struct_type,
181184610Salfred	&ng_ubt_node_stat_type_fields
182184610Salfred};
183184610Salfred
184184610Salfred/* Netgraph node command list */
185187494Semaxstatic const struct ng_cmdlist			ng_ubt_cmdlist[] =
186187494Semax{
187184610Salfred	{
188184610Salfred		NGM_UBT_COOKIE,
189184610Salfred		NGM_UBT_NODE_SET_DEBUG,
190184610Salfred		"set_debug",
191184610Salfred		&ng_parse_uint16_type,
192184610Salfred		NULL
193184610Salfred	},
194184610Salfred	{
195184610Salfred		NGM_UBT_COOKIE,
196184610Salfred		NGM_UBT_NODE_GET_DEBUG,
197184610Salfred		"get_debug",
198184610Salfred		NULL,
199184610Salfred		&ng_parse_uint16_type
200184610Salfred	},
201184610Salfred	{
202184610Salfred		NGM_UBT_COOKIE,
203184610Salfred		NGM_UBT_NODE_SET_QLEN,
204184610Salfred		"set_qlen",
205184610Salfred		&ng_ubt_node_qlen_type,
206184610Salfred		NULL
207184610Salfred	},
208184610Salfred	{
209184610Salfred		NGM_UBT_COOKIE,
210184610Salfred		NGM_UBT_NODE_GET_QLEN,
211184610Salfred		"get_qlen",
212184610Salfred		&ng_ubt_node_qlen_type,
213184610Salfred		&ng_ubt_node_qlen_type
214184610Salfred	},
215184610Salfred	{
216184610Salfred		NGM_UBT_COOKIE,
217184610Salfred		NGM_UBT_NODE_GET_STAT,
218184610Salfred		"get_stat",
219184610Salfred		NULL,
220184610Salfred		&ng_ubt_node_stat_type
221184610Salfred	},
222184610Salfred	{
223184610Salfred		NGM_UBT_COOKIE,
224184610Salfred		NGM_UBT_NODE_RESET_STAT,
225184610Salfred		"reset_stat",
226184610Salfred		NULL,
227184610Salfred		NULL
228184610Salfred	},
229187494Semax	{ 0, }
230184610Salfred};
231184610Salfred
232184610Salfred/* Netgraph node type */
233187494Semaxstatic struct ng_type	typestruct =
234187494Semax{
235187494Semax	.version = 	NG_ABI_VERSION,
236187494Semax	.name =		NG_UBT_NODE_TYPE,
237187494Semax	.constructor =	ng_ubt_constructor,
238187494Semax	.rcvmsg =	ng_ubt_rcvmsg,
239187494Semax	.shutdown =	ng_ubt_shutdown,
240187494Semax	.newhook =	ng_ubt_newhook,
241187494Semax	.connect =	ng_ubt_connect,
242187494Semax	.rcvdata =	ng_ubt_rcvdata,
243187494Semax	.disconnect =	ng_ubt_disconnect,
244187494Semax	.cmdlist =	ng_ubt_cmdlist
245184610Salfred};
246184610Salfred
247187494Semax/****************************************************************************
248187494Semax ****************************************************************************
249187494Semax **                              USB specific
250187494Semax ****************************************************************************
251187494Semax ****************************************************************************/
252187494Semax
253184610Salfred/* USB methods */
254193045Sthompsastatic usb_callback_t	ubt_ctrl_write_callback;
255193045Sthompsastatic usb_callback_t	ubt_intr_read_callback;
256193045Sthompsastatic usb_callback_t	ubt_bulk_read_callback;
257193045Sthompsastatic usb_callback_t	ubt_bulk_write_callback;
258193045Sthompsastatic usb_callback_t	ubt_isoc_read_callback;
259193045Sthompsastatic usb_callback_t	ubt_isoc_write_callback;
260184610Salfred
261187741Semaxstatic int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
262192984Sthompsastatic int		ubt_isoc_read_one_frame(struct usb_xfer *, int);
263184610Salfred
264187494Semax/*
265187494Semax * USB config
266187494Semax *
267187494Semax * The following desribes usb transfers that could be submitted on USB device.
268187494Semax *
269187494Semax * Interface 0 on the USB device must present the following endpoints
270187494Semax *	1) Interrupt endpoint to receive HCI events
271187494Semax *	2) Bulk IN endpoint to receive ACL data
272187494Semax *	3) Bulk OUT endpoint to send ACL data
273187494Semax *
274187494Semax * Interface 1 on the USB device must present the following endpoints
275187494Semax *	1) Isochronous IN endpoint to receive SCO data
276187494Semax *	2) Isochronous OUT endpoint to send SCO data
277187494Semax */
278184610Salfred
279192984Sthompsastatic const struct usb_config		ubt_config[UBT_N_TRANSFER] =
280187494Semax{
281187494Semax	/*
282187494Semax	 * Interface #0
283187494Semax 	 */
284184610Salfred
285187494Semax	/* Outgoing bulk transfer - ACL packets */
286187494Semax	[UBT_IF_0_BULK_DT_WR] = {
287187494Semax		.type =		UE_BULK,
288187494Semax		.endpoint =	UE_ADDR_ANY,
289187494Semax		.direction =	UE_DIR_OUT,
290187741Semax		.if_index = 	0,
291190734Sthompsa		.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
292190734Sthompsa		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
293190734Sthompsa		.callback =	&ubt_bulk_write_callback,
294184610Salfred	},
295187494Semax	/* Incoming bulk transfer - ACL packets */
296187494Semax	[UBT_IF_0_BULK_DT_RD] = {
297187494Semax		.type =		UE_BULK,
298187494Semax		.endpoint =	UE_ADDR_ANY,
299187494Semax		.direction =	UE_DIR_IN,
300187741Semax		.if_index = 	0,
301190734Sthompsa		.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
302190734Sthompsa		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
303190734Sthompsa		.callback =	&ubt_bulk_read_callback,
304184610Salfred	},
305187494Semax	/* Incoming interrupt transfer - HCI events */
306187494Semax	[UBT_IF_0_INTR_DT_RD] = {
307187494Semax		.type =		UE_INTERRUPT,
308187494Semax		.endpoint =	UE_ADDR_ANY,
309187494Semax		.direction =	UE_DIR_IN,
310187741Semax		.if_index = 	0,
311190734Sthompsa		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
312190734Sthompsa		.bufsize =	UBT_INTR_BUFFER_SIZE,
313190734Sthompsa		.callback =	&ubt_intr_read_callback,
314184610Salfred	},
315187494Semax	/* Outgoing control transfer - HCI commands */
316187494Semax	[UBT_IF_0_CTRL_DT_WR] = {
317187494Semax		.type =		UE_CONTROL,
318187494Semax		.endpoint =	0x00,	/* control pipe */
319187494Semax		.direction =	UE_DIR_ANY,
320187741Semax		.if_index = 	0,
321190734Sthompsa		.bufsize =	UBT_CTRL_BUFFER_SIZE,
322190734Sthompsa		.callback =	&ubt_ctrl_write_callback,
323190734Sthompsa		.timeout =	5000,	/* 5 seconds */
324184610Salfred	},
325184610Salfred
326187494Semax	/*
327187494Semax	 * Interface #1
328187494Semax 	 */
329184610Salfred
330187494Semax	/* Incoming isochronous transfer #1 - SCO packets */
331187494Semax	[UBT_IF_1_ISOC_DT_RD1] = {
332187494Semax		.type =		UE_ISOCHRONOUS,
333187494Semax		.endpoint =	UE_ADDR_ANY,
334187494Semax		.direction =	UE_DIR_IN,
335187741Semax		.if_index = 	1,
336190734Sthompsa		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
337190734Sthompsa		.frames =	UBT_ISOC_NFRAMES,
338190734Sthompsa		.flags =	{ .short_xfer_ok = 1, },
339190734Sthompsa		.callback =	&ubt_isoc_read_callback,
340184610Salfred	},
341187494Semax	/* Incoming isochronous transfer #2 - SCO packets */
342187494Semax	[UBT_IF_1_ISOC_DT_RD2] = {
343187494Semax		.type =		UE_ISOCHRONOUS,
344187494Semax		.endpoint =	UE_ADDR_ANY,
345187494Semax		.direction =	UE_DIR_IN,
346187741Semax		.if_index = 	1,
347190734Sthompsa		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
348190734Sthompsa		.frames =	UBT_ISOC_NFRAMES,
349190734Sthompsa		.flags =	{ .short_xfer_ok = 1, },
350190734Sthompsa		.callback =	&ubt_isoc_read_callback,
351184610Salfred	},
352187494Semax	/* Outgoing isochronous transfer #1 - SCO packets */
353187494Semax	[UBT_IF_1_ISOC_DT_WR1] = {
354187494Semax		.type =		UE_ISOCHRONOUS,
355187494Semax		.endpoint =	UE_ADDR_ANY,
356187494Semax		.direction =	UE_DIR_OUT,
357187741Semax		.if_index = 	1,
358190734Sthompsa		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
359190734Sthompsa		.frames =	UBT_ISOC_NFRAMES,
360190734Sthompsa		.flags =	{ .short_xfer_ok = 1, },
361190734Sthompsa		.callback =	&ubt_isoc_write_callback,
362184610Salfred	},
363187494Semax	/* Outgoing isochronous transfer #2 - SCO packets */
364187494Semax	[UBT_IF_1_ISOC_DT_WR2] = {
365187494Semax		.type =		UE_ISOCHRONOUS,
366187494Semax		.endpoint =	UE_ADDR_ANY,
367187494Semax		.direction =	UE_DIR_OUT,
368187741Semax		.if_index = 	1,
369190734Sthompsa		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
370190734Sthompsa		.frames =	UBT_ISOC_NFRAMES,
371190734Sthompsa		.flags =	{ .short_xfer_ok = 1, },
372190734Sthompsa		.callback =	&ubt_isoc_write_callback,
373184610Salfred	},
374184610Salfred};
375184610Salfred
376184610Salfred/*
377184610Salfred * If for some reason device should not be attached then put
378184610Salfred * VendorID/ProductID pair into the list below. The format is
379184610Salfred * as follows:
380184610Salfred *
381187494Semax *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
382184610Salfred *
383184610Salfred * where VENDOR_ID and PRODUCT_ID are hex numbers.
384184610Salfred */
385187741Semax
386192984Sthompsastatic const struct usb_device_id ubt_ignore_devs[] =
387187741Semax{
388184610Salfred	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
389187494Semax	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
390184610Salfred};
391184610Salfred
392184610Salfred/* List of supported bluetooth devices */
393192984Sthompsastatic const struct usb_device_id ubt_devs[] =
394187741Semax{
395187494Semax	/* Generic Bluetooth class devices */
396187494Semax	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
397187494Semax	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
398187494Semax	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
399184610Salfred
400184610Salfred	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
401187494Semax	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
402184610Salfred};
403184610Salfred
404184610Salfred/*
405187494Semax * Probe for a USB Bluetooth device.
406187494Semax * USB context.
407184610Salfred */
408184610Salfred
409184610Salfredstatic int
410184610Salfredubt_probe(device_t dev)
411184610Salfred{
412192984Sthompsa	struct usb_attach_arg	*uaa = device_get_ivars(dev);
413184610Salfred
414192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
415184610Salfred		return (ENXIO);
416187494Semax
417187494Semax	if (uaa->info.bIfaceIndex != 0)
418184610Salfred		return (ENXIO);
419187494Semax
420187865Semax	if (uaa->use_generic == 0)
421187865Semax		return (ENXIO);
422187865Semax
423194228Sthompsa	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
424187494Semax			sizeof(ubt_ignore_devs), uaa) == 0)
425184610Salfred		return (ENXIO);
426187494Semax
427194228Sthompsa	return (usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa));
428187494Semax} /* ubt_probe */
429184610Salfred
430184610Salfred/*
431187494Semax * Attach the device.
432187494Semax * USB context.
433184610Salfred */
434184610Salfred
435184610Salfredstatic int
436184610Salfredubt_attach(device_t dev)
437184610Salfred{
438192984Sthompsa	struct usb_attach_arg		*uaa = device_get_ivars(dev);
439187494Semax	struct ubt_softc		*sc = device_get_softc(dev);
440192984Sthompsa	struct usb_endpoint_descriptor	*ed;
441192984Sthompsa	struct usb_interface_descriptor *id;
442187494Semax	uint16_t			wMaxPacketSize;
443187741Semax	uint8_t				alt_index, i, j;
444187741Semax	uint8_t				iface_index[2] = { 0, 1 };
445184610Salfred
446194228Sthompsa	device_set_usb_desc(dev);
447184610Salfred
448187741Semax	sc->sc_dev = dev;
449187741Semax	sc->sc_debug = NG_UBT_WARN_LEVEL;
450184610Salfred
451187494Semax	/*
452187494Semax	 * Create Netgraph node
453187494Semax	 */
454187494Semax
455187494Semax	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
456187741Semax		UBT_ALERT(sc, "could not create Netgraph node\n");
457187494Semax		return (ENXIO);
458187494Semax	}
459187494Semax
460187494Semax	/* Name Netgraph node */
461187741Semax	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
462187741Semax		UBT_ALERT(sc, "could not name Netgraph node\n");
463187494Semax		NG_NODE_UNREF(sc->sc_node);
464187494Semax		return (ENXIO);
465187494Semax	}
466187494Semax	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
467187494Semax	NG_NODE_FORCE_WRITER(sc->sc_node);
468187494Semax
469184610Salfred	/*
470184610Salfred	 * Initialize device softc structure
471184610Salfred	 */
472184610Salfred
473187494Semax	/* initialize locks */
474187741Semax	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
475187741Semax	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
476187494Semax
477187494Semax	/* initialize packet queues */
478184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
479184610Salfred	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
480187494Semax	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
481184610Salfred
482187494Semax	/* initialize glue task */
483187741Semax	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
484184610Salfred
485184610Salfred	/*
486184610Salfred	 * Configure Bluetooth USB device. Discover all required USB
487184610Salfred	 * interfaces and endpoints.
488184610Salfred	 *
489184610Salfred	 * USB device must present two interfaces:
490184610Salfred	 * 1) Interface 0 that has 3 endpoints
491184610Salfred	 *	1) Interrupt endpoint to receive HCI events
492184610Salfred	 *	2) Bulk IN endpoint to receive ACL data
493184610Salfred	 *	3) Bulk OUT endpoint to send ACL data
494184610Salfred	 *
495184610Salfred	 * 2) Interface 1 then has 2 endpoints
496184610Salfred	 *	1) Isochronous IN endpoint to receive SCO data
497184610Salfred 	 *	2) Isochronous OUT endpoint to send SCO data
498184610Salfred	 *
499184610Salfred	 * Interface 1 (with isochronous endpoints) has several alternate
500184610Salfred	 * configurations with different packet size.
501184610Salfred	 */
502184610Salfred
503184610Salfred	/*
504187741Semax	 * For interface #1 search alternate settings, and find
505187741Semax	 * the descriptor with the largest wMaxPacketSize
506184610Salfred	 */
507184610Salfred
508184610Salfred	wMaxPacketSize = 0;
509187494Semax	alt_index = 0;
510184610Salfred	i = 0;
511184610Salfred	j = 0;
512190728Sthompsa	ed = NULL;
513187494Semax
514190728Sthompsa	/*
515190728Sthompsa	 * Search through all the descriptors looking for the largest
516190728Sthompsa	 * packet size:
517190728Sthompsa	 */
518194228Sthompsa	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
519194228Sthompsa	    usbd_get_config_descriptor(uaa->device),
520192984Sthompsa	    (struct usb_descriptor *)ed))) {
521184610Salfred
522190728Sthompsa		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
523190728Sthompsa		    (ed->bLength >= sizeof(*id))) {
524192984Sthompsa			id = (struct usb_interface_descriptor *)ed;
525190728Sthompsa			i = id->bInterfaceNumber;
526190728Sthompsa			j = id->bAlternateSetting;
527184610Salfred		}
528187494Semax
529190728Sthompsa		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
530190728Sthompsa		    (ed->bLength >= sizeof(*ed)) &&
531190728Sthompsa		    (i == 1)) {
532190728Sthompsa			uint16_t temp;
533190728Sthompsa
534190728Sthompsa			temp = UGETW(ed->wMaxPacketSize);
535190728Sthompsa			if (temp > wMaxPacketSize) {
536190728Sthompsa				wMaxPacketSize = temp;
537190728Sthompsa				alt_index = j;
538190728Sthompsa			}
539184610Salfred		}
540184610Salfred	}
541184610Salfred
542187741Semax	/* Set alt configuration on interface #1 only if we found it */
543187494Semax	if (wMaxPacketSize > 0 &&
544194228Sthompsa	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
545187741Semax		UBT_ALERT(sc, "could not set alternate setting %d " \
546187494Semax			"for interface 1!\n", alt_index);
547184610Salfred		goto detach;
548184610Salfred	}
549184610Salfred
550187741Semax	/* Setup transfers for both interfaces */
551194228Sthompsa	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
552187741Semax			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
553187741Semax		UBT_ALERT(sc, "could not allocate transfers\n");
554184610Salfred		goto detach;
555184610Salfred	}
556184610Salfred
557187494Semax	/* Claim all interfaces on the device */
558194228Sthompsa	for (i = 1; usbd_get_iface(uaa->device, i) != NULL; i ++)
559194228Sthompsa		usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
560184610Salfred
561187494Semax	return (0); /* success */
562184610Salfred
563184610Salfreddetach:
564184610Salfred	ubt_detach(dev);
565184610Salfred
566184610Salfred	return (ENXIO);
567187494Semax} /* ubt_attach */
568184610Salfred
569184610Salfred/*
570187494Semax * Detach the device.
571187494Semax * USB context.
572184610Salfred */
573184610Salfred
574184610Salfredint
575184610Salfredubt_detach(device_t dev)
576184610Salfred{
577187494Semax	struct ubt_softc	*sc = device_get_softc(dev);
578187494Semax	node_p			node = sc->sc_node;
579184610Salfred
580187494Semax	/* Destroy Netgraph node */
581187494Semax	if (node != NULL) {
582187494Semax		sc->sc_node = NULL;
583187494Semax		NG_NODE_REALLY_DIE(node);
584187494Semax		ng_rmnode_self(node);
585184610Salfred	}
586184610Salfred
587187741Semax	/* Make sure ubt_task in gone */
588187741Semax	taskqueue_drain(taskqueue_swi, &sc->sc_task);
589187741Semax
590187494Semax	/* Free USB transfers, if any */
591194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
592184610Salfred
593187494Semax	/* Destroy queues */
594187741Semax	UBT_NG_LOCK(sc);
595184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
596184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
597184610Salfred	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
598187741Semax	UBT_NG_UNLOCK(sc);
599184610Salfred
600187741Semax	mtx_destroy(&sc->sc_if_mtx);
601187741Semax	mtx_destroy(&sc->sc_ng_mtx);
602187494Semax
603184610Salfred	return (0);
604187494Semax} /* ubt_detach */
605184610Salfred
606187494Semax/*
607187494Semax * Called when outgoing control request (HCI command) has completed, i.e.
608187494Semax * HCI command was sent to the device.
609187494Semax * USB context.
610187494Semax */
611187494Semax
612184610Salfredstatic void
613194677Sthompsaubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
614184610Salfred{
615194677Sthompsa	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
616192984Sthompsa	struct usb_device_request	req;
617187494Semax	struct mbuf			*m;
618194677Sthompsa	struct usb_page_cache		*pc;
619194677Sthompsa	int				actlen;
620184610Salfred
621194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
622194677Sthompsa
623184610Salfred	switch (USB_GET_STATE(xfer)) {
624184610Salfred	case USB_ST_TRANSFERRED:
625194677Sthompsa		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
626194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
627187741Semax		UBT_STAT_PCKTS_SENT(sc);
628187494Semax		/* FALLTHROUGH */
629184610Salfred
630184610Salfred	case USB_ST_SETUP:
631187494Semaxsend_next:
632187494Semax		/* Get next command mbuf, if any */
633187741Semax		UBT_NG_LOCK(sc);
634184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
635187741Semax		UBT_NG_UNLOCK(sc);
636184610Salfred
637184610Salfred		if (m == NULL) {
638187494Semax			UBT_INFO(sc, "HCI command queue is empty\n");
639187741Semax			break;	/* transfer complete */
640184610Salfred		}
641184610Salfred
642187494Semax		/* Initialize a USB control request and then schedule it */
643184610Salfred		bzero(&req, sizeof(req));
644184610Salfred		req.bmRequestType = UBT_HCI_REQUEST;
645184610Salfred		USETW(req.wLength, m->m_pkthdr.len);
646184610Salfred
647187494Semax		UBT_INFO(sc, "Sending control request, " \
648187494Semax			"bmRequestType=0x%02x, wLength=%d\n",
649187494Semax			req.bmRequestType, UGETW(req.wLength));
650184610Salfred
651194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
652194677Sthompsa		usbd_copy_in(pc, 0, &req, sizeof(req));
653194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 1);
654194677Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
655184610Salfred
656194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
657194677Sthompsa		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
658194677Sthompsa		usbd_xfer_set_frames(xfer, 2);
659184610Salfred
660184610Salfred		NG_FREE_M(m);
661184610Salfred
662194228Sthompsa		usbd_transfer_submit(xfer);
663187494Semax		break;
664184610Salfred
665187494Semax	default: /* Error */
666194677Sthompsa		if (error != USB_ERR_CANCELLED) {
667187494Semax			UBT_WARN(sc, "control transfer failed: %s\n",
668194677Sthompsa				usbd_errstr(error));
669187494Semax
670187494Semax			UBT_STAT_OERROR(sc);
671187494Semax			goto send_next;
672184610Salfred		}
673187494Semax
674187741Semax		/* transfer cancelled */
675187494Semax		break;
676184610Salfred	}
677187494Semax} /* ubt_ctrl_write_callback */
678184610Salfred
679187494Semax/*
680187494Semax * Called when incoming interrupt transfer (HCI event) has completed, i.e.
681187494Semax * HCI event was received from the device.
682187494Semax * USB context.
683187494Semax */
684187494Semax
685184610Salfredstatic void
686194677Sthompsaubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
687184610Salfred{
688194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
689187494Semax	struct mbuf		*m;
690187494Semax	ng_hci_event_pkt_t	*hdr;
691194677Sthompsa	struct usb_page_cache	*pc;
692194677Sthompsa	int			actlen;
693184610Salfred
694194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
695194677Sthompsa
696187494Semax	m = NULL;
697187494Semax
698184610Salfred	switch (USB_GET_STATE(xfer)) {
699184610Salfred	case USB_ST_TRANSFERRED:
700187494Semax		/* Allocate a new mbuf */
701184610Salfred		MGETHDR(m, M_DONTWAIT, MT_DATA);
702184610Salfred		if (m == NULL) {
703187494Semax			UBT_STAT_IERROR(sc);
704187494Semax			goto submit_next;
705184610Salfred		}
706187494Semax
707184610Salfred		MCLGET(m, M_DONTWAIT);
708184610Salfred		if (!(m->m_flags & M_EXT)) {
709187494Semax			UBT_STAT_IERROR(sc);
710187494Semax			goto submit_next;
711184610Salfred		}
712187494Semax
713187494Semax		/* Add HCI packet type */
714187494Semax		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
715187494Semax		m->m_pkthdr.len = m->m_len = 1;
716187494Semax
717194677Sthompsa		if (actlen > MCLBYTES - 1)
718194677Sthompsa			actlen = MCLBYTES - 1;
719187494Semax
720194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
721194677Sthompsa		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
722194677Sthompsa		m->m_pkthdr.len += actlen;
723194677Sthompsa		m->m_len += actlen;
724187494Semax
725187494Semax		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
726194677Sthompsa			actlen);
727187494Semax
728187494Semax		/* Validate packet and send it up the stack */
729187494Semax		if (m->m_pkthdr.len < sizeof(*hdr)) {
730187494Semax			UBT_INFO(sc, "HCI event packet is too short\n");
731187494Semax
732187494Semax			UBT_STAT_IERROR(sc);
733187494Semax			goto submit_next;
734184610Salfred		}
735184610Salfred
736187494Semax		hdr = mtod(m, ng_hci_event_pkt_t *);
737187494Semax		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
738187494Semax			UBT_ERR(sc, "Invalid HCI event packet size, " \
739187494Semax				"length=%d, pktlen=%d\n",
740187494Semax				hdr->length, m->m_pkthdr.len);
741184610Salfred
742187494Semax			UBT_STAT_IERROR(sc);
743187494Semax			goto submit_next;
744184610Salfred		}
745184610Salfred
746187494Semax		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
747187494Semax			"length=%d\n", m->m_pkthdr.len, hdr->length);
748184610Salfred
749187494Semax		UBT_STAT_PCKTS_RECV(sc);
750187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
751184610Salfred
752187741Semax		ubt_fwd_mbuf_up(sc, &m);
753187494Semax		/* m == NULL at this point */
754187494Semax		/* FALLTHROUGH */
755184610Salfred
756184610Salfred	case USB_ST_SETUP:
757187494Semaxsubmit_next:
758187494Semax		NG_FREE_M(m); /* checks for m != NULL */
759184610Salfred
760194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
761194228Sthompsa		usbd_transfer_submit(xfer);
762187494Semax		break;
763184610Salfred
764187494Semax	default: /* Error */
765194677Sthompsa		if (error != USB_ERR_CANCELLED) {
766187494Semax			UBT_WARN(sc, "interrupt transfer failed: %s\n",
767194677Sthompsa				usbd_errstr(error));
768184610Salfred
769187494Semax			/* Try to clear stall first */
770194677Sthompsa			usbd_xfer_set_stall(xfer);
771187741Semax			goto submit_next;
772187741Semax		}
773187741Semax			/* transfer cancelled */
774187494Semax		break;
775184610Salfred	}
776187494Semax} /* ubt_intr_read_callback */
777184610Salfred
778187494Semax/*
779187494Semax * Called when incoming bulk transfer (ACL packet) has completed, i.e.
780187494Semax * ACL packet was received from the device.
781187494Semax * USB context.
782187494Semax */
783187494Semax
784184610Salfredstatic void
785194677Sthompsaubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
786184610Salfred{
787194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
788187494Semax	struct mbuf		*m;
789187494Semax	ng_hci_acldata_pkt_t	*hdr;
790194677Sthompsa	struct usb_page_cache	*pc;
791187494Semax	uint16_t		len;
792194677Sthompsa	int			actlen;
793184610Salfred
794194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
795194677Sthompsa
796187494Semax	m = NULL;
797184610Salfred
798184610Salfred	switch (USB_GET_STATE(xfer)) {
799184610Salfred	case USB_ST_TRANSFERRED:
800187494Semax		/* Allocate new mbuf */
801184610Salfred		MGETHDR(m, M_DONTWAIT, MT_DATA);
802184610Salfred		if (m == NULL) {
803187494Semax			UBT_STAT_IERROR(sc);
804187494Semax			goto submit_next;
805184610Salfred		}
806187494Semax
807184610Salfred		MCLGET(m, M_DONTWAIT);
808184610Salfred		if (!(m->m_flags & M_EXT)) {
809187494Semax			UBT_STAT_IERROR(sc);
810187494Semax			goto submit_next;
811184610Salfred		}
812184610Salfred
813187494Semax		/* Add HCI packet type */
814187494Semax		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
815187494Semax		m->m_pkthdr.len = m->m_len = 1;
816184610Salfred
817194677Sthompsa		if (actlen > MCLBYTES - 1)
818194677Sthompsa			actlen = MCLBYTES - 1;
819184610Salfred
820194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
821194677Sthompsa		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
822194677Sthompsa		m->m_pkthdr.len += actlen;
823194677Sthompsa		m->m_len += actlen;
824184610Salfred
825187494Semax		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
826194677Sthompsa			actlen);
827184610Salfred
828187494Semax		/* Validate packet and send it up the stack */
829187494Semax		if (m->m_pkthdr.len < sizeof(*hdr)) {
830187494Semax			UBT_INFO(sc, "HCI ACL packet is too short\n");
831184610Salfred
832187494Semax			UBT_STAT_IERROR(sc);
833187494Semax			goto submit_next;
834184610Salfred		}
835184610Salfred
836187494Semax		hdr = mtod(m, ng_hci_acldata_pkt_t *);
837187494Semax		len = le16toh(hdr->length);
838187494Semax		if (len != (m->m_pkthdr.len - sizeof(*hdr))) {
839187494Semax			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
840187494Semax				"pktlen=%d\n", len, m->m_pkthdr.len);
841184610Salfred
842187494Semax			UBT_STAT_IERROR(sc);
843187494Semax			goto submit_next;
844184610Salfred		}
845184610Salfred
846187494Semax		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
847187494Semax			"length=%d\n", m->m_pkthdr.len, len);
848184610Salfred
849187494Semax		UBT_STAT_PCKTS_RECV(sc);
850187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
851184610Salfred
852187741Semax		ubt_fwd_mbuf_up(sc, &m);
853187494Semax		/* m == NULL at this point */
854187494Semax		/* FALLTHOUGH */
855184610Salfred
856187494Semax	case USB_ST_SETUP:
857187494Semaxsubmit_next:
858187494Semax		NG_FREE_M(m); /* checks for m != NULL */
859184610Salfred
860194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
861194228Sthompsa		usbd_transfer_submit(xfer);
862187494Semax		break;
863184610Salfred
864187494Semax	default: /* Error */
865194677Sthompsa		if (error != USB_ERR_CANCELLED) {
866187494Semax			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
867194677Sthompsa				usbd_errstr(error));
868184610Salfred
869187494Semax			/* Try to clear stall first */
870194677Sthompsa			usbd_xfer_set_stall(xfer);
871187741Semax			goto submit_next;
872187741Semax		}
873187741Semax			/* transfer cancelled */
874187494Semax		break;
875187494Semax	}
876187494Semax} /* ubt_bulk_read_callback */
877184610Salfred
878187494Semax/*
879187494Semax * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
880187494Semax * ACL packet was sent to the device.
881187494Semax * USB context.
882187494Semax */
883184610Salfred
884184610Salfredstatic void
885194677Sthompsaubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
886184610Salfred{
887194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
888187494Semax	struct mbuf		*m;
889194677Sthompsa	struct usb_page_cache	*pc;
890194677Sthompsa	int			actlen;
891184610Salfred
892194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
893194677Sthompsa
894184610Salfred	switch (USB_GET_STATE(xfer)) {
895184610Salfred	case USB_ST_TRANSFERRED:
896194677Sthompsa		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
897194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
898187741Semax		UBT_STAT_PCKTS_SENT(sc);
899187494Semax		/* FALLTHROUGH */
900184610Salfred
901187494Semax	case USB_ST_SETUP:
902187741Semaxsend_next:
903187494Semax		/* Get next mbuf, if any */
904187741Semax		UBT_NG_LOCK(sc);
905184610Salfred		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
906187741Semax		UBT_NG_UNLOCK(sc);
907184610Salfred
908184610Salfred		if (m == NULL) {
909187494Semax			UBT_INFO(sc, "ACL data queue is empty\n");
910187741Semax			break; /* transfer completed */
911184610Salfred		}
912187494Semax
913184610Salfred		/*
914187494Semax		 * Copy ACL data frame back to a linear USB transfer buffer
915187494Semax		 * and schedule transfer
916184610Salfred		 */
917184610Salfred
918194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
919194677Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
920194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
921184610Salfred
922187494Semax		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
923187494Semax			m->m_pkthdr.len);
924184610Salfred
925184610Salfred		NG_FREE_M(m);
926184610Salfred
927194228Sthompsa		usbd_transfer_submit(xfer);
928187494Semax		break;
929184610Salfred
930187494Semax	default: /* Error */
931194677Sthompsa		if (error != USB_ERR_CANCELLED) {
932187494Semax			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
933194677Sthompsa				usbd_errstr(error));
934184610Salfred
935187494Semax			UBT_STAT_OERROR(sc);
936184610Salfred
937184610Salfred			/* try to clear stall first */
938194677Sthompsa			usbd_xfer_set_stall(xfer);
939187741Semax			goto send_next;
940187741Semax		}
941187741Semax			/* transfer cancelled */
942187494Semax		break;
943184610Salfred	}
944187494Semax} /* ubt_bulk_write_callback */
945184610Salfred
946187494Semax/*
947187494Semax * Called when incoming isoc transfer (SCO packet) has completed, i.e.
948187494Semax * SCO packet was received from the device.
949187494Semax * USB context.
950187494Semax */
951187494Semax
952184610Salfredstatic void
953194677Sthompsaubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
954184610Salfred{
955194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
956187494Semax	int			n;
957194677Sthompsa	int actlen, nframes;
958184610Salfred
959194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
960194677Sthompsa
961187494Semax	switch (USB_GET_STATE(xfer)) {
962187494Semax	case USB_ST_TRANSFERRED:
963194677Sthompsa		for (n = 0; n < nframes; n ++)
964187494Semax			if (ubt_isoc_read_one_frame(xfer, n) < 0)
965187494Semax				break;
966187494Semax		/* FALLTHROUGH */
967184610Salfred
968187494Semax	case USB_ST_SETUP:
969187494Semaxread_next:
970194677Sthompsa		for (n = 0; n < nframes; n ++)
971194677Sthompsa			usbd_xfer_set_frame_len(xfer, n,
972194677Sthompsa			    usbd_xfer_max_framelen(xfer));
973184610Salfred
974194228Sthompsa		usbd_transfer_submit(xfer);
975187494Semax		break;
976184610Salfred
977187494Semax	default: /* Error */
978194677Sthompsa                if (error != USB_ERR_CANCELLED) {
979187494Semax                        UBT_STAT_IERROR(sc);
980187494Semax                        goto read_next;
981187494Semax                }
982184610Salfred
983187741Semax		/* transfer cancelled */
984187494Semax		break;
985187494Semax	}
986187494Semax} /* ubt_isoc_read_callback */
987184610Salfred
988187494Semax/*
989187494Semax * Helper function. Called from ubt_isoc_read_callback() to read
990187494Semax * SCO data from one frame.
991187494Semax * USB context.
992187494Semax */
993184610Salfred
994187494Semaxstatic int
995192984Sthompsaubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
996187494Semax{
997194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
998194677Sthompsa	struct usb_page_cache	*pc;
999187494Semax	struct mbuf		*m;
1000194677Sthompsa	int			len, want, got, total;
1001184610Salfred
1002187494Semax	/* Get existing SCO reassembly buffer */
1003194677Sthompsa	pc = usbd_xfer_get_frame(xfer, 0);
1004187494Semax	m = sc->sc_isoc_in_buffer;
1005194682Sthompsa	total = usbd_xfer_frame_len(xfer, frame_no);
1006184610Salfred
1007187494Semax	/* While we have data in the frame */
1008194677Sthompsa	while (total > 0) {
1009187494Semax		if (m == NULL) {
1010187494Semax			/* Start new reassembly buffer */
1011187494Semax			MGETHDR(m, M_DONTWAIT, MT_DATA);
1012187494Semax			if (m == NULL) {
1013187494Semax				UBT_STAT_IERROR(sc);
1014187494Semax				return (-1);	/* XXX out of sync! */
1015187494Semax			}
1016184610Salfred
1017187494Semax			MCLGET(m, M_DONTWAIT);
1018187494Semax			if (!(m->m_flags & M_EXT)) {
1019187494Semax				UBT_STAT_IERROR(sc);
1020187494Semax				NG_FREE_M(m);
1021187494Semax				return (-1);	/* XXX out of sync! */
1022184610Salfred			}
1023184610Salfred
1024187494Semax			/* Expect SCO header */
1025187494Semax			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
1026187494Semax			m->m_pkthdr.len = m->m_len = got = 1;
1027187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
1028187494Semax		} else {
1029187494Semax			/*
1030187494Semax			 * Check if we have SCO header and if so
1031187494Semax			 * adjust amount of data we want
1032187494Semax			 */
1033187494Semax			got = m->m_pkthdr.len;
1034187494Semax			want = sizeof(ng_hci_scodata_pkt_t);
1035184610Salfred
1036187494Semax			if (got >= want)
1037187494Semax				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
1038184610Salfred		}
1039184610Salfred
1040187494Semax		/* Append frame data to the SCO reassembly buffer */
1041194677Sthompsa		len = total;
1042187494Semax		if (got + len > want)
1043187494Semax			len = want - got;
1044184610Salfred
1045194677Sthompsa		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
1046187494Semax			mtod(m, uint8_t *) + m->m_pkthdr.len, len);
1047184610Salfred
1048187494Semax		m->m_pkthdr.len += len;
1049187494Semax		m->m_len += len;
1050194677Sthompsa		total -= len;
1051184610Salfred
1052187494Semax		/* Check if we got everything we wanted, if not - continue */
1053187494Semax		if (got != want)
1054187494Semax			continue;
1055184610Salfred
1056187494Semax		/* If we got here then we got complete SCO frame */
1057187494Semax		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
1058187494Semax			"length=%d\n", m->m_pkthdr.len,
1059187494Semax			mtod(m, ng_hci_scodata_pkt_t *)->length);
1060184610Salfred
1061187494Semax		UBT_STAT_PCKTS_RECV(sc);
1062187494Semax		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
1063184610Salfred
1064187741Semax		ubt_fwd_mbuf_up(sc, &m);
1065187494Semax		/* m == NULL at this point */
1066187494Semax	}
1067184610Salfred
1068187494Semax	/* Put SCO reassembly buffer back */
1069187494Semax	sc->sc_isoc_in_buffer = m;
1070184610Salfred
1071187494Semax	return (0);
1072187494Semax} /* ubt_isoc_read_one_frame */
1073184610Salfred
1074187494Semax/*
1075187494Semax * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
1076187494Semax * SCO packet was sent to the device.
1077187494Semax * USB context.
1078187494Semax */
1079184610Salfred
1080184610Salfredstatic void
1081194677Sthompsaubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
1082184610Salfred{
1083194677Sthompsa	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
1084194677Sthompsa	struct usb_page_cache	*pc;
1085187494Semax	struct mbuf		*m;
1086187494Semax	int			n, space, offset;
1087194677Sthompsa	int			actlen, nframes;
1088184610Salfred
1089194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1090194677Sthompsa	pc = usbd_xfer_get_frame(xfer, 0);
1091194677Sthompsa
1092184610Salfred	switch (USB_GET_STATE(xfer)) {
1093184610Salfred	case USB_ST_TRANSFERRED:
1094194677Sthompsa		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1095194677Sthompsa		UBT_STAT_BYTES_SENT(sc, actlen);
1096187741Semax		UBT_STAT_PCKTS_SENT(sc);
1097187494Semax		/* FALLTHROUGH */
1098184610Salfred
1099184610Salfred	case USB_ST_SETUP:
1100187494Semaxsend_next:
1101184610Salfred		offset = 0;
1102194677Sthompsa		space = usbd_xfer_max_framelen(xfer) * nframes;
1103187494Semax		m = NULL;
1104184610Salfred
1105187494Semax		while (space > 0) {
1106187494Semax			if (m == NULL) {
1107187741Semax				UBT_NG_LOCK(sc);
1108187494Semax				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
1109187741Semax				UBT_NG_UNLOCK(sc);
1110184610Salfred
1111187494Semax				if (m == NULL)
1112187494Semax					break;
1113187494Semax			}
1114184610Salfred
1115187494Semax			n = min(space, m->m_pkthdr.len);
1116187494Semax			if (n > 0) {
1117194677Sthompsa				usbd_m_copy_in(pc, offset, m,0, n);
1118187494Semax				m_adj(m, n);
1119184610Salfred
1120187494Semax				offset += n;
1121187494Semax				space -= n;
1122187494Semax			}
1123184610Salfred
1124187494Semax			if (m->m_pkthdr.len == 0)
1125187494Semax				NG_FREE_M(m); /* sets m = NULL */
1126187494Semax		}
1127184610Salfred
1128187494Semax		/* Put whatever is left from mbuf back on queue */
1129187494Semax		if (m != NULL) {
1130187741Semax			UBT_NG_LOCK(sc);
1131187494Semax			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
1132187741Semax			UBT_NG_UNLOCK(sc);
1133184610Salfred		}
1134184610Salfred
1135187494Semax		/*
1136187494Semax		 * Calculate sizes for isoc frames.
1137187494Semax		 * Note that offset could be 0 at this point (i.e. we have
1138187494Semax		 * nothing to send). That is fine, as we have isoc. transfers
1139187494Semax		 * going in both directions all the time. In this case it
1140187494Semax		 * would be just empty isoc. transfer.
1141187494Semax		 */
1142187494Semax
1143194677Sthompsa		for (n = 0; n < nframes; n ++) {
1144194677Sthompsa			usbd_xfer_set_frame_len(xfer, n,
1145194677Sthompsa			    min(offset, usbd_xfer_max_framelen(xfer)));
1146194682Sthompsa			offset -= usbd_xfer_frame_len(xfer, n);
1147187494Semax		}
1148187494Semax
1149194228Sthompsa		usbd_transfer_submit(xfer);
1150187494Semax		break;
1151184610Salfred
1152187494Semax	default: /* Error */
1153194677Sthompsa		if (error != USB_ERR_CANCELLED) {
1154187494Semax			UBT_STAT_OERROR(sc);
1155187494Semax			goto send_next;
1156184610Salfred		}
1157187494Semax
1158187741Semax		/* transfer cancelled */
1159187494Semax		break;
1160184610Salfred	}
1161184610Salfred}
1162184610Salfred
1163187741Semax/*
1164187741Semax * Utility function to forward provided mbuf upstream (i.e. up the stack).
1165187741Semax * Modifies value of the mbuf pointer (sets it to NULL).
1166187741Semax * Save to call from any context.
1167187741Semax */
1168187741Semax
1169187741Semaxstatic int
1170187741Semaxubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
1171187741Semax{
1172187741Semax	hook_p	hook;
1173187741Semax	int	error;
1174187741Semax
1175187741Semax	/*
1176187741Semax	 * Close the race with Netgraph hook newhook/disconnect methods.
1177187741Semax	 * Save the hook pointer atomically. Two cases are possible:
1178187741Semax	 *
1179187741Semax	 * 1) The hook pointer is NULL. It means disconnect method got
1180187741Semax	 *    there first. In this case we are done.
1181187741Semax	 *
1182187741Semax	 * 2) The hook pointer is not NULL. It means that hook pointer
1183187741Semax	 *    could be either in valid or invalid (i.e. in the process
1184187741Semax	 *    of disconnect) state. In any case grab an extra reference
1185187741Semax	 *    to protect the hook pointer.
1186187741Semax	 *
1187187741Semax	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
1188187741Semax	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
1189187741Semax	 */
1190187741Semax
1191187741Semax	UBT_NG_LOCK(sc);
1192187741Semax	if ((hook = sc->sc_hook) != NULL)
1193187741Semax		NG_HOOK_REF(hook);
1194187741Semax	UBT_NG_UNLOCK(sc);
1195187741Semax
1196187741Semax	if (hook == NULL) {
1197187741Semax		NG_FREE_M(*m);
1198187741Semax		return (ENETDOWN);
1199187741Semax	}
1200187741Semax
1201187741Semax	NG_SEND_DATA_ONLY(error, hook, *m);
1202187741Semax	NG_HOOK_UNREF(hook);
1203187741Semax
1204187741Semax	if (error != 0)
1205187741Semax		UBT_STAT_IERROR(sc);
1206187741Semax
1207187741Semax	return (error);
1208187741Semax} /* ubt_fwd_mbuf_up */
1209187741Semax
1210184610Salfred/****************************************************************************
1211184610Salfred ****************************************************************************
1212187494Semax **                                 Glue
1213184610Salfred ****************************************************************************
1214184610Salfred ****************************************************************************/
1215184610Salfred
1216184610Salfred/*
1217187741Semax * Schedule glue task. Should be called with sc_ng_mtx held.
1218187494Semax * Netgraph context.
1219184610Salfred */
1220184610Salfred
1221187741Semaxstatic void
1222187494Semaxubt_task_schedule(ubt_softc_p sc, int action)
1223184610Salfred{
1224187741Semax	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
1225184610Salfred
1226187741Semax	/*
1227187741Semax	 * Try to handle corner case when "start all" and "stop all"
1228187741Semax	 * actions can both be set before task is executed.
1229187741Semax	 *
1230187741Semax	 * The rules are
1231187741Semax	 *
1232187741Semax	 * sc_task_flags	action		new sc_task_flags
1233187741Semax	 * ------------------------------------------------------
1234187741Semax	 * 0			start		start
1235187741Semax	 * 0			stop		stop
1236187741Semax	 * start		start		start
1237187741Semax	 * start		stop		stop
1238187741Semax	 * stop			start		stop|start
1239187741Semax	 * stop			stop		stop
1240187741Semax	 * stop|start		start		stop|start
1241187741Semax	 * stop|start		stop		stop
1242187741Semax	 */
1243187494Semax
1244187741Semax	if (action != 0) {
1245187741Semax		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
1246187494Semax			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
1247187494Semax
1248187494Semax		sc->sc_task_flags |= action;
1249187494Semax	}
1250187494Semax
1251187494Semax	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
1252187741Semax		return;
1253187494Semax
1254187494Semax	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
1255187494Semax		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
1256187741Semax		return;
1257187494Semax	}
1258187494Semax
1259187494Semax	/* XXX: i think this should never happen */
1260187494Semax} /* ubt_task_schedule */
1261187494Semax
1262184610Salfred/*
1263187494Semax * Glue task. Examines sc_task_flags and does things depending on it.
1264187494Semax * Taskqueue context.
1265184610Salfred */
1266184610Salfred
1267187494Semaxstatic void
1268187494Semaxubt_task(void *context, int pending)
1269184610Salfred{
1270187741Semax	ubt_softc_p	sc = context;
1271187741Semax	int		task_flags, i;
1272184610Salfred
1273187741Semax	UBT_NG_LOCK(sc);
1274187494Semax	task_flags = sc->sc_task_flags;
1275187494Semax	sc->sc_task_flags = 0;
1276187741Semax	UBT_NG_UNLOCK(sc);
1277187494Semax
1278187741Semax	/*
1279187741Semax	 * Stop all USB transfers synchronously.
1280187741Semax	 * Stop interface #0 and #1 transfers at the same time and in the
1281194228Sthompsa	 * same loop. usbd_transfer_drain() will do appropriate locking.
1282187741Semax	 */
1283187494Semax
1284187741Semax	if (task_flags & UBT_FLAG_T_STOP_ALL)
1285187741Semax		for (i = 0; i < UBT_N_TRANSFER; i ++)
1286194228Sthompsa			usbd_transfer_drain(sc->sc_xfer[i]);
1287187494Semax
1288187741Semax	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
1289187494Semax	if (task_flags & UBT_FLAG_T_START_ALL) {
1290187494Semax		/*
1291187494Semax		 * Interface #0
1292187494Semax		 */
1293187494Semax
1294187741Semax		mtx_lock(&sc->sc_if_mtx);
1295187741Semax
1296187494Semax		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
1297187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
1298187494Semax
1299187494Semax		/*
1300187494Semax		 * Interface #1
1301187494Semax		 * Start both read and write isoc. transfers by default.
1302187494Semax		 * Get them going all the time even if we have nothing
1303187494Semax		 * to send to avoid any delays.
1304187494Semax		 */
1305187494Semax
1306187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
1307187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
1308187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
1309187494Semax		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
1310187741Semax
1311187741Semax		mtx_unlock(&sc->sc_if_mtx);
1312184610Salfred	}
1313187494Semax
1314187494Semax 	/* Start outgoing control transfer */
1315187494Semax	if (task_flags & UBT_FLAG_T_START_CTRL) {
1316187741Semax		mtx_lock(&sc->sc_if_mtx);
1317187494Semax		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
1318187741Semax		mtx_unlock(&sc->sc_if_mtx);
1319184610Salfred	}
1320184610Salfred
1321187494Semax	/* Start outgoing bulk transfer */
1322187494Semax	if (task_flags & UBT_FLAG_T_START_BULK) {
1323187741Semax		mtx_lock(&sc->sc_if_mtx);
1324187494Semax		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
1325187741Semax		mtx_unlock(&sc->sc_if_mtx);
1326184610Salfred	}
1327187494Semax} /* ubt_task */
1328187494Semax
1329187494Semax/****************************************************************************
1330187494Semax ****************************************************************************
1331187494Semax **                        Netgraph specific
1332187494Semax ****************************************************************************
1333187494Semax ****************************************************************************/
1334184610Salfred
1335187494Semax/*
1336187494Semax * Netgraph node constructor. Do not allow to create node of this type.
1337187494Semax * Netgraph context.
1338187494Semax */
1339184610Salfred
1340187494Semaxstatic int
1341187494Semaxng_ubt_constructor(node_p node)
1342187494Semax{
1343187494Semax	return (EINVAL);
1344187494Semax} /* ng_ubt_constructor */
1345184610Salfred
1346184610Salfred/*
1347187494Semax * Netgraph node destructor. Destroy node only when device has been detached.
1348187494Semax * Netgraph context.
1349184610Salfred */
1350184610Salfred
1351184610Salfredstatic int
1352187494Semaxng_ubt_shutdown(node_p node)
1353184610Salfred{
1354187494Semax	if (node->nd_flags & NGF_REALLY_DIE) {
1355187494Semax		/*
1356187494Semax                 * We came here because the USB device is being
1357187494Semax		 * detached, so stop being persistant.
1358187494Semax                 */
1359187494Semax		NG_NODE_SET_PRIVATE(node, NULL);
1360187494Semax		NG_NODE_UNREF(node);
1361187494Semax	} else
1362187494Semax		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
1363184610Salfred
1364187494Semax	return (0);
1365187494Semax} /* ng_ubt_shutdown */
1366184610Salfred
1367187494Semax/*
1368187494Semax * Create new hook. There can only be one.
1369187494Semax * Netgraph context.
1370187494Semax */
1371184610Salfred
1372187494Semaxstatic int
1373187494Semaxng_ubt_newhook(node_p node, hook_p hook, char const *name)
1374187494Semax{
1375187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1376184610Salfred
1377187494Semax	if (strcmp(name, NG_UBT_HOOK) != 0)
1378187494Semax		return (EINVAL);
1379184610Salfred
1380187741Semax	UBT_NG_LOCK(sc);
1381187741Semax	if (sc->sc_hook != NULL) {
1382187741Semax		UBT_NG_UNLOCK(sc);
1383187741Semax
1384187494Semax		return (EISCONN);
1385187741Semax	}
1386184610Salfred
1387187494Semax	sc->sc_hook = hook;
1388187741Semax	UBT_NG_UNLOCK(sc);
1389184610Salfred
1390187494Semax	return (0);
1391187494Semax} /* ng_ubt_newhook */
1392184610Salfred
1393187494Semax/*
1394187494Semax * Connect hook. Start incoming USB transfers.
1395187494Semax * Netgraph context.
1396187494Semax */
1397184610Salfred
1398187494Semaxstatic int
1399187494Semaxng_ubt_connect(hook_p hook)
1400187494Semax{
1401187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1402184610Salfred
1403187494Semax	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
1404184610Salfred
1405187741Semax	UBT_NG_LOCK(sc);
1406187494Semax	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
1407187741Semax	UBT_NG_UNLOCK(sc);
1408184610Salfred
1409184610Salfred	return (0);
1410187494Semax} /* ng_ubt_connect */
1411184610Salfred
1412184610Salfred/*
1413187494Semax * Disconnect hook.
1414187494Semax * Netgraph context.
1415184610Salfred */
1416184610Salfred
1417184610Salfredstatic int
1418184610Salfredng_ubt_disconnect(hook_p hook)
1419184610Salfred{
1420187741Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1421184610Salfred
1422187741Semax	UBT_NG_LOCK(sc);
1423184610Salfred
1424187741Semax	if (hook != sc->sc_hook) {
1425187741Semax		UBT_NG_UNLOCK(sc);
1426184610Salfred
1427187494Semax		return (EINVAL);
1428187741Semax	}
1429184610Salfred
1430187494Semax	sc->sc_hook = NULL;
1431184610Salfred
1432187741Semax	/* Kick off task to stop all USB xfers */
1433187741Semax	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
1434184610Salfred
1435187494Semax	/* Drain queues */
1436187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
1437187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
1438187494Semax	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
1439184610Salfred
1440187741Semax	UBT_NG_UNLOCK(sc);
1441184610Salfred
1442187494Semax	return (0);
1443187494Semax} /* ng_ubt_disconnect */
1444187494Semax
1445184610Salfred/*
1446187494Semax * Process control message.
1447187494Semax * Netgraph context.
1448184610Salfred */
1449184610Salfred
1450184610Salfredstatic int
1451184610Salfredng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
1452184610Salfred{
1453187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
1454187494Semax	struct ng_mesg		*msg, *rsp = NULL;
1455187494Semax	struct ng_bt_mbufq	*q;
1456187494Semax	int			error = 0, queue, qlen;
1457184610Salfred
1458184610Salfred	NGI_GET_MSG(item, msg);
1459184610Salfred
1460184610Salfred	switch (msg->header.typecookie) {
1461184610Salfred	case NGM_GENERIC_COOKIE:
1462184610Salfred		switch (msg->header.cmd) {
1463184610Salfred		case NGM_TEXT_STATUS:
1464184610Salfred			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
1465187494Semax			if (rsp == NULL) {
1466184610Salfred				error = ENOMEM;
1467187494Semax				break;
1468187494Semax			}
1469187494Semax
1470187494Semax			snprintf(rsp->data, NG_TEXTRESPONSE,
1471187494Semax				"Hook: %s\n" \
1472187494Semax				"Task flags: %#x\n" \
1473187494Semax				"Debug: %d\n" \
1474187494Semax				"CMD queue: [have:%d,max:%d]\n" \
1475187494Semax				"ACL queue: [have:%d,max:%d]\n" \
1476187494Semax				"SCO queue: [have:%d,max:%d]",
1477187741Semax				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
1478187494Semax				sc->sc_task_flags,
1479187494Semax				sc->sc_debug,
1480187494Semax				sc->sc_cmdq.len,
1481187494Semax				sc->sc_cmdq.maxlen,
1482187494Semax				sc->sc_aclq.len,
1483187494Semax				sc->sc_aclq.maxlen,
1484187494Semax				sc->sc_scoq.len,
1485187494Semax				sc->sc_scoq.maxlen);
1486184610Salfred			break;
1487184610Salfred
1488184610Salfred		default:
1489184610Salfred			error = EINVAL;
1490184610Salfred			break;
1491184610Salfred		}
1492184610Salfred		break;
1493184610Salfred
1494184610Salfred	case NGM_UBT_COOKIE:
1495184610Salfred		switch (msg->header.cmd) {
1496184610Salfred		case NGM_UBT_NODE_SET_DEBUG:
1497187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
1498184610Salfred				error = EMSGSIZE;
1499187494Semax				break;
1500187494Semax			}
1501187494Semax
1502187494Semax			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
1503184610Salfred			break;
1504184610Salfred
1505184610Salfred		case NGM_UBT_NODE_GET_DEBUG:
1506184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
1507184610Salfred			    M_NOWAIT);
1508187494Semax			if (rsp == NULL) {
1509184610Salfred				error = ENOMEM;
1510187494Semax				break;
1511187494Semax			}
1512187494Semax
1513187494Semax			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
1514184610Salfred			break;
1515184610Salfred
1516184610Salfred		case NGM_UBT_NODE_SET_QLEN:
1517187494Semax			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1518184610Salfred				error = EMSGSIZE;
1519187494Semax				break;
1520187494Semax			}
1521184610Salfred
1522187494Semax			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1523187494Semax			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
1524184610Salfred
1525187494Semax			switch (queue) {
1526187494Semax			case NGM_UBT_NODE_QUEUE_CMD:
1527187494Semax				q = &sc->sc_cmdq;
1528187494Semax				break;
1529184610Salfred
1530187494Semax			case NGM_UBT_NODE_QUEUE_ACL:
1531187494Semax				q = &sc->sc_aclq;
1532187494Semax				break;
1533184610Salfred
1534187494Semax			case NGM_UBT_NODE_QUEUE_SCO:
1535187494Semax				q = &sc->sc_scoq;
1536187494Semax				break;
1537184610Salfred
1538187494Semax			default:
1539187494Semax				error = EINVAL;
1540187494Semax				goto done;
1541187494Semax				/* NOT REACHED */
1542184610Salfred			}
1543187494Semax
1544187494Semax			q->maxlen = qlen;
1545184610Salfred			break;
1546184610Salfred
1547184610Salfred		case NGM_UBT_NODE_GET_QLEN:
1548184610Salfred			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1549184610Salfred				error = EMSGSIZE;
1550184610Salfred				break;
1551184610Salfred			}
1552187494Semax
1553184610Salfred			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1554187494Semax
1555184610Salfred			switch (queue) {
1556184610Salfred			case NGM_UBT_NODE_QUEUE_CMD:
1557184610Salfred				q = &sc->sc_cmdq;
1558184610Salfred				break;
1559184610Salfred
1560184610Salfred			case NGM_UBT_NODE_QUEUE_ACL:
1561184610Salfred				q = &sc->sc_aclq;
1562184610Salfred				break;
1563184610Salfred
1564184610Salfred			case NGM_UBT_NODE_QUEUE_SCO:
1565184610Salfred				q = &sc->sc_scoq;
1566184610Salfred				break;
1567184610Salfred
1568184610Salfred			default:
1569184610Salfred				error = EINVAL;
1570187494Semax				goto done;
1571187494Semax				/* NOT REACHED */
1572187494Semax			}
1573187494Semax
1574187494Semax			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
1575187494Semax				M_NOWAIT);
1576187494Semax			if (rsp == NULL) {
1577187494Semax				error = ENOMEM;
1578184610Salfred				break;
1579184610Salfred			}
1580184610Salfred
1581187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
1582187494Semax			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
1583184610Salfred			break;
1584184610Salfred
1585184610Salfred		case NGM_UBT_NODE_GET_STAT:
1586184610Salfred			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
1587184610Salfred			    M_NOWAIT);
1588187494Semax			if (rsp == NULL) {
1589184610Salfred				error = ENOMEM;
1590187494Semax				break;
1591184610Salfred			}
1592187494Semax
1593187494Semax			bcopy(&sc->sc_stat, rsp->data,
1594187494Semax				sizeof(ng_ubt_node_stat_ep));
1595184610Salfred			break;
1596184610Salfred
1597184610Salfred		case NGM_UBT_NODE_RESET_STAT:
1598187494Semax			UBT_STAT_RESET(sc);
1599184610Salfred			break;
1600184610Salfred
1601184610Salfred		default:
1602184610Salfred			error = EINVAL;
1603184610Salfred			break;
1604184610Salfred		}
1605184610Salfred		break;
1606184610Salfred
1607184610Salfred	default:
1608184610Salfred		error = EINVAL;
1609184610Salfred		break;
1610184610Salfred	}
1611187494Semaxdone:
1612184610Salfred	NG_RESPOND_MSG(error, node, item, rsp);
1613184610Salfred	NG_FREE_MSG(msg);
1614184610Salfred
1615184610Salfred	return (error);
1616187494Semax} /* ng_ubt_rcvmsg */
1617184610Salfred
1618184610Salfred/*
1619187494Semax * Process data.
1620187494Semax * Netgraph context.
1621184610Salfred */
1622184610Salfred
1623184610Salfredstatic int
1624184610Salfredng_ubt_rcvdata(hook_p hook, item_p item)
1625184610Salfred{
1626187494Semax	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1627187494Semax	struct mbuf		*m;
1628187494Semax	struct ng_bt_mbufq	*q;
1629187494Semax	int			action, error = 0;
1630184610Salfred
1631184610Salfred	if (hook != sc->sc_hook) {
1632184610Salfred		error = EINVAL;
1633184610Salfred		goto done;
1634184610Salfred	}
1635187494Semax
1636187494Semax	/* Deatch mbuf and get HCI frame type */
1637184610Salfred	NGI_GET_M(item, m);
1638184610Salfred
1639187494Semax	/*
1640187494Semax	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
1641187494Semax	 * 2 bytes connection handle and at least 1 byte of length.
1642187494Semax	 * Panic on data frame that has size smaller than 4 bytes (it
1643187494Semax	 * should not happen)
1644187494Semax	 */
1645187494Semax
1646187494Semax	if (m->m_pkthdr.len < 4)
1647187494Semax		panic("HCI frame size is too small! pktlen=%d\n",
1648187494Semax			m->m_pkthdr.len);
1649187494Semax
1650187494Semax	/* Process HCI frame */
1651184610Salfred	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
1652184610Salfred	case NG_HCI_CMD_PKT:
1653187494Semax		if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE)
1654187494Semax			panic("HCI command frame size is too big! " \
1655187494Semax				"buffer size=%zd, packet len=%d\n",
1656187494Semax				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
1657187494Semax
1658184610Salfred		q = &sc->sc_cmdq;
1659187494Semax		action = UBT_FLAG_T_START_CTRL;
1660184610Salfred		break;
1661184610Salfred
1662184610Salfred	case NG_HCI_ACL_DATA_PKT:
1663187494Semax		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
1664187494Semax			panic("ACL data frame size is too big! " \
1665187494Semax				"buffer size=%d, packet len=%d\n",
1666187494Semax				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
1667187494Semax
1668184610Salfred		q = &sc->sc_aclq;
1669187494Semax		action = UBT_FLAG_T_START_BULK;
1670184610Salfred		break;
1671184610Salfred
1672184610Salfred	case NG_HCI_SCO_DATA_PKT:
1673184610Salfred		q = &sc->sc_scoq;
1674187494Semax		action = 0;
1675184610Salfred		break;
1676184610Salfred
1677184610Salfred	default:
1678187494Semax		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
1679187494Semax			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
1680184610Salfred
1681184610Salfred		NG_FREE_M(m);
1682184610Salfred		error = EINVAL;
1683184610Salfred		goto done;
1684187494Semax		/* NOT REACHED */
1685184610Salfred	}
1686184610Salfred
1687187741Semax	UBT_NG_LOCK(sc);
1688184610Salfred	if (NG_BT_MBUFQ_FULL(q)) {
1689187494Semax		NG_BT_MBUFQ_DROP(q);
1690187741Semax		UBT_NG_UNLOCK(sc);
1691187494Semax
1692187494Semax		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
1693187494Semax			*mtod(m, uint8_t *), m->m_pkthdr.len);
1694187494Semax
1695184610Salfred		NG_FREE_M(m);
1696184610Salfred	} else {
1697187494Semax		/* Loose HCI packet type, enqueue mbuf and kick off task */
1698187494Semax		m_adj(m, sizeof(uint8_t));
1699184610Salfred		NG_BT_MBUFQ_ENQUEUE(q, m);
1700187494Semax		ubt_task_schedule(sc, action);
1701187741Semax		UBT_NG_UNLOCK(sc);
1702184610Salfred	}
1703184610Salfreddone:
1704184610Salfred	NG_FREE_ITEM(item);
1705184610Salfred
1706187494Semax	return (error);
1707187494Semax} /* ng_ubt_rcvdata */
1708187494Semax
1709187494Semax/****************************************************************************
1710187494Semax ****************************************************************************
1711187494Semax **                              Module
1712187494Semax ****************************************************************************
1713187494Semax ****************************************************************************/
1714187494Semax
1715187494Semax/*
1716187494Semax * Load/Unload the driver module
1717187494Semax */
1718187494Semax
1719187494Semaxstatic int
1720187494Semaxubt_modevent(module_t mod, int event, void *data)
1721187494Semax{
1722187494Semax	int	error;
1723187494Semax
1724187494Semax	switch (event) {
1725187494Semax	case MOD_LOAD:
1726187494Semax		error = ng_newtype(&typestruct);
1727187494Semax		if (error != 0)
1728187494Semax			printf("%s: Could not register Netgraph node type, " \
1729187494Semax				"error=%d\n", NG_UBT_NODE_TYPE, error);
1730187494Semax		break;
1731187494Semax
1732187494Semax	case MOD_UNLOAD:
1733187494Semax		error = ng_rmtype(&typestruct);
1734187494Semax		break;
1735187494Semax
1736187494Semax	default:
1737187494Semax		error = EOPNOTSUPP;
1738187494Semax		break;
1739184610Salfred	}
1740187494Semax
1741184610Salfred	return (error);
1742187494Semax} /* ubt_modevent */
1743187494Semax
1744187494Semaxstatic devclass_t	ubt_devclass;
1745187494Semax
1746187494Semaxstatic device_method_t	ubt_methods[] =
1747187494Semax{
1748187494Semax	DEVMETHOD(device_probe,	ubt_probe),
1749187494Semax	DEVMETHOD(device_attach, ubt_attach),
1750187494Semax	DEVMETHOD(device_detach, ubt_detach),
1751187494Semax	{ 0, 0 }
1752187494Semax};
1753187494Semax
1754187494Semaxstatic driver_t		ubt_driver =
1755187494Semax{
1756187494Semax	.name =	   "ubt",
1757187494Semax	.methods = ubt_methods,
1758187494Semax	.size =	   sizeof(struct ubt_softc),
1759187494Semax};
1760187494Semax
1761189275SthompsaDRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
1762187494SemaxMODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
1763187494SemaxMODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
1764187494SemaxMODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
1765188942SthompsaMODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
1766187494Semax
1767