1 /*
2  * ng_hci_evnt.c
3  */
4 
5 /*-
6  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id: ng_hci_evnt.c,v 1.6 2003/09/08 18:57:51 max Exp $
31  * $FreeBSD: src/sys/netgraph/bluetooth/hci/ng_hci_evnt.c,v 1.8 2005/01/07 01:45:43 imp Exp $
32  * $DragonFly: src/sys/netgraph7/bluetooth/hci/ng_hci_evnt.c,v 1.2 2008/06/26 23:05:40 dillon Exp $
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/endian.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/queue.h>
42 #include "ng_message.h"
43 #include "netgraph.h"
44 #include "bluetooth/include/ng_bluetooth.h"
45 #include "bluetooth/include/ng_hci.h"
46 #include "bluetooth/hci/ng_hci_var.h"
47 #include "bluetooth/hci/ng_hci_cmds.h"
48 #include "bluetooth/hci/ng_hci_evnt.h"
49 #include "bluetooth/hci/ng_hci_ulpi.h"
50 #include "bluetooth/hci/ng_hci_misc.h"
51 
52 /******************************************************************************
53  ******************************************************************************
54  **                     HCI event processing module
55  ******************************************************************************
56  ******************************************************************************/
57 
58 /*
59  * Event processing routines
60  */
61 
62 static int inquiry_result             (ng_hci_unit_p, struct mbuf *);
63 static int con_compl                  (ng_hci_unit_p, struct mbuf *);
64 static int con_req                    (ng_hci_unit_p, struct mbuf *);
65 static int discon_compl               (ng_hci_unit_p, struct mbuf *);
66 static int encryption_change          (ng_hci_unit_p, struct mbuf *);
67 static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *);
68 static int qos_setup_compl            (ng_hci_unit_p, struct mbuf *);
69 static int hardware_error             (ng_hci_unit_p, struct mbuf *);
70 static int role_change                (ng_hci_unit_p, struct mbuf *);
71 static int num_compl_pkts             (ng_hci_unit_p, struct mbuf *);
72 static int mode_change                (ng_hci_unit_p, struct mbuf *);
73 static int data_buffer_overflow       (ng_hci_unit_p, struct mbuf *);
74 static int read_clock_offset_compl    (ng_hci_unit_p, struct mbuf *);
75 static int qos_violation              (ng_hci_unit_p, struct mbuf *);
76 static int page_scan_mode_change      (ng_hci_unit_p, struct mbuf *);
77 static int page_scan_rep_mode_change  (ng_hci_unit_p, struct mbuf *);
78 static int sync_con_queue             (ng_hci_unit_p, ng_hci_unit_con_p, int);
79 static int send_data_packets          (ng_hci_unit_p, int, int);
80 
81 /*
82  * Process HCI event packet
83  */
84 
85 int
86 ng_hci_process_event(ng_hci_unit_p unit, struct mbuf *event)
87 {
88 	ng_hci_event_pkt_t	*hdr = NULL;
89 	int			 error = 0;
90 
91 	/* Get event packet header */
92 	NG_HCI_M_PULLUP(event, sizeof(*hdr));
93 	if (event == NULL)
94 		return (ENOBUFS);
95 
96 	hdr = mtod(event, ng_hci_event_pkt_t *);
97 
98 	NG_HCI_INFO(
99 "%s: %s - got HCI event=%#x, length=%d\n",
100 		__func__, NG_NODE_NAME(unit->node), hdr->event, hdr->length);
101 
102 	/* Get rid of event header and process event */
103 	m_adj(event, sizeof(*hdr));
104 
105 	switch (hdr->event) {
106 	case NG_HCI_EVENT_INQUIRY_COMPL:
107 	case NG_HCI_EVENT_RETURN_LINK_KEYS:
108 	case NG_HCI_EVENT_PIN_CODE_REQ:
109 	case NG_HCI_EVENT_LINK_KEY_REQ:
110 	case NG_HCI_EVENT_LINK_KEY_NOTIFICATION:
111 	case NG_HCI_EVENT_LOOPBACK_COMMAND:
112 	case NG_HCI_EVENT_AUTH_COMPL:
113 	case NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL:
114 	case NG_HCI_EVENT_MASTER_LINK_KEY_COMPL:
115 	case NG_HCI_EVENT_FLUSH_OCCUR:	/* XXX Do we have to handle it? */
116 	case NG_HCI_EVENT_MAX_SLOT_CHANGE:
117 	case NG_HCI_EVENT_CON_PKT_TYPE_CHANGED:
118 	case NG_HCI_EVENT_BT_LOGO:
119 	case NG_HCI_EVENT_VENDOR:
120 	case NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL:
121 	case NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL:
122 		/* These do not need post processing */
123 		NG_FREE_M(event);
124 		break;
125 
126 	case NG_HCI_EVENT_INQUIRY_RESULT:
127 		error = inquiry_result(unit, event);
128 		break;
129 
130 	case NG_HCI_EVENT_CON_COMPL:
131 		error = con_compl(unit, event);
132 		break;
133 
134 	case NG_HCI_EVENT_CON_REQ:
135 		error = con_req(unit, event);
136 		break;
137 
138 	case NG_HCI_EVENT_DISCON_COMPL:
139 		error = discon_compl(unit, event);
140 		break;
141 
142 	case NG_HCI_EVENT_ENCRYPTION_CHANGE:
143 		error = encryption_change(unit, event);
144 		break;
145 
146 	case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL:
147 		error = read_remote_features_compl(unit, event);
148 		break;
149 
150 	case NG_HCI_EVENT_QOS_SETUP_COMPL:
151 		error = qos_setup_compl(unit, event);
152 		break;
153 
154 	case NG_HCI_EVENT_COMMAND_COMPL:
155 		error = ng_hci_process_command_complete(unit, event);
156 		break;
157 
158 	case NG_HCI_EVENT_COMMAND_STATUS:
159 		error = ng_hci_process_command_status(unit, event);
160 		break;
161 
162 	case NG_HCI_EVENT_HARDWARE_ERROR:
163 		error = hardware_error(unit, event);
164 		break;
165 
166 	case NG_HCI_EVENT_ROLE_CHANGE:
167 		error = role_change(unit, event);
168 		break;
169 
170 	case NG_HCI_EVENT_NUM_COMPL_PKTS:
171 		error = num_compl_pkts(unit, event);
172 		break;
173 
174 	case NG_HCI_EVENT_MODE_CHANGE:
175 		error = mode_change(unit, event);
176 		break;
177 
178 	case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW:
179 		error = data_buffer_overflow(unit, event);
180 		break;
181 
182 	case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL:
183 		error = read_clock_offset_compl(unit, event);
184 		break;
185 
186 	case NG_HCI_EVENT_QOS_VIOLATION:
187 		error = qos_violation(unit, event);
188 		break;
189 
190 	case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE:
191 		error = page_scan_mode_change(unit, event);
192 		break;
193 
194 	case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE:
195 		error = page_scan_rep_mode_change(unit, event);
196 		break;
197 
198 	default:
199 		NG_FREE_M(event);
200 		error = EINVAL;
201 		break;
202 	}
203 
204 	return (error);
205 } /* ng_hci_process_event */
206 
207 /*
208  * Send ACL and/or SCO data to the unit driver
209  */
210 
211 void
212 ng_hci_send_data(ng_hci_unit_p unit)
213 {
214 	int	count;
215 
216 	/* Send ACL data */
217 	NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count);
218 
219 	NG_HCI_INFO(
220 "%s: %s - sending ACL data packets, count=%d\n",
221 		__func__, NG_NODE_NAME(unit->node), count);
222 
223 	if (count > 0) {
224 		count = send_data_packets(unit, NG_HCI_LINK_ACL, count);
225 		NG_HCI_STAT_ACL_SENT(unit->stat, count);
226 		NG_HCI_BUFF_ACL_USE(unit->buffer, count);
227 	}
228 
229 	/* Send SCO data */
230 	NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count);
231 
232 	NG_HCI_INFO(
233 "%s: %s - sending SCO data packets, count=%d\n",
234 		__func__, NG_NODE_NAME(unit->node), count);
235 
236 	if (count > 0) {
237 		count = send_data_packets(unit, NG_HCI_LINK_SCO, count);
238 		NG_HCI_STAT_SCO_SENT(unit->stat, count);
239 		NG_HCI_BUFF_SCO_USE(unit->buffer, count);
240 	}
241 } /* ng_hci_send_data */
242 
243 /*
244  * Send data packets to the lower layer.
245  */
246 
247 static int
248 send_data_packets(ng_hci_unit_p unit, int link_type, int limit)
249 {
250 	ng_hci_unit_con_p	con = NULL, winner = NULL;
251 	item_p			item = NULL;
252 	int			min_pending, total_sent, sent, error, v;
253 
254 	for (total_sent = 0; limit > 0; ) {
255 		min_pending = 0x0fffffff;
256 		winner = NULL;
257 
258 		/*
259 		 * Find the connection that has has data to send
260 		 * and the smallest number of pending packets
261 		 */
262 
263 		LIST_FOREACH(con, &unit->con_list, next) {
264 			if (con->link_type != link_type)
265 				continue;
266 			if (NG_BT_ITEMQ_LEN(&con->conq) == 0)
267 				continue;
268 
269 			if (con->pending < min_pending) {
270 				winner = con;
271 				min_pending = con->pending;
272 			}
273 		}
274 
275 	        if (winner == NULL)
276 			break;
277 
278 		/*
279 		 * OK, we have a winner now send as much packets as we can
280 		 * Count the number of packets we have sent and then sync
281 		 * winner connection queue.
282 		 */
283 
284 		for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) {
285 			NG_BT_ITEMQ_DEQUEUE(&winner->conq, item);
286 			if (item == NULL)
287 				break;
288 
289 			NG_HCI_INFO(
290 "%s: %s - sending data packet, handle=%d, len=%d\n",
291 				__func__, NG_NODE_NAME(unit->node),
292 				winner->con_handle, NGI_M(item)->m_pkthdr.len);
293 
294 			/* Check if driver hook still there */
295 			v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv));
296 			if (!v || (unit->state & NG_HCI_UNIT_READY) !=
297 					NG_HCI_UNIT_READY) {
298 				NG_HCI_ERR(
299 "%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n",
300 					__func__, NG_NODE_NAME(unit->node),
301 					NG_HCI_HOOK_DRV, ((v)? "" : "not "),
302 					unit->state);
303 
304 				NG_FREE_ITEM(item);
305 				error = ENOTCONN;
306 			} else {
307 				v = NGI_M(item)->m_pkthdr.len;
308 
309 				/* Give packet to raw hook */
310 				ng_hci_mtap(unit, NGI_M(item));
311 
312 				/* ... and forward item to the driver */
313 				NG_FWD_ITEM_HOOK(error, item, unit->drv);
314 			}
315 
316 			if (error != 0) {
317 				NG_HCI_ERR(
318 "%s: %s - could not send data packet, handle=%d, error=%d\n",
319 					__func__, NG_NODE_NAME(unit->node),
320 					winner->con_handle, error);
321 				break;
322 			}
323 
324 			winner->pending ++;
325 			NG_HCI_STAT_BYTES_SENT(unit->stat, v);
326 		}
327 
328 		/*
329 		 * Sync connection queue for the winner
330 		 */
331 
332 		sync_con_queue(unit, winner, sent);
333 	}
334 
335 	return (total_sent);
336 } /* send_data_packets */
337 
338 /*
339  * Send flow control messages to the upper layer
340  */
341 
342 static int
343 sync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed)
344 {
345 	hook_p				 hook = NULL;
346 	struct ng_mesg			*msg = NULL;
347 	ng_hci_sync_con_queue_ep	*state = NULL;
348 	int				 error;
349 
350 	hook = (con->link_type == NG_HCI_LINK_ACL)? unit->acl : unit->sco;
351 	if (hook == NULL || NG_HOOK_NOT_VALID(hook))
352 		return (ENOTCONN);
353 
354 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE,
355 		sizeof(*state), M_WAITOK | M_NULLOK);
356 	if (msg == NULL)
357 		return (ENOMEM);
358 
359 	state = (ng_hci_sync_con_queue_ep *)(msg->data);
360 	state->con_handle = con->con_handle;
361 	state->completed = completed;
362 
363 	NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
364 
365 	return (error);
366 } /* sync_con_queue */
367 
368 /* Inquiry result event */
369 static int
370 inquiry_result(ng_hci_unit_p unit, struct mbuf *event)
371 {
372 	ng_hci_inquiry_result_ep	*ep = NULL;
373 	ng_hci_neighbor_p		 n = NULL;
374 	bdaddr_t			 bdaddr;
375 	int				 error = 0;
376 
377 	NG_HCI_M_PULLUP(event, sizeof(*ep));
378 	if (event == NULL)
379 		return (ENOBUFS);
380 
381 	ep = mtod(event, ng_hci_inquiry_result_ep *);
382 	m_adj(event, sizeof(*ep));
383 
384 	for (; ep->num_responses > 0; ep->num_responses --) {
385 		/* Get remote unit address */
386 		m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr);
387 		m_adj(event, sizeof(bdaddr));
388 
389 		/* Lookup entry in the cache */
390 		n = ng_hci_get_neighbor(unit, &bdaddr);
391 		if (n == NULL) {
392 			/* Create new entry */
393 			n = ng_hci_new_neighbor(unit);
394 			if (n == NULL) {
395 				error = ENOMEM;
396 				break;
397 			}
398 		} else
399 			getmicrotime(&n->updated);
400 
401 		bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));
402 
403 		/* XXX call m_pullup here? */
404 
405 		n->page_scan_rep_mode = *mtod(event, u_int8_t *);
406 		m_adj(event, sizeof(u_int8_t));
407 
408 		/* page_scan_period_mode */
409 		m_adj(event, sizeof(u_int8_t));
410 
411 		n->page_scan_mode = *mtod(event, u_int8_t *);
412 		m_adj(event, sizeof(u_int8_t));
413 
414 		/* class */
415 		m_adj(event, NG_HCI_CLASS_SIZE);
416 
417 		/* clock offset */
418 		m_copydata(event, 0, sizeof(n->clock_offset),
419 			(caddr_t) &n->clock_offset);
420 		n->clock_offset = le16toh(n->clock_offset);
421 	}
422 
423 	NG_FREE_M(event);
424 
425 	return (error);
426 } /* inquiry_result */
427 
428 /* Connection complete event */
429 static int
430 con_compl(ng_hci_unit_p unit, struct mbuf *event)
431 {
432 	ng_hci_con_compl_ep	*ep = NULL;
433 	ng_hci_unit_con_p	 con = NULL;
434 	int			 error = 0;
435 
436 	NG_HCI_M_PULLUP(event, sizeof(*ep));
437 	if (event == NULL)
438 		return (ENOBUFS);
439 
440 	ep = mtod(event, ng_hci_con_compl_ep *);
441 
442 	/*
443 	 * Find the first connection descriptor that matches the following:
444 	 *
445 	 * 1) con->link_type == ep->link_type
446 	 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE
447 	 * 3) con->bdaddr == ep->bdaddr
448 	 */
449 
450 	LIST_FOREACH(con, &unit->con_list, next)
451 		if (con->link_type == ep->link_type &&
452 		    con->state == NG_HCI_CON_W4_CONN_COMPLETE &&
453 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
454 			break;
455 
456 	/*
457 	 * Two possible cases:
458 	 *
459 	 * 1) We have found connection descriptor. That means upper layer has
460 	 *    requested this connection via LP_CON_REQ message. In this case
461 	 *    connection must have timeout set. If ng_hci_con_untimeout() fails
462 	 *    then timeout message already went into node's queue. In this case
463 	 *    ignore Connection_Complete event and let timeout deal with it.
464 	 *
465 	 * 2) We do not have connection descriptor. That means upper layer
466 	 *    nas not requested this connection or (less likely) we gave up
467 	 *    on this connection (timeout). The most likely scenario is that
468 	 *    we have received Create_Connection/Add_SCO_Connection command
469 	 *    from the RAW hook
470 	 */
471 
472 	if (con == NULL) {
473 		if (ep->status != 0)
474 			goto out;
475 
476 		con = ng_hci_new_con(unit, ep->link_type);
477 		if (con == NULL) {
478 			error = ENOMEM;
479 			goto out;
480 		}
481 
482 		bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
483 	} else if ((error = ng_hci_con_untimeout(con)) != 0)
484 			goto out;
485 
486 	/*
487 	 * Update connection descriptor and send notification
488 	 * to the upper layers.
489 	 */
490 
491 	con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
492 	con->encryption_mode = ep->encryption_mode;
493 
494 	ng_hci_lp_con_cfm(con, ep->status);
495 
496 	/* Adjust connection state */
497 	if (ep->status != 0)
498 		ng_hci_free_con(con);
499 	else {
500 		con->state = NG_HCI_CON_OPEN;
501 
502 		/*
503 		 * Change link policy for the ACL connections. Enable all
504 		 * supported link modes. Enable Role switch as well if
505 		 * device supports it.
506 		 */
507 
508 		if (ep->link_type == NG_HCI_LINK_ACL) {
509 			struct __link_policy {
510 				ng_hci_cmd_pkt_t			 hdr;
511 				ng_hci_write_link_policy_settings_cp	 cp;
512 			} __attribute__ ((packed))			*lp;
513 			struct mbuf					*m;
514 
515 			MGETHDR(m, MB_DONTWAIT, MT_DATA);
516 			if (m != NULL) {
517 				m->m_pkthdr.len = m->m_len = sizeof(*lp);
518 				lp = mtod(m, struct __link_policy *);
519 
520 				lp->hdr.type = NG_HCI_CMD_PKT;
521 				lp->hdr.opcode = htole16(NG_HCI_OPCODE(
522 					NG_HCI_OGF_LINK_POLICY,
523 					NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS));
524 				lp->hdr.length = sizeof(lp->cp);
525 
526 				lp->cp.con_handle = ep->con_handle;
527 
528 				lp->cp.settings = 0;
529 				if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
530 				    unit->role_switch)
531 					lp->cp.settings |= 0x1;
532 				if (unit->features[0] & NG_HCI_LMP_HOLD_MODE)
533 					lp->cp.settings |= 0x2;
534 				if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE)
535 					lp->cp.settings |= 0x4;
536 				if (unit->features[1] & NG_HCI_LMP_PARK_MODE)
537 					lp->cp.settings |= 0x8;
538 
539 				lp->cp.settings &= unit->link_policy_mask;
540 				lp->cp.settings = htole16(lp->cp.settings);
541 
542 				NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
543 				if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
544 					ng_hci_send_command(unit);
545 			}
546 		}
547 	}
548 out:
549 	NG_FREE_M(event);
550 
551 	return (error);
552 } /* con_compl */
553 
554 /* Connection request event */
555 static int
556 con_req(ng_hci_unit_p unit, struct mbuf *event)
557 {
558 	ng_hci_con_req_ep	*ep = NULL;
559 	ng_hci_unit_con_p	 con = NULL;
560 	int			 error = 0;
561 
562 	NG_HCI_M_PULLUP(event, sizeof(*ep));
563 	if (event == NULL)
564 		return (ENOBUFS);
565 
566 	ep = mtod(event, ng_hci_con_req_ep *);
567 
568 	/*
569 	 * Find the first connection descriptor that matches the following:
570 	 *
571 	 * 1) con->link_type == ep->link_type
572 	 *
573 	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
574 	 *    con->state == NG_HCI_CON_W4_CONN_COMPL
575 	 *
576 	 * 3) con->bdaddr == ep->bdaddr
577 	 *
578 	 * Possible cases:
579 	 *
580 	 * 1) We do not have connection descriptor. This is simple. Create
581 	 *    new fresh connection descriptor and send notification to the
582 	 *    appropriate upstream hook (based on link_type).
583 	 *
584 	 * 2) We found connection handle. This is more complicated.
585 	 *
586 	 * 2.1) ACL links
587 	 *
588 	 *      Since only one ACL link can exist between each pair of
589 	 *      units then we have a race. Our upper layer has requested
590 	 *      an ACL connection to the remote unit, but we did not send
591 	 *      command yet. At the same time the remote unit has requested
592 	 *      an ACL connection from us. In this case we will ignore
593 	 *	Connection_Request event. This probably will cause connect
594 	 *      failure	on both units.
595 	 *
596 	 * 2.2) SCO links
597 	 *
598 	 *      The spec on page 45 says :
599 	 *
600 	 *      "The master can support up to three SCO links to the same
601 	 *       slave or to different slaves. A slave can support up to
602 	 *       three SCO links from the same master, or two SCO links if
603 	 *       the links originate from different masters."
604 	 *
605 	 *      The only problem is how to handle multiple SCO links between
606 	 *      matster and slave. For now we will assume that multiple SCO
607 	 *      links MUST be opened one after another.
608 	 */
609 
610 	LIST_FOREACH(con, &unit->con_list, next)
611 		if (con->link_type == ep->link_type &&
612 		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
613 		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
614 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
615 			break;
616 
617 	if (con == NULL) {
618 		con = ng_hci_new_con(unit, ep->link_type);
619 		if (con != NULL) {
620 			bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
621 
622 			con->state = NG_HCI_CON_W4_LP_CON_RSP;
623 			ng_hci_con_timeout(con);
624 
625 			error = ng_hci_lp_con_ind(con, ep->uclass);
626 			if (error != 0) {
627 				ng_hci_con_untimeout(con);
628 				ng_hci_free_con(con);
629 			}
630 		} else
631 			error = ENOMEM;
632 	}
633 
634 	NG_FREE_M(event);
635 
636 	return (error);
637 } /* con_req */
638 
639 /* Disconnect complete event */
640 static int
641 discon_compl(ng_hci_unit_p unit, struct mbuf *event)
642 {
643 	ng_hci_discon_compl_ep	*ep = NULL;
644 	ng_hci_unit_con_p	 con = NULL;
645 	int			 error = 0;
646 	u_int16_t		 h;
647 
648 	NG_HCI_M_PULLUP(event, sizeof(*ep));
649 	if (event == NULL)
650 		return (ENOBUFS);
651 
652 	ep = mtod(event, ng_hci_discon_compl_ep *);
653 
654 	/*
655 	 * XXX
656 	 * Do we have to send notification if ep->status != 0?
657 	 * For now we will send notification for both ACL and SCO connections
658 	 * ONLY if ep->status == 0.
659 	 */
660 
661 	if (ep->status == 0) {
662 		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
663 		con = ng_hci_con_by_handle(unit, h);
664 		if (con != NULL) {
665 			error = ng_hci_lp_discon_ind(con, ep->reason);
666 
667 			/* Remove all timeouts (if any) */
668 			if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
669 				ng_hci_con_untimeout(con);
670 
671 			ng_hci_free_con(con);
672 		} else {
673 			NG_HCI_ALERT(
674 "%s: %s - invalid connection handle=%d\n",
675 				__func__, NG_NODE_NAME(unit->node), h);
676 			error = ENOENT;
677 		}
678 	}
679 
680 	NG_FREE_M(event);
681 
682 	return (error);
683 } /* discon_compl */
684 
685 /* Encryption change event */
686 static int
687 encryption_change(ng_hci_unit_p unit, struct mbuf *event)
688 {
689 	ng_hci_encryption_change_ep	*ep = NULL;
690 	ng_hci_unit_con_p		 con = NULL;
691 	int				 error = 0;
692 
693 	NG_HCI_M_PULLUP(event, sizeof(*ep));
694 	if (event == NULL)
695 		return (ENOBUFS);
696 
697 	ep = mtod(event, ng_hci_encryption_change_ep *);
698 
699 	if (ep->status == 0) {
700 		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
701 
702 		con = ng_hci_con_by_handle(unit, h);
703 		if (con == NULL) {
704 			NG_HCI_ALERT(
705 "%s: %s - invalid connection handle=%d\n",
706 				__func__, NG_NODE_NAME(unit->node), h);
707 			error = ENOENT;
708 		} else if (con->link_type != NG_HCI_LINK_ACL) {
709 			NG_HCI_ALERT(
710 "%s: %s - invalid link type=%d\n",
711 				__func__, NG_NODE_NAME(unit->node),
712 				con->link_type);
713 			error = EINVAL;
714 		} else if (ep->encryption_enable)
715 			/* XXX is that true? */
716 			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_P2P;
717 		else
718 			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;
719 	} else
720 		NG_HCI_ERR(
721 "%s: %s - failed to change encryption mode, status=%d\n",
722 			__func__, NG_NODE_NAME(unit->node), ep->status);
723 
724 	NG_FREE_M(event);
725 
726 	return (error);
727 } /* encryption_change */
728 
729 /* Read remote feature complete event */
730 static int
731 read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event)
732 {
733 	ng_hci_read_remote_features_compl_ep	*ep = NULL;
734 	ng_hci_unit_con_p			 con = NULL;
735 	ng_hci_neighbor_p			 n = NULL;
736 	u_int16_t				 h;
737 	int					 error = 0;
738 
739 	NG_HCI_M_PULLUP(event, sizeof(*ep));
740 	if (event == NULL)
741 		return (ENOBUFS);
742 
743 	ep = mtod(event, ng_hci_read_remote_features_compl_ep *);
744 
745 	if (ep->status == 0) {
746 		/* Check if we have this connection handle */
747 		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
748 		con = ng_hci_con_by_handle(unit, h);
749 		if (con == NULL) {
750 			NG_HCI_ALERT(
751 "%s: %s - invalid connection handle=%d\n",
752 				__func__, NG_NODE_NAME(unit->node), h);
753 			error = ENOENT;
754 			goto out;
755 		}
756 
757 		/* Update cache entry */
758 		n = ng_hci_get_neighbor(unit, &con->bdaddr);
759 		if (n == NULL) {
760 			n = ng_hci_new_neighbor(unit);
761 			if (n == NULL) {
762 				error = ENOMEM;
763 				goto out;
764 			}
765 
766 			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
767 		} else
768 			getmicrotime(&n->updated);
769 
770 		bcopy(ep->features, n->features, sizeof(n->features));
771 	} else
772 		NG_HCI_ERR(
773 "%s: %s - failed to read remote unit features, status=%d\n",
774 			__func__, NG_NODE_NAME(unit->node), ep->status);
775 out:
776 	NG_FREE_M(event);
777 
778 	return (error);
779 } /* read_remote_features_compl */
780 
781 /* QoS setup complete event */
782 static int
783 qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event)
784 {
785 	ng_hci_qos_setup_compl_ep	*ep = NULL;
786 	ng_hci_unit_con_p		 con = NULL;
787 	u_int16_t			 h;
788 	int				 error = 0;
789 
790 	NG_HCI_M_PULLUP(event, sizeof(*ep));
791 	if (event == NULL)
792 		return (ENOBUFS);
793 
794 	ep = mtod(event, ng_hci_qos_setup_compl_ep *);
795 
796 	/* Check if we have this connection handle */
797 	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
798 	con = ng_hci_con_by_handle(unit, h);
799 	if (con == NULL) {
800 		NG_HCI_ALERT(
801 "%s: %s - invalid connection handle=%d\n",
802 			__func__, NG_NODE_NAME(unit->node), h);
803 		error = ENOENT;
804 	} else if (con->link_type != NG_HCI_LINK_ACL) {
805 		NG_HCI_ALERT(
806 "%s: %s - invalid link type=%d, handle=%d\n",
807 			__func__, NG_NODE_NAME(unit->node), con->link_type, h);
808 		error = EINVAL;
809 	} else if (con->state != NG_HCI_CON_OPEN) {
810 		NG_HCI_ALERT(
811 "%s: %s - invalid connection state=%d, handle=%d\n",
812 			__func__, NG_NODE_NAME(unit->node),
813 			con->state, h);
814 		error = EINVAL;
815 	} else /* Notify upper layer */
816 		error = ng_hci_lp_qos_cfm(con, ep->status);
817 
818 	NG_FREE_M(event);
819 
820 	return (error);
821 } /* qos_setup_compl */
822 
823 /* Hardware error event */
824 static int
825 hardware_error(ng_hci_unit_p unit, struct mbuf *event)
826 {
827 	NG_HCI_ALERT(
828 "%s: %s - hardware error %#x\n",
829 		__func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *));
830 
831 	NG_FREE_M(event);
832 
833 	return (0);
834 } /* hardware_error */
835 
836 /* Role change event */
837 static int
838 role_change(ng_hci_unit_p unit, struct mbuf *event)
839 {
840 	ng_hci_role_change_ep	*ep = NULL;
841 	ng_hci_unit_con_p	 con = NULL;
842 
843 	NG_HCI_M_PULLUP(event, sizeof(*ep));
844 	if (event == NULL)
845 		return (ENOBUFS);
846 
847 	ep = mtod(event, ng_hci_role_change_ep *);
848 
849 	if (ep->status == 0) {
850 		/* XXX shoud we also change "role" for SCO connections? */
851 		con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
852 		if (con != NULL)
853 			con->role = ep->role;
854 		else
855 			NG_HCI_ALERT(
856 "%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n",
857 				__func__, NG_NODE_NAME(unit->node),
858 				ep->bdaddr.b[5], ep->bdaddr.b[4],
859 				ep->bdaddr.b[3], ep->bdaddr.b[2],
860 				ep->bdaddr.b[1], ep->bdaddr.b[0]);
861 	} else
862 		NG_HCI_ERR(
863 "%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n",
864 			__func__, NG_NODE_NAME(unit->node), ep->status,
865 			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
866 			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
867 
868 	NG_FREE_M(event);
869 
870 	return (0);
871 } /* role_change */
872 
873 /* Number of completed packets event */
874 static int
875 num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event)
876 {
877 	ng_hci_num_compl_pkts_ep	*ep = NULL;
878 	ng_hci_unit_con_p		 con = NULL;
879 	u_int16_t			 h, p;
880 
881 	NG_HCI_M_PULLUP(event, sizeof(*ep));
882 	if (event == NULL)
883 		return (ENOBUFS);
884 
885 	ep = mtod(event, ng_hci_num_compl_pkts_ep *);
886 	m_adj(event, sizeof(*ep));
887 
888 	for (; ep->num_con_handles > 0; ep->num_con_handles --) {
889 		/* Get connection handle */
890 		m_copydata(event, 0, sizeof(h), (caddr_t) &h);
891 		m_adj(event, sizeof(h));
892 		h = NG_HCI_CON_HANDLE(le16toh(h));
893 
894 		/* Get number of completed packets */
895 		m_copydata(event, 0, sizeof(p), (caddr_t) &p);
896 		m_adj(event, sizeof(p));
897 		p = le16toh(p);
898 
899 		/* Check if we have this connection handle */
900 		con = ng_hci_con_by_handle(unit, h);
901 		if (con != NULL) {
902 			con->pending -= p;
903 			if (con->pending < 0) {
904 				NG_HCI_WARN(
905 "%s: %s - pending packet counter is out of sync! " \
906 "handle=%d, pending=%d, ncp=%d\n",	__func__, NG_NODE_NAME(unit->node),
907 					con->con_handle, con->pending, p);
908 
909 				con->pending = 0;
910 			}
911 
912 			/* Update buffer descriptor */
913 			if (con->link_type == NG_HCI_LINK_ACL)
914 				NG_HCI_BUFF_ACL_FREE(unit->buffer, p);
915 			else
916 				NG_HCI_BUFF_SCO_FREE(unit->buffer, p);
917 		} else
918 			NG_HCI_ALERT(
919 "%s: %s - invalid connection handle=%d\n",
920 				__func__, NG_NODE_NAME(unit->node), h);
921 	}
922 
923 	NG_FREE_M(event);
924 
925 	/* Send more data */
926 	ng_hci_send_data(unit);
927 
928 	return (0);
929 } /* num_compl_pkts */
930 
931 /* Mode change event */
932 static int
933 mode_change(ng_hci_unit_p unit, struct mbuf *event)
934 {
935 	ng_hci_mode_change_ep	*ep = NULL;
936 	ng_hci_unit_con_p	 con = NULL;
937 	int			 error = 0;
938 
939 	NG_HCI_M_PULLUP(event, sizeof(*ep));
940 	if (event == NULL)
941 		return (ENOBUFS);
942 
943 	ep = mtod(event, ng_hci_mode_change_ep *);
944 
945 	if (ep->status == 0) {
946 		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
947 
948 		con = ng_hci_con_by_handle(unit, h);
949 		if (con == NULL) {
950 			NG_HCI_ALERT(
951 "%s: %s - invalid connection handle=%d\n",
952 				__func__, NG_NODE_NAME(unit->node), h);
953 			error = ENOENT;
954 		} else if (con->link_type != NG_HCI_LINK_ACL) {
955 			NG_HCI_ALERT(
956 "%s: %s - invalid link type=%d\n",
957 				__func__, NG_NODE_NAME(unit->node),
958 				con->link_type);
959 			error = EINVAL;
960 		} else
961 			con->mode = ep->unit_mode;
962 	} else
963 		NG_HCI_ERR(
964 "%s: %s - failed to change mode, status=%d\n",
965 			__func__, NG_NODE_NAME(unit->node), ep->status);
966 
967 	NG_FREE_M(event);
968 
969 	return (error);
970 } /* mode_change */
971 
972 /* Data buffer overflow event */
973 static int
974 data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event)
975 {
976 	NG_HCI_ALERT(
977 "%s: %s - %s data buffer overflow\n",
978 		__func__, NG_NODE_NAME(unit->node),
979 		(*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO");
980 
981 	NG_FREE_M(event);
982 
983 	return (0);
984 } /* data_buffer_overflow */
985 
986 /* Read clock offset complete event */
987 static int
988 read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event)
989 {
990 	ng_hci_read_clock_offset_compl_ep	*ep = NULL;
991 	ng_hci_unit_con_p			 con = NULL;
992 	ng_hci_neighbor_p			 n = NULL;
993 	int					 error = 0;
994 
995 	NG_HCI_M_PULLUP(event, sizeof(*ep));
996 	if (event == NULL)
997 		return (ENOBUFS);
998 
999 	ep = mtod(event, ng_hci_read_clock_offset_compl_ep *);
1000 
1001 	if (ep->status == 0) {
1002 		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1003 
1004 		con = ng_hci_con_by_handle(unit, h);
1005 		if (con == NULL) {
1006 			NG_HCI_ALERT(
1007 "%s: %s - invalid connection handle=%d\n",
1008 				__func__, NG_NODE_NAME(unit->node), h);
1009 			error = ENOENT;
1010 			goto out;
1011 		}
1012 
1013 		/* Update cache entry */
1014 		n = ng_hci_get_neighbor(unit, &con->bdaddr);
1015 		if (n == NULL) {
1016 			n = ng_hci_new_neighbor(unit);
1017 			if (n == NULL) {
1018 				error = ENOMEM;
1019 				goto out;
1020 			}
1021 
1022 			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1023 		} else
1024 			getmicrotime(&n->updated);
1025 
1026 		n->clock_offset = le16toh(ep->clock_offset);
1027 	} else
1028 		NG_HCI_ERR(
1029 "%s: %s - failed to Read Remote Clock Offset, status=%d\n",
1030 			__func__, NG_NODE_NAME(unit->node), ep->status);
1031 out:
1032 	NG_FREE_M(event);
1033 
1034 	return (error);
1035 } /* read_clock_offset_compl */
1036 
1037 /* QoS violation event */
1038 static int
1039 qos_violation(ng_hci_unit_p unit, struct mbuf *event)
1040 {
1041 	ng_hci_qos_violation_ep	*ep = NULL;
1042 	ng_hci_unit_con_p	 con = NULL;
1043 	u_int16_t		 h;
1044 	int			 error = 0;
1045 
1046 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1047 	if (event == NULL)
1048 		return (ENOBUFS);
1049 
1050 	ep = mtod(event, ng_hci_qos_violation_ep *);
1051 
1052 	/* Check if we have this connection handle */
1053 	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1054 	con = ng_hci_con_by_handle(unit, h);
1055 	if (con == NULL) {
1056 		NG_HCI_ALERT(
1057 "%s: %s - invalid connection handle=%d\n",
1058 			__func__, NG_NODE_NAME(unit->node), h);
1059 		error = ENOENT;
1060 	} else if (con->link_type != NG_HCI_LINK_ACL) {
1061 		NG_HCI_ALERT(
1062 "%s: %s - invalid link type=%d\n",
1063 			__func__, NG_NODE_NAME(unit->node), con->link_type);
1064 		error = EINVAL;
1065 	} else if (con->state != NG_HCI_CON_OPEN) {
1066 		NG_HCI_ALERT(
1067 "%s: %s - invalid connection state=%d, handle=%d\n",
1068 			__func__, NG_NODE_NAME(unit->node), con->state, h);
1069 		error = EINVAL;
1070 	} else /* Notify upper layer */
1071 		error = ng_hci_lp_qos_ind(con);
1072 
1073 	NG_FREE_M(event);
1074 
1075 	return (error);
1076 } /* qos_violation */
1077 
1078 /* Page scan mode change event */
1079 static int
1080 page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1081 {
1082 	ng_hci_page_scan_mode_change_ep	*ep = NULL;
1083 	ng_hci_neighbor_p		 n = NULL;
1084 	int				 error = 0;
1085 
1086 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1087 	if (event == NULL)
1088 		return (ENOBUFS);
1089 
1090 	ep = mtod(event, ng_hci_page_scan_mode_change_ep *);
1091 
1092 	/* Update cache entry */
1093 	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
1094 	if (n == NULL) {
1095 		n = ng_hci_new_neighbor(unit);
1096 		if (n == NULL) {
1097 			error = ENOMEM;
1098 			goto out;
1099 		}
1100 
1101 		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1102 	} else
1103 		getmicrotime(&n->updated);
1104 
1105 	n->page_scan_mode = ep->page_scan_mode;
1106 out:
1107 	NG_FREE_M(event);
1108 
1109 	return (error);
1110 } /* page_scan_mode_change */
1111 
1112 /* Page scan repetition mode change event */
1113 static int
1114 page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1115 {
1116 	ng_hci_page_scan_rep_mode_change_ep	*ep = NULL;
1117 	ng_hci_neighbor_p			 n = NULL;
1118 	int					 error = 0;
1119 
1120 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1121 	if (event == NULL)
1122 		return (ENOBUFS);
1123 
1124 	ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *);
1125 
1126 	/* Update cache entry */
1127 	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
1128 	if (n == NULL) {
1129 		n = ng_hci_new_neighbor(unit);
1130 		if (n == NULL) {
1131 			error = ENOMEM;
1132 			goto out;
1133 		}
1134 
1135 		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1136 	} else
1137 		getmicrotime(&n->updated);
1138 
1139 	n->page_scan_rep_mode = ep->page_scan_rep_mode;
1140 out:
1141 	NG_FREE_M(event);
1142 
1143 	return (error);
1144 } /* page_scan_rep_mode_change */
1145 
1146