xref: /freebsd/sys/netgraph/bluetooth/hci/ng_hci_evnt.c (revision d93a896ef95946b0bf1219866fcb324b78543444)
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$
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/endian.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/queue.h>
41 #include <netgraph/ng_message.h>
42 #include <netgraph/netgraph.h>
43 #include <netgraph/bluetooth/include/ng_bluetooth.h>
44 #include <netgraph/bluetooth/include/ng_hci.h>
45 #include <netgraph/bluetooth/hci/ng_hci_var.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  **                     HCI event processing module
54  ******************************************************************************
55  ******************************************************************************/
56 
57 /*
58  * Event processing routines
59  */
60 
61 static int inquiry_result             (ng_hci_unit_p, struct mbuf *);
62 static int con_compl                  (ng_hci_unit_p, struct mbuf *);
63 static int con_req                    (ng_hci_unit_p, struct mbuf *);
64 static int discon_compl               (ng_hci_unit_p, struct mbuf *);
65 static int encryption_change          (ng_hci_unit_p, struct mbuf *);
66 static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *);
67 static int qos_setup_compl            (ng_hci_unit_p, struct mbuf *);
68 static int hardware_error             (ng_hci_unit_p, struct mbuf *);
69 static int role_change                (ng_hci_unit_p, struct mbuf *);
70 static int num_compl_pkts             (ng_hci_unit_p, struct mbuf *);
71 static int mode_change                (ng_hci_unit_p, struct mbuf *);
72 static int data_buffer_overflow       (ng_hci_unit_p, struct mbuf *);
73 static int read_clock_offset_compl    (ng_hci_unit_p, struct mbuf *);
74 static int qos_violation              (ng_hci_unit_p, struct mbuf *);
75 static int page_scan_mode_change      (ng_hci_unit_p, struct mbuf *);
76 static int page_scan_rep_mode_change  (ng_hci_unit_p, struct mbuf *);
77 static int sync_con_queue             (ng_hci_unit_p, ng_hci_unit_con_p, int);
78 static int send_data_packets          (ng_hci_unit_p, int, int);
79 static int le_event		      (ng_hci_unit_p, struct mbuf *);
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 	case NG_HCI_EVENT_LE:
126 		error = le_event(unit, event);
127 		break;
128 
129 	case NG_HCI_EVENT_INQUIRY_RESULT:
130 		error = inquiry_result(unit, event);
131 		break;
132 
133 	case NG_HCI_EVENT_CON_COMPL:
134 		error = con_compl(unit, event);
135 		break;
136 
137 	case NG_HCI_EVENT_CON_REQ:
138 		error = con_req(unit, event);
139 		break;
140 
141 	case NG_HCI_EVENT_DISCON_COMPL:
142 		error = discon_compl(unit, event);
143 		break;
144 
145 	case NG_HCI_EVENT_ENCRYPTION_CHANGE:
146 		error = encryption_change(unit, event);
147 		break;
148 
149 	case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL:
150 		error = read_remote_features_compl(unit, event);
151 		break;
152 
153 	case NG_HCI_EVENT_QOS_SETUP_COMPL:
154 		error = qos_setup_compl(unit, event);
155 		break;
156 
157 	case NG_HCI_EVENT_COMMAND_COMPL:
158 		error = ng_hci_process_command_complete(unit, event);
159 		break;
160 
161 	case NG_HCI_EVENT_COMMAND_STATUS:
162 		error = ng_hci_process_command_status(unit, event);
163 		break;
164 
165 	case NG_HCI_EVENT_HARDWARE_ERROR:
166 		error = hardware_error(unit, event);
167 		break;
168 
169 	case NG_HCI_EVENT_ROLE_CHANGE:
170 		error = role_change(unit, event);
171 		break;
172 
173 	case NG_HCI_EVENT_NUM_COMPL_PKTS:
174 		error = num_compl_pkts(unit, event);
175 		break;
176 
177 	case NG_HCI_EVENT_MODE_CHANGE:
178 		error = mode_change(unit, event);
179 		break;
180 
181 	case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW:
182 		error = data_buffer_overflow(unit, event);
183 		break;
184 
185 	case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL:
186 		error = read_clock_offset_compl(unit, event);
187 		break;
188 
189 	case NG_HCI_EVENT_QOS_VIOLATION:
190 		error = qos_violation(unit, event);
191 		break;
192 
193 	case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE:
194 		error = page_scan_mode_change(unit, event);
195 		break;
196 
197 	case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE:
198 		error = page_scan_rep_mode_change(unit, event);
199 		break;
200 
201 	default:
202 		NG_FREE_M(event);
203 		error = EINVAL;
204 		break;
205 	}
206 
207 	return (error);
208 } /* ng_hci_process_event */
209 
210 /*
211  * Send ACL and/or SCO data to the unit driver
212  */
213 
214 void
215 ng_hci_send_data(ng_hci_unit_p unit)
216 {
217 	int	count;
218 
219 	/* Send ACL data */
220 	NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count);
221 
222 	NG_HCI_INFO(
223 "%s: %s - sending ACL data packets, count=%d\n",
224 		__func__, NG_NODE_NAME(unit->node), count);
225 
226 	if (count > 0) {
227 		count = send_data_packets(unit, NG_HCI_LINK_ACL, count);
228 		NG_HCI_STAT_ACL_SENT(unit->stat, count);
229 		NG_HCI_BUFF_ACL_USE(unit->buffer, count);
230 	}
231 
232 	/* Send SCO data */
233 	NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count);
234 
235 	NG_HCI_INFO(
236 "%s: %s - sending SCO data packets, count=%d\n",
237 		__func__, NG_NODE_NAME(unit->node), count);
238 
239 	if (count > 0) {
240 		count = send_data_packets(unit, NG_HCI_LINK_SCO, count);
241 		NG_HCI_STAT_SCO_SENT(unit->stat, count);
242 		NG_HCI_BUFF_SCO_USE(unit->buffer, count);
243 	}
244 } /* ng_hci_send_data */
245 
246 /*
247  * Send data packets to the lower layer.
248  */
249 
250 static int
251 send_data_packets(ng_hci_unit_p unit, int link_type, int limit)
252 {
253 	ng_hci_unit_con_p	con = NULL, winner = NULL;
254 	int			reallink_type;
255 	item_p			item = NULL;
256 	int			min_pending, total_sent, sent, error, v;
257 
258 	for (total_sent = 0; limit > 0; ) {
259 		min_pending = 0x0fffffff;
260 		winner = NULL;
261 
262 		/*
263 		 * Find the connection that has has data to send
264 		 * and the smallest number of pending packets
265 		 */
266 
267 		LIST_FOREACH(con, &unit->con_list, next) {
268 			reallink_type = (con->link_type == NG_HCI_LINK_SCO)?
269 				NG_HCI_LINK_SCO: NG_HCI_LINK_ACL;
270 			if (reallink_type != link_type){
271 				continue;
272 			}
273 			if (NG_BT_ITEMQ_LEN(&con->conq) == 0)
274 				continue;
275 
276 			if (con->pending < min_pending) {
277 				winner = con;
278 				min_pending = con->pending;
279 			}
280 		}
281 
282 	        if (winner == NULL)
283 			break;
284 
285 		/*
286 		 * OK, we have a winner now send as much packets as we can
287 		 * Count the number of packets we have sent and then sync
288 		 * winner connection queue.
289 		 */
290 
291 		for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) {
292 			NG_BT_ITEMQ_DEQUEUE(&winner->conq, item);
293 			if (item == NULL)
294 				break;
295 
296 			NG_HCI_INFO(
297 "%s: %s - sending data packet, handle=%d, len=%d\n",
298 				__func__, NG_NODE_NAME(unit->node),
299 				winner->con_handle, NGI_M(item)->m_pkthdr.len);
300 
301 			/* Check if driver hook still there */
302 			v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv));
303 			if (!v || (unit->state & NG_HCI_UNIT_READY) !=
304 					NG_HCI_UNIT_READY) {
305 				NG_HCI_ERR(
306 "%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n",
307 					__func__, NG_NODE_NAME(unit->node),
308 					NG_HCI_HOOK_DRV, ((v)? "" : "not "),
309 					unit->state);
310 
311 				NG_FREE_ITEM(item);
312 				error = ENOTCONN;
313 			} else {
314 				v = NGI_M(item)->m_pkthdr.len;
315 
316 				/* Give packet to raw hook */
317 				ng_hci_mtap(unit, NGI_M(item));
318 
319 				/* ... and forward item to the driver */
320 				NG_FWD_ITEM_HOOK(error, item, unit->drv);
321 			}
322 
323 			if (error != 0) {
324 				NG_HCI_ERR(
325 "%s: %s - could not send data packet, handle=%d, error=%d\n",
326 					__func__, NG_NODE_NAME(unit->node),
327 					winner->con_handle, error);
328 				break;
329 			}
330 
331 			winner->pending ++;
332 			NG_HCI_STAT_BYTES_SENT(unit->stat, v);
333 		}
334 
335 		/*
336 		 * Sync connection queue for the winner
337 		 */
338 		sync_con_queue(unit, winner, sent);
339 	}
340 
341 	return (total_sent);
342 } /* send_data_packets */
343 
344 /*
345  * Send flow control messages to the upper layer
346  */
347 
348 static int
349 sync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed)
350 {
351 	hook_p				 hook = NULL;
352 	struct ng_mesg			*msg = NULL;
353 	ng_hci_sync_con_queue_ep	*state = NULL;
354 	int				 error;
355 
356 	hook = (con->link_type != NG_HCI_LINK_SCO)? unit->acl : unit->sco;
357 	if (hook == NULL || NG_HOOK_NOT_VALID(hook))
358 		return (ENOTCONN);
359 
360 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE,
361 		sizeof(*state), M_NOWAIT);
362 	if (msg == NULL)
363 		return (ENOMEM);
364 
365 	state = (ng_hci_sync_con_queue_ep *)(msg->data);
366 	state->con_handle = con->con_handle;
367 	state->completed = completed;
368 
369 	NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
370 
371 	return (error);
372 } /* sync_con_queue */
373 /* le meta event */
374 /* Inquiry result event */
375 static int
376 le_advertizing_report(ng_hci_unit_p unit, struct mbuf *event)
377 {
378 	ng_hci_le_advertising_report_ep	*ep = NULL;
379 	ng_hci_neighbor_p		 n = NULL;
380 	bdaddr_t			 bdaddr;
381 	int				 error = 0;
382 	u_int8_t event_type;
383 	u_int8_t addr_type;
384 
385 	NG_HCI_M_PULLUP(event, sizeof(*ep));
386 	if (event == NULL)
387 		return (ENOBUFS);
388 
389 	ep = mtod(event, ng_hci_le_advertising_report_ep *);
390 	m_adj(event, sizeof(*ep));
391 
392 	for (; ep->num_reports > 0; ep->num_reports --) {
393 		/* Get remote unit address */
394 		NG_HCI_M_PULLUP(event, sizeof(u_int8_t));
395 		event_type = *mtod(event, u_int8_t *);
396 		m_adj(event, sizeof(u_int8_t));
397 		NG_HCI_M_PULLUP(event, sizeof(u_int8_t));
398 		addr_type = *mtod(event, u_int8_t *);
399 		m_adj(event, sizeof(u_int8_t));
400 
401 		m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr);
402 		m_adj(event, sizeof(bdaddr));
403 
404 		/* Lookup entry in the cache */
405 		n = ng_hci_get_neighbor(unit, &bdaddr, (addr_type) ? NG_HCI_LINK_LE_RANDOM:NG_HCI_LINK_LE_PUBLIC);
406 		if (n == NULL) {
407 			/* Create new entry */
408 			n = ng_hci_new_neighbor(unit);
409 			if (n == NULL) {
410 				error = ENOMEM;
411 				break;
412 			}
413 			bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));
414 			n->addrtype = (addr_type)? NG_HCI_LINK_LE_RANDOM :
415 			  NG_HCI_LINK_LE_PUBLIC;
416 
417 		} else
418 			getmicrotime(&n->updated);
419 
420 		{
421 			/*
422 			 * TODO: Make these information
423 			 * Available from userland.
424 			 */
425 			u_int8_t length_data;
426 
427 			event = m_pullup(event, sizeof(u_int8_t));
428 			if(event == NULL){
429 				NG_HCI_WARN("%s: Event datasize Pullup Failed\n", __func__);
430 				goto out;
431 			}
432 			length_data = *mtod(event, u_int8_t *);
433 			m_adj(event, sizeof(u_int8_t));
434 			n->extinq_size = (length_data < NG_HCI_EXTINQ_MAX)?
435 				length_data : NG_HCI_EXTINQ_MAX;
436 
437 			/*Advertizement data*/
438 			event = m_pullup(event, n->extinq_size);
439 			if(event == NULL){
440 				NG_HCI_WARN("%s: Event data pullup Failed\n", __func__);
441 				goto out;
442 			}
443 			m_copydata(event, 0, n->extinq_size, n->extinq_data);
444 			m_adj(event, n->extinq_size);
445 			event = m_pullup(event, sizeof(char ));
446 			/*Get RSSI*/
447 			if(event == NULL){
448 				NG_HCI_WARN("%s: Event rssi pull up Failed\n", __func__);
449 
450 				goto out;
451 			}
452 			n->page_scan_mode = *mtod(event, char *);
453 			m_adj(event, sizeof(u_int8_t));
454 		}
455 	}
456  out:
457 	NG_FREE_M(event);
458 
459 	return (error);
460 } /* inquiry_result */
461 
462 static int le_connection_complete(ng_hci_unit_p unit, struct mbuf *event)
463 {
464 	int			 error = 0;
465 
466 	ng_hci_le_connection_complete_ep	*ep = NULL;
467 	ng_hci_unit_con_p	 con = NULL;
468 	int link_type;
469 	uint8_t uclass[3] = {0,0,0};//dummy uclass
470 
471 	NG_HCI_M_PULLUP(event, sizeof(*ep));
472 	if (event == NULL)
473 		return (ENOBUFS);
474 
475 	ep = mtod(event, ng_hci_le_connection_complete_ep *);
476 	link_type = (ep->address_type)? NG_HCI_LINK_LE_RANDOM :
477 	  NG_HCI_LINK_LE_PUBLIC;
478 	/*
479 	 * Find the first connection descriptor that matches the following:
480 	 *
481 	 * 1) con->link_type == link_type
482 	 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE
483 	 * 3) con->bdaddr == ep->address
484 	 */
485 	LIST_FOREACH(con, &unit->con_list, next)
486 		if (con->link_type == link_type &&
487 		    con->state == NG_HCI_CON_W4_CONN_COMPLETE &&
488 		    bcmp(&con->bdaddr, &ep->address, sizeof(bdaddr_t)) == 0)
489 			break;
490 
491 	/*
492 	 * Two possible cases:
493 	 *
494 	 * 1) We have found connection descriptor. That means upper layer has
495 	 *    requested this connection via LP_CON_REQ message. In this case
496 	 *    connection must have timeout set. If ng_hci_con_untimeout() fails
497 	 *    then timeout message already went into node's queue. In this case
498 	 *    ignore Connection_Complete event and let timeout deal with it.
499 	 *
500 	 * 2) We do not have connection descriptor. That means upper layer
501 	 *    nas not requested this connection , (less likely) we gave up
502 	 *    on this connection (timeout) or as node act as slave role.
503 	 *    The most likely scenario is that
504 	 *    we have received LE_Create_Connection command
505 	 *    from the RAW hook
506 	 */
507 
508 	if (con == NULL) {
509 		if (ep->status != 0)
510 			goto out;
511 
512 		con = ng_hci_new_con(unit, link_type);
513 		if (con == NULL) {
514 			error = ENOMEM;
515 			goto out;
516 		}
517 
518 		con->state = NG_HCI_CON_W4_LP_CON_RSP;
519 		ng_hci_con_timeout(con);
520 
521 		bcopy(&ep->address, &con->bdaddr, sizeof(con->bdaddr));
522 		error = ng_hci_lp_con_ind(con, uclass);
523 		if (error != 0) {
524 			ng_hci_con_untimeout(con);
525 			ng_hci_free_con(con);
526 		}
527 
528 	} else if ((error = ng_hci_con_untimeout(con)) != 0)
529 			goto out;
530 
531 	/*
532 	 * Update connection descriptor and send notification
533 	 * to the upper layers.
534 	 */
535 
536 	con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->handle));
537 	con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;
538 
539 	ng_hci_lp_con_cfm(con, ep->status);
540 
541 	/* Adjust connection state */
542 	if (ep->status != 0)
543 		ng_hci_free_con(con);
544 	else {
545 		con->state = NG_HCI_CON_OPEN;
546 
547 		/*
548 		 * Change link policy for the ACL connections. Enable all
549 		 * supported link modes. Enable Role switch as well if
550 		 * device supports it.
551 		 */
552 
553 	}
554 
555 out:
556 	NG_FREE_M(event);
557 
558 	return (error);
559 
560 }
561 
562 static int le_connection_update(ng_hci_unit_p unit, struct mbuf *event)
563 {
564 	int error = 0;
565 	/*TBD*/
566 
567 	NG_FREE_M(event);
568 	return error;
569 
570 }
571 static int
572 le_event(ng_hci_unit_p unit, struct mbuf *event)
573 {
574 	int error = 0;
575 	ng_hci_le_ep *lep;
576 
577 	NG_HCI_M_PULLUP(event, sizeof(*lep));
578 	if(event ==NULL){
579 		return ENOBUFS;
580 	}
581 	lep = mtod(event, ng_hci_le_ep *);
582 	m_adj(event, sizeof(*lep));
583 	switch(lep->subevent_code){
584 	case NG_HCI_LEEV_CON_COMPL:
585 		le_connection_complete(unit, event);
586 		break;
587 	case NG_HCI_LEEV_ADVREP:
588 		le_advertizing_report(unit, event);
589 		break;
590 	case NG_HCI_LEEV_CON_UPDATE_COMPL:
591 		le_connection_update(unit, event);
592 		break;
593 	case NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL:
594 		//TBD
595 	  /*FALLTHROUGH*/
596 	case NG_HCI_LEEV_LONG_TERM_KEY_REQUEST:
597 		//TBD
598 	  /*FALLTHROUGH*/
599 	default:
600 	  	NG_FREE_M(event);
601 	}
602 	return error;
603 }
604 
605 /* Inquiry result event */
606 static int
607 inquiry_result(ng_hci_unit_p unit, struct mbuf *event)
608 {
609 	ng_hci_inquiry_result_ep	*ep = NULL;
610 	ng_hci_neighbor_p		 n = NULL;
611 	bdaddr_t			 bdaddr;
612 	int				 error = 0;
613 
614 	NG_HCI_M_PULLUP(event, sizeof(*ep));
615 	if (event == NULL)
616 		return (ENOBUFS);
617 
618 	ep = mtod(event, ng_hci_inquiry_result_ep *);
619 	m_adj(event, sizeof(*ep));
620 
621 	for (; ep->num_responses > 0; ep->num_responses --) {
622 		/* Get remote unit address */
623 		m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr);
624 		m_adj(event, sizeof(bdaddr));
625 
626 		/* Lookup entry in the cache */
627 		n = ng_hci_get_neighbor(unit, &bdaddr, NG_HCI_LINK_ACL);
628 		if (n == NULL) {
629 			/* Create new entry */
630 			n = ng_hci_new_neighbor(unit);
631 			if (n == NULL) {
632 				error = ENOMEM;
633 				break;
634 			}
635 		} else
636 			getmicrotime(&n->updated);
637 
638 		bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));
639 		n->addrtype = NG_HCI_LINK_ACL;
640 
641 		/* XXX call m_pullup here? */
642 
643 		n->page_scan_rep_mode = *mtod(event, u_int8_t *);
644 		m_adj(event, sizeof(u_int8_t));
645 
646 		/* page_scan_period_mode */
647 		m_adj(event, sizeof(u_int8_t));
648 
649 		n->page_scan_mode = *mtod(event, u_int8_t *);
650 		m_adj(event, sizeof(u_int8_t));
651 
652 		/* class */
653 		m_adj(event, NG_HCI_CLASS_SIZE);
654 
655 		/* clock offset */
656 		m_copydata(event, 0, sizeof(n->clock_offset),
657 			(caddr_t) &n->clock_offset);
658 		n->clock_offset = le16toh(n->clock_offset);
659 	}
660 
661 	NG_FREE_M(event);
662 
663 	return (error);
664 } /* inquiry_result */
665 
666 /* Connection complete event */
667 static int
668 con_compl(ng_hci_unit_p unit, struct mbuf *event)
669 {
670 	ng_hci_con_compl_ep	*ep = NULL;
671 	ng_hci_unit_con_p	 con = NULL;
672 	int			 error = 0;
673 
674 	NG_HCI_M_PULLUP(event, sizeof(*ep));
675 	if (event == NULL)
676 		return (ENOBUFS);
677 
678 	ep = mtod(event, ng_hci_con_compl_ep *);
679 
680 	/*
681 	 * Find the first connection descriptor that matches the following:
682 	 *
683 	 * 1) con->link_type == ep->link_type
684 	 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE
685 	 * 3) con->bdaddr == ep->bdaddr
686 	 */
687 
688 	LIST_FOREACH(con, &unit->con_list, next)
689 		if (con->link_type == ep->link_type &&
690 		    con->state == NG_HCI_CON_W4_CONN_COMPLETE &&
691 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
692 			break;
693 
694 	/*
695 	 * Two possible cases:
696 	 *
697 	 * 1) We have found connection descriptor. That means upper layer has
698 	 *    requested this connection via LP_CON_REQ message. In this case
699 	 *    connection must have timeout set. If ng_hci_con_untimeout() fails
700 	 *    then timeout message already went into node's queue. In this case
701 	 *    ignore Connection_Complete event and let timeout deal with it.
702 	 *
703 	 * 2) We do not have connection descriptor. That means upper layer
704 	 *    nas not requested this connection or (less likely) we gave up
705 	 *    on this connection (timeout). The most likely scenario is that
706 	 *    we have received Create_Connection/Add_SCO_Connection command
707 	 *    from the RAW hook
708 	 */
709 
710 	if (con == NULL) {
711 		if (ep->status != 0)
712 			goto out;
713 
714 		con = ng_hci_new_con(unit, ep->link_type);
715 		if (con == NULL) {
716 			error = ENOMEM;
717 			goto out;
718 		}
719 
720 		bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
721 	} else if ((error = ng_hci_con_untimeout(con)) != 0)
722 			goto out;
723 
724 	/*
725 	 * Update connection descriptor and send notification
726 	 * to the upper layers.
727 	 */
728 
729 	con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
730 	con->encryption_mode = ep->encryption_mode;
731 
732 	ng_hci_lp_con_cfm(con, ep->status);
733 
734 	/* Adjust connection state */
735 	if (ep->status != 0)
736 		ng_hci_free_con(con);
737 	else {
738 		con->state = NG_HCI_CON_OPEN;
739 
740 		/*
741 		 * Change link policy for the ACL connections. Enable all
742 		 * supported link modes. Enable Role switch as well if
743 		 * device supports it.
744 		 */
745 
746 		if (ep->link_type == NG_HCI_LINK_ACL) {
747 			struct __link_policy {
748 				ng_hci_cmd_pkt_t			 hdr;
749 				ng_hci_write_link_policy_settings_cp	 cp;
750 			} __attribute__ ((packed))			*lp;
751 			struct mbuf					*m;
752 
753 			MGETHDR(m, M_NOWAIT, MT_DATA);
754 			if (m != NULL) {
755 				m->m_pkthdr.len = m->m_len = sizeof(*lp);
756 				lp = mtod(m, struct __link_policy *);
757 
758 				lp->hdr.type = NG_HCI_CMD_PKT;
759 				lp->hdr.opcode = htole16(NG_HCI_OPCODE(
760 					NG_HCI_OGF_LINK_POLICY,
761 					NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS));
762 				lp->hdr.length = sizeof(lp->cp);
763 
764 				lp->cp.con_handle = ep->con_handle;
765 
766 				lp->cp.settings = 0;
767 				if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
768 				    unit->role_switch)
769 					lp->cp.settings |= 0x1;
770 				if (unit->features[0] & NG_HCI_LMP_HOLD_MODE)
771 					lp->cp.settings |= 0x2;
772 				if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE)
773 					lp->cp.settings |= 0x4;
774 				if (unit->features[1] & NG_HCI_LMP_PARK_MODE)
775 					lp->cp.settings |= 0x8;
776 
777 				lp->cp.settings &= unit->link_policy_mask;
778 				lp->cp.settings = htole16(lp->cp.settings);
779 
780 				NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
781 				if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
782 					ng_hci_send_command(unit);
783 			}
784 		}
785 	}
786 out:
787 	NG_FREE_M(event);
788 
789 	return (error);
790 } /* con_compl */
791 
792 /* Connection request event */
793 static int
794 con_req(ng_hci_unit_p unit, struct mbuf *event)
795 {
796 	ng_hci_con_req_ep	*ep = NULL;
797 	ng_hci_unit_con_p	 con = NULL;
798 	int			 error = 0;
799 
800 	NG_HCI_M_PULLUP(event, sizeof(*ep));
801 	if (event == NULL)
802 		return (ENOBUFS);
803 
804 	ep = mtod(event, ng_hci_con_req_ep *);
805 
806 	/*
807 	 * Find the first connection descriptor that matches the following:
808 	 *
809 	 * 1) con->link_type == ep->link_type
810 	 *
811 	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
812 	 *    con->state == NG_HCI_CON_W4_CONN_COMPL
813 	 *
814 	 * 3) con->bdaddr == ep->bdaddr
815 	 *
816 	 * Possible cases:
817 	 *
818 	 * 1) We do not have connection descriptor. This is simple. Create
819 	 *    new fresh connection descriptor and send notification to the
820 	 *    appropriate upstream hook (based on link_type).
821 	 *
822 	 * 2) We found connection handle. This is more complicated.
823 	 *
824 	 * 2.1) ACL links
825 	 *
826 	 *      Since only one ACL link can exist between each pair of
827 	 *      units then we have a race. Our upper layer has requested
828 	 *      an ACL connection to the remote unit, but we did not send
829 	 *      command yet. At the same time the remote unit has requested
830 	 *      an ACL connection from us. In this case we will ignore
831 	 *	Connection_Request event. This probably will cause connect
832 	 *      failure	on both units.
833 	 *
834 	 * 2.2) SCO links
835 	 *
836 	 *      The spec on page 45 says :
837 	 *
838 	 *      "The master can support up to three SCO links to the same
839 	 *       slave or to different slaves. A slave can support up to
840 	 *       three SCO links from the same master, or two SCO links if
841 	 *       the links originate from different masters."
842 	 *
843 	 *      The only problem is how to handle multiple SCO links between
844 	 *      matster and slave. For now we will assume that multiple SCO
845 	 *      links MUST be opened one after another.
846 	 */
847 
848 	LIST_FOREACH(con, &unit->con_list, next)
849 		if (con->link_type == ep->link_type &&
850 		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
851 		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
852 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
853 			break;
854 
855 	if (con == NULL) {
856 		con = ng_hci_new_con(unit, ep->link_type);
857 		if (con != NULL) {
858 			bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
859 
860 			con->state = NG_HCI_CON_W4_LP_CON_RSP;
861 			ng_hci_con_timeout(con);
862 
863 			error = ng_hci_lp_con_ind(con, ep->uclass);
864 			if (error != 0) {
865 				ng_hci_con_untimeout(con);
866 				ng_hci_free_con(con);
867 			}
868 		} else
869 			error = ENOMEM;
870 	}
871 
872 	NG_FREE_M(event);
873 
874 	return (error);
875 } /* con_req */
876 
877 /* Disconnect complete event */
878 static int
879 discon_compl(ng_hci_unit_p unit, struct mbuf *event)
880 {
881 	ng_hci_discon_compl_ep	*ep = NULL;
882 	ng_hci_unit_con_p	 con = NULL;
883 	int			 error = 0;
884 	u_int16_t		 h;
885 
886 	NG_HCI_M_PULLUP(event, sizeof(*ep));
887 	if (event == NULL)
888 		return (ENOBUFS);
889 
890 	ep = mtod(event, ng_hci_discon_compl_ep *);
891 
892 	/*
893 	 * XXX
894 	 * Do we have to send notification if ep->status != 0?
895 	 * For now we will send notification for both ACL and SCO connections
896 	 * ONLY if ep->status == 0.
897 	 */
898 
899 	if (ep->status == 0) {
900 		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
901 		con = ng_hci_con_by_handle(unit, h);
902 		if (con != NULL) {
903 			error = ng_hci_lp_discon_ind(con, ep->reason);
904 
905 			/* Remove all timeouts (if any) */
906 			if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
907 				ng_hci_con_untimeout(con);
908 
909 			ng_hci_free_con(con);
910 		} else {
911 			NG_HCI_ALERT(
912 "%s: %s - invalid connection handle=%d\n",
913 				__func__, NG_NODE_NAME(unit->node), h);
914 			error = ENOENT;
915 		}
916 	}
917 
918 	NG_FREE_M(event);
919 
920 	return (error);
921 } /* discon_compl */
922 
923 /* Encryption change event */
924 static int
925 encryption_change(ng_hci_unit_p unit, struct mbuf *event)
926 {
927 	ng_hci_encryption_change_ep	*ep = NULL;
928 	ng_hci_unit_con_p		 con = NULL;
929 	int				 error = 0;
930 	u_int16_t	h;
931 
932 	NG_HCI_M_PULLUP(event, sizeof(*ep));
933 	if (event == NULL)
934 		return (ENOBUFS);
935 
936 	ep = mtod(event, ng_hci_encryption_change_ep *);
937 	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
938 	con = ng_hci_con_by_handle(unit, h);
939 
940 	if (ep->status == 0) {
941 		if (con == NULL) {
942 			NG_HCI_ALERT(
943 "%s: %s - invalid connection handle=%d\n",
944 				__func__, NG_NODE_NAME(unit->node), h);
945 			error = ENOENT;
946 		} else if (con->link_type == NG_HCI_LINK_SCO) {
947 			NG_HCI_ALERT(
948 "%s: %s - invalid link type=%d\n",
949 				__func__, NG_NODE_NAME(unit->node),
950 				con->link_type);
951 			error = EINVAL;
952 		} else if (ep->encryption_enable)
953 			/* XXX is that true? */
954 			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_P2P;
955 		else
956 			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;
957 	} else
958 		NG_HCI_ERR(
959 "%s: %s - failed to change encryption mode, status=%d\n",
960 			__func__, NG_NODE_NAME(unit->node), ep->status);
961 
962 	/*Anyway, propagete encryption status to upper layer*/
963 	ng_hci_lp_enc_change(con, con->encryption_mode);
964 
965 	NG_FREE_M(event);
966 
967 	return (error);
968 } /* encryption_change */
969 
970 /* Read remote feature complete event */
971 static int
972 read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event)
973 {
974 	ng_hci_read_remote_features_compl_ep	*ep = NULL;
975 	ng_hci_unit_con_p			 con = NULL;
976 	ng_hci_neighbor_p			 n = NULL;
977 	u_int16_t				 h;
978 	int					 error = 0;
979 
980 	NG_HCI_M_PULLUP(event, sizeof(*ep));
981 	if (event == NULL)
982 		return (ENOBUFS);
983 
984 	ep = mtod(event, ng_hci_read_remote_features_compl_ep *);
985 
986 	if (ep->status == 0) {
987 		/* Check if we have this connection handle */
988 		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
989 		con = ng_hci_con_by_handle(unit, h);
990 		if (con == NULL) {
991 			NG_HCI_ALERT(
992 "%s: %s - invalid connection handle=%d\n",
993 				__func__, NG_NODE_NAME(unit->node), h);
994 			error = ENOENT;
995 			goto out;
996 		}
997 
998 		/* Update cache entry */
999 		n = ng_hci_get_neighbor(unit, &con->bdaddr, NG_HCI_LINK_ACL);
1000 		if (n == NULL) {
1001 			n = ng_hci_new_neighbor(unit);
1002 			if (n == NULL) {
1003 				error = ENOMEM;
1004 				goto out;
1005 			}
1006 
1007 			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1008 			n->addrtype = NG_HCI_LINK_ACL;
1009 		} else
1010 			getmicrotime(&n->updated);
1011 
1012 		bcopy(ep->features, n->features, sizeof(n->features));
1013 	} else
1014 		NG_HCI_ERR(
1015 "%s: %s - failed to read remote unit features, status=%d\n",
1016 			__func__, NG_NODE_NAME(unit->node), ep->status);
1017 out:
1018 	NG_FREE_M(event);
1019 
1020 	return (error);
1021 } /* read_remote_features_compl */
1022 
1023 /* QoS setup complete event */
1024 static int
1025 qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event)
1026 {
1027 	ng_hci_qos_setup_compl_ep	*ep = NULL;
1028 	ng_hci_unit_con_p		 con = NULL;
1029 	u_int16_t			 h;
1030 	int				 error = 0;
1031 
1032 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1033 	if (event == NULL)
1034 		return (ENOBUFS);
1035 
1036 	ep = mtod(event, ng_hci_qos_setup_compl_ep *);
1037 
1038 	/* Check if we have this connection handle */
1039 	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1040 	con = ng_hci_con_by_handle(unit, h);
1041 	if (con == NULL) {
1042 		NG_HCI_ALERT(
1043 "%s: %s - invalid connection handle=%d\n",
1044 			__func__, NG_NODE_NAME(unit->node), h);
1045 		error = ENOENT;
1046 	} else if (con->link_type != NG_HCI_LINK_ACL) {
1047 		NG_HCI_ALERT(
1048 "%s: %s - invalid link type=%d, handle=%d\n",
1049 			__func__, NG_NODE_NAME(unit->node), con->link_type, h);
1050 		error = EINVAL;
1051 	} else if (con->state != NG_HCI_CON_OPEN) {
1052 		NG_HCI_ALERT(
1053 "%s: %s - invalid connection state=%d, handle=%d\n",
1054 			__func__, NG_NODE_NAME(unit->node),
1055 			con->state, h);
1056 		error = EINVAL;
1057 	} else /* Notify upper layer */
1058 		error = ng_hci_lp_qos_cfm(con, ep->status);
1059 
1060 	NG_FREE_M(event);
1061 
1062 	return (error);
1063 } /* qos_setup_compl */
1064 
1065 /* Hardware error event */
1066 static int
1067 hardware_error(ng_hci_unit_p unit, struct mbuf *event)
1068 {
1069 	NG_HCI_ALERT(
1070 "%s: %s - hardware error %#x\n",
1071 		__func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *));
1072 
1073 	NG_FREE_M(event);
1074 
1075 	return (0);
1076 } /* hardware_error */
1077 
1078 /* Role change event */
1079 static int
1080 role_change(ng_hci_unit_p unit, struct mbuf *event)
1081 {
1082 	ng_hci_role_change_ep	*ep = NULL;
1083 	ng_hci_unit_con_p	 con = NULL;
1084 
1085 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1086 	if (event == NULL)
1087 		return (ENOBUFS);
1088 
1089 	ep = mtod(event, ng_hci_role_change_ep *);
1090 
1091 	if (ep->status == 0) {
1092 		/* XXX shoud we also change "role" for SCO connections? */
1093 		con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
1094 		if (con != NULL)
1095 			con->role = ep->role;
1096 		else
1097 			NG_HCI_ALERT(
1098 "%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n",
1099 				__func__, NG_NODE_NAME(unit->node),
1100 				ep->bdaddr.b[5], ep->bdaddr.b[4],
1101 				ep->bdaddr.b[3], ep->bdaddr.b[2],
1102 				ep->bdaddr.b[1], ep->bdaddr.b[0]);
1103 	} else
1104 		NG_HCI_ERR(
1105 "%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n",
1106 			__func__, NG_NODE_NAME(unit->node), ep->status,
1107 			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
1108 			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
1109 
1110 	NG_FREE_M(event);
1111 
1112 	return (0);
1113 } /* role_change */
1114 
1115 /* Number of completed packets event */
1116 static int
1117 num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event)
1118 {
1119 	ng_hci_num_compl_pkts_ep	*ep = NULL;
1120 	ng_hci_unit_con_p		 con = NULL;
1121 	u_int16_t			 h, p;
1122 
1123 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1124 	if (event == NULL)
1125 		return (ENOBUFS);
1126 
1127 	ep = mtod(event, ng_hci_num_compl_pkts_ep *);
1128 	m_adj(event, sizeof(*ep));
1129 
1130 	for (; ep->num_con_handles > 0; ep->num_con_handles --) {
1131 		/* Get connection handle */
1132 		m_copydata(event, 0, sizeof(h), (caddr_t) &h);
1133 		m_adj(event, sizeof(h));
1134 		h = NG_HCI_CON_HANDLE(le16toh(h));
1135 
1136 		/* Get number of completed packets */
1137 		m_copydata(event, 0, sizeof(p), (caddr_t) &p);
1138 		m_adj(event, sizeof(p));
1139 		p = le16toh(p);
1140 
1141 		/* Check if we have this connection handle */
1142 		con = ng_hci_con_by_handle(unit, h);
1143 		if (con != NULL) {
1144 			con->pending -= p;
1145 			if (con->pending < 0) {
1146 				NG_HCI_WARN(
1147 "%s: %s - pending packet counter is out of sync! " \
1148 "handle=%d, pending=%d, ncp=%d\n",	__func__, NG_NODE_NAME(unit->node),
1149 					con->con_handle, con->pending, p);
1150 
1151 				con->pending = 0;
1152 			}
1153 
1154 			/* Update buffer descriptor */
1155 			if (con->link_type != NG_HCI_LINK_SCO)
1156 				NG_HCI_BUFF_ACL_FREE(unit->buffer, p);
1157 			else
1158 				NG_HCI_BUFF_SCO_FREE(unit->buffer, p);
1159 		} else
1160 			NG_HCI_ALERT(
1161 "%s: %s - invalid connection handle=%d\n",
1162 				__func__, NG_NODE_NAME(unit->node), h);
1163 	}
1164 
1165 	NG_FREE_M(event);
1166 
1167 	/* Send more data */
1168 	ng_hci_send_data(unit);
1169 
1170 	return (0);
1171 } /* num_compl_pkts */
1172 
1173 /* Mode change event */
1174 static int
1175 mode_change(ng_hci_unit_p unit, struct mbuf *event)
1176 {
1177 	ng_hci_mode_change_ep	*ep = NULL;
1178 	ng_hci_unit_con_p	 con = NULL;
1179 	int			 error = 0;
1180 
1181 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1182 	if (event == NULL)
1183 		return (ENOBUFS);
1184 
1185 	ep = mtod(event, ng_hci_mode_change_ep *);
1186 
1187 	if (ep->status == 0) {
1188 		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1189 
1190 		con = ng_hci_con_by_handle(unit, h);
1191 		if (con == NULL) {
1192 			NG_HCI_ALERT(
1193 "%s: %s - invalid connection handle=%d\n",
1194 				__func__, NG_NODE_NAME(unit->node), h);
1195 			error = ENOENT;
1196 		} else if (con->link_type != NG_HCI_LINK_ACL) {
1197 			NG_HCI_ALERT(
1198 "%s: %s - invalid link type=%d\n",
1199 				__func__, NG_NODE_NAME(unit->node),
1200 				con->link_type);
1201 			error = EINVAL;
1202 		} else
1203 			con->mode = ep->unit_mode;
1204 	} else
1205 		NG_HCI_ERR(
1206 "%s: %s - failed to change mode, status=%d\n",
1207 			__func__, NG_NODE_NAME(unit->node), ep->status);
1208 
1209 	NG_FREE_M(event);
1210 
1211 	return (error);
1212 } /* mode_change */
1213 
1214 /* Data buffer overflow event */
1215 static int
1216 data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event)
1217 {
1218 	NG_HCI_ALERT(
1219 "%s: %s - %s data buffer overflow\n",
1220 		__func__, NG_NODE_NAME(unit->node),
1221 		(*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO");
1222 
1223 	NG_FREE_M(event);
1224 
1225 	return (0);
1226 } /* data_buffer_overflow */
1227 
1228 /* Read clock offset complete event */
1229 static int
1230 read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event)
1231 {
1232 	ng_hci_read_clock_offset_compl_ep	*ep = NULL;
1233 	ng_hci_unit_con_p			 con = NULL;
1234 	ng_hci_neighbor_p			 n = NULL;
1235 	int					 error = 0;
1236 
1237 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1238 	if (event == NULL)
1239 		return (ENOBUFS);
1240 
1241 	ep = mtod(event, ng_hci_read_clock_offset_compl_ep *);
1242 
1243 	if (ep->status == 0) {
1244 		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1245 
1246 		con = ng_hci_con_by_handle(unit, h);
1247 		if (con == NULL) {
1248 			NG_HCI_ALERT(
1249 "%s: %s - invalid connection handle=%d\n",
1250 				__func__, NG_NODE_NAME(unit->node), h);
1251 			error = ENOENT;
1252 			goto out;
1253 		}
1254 
1255 		/* Update cache entry */
1256 		n = ng_hci_get_neighbor(unit, &con->bdaddr, NG_HCI_LINK_ACL);
1257 		if (n == NULL) {
1258 			n = ng_hci_new_neighbor(unit);
1259 			if (n == NULL) {
1260 				error = ENOMEM;
1261 				goto out;
1262 			}
1263 
1264 			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1265 			n->addrtype = NG_HCI_LINK_ACL;
1266 		} else
1267 			getmicrotime(&n->updated);
1268 
1269 		n->clock_offset = le16toh(ep->clock_offset);
1270 	} else
1271 		NG_HCI_ERR(
1272 "%s: %s - failed to Read Remote Clock Offset, status=%d\n",
1273 			__func__, NG_NODE_NAME(unit->node), ep->status);
1274 out:
1275 	NG_FREE_M(event);
1276 
1277 	return (error);
1278 } /* read_clock_offset_compl */
1279 
1280 /* QoS violation event */
1281 static int
1282 qos_violation(ng_hci_unit_p unit, struct mbuf *event)
1283 {
1284 	ng_hci_qos_violation_ep	*ep = NULL;
1285 	ng_hci_unit_con_p	 con = NULL;
1286 	u_int16_t		 h;
1287 	int			 error = 0;
1288 
1289 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1290 	if (event == NULL)
1291 		return (ENOBUFS);
1292 
1293 	ep = mtod(event, ng_hci_qos_violation_ep *);
1294 
1295 	/* Check if we have this connection handle */
1296 	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1297 	con = ng_hci_con_by_handle(unit, h);
1298 	if (con == NULL) {
1299 		NG_HCI_ALERT(
1300 "%s: %s - invalid connection handle=%d\n",
1301 			__func__, NG_NODE_NAME(unit->node), h);
1302 		error = ENOENT;
1303 	} else if (con->link_type != NG_HCI_LINK_ACL) {
1304 		NG_HCI_ALERT(
1305 "%s: %s - invalid link type=%d\n",
1306 			__func__, NG_NODE_NAME(unit->node), con->link_type);
1307 		error = EINVAL;
1308 	} else if (con->state != NG_HCI_CON_OPEN) {
1309 		NG_HCI_ALERT(
1310 "%s: %s - invalid connection state=%d, handle=%d\n",
1311 			__func__, NG_NODE_NAME(unit->node), con->state, h);
1312 		error = EINVAL;
1313 	} else /* Notify upper layer */
1314 		error = ng_hci_lp_qos_ind(con);
1315 
1316 	NG_FREE_M(event);
1317 
1318 	return (error);
1319 } /* qos_violation */
1320 
1321 /* Page scan mode change event */
1322 static int
1323 page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1324 {
1325 	ng_hci_page_scan_mode_change_ep	*ep = NULL;
1326 	ng_hci_neighbor_p		 n = NULL;
1327 	int				 error = 0;
1328 
1329 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1330 	if (event == NULL)
1331 		return (ENOBUFS);
1332 
1333 	ep = mtod(event, ng_hci_page_scan_mode_change_ep *);
1334 
1335 	/* Update cache entry */
1336 	n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
1337 	if (n == NULL) {
1338 		n = ng_hci_new_neighbor(unit);
1339 		if (n == NULL) {
1340 			error = ENOMEM;
1341 			goto out;
1342 		}
1343 
1344 		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1345 		n->addrtype = NG_HCI_LINK_ACL;
1346 	} else
1347 		getmicrotime(&n->updated);
1348 
1349 	n->page_scan_mode = ep->page_scan_mode;
1350 out:
1351 	NG_FREE_M(event);
1352 
1353 	return (error);
1354 } /* page_scan_mode_change */
1355 
1356 /* Page scan repetition mode change event */
1357 static int
1358 page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1359 {
1360 	ng_hci_page_scan_rep_mode_change_ep	*ep = NULL;
1361 	ng_hci_neighbor_p			 n = NULL;
1362 	int					 error = 0;
1363 
1364 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1365 	if (event == NULL)
1366 		return (ENOBUFS);
1367 
1368 	ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *);
1369 
1370 	/* Update cache entry */
1371 	n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
1372 	if (n == NULL) {
1373 		n = ng_hci_new_neighbor(unit);
1374 		if (n == NULL) {
1375 			error = ENOMEM;
1376 			goto out;
1377 		}
1378 
1379 		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1380 		n->addrtype = NG_HCI_LINK_ACL;
1381 	} else
1382 		getmicrotime(&n->updated);
1383 
1384 	n->page_scan_rep_mode = ep->page_scan_rep_mode;
1385 out:
1386 	NG_FREE_M(event);
1387 
1388 	return (error);
1389 } /* page_scan_rep_mode_change */
1390 
1391