xref: /dflybsd-src/sys/netgraph7/bluetooth/hci/ng_hci_main.c (revision e85b99abf6da4a83a7dc495b0ef37ce19864149f)
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