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