ng_hci_main.c revision 107120
1107120Sjulian/*
2107120Sjulian * ng_hci_main.c
3107120Sjulian *
4107120Sjulian * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
5107120Sjulian * All rights reserved.
6107120Sjulian *
7107120Sjulian * Redistribution and use in source and binary forms, with or without
8107120Sjulian * modification, are permitted provided that the following conditions
9107120Sjulian * are met:
10107120Sjulian * 1. Redistributions of source code must retain the above copyright
11107120Sjulian *    notice, this list of conditions and the following disclaimer.
12107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright
13107120Sjulian *    notice, this list of conditions and the following disclaimer in the
14107120Sjulian *    documentation and/or other materials provided with the distribution.
15107120Sjulian *
16107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26107120Sjulian * SUCH DAMAGE.
27107120Sjulian *
28107120Sjulian * $Id: ng_hci_main.c,v 1.28 2002/11/12 22:35:40 max Exp $
29107120Sjulian * $FreeBSD: head/sys/netgraph/bluetooth/hci/ng_hci_main.c 107120 2002-11-20 23:01:59Z julian $
30107120Sjulian */
31107120Sjulian
32107120Sjulian#include <sys/param.h>
33107120Sjulian#include <sys/systm.h>
34107120Sjulian#include <sys/kernel.h>
35107120Sjulian#include <sys/endian.h>
36107120Sjulian#include <sys/malloc.h>
37107120Sjulian#include <sys/mbuf.h>
38107120Sjulian#include <sys/queue.h>
39107120Sjulian#include <netgraph/ng_message.h>
40107120Sjulian#include <netgraph/netgraph.h>
41107120Sjulian#include <netgraph/ng_parse.h>
42107120Sjulian#include "ng_bluetooth.h"
43107120Sjulian#include "ng_hci.h"
44107120Sjulian#include "ng_hci_var.h"
45107120Sjulian#include "ng_hci_prse.h"
46107120Sjulian#include "ng_hci_cmds.h"
47107120Sjulian#include "ng_hci_evnt.h"
48107120Sjulian#include "ng_hci_ulpi.h"
49107120Sjulian#include "ng_hci_misc.h"
50107120Sjulian
51107120Sjulian/******************************************************************************
52107120Sjulian ******************************************************************************
53107120Sjulian **     This node implements Bluetooth Host Controller Interface (HCI)
54107120Sjulian ******************************************************************************
55107120Sjulian ******************************************************************************/
56107120Sjulian
57107120Sjulian/* MALLOC define */
58107120Sjulian#ifdef NG_SEPARATE_MALLOC
59107120SjulianMALLOC_DEFINE(M_NETGRAPH_HCI, "netgraph_hci", "Netgraph Bluetooth HCI node");
60107120Sjulian#else
61107120Sjulian#define M_NETGRAPH_HCI M_NETGRAPH
62107120Sjulian#endif /* NG_SEPARATE_MALLOC */
63107120Sjulian
64107120Sjulian/* Netgraph node methods */
65107120Sjulianstatic	ng_constructor_t	ng_hci_constructor;
66107120Sjulianstatic	ng_shutdown_t		ng_hci_shutdown;
67107120Sjulianstatic	ng_newhook_t		ng_hci_newhook;
68107120Sjulianstatic	ng_connect_t		ng_hci_connect;
69107120Sjulianstatic	ng_disconnect_t		ng_hci_disconnect;
70107120Sjulianstatic	ng_rcvmsg_t		ng_hci_default_rcvmsg;
71107120Sjulianstatic	ng_rcvmsg_t		ng_hci_upper_rcvmsg;
72107120Sjulianstatic	ng_rcvdata_t		ng_hci_drv_rcvdata;
73107120Sjulianstatic	ng_rcvdata_t		ng_hci_acl_rcvdata;
74107120Sjulianstatic	ng_rcvdata_t		ng_hci_sco_rcvdata;
75107120Sjulianstatic	ng_rcvdata_t		ng_hci_raw_rcvdata;
76107120Sjulian
77107120Sjulian/* Netgraph node type descriptor */
78107120Sjulianstatic	struct ng_type		typestruct = {
79107120Sjulian	NG_ABI_VERSION,
80107120Sjulian	NG_HCI_NODE_TYPE,	/* typename */
81107120Sjulian	NULL,			/* modevent */
82107120Sjulian	ng_hci_constructor,	/* constructor */
83107120Sjulian	ng_hci_default_rcvmsg,	/* control message */
84107120Sjulian	ng_hci_shutdown,	/* destructor */
85107120Sjulian	ng_hci_newhook,		/* new hook */
86107120Sjulian	NULL,			/* findhook */
87107120Sjulian	ng_hci_connect,		/* connect hook */
88107120Sjulian	ng_hci_drv_rcvdata,	/* data */
89107120Sjulian	ng_hci_disconnect,	/* disconnect hook */
90107120Sjulian	ng_hci_cmdlist		/* node command list */
91107120Sjulian};
92107120SjulianNETGRAPH_INIT(hci, &typestruct);
93107120SjulianMODULE_VERSION(ng_hci, NG_BLUETOOTH_VERSION);
94107120SjulianMODULE_DEPEND(ng_hci, ng_bluetooth, NG_BLUETOOTH_VERSION,
95107120Sjulian	NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
96107120Sjulian
97107120Sjulian/*****************************************************************************
98107120Sjulian *****************************************************************************
99107120Sjulian **                   Netgraph methods implementation
100107120Sjulian *****************************************************************************
101107120Sjulian *****************************************************************************/
102107120Sjulian
103107120Sjulian/*
104107120Sjulian * Create new instance of HCI node (new unit)
105107120Sjulian */
106107120Sjulian
107107120Sjulianstatic int
108107120Sjulianng_hci_constructor(node_p node)
109107120Sjulian{
110107120Sjulian	ng_hci_unit_p	unit = NULL;
111107120Sjulian
112107120Sjulian	MALLOC(unit, ng_hci_unit_p, sizeof(*unit), M_NETGRAPH_HCI,
113107120Sjulian		M_NOWAIT | M_ZERO);
114107120Sjulian	if (unit == NULL)
115107120Sjulian		return (ENOMEM);
116107120Sjulian
117107120Sjulian	unit->node = node;
118107120Sjulian	unit->debug = NG_HCI_WARN_LEVEL;
119107120Sjulian
120107120Sjulian	unit->link_policy_mask = 0xffff; /* Enable all supported modes */
121107120Sjulian	unit->packet_mask = 0xffff; /* Enable all packet types */
122107120Sjulian
123107120Sjulian	/*
124107120Sjulian	 * Set default buffer info
125107120Sjulian	 *
126107120Sjulian	 * One HCI command
127107120Sjulian	 * One ACL packet with max. size of 17 bytes (1 DM1 packet)
128107120Sjulian	 * One SCO packet with max. size of 10 bytes (1 HV1 packet)
129107120Sjulian	 */
130107120Sjulian
131107120Sjulian	NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
132107120Sjulian	NG_HCI_BUFF_ACL_SET(unit->buffer, 1, 17, 1);
133107120Sjulian	NG_HCI_BUFF_SCO_SET(unit->buffer, 1, 10, 1);
134107120Sjulian
135107120Sjulian	/* Init command queue & command timeout handler */
136107120Sjulian	callout_handle_init(&unit->cmd_timo);
137107120Sjulian	NG_BT_MBUFQ_INIT(&unit->cmdq, NG_HCI_CMD_QUEUE_LEN);
138107120Sjulian
139107120Sjulian	/* Init lists */
140107120Sjulian	LIST_INIT(&unit->con_list);
141107120Sjulian	LIST_INIT(&unit->neighbors);
142107120Sjulian
143107120Sjulian	/*
144107120Sjulian	 * This node has to be a WRITER because both data and messages
145107120Sjulian	 * can change node state.
146107120Sjulian	 */
147107120Sjulian
148107120Sjulian	NG_NODE_FORCE_WRITER(node);
149107120Sjulian	NG_NODE_SET_PRIVATE(node, unit);
150107120Sjulian
151107120Sjulian	return (0);
152107120Sjulian} /* ng_hci_constructor */
153107120Sjulian
154107120Sjulian/*
155107120Sjulian * Destroy the node
156107120Sjulian */
157107120Sjulian
158107120Sjulianstatic int
159107120Sjulianng_hci_shutdown(node_p node)
160107120Sjulian{
161107120Sjulian	ng_hci_unit_p	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
162107120Sjulian
163107120Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
164107120Sjulian	NG_NODE_UNREF(node);
165107120Sjulian
166107120Sjulian	unit->node = NULL;
167107120Sjulian	ng_hci_unit_clean(unit, 0x16 /* Connection terminated by local host */);
168107120Sjulian
169107120Sjulian	NG_BT_MBUFQ_DESTROY(&unit->cmdq);
170107120Sjulian
171107120Sjulian	bzero(unit, sizeof(*unit));
172107120Sjulian	FREE(unit, M_NETGRAPH_HCI);
173107120Sjulian
174107120Sjulian	return (0);
175107120Sjulian} /* ng_hci_shutdown */
176107120Sjulian
177107120Sjulian/*
178107120Sjulian * Give our OK for a hook to be added. Unit driver is connected to the driver
179107120Sjulian * (NG_HCI_HOOK_DRV) hook. Upper layer protocols are connected to appropriate
180107120Sjulian * (NG_HCI_HOOK_ACL or NG_HCI_HOOK_SCO) hooks.
181107120Sjulian */
182107120Sjulian
183107120Sjulianstatic int
184107120Sjulianng_hci_newhook(node_p node, hook_p hook, char const *name)
185107120Sjulian{
186107120Sjulian	ng_hci_unit_p	 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
187107120Sjulian	hook_p		*h = NULL;
188107120Sjulian
189107120Sjulian	if (strcmp(name, NG_HCI_HOOK_DRV) == 0)
190107120Sjulian		h = &unit->drv;
191107120Sjulian	else if (strcmp(name, NG_HCI_HOOK_ACL) == 0)
192107120Sjulian		h = &unit->acl;
193107120Sjulian	else if (strcmp(name, NG_HCI_HOOK_SCO) == 0)
194107120Sjulian		h = &unit->sco;
195107120Sjulian	else if (strcmp(name, NG_HCI_HOOK_RAW) == 0)
196107120Sjulian		h = &unit->raw;
197107120Sjulian	else
198107120Sjulian		return (EINVAL);
199107120Sjulian
200107120Sjulian	if (*h != NULL)
201107120Sjulian		return (EISCONN);
202107120Sjulian
203107120Sjulian	*h = hook;
204107120Sjulian
205107120Sjulian	return (0);
206107120Sjulian} /* ng_hci_newhook */
207107120Sjulian
208107120Sjulian/*
209107120Sjulian * Give our final OK to connect hook
210107120Sjulian */
211107120Sjulian
212107120Sjulianstatic int
213107120Sjulianng_hci_connect(hook_p hook)
214107120Sjulian{
215107120Sjulian	ng_hci_unit_p	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
216107120Sjulian
217107120Sjulian	if (hook != unit->drv) {
218107120Sjulian		if (hook == unit->acl) {
219107120Sjulian			NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);
220107120Sjulian			NG_HOOK_SET_RCVDATA(hook, ng_hci_acl_rcvdata);
221107120Sjulian		} else if (hook == unit->sco) {
222107120Sjulian			NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);
223107120Sjulian			NG_HOOK_SET_RCVDATA(hook, ng_hci_sco_rcvdata);
224107120Sjulian		} else
225107120Sjulian			NG_HOOK_SET_RCVDATA(hook, ng_hci_raw_rcvdata);
226107120Sjulian
227107120Sjulian		/* Send delayed notification to the upper layers */
228107120Sjulian		if (hook != unit->raw)
229107120Sjulian			ng_send_fn(unit->node, hook, ng_hci_node_is_up, NULL,0);
230107120Sjulian	} else
231107120Sjulian		unit->state |= NG_HCI_UNIT_CONNECTED;
232107120Sjulian
233107120Sjulian	return (0);
234107120Sjulian} /* ng_hci_connect */
235107120Sjulian
236107120Sjulian/*
237107120Sjulian * Disconnect the hook
238107120Sjulian */
239107120Sjulian
240107120Sjulianstatic int
241107120Sjulianng_hci_disconnect(hook_p hook)
242107120Sjulian{
243107120Sjulian	ng_hci_unit_p	 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
244107120Sjulian
245107120Sjulian	if (hook == unit->acl)
246107120Sjulian		unit->acl = NULL;
247107120Sjulian	else if (hook == unit->sco)
248107120Sjulian		unit->sco = NULL;
249107120Sjulian	else if (hook == unit->raw)
250107120Sjulian		unit->raw = NULL;
251107120Sjulian	else if (hook == unit->drv) {
252107120Sjulian		unit->drv = NULL;
253107120Sjulian
254107120Sjulian		/* Connection terminated by local host */
255107120Sjulian		ng_hci_unit_clean(unit, 0x16);
256107120Sjulian		unit->state &= ~(NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED);
257107120Sjulian	} else
258107120Sjulian		return (EINVAL);
259107120Sjulian
260107120Sjulian	/* Shutdown when all hooks are disconnected */
261107120Sjulian	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
262107120Sjulian	    (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
263107120Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));
264107120Sjulian
265107120Sjulian	return (0);
266107120Sjulian} /* ng_hci_disconnect */
267107120Sjulian
268107120Sjulian/*
269107120Sjulian * Default control message processing routine. Control message could be:
270107120Sjulian *
271107120Sjulian * 1) GENERIC Netgraph messages
272107120Sjulian *
273107120Sjulian * 2) Control message directed to the node itself.
274107120Sjulian */
275107120Sjulian
276107120Sjulianstatic int
277107120Sjulianng_hci_default_rcvmsg(node_p node, item_p item, hook_p lasthook)
278107120Sjulian{
279107120Sjulian	ng_hci_unit_p	 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
280107120Sjulian	struct ng_mesg	*msg = NULL, *rsp = NULL;
281107120Sjulian	int		 error = 0;
282107120Sjulian
283107120Sjulian	NGI_GET_MSG(item, msg);
284107120Sjulian
285107120Sjulian	switch (msg->header.typecookie) {
286107120Sjulian	case NGM_GENERIC_COOKIE:
287107120Sjulian		switch (msg->header.cmd) {
288107120Sjulian		case NGM_TEXT_STATUS: {
289107120Sjulian			int	cmd_avail,
290107120Sjulian				acl_total, acl_avail, acl_size,
291107120Sjulian				sco_total, sco_avail, sco_size;
292107120Sjulian
293107120Sjulian			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
294107120Sjulian			if (rsp == NULL) {
295107120Sjulian				error = ENOMEM;
296107120Sjulian				break;
297107120Sjulian			}
298107120Sjulian
299107120Sjulian			NG_HCI_BUFF_CMD_GET(unit->buffer, cmd_avail);
300107120Sjulian
301107120Sjulian			NG_HCI_BUFF_ACL_AVAIL(unit->buffer, acl_avail);
302107120Sjulian			NG_HCI_BUFF_ACL_TOTAL(unit->buffer, acl_total);
303107120Sjulian			NG_HCI_BUFF_ACL_SIZE(unit->buffer, acl_size);
304107120Sjulian
305107120Sjulian			NG_HCI_BUFF_SCO_AVAIL(unit->buffer, sco_avail);
306107120Sjulian			NG_HCI_BUFF_SCO_TOTAL(unit->buffer, sco_total);
307107120Sjulian			NG_HCI_BUFF_SCO_SIZE(unit->buffer, sco_size);
308107120Sjulian
309107120Sjulian			snprintf(rsp->data, NG_TEXTRESPONSE,
310107120Sjulian				"bdaddr %x:%x:%x:%x:%x:%x\n" \
311107120Sjulian				"Hooks  %s %s %s %s\n" \
312107120Sjulian				"State  %#x\n" \
313107120Sjulian				"Queue  cmd:%d\n" \
314107120Sjulian				"Buffer cmd:%d,acl:%d,%d,%d,sco:%d,%d,%d",
315107120Sjulian				unit->bdaddr.b[5], unit->bdaddr.b[4],
316107120Sjulian				unit->bdaddr.b[3], unit->bdaddr.b[2],
317107120Sjulian				unit->bdaddr.b[1], unit->bdaddr.b[0],
318107120Sjulian				(unit->drv != NULL)? NG_HCI_HOOK_DRV : "",
319107120Sjulian				(unit->acl != NULL)? NG_HCI_HOOK_ACL : "",
320107120Sjulian				(unit->sco != NULL)? NG_HCI_HOOK_SCO : "",
321107120Sjulian				(unit->raw != NULL)? NG_HCI_HOOK_RAW : "",
322107120Sjulian				unit->state,
323107120Sjulian				NG_BT_MBUFQ_LEN(&unit->cmdq),
324107120Sjulian				cmd_avail,
325107120Sjulian				acl_avail, acl_total, acl_size,
326107120Sjulian				sco_avail, sco_total, sco_size);
327107120Sjulian			} break;
328107120Sjulian
329107120Sjulian		default:
330107120Sjulian			error = EINVAL;
331107120Sjulian			break;
332107120Sjulian		}
333107120Sjulian		break;
334107120Sjulian
335107120Sjulian	case NGM_HCI_COOKIE:
336107120Sjulian		switch (msg->header.cmd) {
337107120Sjulian		/* Get current node state */
338107120Sjulian		case NGM_HCI_NODE_GET_STATE:
339107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(unit->state), M_NOWAIT);
340107120Sjulian			if (rsp == NULL) {
341107120Sjulian				error = ENOMEM;
342107120Sjulian				break;
343107120Sjulian			}
344107120Sjulian
345107120Sjulian			*((ng_hci_node_state_ep *)(rsp->data)) = unit->state;
346107120Sjulian			break;
347107120Sjulian
348107120Sjulian		/* Turn INITED bit - node initialized */
349107120Sjulian		case NGM_HCI_NODE_INIT:
350107120Sjulian			if (bcmp(&unit->bdaddr, NG_HCI_BDADDR_ANY,
351107120Sjulian					sizeof(bdaddr_t)) == 0) {
352107120Sjulian				error = ENXIO;
353107120Sjulian				break;
354107120Sjulian			}
355107120Sjulian
356107120Sjulian			unit->state |= NG_HCI_UNIT_INITED;
357107120Sjulian
358107120Sjulian			ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);
359107120Sjulian			ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);
360107120Sjulian			break;
361107120Sjulian
362107120Sjulian		/* Get node debug level */
363107120Sjulian		case NGM_HCI_NODE_GET_DEBUG:
364107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(unit->debug), M_NOWAIT);
365107120Sjulian			if (rsp == NULL) {
366107120Sjulian				error = ENOMEM;
367107120Sjulian				break;
368107120Sjulian			}
369107120Sjulian
370107120Sjulian			*((ng_hci_node_debug_ep *)(rsp->data)) = unit->debug;
371107120Sjulian			break;
372107120Sjulian
373107120Sjulian		/* Set node debug level */
374107120Sjulian		case NGM_HCI_NODE_SET_DEBUG:
375107120Sjulian			if (msg->header.arglen != sizeof(ng_hci_node_debug_ep)){
376107120Sjulian				error = EMSGSIZE;
377107120Sjulian				break;
378107120Sjulian			}
379107120Sjulian
380107120Sjulian			unit->debug = *((ng_hci_node_debug_ep *)(msg->data));
381107120Sjulian			break;
382107120Sjulian
383107120Sjulian		/* Get buffer info */
384107120Sjulian		case NGM_HCI_NODE_GET_BUFFER: {
385107120Sjulian			ng_hci_node_buffer_ep	*ep = NULL;
386107120Sjulian
387107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(ng_hci_node_buffer_ep),
388107120Sjulian				M_NOWAIT);
389107120Sjulian			if (rsp == NULL) {
390107120Sjulian				error = ENOMEM;
391107120Sjulian				break;
392107120Sjulian			}
393107120Sjulian
394107120Sjulian			ep = (ng_hci_node_buffer_ep *)(rsp->data);
395107120Sjulian
396107120Sjulian			NG_HCI_BUFF_CMD_GET(unit->buffer, ep->cmd_free);
397107120Sjulian			NG_HCI_BUFF_ACL_AVAIL(unit->buffer, ep->acl_free);
398107120Sjulian			NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->acl_pkts);
399107120Sjulian			NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->acl_size);
400107120Sjulian			NG_HCI_BUFF_SCO_AVAIL(unit->buffer, ep->sco_free);
401107120Sjulian			NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->sco_pkts);
402107120Sjulian			NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->sco_size);
403107120Sjulian			} break;
404107120Sjulian
405107120Sjulian		/* Get BDADDR */
406107120Sjulian		case NGM_HCI_NODE_GET_BDADDR:
407107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(bdaddr_t), M_NOWAIT);
408107120Sjulian			if (rsp == NULL) {
409107120Sjulian				error = ENOMEM;
410107120Sjulian				break;
411107120Sjulian			}
412107120Sjulian
413107120Sjulian			bcopy(&unit->bdaddr, rsp->data, sizeof(bdaddr_t));
414107120Sjulian			break;
415107120Sjulian
416107120Sjulian		/* Get features */
417107120Sjulian		case NGM_HCI_NODE_GET_FEATURES:
418107120Sjulian			NG_MKRESPONSE(rsp,msg,sizeof(unit->features),M_NOWAIT);
419107120Sjulian			if (rsp == NULL) {
420107120Sjulian				error = ENOMEM;
421107120Sjulian				break;
422107120Sjulian			}
423107120Sjulian
424107120Sjulian			bcopy(&unit->features,rsp->data,sizeof(unit->features));
425107120Sjulian			break;
426107120Sjulian
427107120Sjulian		/* Get stat */
428107120Sjulian		case NGM_HCI_NODE_GET_STAT:
429107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(unit->stat), M_NOWAIT);
430107120Sjulian			if (rsp == NULL) {
431107120Sjulian				error = ENOMEM;
432107120Sjulian				break;
433107120Sjulian			}
434107120Sjulian
435107120Sjulian			bcopy(&unit->stat, rsp->data, sizeof(unit->stat));
436107120Sjulian			break;
437107120Sjulian
438107120Sjulian		/* Reset stat */
439107120Sjulian		case NGM_HCI_NODE_RESET_STAT:
440107120Sjulian			NG_HCI_STAT_RESET(unit->stat);
441107120Sjulian			break;
442107120Sjulian
443107120Sjulian		/* Clean up neighbors list */
444107120Sjulian		case NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE:
445107120Sjulian			ng_hci_flush_neighbor_cache(unit);
446107120Sjulian			break;
447107120Sjulian
448107120Sjulian		/* Get neighbor cache entries */
449107120Sjulian		case NGM_HCI_NODE_GET_NEIGHBOR_CACHE: {
450107120Sjulian			ng_hci_neighbor_p			 n = NULL;
451107120Sjulian			ng_hci_node_get_neighbor_cache_ep	*e1 = NULL;
452107120Sjulian			ng_hci_node_neighbor_cache_entry_ep	*e2 = NULL;
453107120Sjulian			int					 s = 0;
454107120Sjulian
455107120Sjulian			/* Look for the fresh entries in the cache */
456107120Sjulian			for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {
457107120Sjulian				ng_hci_neighbor_p	nn = LIST_NEXT(n, next);
458107120Sjulian
459107120Sjulian				if (ng_hci_neighbor_stale(n))
460107120Sjulian					ng_hci_free_neighbor(n);
461107120Sjulian				else
462107120Sjulian					s ++;
463107120Sjulian
464107120Sjulian				n = nn;
465107120Sjulian			}
466107120Sjulian			if (s > NG_HCI_MAX_NEIGHBOR_NUM)
467107120Sjulian				s = NG_HCI_MAX_NEIGHBOR_NUM;
468107120Sjulian
469107120Sjulian			/* Prepare response */
470107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),
471107120Sjulian				M_NOWAIT);
472107120Sjulian			if (rsp == NULL) {
473107120Sjulian				error = ENOMEM;
474107120Sjulian				break;
475107120Sjulian			}
476107120Sjulian
477107120Sjulian			e1 = (ng_hci_node_get_neighbor_cache_ep *)(rsp->data);
478107120Sjulian			e2 = (ng_hci_node_neighbor_cache_entry_ep *)(e1 + 1);
479107120Sjulian
480107120Sjulian			e1->num_entries = s;
481107120Sjulian
482107120Sjulian			LIST_FOREACH(n, &unit->neighbors, next) {
483107120Sjulian				e2->page_scan_rep_mode = n->page_scan_rep_mode;
484107120Sjulian				e2->page_scan_mode = n->page_scan_mode;
485107120Sjulian				e2->clock_offset = n->clock_offset;
486107120Sjulian				bcopy(&n->bdaddr, &e2->bdaddr,
487107120Sjulian					sizeof(e2->bdaddr));
488107120Sjulian				bcopy(&n->features, &e2->features,
489107120Sjulian					sizeof(e2->features));
490107120Sjulian
491107120Sjulian				e2 ++;
492107120Sjulian				if (--s <= 0)
493107120Sjulian					break;
494107120Sjulian			}
495107120Sjulian			} break;
496107120Sjulian
497107120Sjulian		/* Get connection list */
498107120Sjulian		case NGM_HCI_NODE_GET_CON_LIST: {
499107120Sjulian			ng_hci_unit_con_p	 c = NULL;
500107120Sjulian			ng_hci_node_con_list_ep	*e1 = NULL;
501107120Sjulian			ng_hci_node_con_ep	*e2 = NULL;
502107120Sjulian			int			 s = 0;
503107120Sjulian
504107120Sjulian			/* Count number of connections in the list */
505107120Sjulian			LIST_FOREACH(c, &unit->con_list, next)
506107120Sjulian				s ++;
507107120Sjulian			if (s > NG_HCI_MAX_CON_NUM)
508107120Sjulian				s = NG_HCI_MAX_CON_NUM;
509107120Sjulian
510107120Sjulian			/* Prepare response */
511107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),
512107120Sjulian				M_NOWAIT);
513107120Sjulian			if (rsp == NULL) {
514107120Sjulian				error = ENOMEM;
515107120Sjulian				break;
516107120Sjulian			}
517107120Sjulian
518107120Sjulian			e1 = (ng_hci_node_con_list_ep *)(rsp->data);
519107120Sjulian			e2 = (ng_hci_node_con_ep *)(e1 + 1);
520107120Sjulian
521107120Sjulian			e1->num_connections = s;
522107120Sjulian
523107120Sjulian			LIST_FOREACH(c, &unit->con_list, next) {
524107120Sjulian				e2->link_type = c->link_type;
525107120Sjulian				e2->encryption_mode= c->encryption_mode;
526107120Sjulian				e2->mode = c->mode;
527107120Sjulian				e2->role = c->role;
528107120Sjulian
529107120Sjulian				e2->state = c->state;
530107120Sjulian
531107120Sjulian				e2->pending = c->pending;
532107120Sjulian				e2->queue_len = NG_BT_ITEMQ_LEN(&c->conq);
533107120Sjulian
534107120Sjulian				e2->con_handle = c->con_handle;
535107120Sjulian				bcopy(&c->bdaddr, &e2->bdaddr,
536107120Sjulian					sizeof(e2->bdaddr));
537107120Sjulian
538107120Sjulian
539107120Sjulian				e2 ++;
540107120Sjulian				if (--s <= 0)
541107120Sjulian					break;
542107120Sjulian			}
543107120Sjulian			} break;
544107120Sjulian
545107120Sjulian		/* Get link policy settings mask */
546107120Sjulian		case NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK:
547107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(unit->link_policy_mask),
548107120Sjulian				M_NOWAIT);
549107120Sjulian			if (rsp == NULL) {
550107120Sjulian				error = ENOMEM;
551107120Sjulian				break;
552107120Sjulian			}
553107120Sjulian
554107120Sjulian			*((ng_hci_node_link_policy_mask_ep *)(rsp->data)) =
555107120Sjulian				unit->link_policy_mask;
556107120Sjulian			break;
557107120Sjulian
558107120Sjulian		/* Set link policy settings mask */
559107120Sjulian		case NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK:
560107120Sjulian			if (msg->header.arglen !=
561107120Sjulian				sizeof(ng_hci_node_link_policy_mask_ep)) {
562107120Sjulian				error = EMSGSIZE;
563107120Sjulian				break;
564107120Sjulian			}
565107120Sjulian
566107120Sjulian			unit->link_policy_mask =
567107120Sjulian				*((ng_hci_node_link_policy_mask_ep *)
568107120Sjulian					(msg->data));
569107120Sjulian			break;
570107120Sjulian
571107120Sjulian		/* Get packet mask */
572107120Sjulian		case NGM_HCI_NODE_GET_PACKET_MASK:
573107120Sjulian			NG_MKRESPONSE(rsp, msg, sizeof(unit->packet_mask),
574107120Sjulian				M_NOWAIT);
575107120Sjulian			if (rsp == NULL) {
576107120Sjulian				error = ENOMEM;
577107120Sjulian				break;
578107120Sjulian			}
579107120Sjulian
580107120Sjulian			*((ng_hci_node_packet_mask_ep *)(rsp->data)) =
581107120Sjulian				unit->packet_mask;
582107120Sjulian			break;
583107120Sjulian
584107120Sjulian		/* Set packet mask */
585107120Sjulian		case NGM_HCI_NODE_SET_PACKET_MASK:
586107120Sjulian			if (msg->header.arglen !=
587107120Sjulian					sizeof(ng_hci_node_packet_mask_ep)) {
588107120Sjulian				error = EMSGSIZE;
589107120Sjulian				break;
590107120Sjulian			}
591107120Sjulian
592107120Sjulian			unit->packet_mask =
593107120Sjulian				*((ng_hci_node_packet_mask_ep *)(msg->data));
594107120Sjulian			break;
595107120Sjulian
596107120Sjulian		default:
597107120Sjulian			error = EINVAL;
598107120Sjulian			break;
599107120Sjulian		}
600107120Sjulian		break;
601107120Sjulian
602107120Sjulian	default:
603107120Sjulian		error = EINVAL;
604107120Sjulian		break;
605107120Sjulian	}
606107120Sjulian
607107120Sjulian	/* NG_RESPOND_MSG should take care of "item" and "rsp" */
608107120Sjulian	NG_RESPOND_MSG(error, node, item, rsp);
609107120Sjulian	NG_FREE_MSG(msg);
610107120Sjulian
611107120Sjulian	return (error);
612107120Sjulian} /* ng_hci_default_rcvmsg */
613107120Sjulian
614107120Sjulian/*
615107120Sjulian * Process control message from upstream hooks (ACL and SCO).
616107120Sjulian * Handle LP_xxx messages here, give everything else to default routine.
617107120Sjulian */
618107120Sjulian
619107120Sjulianstatic int
620107120Sjulianng_hci_upper_rcvmsg(node_p node, item_p item, hook_p lasthook)
621107120Sjulian{
622107120Sjulian	ng_hci_unit_p	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
623107120Sjulian	int		error = 0;
624107120Sjulian
625107120Sjulian	switch (NGI_MSG(item)->header.typecookie) {
626107120Sjulian	case NGM_HCI_COOKIE:
627107120Sjulian		switch (NGI_MSG(item)->header.cmd) {
628107120Sjulian		case NGM_HCI_LP_CON_REQ:
629107120Sjulian			error = ng_hci_lp_con_req(unit, item, lasthook);
630107120Sjulian			break;
631107120Sjulian
632107120Sjulian		case NGM_HCI_LP_DISCON_REQ: /* XXX not defined by specs */
633107120Sjulian			error = ng_hci_lp_discon_req(unit, item, lasthook);
634107120Sjulian			break;
635107120Sjulian
636107120Sjulian		case NGM_HCI_LP_CON_RSP:
637107120Sjulian			error = ng_hci_lp_con_rsp(unit, item, lasthook);
638107120Sjulian			break;
639107120Sjulian
640107120Sjulian		case NGM_HCI_LP_QOS_REQ:
641107120Sjulian			error = ng_hci_lp_qos_req(unit, item, lasthook);
642107120Sjulian			break;
643107120Sjulian
644107120Sjulian		default:
645107120Sjulian			error = ng_hci_default_rcvmsg(node, item, lasthook);
646107120Sjulian			break;
647107120Sjulian		}
648107120Sjulian		break;
649107120Sjulian
650107120Sjulian	default:
651107120Sjulian		error = ng_hci_default_rcvmsg(node, item, lasthook);
652107120Sjulian		break;
653107120Sjulian	}
654107120Sjulian
655107120Sjulian	return (error);
656107120Sjulian} /* ng_hci_upper_rcvmsg */
657107120Sjulian
658107120Sjulian/*
659107120Sjulian * Process data packet from the driver hook.
660107120Sjulian * We expect HCI events, ACL or SCO data packets.
661107120Sjulian */
662107120Sjulian
663107120Sjulianstatic int
664107120Sjulianng_hci_drv_rcvdata(hook_p hook, item_p item)
665107120Sjulian{
666107120Sjulian	ng_hci_unit_p	 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
667107120Sjulian	struct mbuf	*m = NULL;
668107120Sjulian	int		 error = 0;
669107120Sjulian
670107120Sjulian	/* Process packet */
671107120Sjulian	m = NGI_M(item); /* item still has mbuf, just peeking */
672107120Sjulian	m->m_flags |= M_PROTO1; /* mark as incoming packet */
673107120Sjulian
674107120Sjulian	NG_HCI_STAT_BYTES_RECV(unit->stat, m->m_pkthdr.len);
675107120Sjulian
676107120Sjulian	/* Give copy packet to RAW hook */
677107120Sjulian	ng_hci_mtap(unit, m);
678107120Sjulian
679107120Sjulian	/*
680107120Sjulian	 * XXX XXX XXX
681107120Sjulian	 * Lower layer drivers MUST NOT send mbuf chain with empty mbuf at
682107120Sjulian	 * the beginning of the chain. HCI layer WILL NOT call m_pullup() here.
683107120Sjulian	 */
684107120Sjulian
685107120Sjulian	switch (*mtod(m, u_int8_t *)) {
686107120Sjulian	case NG_HCI_ACL_DATA_PKT:
687107120Sjulian		NG_HCI_STAT_ACL_RECV(unit->stat);
688107120Sjulian
689107120Sjulian		if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||
690107120Sjulian		    unit->acl == NULL || NG_HOOK_NOT_VALID(unit->acl)) {
691107120Sjulian			NG_HCI_WARN(
692107120Sjulian"%s: %s - could not forward HCI ACL data packet, state=%#x, hook=%p\n",
693107120Sjulian				__func__, NG_NODE_NAME(unit->node),
694107120Sjulian				unit->state, unit->acl);
695107120Sjulian
696107120Sjulian			NG_FREE_ITEM(item);
697107120Sjulian		} else
698107120Sjulian			NG_FWD_ITEM_HOOK(error, item, unit->acl);
699107120Sjulian		break;
700107120Sjulian
701107120Sjulian	case NG_HCI_SCO_DATA_PKT:
702107120Sjulian		NG_HCI_STAT_SCO_RECV(unit->stat);
703107120Sjulian
704107120Sjulian		if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||
705107120Sjulian		    unit->sco == NULL || NG_HOOK_NOT_VALID(unit->sco)) {
706107120Sjulian			NG_HCI_WARN(
707107120Sjulian"%s: %s - could not forward HCI SCO data packet, state=%#x, hook=%p\n",
708107120Sjulian				__func__, NG_NODE_NAME(unit->node),
709107120Sjulian				unit->state, unit->sco);
710107120Sjulian
711107120Sjulian			NG_FREE_ITEM(item);
712107120Sjulian		} else
713107120Sjulian			NG_FWD_ITEM_HOOK(error, item, unit->sco);
714107120Sjulian		break;
715107120Sjulian
716107120Sjulian	case NG_HCI_EVENT_PKT:
717107120Sjulian		NG_HCI_STAT_EVNT_RECV(unit->stat);
718107120Sjulian
719107120Sjulian		/* Detach mbuf, discard item and process event */
720107120Sjulian		NGI_GET_M(item, m);
721107120Sjulian		NG_FREE_ITEM(item);
722107120Sjulian
723107120Sjulian		error = ng_hci_process_event(unit, m);
724107120Sjulian		break;
725107120Sjulian
726107120Sjulian	default:
727107120Sjulian		NG_HCI_ALERT(
728107120Sjulian"%s: %s - got unknown HCI packet type=%#x\n",
729107120Sjulian			__func__, NG_NODE_NAME(unit->node),
730107120Sjulian			*mtod(m, u_int8_t *));
731107120Sjulian
732107120Sjulian		NG_FREE_ITEM(item);
733107120Sjulian
734107120Sjulian		error = EINVAL;
735107120Sjulian		break;
736107120Sjulian	}
737107120Sjulian
738107120Sjulian	return (error);
739107120Sjulian} /* ng_hci_drv_rcvdata */
740107120Sjulian
741107120Sjulian/*
742107120Sjulian * Process data packet from ACL upstream hook.
743107120Sjulian * We expect valid HCI ACL data packets.
744107120Sjulian */
745107120Sjulian
746107120Sjulianstatic int
747107120Sjulianng_hci_acl_rcvdata(hook_p hook, item_p item)
748107120Sjulian{
749107120Sjulian	ng_hci_unit_p		 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
750107120Sjulian	struct mbuf		*m = NULL;
751107120Sjulian	ng_hci_unit_con_p	 con = NULL;
752107120Sjulian	u_int16_t		 con_handle;
753107120Sjulian	int			 size, error = 0;
754107120Sjulian
755107120Sjulian	NG_HCI_BUFF_ACL_SIZE(unit->buffer, size);
756107120Sjulian
757107120Sjulian	/* Check packet */
758107120Sjulian	NGI_GET_M(item, m);
759107120Sjulian
760107120Sjulian	if (*mtod(m, u_int8_t *) != NG_HCI_ACL_DATA_PKT) {
761107120Sjulian		NG_HCI_ALERT(
762107120Sjulian"%s: %s - invalid HCI data packet type=%#x\n",
763107120Sjulian			__func__, NG_NODE_NAME(unit->node),
764107120Sjulian			*mtod(m, u_int8_t *));
765107120Sjulian
766107120Sjulian		error = EINVAL;
767107120Sjulian		goto drop;
768107120Sjulian	}
769107120Sjulian
770107120Sjulian	if (m->m_pkthdr.len < sizeof(ng_hci_acldata_pkt_t) ||
771107120Sjulian	    m->m_pkthdr.len > sizeof(ng_hci_acldata_pkt_t) + size) {
772107120Sjulian		NG_HCI_ALERT(
773107120Sjulian"%s: %s - invalid HCI ACL data packet, len=%d, mtu=%d\n",
774107120Sjulian			__func__, NG_NODE_NAME(unit->node),
775107120Sjulian			m->m_pkthdr.len, size);
776107120Sjulian
777107120Sjulian		error = EMSGSIZE;
778107120Sjulian		goto drop;
779107120Sjulian	}
780107120Sjulian
781107120Sjulian	NG_HCI_M_PULLUP(m, sizeof(ng_hci_acldata_pkt_t));
782107120Sjulian	if (m == NULL) {
783107120Sjulian		error = ENOBUFS;
784107120Sjulian		goto drop;
785107120Sjulian	}
786107120Sjulian
787107120Sjulian	con_handle = NG_HCI_CON_HANDLE(le16toh(
788107120Sjulian			mtod(m, ng_hci_acldata_pkt_t *)->con_handle));
789107120Sjulian	size = le16toh(mtod(m, ng_hci_acldata_pkt_t *)->length);
790107120Sjulian
791107120Sjulian	if (m->m_pkthdr.len != sizeof(ng_hci_acldata_pkt_t) + size) {
792107120Sjulian		NG_HCI_ALERT(
793107120Sjulian"%s: %s - invalid HCI ACL data packet size, len=%d, length=%d\n",
794107120Sjulian			__func__, NG_NODE_NAME(unit->node),
795107120Sjulian			m->m_pkthdr.len, size);
796107120Sjulian
797107120Sjulian		error = EMSGSIZE;
798107120Sjulian		goto drop;
799107120Sjulian	}
800107120Sjulian
801107120Sjulian	/* Queue packet */
802107120Sjulian	con = ng_hci_con_by_handle(unit, con_handle);
803107120Sjulian	if (con == NULL) {
804107120Sjulian		NG_HCI_ERR(
805107120Sjulian"%s: %s - unexpected HCI ACL data packet. Connection does not exists, " \
806107120Sjulian"con_handle=%d\n",	__func__, NG_NODE_NAME(unit->node), con_handle);
807107120Sjulian
808107120Sjulian		error = ENOENT;
809107120Sjulian		goto drop;
810107120Sjulian	}
811107120Sjulian
812107120Sjulian	if (con->link_type != NG_HCI_LINK_ACL) {
813107120Sjulian		NG_HCI_ERR(
814107120Sjulian"%s: %s - unexpected HCI ACL data packet. Not ACL link, con_handle=%d, " \
815107120Sjulian"link_type=%d\n",	__func__, NG_NODE_NAME(unit->node),
816107120Sjulian			con_handle, con->link_type);
817107120Sjulian
818107120Sjulian		error = EINVAL;
819107120Sjulian		goto drop;
820107120Sjulian	}
821107120Sjulian
822107120Sjulian	if (con->state != NG_HCI_CON_OPEN) {
823107120Sjulian		NG_HCI_ERR(
824107120Sjulian"%s: %s - unexpected HCI ACL data packet. Invalid connection state=%d, " \
825107120Sjulian"con_handle=%d\n",	 __func__, NG_NODE_NAME(unit->node),
826107120Sjulian			con->state, con_handle);
827107120Sjulian
828107120Sjulian		error = EHOSTDOWN;
829107120Sjulian		goto drop;
830107120Sjulian	}
831107120Sjulian
832107120Sjulian	if (NG_BT_ITEMQ_FULL(&con->conq)) {
833107120Sjulian		NG_HCI_ALERT(
834107120Sjulian"%s: %s - dropping HCI ACL data packet, con_handle=%d, len=%d, queue_len=%d\n",
835107120Sjulian			 __func__, NG_NODE_NAME(unit->node), con_handle,
836107120Sjulian			m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));
837107120Sjulian
838107120Sjulian		NG_BT_ITEMQ_DROP(&con->conq);
839107120Sjulian
840107120Sjulian		error = ENOBUFS;
841107120Sjulian		goto drop;
842107120Sjulian	}
843107120Sjulian
844107120Sjulian	/* Queue item and schedule data transfer */
845107120Sjulian	NGI_M(item) = m;
846107120Sjulian	NG_BT_ITEMQ_ENQUEUE(&con->conq, item);
847107120Sjulian	item = NULL;
848107120Sjulian	m = NULL;
849107120Sjulian
850107120Sjulian	ng_hci_send_data(unit);
851107120Sjuliandrop:
852107120Sjulian	if (item != NULL)
853107120Sjulian		NG_FREE_ITEM(item);
854107120Sjulian
855107120Sjulian	NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
856107120Sjulian
857107120Sjulian	return (error);
858107120Sjulian} /* ng_hci_acl_rcvdata */
859107120Sjulian
860107120Sjulian/*
861107120Sjulian * Process data packet from SCO upstream hook.
862107120Sjulian * We expect valid HCI SCO data packets
863107120Sjulian */
864107120Sjulian
865107120Sjulianstatic int
866107120Sjulianng_hci_sco_rcvdata(hook_p hook, item_p item)
867107120Sjulian{
868107120Sjulian	ng_hci_unit_p		 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
869107120Sjulian	struct mbuf		*m = NULL;
870107120Sjulian	ng_hci_unit_con_p	 con = NULL;
871107120Sjulian	u_int16_t		 con_handle;
872107120Sjulian	int			 size, error = 0;
873107120Sjulian
874107120Sjulian	NG_HCI_BUFF_SCO_SIZE(unit->buffer, size);
875107120Sjulian
876107120Sjulian	/* Check packet */
877107120Sjulian	NGI_GET_M(item, m);
878107120Sjulian
879107120Sjulian	if (*mtod(m, u_int8_t *) != NG_HCI_SCO_DATA_PKT) {
880107120Sjulian		NG_HCI_ALERT(
881107120Sjulian"%s: %s - invalid HCI data packet type=%#x\n",
882107120Sjulian			__func__, NG_NODE_NAME(unit->node),
883107120Sjulian			*mtod(m, u_int8_t *));
884107120Sjulian
885107120Sjulian		error = EINVAL;
886107120Sjulian		goto drop;
887107120Sjulian	}
888107120Sjulian
889107120Sjulian	if (m->m_pkthdr.len < sizeof(ng_hci_scodata_pkt_t) ||
890107120Sjulian	    m->m_pkthdr.len > sizeof(ng_hci_scodata_pkt_t) + size) {
891107120Sjulian		NG_HCI_ALERT(
892107120Sjulian"%s: %s - invalid HCI SCO data packet, len=%d, mtu=%d\n",
893107120Sjulian			__func__, NG_NODE_NAME(unit->node),
894107120Sjulian			m->m_pkthdr.len, size);
895107120Sjulian
896107120Sjulian		error = EMSGSIZE;
897107120Sjulian		goto drop;
898107120Sjulian	}
899107120Sjulian
900107120Sjulian	NG_HCI_M_PULLUP(m, sizeof(ng_hci_scodata_pkt_t));
901107120Sjulian	if (m == NULL) {
902107120Sjulian		error = ENOBUFS;
903107120Sjulian		goto drop;
904107120Sjulian	}
905107120Sjulian
906107120Sjulian	con_handle = NG_HCI_CON_HANDLE(le16toh(
907107120Sjulian			mtod(m, ng_hci_scodata_pkt_t *)->con_handle));
908107120Sjulian	size = mtod(m, ng_hci_scodata_pkt_t *)->length;
909107120Sjulian
910107120Sjulian	if (m->m_pkthdr.len != sizeof(ng_hci_scodata_pkt_t) + size) {
911107120Sjulian		NG_HCI_ALERT(
912107120Sjulian"%s: %s - invalid HCI SCO data packet size, len=%d, length=%d\n",
913107120Sjulian			__func__, NG_NODE_NAME(unit->node),
914107120Sjulian			m->m_pkthdr.len, size);
915107120Sjulian
916107120Sjulian		error = EMSGSIZE;
917107120Sjulian		goto drop;
918107120Sjulian	}
919107120Sjulian
920107120Sjulian	/* Queue packet */
921107120Sjulian	con = ng_hci_con_by_handle(unit, con_handle);
922107120Sjulian	if (con == NULL) {
923107120Sjulian		NG_HCI_ERR(
924107120Sjulian"%s: %s - unexpected HCI SCO data packet. Connection does not exists, " \
925107120Sjulian"con_handle=%d\n",	__func__, NG_NODE_NAME(unit->node), con_handle);
926107120Sjulian
927107120Sjulian		error = ENOENT;
928107120Sjulian		goto drop;
929107120Sjulian	}
930107120Sjulian
931107120Sjulian	if (con->link_type != NG_HCI_LINK_SCO) {
932107120Sjulian		NG_HCI_ERR(
933107120Sjulian"%s: %s - unexpected HCI SCO data packet. Not SCO link, con_handle=%d, " \
934107120Sjulian"link_type=%d\n",	__func__, NG_NODE_NAME(unit->node),
935107120Sjulian			con_handle, con->link_type);
936107120Sjulian
937107120Sjulian		error = EINVAL;
938107120Sjulian		goto drop;
939107120Sjulian	}
940107120Sjulian
941107120Sjulian	if (con->state != NG_HCI_CON_OPEN) {
942107120Sjulian		NG_HCI_ERR(
943107120Sjulian"%s: %s - unexpected HCI SCO data packet. Invalid connection state=%d, " \
944107120Sjulian"con_handle=%d\n",	__func__, NG_NODE_NAME(unit->node),
945107120Sjulian			con->state, con_handle);
946107120Sjulian
947107120Sjulian		error = EHOSTDOWN;
948107120Sjulian		goto drop;
949107120Sjulian	}
950107120Sjulian
951107120Sjulian	if (NG_BT_ITEMQ_FULL(&con->conq)) {
952107120Sjulian		NG_HCI_ALERT(
953107120Sjulian"%s: %s - dropping HCI SCO data packet, con_handle=%d, len=%d, queue_len=%d\n",
954107120Sjulian			__func__, NG_NODE_NAME(unit->node), con_handle,
955107120Sjulian			m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));
956107120Sjulian
957107120Sjulian		NG_BT_ITEMQ_DROP(&con->conq);
958107120Sjulian
959107120Sjulian		error = ENOBUFS;
960107120Sjulian		goto drop;
961107120Sjulian	}
962107120Sjulian
963107120Sjulian	/* Queue item and schedule data transfer */
964107120Sjulian	NGI_M(item) = m;
965107120Sjulian	NG_BT_ITEMQ_ENQUEUE(&con->conq, item);
966107120Sjulian	item = NULL;
967107120Sjulian	m = NULL;
968107120Sjulian
969107120Sjulian	ng_hci_send_data(unit);
970107120Sjuliandrop:
971107120Sjulian	if (item != NULL)
972107120Sjulian		NG_FREE_ITEM(item);
973107120Sjulian
974107120Sjulian	NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
975107120Sjulian
976107120Sjulian	return (error);
977107120Sjulian} /* ng_hci_sco_rcvdata */
978107120Sjulian
979107120Sjulian/*
980107120Sjulian * Process data packet from uptream RAW hook.
981107120Sjulian * We expect valid HCI command packets.
982107120Sjulian */
983107120Sjulian
984107120Sjulianstatic int
985107120Sjulianng_hci_raw_rcvdata(hook_p hook, item_p item)
986107120Sjulian{
987107120Sjulian	ng_hci_unit_p	 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
988107120Sjulian	struct mbuf	*m = NULL;
989107120Sjulian	int		 error = 0;
990107120Sjulian
991107120Sjulian	NGI_GET_M(item, m);
992107120Sjulian	NG_FREE_ITEM(item);
993107120Sjulian
994107120Sjulian	/* Check packet */
995107120Sjulian	if (*mtod(m, u_int8_t *) != NG_HCI_CMD_PKT) {
996107120Sjulian		NG_HCI_ALERT(
997107120Sjulian"%s: %s - invalid HCI command packet type=%#x\n",
998107120Sjulian			__func__, NG_NODE_NAME(unit->node),
999107120Sjulian			*mtod(m, u_int8_t *));
1000107120Sjulian
1001107120Sjulian		error = EINVAL;
1002107120Sjulian		goto drop;
1003107120Sjulian	}
1004107120Sjulian
1005107120Sjulian	if (m->m_pkthdr.len < sizeof(ng_hci_cmd_pkt_t)) {
1006107120Sjulian		NG_HCI_ALERT(
1007107120Sjulian"%s: %s - invalid HCI command packet len=%d\n",
1008107120Sjulian			__func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len);
1009107120Sjulian
1010107120Sjulian		error = EMSGSIZE;
1011107120Sjulian		goto drop;
1012107120Sjulian	}
1013107120Sjulian
1014107120Sjulian	NG_HCI_M_PULLUP(m, sizeof(ng_hci_cmd_pkt_t));
1015107120Sjulian	if (m == NULL) {
1016107120Sjulian		error = ENOBUFS;
1017107120Sjulian		goto drop;
1018107120Sjulian	}
1019107120Sjulian
1020107120Sjulian	if (m->m_pkthdr.len !=
1021107120Sjulian	    mtod(m, ng_hci_cmd_pkt_t *)->length + sizeof(ng_hci_cmd_pkt_t)) {
1022107120Sjulian		NG_HCI_ALERT(
1023107120Sjulian"%s: %s - invalid HCI command packet size, len=%d, length=%d\n",
1024107120Sjulian			__func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,
1025107120Sjulian			mtod(m, ng_hci_cmd_pkt_t *)->length);
1026107120Sjulian
1027107120Sjulian		error = EMSGSIZE;
1028107120Sjulian		goto drop;
1029107120Sjulian	}
1030107120Sjulian
1031107120Sjulian	if (mtod(m, ng_hci_cmd_pkt_t *)->opcode == 0) {
1032107120Sjulian		NG_HCI_ALERT(
1033107120Sjulian"%s: %s - invalid HCI command opcode\n",
1034107120Sjulian			__func__, NG_NODE_NAME(unit->node));
1035107120Sjulian
1036107120Sjulian		error = EINVAL;
1037107120Sjulian		goto drop;
1038107120Sjulian	}
1039107120Sjulian
1040107120Sjulian	if (NG_BT_MBUFQ_FULL(&unit->cmdq)) {
1041107120Sjulian		NG_HCI_ALERT(
1042107120Sjulian"%s: %s - dropping HCI command packet, len=%d, queue_len=%d\n",
1043107120Sjulian			__func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,
1044107120Sjulian			NG_BT_MBUFQ_LEN(&unit->cmdq));
1045107120Sjulian
1046107120Sjulian		NG_BT_MBUFQ_DROP(&unit->cmdq);
1047107120Sjulian
1048107120Sjulian		error = ENOBUFS;
1049107120Sjulian		goto drop;
1050107120Sjulian	}
1051107120Sjulian
1052107120Sjulian	/* Queue and send command */
1053107120Sjulian	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1054107120Sjulian	m = NULL;
1055107120Sjulian
1056107120Sjulian	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1057107120Sjulian		error = ng_hci_send_command(unit);
1058107120Sjuliandrop:
1059107120Sjulian	NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
1060107120Sjulian
1061107120Sjulian	return (error);
1062107120Sjulian} /* ng_hci_raw_rcvdata */
1063107120Sjulian
1064