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