xref: /freebsd/sys/netgraph/bluetooth/hci/ng_hci_main.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
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$
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
59 MALLOC_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 */
65 static	ng_constructor_t	ng_hci_constructor;
66 static	ng_shutdown_t		ng_hci_shutdown;
67 static	ng_newhook_t		ng_hci_newhook;
68 static	ng_connect_t		ng_hci_connect;
69 static	ng_disconnect_t		ng_hci_disconnect;
70 static	ng_rcvmsg_t		ng_hci_default_rcvmsg;
71 static	ng_rcvmsg_t		ng_hci_upper_rcvmsg;
72 static	ng_rcvdata_t		ng_hci_drv_rcvdata;
73 static	ng_rcvdata_t		ng_hci_acl_rcvdata;
74 static	ng_rcvdata_t		ng_hci_sco_rcvdata;
75 static	ng_rcvdata_t		ng_hci_raw_rcvdata;
76 
77 /* Netgraph node type descriptor */
78 static	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 };
92 NETGRAPH_INIT(hci, &typestruct);
93 MODULE_VERSION(ng_hci, NG_BLUETOOTH_VERSION);
94 MODULE_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 
107 static int
108 ng_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 
158 static int
159 ng_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 
183 static int
184 ng_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 
212 static int
213 ng_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 
240 static int
241 ng_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 
276 static int
277 ng_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 
619 static int
620 ng_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 
663 static int
664 ng_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 
746 static int
747 ng_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);
851 drop:
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 
865 static int
866 ng_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);
970 drop:
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 
984 static int
985 ng_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);
1058 drop:
1059 	NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
1060 
1061 	return (error);
1062 } /* ng_hci_raw_rcvdata */
1063 
1064