1b06ebda0SMatthew Dillon /*
2b06ebda0SMatthew Dillon * ng_ubt.c
3b06ebda0SMatthew Dillon */
4b06ebda0SMatthew Dillon
5b06ebda0SMatthew Dillon /*-
6d2902f79SSascha Wildner * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7b06ebda0SMatthew Dillon * All rights reserved.
8b06ebda0SMatthew Dillon *
9b06ebda0SMatthew Dillon * Redistribution and use in source and binary forms, with or without
10b06ebda0SMatthew Dillon * modification, are permitted provided that the following conditions
11b06ebda0SMatthew Dillon * are met:
12b06ebda0SMatthew Dillon * 1. Redistributions of source code must retain the above copyright
13b06ebda0SMatthew Dillon * notice, this list of conditions and the following disclaimer.
14b06ebda0SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
15b06ebda0SMatthew Dillon * notice, this list of conditions and the following disclaimer in the
16b06ebda0SMatthew Dillon * documentation and/or other materials provided with the distribution.
17b06ebda0SMatthew Dillon *
18b06ebda0SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19b06ebda0SMatthew Dillon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20b06ebda0SMatthew Dillon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21b06ebda0SMatthew Dillon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22b06ebda0SMatthew Dillon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23b06ebda0SMatthew Dillon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24b06ebda0SMatthew Dillon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25b06ebda0SMatthew Dillon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26b06ebda0SMatthew Dillon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27b06ebda0SMatthew Dillon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28b06ebda0SMatthew Dillon * SUCH DAMAGE.
29b06ebda0SMatthew Dillon *
30b06ebda0SMatthew Dillon * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
31d2902f79SSascha Wildner * $FreeBSD: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c 260482 2014-01-09 15:31:44Z adrian $
32b06ebda0SMatthew Dillon */
33b06ebda0SMatthew Dillon
34b06ebda0SMatthew Dillon /*
35d2902f79SSascha Wildner * NOTE: ng_ubt2 driver has a split personality. On one side it is
36d2902f79SSascha Wildner * a USB device driver and on the other it is a Netgraph node. This
37d2902f79SSascha Wildner * driver will *NOT* create traditional /dev/ enties, only Netgraph
38d2902f79SSascha Wildner * node.
39d2902f79SSascha Wildner *
40d2902f79SSascha Wildner * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
41d2902f79SSascha Wildner *
42d2902f79SSascha Wildner * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
43d2902f79SSascha Wildner * by USB for any USB request going over device's interface #0 and #1,
44d2902f79SSascha Wildner * i.e. interrupt, control, bulk and isoc. transfers.
45d2902f79SSascha Wildner *
46d2902f79SSascha Wildner * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
47d2902f79SSascha Wildner * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
48d2902f79SSascha Wildner * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
49d2902f79SSascha Wildner * think of it as a spin lock.
50d2902f79SSascha Wildner *
51d2902f79SSascha Wildner * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
52d2902f79SSascha Wildner *
53d2902f79SSascha Wildner * 1) USB context. This is where all the USB related stuff happens. All
54d2902f79SSascha Wildner * callbacks run in this context. All callbacks are called (by USB) with
55d2902f79SSascha Wildner * appropriate interface lock held. It is (generally) allowed to grab
56d2902f79SSascha Wildner * any additional locks.
57d2902f79SSascha Wildner *
58d2902f79SSascha Wildner * 2) Netgraph context. This is where all the Netgraph related stuff happens.
59d2902f79SSascha Wildner * Since we mark node as WRITER, the Netgraph node will be "locked" (from
60d2902f79SSascha Wildner * Netgraph point of view). Any variable that is only modified from the
61d2902f79SSascha Wildner * Netgraph context does not require any additonal locking. It is generally
62d2902f79SSascha Wildner * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
63d2902f79SSascha Wildner * grab any lock in the Netgraph context that could cause de-scheduling of
64d2902f79SSascha Wildner * the Netgraph thread for significant amount of time. In fact, the only
65d2902f79SSascha Wildner * lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
66d2902f79SSascha Wildner * Also make sure that any code that is called from the Netgraph context
67d2902f79SSascha Wildner * follows the rule above.
68d2902f79SSascha Wildner *
69d2902f79SSascha Wildner * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
70d2902f79SSascha Wildner * NOT allowed to grab any lock that could cause de-scheduling in the
71d2902f79SSascha Wildner * Netgraph context, and, USB requires us to grab interface lock before
72d2902f79SSascha Wildner * doing things with transfers, it is safer to transition from the Netgraph
73d2902f79SSascha Wildner * context to the Taskqueue context before we can call into USB subsystem.
74d2902f79SSascha Wildner *
75d2902f79SSascha Wildner * So, to put everything together, the rules are as follows.
76d2902f79SSascha Wildner * It is OK to call from the USB context or the Taskqueue context into
77d2902f79SSascha Wildner * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
78d2902f79SSascha Wildner * it is allowed to call into the Netgraph context with locks held.
79d2902f79SSascha Wildner * Is it *NOT* OK to call from the Netgraph context into the USB context,
80d2902f79SSascha Wildner * because USB requires us to grab interface locks, and, it is safer to
81d2902f79SSascha Wildner * avoid it. So, to make things safer we set task flags to indicate which
82d2902f79SSascha Wildner * actions we want to perform and schedule ubt_task which would run in the
83d2902f79SSascha Wildner * Taskqueue context.
84d2902f79SSascha Wildner * Is is OK to call from the Taskqueue context into the USB context,
85d2902f79SSascha Wildner * and, ubt_task does just that (i.e. grabs appropriate interface locks
86d2902f79SSascha Wildner * before calling into USB).
87d2902f79SSascha Wildner * Access to the outgoing queues, task flags and hook pointer is
88d2902f79SSascha Wildner * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
89d2902f79SSascha Wildner * sc_ng_mtx should really be a spin lock (and it is very likely to an
90d2902f79SSascha Wildner * equivalent of spin lock due to adaptive nature of FreeBSD mutexes).
91d2902f79SSascha Wildner * All USB callbacks accept softc pointer as a private data. USB ensures
92d2902f79SSascha Wildner * that this pointer is valid.
93b06ebda0SMatthew Dillon */
94b06ebda0SMatthew Dillon
95d2902f79SSascha Wildner #include <sys/stdint.h>
96d2902f79SSascha Wildner #include <sys/param.h>
97d2902f79SSascha Wildner #include <sys/queue.h>
98d2902f79SSascha Wildner #include <sys/types.h>
99d2902f79SSascha Wildner #include <sys/systm.h>
100d2902f79SSascha Wildner #include <sys/kernel.h>
101d2902f79SSascha Wildner #include <sys/bus.h>
102d2902f79SSascha Wildner #include <sys/module.h>
103d2902f79SSascha Wildner #include <sys/lock.h>
104d2902f79SSascha Wildner #include <sys/condvar.h>
105d2902f79SSascha Wildner #include <sys/sysctl.h>
106d2902f79SSascha Wildner #include <sys/unistd.h>
107d2902f79SSascha Wildner #include <sys/callout.h>
108d2902f79SSascha Wildner #include <sys/malloc.h>
109*2b3f93eaSMatthew Dillon #include <sys/caps.h>
110d2902f79SSascha Wildner
111d2902f79SSascha Wildner #include "usbdevs.h"
112d2902f79SSascha Wildner #include <bus/u4b/usb.h>
113d2902f79SSascha Wildner #include <bus/u4b/usbdi.h>
114d2902f79SSascha Wildner #include <bus/u4b/usbdi_util.h>
115d2902f79SSascha Wildner
116d2902f79SSascha Wildner #define USB_DEBUG_VAR usb_debug
117d2902f79SSascha Wildner #include <bus/u4b/usb_debug.h>
118d2902f79SSascha Wildner #include <bus/u4b/usb_busdma.h>
119d2902f79SSascha Wildner
120d2902f79SSascha Wildner #include <sys/mbuf.h>
121d2902f79SSascha Wildner #include <sys/taskqueue.h>
122d2902f79SSascha Wildner
123d2902f79SSascha Wildner #include <netgraph7/ng_message.h>
124d2902f79SSascha Wildner #include <netgraph7/netgraph.h>
125d2902f79SSascha Wildner #include <netgraph7/ng_parse.h>
126d2902f79SSascha Wildner #include <netgraph7/bluetooth/include/ng_bluetooth.h>
127d2902f79SSascha Wildner #include <netgraph7/bluetooth/include/ng_hci.h>
128d2902f79SSascha Wildner #include <netgraph7/bluetooth/include/ng_ubt.h>
129d2902f79SSascha Wildner #include <netgraph7/bluetooth/drivers/ubt/ng_ubt_var.h>
130d2902f79SSascha Wildner
131d2902f79SSascha Wildner static int ubt_modevent(module_t, int, void *);
132d2902f79SSascha Wildner static device_probe_t ubt_probe;
133b06ebda0SMatthew Dillon static device_attach_t ubt_attach;
134b06ebda0SMatthew Dillon static device_detach_t ubt_detach;
135b06ebda0SMatthew Dillon
136d2902f79SSascha Wildner static void ubt_task_schedule(ubt_softc_p, int);
137d2902f79SSascha Wildner static task_fn_t ubt_task;
138b06ebda0SMatthew Dillon
139d2902f79SSascha Wildner #define ubt_xfer_start(sc, i) usbd_transfer_start((sc)->sc_xfer[(i)])
140b06ebda0SMatthew Dillon
141d2902f79SSascha Wildner /* Netgraph methods */
142b06ebda0SMatthew Dillon static ng_constructor_t ng_ubt_constructor;
143b06ebda0SMatthew Dillon static ng_shutdown_t ng_ubt_shutdown;
144b06ebda0SMatthew Dillon static ng_newhook_t ng_ubt_newhook;
145b06ebda0SMatthew Dillon static ng_connect_t ng_ubt_connect;
146b06ebda0SMatthew Dillon static ng_disconnect_t ng_ubt_disconnect;
147b06ebda0SMatthew Dillon static ng_rcvmsg_t ng_ubt_rcvmsg;
148b06ebda0SMatthew Dillon static ng_rcvdata_t ng_ubt_rcvdata;
149b06ebda0SMatthew Dillon
150b06ebda0SMatthew Dillon /* Queue length */
151b06ebda0SMatthew Dillon static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] =
152b06ebda0SMatthew Dillon {
153b06ebda0SMatthew Dillon { "queue", &ng_parse_int32_type, },
154b06ebda0SMatthew Dillon { "qlen", &ng_parse_int32_type, },
155b06ebda0SMatthew Dillon { NULL, }
156b06ebda0SMatthew Dillon };
157d2902f79SSascha Wildner static const struct ng_parse_type ng_ubt_node_qlen_type =
158d2902f79SSascha Wildner {
159b06ebda0SMatthew Dillon &ng_parse_struct_type,
160b06ebda0SMatthew Dillon &ng_ubt_node_qlen_type_fields
161b06ebda0SMatthew Dillon };
162b06ebda0SMatthew Dillon
163b06ebda0SMatthew Dillon /* Stat info */
164b06ebda0SMatthew Dillon static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] =
165b06ebda0SMatthew Dillon {
166b06ebda0SMatthew Dillon { "pckts_recv", &ng_parse_uint32_type, },
167b06ebda0SMatthew Dillon { "bytes_recv", &ng_parse_uint32_type, },
168b06ebda0SMatthew Dillon { "pckts_sent", &ng_parse_uint32_type, },
169b06ebda0SMatthew Dillon { "bytes_sent", &ng_parse_uint32_type, },
170b06ebda0SMatthew Dillon { "oerrors", &ng_parse_uint32_type, },
171b06ebda0SMatthew Dillon { "ierrors", &ng_parse_uint32_type, },
172b06ebda0SMatthew Dillon { NULL, }
173b06ebda0SMatthew Dillon };
174d2902f79SSascha Wildner static const struct ng_parse_type ng_ubt_node_stat_type =
175d2902f79SSascha Wildner {
176b06ebda0SMatthew Dillon &ng_parse_struct_type,
177b06ebda0SMatthew Dillon &ng_ubt_node_stat_type_fields
178b06ebda0SMatthew Dillon };
179b06ebda0SMatthew Dillon
180b06ebda0SMatthew Dillon /* Netgraph node command list */
181d2902f79SSascha Wildner static const struct ng_cmdlist ng_ubt_cmdlist[] =
182d2902f79SSascha Wildner {
183b06ebda0SMatthew Dillon {
184b06ebda0SMatthew Dillon NGM_UBT_COOKIE,
185b06ebda0SMatthew Dillon NGM_UBT_NODE_SET_DEBUG,
186b06ebda0SMatthew Dillon "set_debug",
187b06ebda0SMatthew Dillon &ng_parse_uint16_type,
188b06ebda0SMatthew Dillon NULL
189b06ebda0SMatthew Dillon },
190b06ebda0SMatthew Dillon {
191b06ebda0SMatthew Dillon NGM_UBT_COOKIE,
192b06ebda0SMatthew Dillon NGM_UBT_NODE_GET_DEBUG,
193b06ebda0SMatthew Dillon "get_debug",
194b06ebda0SMatthew Dillon NULL,
195b06ebda0SMatthew Dillon &ng_parse_uint16_type
196b06ebda0SMatthew Dillon },
197b06ebda0SMatthew Dillon {
198b06ebda0SMatthew Dillon NGM_UBT_COOKIE,
199b06ebda0SMatthew Dillon NGM_UBT_NODE_SET_QLEN,
200b06ebda0SMatthew Dillon "set_qlen",
201b06ebda0SMatthew Dillon &ng_ubt_node_qlen_type,
202b06ebda0SMatthew Dillon NULL
203b06ebda0SMatthew Dillon },
204b06ebda0SMatthew Dillon {
205b06ebda0SMatthew Dillon NGM_UBT_COOKIE,
206b06ebda0SMatthew Dillon NGM_UBT_NODE_GET_QLEN,
207b06ebda0SMatthew Dillon "get_qlen",
208b06ebda0SMatthew Dillon &ng_ubt_node_qlen_type,
209b06ebda0SMatthew Dillon &ng_ubt_node_qlen_type
210b06ebda0SMatthew Dillon },
211b06ebda0SMatthew Dillon {
212b06ebda0SMatthew Dillon NGM_UBT_COOKIE,
213b06ebda0SMatthew Dillon NGM_UBT_NODE_GET_STAT,
214b06ebda0SMatthew Dillon "get_stat",
215b06ebda0SMatthew Dillon NULL,
216b06ebda0SMatthew Dillon &ng_ubt_node_stat_type
217b06ebda0SMatthew Dillon },
218b06ebda0SMatthew Dillon {
219b06ebda0SMatthew Dillon NGM_UBT_COOKIE,
220b06ebda0SMatthew Dillon NGM_UBT_NODE_RESET_STAT,
221b06ebda0SMatthew Dillon "reset_stat",
222b06ebda0SMatthew Dillon NULL,
223b06ebda0SMatthew Dillon NULL
224b06ebda0SMatthew Dillon },
225b06ebda0SMatthew Dillon { 0, }
226b06ebda0SMatthew Dillon };
227b06ebda0SMatthew Dillon
228b06ebda0SMatthew Dillon /* Netgraph node type */
229d2902f79SSascha Wildner static struct ng_type typestruct =
230d2902f79SSascha Wildner {
231b06ebda0SMatthew Dillon .version = NG_ABI_VERSION,
232b06ebda0SMatthew Dillon .name = NG_UBT_NODE_TYPE,
233b06ebda0SMatthew Dillon .constructor = ng_ubt_constructor,
234b06ebda0SMatthew Dillon .rcvmsg = ng_ubt_rcvmsg,
235b06ebda0SMatthew Dillon .shutdown = ng_ubt_shutdown,
236b06ebda0SMatthew Dillon .newhook = ng_ubt_newhook,
237b06ebda0SMatthew Dillon .connect = ng_ubt_connect,
238b06ebda0SMatthew Dillon .rcvdata = ng_ubt_rcvdata,
239b06ebda0SMatthew Dillon .disconnect = ng_ubt_disconnect,
240b06ebda0SMatthew Dillon .cmdlist = ng_ubt_cmdlist
241b06ebda0SMatthew Dillon };
242b06ebda0SMatthew Dillon
243b06ebda0SMatthew Dillon /****************************************************************************
244b06ebda0SMatthew Dillon ****************************************************************************
245b06ebda0SMatthew Dillon ** USB specific
246b06ebda0SMatthew Dillon ****************************************************************************
247b06ebda0SMatthew Dillon ****************************************************************************/
248b06ebda0SMatthew Dillon
249d2902f79SSascha Wildner /* USB methods */
250d2902f79SSascha Wildner static usb_callback_t ubt_ctrl_write_callback;
251d2902f79SSascha Wildner static usb_callback_t ubt_intr_read_callback;
252d2902f79SSascha Wildner static usb_callback_t ubt_bulk_read_callback;
253d2902f79SSascha Wildner static usb_callback_t ubt_bulk_write_callback;
254d2902f79SSascha Wildner static usb_callback_t ubt_isoc_read_callback;
255d2902f79SSascha Wildner static usb_callback_t ubt_isoc_write_callback;
256b06ebda0SMatthew Dillon
257d2902f79SSascha Wildner static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
258d2902f79SSascha Wildner static int ubt_isoc_read_one_frame(struct usb_xfer *, int);
259b06ebda0SMatthew Dillon
260b06ebda0SMatthew Dillon /*
261d2902f79SSascha Wildner * USB config
262d2902f79SSascha Wildner *
263d2902f79SSascha Wildner * The following desribes usb transfers that could be submitted on USB device.
264d2902f79SSascha Wildner *
265d2902f79SSascha Wildner * Interface 0 on the USB device must present the following endpoints
266d2902f79SSascha Wildner * 1) Interrupt endpoint to receive HCI events
267d2902f79SSascha Wildner * 2) Bulk IN endpoint to receive ACL data
268d2902f79SSascha Wildner * 3) Bulk OUT endpoint to send ACL data
269d2902f79SSascha Wildner *
270d2902f79SSascha Wildner * Interface 1 on the USB device must present the following endpoints
271d2902f79SSascha Wildner * 1) Isochronous IN endpoint to receive SCO data
272d2902f79SSascha Wildner * 2) Isochronous OUT endpoint to send SCO data
273b06ebda0SMatthew Dillon */
274b06ebda0SMatthew Dillon
275d2902f79SSascha Wildner static const struct usb_config ubt_config[UBT_N_TRANSFER] =
276b06ebda0SMatthew Dillon {
277b06ebda0SMatthew Dillon /*
278d2902f79SSascha Wildner * Interface #0
279d2902f79SSascha Wildner */
280d2902f79SSascha Wildner
281d2902f79SSascha Wildner /* Outgoing bulk transfer - ACL packets */
282d2902f79SSascha Wildner [UBT_IF_0_BULK_DT_WR] = {
283d2902f79SSascha Wildner .type = UE_BULK,
284d2902f79SSascha Wildner .endpoint = UE_ADDR_ANY,
285d2902f79SSascha Wildner .direction = UE_DIR_OUT,
286d2902f79SSascha Wildner .if_index = 0,
287d2902f79SSascha Wildner .bufsize = UBT_BULK_WRITE_BUFFER_SIZE,
288d2902f79SSascha Wildner .flags = { .pipe_bof = 1, .force_short_xfer = 1, },
289d2902f79SSascha Wildner .callback = &ubt_bulk_write_callback,
290d2902f79SSascha Wildner },
291d2902f79SSascha Wildner /* Incoming bulk transfer - ACL packets */
292d2902f79SSascha Wildner [UBT_IF_0_BULK_DT_RD] = {
293d2902f79SSascha Wildner .type = UE_BULK,
294d2902f79SSascha Wildner .endpoint = UE_ADDR_ANY,
295d2902f79SSascha Wildner .direction = UE_DIR_IN,
296d2902f79SSascha Wildner .if_index = 0,
297d2902f79SSascha Wildner .bufsize = UBT_BULK_READ_BUFFER_SIZE,
298d2902f79SSascha Wildner .flags = { .pipe_bof = 1, .short_xfer_ok = 1, },
299d2902f79SSascha Wildner .callback = &ubt_bulk_read_callback,
300d2902f79SSascha Wildner },
301d2902f79SSascha Wildner /* Incoming interrupt transfer - HCI events */
302d2902f79SSascha Wildner [UBT_IF_0_INTR_DT_RD] = {
303d2902f79SSascha Wildner .type = UE_INTERRUPT,
304d2902f79SSascha Wildner .endpoint = UE_ADDR_ANY,
305d2902f79SSascha Wildner .direction = UE_DIR_IN,
306d2902f79SSascha Wildner .if_index = 0,
307d2902f79SSascha Wildner .flags = { .pipe_bof = 1, .short_xfer_ok = 1, },
308d2902f79SSascha Wildner .bufsize = UBT_INTR_BUFFER_SIZE,
309d2902f79SSascha Wildner .callback = &ubt_intr_read_callback,
310d2902f79SSascha Wildner },
311d2902f79SSascha Wildner /* Outgoing control transfer - HCI commands */
312d2902f79SSascha Wildner [UBT_IF_0_CTRL_DT_WR] = {
313d2902f79SSascha Wildner .type = UE_CONTROL,
314d2902f79SSascha Wildner .endpoint = 0x00, /* control pipe */
315d2902f79SSascha Wildner .direction = UE_DIR_ANY,
316d2902f79SSascha Wildner .if_index = 0,
317d2902f79SSascha Wildner .bufsize = UBT_CTRL_BUFFER_SIZE,
318d2902f79SSascha Wildner .callback = &ubt_ctrl_write_callback,
319d2902f79SSascha Wildner .timeout = 5000, /* 5 seconds */
320d2902f79SSascha Wildner },
321d2902f79SSascha Wildner
322d2902f79SSascha Wildner /*
323d2902f79SSascha Wildner * Interface #1
324d2902f79SSascha Wildner */
325d2902f79SSascha Wildner
326d2902f79SSascha Wildner /* Incoming isochronous transfer #1 - SCO packets */
327d2902f79SSascha Wildner [UBT_IF_1_ISOC_DT_RD1] = {
328d2902f79SSascha Wildner .type = UE_ISOCHRONOUS,
329d2902f79SSascha Wildner .endpoint = UE_ADDR_ANY,
330d2902f79SSascha Wildner .direction = UE_DIR_IN,
331d2902f79SSascha Wildner .if_index = 1,
332d2902f79SSascha Wildner .bufsize = 0, /* use "wMaxPacketSize * frames" */
333d2902f79SSascha Wildner .frames = UBT_ISOC_NFRAMES,
334d2902f79SSascha Wildner .flags = { .short_xfer_ok = 1, },
335d2902f79SSascha Wildner .callback = &ubt_isoc_read_callback,
336d2902f79SSascha Wildner },
337d2902f79SSascha Wildner /* Incoming isochronous transfer #2 - SCO packets */
338d2902f79SSascha Wildner [UBT_IF_1_ISOC_DT_RD2] = {
339d2902f79SSascha Wildner .type = UE_ISOCHRONOUS,
340d2902f79SSascha Wildner .endpoint = UE_ADDR_ANY,
341d2902f79SSascha Wildner .direction = UE_DIR_IN,
342d2902f79SSascha Wildner .if_index = 1,
343d2902f79SSascha Wildner .bufsize = 0, /* use "wMaxPacketSize * frames" */
344d2902f79SSascha Wildner .frames = UBT_ISOC_NFRAMES,
345d2902f79SSascha Wildner .flags = { .short_xfer_ok = 1, },
346d2902f79SSascha Wildner .callback = &ubt_isoc_read_callback,
347d2902f79SSascha Wildner },
348d2902f79SSascha Wildner /* Outgoing isochronous transfer #1 - SCO packets */
349d2902f79SSascha Wildner [UBT_IF_1_ISOC_DT_WR1] = {
350d2902f79SSascha Wildner .type = UE_ISOCHRONOUS,
351d2902f79SSascha Wildner .endpoint = UE_ADDR_ANY,
352d2902f79SSascha Wildner .direction = UE_DIR_OUT,
353d2902f79SSascha Wildner .if_index = 1,
354d2902f79SSascha Wildner .bufsize = 0, /* use "wMaxPacketSize * frames" */
355d2902f79SSascha Wildner .frames = UBT_ISOC_NFRAMES,
356d2902f79SSascha Wildner .flags = { .short_xfer_ok = 1, },
357d2902f79SSascha Wildner .callback = &ubt_isoc_write_callback,
358d2902f79SSascha Wildner },
359d2902f79SSascha Wildner /* Outgoing isochronous transfer #2 - SCO packets */
360d2902f79SSascha Wildner [UBT_IF_1_ISOC_DT_WR2] = {
361d2902f79SSascha Wildner .type = UE_ISOCHRONOUS,
362d2902f79SSascha Wildner .endpoint = UE_ADDR_ANY,
363d2902f79SSascha Wildner .direction = UE_DIR_OUT,
364d2902f79SSascha Wildner .if_index = 1,
365d2902f79SSascha Wildner .bufsize = 0, /* use "wMaxPacketSize * frames" */
366d2902f79SSascha Wildner .frames = UBT_ISOC_NFRAMES,
367d2902f79SSascha Wildner .flags = { .short_xfer_ok = 1, },
368d2902f79SSascha Wildner .callback = &ubt_isoc_write_callback,
369d2902f79SSascha Wildner },
370d2902f79SSascha Wildner };
371d2902f79SSascha Wildner
372d2902f79SSascha Wildner /*
373b06ebda0SMatthew Dillon * If for some reason device should not be attached then put
374b06ebda0SMatthew Dillon * VendorID/ProductID pair into the list below. The format is
375b06ebda0SMatthew Dillon * as follows:
376b06ebda0SMatthew Dillon *
377d2902f79SSascha Wildner * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
378b06ebda0SMatthew Dillon *
379b06ebda0SMatthew Dillon * where VENDOR_ID and PRODUCT_ID are hex numbers.
380b06ebda0SMatthew Dillon */
381b06ebda0SMatthew Dillon
382d2902f79SSascha Wildner static const STRUCT_USB_HOST_ID ubt_ignore_devs[] =
383d2902f79SSascha Wildner {
384d2902f79SSascha Wildner /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
385d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
386d2902f79SSascha Wildner
387d2902f79SSascha Wildner /* Atheros 3011 with sflash firmware */
388d2902f79SSascha Wildner { USB_VPI(0x0cf3, 0x3002, 0) },
389d2902f79SSascha Wildner { USB_VPI(0x0cf3, 0xe019, 0) },
390d2902f79SSascha Wildner { USB_VPI(0x13d3, 0x3304, 0) },
391d2902f79SSascha Wildner { USB_VPI(0x0930, 0x0215, 0) },
392d2902f79SSascha Wildner { USB_VPI(0x0489, 0xe03d, 0) },
393d2902f79SSascha Wildner { USB_VPI(0x0489, 0xe027, 0) },
394d2902f79SSascha Wildner
395d2902f79SSascha Wildner /* Atheros AR9285 Malbec with sflash firmware */
396d2902f79SSascha Wildner { USB_VPI(0x03f0, 0x311d, 0) },
397d2902f79SSascha Wildner
398d2902f79SSascha Wildner /* Atheros 3012 with sflash firmware */
399d2902f79SSascha Wildner { USB_VPI(0x0cf3, 0x3004, 0), USB_DEV_BCD_LTEQ(1) },
400d2902f79SSascha Wildner { USB_VPI(0x0cf3, 0x311d, 0), USB_DEV_BCD_LTEQ(1) },
401d2902f79SSascha Wildner { USB_VPI(0x13d3, 0x3375, 0), USB_DEV_BCD_LTEQ(1) },
402d2902f79SSascha Wildner { USB_VPI(0x04ca, 0x3005, 0), USB_DEV_BCD_LTEQ(1) },
403d2902f79SSascha Wildner { USB_VPI(0x04ca, 0x3006, 0), USB_DEV_BCD_LTEQ(1) },
404d2902f79SSascha Wildner { USB_VPI(0x04ca, 0x3008, 0), USB_DEV_BCD_LTEQ(1) },
405d2902f79SSascha Wildner { USB_VPI(0x13d3, 0x3362, 0), USB_DEV_BCD_LTEQ(1) },
406d2902f79SSascha Wildner { USB_VPI(0x0cf3, 0xe004, 0), USB_DEV_BCD_LTEQ(1) },
407d2902f79SSascha Wildner { USB_VPI(0x0930, 0x0219, 0), USB_DEV_BCD_LTEQ(1) },
408d2902f79SSascha Wildner { USB_VPI(0x0489, 0xe057, 0), USB_DEV_BCD_LTEQ(1) },
409d2902f79SSascha Wildner { USB_VPI(0x13d3, 0x3393, 0), USB_DEV_BCD_LTEQ(1) },
410d2902f79SSascha Wildner { USB_VPI(0x0489, 0xe04e, 0), USB_DEV_BCD_LTEQ(1) },
411d2902f79SSascha Wildner { USB_VPI(0x0489, 0xe056, 0), USB_DEV_BCD_LTEQ(1) },
412d2902f79SSascha Wildner
413d2902f79SSascha Wildner /* Atheros AR5BBU12 with sflash firmware */
414d2902f79SSascha Wildner { USB_VPI(0x0489, 0xe02c, 0), USB_DEV_BCD_LTEQ(1) },
415d2902f79SSascha Wildner
416d2902f79SSascha Wildner /* Atheros AR5BBU12 with sflash firmware */
417d2902f79SSascha Wildner { USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) },
418d2902f79SSascha Wildner { USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) },
419d2902f79SSascha Wildner };
420d2902f79SSascha Wildner
421d2902f79SSascha Wildner /* List of supported bluetooth devices */
422d2902f79SSascha Wildner static const STRUCT_USB_HOST_ID ubt_devs[] =
423d2902f79SSascha Wildner {
424d2902f79SSascha Wildner /* Generic Bluetooth class devices */
425d2902f79SSascha Wildner { USB_IFACE_CLASS(UDCLASS_WIRELESS),
426d2902f79SSascha Wildner USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
427d2902f79SSascha Wildner USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
428d2902f79SSascha Wildner
429d2902f79SSascha Wildner /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
430d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
431d2902f79SSascha Wildner
432d2902f79SSascha Wildner /* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
433d2902f79SSascha Wildner { USB_VENDOR(USB_VENDOR_BROADCOM),
434d2902f79SSascha Wildner USB_IFACE_CLASS(UICLASS_VENDOR),
435d2902f79SSascha Wildner USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
436d2902f79SSascha Wildner USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
437d2902f79SSascha Wildner
438d2902f79SSascha Wildner /* Apple-specific (Broadcom) devices */
439d2902f79SSascha Wildner { USB_VENDOR(USB_VENDOR_APPLE),
440d2902f79SSascha Wildner USB_IFACE_CLASS(UICLASS_VENDOR),
441d2902f79SSascha Wildner USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
442d2902f79SSascha Wildner USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
443d2902f79SSascha Wildner
444d2902f79SSascha Wildner /* Foxconn - Hon Hai */
445d2902f79SSascha Wildner { USB_VENDOR(USB_VENDOR_FOXCONN),
446d2902f79SSascha Wildner USB_IFACE_CLASS(UICLASS_VENDOR),
447d2902f79SSascha Wildner USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
448d2902f79SSascha Wildner USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
449d2902f79SSascha Wildner
450d2902f79SSascha Wildner /* MediaTek MT76x0E */
451d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },
452d2902f79SSascha Wildner
453d2902f79SSascha Wildner /* Broadcom SoftSailing reporting vendor specific */
454d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },
455d2902f79SSascha Wildner
456d2902f79SSascha Wildner /* Apple MacBookPro 7,1 */
457d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },
458d2902f79SSascha Wildner
459d2902f79SSascha Wildner /* Apple iMac11,1 */
460d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },
461d2902f79SSascha Wildner
462d2902f79SSascha Wildner /* Apple MacBookPro6,2 */
463d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },
464d2902f79SSascha Wildner
465d2902f79SSascha Wildner /* Apple MacBookAir3,1, MacBookAir3,2 */
466d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },
467d2902f79SSascha Wildner
468d2902f79SSascha Wildner /* Apple MacBookAir4,1 */
469d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },
470d2902f79SSascha Wildner
471d2902f79SSascha Wildner /* MacBookAir6,1 */
472d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },
473d2902f79SSascha Wildner
474d2902f79SSascha Wildner /* Apple MacBookPro8,2 */
475d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },
476d2902f79SSascha Wildner
477d2902f79SSascha Wildner /* Apple MacMini5,1 */
478d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },
479d2902f79SSascha Wildner
480d2902f79SSascha Wildner /* Bluetooth Ultraport Module from IBM */
481d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },
482d2902f79SSascha Wildner
483d2902f79SSascha Wildner /* ALPS Modules with non-standard ID */
484d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },
485d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },
486d2902f79SSascha Wildner
487d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },
488d2902f79SSascha Wildner
489d2902f79SSascha Wildner /* Canyon CN-BTU1 with HID interfaces */
490d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },
491d2902f79SSascha Wildner
492d2902f79SSascha Wildner /* Broadcom BCM20702A0 */
493d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },
494d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },
495d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },
496d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },
497d2902f79SSascha Wildner { USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
498b06ebda0SMatthew Dillon };
499b06ebda0SMatthew Dillon
500b06ebda0SMatthew Dillon /*
501d2902f79SSascha Wildner * Probe for a USB Bluetooth device.
502d2902f79SSascha Wildner * USB context.
503b06ebda0SMatthew Dillon */
504b06ebda0SMatthew Dillon
505b06ebda0SMatthew Dillon static int
ubt_probe(device_t dev)506d2902f79SSascha Wildner ubt_probe(device_t dev)
507b06ebda0SMatthew Dillon {
508d2902f79SSascha Wildner struct usb_attach_arg *uaa = device_get_ivars(dev);
509d2902f79SSascha Wildner int error;
510b06ebda0SMatthew Dillon
511d2902f79SSascha Wildner if (uaa->usb_mode != USB_MODE_HOST)
512d2902f79SSascha Wildner return (ENXIO);
513d2902f79SSascha Wildner
514d2902f79SSascha Wildner if (uaa->info.bIfaceIndex != 0)
515d2902f79SSascha Wildner return (ENXIO);
516d2902f79SSascha Wildner
517d2902f79SSascha Wildner if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
518d2902f79SSascha Wildner sizeof(ubt_ignore_devs), uaa) == 0)
519d2902f79SSascha Wildner return (ENXIO);
520d2902f79SSascha Wildner
521d2902f79SSascha Wildner error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
522d2902f79SSascha Wildner if (error == 0)
523d2902f79SSascha Wildner return (BUS_PROBE_GENERIC);
524d2902f79SSascha Wildner return (error);
525d2902f79SSascha Wildner } /* ubt_probe */
526d2902f79SSascha Wildner
527d2902f79SSascha Wildner /*
528d2902f79SSascha Wildner * Attach the device.
529d2902f79SSascha Wildner * USB context.
530d2902f79SSascha Wildner */
531d2902f79SSascha Wildner
532d2902f79SSascha Wildner static int
ubt_attach(device_t dev)533d2902f79SSascha Wildner ubt_attach(device_t dev)
534d2902f79SSascha Wildner {
535d2902f79SSascha Wildner struct usb_attach_arg *uaa = device_get_ivars(dev);
536d2902f79SSascha Wildner struct ubt_softc *sc = device_get_softc(dev);
537d2902f79SSascha Wildner struct usb_endpoint_descriptor *ed;
538d2902f79SSascha Wildner struct usb_interface_descriptor *id;
539d2902f79SSascha Wildner struct usb_interface *iface;
540d2902f79SSascha Wildner uint16_t wMaxPacketSize;
541d2902f79SSascha Wildner uint8_t alt_index, i, j;
542d2902f79SSascha Wildner uint8_t iface_index[2] = { 0, 1 };
543d2902f79SSascha Wildner
544d2902f79SSascha Wildner device_set_usb_desc(dev);
545d2902f79SSascha Wildner
546d2902f79SSascha Wildner sc->sc_dev = dev;
547d2902f79SSascha Wildner sc->sc_debug = NG_UBT_WARN_LEVEL;
548d2902f79SSascha Wildner
549d2902f79SSascha Wildner /*
550d2902f79SSascha Wildner * Create Netgraph node
551d2902f79SSascha Wildner */
552d2902f79SSascha Wildner
553d2902f79SSascha Wildner if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
554d2902f79SSascha Wildner UBT_ALERT(sc, "could not create Netgraph node\n");
555d2902f79SSascha Wildner return (ENXIO);
556d2902f79SSascha Wildner }
557d2902f79SSascha Wildner
558d2902f79SSascha Wildner /* Name Netgraph node */
559d2902f79SSascha Wildner if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
560d2902f79SSascha Wildner UBT_ALERT(sc, "could not name Netgraph node\n");
561d2902f79SSascha Wildner NG_NODE_UNREF(sc->sc_node);
562d2902f79SSascha Wildner return (ENXIO);
563d2902f79SSascha Wildner }
564d2902f79SSascha Wildner NG_NODE_SET_PRIVATE(sc->sc_node, sc);
565d2902f79SSascha Wildner NG_NODE_FORCE_WRITER(sc->sc_node);
566b06ebda0SMatthew Dillon
567b06ebda0SMatthew Dillon /*
568b06ebda0SMatthew Dillon * Initialize device softc structure
569b06ebda0SMatthew Dillon */
570b06ebda0SMatthew Dillon
571d2902f79SSascha Wildner /* initialize locks */
572d2902f79SSascha Wildner lockinit(&sc->sc_ng_lock, "ubt ng", 0, 0);
573d2902f79SSascha Wildner lockinit(&sc->sc_if_lock, "ubt if", 0, LK_CANRECURSE);
574b06ebda0SMatthew Dillon
575d2902f79SSascha Wildner /* initialize packet queues */
576b06ebda0SMatthew Dillon NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
577b06ebda0SMatthew Dillon NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
578b06ebda0SMatthew Dillon NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
579b06ebda0SMatthew Dillon
580d2902f79SSascha Wildner /* initialize glue task */
581d2902f79SSascha Wildner TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
582b06ebda0SMatthew Dillon
583b06ebda0SMatthew Dillon /*
584d2902f79SSascha Wildner * Configure Bluetooth USB device. Discover all required USB
585d2902f79SSascha Wildner * interfaces and endpoints.
586b06ebda0SMatthew Dillon *
587b06ebda0SMatthew Dillon * USB device must present two interfaces:
588b06ebda0SMatthew Dillon * 1) Interface 0 that has 3 endpoints
589b06ebda0SMatthew Dillon * 1) Interrupt endpoint to receive HCI events
590b06ebda0SMatthew Dillon * 2) Bulk IN endpoint to receive ACL data
591b06ebda0SMatthew Dillon * 3) Bulk OUT endpoint to send ACL data
592b06ebda0SMatthew Dillon *
593b06ebda0SMatthew Dillon * 2) Interface 1 then has 2 endpoints
594b06ebda0SMatthew Dillon * 1) Isochronous IN endpoint to receive SCO data
595b06ebda0SMatthew Dillon * 2) Isochronous OUT endpoint to send SCO data
596b06ebda0SMatthew Dillon *
597b06ebda0SMatthew Dillon * Interface 1 (with isochronous endpoints) has several alternate
598b06ebda0SMatthew Dillon * configurations with different packet size.
599b06ebda0SMatthew Dillon */
600b06ebda0SMatthew Dillon
601b06ebda0SMatthew Dillon /*
602d2902f79SSascha Wildner * For interface #1 search alternate settings, and find
603d2902f79SSascha Wildner * the descriptor with the largest wMaxPacketSize
604b06ebda0SMatthew Dillon */
605b06ebda0SMatthew Dillon
606d2902f79SSascha Wildner wMaxPacketSize = 0;
607d2902f79SSascha Wildner alt_index = 0;
608d2902f79SSascha Wildner i = 0;
609d2902f79SSascha Wildner j = 0;
610d2902f79SSascha Wildner ed = NULL;
611d2902f79SSascha Wildner
612d2902f79SSascha Wildner /*
613d2902f79SSascha Wildner * Search through all the descriptors looking for the largest
614d2902f79SSascha Wildner * packet size:
615d2902f79SSascha Wildner */
616d2902f79SSascha Wildner while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
617d2902f79SSascha Wildner usbd_get_config_descriptor(uaa->device),
618d2902f79SSascha Wildner (struct usb_descriptor *)ed))) {
619d2902f79SSascha Wildner
620d2902f79SSascha Wildner if ((ed->bDescriptorType == UDESC_INTERFACE) &&
621d2902f79SSascha Wildner (ed->bLength >= sizeof(*id))) {
622d2902f79SSascha Wildner id = (struct usb_interface_descriptor *)ed;
623d2902f79SSascha Wildner i = id->bInterfaceNumber;
624d2902f79SSascha Wildner j = id->bAlternateSetting;
625b06ebda0SMatthew Dillon }
626b06ebda0SMatthew Dillon
627d2902f79SSascha Wildner if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
628d2902f79SSascha Wildner (ed->bLength >= sizeof(*ed)) &&
629d2902f79SSascha Wildner (i == 1)) {
630d2902f79SSascha Wildner uint16_t temp;
631d2902f79SSascha Wildner
632d2902f79SSascha Wildner temp = UGETW(ed->wMaxPacketSize);
633d2902f79SSascha Wildner if (temp > wMaxPacketSize) {
634d2902f79SSascha Wildner wMaxPacketSize = temp;
635d2902f79SSascha Wildner alt_index = j;
636d2902f79SSascha Wildner }
637d2902f79SSascha Wildner }
638b06ebda0SMatthew Dillon }
639b06ebda0SMatthew Dillon
640d2902f79SSascha Wildner /* Set alt configuration on interface #1 only if we found it */
641d2902f79SSascha Wildner if (wMaxPacketSize > 0 &&
642d2902f79SSascha Wildner usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
643d2902f79SSascha Wildner UBT_ALERT(sc, "could not set alternate setting %d " \
644d2902f79SSascha Wildner "for interface 1!\n", alt_index);
645d2902f79SSascha Wildner goto detach;
646b06ebda0SMatthew Dillon }
647b06ebda0SMatthew Dillon
648d2902f79SSascha Wildner /* Setup transfers for both interfaces */
649d2902f79SSascha Wildner if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
650d2902f79SSascha Wildner ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_lock)) {
651d2902f79SSascha Wildner UBT_ALERT(sc, "could not allocate transfers\n");
652d2902f79SSascha Wildner goto detach;
653d2902f79SSascha Wildner }
654d2902f79SSascha Wildner
655d2902f79SSascha Wildner /* Claim all interfaces belonging to the Bluetooth part */
656d2902f79SSascha Wildner for (i = 1;; i++) {
657d2902f79SSascha Wildner iface = usbd_get_iface(uaa->device, i);
658d2902f79SSascha Wildner if (iface == NULL)
659b06ebda0SMatthew Dillon break;
660d2902f79SSascha Wildner id = usbd_get_interface_descriptor(iface);
661b06ebda0SMatthew Dillon
662d2902f79SSascha Wildner if ((id != NULL) &&
663d2902f79SSascha Wildner (id->bInterfaceClass == UICLASS_WIRELESS) &&
664d2902f79SSascha Wildner (id->bInterfaceSubClass == UISUBCLASS_RF) &&
665d2902f79SSascha Wildner (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) {
666d2902f79SSascha Wildner usbd_set_parent_iface(uaa->device, i,
667d2902f79SSascha Wildner uaa->info.bIfaceIndex);
668b06ebda0SMatthew Dillon }
669b06ebda0SMatthew Dillon }
670d2902f79SSascha Wildner return (0); /* success */
671b06ebda0SMatthew Dillon
672d2902f79SSascha Wildner detach:
673d2902f79SSascha Wildner ubt_detach(dev);
674b06ebda0SMatthew Dillon
675d2902f79SSascha Wildner return (ENXIO);
676b06ebda0SMatthew Dillon } /* ubt_attach */
677b06ebda0SMatthew Dillon
678b06ebda0SMatthew Dillon /*
679d2902f79SSascha Wildner * Detach the device.
680d2902f79SSascha Wildner * USB context.
681b06ebda0SMatthew Dillon */
682b06ebda0SMatthew Dillon
683d2902f79SSascha Wildner int
ubt_detach(device_t dev)684d2902f79SSascha Wildner ubt_detach(device_t dev)
685b06ebda0SMatthew Dillon {
686d2902f79SSascha Wildner struct ubt_softc *sc = device_get_softc(dev);
687d2902f79SSascha Wildner node_p node = sc->sc_node;
688b06ebda0SMatthew Dillon
689b06ebda0SMatthew Dillon /* Destroy Netgraph node */
690d2902f79SSascha Wildner if (node != NULL) {
691b06ebda0SMatthew Dillon sc->sc_node = NULL;
692d2902f79SSascha Wildner NG_NODE_REALLY_DIE(node);
693d2902f79SSascha Wildner ng_rmnode_self(node);
694b06ebda0SMatthew Dillon }
695b06ebda0SMatthew Dillon
696d2902f79SSascha Wildner /* Make sure ubt_task in gone */
697d2902f79SSascha Wildner taskqueue_drain(taskqueue_swi, &sc->sc_task);
698b06ebda0SMatthew Dillon
699d2902f79SSascha Wildner /* Free USB transfers, if any */
700d2902f79SSascha Wildner usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
701b06ebda0SMatthew Dillon
702b06ebda0SMatthew Dillon /* Destroy queues */
703d2902f79SSascha Wildner UBT_NG_LOCK(sc);
704d2902f79SSascha Wildner NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
705d2902f79SSascha Wildner NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
706d2902f79SSascha Wildner NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
707d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
708b06ebda0SMatthew Dillon
709d2902f79SSascha Wildner lockuninit(&sc->sc_if_lock);
710d2902f79SSascha Wildner lockuninit(&sc->sc_ng_lock);
711b06ebda0SMatthew Dillon
712b06ebda0SMatthew Dillon return (0);
713b06ebda0SMatthew Dillon } /* ubt_detach */
714b06ebda0SMatthew Dillon
715b06ebda0SMatthew Dillon /*
716d2902f79SSascha Wildner * Called when outgoing control request (HCI command) has completed, i.e.
717d2902f79SSascha Wildner * HCI command was sent to the device.
718d2902f79SSascha Wildner * USB context.
719b06ebda0SMatthew Dillon */
720b06ebda0SMatthew Dillon
721d2902f79SSascha Wildner static void
ubt_ctrl_write_callback(struct usb_xfer * xfer,usb_error_t error)722d2902f79SSascha Wildner ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
723b06ebda0SMatthew Dillon {
724d2902f79SSascha Wildner struct ubt_softc *sc = usbd_xfer_softc(xfer);
725d2902f79SSascha Wildner struct usb_device_request req;
726d2902f79SSascha Wildner struct mbuf *m;
727d2902f79SSascha Wildner struct usb_page_cache *pc;
728d2902f79SSascha Wildner int actlen;
729b06ebda0SMatthew Dillon
730d2902f79SSascha Wildner usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
731b06ebda0SMatthew Dillon
732d2902f79SSascha Wildner switch (USB_GET_STATE(xfer)) {
733d2902f79SSascha Wildner case USB_ST_TRANSFERRED:
734d2902f79SSascha Wildner UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
735d2902f79SSascha Wildner UBT_STAT_BYTES_SENT(sc, actlen);
736d2902f79SSascha Wildner UBT_STAT_PCKTS_SENT(sc);
737d2902f79SSascha Wildner /* FALLTHROUGH */
738d2902f79SSascha Wildner
739d2902f79SSascha Wildner case USB_ST_SETUP:
740d2902f79SSascha Wildner send_next:
741d2902f79SSascha Wildner /* Get next command mbuf, if any */
742d2902f79SSascha Wildner UBT_NG_LOCK(sc);
743b06ebda0SMatthew Dillon NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
744d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
745d2902f79SSascha Wildner
746b06ebda0SMatthew Dillon if (m == NULL) {
747d2902f79SSascha Wildner UBT_INFO(sc, "HCI command queue is empty\n");
748d2902f79SSascha Wildner break; /* transfer complete */
749b06ebda0SMatthew Dillon }
750b06ebda0SMatthew Dillon
751b06ebda0SMatthew Dillon /* Initialize a USB control request and then schedule it */
752b06ebda0SMatthew Dillon bzero(&req, sizeof(req));
753b06ebda0SMatthew Dillon req.bmRequestType = UBT_HCI_REQUEST;
754b06ebda0SMatthew Dillon USETW(req.wLength, m->m_pkthdr.len);
755b06ebda0SMatthew Dillon
756d2902f79SSascha Wildner UBT_INFO(sc, "Sending control request, " \
757d2902f79SSascha Wildner "bmRequestType=0x%02x, wLength=%d\n",
758d2902f79SSascha Wildner req.bmRequestType, UGETW(req.wLength));
759b06ebda0SMatthew Dillon
760d2902f79SSascha Wildner pc = usbd_xfer_get_frame(xfer, 0);
761d2902f79SSascha Wildner usbd_copy_in(pc, 0, &req, sizeof(req));
762d2902f79SSascha Wildner pc = usbd_xfer_get_frame(xfer, 1);
763d2902f79SSascha Wildner usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
764b06ebda0SMatthew Dillon
765d2902f79SSascha Wildner usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
766d2902f79SSascha Wildner usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
767d2902f79SSascha Wildner usbd_xfer_set_frames(xfer, 2);
768b06ebda0SMatthew Dillon
769b06ebda0SMatthew Dillon NG_FREE_M(m);
770b06ebda0SMatthew Dillon
771d2902f79SSascha Wildner usbd_transfer_submit(xfer);
772d2902f79SSascha Wildner break;
773d2902f79SSascha Wildner
774d2902f79SSascha Wildner default: /* Error */
775d2902f79SSascha Wildner if (error != USB_ERR_CANCELLED) {
776d2902f79SSascha Wildner UBT_WARN(sc, "control transfer failed: %s\n",
777d2902f79SSascha Wildner usbd_errstr(error));
778d2902f79SSascha Wildner
779d2902f79SSascha Wildner UBT_STAT_OERROR(sc);
780d2902f79SSascha Wildner goto send_next;
781d2902f79SSascha Wildner }
782d2902f79SSascha Wildner
783d2902f79SSascha Wildner /* transfer cancelled */
784d2902f79SSascha Wildner break;
785d2902f79SSascha Wildner }
786d2902f79SSascha Wildner } /* ubt_ctrl_write_callback */
787b06ebda0SMatthew Dillon
788b06ebda0SMatthew Dillon /*
789d2902f79SSascha Wildner * Called when incoming interrupt transfer (HCI event) has completed, i.e.
790d2902f79SSascha Wildner * HCI event was received from the device.
791d2902f79SSascha Wildner * USB context.
792b06ebda0SMatthew Dillon */
793b06ebda0SMatthew Dillon
794b06ebda0SMatthew Dillon static void
ubt_intr_read_callback(struct usb_xfer * xfer,usb_error_t error)795d2902f79SSascha Wildner ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
796b06ebda0SMatthew Dillon {
797d2902f79SSascha Wildner struct ubt_softc *sc = usbd_xfer_softc(xfer);
798d2902f79SSascha Wildner struct mbuf *m;
799d2902f79SSascha Wildner ng_hci_event_pkt_t *hdr;
800d2902f79SSascha Wildner struct usb_page_cache *pc;
801d2902f79SSascha Wildner int actlen;
802b06ebda0SMatthew Dillon
803d2902f79SSascha Wildner usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
804b06ebda0SMatthew Dillon
805d2902f79SSascha Wildner m = NULL;
806b06ebda0SMatthew Dillon
807d2902f79SSascha Wildner switch (USB_GET_STATE(xfer)) {
808d2902f79SSascha Wildner case USB_ST_TRANSFERRED:
809d2902f79SSascha Wildner /* Allocate a new mbuf */
810b5523eacSSascha Wildner MGETHDR(m, M_NOWAIT, MT_DATA);
811d2902f79SSascha Wildner if (m == NULL) {
812d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
813d2902f79SSascha Wildner goto submit_next;
814d2902f79SSascha Wildner }
815b06ebda0SMatthew Dillon
816b5523eacSSascha Wildner MCLGET(m, M_NOWAIT);
817b06ebda0SMatthew Dillon if (!(m->m_flags & M_EXT)) {
818d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
819d2902f79SSascha Wildner goto submit_next;
820b06ebda0SMatthew Dillon }
821b06ebda0SMatthew Dillon
822d2902f79SSascha Wildner /* Add HCI packet type */
823d2902f79SSascha Wildner *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
824b06ebda0SMatthew Dillon m->m_pkthdr.len = m->m_len = 1;
825b06ebda0SMatthew Dillon
826d2902f79SSascha Wildner if (actlen > MCLBYTES - 1)
827d2902f79SSascha Wildner actlen = MCLBYTES - 1;
828b06ebda0SMatthew Dillon
829d2902f79SSascha Wildner pc = usbd_xfer_get_frame(xfer, 0);
830d2902f79SSascha Wildner usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
831d2902f79SSascha Wildner m->m_pkthdr.len += actlen;
832d2902f79SSascha Wildner m->m_len += actlen;
833b06ebda0SMatthew Dillon
834d2902f79SSascha Wildner UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
835d2902f79SSascha Wildner actlen);
836b06ebda0SMatthew Dillon
837d2902f79SSascha Wildner /* Validate packet and send it up the stack */
838d2902f79SSascha Wildner if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
839d2902f79SSascha Wildner UBT_INFO(sc, "HCI event packet is too short\n");
840b06ebda0SMatthew Dillon
841d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
842d2902f79SSascha Wildner goto submit_next;
843b06ebda0SMatthew Dillon }
844b06ebda0SMatthew Dillon
845b06ebda0SMatthew Dillon hdr = mtod(m, ng_hci_event_pkt_t *);
846d2902f79SSascha Wildner if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
847d2902f79SSascha Wildner UBT_ERR(sc, "Invalid HCI event packet size, " \
848d2902f79SSascha Wildner "length=%d, pktlen=%d\n",
849d2902f79SSascha Wildner hdr->length, m->m_pkthdr.len);
850b06ebda0SMatthew Dillon
851d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
852d2902f79SSascha Wildner goto submit_next;
853b06ebda0SMatthew Dillon }
854b06ebda0SMatthew Dillon
855d2902f79SSascha Wildner UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
856d2902f79SSascha Wildner "length=%d\n", m->m_pkthdr.len, hdr->length);
857b06ebda0SMatthew Dillon
858d2902f79SSascha Wildner UBT_STAT_PCKTS_RECV(sc);
859d2902f79SSascha Wildner UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
860d2902f79SSascha Wildner
861d2902f79SSascha Wildner ubt_fwd_mbuf_up(sc, &m);
862d2902f79SSascha Wildner /* m == NULL at this point */
863d2902f79SSascha Wildner /* FALLTHROUGH */
864d2902f79SSascha Wildner
865d2902f79SSascha Wildner case USB_ST_SETUP:
866d2902f79SSascha Wildner submit_next:
867d2902f79SSascha Wildner NG_FREE_M(m); /* checks for m != NULL */
868d2902f79SSascha Wildner
869d2902f79SSascha Wildner usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
870d2902f79SSascha Wildner usbd_transfer_submit(xfer);
871d2902f79SSascha Wildner break;
872d2902f79SSascha Wildner
873d2902f79SSascha Wildner default: /* Error */
874d2902f79SSascha Wildner if (error != USB_ERR_CANCELLED) {
875d2902f79SSascha Wildner UBT_WARN(sc, "interrupt transfer failed: %s\n",
876d2902f79SSascha Wildner usbd_errstr(error));
877d2902f79SSascha Wildner
878d2902f79SSascha Wildner /* Try to clear stall first */
879d2902f79SSascha Wildner usbd_xfer_set_stall(xfer);
880d2902f79SSascha Wildner goto submit_next;
881b06ebda0SMatthew Dillon }
882d2902f79SSascha Wildner /* transfer cancelled */
883d2902f79SSascha Wildner break;
884b06ebda0SMatthew Dillon }
885d2902f79SSascha Wildner } /* ubt_intr_read_callback */
886b06ebda0SMatthew Dillon
887b06ebda0SMatthew Dillon /*
888d2902f79SSascha Wildner * Called when incoming bulk transfer (ACL packet) has completed, i.e.
889d2902f79SSascha Wildner * ACL packet was received from the device.
890d2902f79SSascha Wildner * USB context.
891b06ebda0SMatthew Dillon */
892b06ebda0SMatthew Dillon
893d2902f79SSascha Wildner static void
ubt_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)894d2902f79SSascha Wildner ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
895b06ebda0SMatthew Dillon {
896d2902f79SSascha Wildner struct ubt_softc *sc = usbd_xfer_softc(xfer);
897d2902f79SSascha Wildner struct mbuf *m;
898d2902f79SSascha Wildner ng_hci_acldata_pkt_t *hdr;
899d2902f79SSascha Wildner struct usb_page_cache *pc;
900d2902f79SSascha Wildner int len;
901d2902f79SSascha Wildner int actlen;
902b06ebda0SMatthew Dillon
903d2902f79SSascha Wildner usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
904b06ebda0SMatthew Dillon
905d2902f79SSascha Wildner m = NULL;
906d2902f79SSascha Wildner
907d2902f79SSascha Wildner switch (USB_GET_STATE(xfer)) {
908d2902f79SSascha Wildner case USB_ST_TRANSFERRED:
909d2902f79SSascha Wildner /* Allocate new mbuf */
910b5523eacSSascha Wildner MGETHDR(m, M_NOWAIT, MT_DATA);
911d2902f79SSascha Wildner if (m == NULL) {
912d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
913d2902f79SSascha Wildner goto submit_next;
914d2902f79SSascha Wildner }
915b06ebda0SMatthew Dillon
916b5523eacSSascha Wildner MCLGET(m, M_NOWAIT);
917b06ebda0SMatthew Dillon if (!(m->m_flags & M_EXT)) {
918d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
919d2902f79SSascha Wildner goto submit_next;
920b06ebda0SMatthew Dillon }
921b06ebda0SMatthew Dillon
922d2902f79SSascha Wildner /* Add HCI packet type */
923d2902f79SSascha Wildner *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
924b06ebda0SMatthew Dillon m->m_pkthdr.len = m->m_len = 1;
925b06ebda0SMatthew Dillon
926d2902f79SSascha Wildner if (actlen > MCLBYTES - 1)
927d2902f79SSascha Wildner actlen = MCLBYTES - 1;
928b06ebda0SMatthew Dillon
929d2902f79SSascha Wildner pc = usbd_xfer_get_frame(xfer, 0);
930d2902f79SSascha Wildner usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
931d2902f79SSascha Wildner m->m_pkthdr.len += actlen;
932d2902f79SSascha Wildner m->m_len += actlen;
933b06ebda0SMatthew Dillon
934d2902f79SSascha Wildner UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
935d2902f79SSascha Wildner actlen);
936b06ebda0SMatthew Dillon
937d2902f79SSascha Wildner /* Validate packet and send it up the stack */
938d2902f79SSascha Wildner if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
939d2902f79SSascha Wildner UBT_INFO(sc, "HCI ACL packet is too short\n");
940b06ebda0SMatthew Dillon
941d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
942d2902f79SSascha Wildner goto submit_next;
943b06ebda0SMatthew Dillon }
944b06ebda0SMatthew Dillon
945b06ebda0SMatthew Dillon hdr = mtod(m, ng_hci_acldata_pkt_t *);
946b06ebda0SMatthew Dillon len = le16toh(hdr->length);
947d2902f79SSascha Wildner if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
948d2902f79SSascha Wildner UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
949d2902f79SSascha Wildner "pktlen=%d\n", len, m->m_pkthdr.len);
950b06ebda0SMatthew Dillon
951d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
952d2902f79SSascha Wildner goto submit_next;
953b06ebda0SMatthew Dillon }
954d2902f79SSascha Wildner
955d2902f79SSascha Wildner UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
956d2902f79SSascha Wildner "length=%d\n", m->m_pkthdr.len, len);
957d2902f79SSascha Wildner
958d2902f79SSascha Wildner UBT_STAT_PCKTS_RECV(sc);
959d2902f79SSascha Wildner UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
960d2902f79SSascha Wildner
961d2902f79SSascha Wildner ubt_fwd_mbuf_up(sc, &m);
962d2902f79SSascha Wildner /* m == NULL at this point */
963d2902f79SSascha Wildner /* FALLTHOUGH */
964d2902f79SSascha Wildner
965d2902f79SSascha Wildner case USB_ST_SETUP:
966d2902f79SSascha Wildner submit_next:
967d2902f79SSascha Wildner NG_FREE_M(m); /* checks for m != NULL */
968d2902f79SSascha Wildner
969d2902f79SSascha Wildner usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
970d2902f79SSascha Wildner usbd_transfer_submit(xfer);
971d2902f79SSascha Wildner break;
972d2902f79SSascha Wildner
973d2902f79SSascha Wildner default: /* Error */
974d2902f79SSascha Wildner if (error != USB_ERR_CANCELLED) {
975d2902f79SSascha Wildner UBT_WARN(sc, "bulk-in transfer failed: %s\n",
976d2902f79SSascha Wildner usbd_errstr(error));
977d2902f79SSascha Wildner
978d2902f79SSascha Wildner /* Try to clear stall first */
979d2902f79SSascha Wildner usbd_xfer_set_stall(xfer);
980d2902f79SSascha Wildner goto submit_next;
981d2902f79SSascha Wildner }
982d2902f79SSascha Wildner /* transfer cancelled */
983d2902f79SSascha Wildner break;
984d2902f79SSascha Wildner }
985d2902f79SSascha Wildner } /* ubt_bulk_read_callback */
986b06ebda0SMatthew Dillon
987b06ebda0SMatthew Dillon /*
988d2902f79SSascha Wildner * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
989d2902f79SSascha Wildner * ACL packet was sent to the device.
990d2902f79SSascha Wildner * USB context.
991b06ebda0SMatthew Dillon */
992b06ebda0SMatthew Dillon
993d2902f79SSascha Wildner static void
ubt_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)994d2902f79SSascha Wildner ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
995b06ebda0SMatthew Dillon {
996d2902f79SSascha Wildner struct ubt_softc *sc = usbd_xfer_softc(xfer);
997d2902f79SSascha Wildner struct mbuf *m;
998d2902f79SSascha Wildner struct usb_page_cache *pc;
999d2902f79SSascha Wildner int actlen;
1000b06ebda0SMatthew Dillon
1001d2902f79SSascha Wildner usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1002b06ebda0SMatthew Dillon
1003d2902f79SSascha Wildner switch (USB_GET_STATE(xfer)) {
1004d2902f79SSascha Wildner case USB_ST_TRANSFERRED:
1005d2902f79SSascha Wildner UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
1006d2902f79SSascha Wildner UBT_STAT_BYTES_SENT(sc, actlen);
1007d2902f79SSascha Wildner UBT_STAT_PCKTS_SENT(sc);
1008d2902f79SSascha Wildner /* FALLTHROUGH */
1009d2902f79SSascha Wildner
1010d2902f79SSascha Wildner case USB_ST_SETUP:
1011d2902f79SSascha Wildner send_next:
1012d2902f79SSascha Wildner /* Get next mbuf, if any */
1013d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1014b06ebda0SMatthew Dillon NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
1015d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1016b06ebda0SMatthew Dillon
1017d2902f79SSascha Wildner if (m == NULL) {
1018d2902f79SSascha Wildner UBT_INFO(sc, "ACL data queue is empty\n");
1019d2902f79SSascha Wildner break; /* transfer completed */
1020b06ebda0SMatthew Dillon }
1021b06ebda0SMatthew Dillon
1022b06ebda0SMatthew Dillon /*
1023d2902f79SSascha Wildner * Copy ACL data frame back to a linear USB transfer buffer
1024d2902f79SSascha Wildner * and schedule transfer
1025b06ebda0SMatthew Dillon */
1026b06ebda0SMatthew Dillon
1027d2902f79SSascha Wildner pc = usbd_xfer_get_frame(xfer, 0);
1028d2902f79SSascha Wildner usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
1029d2902f79SSascha Wildner usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
1030d2902f79SSascha Wildner
1031d2902f79SSascha Wildner UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
1032b06ebda0SMatthew Dillon m->m_pkthdr.len);
1033b06ebda0SMatthew Dillon
1034b06ebda0SMatthew Dillon NG_FREE_M(m);
1035b06ebda0SMatthew Dillon
1036d2902f79SSascha Wildner usbd_transfer_submit(xfer);
1037d2902f79SSascha Wildner break;
1038d2902f79SSascha Wildner
1039d2902f79SSascha Wildner default: /* Error */
1040d2902f79SSascha Wildner if (error != USB_ERR_CANCELLED) {
1041d2902f79SSascha Wildner UBT_WARN(sc, "bulk-out transfer failed: %s\n",
1042d2902f79SSascha Wildner usbd_errstr(error));
1043d2902f79SSascha Wildner
1044d2902f79SSascha Wildner UBT_STAT_OERROR(sc);
1045d2902f79SSascha Wildner
1046d2902f79SSascha Wildner /* try to clear stall first */
1047d2902f79SSascha Wildner usbd_xfer_set_stall(xfer);
1048d2902f79SSascha Wildner goto send_next;
1049d2902f79SSascha Wildner }
1050d2902f79SSascha Wildner /* transfer cancelled */
1051d2902f79SSascha Wildner break;
1052d2902f79SSascha Wildner }
1053d2902f79SSascha Wildner } /* ubt_bulk_write_callback */
1054b06ebda0SMatthew Dillon
1055b06ebda0SMatthew Dillon /*
1056d2902f79SSascha Wildner * Called when incoming isoc transfer (SCO packet) has completed, i.e.
1057d2902f79SSascha Wildner * SCO packet was received from the device.
1058d2902f79SSascha Wildner * USB context.
1059b06ebda0SMatthew Dillon */
1060b06ebda0SMatthew Dillon
1061b06ebda0SMatthew Dillon static void
ubt_isoc_read_callback(struct usb_xfer * xfer,usb_error_t error)1062d2902f79SSascha Wildner ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
1063b06ebda0SMatthew Dillon {
1064d2902f79SSascha Wildner struct ubt_softc *sc = usbd_xfer_softc(xfer);
1065d2902f79SSascha Wildner int n;
1066d2902f79SSascha Wildner int actlen, nframes;
1067b06ebda0SMatthew Dillon
1068d2902f79SSascha Wildner usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1069b06ebda0SMatthew Dillon
1070d2902f79SSascha Wildner switch (USB_GET_STATE(xfer)) {
1071d2902f79SSascha Wildner case USB_ST_TRANSFERRED:
1072d2902f79SSascha Wildner for (n = 0; n < nframes; n ++)
1073d2902f79SSascha Wildner if (ubt_isoc_read_one_frame(xfer, n) < 0)
1074d2902f79SSascha Wildner break;
1075d2902f79SSascha Wildner /* FALLTHROUGH */
1076b06ebda0SMatthew Dillon
1077d2902f79SSascha Wildner case USB_ST_SETUP:
1078d2902f79SSascha Wildner read_next:
1079d2902f79SSascha Wildner for (n = 0; n < nframes; n ++)
1080d2902f79SSascha Wildner usbd_xfer_set_frame_len(xfer, n,
1081d2902f79SSascha Wildner usbd_xfer_max_framelen(xfer));
1082b06ebda0SMatthew Dillon
1083d2902f79SSascha Wildner usbd_transfer_submit(xfer);
1084d2902f79SSascha Wildner break;
1085b06ebda0SMatthew Dillon
1086d2902f79SSascha Wildner default: /* Error */
1087d2902f79SSascha Wildner if (error != USB_ERR_CANCELLED) {
1088d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
1089d2902f79SSascha Wildner goto read_next;
1090b06ebda0SMatthew Dillon }
1091b06ebda0SMatthew Dillon
1092d2902f79SSascha Wildner /* transfer cancelled */
1093d2902f79SSascha Wildner break;
1094b06ebda0SMatthew Dillon }
1095d2902f79SSascha Wildner } /* ubt_isoc_read_callback */
1096b06ebda0SMatthew Dillon
1097b06ebda0SMatthew Dillon /*
1098d2902f79SSascha Wildner * Helper function. Called from ubt_isoc_read_callback() to read
1099d2902f79SSascha Wildner * SCO data from one frame.
1100d2902f79SSascha Wildner * USB context.
1101b06ebda0SMatthew Dillon */
1102b06ebda0SMatthew Dillon
1103d2902f79SSascha Wildner static int
ubt_isoc_read_one_frame(struct usb_xfer * xfer,int frame_no)1104d2902f79SSascha Wildner ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
1105b06ebda0SMatthew Dillon {
1106d2902f79SSascha Wildner struct ubt_softc *sc = usbd_xfer_softc(xfer);
1107d2902f79SSascha Wildner struct usb_page_cache *pc;
1108d2902f79SSascha Wildner struct mbuf *m;
1109d2902f79SSascha Wildner int len, want, got, total;
1110b06ebda0SMatthew Dillon
1111d2902f79SSascha Wildner /* Get existing SCO reassembly buffer */
1112d2902f79SSascha Wildner pc = usbd_xfer_get_frame(xfer, 0);
1113d2902f79SSascha Wildner m = sc->sc_isoc_in_buffer;
1114d2902f79SSascha Wildner total = usbd_xfer_frame_len(xfer, frame_no);
1115b06ebda0SMatthew Dillon
1116d2902f79SSascha Wildner /* While we have data in the frame */
1117d2902f79SSascha Wildner while (total > 0) {
1118d2902f79SSascha Wildner if (m == NULL) {
1119d2902f79SSascha Wildner /* Start new reassembly buffer */
1120b5523eacSSascha Wildner MGETHDR(m, M_NOWAIT, MT_DATA);
1121b06ebda0SMatthew Dillon if (m == NULL) {
1122d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
1123d2902f79SSascha Wildner return (-1); /* XXX out of sync! */
1124b06ebda0SMatthew Dillon }
1125b06ebda0SMatthew Dillon
1126b5523eacSSascha Wildner MCLGET(m, M_NOWAIT);
1127d2902f79SSascha Wildner if (!(m->m_flags & M_EXT)) {
1128d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
1129b06ebda0SMatthew Dillon NG_FREE_M(m);
1130d2902f79SSascha Wildner return (-1); /* XXX out of sync! */
1131b06ebda0SMatthew Dillon }
1132d2902f79SSascha Wildner
1133d2902f79SSascha Wildner /* Expect SCO header */
1134d2902f79SSascha Wildner *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
1135d2902f79SSascha Wildner m->m_pkthdr.len = m->m_len = got = 1;
1136d2902f79SSascha Wildner want = sizeof(ng_hci_scodata_pkt_t);
1137d2902f79SSascha Wildner } else {
1138d2902f79SSascha Wildner /*
1139d2902f79SSascha Wildner * Check if we have SCO header and if so
1140d2902f79SSascha Wildner * adjust amount of data we want
1141d2902f79SSascha Wildner */
1142d2902f79SSascha Wildner got = m->m_pkthdr.len;
1143d2902f79SSascha Wildner want = sizeof(ng_hci_scodata_pkt_t);
1144d2902f79SSascha Wildner
1145d2902f79SSascha Wildner if (got >= want)
1146d2902f79SSascha Wildner want += mtod(m, ng_hci_scodata_pkt_t *)->length;
1147d2902f79SSascha Wildner }
1148d2902f79SSascha Wildner
1149d2902f79SSascha Wildner /* Append frame data to the SCO reassembly buffer */
1150d2902f79SSascha Wildner len = total;
1151d2902f79SSascha Wildner if (got + len > want)
1152d2902f79SSascha Wildner len = want - got;
1153d2902f79SSascha Wildner
1154d2902f79SSascha Wildner usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
1155d2902f79SSascha Wildner mtod(m, uint8_t *) + m->m_pkthdr.len, len);
1156d2902f79SSascha Wildner
1157d2902f79SSascha Wildner m->m_pkthdr.len += len;
1158d2902f79SSascha Wildner m->m_len += len;
1159d2902f79SSascha Wildner total -= len;
1160d2902f79SSascha Wildner
1161d2902f79SSascha Wildner /* Check if we got everything we wanted, if not - continue */
1162d2902f79SSascha Wildner if (got != want)
1163d2902f79SSascha Wildner continue;
1164d2902f79SSascha Wildner
1165d2902f79SSascha Wildner /* If we got here then we got complete SCO frame */
1166d2902f79SSascha Wildner UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
1167d2902f79SSascha Wildner "length=%d\n", m->m_pkthdr.len,
1168d2902f79SSascha Wildner mtod(m, ng_hci_scodata_pkt_t *)->length);
1169d2902f79SSascha Wildner
1170d2902f79SSascha Wildner UBT_STAT_PCKTS_RECV(sc);
1171d2902f79SSascha Wildner UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
1172d2902f79SSascha Wildner
1173d2902f79SSascha Wildner ubt_fwd_mbuf_up(sc, &m);
1174d2902f79SSascha Wildner /* m == NULL at this point */
1175d2902f79SSascha Wildner }
1176d2902f79SSascha Wildner
1177d2902f79SSascha Wildner /* Put SCO reassembly buffer back */
1178d2902f79SSascha Wildner sc->sc_isoc_in_buffer = m;
1179d2902f79SSascha Wildner
1180d2902f79SSascha Wildner return (0);
1181d2902f79SSascha Wildner } /* ubt_isoc_read_one_frame */
1182b06ebda0SMatthew Dillon
1183b06ebda0SMatthew Dillon /*
1184d2902f79SSascha Wildner * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
1185d2902f79SSascha Wildner * SCO packet was sent to the device.
1186d2902f79SSascha Wildner * USB context.
1187b06ebda0SMatthew Dillon */
1188b06ebda0SMatthew Dillon
1189d2902f79SSascha Wildner static void
ubt_isoc_write_callback(struct usb_xfer * xfer,usb_error_t error)1190d2902f79SSascha Wildner ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
1191b06ebda0SMatthew Dillon {
1192d2902f79SSascha Wildner struct ubt_softc *sc = usbd_xfer_softc(xfer);
1193d2902f79SSascha Wildner struct usb_page_cache *pc;
1194d2902f79SSascha Wildner struct mbuf *m;
1195d2902f79SSascha Wildner int n, space, offset;
1196d2902f79SSascha Wildner int actlen, nframes;
1197b06ebda0SMatthew Dillon
1198d2902f79SSascha Wildner usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1199d2902f79SSascha Wildner pc = usbd_xfer_get_frame(xfer, 0);
1200b06ebda0SMatthew Dillon
1201d2902f79SSascha Wildner switch (USB_GET_STATE(xfer)) {
1202d2902f79SSascha Wildner case USB_ST_TRANSFERRED:
1203d2902f79SSascha Wildner UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
1204d2902f79SSascha Wildner UBT_STAT_BYTES_SENT(sc, actlen);
1205d2902f79SSascha Wildner UBT_STAT_PCKTS_SENT(sc);
1206d2902f79SSascha Wildner /* FALLTHROUGH */
1207d2902f79SSascha Wildner
1208d2902f79SSascha Wildner case USB_ST_SETUP:
1209d2902f79SSascha Wildner send_next:
1210d2902f79SSascha Wildner offset = 0;
1211d2902f79SSascha Wildner space = usbd_xfer_max_framelen(xfer) * nframes;
1212d2902f79SSascha Wildner m = NULL;
1213d2902f79SSascha Wildner
1214d2902f79SSascha Wildner while (space > 0) {
1215b06ebda0SMatthew Dillon if (m == NULL) {
1216d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1217d2902f79SSascha Wildner NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
1218d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1219b06ebda0SMatthew Dillon
1220d2902f79SSascha Wildner if (m == NULL)
1221d2902f79SSascha Wildner break;
1222b06ebda0SMatthew Dillon }
1223b06ebda0SMatthew Dillon
1224d2902f79SSascha Wildner n = min(space, m->m_pkthdr.len);
1225d2902f79SSascha Wildner if (n > 0) {
1226d2902f79SSascha Wildner usbd_m_copy_in(pc, offset, m,0, n);
1227d2902f79SSascha Wildner m_adj(m, n);
1228b06ebda0SMatthew Dillon
1229d2902f79SSascha Wildner offset += n;
1230d2902f79SSascha Wildner space -= n;
1231b06ebda0SMatthew Dillon }
1232b06ebda0SMatthew Dillon
1233d2902f79SSascha Wildner if (m->m_pkthdr.len == 0)
1234d2902f79SSascha Wildner NG_FREE_M(m); /* sets m = NULL */
1235b06ebda0SMatthew Dillon }
1236b06ebda0SMatthew Dillon
1237d2902f79SSascha Wildner /* Put whatever is left from mbuf back on queue */
1238d2902f79SSascha Wildner if (m != NULL) {
1239d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1240d2902f79SSascha Wildner NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
1241d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1242b06ebda0SMatthew Dillon }
1243b06ebda0SMatthew Dillon
1244b06ebda0SMatthew Dillon /*
1245d2902f79SSascha Wildner * Calculate sizes for isoc frames.
1246d2902f79SSascha Wildner * Note that offset could be 0 at this point (i.e. we have
1247d2902f79SSascha Wildner * nothing to send). That is fine, as we have isoc. transfers
1248d2902f79SSascha Wildner * going in both directions all the time. In this case it
1249d2902f79SSascha Wildner * would be just empty isoc. transfer.
1250d2902f79SSascha Wildner */
1251d2902f79SSascha Wildner
1252d2902f79SSascha Wildner for (n = 0; n < nframes; n ++) {
1253d2902f79SSascha Wildner usbd_xfer_set_frame_len(xfer, n,
1254d2902f79SSascha Wildner min(offset, usbd_xfer_max_framelen(xfer)));
1255d2902f79SSascha Wildner offset -= usbd_xfer_frame_len(xfer, n);
1256d2902f79SSascha Wildner }
1257d2902f79SSascha Wildner
1258d2902f79SSascha Wildner usbd_transfer_submit(xfer);
1259d2902f79SSascha Wildner break;
1260d2902f79SSascha Wildner
1261d2902f79SSascha Wildner default: /* Error */
1262d2902f79SSascha Wildner if (error != USB_ERR_CANCELLED) {
1263d2902f79SSascha Wildner UBT_STAT_OERROR(sc);
1264d2902f79SSascha Wildner goto send_next;
1265d2902f79SSascha Wildner }
1266d2902f79SSascha Wildner
1267d2902f79SSascha Wildner /* transfer cancelled */
1268d2902f79SSascha Wildner break;
1269d2902f79SSascha Wildner }
1270d2902f79SSascha Wildner }
1271d2902f79SSascha Wildner
1272d2902f79SSascha Wildner /*
1273d2902f79SSascha Wildner * Utility function to forward provided mbuf upstream (i.e. up the stack).
1274d2902f79SSascha Wildner * Modifies value of the mbuf pointer (sets it to NULL).
1275d2902f79SSascha Wildner * Save to call from any context.
1276d2902f79SSascha Wildner */
1277d2902f79SSascha Wildner
1278d2902f79SSascha Wildner static int
ubt_fwd_mbuf_up(ubt_softc_p sc,struct mbuf ** m)1279d2902f79SSascha Wildner ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
1280d2902f79SSascha Wildner {
1281d2902f79SSascha Wildner hook_p hook;
1282d2902f79SSascha Wildner int error;
1283d2902f79SSascha Wildner
1284d2902f79SSascha Wildner /*
1285d2902f79SSascha Wildner * Close the race with Netgraph hook newhook/disconnect methods.
1286d2902f79SSascha Wildner * Save the hook pointer atomically. Two cases are possible:
1287d2902f79SSascha Wildner *
1288d2902f79SSascha Wildner * 1) The hook pointer is NULL. It means disconnect method got
1289d2902f79SSascha Wildner * there first. In this case we are done.
1290d2902f79SSascha Wildner *
1291d2902f79SSascha Wildner * 2) The hook pointer is not NULL. It means that hook pointer
1292d2902f79SSascha Wildner * could be either in valid or invalid (i.e. in the process
1293d2902f79SSascha Wildner * of disconnect) state. In any case grab an extra reference
1294d2902f79SSascha Wildner * to protect the hook pointer.
1295d2902f79SSascha Wildner *
1296d2902f79SSascha Wildner * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
1297d2902f79SSascha Wildner * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
1298d2902f79SSascha Wildner */
1299d2902f79SSascha Wildner
1300d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1301d2902f79SSascha Wildner if ((hook = sc->sc_hook) != NULL)
1302d2902f79SSascha Wildner NG_HOOK_REF(hook);
1303d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1304d2902f79SSascha Wildner
1305d2902f79SSascha Wildner if (hook == NULL) {
1306d2902f79SSascha Wildner NG_FREE_M(*m);
1307d2902f79SSascha Wildner return (ENETDOWN);
1308d2902f79SSascha Wildner }
1309d2902f79SSascha Wildner
1310d2902f79SSascha Wildner NG_SEND_DATA_ONLY(error, hook, *m);
1311d2902f79SSascha Wildner NG_HOOK_UNREF(hook);
1312d2902f79SSascha Wildner
1313d2902f79SSascha Wildner if (error != 0)
1314d2902f79SSascha Wildner UBT_STAT_IERROR(sc);
1315d2902f79SSascha Wildner
1316d2902f79SSascha Wildner return (error);
1317d2902f79SSascha Wildner } /* ubt_fwd_mbuf_up */
1318d2902f79SSascha Wildner
1319d2902f79SSascha Wildner /****************************************************************************
1320d2902f79SSascha Wildner ****************************************************************************
1321d2902f79SSascha Wildner ** Glue
1322d2902f79SSascha Wildner ****************************************************************************
1323d2902f79SSascha Wildner ****************************************************************************/
1324d2902f79SSascha Wildner
1325d2902f79SSascha Wildner /*
1326d2902f79SSascha Wildner * Schedule glue task. Should be called with sc_ng_mtx held.
1327d2902f79SSascha Wildner * Netgraph context.
1328b06ebda0SMatthew Dillon */
1329b06ebda0SMatthew Dillon
1330b06ebda0SMatthew Dillon static void
ubt_task_schedule(ubt_softc_p sc,int action)1331d2902f79SSascha Wildner ubt_task_schedule(ubt_softc_p sc, int action)
1332b06ebda0SMatthew Dillon {
1333d2902f79SSascha Wildner KKASSERT(lockowned(&sc->sc_ng_lock) != 0);
1334b06ebda0SMatthew Dillon
1335b06ebda0SMatthew Dillon /*
1336d2902f79SSascha Wildner * Try to handle corner case when "start all" and "stop all"
1337d2902f79SSascha Wildner * actions can both be set before task is executed.
1338d2902f79SSascha Wildner *
1339d2902f79SSascha Wildner * The rules are
1340d2902f79SSascha Wildner *
1341d2902f79SSascha Wildner * sc_task_flags action new sc_task_flags
1342d2902f79SSascha Wildner * ------------------------------------------------------
1343d2902f79SSascha Wildner * 0 start start
1344d2902f79SSascha Wildner * 0 stop stop
1345d2902f79SSascha Wildner * start start start
1346d2902f79SSascha Wildner * start stop stop
1347d2902f79SSascha Wildner * stop start stop|start
1348d2902f79SSascha Wildner * stop stop stop
1349d2902f79SSascha Wildner * stop|start start stop|start
1350d2902f79SSascha Wildner * stop|start stop stop
1351d2902f79SSascha Wildner */
1352d2902f79SSascha Wildner
1353d2902f79SSascha Wildner if (action != 0) {
1354d2902f79SSascha Wildner if ((action & UBT_FLAG_T_STOP_ALL) != 0)
1355d2902f79SSascha Wildner sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
1356d2902f79SSascha Wildner
1357d2902f79SSascha Wildner sc->sc_task_flags |= action;
1358d2902f79SSascha Wildner }
1359d2902f79SSascha Wildner
1360d2902f79SSascha Wildner if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
1361d2902f79SSascha Wildner return;
1362d2902f79SSascha Wildner
1363d2902f79SSascha Wildner if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
1364d2902f79SSascha Wildner sc->sc_task_flags |= UBT_FLAG_T_PENDING;
1365d2902f79SSascha Wildner return;
1366d2902f79SSascha Wildner }
1367d2902f79SSascha Wildner
1368d2902f79SSascha Wildner /* XXX: i think this should never happen */
1369d2902f79SSascha Wildner } /* ubt_task_schedule */
1370d2902f79SSascha Wildner
1371d2902f79SSascha Wildner /*
1372d2902f79SSascha Wildner * Glue task. Examines sc_task_flags and does things depending on it.
1373d2902f79SSascha Wildner * Taskqueue context.
1374b06ebda0SMatthew Dillon */
1375b06ebda0SMatthew Dillon
1376b06ebda0SMatthew Dillon static void
ubt_task(void * context,int pending)1377d2902f79SSascha Wildner ubt_task(void *context, int pending)
1378b06ebda0SMatthew Dillon {
1379d2902f79SSascha Wildner ubt_softc_p sc = context;
1380d2902f79SSascha Wildner int task_flags, i;
1381b06ebda0SMatthew Dillon
1382d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1383d2902f79SSascha Wildner task_flags = sc->sc_task_flags;
1384d2902f79SSascha Wildner sc->sc_task_flags = 0;
1385d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1386b06ebda0SMatthew Dillon
1387d2902f79SSascha Wildner /*
1388d2902f79SSascha Wildner * Stop all USB transfers synchronously.
1389d2902f79SSascha Wildner * Stop interface #0 and #1 transfers at the same time and in the
1390d2902f79SSascha Wildner * same loop. usbd_transfer_drain() will do appropriate locking.
1391d2902f79SSascha Wildner */
1392b06ebda0SMatthew Dillon
1393d2902f79SSascha Wildner if (task_flags & UBT_FLAG_T_STOP_ALL)
1394d2902f79SSascha Wildner for (i = 0; i < UBT_N_TRANSFER; i ++)
1395d2902f79SSascha Wildner usbd_transfer_drain(sc->sc_xfer[i]);
1396d2902f79SSascha Wildner
1397d2902f79SSascha Wildner /* Start incoming interrupt and bulk, and all isoc. USB transfers */
1398d2902f79SSascha Wildner if (task_flags & UBT_FLAG_T_START_ALL) {
1399d2902f79SSascha Wildner /*
1400d2902f79SSascha Wildner * Interface #0
1401d2902f79SSascha Wildner */
1402d2902f79SSascha Wildner
1403d2902f79SSascha Wildner lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
1404d2902f79SSascha Wildner
1405d2902f79SSascha Wildner ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
1406d2902f79SSascha Wildner ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
1407d2902f79SSascha Wildner
1408d2902f79SSascha Wildner /*
1409d2902f79SSascha Wildner * Interface #1
1410d2902f79SSascha Wildner * Start both read and write isoc. transfers by default.
1411d2902f79SSascha Wildner * Get them going all the time even if we have nothing
1412d2902f79SSascha Wildner * to send to avoid any delays.
1413d2902f79SSascha Wildner */
1414d2902f79SSascha Wildner
1415d2902f79SSascha Wildner ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
1416d2902f79SSascha Wildner ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
1417d2902f79SSascha Wildner ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
1418d2902f79SSascha Wildner ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
1419d2902f79SSascha Wildner
1420d2902f79SSascha Wildner lockmgr(&sc->sc_if_lock, LK_RELEASE);
1421d2902f79SSascha Wildner }
1422d2902f79SSascha Wildner
1423d2902f79SSascha Wildner /* Start outgoing control transfer */
1424d2902f79SSascha Wildner if (task_flags & UBT_FLAG_T_START_CTRL) {
1425d2902f79SSascha Wildner lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
1426d2902f79SSascha Wildner ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
1427d2902f79SSascha Wildner lockmgr(&sc->sc_if_lock, LK_RELEASE);
1428d2902f79SSascha Wildner }
1429d2902f79SSascha Wildner
1430d2902f79SSascha Wildner /* Start outgoing bulk transfer */
1431d2902f79SSascha Wildner if (task_flags & UBT_FLAG_T_START_BULK) {
1432d2902f79SSascha Wildner lockmgr(&sc->sc_if_lock, LK_EXCLUSIVE);
1433d2902f79SSascha Wildner ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
1434d2902f79SSascha Wildner lockmgr(&sc->sc_if_lock, LK_RELEASE);
1435d2902f79SSascha Wildner }
1436d2902f79SSascha Wildner } /* ubt_task */
1437b06ebda0SMatthew Dillon
1438b06ebda0SMatthew Dillon /****************************************************************************
1439b06ebda0SMatthew Dillon ****************************************************************************
1440b06ebda0SMatthew Dillon ** Netgraph specific
1441b06ebda0SMatthew Dillon ****************************************************************************
1442b06ebda0SMatthew Dillon ****************************************************************************/
1443b06ebda0SMatthew Dillon
1444b06ebda0SMatthew Dillon /*
1445b06ebda0SMatthew Dillon * Netgraph node constructor. Do not allow to create node of this type.
1446d2902f79SSascha Wildner * Netgraph context.
1447b06ebda0SMatthew Dillon */
1448b06ebda0SMatthew Dillon
1449b06ebda0SMatthew Dillon static int
ng_ubt_constructor(node_p node)1450b06ebda0SMatthew Dillon ng_ubt_constructor(node_p node)
1451b06ebda0SMatthew Dillon {
1452b06ebda0SMatthew Dillon return (EINVAL);
1453b06ebda0SMatthew Dillon } /* ng_ubt_constructor */
1454b06ebda0SMatthew Dillon
1455b06ebda0SMatthew Dillon /*
1456d2902f79SSascha Wildner * Netgraph node destructor. Destroy node only when device has been detached.
1457d2902f79SSascha Wildner * Netgraph context.
1458b06ebda0SMatthew Dillon */
1459b06ebda0SMatthew Dillon
1460b06ebda0SMatthew Dillon static int
ng_ubt_shutdown(node_p node)1461b06ebda0SMatthew Dillon ng_ubt_shutdown(node_p node)
1462b06ebda0SMatthew Dillon {
1463d2902f79SSascha Wildner if (node->nd_flags & NGF_REALLY_DIE) {
1464d2902f79SSascha Wildner /*
1465d2902f79SSascha Wildner * We came here because the USB device is being
1466d2902f79SSascha Wildner * detached, so stop being persistant.
1467d2902f79SSascha Wildner */
1468b06ebda0SMatthew Dillon NG_NODE_SET_PRIVATE(node, NULL);
1469b06ebda0SMatthew Dillon NG_NODE_UNREF(node);
1470d2902f79SSascha Wildner } else
1471d2902f79SSascha Wildner NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
1472b06ebda0SMatthew Dillon
1473b06ebda0SMatthew Dillon return (0);
1474b06ebda0SMatthew Dillon } /* ng_ubt_shutdown */
1475b06ebda0SMatthew Dillon
1476b06ebda0SMatthew Dillon /*
1477b06ebda0SMatthew Dillon * Create new hook. There can only be one.
1478d2902f79SSascha Wildner * Netgraph context.
1479b06ebda0SMatthew Dillon */
1480b06ebda0SMatthew Dillon
1481b06ebda0SMatthew Dillon static int
ng_ubt_newhook(node_p node,hook_p hook,char const * name)1482b06ebda0SMatthew Dillon ng_ubt_newhook(node_p node, hook_p hook, char const *name)
1483b06ebda0SMatthew Dillon {
1484d2902f79SSascha Wildner struct ubt_softc *sc = NG_NODE_PRIVATE(node);
1485b06ebda0SMatthew Dillon
1486b06ebda0SMatthew Dillon if (strcmp(name, NG_UBT_HOOK) != 0)
1487b06ebda0SMatthew Dillon return (EINVAL);
1488b06ebda0SMatthew Dillon
1489d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1490d2902f79SSascha Wildner if (sc->sc_hook != NULL) {
1491d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1492d2902f79SSascha Wildner
1493b06ebda0SMatthew Dillon return (EISCONN);
1494d2902f79SSascha Wildner }
1495b06ebda0SMatthew Dillon
1496b06ebda0SMatthew Dillon sc->sc_hook = hook;
1497d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1498b06ebda0SMatthew Dillon
1499b06ebda0SMatthew Dillon return (0);
1500b06ebda0SMatthew Dillon } /* ng_ubt_newhook */
1501b06ebda0SMatthew Dillon
1502b06ebda0SMatthew Dillon /*
1503d2902f79SSascha Wildner * Connect hook. Start incoming USB transfers.
1504d2902f79SSascha Wildner * Netgraph context.
1505b06ebda0SMatthew Dillon */
1506b06ebda0SMatthew Dillon
1507b06ebda0SMatthew Dillon static int
ng_ubt_connect(hook_p hook)1508b06ebda0SMatthew Dillon ng_ubt_connect(hook_p hook)
1509b06ebda0SMatthew Dillon {
1510d2902f79SSascha Wildner struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1511b06ebda0SMatthew Dillon
1512b06ebda0SMatthew Dillon NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
1513b06ebda0SMatthew Dillon
1514d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1515d2902f79SSascha Wildner ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
1516d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1517b06ebda0SMatthew Dillon
1518b06ebda0SMatthew Dillon return (0);
1519b06ebda0SMatthew Dillon } /* ng_ubt_connect */
1520b06ebda0SMatthew Dillon
1521b06ebda0SMatthew Dillon /*
1522d2902f79SSascha Wildner * Disconnect hook.
1523d2902f79SSascha Wildner * Netgraph context.
1524b06ebda0SMatthew Dillon */
1525b06ebda0SMatthew Dillon
1526b06ebda0SMatthew Dillon static int
ng_ubt_disconnect(hook_p hook)1527b06ebda0SMatthew Dillon ng_ubt_disconnect(hook_p hook)
1528b06ebda0SMatthew Dillon {
1529d2902f79SSascha Wildner struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1530b06ebda0SMatthew Dillon
1531d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1532d2902f79SSascha Wildner
1533d2902f79SSascha Wildner if (hook != sc->sc_hook) {
1534d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1535d2902f79SSascha Wildner
1536b06ebda0SMatthew Dillon return (EINVAL);
1537b06ebda0SMatthew Dillon }
1538b06ebda0SMatthew Dillon
1539d2902f79SSascha Wildner sc->sc_hook = NULL;
1540d2902f79SSascha Wildner
1541d2902f79SSascha Wildner /* Kick off task to stop all USB xfers */
1542d2902f79SSascha Wildner ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
1543d2902f79SSascha Wildner
1544d2902f79SSascha Wildner /* Drain queues */
1545d2902f79SSascha Wildner NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
1546d2902f79SSascha Wildner NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
1547d2902f79SSascha Wildner NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
1548d2902f79SSascha Wildner
1549d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1550d2902f79SSascha Wildner
1551b06ebda0SMatthew Dillon return (0);
1552b06ebda0SMatthew Dillon } /* ng_ubt_disconnect */
1553b06ebda0SMatthew Dillon
1554b06ebda0SMatthew Dillon /*
1555d2902f79SSascha Wildner * Process control message.
1556d2902f79SSascha Wildner * Netgraph context.
1557b06ebda0SMatthew Dillon */
1558b06ebda0SMatthew Dillon
1559b06ebda0SMatthew Dillon static int
ng_ubt_rcvmsg(node_p node,item_p item,hook_p lasthook)1560b06ebda0SMatthew Dillon ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
1561b06ebda0SMatthew Dillon {
1562d2902f79SSascha Wildner struct ubt_softc *sc = NG_NODE_PRIVATE(node);
1563d2902f79SSascha Wildner struct ng_mesg *msg, *rsp = NULL;
1564d2902f79SSascha Wildner struct ng_bt_mbufq *q;
1565b06ebda0SMatthew Dillon int error = 0, queue, qlen;
1566b06ebda0SMatthew Dillon
1567b06ebda0SMatthew Dillon NGI_GET_MSG(item, msg);
1568b06ebda0SMatthew Dillon
1569b06ebda0SMatthew Dillon switch (msg->header.typecookie) {
1570b06ebda0SMatthew Dillon case NGM_GENERIC_COOKIE:
1571b06ebda0SMatthew Dillon switch (msg->header.cmd) {
1572b06ebda0SMatthew Dillon case NGM_TEXT_STATUS:
15735a975a3dSMatthew Dillon NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_WAITOK | M_NULLOK);
1574d2902f79SSascha Wildner if (rsp == NULL) {
1575b06ebda0SMatthew Dillon error = ENOMEM;
1576d2902f79SSascha Wildner break;
1577d2902f79SSascha Wildner }
1578d2902f79SSascha Wildner
1579a62226e4SSascha Wildner ksnprintf(rsp->data, NG_TEXTRESPONSE,
1580b06ebda0SMatthew Dillon "Hook: %s\n" \
1581d2902f79SSascha Wildner "Task flags: %#x\n" \
1582b06ebda0SMatthew Dillon "Debug: %d\n" \
1583b06ebda0SMatthew Dillon "CMD queue: [have:%d,max:%d]\n" \
1584b06ebda0SMatthew Dillon "ACL queue: [have:%d,max:%d]\n" \
1585b06ebda0SMatthew Dillon "SCO queue: [have:%d,max:%d]",
1586b06ebda0SMatthew Dillon (sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
1587d2902f79SSascha Wildner sc->sc_task_flags,
1588b06ebda0SMatthew Dillon sc->sc_debug,
1589d2902f79SSascha Wildner sc->sc_cmdq.len,
1590b06ebda0SMatthew Dillon sc->sc_cmdq.maxlen,
1591d2902f79SSascha Wildner sc->sc_aclq.len,
1592b06ebda0SMatthew Dillon sc->sc_aclq.maxlen,
1593d2902f79SSascha Wildner sc->sc_scoq.len,
1594b06ebda0SMatthew Dillon sc->sc_scoq.maxlen);
1595b06ebda0SMatthew Dillon break;
1596b06ebda0SMatthew Dillon
1597b06ebda0SMatthew Dillon default:
1598b06ebda0SMatthew Dillon error = EINVAL;
1599b06ebda0SMatthew Dillon break;
1600b06ebda0SMatthew Dillon }
1601b06ebda0SMatthew Dillon break;
1602b06ebda0SMatthew Dillon
1603b06ebda0SMatthew Dillon case NGM_UBT_COOKIE:
1604b06ebda0SMatthew Dillon switch (msg->header.cmd) {
1605b06ebda0SMatthew Dillon case NGM_UBT_NODE_SET_DEBUG:
1606d2902f79SSascha Wildner if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
1607b06ebda0SMatthew Dillon error = EMSGSIZE;
1608d2902f79SSascha Wildner break;
1609d2902f79SSascha Wildner }
1610d2902f79SSascha Wildner
1611d2902f79SSascha Wildner sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
1612b06ebda0SMatthew Dillon break;
1613b06ebda0SMatthew Dillon
1614b06ebda0SMatthew Dillon case NGM_UBT_NODE_GET_DEBUG:
1615b06ebda0SMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
16165a975a3dSMatthew Dillon M_WAITOK | M_NULLOK);
1617d2902f79SSascha Wildner if (rsp == NULL) {
1618b06ebda0SMatthew Dillon error = ENOMEM;
1619d2902f79SSascha Wildner break;
1620d2902f79SSascha Wildner }
1621d2902f79SSascha Wildner
1622d2902f79SSascha Wildner *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
1623b06ebda0SMatthew Dillon break;
1624b06ebda0SMatthew Dillon
1625b06ebda0SMatthew Dillon case NGM_UBT_NODE_SET_QLEN:
1626d2902f79SSascha Wildner if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1627b06ebda0SMatthew Dillon error = EMSGSIZE;
1628b06ebda0SMatthew Dillon break;
1629b06ebda0SMatthew Dillon }
1630b06ebda0SMatthew Dillon
1631d2902f79SSascha Wildner queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1632d2902f79SSascha Wildner qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
1633d2902f79SSascha Wildner
1634b06ebda0SMatthew Dillon switch (queue) {
1635b06ebda0SMatthew Dillon case NGM_UBT_NODE_QUEUE_CMD:
1636b06ebda0SMatthew Dillon q = &sc->sc_cmdq;
1637b06ebda0SMatthew Dillon break;
1638b06ebda0SMatthew Dillon
1639b06ebda0SMatthew Dillon case NGM_UBT_NODE_QUEUE_ACL:
1640b06ebda0SMatthew Dillon q = &sc->sc_aclq;
1641b06ebda0SMatthew Dillon break;
1642b06ebda0SMatthew Dillon
1643b06ebda0SMatthew Dillon case NGM_UBT_NODE_QUEUE_SCO:
1644b06ebda0SMatthew Dillon q = &sc->sc_scoq;
1645b06ebda0SMatthew Dillon break;
1646b06ebda0SMatthew Dillon
1647b06ebda0SMatthew Dillon default:
1648b06ebda0SMatthew Dillon error = EINVAL;
1649d2902f79SSascha Wildner goto done;
1650d2902f79SSascha Wildner /* NOT REACHED */
1651b06ebda0SMatthew Dillon }
1652b06ebda0SMatthew Dillon
1653b06ebda0SMatthew Dillon q->maxlen = qlen;
1654b06ebda0SMatthew Dillon break;
1655b06ebda0SMatthew Dillon
1656b06ebda0SMatthew Dillon case NGM_UBT_NODE_GET_QLEN:
1657b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
1658b06ebda0SMatthew Dillon error = EMSGSIZE;
1659b06ebda0SMatthew Dillon break;
1660b06ebda0SMatthew Dillon }
1661b06ebda0SMatthew Dillon
1662b06ebda0SMatthew Dillon queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
1663d2902f79SSascha Wildner
1664b06ebda0SMatthew Dillon switch (queue) {
1665b06ebda0SMatthew Dillon case NGM_UBT_NODE_QUEUE_CMD:
1666b06ebda0SMatthew Dillon q = &sc->sc_cmdq;
1667b06ebda0SMatthew Dillon break;
1668b06ebda0SMatthew Dillon
1669b06ebda0SMatthew Dillon case NGM_UBT_NODE_QUEUE_ACL:
1670b06ebda0SMatthew Dillon q = &sc->sc_aclq;
1671b06ebda0SMatthew Dillon break;
1672b06ebda0SMatthew Dillon
1673b06ebda0SMatthew Dillon case NGM_UBT_NODE_QUEUE_SCO:
1674b06ebda0SMatthew Dillon q = &sc->sc_scoq;
1675b06ebda0SMatthew Dillon break;
1676b06ebda0SMatthew Dillon
1677b06ebda0SMatthew Dillon default:
1678b06ebda0SMatthew Dillon error = EINVAL;
1679d2902f79SSascha Wildner goto done;
1680d2902f79SSascha Wildner /* NOT REACHED */
1681b06ebda0SMatthew Dillon }
1682b06ebda0SMatthew Dillon
1683d2902f79SSascha Wildner NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
1684d2902f79SSascha Wildner M_WAITOK | M_NULLOK);
1685b06ebda0SMatthew Dillon if (rsp == NULL) {
1686b06ebda0SMatthew Dillon error = ENOMEM;
1687b06ebda0SMatthew Dillon break;
1688b06ebda0SMatthew Dillon }
1689b06ebda0SMatthew Dillon
1690d2902f79SSascha Wildner ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
1691d2902f79SSascha Wildner ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
1692b06ebda0SMatthew Dillon break;
1693b06ebda0SMatthew Dillon
1694b06ebda0SMatthew Dillon case NGM_UBT_NODE_GET_STAT:
1695b06ebda0SMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
16965a975a3dSMatthew Dillon M_WAITOK | M_NULLOK);
1697d2902f79SSascha Wildner if (rsp == NULL) {
1698b06ebda0SMatthew Dillon error = ENOMEM;
1699d2902f79SSascha Wildner break;
1700d2902f79SSascha Wildner }
1701d2902f79SSascha Wildner
1702b06ebda0SMatthew Dillon bcopy(&sc->sc_stat, rsp->data,
1703b06ebda0SMatthew Dillon sizeof(ng_ubt_node_stat_ep));
1704b06ebda0SMatthew Dillon break;
1705b06ebda0SMatthew Dillon
1706b06ebda0SMatthew Dillon case NGM_UBT_NODE_RESET_STAT:
1707d2902f79SSascha Wildner UBT_STAT_RESET(sc);
1708b06ebda0SMatthew Dillon break;
1709b06ebda0SMatthew Dillon
1710b06ebda0SMatthew Dillon default:
1711b06ebda0SMatthew Dillon error = EINVAL;
1712b06ebda0SMatthew Dillon break;
1713b06ebda0SMatthew Dillon }
1714b06ebda0SMatthew Dillon break;
1715b06ebda0SMatthew Dillon
1716b06ebda0SMatthew Dillon default:
1717b06ebda0SMatthew Dillon error = EINVAL;
1718b06ebda0SMatthew Dillon break;
1719b06ebda0SMatthew Dillon }
1720d2902f79SSascha Wildner done:
1721b06ebda0SMatthew Dillon NG_RESPOND_MSG(error, node, item, rsp);
1722b06ebda0SMatthew Dillon NG_FREE_MSG(msg);
1723b06ebda0SMatthew Dillon
1724b06ebda0SMatthew Dillon return (error);
1725b06ebda0SMatthew Dillon } /* ng_ubt_rcvmsg */
1726b06ebda0SMatthew Dillon
1727b06ebda0SMatthew Dillon /*
1728d2902f79SSascha Wildner * Process data.
1729d2902f79SSascha Wildner * Netgraph context.
1730b06ebda0SMatthew Dillon */
1731b06ebda0SMatthew Dillon
1732b06ebda0SMatthew Dillon static int
ng_ubt_rcvdata(hook_p hook,item_p item)1733b06ebda0SMatthew Dillon ng_ubt_rcvdata(hook_p hook, item_p item)
1734b06ebda0SMatthew Dillon {
1735d2902f79SSascha Wildner struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1736d2902f79SSascha Wildner struct mbuf *m;
1737d2902f79SSascha Wildner struct ng_bt_mbufq *q;
1738d2902f79SSascha Wildner int action, error = 0;
1739b06ebda0SMatthew Dillon
1740b06ebda0SMatthew Dillon if (hook != sc->sc_hook) {
1741b06ebda0SMatthew Dillon error = EINVAL;
1742b06ebda0SMatthew Dillon goto done;
1743b06ebda0SMatthew Dillon }
1744b06ebda0SMatthew Dillon
1745b06ebda0SMatthew Dillon /* Deatch mbuf and get HCI frame type */
1746b06ebda0SMatthew Dillon NGI_GET_M(item, m);
1747b06ebda0SMatthew Dillon
1748d2902f79SSascha Wildner /*
1749d2902f79SSascha Wildner * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
1750d2902f79SSascha Wildner * 2 bytes connection handle and at least 1 byte of length.
1751d2902f79SSascha Wildner * Panic on data frame that has size smaller than 4 bytes (it
1752d2902f79SSascha Wildner * should not happen)
1753d2902f79SSascha Wildner */
1754d2902f79SSascha Wildner
1755d2902f79SSascha Wildner if (m->m_pkthdr.len < 4)
1756d2902f79SSascha Wildner panic("HCI frame size is too small! pktlen=%d\n",
1757d2902f79SSascha Wildner m->m_pkthdr.len);
1758d2902f79SSascha Wildner
1759b06ebda0SMatthew Dillon /* Process HCI frame */
1760d2902f79SSascha Wildner switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */
1761b06ebda0SMatthew Dillon case NG_HCI_CMD_PKT:
1762d2902f79SSascha Wildner if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
1763d2902f79SSascha Wildner panic("HCI command frame size is too big! " \
1764d2902f79SSascha Wildner "buffer size=%zd, packet len=%d\n",
1765d2902f79SSascha Wildner UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
1766d2902f79SSascha Wildner
1767b06ebda0SMatthew Dillon q = &sc->sc_cmdq;
1768d2902f79SSascha Wildner action = UBT_FLAG_T_START_CTRL;
1769b06ebda0SMatthew Dillon break;
1770b06ebda0SMatthew Dillon
1771b06ebda0SMatthew Dillon case NG_HCI_ACL_DATA_PKT:
1772d2902f79SSascha Wildner if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
1773d2902f79SSascha Wildner panic("ACL data frame size is too big! " \
1774d2902f79SSascha Wildner "buffer size=%d, packet len=%d\n",
1775d2902f79SSascha Wildner UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
1776d2902f79SSascha Wildner
1777b06ebda0SMatthew Dillon q = &sc->sc_aclq;
1778d2902f79SSascha Wildner action = UBT_FLAG_T_START_BULK;
1779b06ebda0SMatthew Dillon break;
1780b06ebda0SMatthew Dillon
1781b06ebda0SMatthew Dillon case NG_HCI_SCO_DATA_PKT:
1782b06ebda0SMatthew Dillon q = &sc->sc_scoq;
1783d2902f79SSascha Wildner action = 0;
1784b06ebda0SMatthew Dillon break;
1785b06ebda0SMatthew Dillon
1786b06ebda0SMatthew Dillon default:
1787d2902f79SSascha Wildner UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
1788d2902f79SSascha Wildner "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
1789b06ebda0SMatthew Dillon
1790b06ebda0SMatthew Dillon NG_FREE_M(m);
1791b06ebda0SMatthew Dillon error = EINVAL;
1792b06ebda0SMatthew Dillon goto done;
1793b06ebda0SMatthew Dillon /* NOT REACHED */
1794b06ebda0SMatthew Dillon }
1795b06ebda0SMatthew Dillon
1796d2902f79SSascha Wildner UBT_NG_LOCK(sc);
1797b06ebda0SMatthew Dillon if (NG_BT_MBUFQ_FULL(q)) {
1798d2902f79SSascha Wildner NG_BT_MBUFQ_DROP(q);
1799d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1800d2902f79SSascha Wildner
1801d2902f79SSascha Wildner UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
1802d2902f79SSascha Wildner *mtod(m, uint8_t *), m->m_pkthdr.len);
1803b06ebda0SMatthew Dillon
1804b06ebda0SMatthew Dillon NG_FREE_M(m);
1805d2902f79SSascha Wildner } else {
1806d2902f79SSascha Wildner /* Loose HCI packet type, enqueue mbuf and kick off task */
1807d2902f79SSascha Wildner m_adj(m, sizeof(uint8_t));
1808b06ebda0SMatthew Dillon NG_BT_MBUFQ_ENQUEUE(q, m);
1809d2902f79SSascha Wildner ubt_task_schedule(sc, action);
1810d2902f79SSascha Wildner UBT_NG_UNLOCK(sc);
1811d2902f79SSascha Wildner }
1812b06ebda0SMatthew Dillon done:
1813b06ebda0SMatthew Dillon NG_FREE_ITEM(item);
1814b06ebda0SMatthew Dillon
1815b06ebda0SMatthew Dillon return (error);
1816b06ebda0SMatthew Dillon } /* ng_ubt_rcvdata */
1817b06ebda0SMatthew Dillon
1818d2902f79SSascha Wildner /****************************************************************************
1819d2902f79SSascha Wildner ****************************************************************************
1820d2902f79SSascha Wildner ** Module
1821d2902f79SSascha Wildner ****************************************************************************
1822d2902f79SSascha Wildner ****************************************************************************/
1823d2902f79SSascha Wildner
1824d2902f79SSascha Wildner /*
1825d2902f79SSascha Wildner * Load/Unload the driver module
1826d2902f79SSascha Wildner */
1827d2902f79SSascha Wildner
1828d2902f79SSascha Wildner static int
ubt_modevent(module_t mod,int event,void * data)1829d2902f79SSascha Wildner ubt_modevent(module_t mod, int event, void *data)
1830d2902f79SSascha Wildner {
1831d2902f79SSascha Wildner int error;
1832d2902f79SSascha Wildner
1833d2902f79SSascha Wildner switch (event) {
1834d2902f79SSascha Wildner case MOD_LOAD:
1835d2902f79SSascha Wildner error = ng_newtype(&typestruct);
1836d2902f79SSascha Wildner if (error != 0)
1837a62226e4SSascha Wildner kprintf("%s: Could not register Netgraph node type, " \
1838d2902f79SSascha Wildner "error=%d\n", NG_UBT_NODE_TYPE, error);
1839d2902f79SSascha Wildner break;
1840d2902f79SSascha Wildner
1841d2902f79SSascha Wildner case MOD_UNLOAD:
1842d2902f79SSascha Wildner error = ng_rmtype(&typestruct);
1843d2902f79SSascha Wildner break;
1844d2902f79SSascha Wildner
1845d2902f79SSascha Wildner default:
1846d2902f79SSascha Wildner error = EOPNOTSUPP;
1847d2902f79SSascha Wildner break;
1848d2902f79SSascha Wildner }
1849d2902f79SSascha Wildner
1850d2902f79SSascha Wildner return (error);
1851d2902f79SSascha Wildner } /* ubt_modevent */
1852d2902f79SSascha Wildner
1853d2902f79SSascha Wildner static devclass_t ubt_devclass;
1854d2902f79SSascha Wildner
1855d2902f79SSascha Wildner static device_method_t ubt_methods[] =
1856d2902f79SSascha Wildner {
1857d2902f79SSascha Wildner DEVMETHOD(device_probe, ubt_probe),
1858d2902f79SSascha Wildner DEVMETHOD(device_attach, ubt_attach),
1859d2902f79SSascha Wildner DEVMETHOD(device_detach, ubt_detach),
1860d2902f79SSascha Wildner
1861d2902f79SSascha Wildner DEVMETHOD_END
1862d2902f79SSascha Wildner };
1863d2902f79SSascha Wildner
1864d2902f79SSascha Wildner static driver_t ubt_driver =
1865d2902f79SSascha Wildner {
1866d2902f79SSascha Wildner .name = "ubt",
1867d2902f79SSascha Wildner .methods = ubt_methods,
1868d2902f79SSascha Wildner .size = sizeof(struct ubt_softc),
1869d2902f79SSascha Wildner };
1870d2902f79SSascha Wildner
1871d2902f79SSascha Wildner DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, NULL);
1872d2902f79SSascha Wildner MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
1873d2902f79SSascha Wildner MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
1874d2902f79SSascha Wildner MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
1875d2902f79SSascha Wildner MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
1876d2902f79SSascha Wildner
1877