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