1b06ebda0SMatthew Dillon /*
2b06ebda0SMatthew Dillon * ng_hci_main.c
3b06ebda0SMatthew Dillon */
4b06ebda0SMatthew Dillon
5b06ebda0SMatthew Dillon /*-
6b06ebda0SMatthew Dillon * Copyright (c) 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_hci_main.c,v 1.2 2003/03/18 00:09:36 max Exp $
31b06ebda0SMatthew Dillon * $FreeBSD: src/sys/netgraph/bluetooth/hci/ng_hci_main.c,v 1.6 2005/01/07 01:45:43 imp Exp $
32b06ebda0SMatthew Dillon */
33b06ebda0SMatthew Dillon
34b06ebda0SMatthew Dillon #include <sys/param.h>
35b06ebda0SMatthew Dillon #include <sys/systm.h>
36b06ebda0SMatthew Dillon #include <sys/kernel.h>
37b06ebda0SMatthew Dillon #include <sys/endian.h>
38b06ebda0SMatthew Dillon #include <sys/malloc.h>
39b06ebda0SMatthew Dillon #include <sys/mbuf.h>
40b06ebda0SMatthew Dillon #include <sys/queue.h>
41*e85b99abSSascha Wildner #include <sys/refcount.h>
42*e85b99abSSascha Wildner #include <netgraph7/ng_message.h>
43*e85b99abSSascha Wildner #include <netgraph7/netgraph.h>
44*e85b99abSSascha Wildner #include <netgraph7/netgraph2.h>
45*e85b99abSSascha Wildner #include <netgraph7/ng_parse.h>
46*e85b99abSSascha Wildner #include <netgraph7/bluetooth/include/ng_bluetooth.h>
47*e85b99abSSascha Wildner #include <netgraph7/bluetooth/include/ng_hci.h>
48*e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_var.h>
49*e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_prse.h>
50*e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_cmds.h>
51*e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_evnt.h>
52*e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_ulpi.h>
53*e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_misc.h>
54b06ebda0SMatthew Dillon
55b06ebda0SMatthew Dillon /******************************************************************************
56b06ebda0SMatthew Dillon ******************************************************************************
57b06ebda0SMatthew Dillon ** This node implements Bluetooth Host Controller Interface (HCI)
58b06ebda0SMatthew Dillon ******************************************************************************
59b06ebda0SMatthew Dillon ******************************************************************************/
60b06ebda0SMatthew Dillon
61b06ebda0SMatthew Dillon /* MALLOC define */
62b06ebda0SMatthew Dillon #ifdef NG_SEPARATE_MALLOC
63b06ebda0SMatthew Dillon MALLOC_DEFINE(M_NETGRAPH_HCI, "netgraph_hci", "Netgraph Bluetooth HCI node");
64b06ebda0SMatthew Dillon #else
65b06ebda0SMatthew Dillon #define M_NETGRAPH_HCI M_NETGRAPH
66b06ebda0SMatthew Dillon #endif /* NG_SEPARATE_MALLOC */
67b06ebda0SMatthew Dillon
68b06ebda0SMatthew Dillon /* Netgraph node methods */
69b06ebda0SMatthew Dillon static ng_constructor_t ng_hci_constructor;
70b06ebda0SMatthew Dillon static ng_shutdown_t ng_hci_shutdown;
71b06ebda0SMatthew Dillon static ng_newhook_t ng_hci_newhook;
72b06ebda0SMatthew Dillon static ng_connect_t ng_hci_connect;
73b06ebda0SMatthew Dillon static ng_disconnect_t ng_hci_disconnect;
74b06ebda0SMatthew Dillon static ng_rcvmsg_t ng_hci_default_rcvmsg;
75b06ebda0SMatthew Dillon static ng_rcvmsg_t ng_hci_upper_rcvmsg;
76b06ebda0SMatthew Dillon static ng_rcvdata_t ng_hci_drv_rcvdata;
77b06ebda0SMatthew Dillon static ng_rcvdata_t ng_hci_acl_rcvdata;
78b06ebda0SMatthew Dillon static ng_rcvdata_t ng_hci_sco_rcvdata;
79b06ebda0SMatthew Dillon static ng_rcvdata_t ng_hci_raw_rcvdata;
80b06ebda0SMatthew Dillon
81b06ebda0SMatthew Dillon /* Netgraph node type descriptor */
82b06ebda0SMatthew Dillon static struct ng_type typestruct = {
83b06ebda0SMatthew Dillon .version = NG_ABI_VERSION,
84b06ebda0SMatthew Dillon .name = NG_HCI_NODE_TYPE,
85b06ebda0SMatthew Dillon .constructor = ng_hci_constructor,
86b06ebda0SMatthew Dillon .rcvmsg = ng_hci_default_rcvmsg,
87b06ebda0SMatthew Dillon .shutdown = ng_hci_shutdown,
88b06ebda0SMatthew Dillon .newhook = ng_hci_newhook,
89b06ebda0SMatthew Dillon .connect = ng_hci_connect,
90b06ebda0SMatthew Dillon .rcvdata = ng_hci_drv_rcvdata,
91b06ebda0SMatthew Dillon .disconnect = ng_hci_disconnect,
92b06ebda0SMatthew Dillon .cmdlist = ng_hci_cmdlist,
93b06ebda0SMatthew Dillon };
94b06ebda0SMatthew Dillon NETGRAPH_INIT(hci, &typestruct);
95b06ebda0SMatthew Dillon MODULE_VERSION(ng_hci, NG_BLUETOOTH_VERSION);
96b06ebda0SMatthew Dillon MODULE_DEPEND(ng_hci, ng_bluetooth, NG_BLUETOOTH_VERSION,
97b06ebda0SMatthew Dillon NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
98b06ebda0SMatthew Dillon
99b06ebda0SMatthew Dillon /*****************************************************************************
100b06ebda0SMatthew Dillon *****************************************************************************
101b06ebda0SMatthew Dillon ** Netgraph methods implementation
102b06ebda0SMatthew Dillon *****************************************************************************
103b06ebda0SMatthew Dillon *****************************************************************************/
104b06ebda0SMatthew Dillon
105b06ebda0SMatthew Dillon /*
106b06ebda0SMatthew Dillon * Create new instance of HCI node (new unit)
107b06ebda0SMatthew Dillon */
108b06ebda0SMatthew Dillon
109b06ebda0SMatthew Dillon static int
ng_hci_constructor(node_p node)110b06ebda0SMatthew Dillon ng_hci_constructor(node_p node)
111b06ebda0SMatthew Dillon {
112b06ebda0SMatthew Dillon ng_hci_unit_p unit = NULL;
113b06ebda0SMatthew Dillon
114fc025606SSascha Wildner unit = kmalloc(sizeof(*unit), M_NETGRAPH_HCI,
1155a975a3dSMatthew Dillon M_WAITOK | M_NULLOK | M_ZERO);
116b06ebda0SMatthew Dillon if (unit == NULL)
117b06ebda0SMatthew Dillon return (ENOMEM);
118b06ebda0SMatthew Dillon
119b06ebda0SMatthew Dillon unit->node = node;
120b06ebda0SMatthew Dillon unit->debug = NG_HCI_WARN_LEVEL;
121b06ebda0SMatthew Dillon
122b06ebda0SMatthew Dillon unit->link_policy_mask = 0xffff; /* Enable all supported modes */
123b06ebda0SMatthew Dillon unit->packet_mask = 0xffff; /* Enable all packet types */
124b06ebda0SMatthew Dillon unit->role_switch = 1; /* Enable role switch (if device supports it) */
125b06ebda0SMatthew Dillon
126b06ebda0SMatthew Dillon /*
127b06ebda0SMatthew Dillon * Set default buffer info
128b06ebda0SMatthew Dillon *
129b06ebda0SMatthew Dillon * One HCI command
130b06ebda0SMatthew Dillon * One ACL packet with max. size of 17 bytes (1 DM1 packet)
131b06ebda0SMatthew Dillon * One SCO packet with max. size of 10 bytes (1 HV1 packet)
132b06ebda0SMatthew Dillon */
133b06ebda0SMatthew Dillon
134b06ebda0SMatthew Dillon NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
135b06ebda0SMatthew Dillon NG_HCI_BUFF_ACL_SET(unit->buffer, 1, 17, 1);
136b06ebda0SMatthew Dillon NG_HCI_BUFF_SCO_SET(unit->buffer, 1, 10, 1);
137b06ebda0SMatthew Dillon
138b06ebda0SMatthew Dillon /* Init command queue & command timeout handler */
139b06ebda0SMatthew Dillon ng_callout_init(&unit->cmd_timo);
140b06ebda0SMatthew Dillon NG_BT_MBUFQ_INIT(&unit->cmdq, NG_HCI_CMD_QUEUE_LEN);
141b06ebda0SMatthew Dillon
142b06ebda0SMatthew Dillon /* Init lists */
143b06ebda0SMatthew Dillon LIST_INIT(&unit->con_list);
144b06ebda0SMatthew Dillon LIST_INIT(&unit->neighbors);
145b06ebda0SMatthew Dillon
146b06ebda0SMatthew Dillon /*
147b06ebda0SMatthew Dillon * This node has to be a WRITER because both data and messages
148b06ebda0SMatthew Dillon * can change node state.
149b06ebda0SMatthew Dillon */
150b06ebda0SMatthew Dillon
151b06ebda0SMatthew Dillon NG_NODE_FORCE_WRITER(node);
152b06ebda0SMatthew Dillon NG_NODE_SET_PRIVATE(node, unit);
153b06ebda0SMatthew Dillon
154b06ebda0SMatthew Dillon return (0);
155b06ebda0SMatthew Dillon } /* ng_hci_constructor */
156b06ebda0SMatthew Dillon
157b06ebda0SMatthew Dillon /*
158b06ebda0SMatthew Dillon * Destroy the node
159b06ebda0SMatthew Dillon */
160b06ebda0SMatthew Dillon
161b06ebda0SMatthew Dillon static int
ng_hci_shutdown(node_p node)162b06ebda0SMatthew Dillon ng_hci_shutdown(node_p node)
163b06ebda0SMatthew Dillon {
164b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
165b06ebda0SMatthew Dillon
166b06ebda0SMatthew Dillon NG_NODE_SET_PRIVATE(node, NULL);
167b06ebda0SMatthew Dillon NG_NODE_UNREF(node);
168b06ebda0SMatthew Dillon
169b06ebda0SMatthew Dillon unit->node = NULL;
170b06ebda0SMatthew Dillon ng_hci_unit_clean(unit, 0x16 /* Connection terminated by local host */);
171b06ebda0SMatthew Dillon
172b06ebda0SMatthew Dillon NG_BT_MBUFQ_DESTROY(&unit->cmdq);
173b06ebda0SMatthew Dillon
174b06ebda0SMatthew Dillon bzero(unit, sizeof(*unit));
175fc025606SSascha Wildner kfree(unit, M_NETGRAPH_HCI);
176b06ebda0SMatthew Dillon
177b06ebda0SMatthew Dillon return (0);
178b06ebda0SMatthew Dillon } /* ng_hci_shutdown */
179b06ebda0SMatthew Dillon
180b06ebda0SMatthew Dillon /*
181b06ebda0SMatthew Dillon * Give our OK for a hook to be added. Unit driver is connected to the driver
182b06ebda0SMatthew Dillon * (NG_HCI_HOOK_DRV) hook. Upper layer protocols are connected to appropriate
183b06ebda0SMatthew Dillon * (NG_HCI_HOOK_ACL or NG_HCI_HOOK_SCO) hooks.
184b06ebda0SMatthew Dillon */
185b06ebda0SMatthew Dillon
186b06ebda0SMatthew Dillon static int
ng_hci_newhook(node_p node,hook_p hook,char const * name)187b06ebda0SMatthew Dillon ng_hci_newhook(node_p node, hook_p hook, char const *name)
188b06ebda0SMatthew Dillon {
189b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
190b06ebda0SMatthew Dillon hook_p *h = NULL;
191b06ebda0SMatthew Dillon
192b06ebda0SMatthew Dillon if (strcmp(name, NG_HCI_HOOK_DRV) == 0)
193b06ebda0SMatthew Dillon h = &unit->drv;
194b06ebda0SMatthew Dillon else if (strcmp(name, NG_HCI_HOOK_ACL) == 0)
195b06ebda0SMatthew Dillon h = &unit->acl;
196b06ebda0SMatthew Dillon else if (strcmp(name, NG_HCI_HOOK_SCO) == 0)
197b06ebda0SMatthew Dillon h = &unit->sco;
198b06ebda0SMatthew Dillon else if (strcmp(name, NG_HCI_HOOK_RAW) == 0)
199b06ebda0SMatthew Dillon h = &unit->raw;
200b06ebda0SMatthew Dillon else
201b06ebda0SMatthew Dillon return (EINVAL);
202b06ebda0SMatthew Dillon
203b06ebda0SMatthew Dillon if (*h != NULL)
204b06ebda0SMatthew Dillon return (EISCONN);
205b06ebda0SMatthew Dillon
206b06ebda0SMatthew Dillon *h = hook;
207b06ebda0SMatthew Dillon
208b06ebda0SMatthew Dillon return (0);
209b06ebda0SMatthew Dillon } /* ng_hci_newhook */
210b06ebda0SMatthew Dillon
211b06ebda0SMatthew Dillon /*
212b06ebda0SMatthew Dillon * Give our final OK to connect hook
213b06ebda0SMatthew Dillon */
214b06ebda0SMatthew Dillon
215b06ebda0SMatthew Dillon static int
ng_hci_connect(hook_p hook)216b06ebda0SMatthew Dillon ng_hci_connect(hook_p hook)
217b06ebda0SMatthew Dillon {
218b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
219b06ebda0SMatthew Dillon
220b06ebda0SMatthew Dillon if (hook != unit->drv) {
221b06ebda0SMatthew Dillon if (hook == unit->acl) {
222b06ebda0SMatthew Dillon NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);
223b06ebda0SMatthew Dillon NG_HOOK_SET_RCVDATA(hook, ng_hci_acl_rcvdata);
224b06ebda0SMatthew Dillon } else if (hook == unit->sco) {
225b06ebda0SMatthew Dillon NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);
226b06ebda0SMatthew Dillon NG_HOOK_SET_RCVDATA(hook, ng_hci_sco_rcvdata);
227b06ebda0SMatthew Dillon } else
228b06ebda0SMatthew Dillon NG_HOOK_SET_RCVDATA(hook, ng_hci_raw_rcvdata);
229b06ebda0SMatthew Dillon
230b06ebda0SMatthew Dillon /* Send delayed notification to the upper layers */
231b06ebda0SMatthew Dillon if (hook != unit->raw)
232b06ebda0SMatthew Dillon ng_send_fn(unit->node, hook, ng_hci_node_is_up, NULL,0);
233b06ebda0SMatthew Dillon } else
234b06ebda0SMatthew Dillon unit->state |= NG_HCI_UNIT_CONNECTED;
235b06ebda0SMatthew Dillon
236b06ebda0SMatthew Dillon return (0);
237b06ebda0SMatthew Dillon } /* ng_hci_connect */
238b06ebda0SMatthew Dillon
239b06ebda0SMatthew Dillon /*
240b06ebda0SMatthew Dillon * Disconnect the hook
241b06ebda0SMatthew Dillon */
242b06ebda0SMatthew Dillon
243b06ebda0SMatthew Dillon static int
ng_hci_disconnect(hook_p hook)244b06ebda0SMatthew Dillon ng_hci_disconnect(hook_p hook)
245b06ebda0SMatthew Dillon {
246b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
247b06ebda0SMatthew Dillon
248b06ebda0SMatthew Dillon if (hook == unit->acl)
249b06ebda0SMatthew Dillon unit->acl = NULL;
250b06ebda0SMatthew Dillon else if (hook == unit->sco)
251b06ebda0SMatthew Dillon unit->sco = NULL;
252b06ebda0SMatthew Dillon else if (hook == unit->raw)
253b06ebda0SMatthew Dillon unit->raw = NULL;
254b06ebda0SMatthew Dillon else if (hook == unit->drv) {
255b06ebda0SMatthew Dillon unit->drv = NULL;
256b06ebda0SMatthew Dillon
257b06ebda0SMatthew Dillon /* Connection terminated by local host */
258b06ebda0SMatthew Dillon ng_hci_unit_clean(unit, 0x16);
259b06ebda0SMatthew Dillon unit->state &= ~(NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED);
260b06ebda0SMatthew Dillon } else
261b06ebda0SMatthew Dillon return (EINVAL);
262b06ebda0SMatthew Dillon
263b06ebda0SMatthew Dillon /* Shutdown when all hooks are disconnected */
264b06ebda0SMatthew Dillon if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
265b06ebda0SMatthew Dillon (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
266b06ebda0SMatthew Dillon ng_rmnode_self(NG_HOOK_NODE(hook));
267b06ebda0SMatthew Dillon
268b06ebda0SMatthew Dillon return (0);
269b06ebda0SMatthew Dillon } /* ng_hci_disconnect */
270b06ebda0SMatthew Dillon
271b06ebda0SMatthew Dillon /*
272b06ebda0SMatthew Dillon * Default control message processing routine. Control message could be:
273b06ebda0SMatthew Dillon *
274b06ebda0SMatthew Dillon * 1) GENERIC Netgraph messages
275b06ebda0SMatthew Dillon *
276b06ebda0SMatthew Dillon * 2) Control message directed to the node itself.
277b06ebda0SMatthew Dillon */
278b06ebda0SMatthew Dillon
279b06ebda0SMatthew Dillon static int
ng_hci_default_rcvmsg(node_p node,item_p item,hook_p lasthook)280b06ebda0SMatthew Dillon ng_hci_default_rcvmsg(node_p node, item_p item, hook_p lasthook)
281b06ebda0SMatthew Dillon {
282b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
283b06ebda0SMatthew Dillon struct ng_mesg *msg = NULL, *rsp = NULL;
284b06ebda0SMatthew Dillon int error = 0;
285b06ebda0SMatthew Dillon
286b06ebda0SMatthew Dillon NGI_GET_MSG(item, msg);
287b06ebda0SMatthew Dillon
288b06ebda0SMatthew Dillon switch (msg->header.typecookie) {
289b06ebda0SMatthew Dillon case NGM_GENERIC_COOKIE:
290b06ebda0SMatthew Dillon switch (msg->header.cmd) {
291b06ebda0SMatthew Dillon case NGM_TEXT_STATUS: {
292b06ebda0SMatthew Dillon int cmd_avail,
293b06ebda0SMatthew Dillon acl_total, acl_avail, acl_size,
294b06ebda0SMatthew Dillon sco_total, sco_avail, sco_size;
295b06ebda0SMatthew Dillon
2965a975a3dSMatthew Dillon NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_WAITOK | M_NULLOK);
297b06ebda0SMatthew Dillon if (rsp == NULL) {
298b06ebda0SMatthew Dillon error = ENOMEM;
299b06ebda0SMatthew Dillon break;
300b06ebda0SMatthew Dillon }
301b06ebda0SMatthew Dillon
302b06ebda0SMatthew Dillon NG_HCI_BUFF_CMD_GET(unit->buffer, cmd_avail);
303b06ebda0SMatthew Dillon
304b06ebda0SMatthew Dillon NG_HCI_BUFF_ACL_AVAIL(unit->buffer, acl_avail);
305b06ebda0SMatthew Dillon NG_HCI_BUFF_ACL_TOTAL(unit->buffer, acl_total);
306b06ebda0SMatthew Dillon NG_HCI_BUFF_ACL_SIZE(unit->buffer, acl_size);
307b06ebda0SMatthew Dillon
308b06ebda0SMatthew Dillon NG_HCI_BUFF_SCO_AVAIL(unit->buffer, sco_avail);
309b06ebda0SMatthew Dillon NG_HCI_BUFF_SCO_TOTAL(unit->buffer, sco_total);
310b06ebda0SMatthew Dillon NG_HCI_BUFF_SCO_SIZE(unit->buffer, sco_size);
311b06ebda0SMatthew Dillon
312a62226e4SSascha Wildner ksnprintf(rsp->data, NG_TEXTRESPONSE,
313b06ebda0SMatthew Dillon "bdaddr %x:%x:%x:%x:%x:%x\n" \
314b06ebda0SMatthew Dillon "Hooks %s %s %s %s\n" \
315b06ebda0SMatthew Dillon "State %#x\n" \
316b06ebda0SMatthew Dillon "Queue cmd:%d\n" \
317b06ebda0SMatthew Dillon "Buffer cmd:%d,acl:%d,%d,%d,sco:%d,%d,%d",
318b06ebda0SMatthew Dillon unit->bdaddr.b[5], unit->bdaddr.b[4],
319b06ebda0SMatthew Dillon unit->bdaddr.b[3], unit->bdaddr.b[2],
320b06ebda0SMatthew Dillon unit->bdaddr.b[1], unit->bdaddr.b[0],
321b06ebda0SMatthew Dillon (unit->drv != NULL)? NG_HCI_HOOK_DRV : "",
322b06ebda0SMatthew Dillon (unit->acl != NULL)? NG_HCI_HOOK_ACL : "",
323b06ebda0SMatthew Dillon (unit->sco != NULL)? NG_HCI_HOOK_SCO : "",
324b06ebda0SMatthew Dillon (unit->raw != NULL)? NG_HCI_HOOK_RAW : "",
325b06ebda0SMatthew Dillon unit->state,
326b06ebda0SMatthew Dillon NG_BT_MBUFQ_LEN(&unit->cmdq),
327b06ebda0SMatthew Dillon cmd_avail,
328b06ebda0SMatthew Dillon acl_avail, acl_total, acl_size,
329b06ebda0SMatthew Dillon sco_avail, sco_total, sco_size);
330b06ebda0SMatthew Dillon } break;
331b06ebda0SMatthew Dillon
332b06ebda0SMatthew Dillon default:
333b06ebda0SMatthew Dillon error = EINVAL;
334b06ebda0SMatthew Dillon break;
335b06ebda0SMatthew Dillon }
336b06ebda0SMatthew Dillon break;
337b06ebda0SMatthew Dillon
338b06ebda0SMatthew Dillon case NGM_HCI_COOKIE:
339b06ebda0SMatthew Dillon switch (msg->header.cmd) {
340b06ebda0SMatthew Dillon /* Get current node state */
341b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_STATE:
3425a975a3dSMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(unit->state), M_WAITOK | M_NULLOK);
343b06ebda0SMatthew Dillon if (rsp == NULL) {
344b06ebda0SMatthew Dillon error = ENOMEM;
345b06ebda0SMatthew Dillon break;
346b06ebda0SMatthew Dillon }
347b06ebda0SMatthew Dillon
348b06ebda0SMatthew Dillon *((ng_hci_node_state_ep *)(rsp->data)) = unit->state;
349b06ebda0SMatthew Dillon break;
350b06ebda0SMatthew Dillon
351b06ebda0SMatthew Dillon /* Turn INITED bit - node initialized */
352b06ebda0SMatthew Dillon case NGM_HCI_NODE_INIT:
353b06ebda0SMatthew Dillon if (bcmp(&unit->bdaddr, NG_HCI_BDADDR_ANY,
354b06ebda0SMatthew Dillon sizeof(bdaddr_t)) == 0) {
355b06ebda0SMatthew Dillon error = ENXIO;
356b06ebda0SMatthew Dillon break;
357b06ebda0SMatthew Dillon }
358b06ebda0SMatthew Dillon
359b06ebda0SMatthew Dillon unit->state |= NG_HCI_UNIT_INITED;
360b06ebda0SMatthew Dillon
361b06ebda0SMatthew Dillon ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);
362b06ebda0SMatthew Dillon ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);
363b06ebda0SMatthew Dillon break;
364b06ebda0SMatthew Dillon
365b06ebda0SMatthew Dillon /* Get node debug level */
366b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_DEBUG:
3675a975a3dSMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(unit->debug), M_WAITOK | M_NULLOK);
368b06ebda0SMatthew Dillon if (rsp == NULL) {
369b06ebda0SMatthew Dillon error = ENOMEM;
370b06ebda0SMatthew Dillon break;
371b06ebda0SMatthew Dillon }
372b06ebda0SMatthew Dillon
373b06ebda0SMatthew Dillon *((ng_hci_node_debug_ep *)(rsp->data)) = unit->debug;
374b06ebda0SMatthew Dillon break;
375b06ebda0SMatthew Dillon
376b06ebda0SMatthew Dillon /* Set node debug level */
377b06ebda0SMatthew Dillon case NGM_HCI_NODE_SET_DEBUG:
378b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(ng_hci_node_debug_ep)){
379b06ebda0SMatthew Dillon error = EMSGSIZE;
380b06ebda0SMatthew Dillon break;
381b06ebda0SMatthew Dillon }
382b06ebda0SMatthew Dillon
383b06ebda0SMatthew Dillon unit->debug = *((ng_hci_node_debug_ep *)(msg->data));
384b06ebda0SMatthew Dillon break;
385b06ebda0SMatthew Dillon
386b06ebda0SMatthew Dillon /* Get buffer info */
387b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_BUFFER: {
388b06ebda0SMatthew Dillon ng_hci_node_buffer_ep *ep = NULL;
389b06ebda0SMatthew Dillon
390b06ebda0SMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(ng_hci_node_buffer_ep),
3915a975a3dSMatthew Dillon M_WAITOK | M_NULLOK);
392b06ebda0SMatthew Dillon if (rsp == NULL) {
393b06ebda0SMatthew Dillon error = ENOMEM;
394b06ebda0SMatthew Dillon break;
395b06ebda0SMatthew Dillon }
396b06ebda0SMatthew Dillon
397b06ebda0SMatthew Dillon ep = (ng_hci_node_buffer_ep *)(rsp->data);
398b06ebda0SMatthew Dillon
399b06ebda0SMatthew Dillon NG_HCI_BUFF_CMD_GET(unit->buffer, ep->cmd_free);
400b06ebda0SMatthew Dillon NG_HCI_BUFF_ACL_AVAIL(unit->buffer, ep->acl_free);
401b06ebda0SMatthew Dillon NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->acl_pkts);
402b06ebda0SMatthew Dillon NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->acl_size);
403b06ebda0SMatthew Dillon NG_HCI_BUFF_SCO_AVAIL(unit->buffer, ep->sco_free);
404b06ebda0SMatthew Dillon NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->sco_pkts);
405b06ebda0SMatthew Dillon NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->sco_size);
406b06ebda0SMatthew Dillon } break;
407b06ebda0SMatthew Dillon
408b06ebda0SMatthew Dillon /* Get BDADDR */
409b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_BDADDR:
4105a975a3dSMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(bdaddr_t), M_WAITOK | M_NULLOK);
411b06ebda0SMatthew Dillon if (rsp == NULL) {
412b06ebda0SMatthew Dillon error = ENOMEM;
413b06ebda0SMatthew Dillon break;
414b06ebda0SMatthew Dillon }
415b06ebda0SMatthew Dillon
416b06ebda0SMatthew Dillon bcopy(&unit->bdaddr, rsp->data, sizeof(bdaddr_t));
417b06ebda0SMatthew Dillon break;
418b06ebda0SMatthew Dillon
419b06ebda0SMatthew Dillon /* Get features */
420b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_FEATURES:
4215a975a3dSMatthew Dillon NG_MKRESPONSE(rsp,msg,sizeof(unit->features),M_WAITOK | M_NULLOK);
422b06ebda0SMatthew Dillon if (rsp == NULL) {
423b06ebda0SMatthew Dillon error = ENOMEM;
424b06ebda0SMatthew Dillon break;
425b06ebda0SMatthew Dillon }
426b06ebda0SMatthew Dillon
427b06ebda0SMatthew Dillon bcopy(&unit->features,rsp->data,sizeof(unit->features));
428b06ebda0SMatthew Dillon break;
429b06ebda0SMatthew Dillon
430b06ebda0SMatthew Dillon /* Get stat */
431b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_STAT:
4325a975a3dSMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(unit->stat), M_WAITOK | M_NULLOK);
433b06ebda0SMatthew Dillon if (rsp == NULL) {
434b06ebda0SMatthew Dillon error = ENOMEM;
435b06ebda0SMatthew Dillon break;
436b06ebda0SMatthew Dillon }
437b06ebda0SMatthew Dillon
438b06ebda0SMatthew Dillon bcopy(&unit->stat, rsp->data, sizeof(unit->stat));
439b06ebda0SMatthew Dillon break;
440b06ebda0SMatthew Dillon
441b06ebda0SMatthew Dillon /* Reset stat */
442b06ebda0SMatthew Dillon case NGM_HCI_NODE_RESET_STAT:
443b06ebda0SMatthew Dillon NG_HCI_STAT_RESET(unit->stat);
444b06ebda0SMatthew Dillon break;
445b06ebda0SMatthew Dillon
446b06ebda0SMatthew Dillon /* Clean up neighbors list */
447b06ebda0SMatthew Dillon case NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE:
448b06ebda0SMatthew Dillon ng_hci_flush_neighbor_cache(unit);
449b06ebda0SMatthew Dillon break;
450b06ebda0SMatthew Dillon
451b06ebda0SMatthew Dillon /* Get neighbor cache entries */
452b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_NEIGHBOR_CACHE: {
453b06ebda0SMatthew Dillon ng_hci_neighbor_p n = NULL;
454b06ebda0SMatthew Dillon ng_hci_node_get_neighbor_cache_ep *e1 = NULL;
455b06ebda0SMatthew Dillon ng_hci_node_neighbor_cache_entry_ep *e2 = NULL;
456b06ebda0SMatthew Dillon int s = 0;
457b06ebda0SMatthew Dillon
458b06ebda0SMatthew Dillon /* Look for the fresh entries in the cache */
459b06ebda0SMatthew Dillon for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {
460b06ebda0SMatthew Dillon ng_hci_neighbor_p nn = LIST_NEXT(n, next);
461b06ebda0SMatthew Dillon
462b06ebda0SMatthew Dillon if (ng_hci_neighbor_stale(n))
463b06ebda0SMatthew Dillon ng_hci_free_neighbor(n);
464b06ebda0SMatthew Dillon else
465b06ebda0SMatthew Dillon s ++;
466b06ebda0SMatthew Dillon
467b06ebda0SMatthew Dillon n = nn;
468b06ebda0SMatthew Dillon }
469b06ebda0SMatthew Dillon if (s > NG_HCI_MAX_NEIGHBOR_NUM)
470b06ebda0SMatthew Dillon s = NG_HCI_MAX_NEIGHBOR_NUM;
471b06ebda0SMatthew Dillon
472b06ebda0SMatthew Dillon /* Prepare response */
473b06ebda0SMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),
4745a975a3dSMatthew Dillon M_WAITOK | M_NULLOK);
475b06ebda0SMatthew Dillon if (rsp == NULL) {
476b06ebda0SMatthew Dillon error = ENOMEM;
477b06ebda0SMatthew Dillon break;
478b06ebda0SMatthew Dillon }
479b06ebda0SMatthew Dillon
480b06ebda0SMatthew Dillon e1 = (ng_hci_node_get_neighbor_cache_ep *)(rsp->data);
481b06ebda0SMatthew Dillon e2 = (ng_hci_node_neighbor_cache_entry_ep *)(e1 + 1);
482b06ebda0SMatthew Dillon
483b06ebda0SMatthew Dillon e1->num_entries = s;
484b06ebda0SMatthew Dillon
485b06ebda0SMatthew Dillon LIST_FOREACH(n, &unit->neighbors, next) {
486b06ebda0SMatthew Dillon e2->page_scan_rep_mode = n->page_scan_rep_mode;
487b06ebda0SMatthew Dillon e2->page_scan_mode = n->page_scan_mode;
488b06ebda0SMatthew Dillon e2->clock_offset = n->clock_offset;
489b06ebda0SMatthew Dillon bcopy(&n->bdaddr, &e2->bdaddr,
490b06ebda0SMatthew Dillon sizeof(e2->bdaddr));
491b06ebda0SMatthew Dillon bcopy(&n->features, &e2->features,
492b06ebda0SMatthew Dillon sizeof(e2->features));
493b06ebda0SMatthew Dillon
494b06ebda0SMatthew Dillon e2 ++;
495b06ebda0SMatthew Dillon if (--s <= 0)
496b06ebda0SMatthew Dillon break;
497b06ebda0SMatthew Dillon }
498b06ebda0SMatthew Dillon } break;
499b06ebda0SMatthew Dillon
500b06ebda0SMatthew Dillon /* Get connection list */
501b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_CON_LIST: {
502b06ebda0SMatthew Dillon ng_hci_unit_con_p c = NULL;
503b06ebda0SMatthew Dillon ng_hci_node_con_list_ep *e1 = NULL;
504b06ebda0SMatthew Dillon ng_hci_node_con_ep *e2 = NULL;
505b06ebda0SMatthew Dillon int s = 0;
506b06ebda0SMatthew Dillon
507b06ebda0SMatthew Dillon /* Count number of connections in the list */
508b06ebda0SMatthew Dillon LIST_FOREACH(c, &unit->con_list, next)
509b06ebda0SMatthew Dillon s ++;
510b06ebda0SMatthew Dillon if (s > NG_HCI_MAX_CON_NUM)
511b06ebda0SMatthew Dillon s = NG_HCI_MAX_CON_NUM;
512b06ebda0SMatthew Dillon
513b06ebda0SMatthew Dillon /* Prepare response */
514b06ebda0SMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),
5155a975a3dSMatthew Dillon M_WAITOK | M_NULLOK);
516b06ebda0SMatthew Dillon if (rsp == NULL) {
517b06ebda0SMatthew Dillon error = ENOMEM;
518b06ebda0SMatthew Dillon break;
519b06ebda0SMatthew Dillon }
520b06ebda0SMatthew Dillon
521b06ebda0SMatthew Dillon e1 = (ng_hci_node_con_list_ep *)(rsp->data);
522b06ebda0SMatthew Dillon e2 = (ng_hci_node_con_ep *)(e1 + 1);
523b06ebda0SMatthew Dillon
524b06ebda0SMatthew Dillon e1->num_connections = s;
525b06ebda0SMatthew Dillon
526b06ebda0SMatthew Dillon LIST_FOREACH(c, &unit->con_list, next) {
527b06ebda0SMatthew Dillon e2->link_type = c->link_type;
528b06ebda0SMatthew Dillon e2->encryption_mode= c->encryption_mode;
529b06ebda0SMatthew Dillon e2->mode = c->mode;
530b06ebda0SMatthew Dillon e2->role = c->role;
531b06ebda0SMatthew Dillon
532b06ebda0SMatthew Dillon e2->state = c->state;
533b06ebda0SMatthew Dillon
534b06ebda0SMatthew Dillon e2->pending = c->pending;
535b06ebda0SMatthew Dillon e2->queue_len = NG_BT_ITEMQ_LEN(&c->conq);
536b06ebda0SMatthew Dillon
537b06ebda0SMatthew Dillon e2->con_handle = c->con_handle;
538b06ebda0SMatthew Dillon bcopy(&c->bdaddr, &e2->bdaddr,
539b06ebda0SMatthew Dillon sizeof(e2->bdaddr));
540b06ebda0SMatthew Dillon
541b06ebda0SMatthew Dillon e2 ++;
542b06ebda0SMatthew Dillon if (--s <= 0)
543b06ebda0SMatthew Dillon break;
544b06ebda0SMatthew Dillon }
545b06ebda0SMatthew Dillon } break;
546b06ebda0SMatthew Dillon
547b06ebda0SMatthew Dillon /* Get link policy settings mask */
548b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK:
549b06ebda0SMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(unit->link_policy_mask),
5505a975a3dSMatthew Dillon M_WAITOK | M_NULLOK);
551b06ebda0SMatthew Dillon if (rsp == NULL) {
552b06ebda0SMatthew Dillon error = ENOMEM;
553b06ebda0SMatthew Dillon break;
554b06ebda0SMatthew Dillon }
555b06ebda0SMatthew Dillon
556b06ebda0SMatthew Dillon *((ng_hci_node_link_policy_mask_ep *)(rsp->data)) =
557b06ebda0SMatthew Dillon unit->link_policy_mask;
558b06ebda0SMatthew Dillon break;
559b06ebda0SMatthew Dillon
560b06ebda0SMatthew Dillon /* Set link policy settings mask */
561b06ebda0SMatthew Dillon case NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK:
562b06ebda0SMatthew Dillon if (msg->header.arglen !=
563b06ebda0SMatthew Dillon sizeof(ng_hci_node_link_policy_mask_ep)) {
564b06ebda0SMatthew Dillon error = EMSGSIZE;
565b06ebda0SMatthew Dillon break;
566b06ebda0SMatthew Dillon }
567b06ebda0SMatthew Dillon
568b06ebda0SMatthew Dillon unit->link_policy_mask =
569b06ebda0SMatthew Dillon *((ng_hci_node_link_policy_mask_ep *)
570b06ebda0SMatthew Dillon (msg->data));
571b06ebda0SMatthew Dillon break;
572b06ebda0SMatthew Dillon
573b06ebda0SMatthew Dillon /* Get packet mask */
574b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_PACKET_MASK:
575b06ebda0SMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(unit->packet_mask),
5765a975a3dSMatthew Dillon M_WAITOK | M_NULLOK);
577b06ebda0SMatthew Dillon if (rsp == NULL) {
578b06ebda0SMatthew Dillon error = ENOMEM;
579b06ebda0SMatthew Dillon break;
580b06ebda0SMatthew Dillon }
581b06ebda0SMatthew Dillon
582b06ebda0SMatthew Dillon *((ng_hci_node_packet_mask_ep *)(rsp->data)) =
583b06ebda0SMatthew Dillon unit->packet_mask;
584b06ebda0SMatthew Dillon break;
585b06ebda0SMatthew Dillon
586b06ebda0SMatthew Dillon /* Set packet mask */
587b06ebda0SMatthew Dillon case NGM_HCI_NODE_SET_PACKET_MASK:
588b06ebda0SMatthew Dillon if (msg->header.arglen !=
589b06ebda0SMatthew Dillon sizeof(ng_hci_node_packet_mask_ep)) {
590b06ebda0SMatthew Dillon error = EMSGSIZE;
591b06ebda0SMatthew Dillon break;
592b06ebda0SMatthew Dillon }
593b06ebda0SMatthew Dillon
594b06ebda0SMatthew Dillon unit->packet_mask =
595b06ebda0SMatthew Dillon *((ng_hci_node_packet_mask_ep *)(msg->data));
596b06ebda0SMatthew Dillon break;
597b06ebda0SMatthew Dillon
598b06ebda0SMatthew Dillon /* Get role switch */
599b06ebda0SMatthew Dillon case NGM_HCI_NODE_GET_ROLE_SWITCH:
600b06ebda0SMatthew Dillon NG_MKRESPONSE(rsp, msg, sizeof(unit->role_switch),
6015a975a3dSMatthew Dillon M_WAITOK | M_NULLOK);
602b06ebda0SMatthew Dillon if (rsp == NULL) {
603b06ebda0SMatthew Dillon error = ENOMEM;
604b06ebda0SMatthew Dillon break;
605b06ebda0SMatthew Dillon }
606b06ebda0SMatthew Dillon
607b06ebda0SMatthew Dillon *((ng_hci_node_role_switch_ep *)(rsp->data)) =
608b06ebda0SMatthew Dillon unit->role_switch;
609b06ebda0SMatthew Dillon break;
610b06ebda0SMatthew Dillon
611b06ebda0SMatthew Dillon /* Set role switch */
612b06ebda0SMatthew Dillon case NGM_HCI_NODE_SET_ROLE_SWITCH:
613b06ebda0SMatthew Dillon if (msg->header.arglen !=
614b06ebda0SMatthew Dillon sizeof(ng_hci_node_role_switch_ep)) {
615b06ebda0SMatthew Dillon error = EMSGSIZE;
616b06ebda0SMatthew Dillon break;
617b06ebda0SMatthew Dillon }
618b06ebda0SMatthew Dillon
619b06ebda0SMatthew Dillon unit->role_switch =
620b06ebda0SMatthew Dillon *((ng_hci_node_role_switch_ep *)(msg->data));
621b06ebda0SMatthew Dillon break;
622b06ebda0SMatthew Dillon
623b06ebda0SMatthew Dillon default:
624b06ebda0SMatthew Dillon error = EINVAL;
625b06ebda0SMatthew Dillon break;
626b06ebda0SMatthew Dillon }
627b06ebda0SMatthew Dillon break;
628b06ebda0SMatthew Dillon
629b06ebda0SMatthew Dillon default:
630b06ebda0SMatthew Dillon error = EINVAL;
631b06ebda0SMatthew Dillon break;
632b06ebda0SMatthew Dillon }
633b06ebda0SMatthew Dillon
634b06ebda0SMatthew Dillon /* NG_RESPOND_MSG should take care of "item" and "rsp" */
635b06ebda0SMatthew Dillon NG_RESPOND_MSG(error, node, item, rsp);
636b06ebda0SMatthew Dillon NG_FREE_MSG(msg);
637b06ebda0SMatthew Dillon
638b06ebda0SMatthew Dillon return (error);
639b06ebda0SMatthew Dillon } /* ng_hci_default_rcvmsg */
640b06ebda0SMatthew Dillon
641b06ebda0SMatthew Dillon /*
642b06ebda0SMatthew Dillon * Process control message from upstream hooks (ACL and SCO).
643b06ebda0SMatthew Dillon * Handle LP_xxx messages here, give everything else to default routine.
644b06ebda0SMatthew Dillon */
645b06ebda0SMatthew Dillon
646b06ebda0SMatthew Dillon static int
ng_hci_upper_rcvmsg(node_p node,item_p item,hook_p lasthook)647b06ebda0SMatthew Dillon ng_hci_upper_rcvmsg(node_p node, item_p item, hook_p lasthook)
648b06ebda0SMatthew Dillon {
649b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
650b06ebda0SMatthew Dillon int error = 0;
651b06ebda0SMatthew Dillon
652b06ebda0SMatthew Dillon switch (NGI_MSG(item)->header.typecookie) {
653b06ebda0SMatthew Dillon case NGM_HCI_COOKIE:
654b06ebda0SMatthew Dillon switch (NGI_MSG(item)->header.cmd) {
655b06ebda0SMatthew Dillon case NGM_HCI_LP_CON_REQ:
656b06ebda0SMatthew Dillon error = ng_hci_lp_con_req(unit, item, lasthook);
657b06ebda0SMatthew Dillon break;
658b06ebda0SMatthew Dillon
659b06ebda0SMatthew Dillon case NGM_HCI_LP_DISCON_REQ: /* XXX not defined by specs */
660b06ebda0SMatthew Dillon error = ng_hci_lp_discon_req(unit, item, lasthook);
661b06ebda0SMatthew Dillon break;
662b06ebda0SMatthew Dillon
663b06ebda0SMatthew Dillon case NGM_HCI_LP_CON_RSP:
664b06ebda0SMatthew Dillon error = ng_hci_lp_con_rsp(unit, item, lasthook);
665b06ebda0SMatthew Dillon break;
666b06ebda0SMatthew Dillon
667b06ebda0SMatthew Dillon case NGM_HCI_LP_QOS_REQ:
668b06ebda0SMatthew Dillon error = ng_hci_lp_qos_req(unit, item, lasthook);
669b06ebda0SMatthew Dillon break;
670b06ebda0SMatthew Dillon
671b06ebda0SMatthew Dillon default:
672b06ebda0SMatthew Dillon error = ng_hci_default_rcvmsg(node, item, lasthook);
673b06ebda0SMatthew Dillon break;
674b06ebda0SMatthew Dillon }
675b06ebda0SMatthew Dillon break;
676b06ebda0SMatthew Dillon
677b06ebda0SMatthew Dillon default:
678b06ebda0SMatthew Dillon error = ng_hci_default_rcvmsg(node, item, lasthook);
679b06ebda0SMatthew Dillon break;
680b06ebda0SMatthew Dillon }
681b06ebda0SMatthew Dillon
682b06ebda0SMatthew Dillon return (error);
683b06ebda0SMatthew Dillon } /* ng_hci_upper_rcvmsg */
684b06ebda0SMatthew Dillon
685b06ebda0SMatthew Dillon /*
686b06ebda0SMatthew Dillon * Process data packet from the driver hook.
687b06ebda0SMatthew Dillon * We expect HCI events, ACL or SCO data packets.
688b06ebda0SMatthew Dillon */
689b06ebda0SMatthew Dillon
690b06ebda0SMatthew Dillon static int
ng_hci_drv_rcvdata(hook_p hook,item_p item)691b06ebda0SMatthew Dillon ng_hci_drv_rcvdata(hook_p hook, item_p item)
692b06ebda0SMatthew Dillon {
693b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
694b06ebda0SMatthew Dillon struct mbuf *m = NULL;
695b06ebda0SMatthew Dillon int error = 0;
696b06ebda0SMatthew Dillon
697b06ebda0SMatthew Dillon /* Process packet */
698b06ebda0SMatthew Dillon m = NGI_M(item); /* item still has mbuf, just peeking */
699b06ebda0SMatthew Dillon m->m_flags |= M_PROTO1; /* mark as incoming packet */
700b06ebda0SMatthew Dillon
701b06ebda0SMatthew Dillon NG_HCI_STAT_BYTES_RECV(unit->stat, m->m_pkthdr.len);
702b06ebda0SMatthew Dillon
703b06ebda0SMatthew Dillon /* Give copy packet to RAW hook */
704b06ebda0SMatthew Dillon ng_hci_mtap(unit, m);
705b06ebda0SMatthew Dillon
706b06ebda0SMatthew Dillon /*
707b06ebda0SMatthew Dillon * XXX XXX XXX
708b06ebda0SMatthew Dillon * Lower layer drivers MUST NOT send mbuf chain with empty mbuf at
709b06ebda0SMatthew Dillon * the beginning of the chain. HCI layer WILL NOT call m_pullup() here.
710b06ebda0SMatthew Dillon */
711b06ebda0SMatthew Dillon
712b06ebda0SMatthew Dillon switch (*mtod(m, u_int8_t *)) {
713b06ebda0SMatthew Dillon case NG_HCI_ACL_DATA_PKT:
714b06ebda0SMatthew Dillon NG_HCI_STAT_ACL_RECV(unit->stat);
715b06ebda0SMatthew Dillon
716b06ebda0SMatthew Dillon if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||
717b06ebda0SMatthew Dillon unit->acl == NULL || NG_HOOK_NOT_VALID(unit->acl)) {
718b06ebda0SMatthew Dillon NG_HCI_WARN(
719b06ebda0SMatthew Dillon "%s: %s - could not forward HCI ACL data packet, state=%#x, hook=%p\n",
720b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
721b06ebda0SMatthew Dillon unit->state, unit->acl);
722b06ebda0SMatthew Dillon
723b06ebda0SMatthew Dillon NG_FREE_ITEM(item);
724b06ebda0SMatthew Dillon } else
725b06ebda0SMatthew Dillon NG_FWD_ITEM_HOOK(error, item, unit->acl);
726b06ebda0SMatthew Dillon break;
727b06ebda0SMatthew Dillon
728b06ebda0SMatthew Dillon case NG_HCI_SCO_DATA_PKT:
729b06ebda0SMatthew Dillon NG_HCI_STAT_SCO_RECV(unit->stat);
730b06ebda0SMatthew Dillon
731b06ebda0SMatthew Dillon if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||
732b06ebda0SMatthew Dillon unit->sco == NULL || NG_HOOK_NOT_VALID(unit->sco)) {
733b06ebda0SMatthew Dillon NG_HCI_WARN(
734b06ebda0SMatthew Dillon "%s: %s - could not forward HCI SCO data packet, state=%#x, hook=%p\n",
735b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
736b06ebda0SMatthew Dillon unit->state, unit->sco);
737b06ebda0SMatthew Dillon
738b06ebda0SMatthew Dillon NG_FREE_ITEM(item);
739b06ebda0SMatthew Dillon } else
740b06ebda0SMatthew Dillon NG_FWD_ITEM_HOOK(error, item, unit->sco);
741b06ebda0SMatthew Dillon break;
742b06ebda0SMatthew Dillon
743b06ebda0SMatthew Dillon case NG_HCI_EVENT_PKT:
744b06ebda0SMatthew Dillon NG_HCI_STAT_EVNT_RECV(unit->stat);
745b06ebda0SMatthew Dillon
746b06ebda0SMatthew Dillon /* Detach mbuf, discard item and process event */
747b06ebda0SMatthew Dillon NGI_GET_M(item, m);
748b06ebda0SMatthew Dillon NG_FREE_ITEM(item);
749b06ebda0SMatthew Dillon
750b06ebda0SMatthew Dillon error = ng_hci_process_event(unit, m);
751b06ebda0SMatthew Dillon break;
752b06ebda0SMatthew Dillon
753b06ebda0SMatthew Dillon default:
754b06ebda0SMatthew Dillon NG_HCI_ALERT(
755b06ebda0SMatthew Dillon "%s: %s - got unknown HCI packet type=%#x\n",
756b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
757b06ebda0SMatthew Dillon *mtod(m, u_int8_t *));
758b06ebda0SMatthew Dillon
759b06ebda0SMatthew Dillon NG_FREE_ITEM(item);
760b06ebda0SMatthew Dillon
761b06ebda0SMatthew Dillon error = EINVAL;
762b06ebda0SMatthew Dillon break;
763b06ebda0SMatthew Dillon }
764b06ebda0SMatthew Dillon
765b06ebda0SMatthew Dillon return (error);
766b06ebda0SMatthew Dillon } /* ng_hci_drv_rcvdata */
767b06ebda0SMatthew Dillon
768b06ebda0SMatthew Dillon /*
769b06ebda0SMatthew Dillon * Process data packet from ACL upstream hook.
770b06ebda0SMatthew Dillon * We expect valid HCI ACL data packets.
771b06ebda0SMatthew Dillon */
772b06ebda0SMatthew Dillon
773b06ebda0SMatthew Dillon static int
ng_hci_acl_rcvdata(hook_p hook,item_p item)774b06ebda0SMatthew Dillon ng_hci_acl_rcvdata(hook_p hook, item_p item)
775b06ebda0SMatthew Dillon {
776b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
777b06ebda0SMatthew Dillon struct mbuf *m = NULL;
778b06ebda0SMatthew Dillon ng_hci_unit_con_p con = NULL;
779b06ebda0SMatthew Dillon u_int16_t con_handle;
780b06ebda0SMatthew Dillon int size, error = 0;
781b06ebda0SMatthew Dillon
782b06ebda0SMatthew Dillon NG_HCI_BUFF_ACL_SIZE(unit->buffer, size);
783b06ebda0SMatthew Dillon
784b06ebda0SMatthew Dillon /* Check packet */
785b06ebda0SMatthew Dillon NGI_GET_M(item, m);
786b06ebda0SMatthew Dillon
787b06ebda0SMatthew Dillon if (*mtod(m, u_int8_t *) != NG_HCI_ACL_DATA_PKT) {
788b06ebda0SMatthew Dillon NG_HCI_ALERT(
789b06ebda0SMatthew Dillon "%s: %s - invalid HCI data packet type=%#x\n",
790b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
791b06ebda0SMatthew Dillon *mtod(m, u_int8_t *));
792b06ebda0SMatthew Dillon
793b06ebda0SMatthew Dillon error = EINVAL;
794b06ebda0SMatthew Dillon goto drop;
795b06ebda0SMatthew Dillon }
796b06ebda0SMatthew Dillon
797b06ebda0SMatthew Dillon if (m->m_pkthdr.len < sizeof(ng_hci_acldata_pkt_t) ||
798b06ebda0SMatthew Dillon m->m_pkthdr.len > sizeof(ng_hci_acldata_pkt_t) + size) {
799b06ebda0SMatthew Dillon NG_HCI_ALERT(
800b06ebda0SMatthew Dillon "%s: %s - invalid HCI ACL data packet, len=%d, mtu=%d\n",
801b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
802b06ebda0SMatthew Dillon m->m_pkthdr.len, size);
803b06ebda0SMatthew Dillon
804b06ebda0SMatthew Dillon error = EMSGSIZE;
805b06ebda0SMatthew Dillon goto drop;
806b06ebda0SMatthew Dillon }
807b06ebda0SMatthew Dillon
808b06ebda0SMatthew Dillon NG_HCI_M_PULLUP(m, sizeof(ng_hci_acldata_pkt_t));
809b06ebda0SMatthew Dillon if (m == NULL) {
810b06ebda0SMatthew Dillon error = ENOBUFS;
811b06ebda0SMatthew Dillon goto drop;
812b06ebda0SMatthew Dillon }
813b06ebda0SMatthew Dillon
814b06ebda0SMatthew Dillon con_handle = NG_HCI_CON_HANDLE(le16toh(
815b06ebda0SMatthew Dillon mtod(m, ng_hci_acldata_pkt_t *)->con_handle));
816b06ebda0SMatthew Dillon size = le16toh(mtod(m, ng_hci_acldata_pkt_t *)->length);
817b06ebda0SMatthew Dillon
818b06ebda0SMatthew Dillon if (m->m_pkthdr.len != sizeof(ng_hci_acldata_pkt_t) + size) {
819b06ebda0SMatthew Dillon NG_HCI_ALERT(
820b06ebda0SMatthew Dillon "%s: %s - invalid HCI ACL data packet size, len=%d, length=%d\n",
821b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
822b06ebda0SMatthew Dillon m->m_pkthdr.len, size);
823b06ebda0SMatthew Dillon
824b06ebda0SMatthew Dillon error = EMSGSIZE;
825b06ebda0SMatthew Dillon goto drop;
826b06ebda0SMatthew Dillon }
827b06ebda0SMatthew Dillon
828b06ebda0SMatthew Dillon /* Queue packet */
829b06ebda0SMatthew Dillon con = ng_hci_con_by_handle(unit, con_handle);
830b06ebda0SMatthew Dillon if (con == NULL) {
831b06ebda0SMatthew Dillon NG_HCI_ERR(
832b06ebda0SMatthew Dillon "%s: %s - unexpected HCI ACL data packet. Connection does not exists, " \
833b06ebda0SMatthew Dillon "con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle);
834b06ebda0SMatthew Dillon
835b06ebda0SMatthew Dillon error = ENOENT;
836b06ebda0SMatthew Dillon goto drop;
837b06ebda0SMatthew Dillon }
838b06ebda0SMatthew Dillon
839b06ebda0SMatthew Dillon if (con->link_type != NG_HCI_LINK_ACL) {
840b06ebda0SMatthew Dillon NG_HCI_ERR(
841b06ebda0SMatthew Dillon "%s: %s - unexpected HCI ACL data packet. Not ACL link, con_handle=%d, " \
842b06ebda0SMatthew Dillon "link_type=%d\n", __func__, NG_NODE_NAME(unit->node),
843b06ebda0SMatthew Dillon con_handle, con->link_type);
844b06ebda0SMatthew Dillon
845b06ebda0SMatthew Dillon error = EINVAL;
846b06ebda0SMatthew Dillon goto drop;
847b06ebda0SMatthew Dillon }
848b06ebda0SMatthew Dillon
849b06ebda0SMatthew Dillon if (con->state != NG_HCI_CON_OPEN) {
850b06ebda0SMatthew Dillon NG_HCI_ERR(
851b06ebda0SMatthew Dillon "%s: %s - unexpected HCI ACL data packet. Invalid connection state=%d, " \
852b06ebda0SMatthew Dillon "con_handle=%d\n", __func__, NG_NODE_NAME(unit->node),
853b06ebda0SMatthew Dillon con->state, con_handle);
854b06ebda0SMatthew Dillon
855b06ebda0SMatthew Dillon error = EHOSTDOWN;
856b06ebda0SMatthew Dillon goto drop;
857b06ebda0SMatthew Dillon }
858b06ebda0SMatthew Dillon
859b06ebda0SMatthew Dillon if (NG_BT_ITEMQ_FULL(&con->conq)) {
860b06ebda0SMatthew Dillon NG_HCI_ALERT(
861b06ebda0SMatthew Dillon "%s: %s - dropping HCI ACL data packet, con_handle=%d, len=%d, queue_len=%d\n",
862b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node), con_handle,
863b06ebda0SMatthew Dillon m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));
864b06ebda0SMatthew Dillon
865b06ebda0SMatthew Dillon NG_BT_ITEMQ_DROP(&con->conq);
866b06ebda0SMatthew Dillon
867b06ebda0SMatthew Dillon error = ENOBUFS;
868b06ebda0SMatthew Dillon goto drop;
869b06ebda0SMatthew Dillon }
870b06ebda0SMatthew Dillon
871b06ebda0SMatthew Dillon /* Queue item and schedule data transfer */
872b06ebda0SMatthew Dillon NGI_M(item) = m;
873*e85b99abSSascha Wildner ng_ref_item(item);
874b06ebda0SMatthew Dillon NG_BT_ITEMQ_ENQUEUE(&con->conq, item);
875b06ebda0SMatthew Dillon item = NULL;
876b06ebda0SMatthew Dillon m = NULL;
877b06ebda0SMatthew Dillon
878b06ebda0SMatthew Dillon ng_hci_send_data(unit);
879b06ebda0SMatthew Dillon drop:
880b06ebda0SMatthew Dillon if (item != NULL)
881b06ebda0SMatthew Dillon NG_FREE_ITEM(item);
882b06ebda0SMatthew Dillon
883b06ebda0SMatthew Dillon NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
884b06ebda0SMatthew Dillon
885b06ebda0SMatthew Dillon return (error);
886b06ebda0SMatthew Dillon } /* ng_hci_acl_rcvdata */
887b06ebda0SMatthew Dillon
888b06ebda0SMatthew Dillon /*
889b06ebda0SMatthew Dillon * Process data packet from SCO upstream hook.
890b06ebda0SMatthew Dillon * We expect valid HCI SCO data packets
891b06ebda0SMatthew Dillon */
892b06ebda0SMatthew Dillon
893b06ebda0SMatthew Dillon static int
ng_hci_sco_rcvdata(hook_p hook,item_p item)894b06ebda0SMatthew Dillon ng_hci_sco_rcvdata(hook_p hook, item_p item)
895b06ebda0SMatthew Dillon {
896b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
897b06ebda0SMatthew Dillon struct mbuf *m = NULL;
898b06ebda0SMatthew Dillon ng_hci_unit_con_p con = NULL;
899b06ebda0SMatthew Dillon u_int16_t con_handle;
900b06ebda0SMatthew Dillon int size, error = 0;
901b06ebda0SMatthew Dillon
902b06ebda0SMatthew Dillon NG_HCI_BUFF_SCO_SIZE(unit->buffer, size);
903b06ebda0SMatthew Dillon
904b06ebda0SMatthew Dillon /* Check packet */
905b06ebda0SMatthew Dillon NGI_GET_M(item, m);
906b06ebda0SMatthew Dillon
907b06ebda0SMatthew Dillon if (*mtod(m, u_int8_t *) != NG_HCI_SCO_DATA_PKT) {
908b06ebda0SMatthew Dillon NG_HCI_ALERT(
909b06ebda0SMatthew Dillon "%s: %s - invalid HCI data packet type=%#x\n",
910b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
911b06ebda0SMatthew Dillon *mtod(m, u_int8_t *));
912b06ebda0SMatthew Dillon
913b06ebda0SMatthew Dillon error = EINVAL;
914b06ebda0SMatthew Dillon goto drop;
915b06ebda0SMatthew Dillon }
916b06ebda0SMatthew Dillon
917b06ebda0SMatthew Dillon if (m->m_pkthdr.len < sizeof(ng_hci_scodata_pkt_t) ||
918b06ebda0SMatthew Dillon m->m_pkthdr.len > sizeof(ng_hci_scodata_pkt_t) + size) {
919b06ebda0SMatthew Dillon NG_HCI_ALERT(
920b06ebda0SMatthew Dillon "%s: %s - invalid HCI SCO data packet, len=%d, mtu=%d\n",
921b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
922b06ebda0SMatthew Dillon m->m_pkthdr.len, size);
923b06ebda0SMatthew Dillon
924b06ebda0SMatthew Dillon error = EMSGSIZE;
925b06ebda0SMatthew Dillon goto drop;
926b06ebda0SMatthew Dillon }
927b06ebda0SMatthew Dillon
928b06ebda0SMatthew Dillon NG_HCI_M_PULLUP(m, sizeof(ng_hci_scodata_pkt_t));
929b06ebda0SMatthew Dillon if (m == NULL) {
930b06ebda0SMatthew Dillon error = ENOBUFS;
931b06ebda0SMatthew Dillon goto drop;
932b06ebda0SMatthew Dillon }
933b06ebda0SMatthew Dillon
934b06ebda0SMatthew Dillon con_handle = NG_HCI_CON_HANDLE(le16toh(
935b06ebda0SMatthew Dillon mtod(m, ng_hci_scodata_pkt_t *)->con_handle));
936b06ebda0SMatthew Dillon size = mtod(m, ng_hci_scodata_pkt_t *)->length;
937b06ebda0SMatthew Dillon
938b06ebda0SMatthew Dillon if (m->m_pkthdr.len != sizeof(ng_hci_scodata_pkt_t) + size) {
939b06ebda0SMatthew Dillon NG_HCI_ALERT(
940b06ebda0SMatthew Dillon "%s: %s - invalid HCI SCO data packet size, len=%d, length=%d\n",
941b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
942b06ebda0SMatthew Dillon m->m_pkthdr.len, size);
943b06ebda0SMatthew Dillon
944b06ebda0SMatthew Dillon error = EMSGSIZE;
945b06ebda0SMatthew Dillon goto drop;
946b06ebda0SMatthew Dillon }
947b06ebda0SMatthew Dillon
948b06ebda0SMatthew Dillon /* Queue packet */
949b06ebda0SMatthew Dillon con = ng_hci_con_by_handle(unit, con_handle);
950b06ebda0SMatthew Dillon if (con == NULL) {
951b06ebda0SMatthew Dillon NG_HCI_ERR(
952b06ebda0SMatthew Dillon "%s: %s - unexpected HCI SCO data packet. Connection does not exists, " \
953b06ebda0SMatthew Dillon "con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle);
954b06ebda0SMatthew Dillon
955b06ebda0SMatthew Dillon error = ENOENT;
956b06ebda0SMatthew Dillon goto drop;
957b06ebda0SMatthew Dillon }
958b06ebda0SMatthew Dillon
959b06ebda0SMatthew Dillon if (con->link_type != NG_HCI_LINK_SCO) {
960b06ebda0SMatthew Dillon NG_HCI_ERR(
961b06ebda0SMatthew Dillon "%s: %s - unexpected HCI SCO data packet. Not SCO link, con_handle=%d, " \
962b06ebda0SMatthew Dillon "link_type=%d\n", __func__, NG_NODE_NAME(unit->node),
963b06ebda0SMatthew Dillon con_handle, con->link_type);
964b06ebda0SMatthew Dillon
965b06ebda0SMatthew Dillon error = EINVAL;
966b06ebda0SMatthew Dillon goto drop;
967b06ebda0SMatthew Dillon }
968b06ebda0SMatthew Dillon
969b06ebda0SMatthew Dillon if (con->state != NG_HCI_CON_OPEN) {
970b06ebda0SMatthew Dillon NG_HCI_ERR(
971b06ebda0SMatthew Dillon "%s: %s - unexpected HCI SCO data packet. Invalid connection state=%d, " \
972b06ebda0SMatthew Dillon "con_handle=%d\n", __func__, NG_NODE_NAME(unit->node),
973b06ebda0SMatthew Dillon con->state, con_handle);
974b06ebda0SMatthew Dillon
975b06ebda0SMatthew Dillon error = EHOSTDOWN;
976b06ebda0SMatthew Dillon goto drop;
977b06ebda0SMatthew Dillon }
978b06ebda0SMatthew Dillon
979b06ebda0SMatthew Dillon if (NG_BT_ITEMQ_FULL(&con->conq)) {
980b06ebda0SMatthew Dillon NG_HCI_ALERT(
981b06ebda0SMatthew Dillon "%s: %s - dropping HCI SCO data packet, con_handle=%d, len=%d, queue_len=%d\n",
982b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node), con_handle,
983b06ebda0SMatthew Dillon m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));
984b06ebda0SMatthew Dillon
985b06ebda0SMatthew Dillon NG_BT_ITEMQ_DROP(&con->conq);
986b06ebda0SMatthew Dillon
987b06ebda0SMatthew Dillon error = ENOBUFS;
988b06ebda0SMatthew Dillon goto drop;
989b06ebda0SMatthew Dillon }
990b06ebda0SMatthew Dillon
991b06ebda0SMatthew Dillon /* Queue item and schedule data transfer */
992b06ebda0SMatthew Dillon NGI_M(item) = m;
993*e85b99abSSascha Wildner ng_ref_item(item);
994b06ebda0SMatthew Dillon NG_BT_ITEMQ_ENQUEUE(&con->conq, item);
995b06ebda0SMatthew Dillon item = NULL;
996b06ebda0SMatthew Dillon m = NULL;
997b06ebda0SMatthew Dillon
998b06ebda0SMatthew Dillon ng_hci_send_data(unit);
999b06ebda0SMatthew Dillon drop:
1000b06ebda0SMatthew Dillon if (item != NULL)
1001b06ebda0SMatthew Dillon NG_FREE_ITEM(item);
1002b06ebda0SMatthew Dillon
1003b06ebda0SMatthew Dillon NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
1004b06ebda0SMatthew Dillon
1005b06ebda0SMatthew Dillon return (error);
1006b06ebda0SMatthew Dillon } /* ng_hci_sco_rcvdata */
1007b06ebda0SMatthew Dillon
1008b06ebda0SMatthew Dillon /*
1009b06ebda0SMatthew Dillon * Process data packet from uptream RAW hook.
1010b06ebda0SMatthew Dillon * We expect valid HCI command packets.
1011b06ebda0SMatthew Dillon */
1012b06ebda0SMatthew Dillon
1013b06ebda0SMatthew Dillon static int
ng_hci_raw_rcvdata(hook_p hook,item_p item)1014b06ebda0SMatthew Dillon ng_hci_raw_rcvdata(hook_p hook, item_p item)
1015b06ebda0SMatthew Dillon {
1016b06ebda0SMatthew Dillon ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1017b06ebda0SMatthew Dillon struct mbuf *m = NULL;
1018b06ebda0SMatthew Dillon int error = 0;
1019b06ebda0SMatthew Dillon
1020b06ebda0SMatthew Dillon NGI_GET_M(item, m);
1021b06ebda0SMatthew Dillon NG_FREE_ITEM(item);
1022b06ebda0SMatthew Dillon
1023b06ebda0SMatthew Dillon /* Check packet */
1024b06ebda0SMatthew Dillon if (*mtod(m, u_int8_t *) != NG_HCI_CMD_PKT) {
1025b06ebda0SMatthew Dillon NG_HCI_ALERT(
1026b06ebda0SMatthew Dillon "%s: %s - invalid HCI command packet type=%#x\n",
1027b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node),
1028b06ebda0SMatthew Dillon *mtod(m, u_int8_t *));
1029b06ebda0SMatthew Dillon
1030b06ebda0SMatthew Dillon error = EINVAL;
1031b06ebda0SMatthew Dillon goto drop;
1032b06ebda0SMatthew Dillon }
1033b06ebda0SMatthew Dillon
1034b06ebda0SMatthew Dillon if (m->m_pkthdr.len < sizeof(ng_hci_cmd_pkt_t)) {
1035b06ebda0SMatthew Dillon NG_HCI_ALERT(
1036b06ebda0SMatthew Dillon "%s: %s - invalid HCI command packet len=%d\n",
1037b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len);
1038b06ebda0SMatthew Dillon
1039b06ebda0SMatthew Dillon error = EMSGSIZE;
1040b06ebda0SMatthew Dillon goto drop;
1041b06ebda0SMatthew Dillon }
1042b06ebda0SMatthew Dillon
1043b06ebda0SMatthew Dillon NG_HCI_M_PULLUP(m, sizeof(ng_hci_cmd_pkt_t));
1044b06ebda0SMatthew Dillon if (m == NULL) {
1045b06ebda0SMatthew Dillon error = ENOBUFS;
1046b06ebda0SMatthew Dillon goto drop;
1047b06ebda0SMatthew Dillon }
1048b06ebda0SMatthew Dillon
1049b06ebda0SMatthew Dillon if (m->m_pkthdr.len !=
1050b06ebda0SMatthew Dillon mtod(m, ng_hci_cmd_pkt_t *)->length + sizeof(ng_hci_cmd_pkt_t)) {
1051b06ebda0SMatthew Dillon NG_HCI_ALERT(
1052b06ebda0SMatthew Dillon "%s: %s - invalid HCI command packet size, len=%d, length=%d\n",
1053b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,
1054b06ebda0SMatthew Dillon mtod(m, ng_hci_cmd_pkt_t *)->length);
1055b06ebda0SMatthew Dillon
1056b06ebda0SMatthew Dillon error = EMSGSIZE;
1057b06ebda0SMatthew Dillon goto drop;
1058b06ebda0SMatthew Dillon }
1059b06ebda0SMatthew Dillon
1060b06ebda0SMatthew Dillon if (mtod(m, ng_hci_cmd_pkt_t *)->opcode == 0) {
1061b06ebda0SMatthew Dillon NG_HCI_ALERT(
1062b06ebda0SMatthew Dillon "%s: %s - invalid HCI command opcode\n",
1063b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node));
1064b06ebda0SMatthew Dillon
1065b06ebda0SMatthew Dillon error = EINVAL;
1066b06ebda0SMatthew Dillon goto drop;
1067b06ebda0SMatthew Dillon }
1068b06ebda0SMatthew Dillon
1069b06ebda0SMatthew Dillon if (NG_BT_MBUFQ_FULL(&unit->cmdq)) {
1070b06ebda0SMatthew Dillon NG_HCI_ALERT(
1071b06ebda0SMatthew Dillon "%s: %s - dropping HCI command packet, len=%d, queue_len=%d\n",
1072b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,
1073b06ebda0SMatthew Dillon NG_BT_MBUFQ_LEN(&unit->cmdq));
1074b06ebda0SMatthew Dillon
1075b06ebda0SMatthew Dillon NG_BT_MBUFQ_DROP(&unit->cmdq);
1076b06ebda0SMatthew Dillon
1077b06ebda0SMatthew Dillon error = ENOBUFS;
1078b06ebda0SMatthew Dillon goto drop;
1079b06ebda0SMatthew Dillon }
1080b06ebda0SMatthew Dillon
1081b06ebda0SMatthew Dillon /* Queue and send command */
1082b06ebda0SMatthew Dillon NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1083b06ebda0SMatthew Dillon m = NULL;
1084b06ebda0SMatthew Dillon
1085b06ebda0SMatthew Dillon if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1086b06ebda0SMatthew Dillon error = ng_hci_send_command(unit);
1087b06ebda0SMatthew Dillon drop:
1088b06ebda0SMatthew Dillon NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
1089b06ebda0SMatthew Dillon
1090b06ebda0SMatthew Dillon return (error);
1091b06ebda0SMatthew Dillon } /* ng_hci_raw_rcvdata */
1092b06ebda0SMatthew Dillon
1093