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