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: stable/11/sys/netgraph/bluetooth/hci/ng_hci_evnt.c 370056 2021-06-28 04:03:43Z git2svn $
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
61static int inquiry_result             (ng_hci_unit_p, struct mbuf *);
62static int con_compl                  (ng_hci_unit_p, struct mbuf *);
63static int con_req                    (ng_hci_unit_p, struct mbuf *);
64static int discon_compl               (ng_hci_unit_p, struct mbuf *);
65static int encryption_change          (ng_hci_unit_p, struct mbuf *);
66static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *);
67static int qos_setup_compl            (ng_hci_unit_p, struct mbuf *);
68static int hardware_error             (ng_hci_unit_p, struct mbuf *);
69static int role_change                (ng_hci_unit_p, struct mbuf *);
70static int num_compl_pkts             (ng_hci_unit_p, struct mbuf *);
71static int mode_change                (ng_hci_unit_p, struct mbuf *);
72static int data_buffer_overflow       (ng_hci_unit_p, struct mbuf *);
73static int read_clock_offset_compl    (ng_hci_unit_p, struct mbuf *);
74static int qos_violation              (ng_hci_unit_p, struct mbuf *);
75static int page_scan_mode_change      (ng_hci_unit_p, struct mbuf *);
76static int page_scan_rep_mode_change  (ng_hci_unit_p, struct mbuf *);
77static int sync_con_queue             (ng_hci_unit_p, ng_hci_unit_con_p, int);
78static int send_data_packets          (ng_hci_unit_p, int, int);
79static int le_event		      (ng_hci_unit_p, struct mbuf *);
80
81/*
82 * Process HCI event packet
83 */
84
85int
86ng_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
214void
215ng_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
250static int
251send_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
348static int
349sync_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 */
375static int
376le_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#if 0
421		{
422			/*
423			 * TODO: Make these information
424			 * Available from userland.
425			 */
426			u_int8_t length_data;
427
428			char *rssi;
429
430			NG_HCI_M_PULLUP(event, sizeof(u_int8_t));
431			length_data = *mtod(event, u_int8_t *);
432			m_adj(event, sizeof(u_int8_t));
433			/*Advertizement data*/
434			NG_HCI_M_PULLUP(event, length_data);
435			m_adj(event, length_data);
436			NG_HCI_M_PULLUP(event, sizeof(char ));
437			/*Get RSSI*/
438			rssi = mtod(event, char *);
439			m_adj(event, sizeof(u_int8_t));
440		}
441#endif
442	}
443	NG_FREE_M(event);
444
445	return (error);
446} /* inquiry_result */
447
448static int le_connection_complete(ng_hci_unit_p unit, struct mbuf *event)
449{
450	int			 error = 0;
451
452	ng_hci_le_connection_complete_ep	*ep = NULL;
453	ng_hci_unit_con_p	 con = NULL;
454	int link_type;
455	uint8_t uclass[3] = {0,0,0};//dummy uclass
456
457	NG_HCI_M_PULLUP(event, sizeof(*ep));
458	if (event == NULL)
459		return (ENOBUFS);
460
461	ep = mtod(event, ng_hci_le_connection_complete_ep *);
462	link_type = (ep->address_type)? NG_HCI_LINK_LE_RANDOM :
463	  NG_HCI_LINK_LE_PUBLIC;
464	/*
465	 * Find the first connection descriptor that matches the following:
466	 *
467	 * 1) con->link_type == link_type
468	 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE
469	 * 3) con->bdaddr == ep->address
470	 */
471	LIST_FOREACH(con, &unit->con_list, next)
472		if (con->link_type == link_type &&
473		    con->state == NG_HCI_CON_W4_CONN_COMPLETE &&
474		    bcmp(&con->bdaddr, &ep->address, sizeof(bdaddr_t)) == 0)
475			break;
476
477	/*
478	 * Two possible cases:
479	 *
480	 * 1) We have found connection descriptor. That means upper layer has
481	 *    requested this connection via LP_CON_REQ message. In this case
482	 *    connection must have timeout set. If ng_hci_con_untimeout() fails
483	 *    then timeout message already went into node's queue. In this case
484	 *    ignore Connection_Complete event and let timeout deal with it.
485	 *
486	 * 2) We do not have connection descriptor. That means upper layer
487	 *    nas not requested this connection , (less likely) we gave up
488	 *    on this connection (timeout) or as node act as slave role.
489	 *    The most likely scenario is that
490	 *    we have received LE_Create_Connection command
491	 *    from the RAW hook
492	 */
493
494	if (con == NULL) {
495		if (ep->status != 0)
496			goto out;
497
498		con = ng_hci_new_con(unit, link_type);
499		if (con == NULL) {
500			error = ENOMEM;
501			goto out;
502		}
503
504		con->state = NG_HCI_CON_W4_LP_CON_RSP;
505		ng_hci_con_timeout(con);
506
507		bcopy(&ep->address, &con->bdaddr, sizeof(con->bdaddr));
508		error = ng_hci_lp_con_ind(con, uclass);
509		if (error != 0) {
510			ng_hci_con_untimeout(con);
511			ng_hci_free_con(con);
512			goto out;
513		}
514
515	} else if ((error = ng_hci_con_untimeout(con)) != 0)
516			goto out;
517
518	/*
519	 * Update connection descriptor and send notification
520	 * to the upper layers.
521	 */
522
523	con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->handle));
524	con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;
525
526	ng_hci_lp_con_cfm(con, ep->status);
527
528	/* Adjust connection state */
529	if (ep->status != 0)
530		ng_hci_free_con(con);
531	else {
532		con->state = NG_HCI_CON_OPEN;
533
534		/*
535		 * Change link policy for the ACL connections. Enable all
536		 * supported link modes. Enable Role switch as well if
537		 * device supports it.
538		 */
539
540	}
541
542out:
543	NG_FREE_M(event);
544
545	return (error);
546
547}
548
549static int le_connection_update(ng_hci_unit_p unit, struct mbuf *event)
550{
551	int error = 0;
552	/*TBD*/
553
554	NG_FREE_M(event);
555	return error;
556
557}
558static int
559le_event(ng_hci_unit_p unit, struct mbuf *event)
560{
561	int error = 0;
562	ng_hci_le_ep *lep;
563
564	NG_HCI_M_PULLUP(event, sizeof(*lep));
565	if(event ==NULL){
566		return ENOBUFS;
567	}
568	lep = mtod(event, ng_hci_le_ep *);
569	m_adj(event, sizeof(*lep));
570	switch(lep->subevent_code){
571	case NG_HCI_LEEV_CON_COMPL:
572		le_connection_complete(unit, event);
573		break;
574	case NG_HCI_LEEV_ADVREP:
575		le_advertizing_report(unit, event);
576		break;
577	case NG_HCI_LEEV_CON_UPDATE_COMPL:
578		le_connection_update(unit, event);
579		break;
580	case NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL:
581		//TBD
582	  /*FALLTHROUGH*/
583	case NG_HCI_LEEV_LONG_TERM_KEY_REQUEST:
584		//TBD
585	  /*FALLTHROUGH*/
586	default:
587	  	NG_FREE_M(event);
588	}
589	return error;
590}
591
592/* Inquiry result event */
593static int
594inquiry_result(ng_hci_unit_p unit, struct mbuf *event)
595{
596	ng_hci_inquiry_result_ep	*ep = NULL;
597	ng_hci_neighbor_p		 n = NULL;
598	bdaddr_t			 bdaddr;
599	int				 error = 0;
600
601	NG_HCI_M_PULLUP(event, sizeof(*ep));
602	if (event == NULL)
603		return (ENOBUFS);
604
605	ep = mtod(event, ng_hci_inquiry_result_ep *);
606	m_adj(event, sizeof(*ep));
607
608	for (; ep->num_responses > 0; ep->num_responses --) {
609		/* Get remote unit address */
610		m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr);
611		m_adj(event, sizeof(bdaddr));
612
613		/* Lookup entry in the cache */
614		n = ng_hci_get_neighbor(unit, &bdaddr, NG_HCI_LINK_ACL);
615		if (n == NULL) {
616			/* Create new entry */
617			n = ng_hci_new_neighbor(unit);
618			if (n == NULL) {
619				error = ENOMEM;
620				break;
621			}
622		} else
623			getmicrotime(&n->updated);
624
625		bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));
626		n->addrtype = NG_HCI_LINK_ACL;
627
628		/* XXX call m_pullup here? */
629
630		n->page_scan_rep_mode = *mtod(event, u_int8_t *);
631		m_adj(event, sizeof(u_int8_t));
632
633		/* page_scan_period_mode */
634		m_adj(event, sizeof(u_int8_t));
635
636		n->page_scan_mode = *mtod(event, u_int8_t *);
637		m_adj(event, sizeof(u_int8_t));
638
639		/* class */
640		m_adj(event, NG_HCI_CLASS_SIZE);
641
642		/* clock offset */
643		m_copydata(event, 0, sizeof(n->clock_offset),
644			(caddr_t) &n->clock_offset);
645		n->clock_offset = le16toh(n->clock_offset);
646	}
647
648	NG_FREE_M(event);
649
650	return (error);
651} /* inquiry_result */
652
653/* Connection complete event */
654static int
655con_compl(ng_hci_unit_p unit, struct mbuf *event)
656{
657	ng_hci_con_compl_ep	*ep = NULL;
658	ng_hci_unit_con_p	 con = NULL;
659	int			 error = 0;
660
661	NG_HCI_M_PULLUP(event, sizeof(*ep));
662	if (event == NULL)
663		return (ENOBUFS);
664
665	ep = mtod(event, ng_hci_con_compl_ep *);
666
667	/*
668	 * Find the first connection descriptor that matches the following:
669	 *
670	 * 1) con->link_type == ep->link_type
671	 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE
672	 * 3) con->bdaddr == ep->bdaddr
673	 */
674
675	LIST_FOREACH(con, &unit->con_list, next)
676		if (con->link_type == ep->link_type &&
677		    con->state == NG_HCI_CON_W4_CONN_COMPLETE &&
678		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
679			break;
680
681	/*
682	 * Two possible cases:
683	 *
684	 * 1) We have found connection descriptor. That means upper layer has
685	 *    requested this connection via LP_CON_REQ message. In this case
686	 *    connection must have timeout set. If ng_hci_con_untimeout() fails
687	 *    then timeout message already went into node's queue. In this case
688	 *    ignore Connection_Complete event and let timeout deal with it.
689	 *
690	 * 2) We do not have connection descriptor. That means upper layer
691	 *    nas not requested this connection or (less likely) we gave up
692	 *    on this connection (timeout). The most likely scenario is that
693	 *    we have received Create_Connection/Add_SCO_Connection command
694	 *    from the RAW hook
695	 */
696
697	if (con == NULL) {
698		if (ep->status != 0)
699			goto out;
700
701		con = ng_hci_new_con(unit, ep->link_type);
702		if (con == NULL) {
703			error = ENOMEM;
704			goto out;
705		}
706
707		bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
708	} else if ((error = ng_hci_con_untimeout(con)) != 0)
709			goto out;
710
711	/*
712	 * Update connection descriptor and send notification
713	 * to the upper layers.
714	 */
715
716	con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
717	con->encryption_mode = ep->encryption_mode;
718
719	ng_hci_lp_con_cfm(con, ep->status);
720
721	/* Adjust connection state */
722	if (ep->status != 0)
723		ng_hci_free_con(con);
724	else {
725		con->state = NG_HCI_CON_OPEN;
726
727		/*
728		 * Change link policy for the ACL connections. Enable all
729		 * supported link modes. Enable Role switch as well if
730		 * device supports it.
731		 */
732
733		if (ep->link_type == NG_HCI_LINK_ACL) {
734			struct __link_policy {
735				ng_hci_cmd_pkt_t			 hdr;
736				ng_hci_write_link_policy_settings_cp	 cp;
737			} __attribute__ ((packed))			*lp;
738			struct mbuf					*m;
739
740			MGETHDR(m, M_NOWAIT, MT_DATA);
741			if (m != NULL) {
742				m->m_pkthdr.len = m->m_len = sizeof(*lp);
743				lp = mtod(m, struct __link_policy *);
744
745				lp->hdr.type = NG_HCI_CMD_PKT;
746				lp->hdr.opcode = htole16(NG_HCI_OPCODE(
747					NG_HCI_OGF_LINK_POLICY,
748					NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS));
749				lp->hdr.length = sizeof(lp->cp);
750
751				lp->cp.con_handle = ep->con_handle;
752
753				lp->cp.settings = 0;
754				if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
755				    unit->role_switch)
756					lp->cp.settings |= 0x1;
757				if (unit->features[0] & NG_HCI_LMP_HOLD_MODE)
758					lp->cp.settings |= 0x2;
759				if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE)
760					lp->cp.settings |= 0x4;
761				if (unit->features[1] & NG_HCI_LMP_PARK_MODE)
762					lp->cp.settings |= 0x8;
763
764				lp->cp.settings &= unit->link_policy_mask;
765				lp->cp.settings = htole16(lp->cp.settings);
766
767				NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
768				if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
769					ng_hci_send_command(unit);
770			}
771		}
772	}
773out:
774	NG_FREE_M(event);
775
776	return (error);
777} /* con_compl */
778
779/* Connection request event */
780static int
781con_req(ng_hci_unit_p unit, struct mbuf *event)
782{
783	ng_hci_con_req_ep	*ep = NULL;
784	ng_hci_unit_con_p	 con = NULL;
785	int			 error = 0;
786
787	NG_HCI_M_PULLUP(event, sizeof(*ep));
788	if (event == NULL)
789		return (ENOBUFS);
790
791	ep = mtod(event, ng_hci_con_req_ep *);
792
793	/*
794	 * Find the first connection descriptor that matches the following:
795	 *
796	 * 1) con->link_type == ep->link_type
797	 *
798	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
799	 *    con->state == NG_HCI_CON_W4_CONN_COMPL
800	 *
801	 * 3) con->bdaddr == ep->bdaddr
802	 *
803	 * Possible cases:
804	 *
805	 * 1) We do not have connection descriptor. This is simple. Create
806	 *    new fresh connection descriptor and send notification to the
807	 *    appropriate upstream hook (based on link_type).
808	 *
809	 * 2) We found connection handle. This is more complicated.
810	 *
811	 * 2.1) ACL links
812	 *
813	 *      Since only one ACL link can exist between each pair of
814	 *      units then we have a race. Our upper layer has requested
815	 *      an ACL connection to the remote unit, but we did not send
816	 *      command yet. At the same time the remote unit has requested
817	 *      an ACL connection from us. In this case we will ignore
818	 *	Connection_Request event. This probably will cause connect
819	 *      failure	on both units.
820	 *
821	 * 2.2) SCO links
822	 *
823	 *      The spec on page 45 says :
824	 *
825	 *      "The master can support up to three SCO links to the same
826	 *       slave or to different slaves. A slave can support up to
827	 *       three SCO links from the same master, or two SCO links if
828	 *       the links originate from different masters."
829	 *
830	 *      The only problem is how to handle multiple SCO links between
831	 *      matster and slave. For now we will assume that multiple SCO
832	 *      links MUST be opened one after another.
833	 */
834
835	LIST_FOREACH(con, &unit->con_list, next)
836		if (con->link_type == ep->link_type &&
837		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
838		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
839		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
840			break;
841
842	if (con == NULL) {
843		con = ng_hci_new_con(unit, ep->link_type);
844		if (con != NULL) {
845			bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
846
847			con->state = NG_HCI_CON_W4_LP_CON_RSP;
848			ng_hci_con_timeout(con);
849
850			error = ng_hci_lp_con_ind(con, ep->uclass);
851			if (error != 0) {
852				ng_hci_con_untimeout(con);
853				ng_hci_free_con(con);
854			}
855		} else
856			error = ENOMEM;
857	}
858
859	NG_FREE_M(event);
860
861	return (error);
862} /* con_req */
863
864/* Disconnect complete event */
865static int
866discon_compl(ng_hci_unit_p unit, struct mbuf *event)
867{
868	ng_hci_discon_compl_ep	*ep = NULL;
869	ng_hci_unit_con_p	 con = NULL;
870	int			 error = 0;
871	u_int16_t		 h;
872
873	NG_HCI_M_PULLUP(event, sizeof(*ep));
874	if (event == NULL)
875		return (ENOBUFS);
876
877	ep = mtod(event, ng_hci_discon_compl_ep *);
878
879	/*
880	 * XXX
881	 * Do we have to send notification if ep->status != 0?
882	 * For now we will send notification for both ACL and SCO connections
883	 * ONLY if ep->status == 0.
884	 */
885
886	if (ep->status == 0) {
887		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
888		con = ng_hci_con_by_handle(unit, h);
889		if (con != NULL) {
890			error = ng_hci_lp_discon_ind(con, ep->reason);
891
892			/* Remove all timeouts (if any) */
893			if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
894				ng_hci_con_untimeout(con);
895
896			ng_hci_free_con(con);
897		} else {
898			NG_HCI_ALERT(
899"%s: %s - invalid connection handle=%d\n",
900				__func__, NG_NODE_NAME(unit->node), h);
901			error = ENOENT;
902		}
903	}
904
905	NG_FREE_M(event);
906
907	return (error);
908} /* discon_compl */
909
910/* Encryption change event */
911static int
912encryption_change(ng_hci_unit_p unit, struct mbuf *event)
913{
914	ng_hci_encryption_change_ep	*ep = NULL;
915	ng_hci_unit_con_p		 con = NULL;
916	int				 error = 0;
917	u_int16_t	h;
918
919	NG_HCI_M_PULLUP(event, sizeof(*ep));
920	if (event == NULL)
921		return (ENOBUFS);
922
923	ep = mtod(event, ng_hci_encryption_change_ep *);
924	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
925	con = ng_hci_con_by_handle(unit, h);
926
927	if (ep->status == 0) {
928		if (con == NULL) {
929			NG_HCI_ALERT(
930"%s: %s - invalid connection handle=%d\n",
931				__func__, NG_NODE_NAME(unit->node), h);
932			error = ENOENT;
933		} else if (con->link_type == NG_HCI_LINK_SCO) {
934			NG_HCI_ALERT(
935"%s: %s - invalid link type=%d\n",
936				__func__, NG_NODE_NAME(unit->node),
937				con->link_type);
938			error = EINVAL;
939		} else if (ep->encryption_enable)
940			/* XXX is that true? */
941			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_P2P;
942		else
943			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;
944	} else
945		NG_HCI_ERR(
946"%s: %s - failed to change encryption mode, status=%d\n",
947			__func__, NG_NODE_NAME(unit->node), ep->status);
948
949	/*Anyway, propagete encryption status to upper layer*/
950	ng_hci_lp_enc_change(con, con->encryption_mode);
951
952	NG_FREE_M(event);
953
954	return (error);
955} /* encryption_change */
956
957/* Read remote feature complete event */
958static int
959read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event)
960{
961	ng_hci_read_remote_features_compl_ep	*ep = NULL;
962	ng_hci_unit_con_p			 con = NULL;
963	ng_hci_neighbor_p			 n = NULL;
964	u_int16_t				 h;
965	int					 error = 0;
966
967	NG_HCI_M_PULLUP(event, sizeof(*ep));
968	if (event == NULL)
969		return (ENOBUFS);
970
971	ep = mtod(event, ng_hci_read_remote_features_compl_ep *);
972
973	if (ep->status == 0) {
974		/* Check if we have this connection handle */
975		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
976		con = ng_hci_con_by_handle(unit, h);
977		if (con == NULL) {
978			NG_HCI_ALERT(
979"%s: %s - invalid connection handle=%d\n",
980				__func__, NG_NODE_NAME(unit->node), h);
981			error = ENOENT;
982			goto out;
983		}
984
985		/* Update cache entry */
986		n = ng_hci_get_neighbor(unit, &con->bdaddr, NG_HCI_LINK_ACL);
987		if (n == NULL) {
988			n = ng_hci_new_neighbor(unit);
989			if (n == NULL) {
990				error = ENOMEM;
991				goto out;
992			}
993
994			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
995			n->addrtype = NG_HCI_LINK_ACL;
996		} else
997			getmicrotime(&n->updated);
998
999		bcopy(ep->features, n->features, sizeof(n->features));
1000	} else
1001		NG_HCI_ERR(
1002"%s: %s - failed to read remote unit features, status=%d\n",
1003			__func__, NG_NODE_NAME(unit->node), ep->status);
1004out:
1005	NG_FREE_M(event);
1006
1007	return (error);
1008} /* read_remote_features_compl */
1009
1010/* QoS setup complete event */
1011static int
1012qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event)
1013{
1014	ng_hci_qos_setup_compl_ep	*ep = NULL;
1015	ng_hci_unit_con_p		 con = NULL;
1016	u_int16_t			 h;
1017	int				 error = 0;
1018
1019	NG_HCI_M_PULLUP(event, sizeof(*ep));
1020	if (event == NULL)
1021		return (ENOBUFS);
1022
1023	ep = mtod(event, ng_hci_qos_setup_compl_ep *);
1024
1025	/* Check if we have this connection handle */
1026	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1027	con = ng_hci_con_by_handle(unit, h);
1028	if (con == NULL) {
1029		NG_HCI_ALERT(
1030"%s: %s - invalid connection handle=%d\n",
1031			__func__, NG_NODE_NAME(unit->node), h);
1032		error = ENOENT;
1033	} else if (con->link_type != NG_HCI_LINK_ACL) {
1034		NG_HCI_ALERT(
1035"%s: %s - invalid link type=%d, handle=%d\n",
1036			__func__, NG_NODE_NAME(unit->node), con->link_type, h);
1037		error = EINVAL;
1038	} else if (con->state != NG_HCI_CON_OPEN) {
1039		NG_HCI_ALERT(
1040"%s: %s - invalid connection state=%d, handle=%d\n",
1041			__func__, NG_NODE_NAME(unit->node),
1042			con->state, h);
1043		error = EINVAL;
1044	} else /* Notify upper layer */
1045		error = ng_hci_lp_qos_cfm(con, ep->status);
1046
1047	NG_FREE_M(event);
1048
1049	return (error);
1050} /* qos_setup_compl */
1051
1052/* Hardware error event */
1053static int
1054hardware_error(ng_hci_unit_p unit, struct mbuf *event)
1055{
1056	NG_HCI_ALERT(
1057"%s: %s - hardware error %#x\n",
1058		__func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *));
1059
1060	NG_FREE_M(event);
1061
1062	return (0);
1063} /* hardware_error */
1064
1065/* Role change event */
1066static int
1067role_change(ng_hci_unit_p unit, struct mbuf *event)
1068{
1069	ng_hci_role_change_ep	*ep = NULL;
1070	ng_hci_unit_con_p	 con = NULL;
1071
1072	NG_HCI_M_PULLUP(event, sizeof(*ep));
1073	if (event == NULL)
1074		return (ENOBUFS);
1075
1076	ep = mtod(event, ng_hci_role_change_ep *);
1077
1078	if (ep->status == 0) {
1079		/* XXX shoud we also change "role" for SCO connections? */
1080		con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
1081		if (con != NULL)
1082			con->role = ep->role;
1083		else
1084			NG_HCI_ALERT(
1085"%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n",
1086				__func__, NG_NODE_NAME(unit->node),
1087				ep->bdaddr.b[5], ep->bdaddr.b[4],
1088				ep->bdaddr.b[3], ep->bdaddr.b[2],
1089				ep->bdaddr.b[1], ep->bdaddr.b[0]);
1090	} else
1091		NG_HCI_ERR(
1092"%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n",
1093			__func__, NG_NODE_NAME(unit->node), ep->status,
1094			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
1095			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
1096
1097	NG_FREE_M(event);
1098
1099	return (0);
1100} /* role_change */
1101
1102/* Number of completed packets event */
1103static int
1104num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event)
1105{
1106	ng_hci_num_compl_pkts_ep	*ep = NULL;
1107	ng_hci_unit_con_p		 con = NULL;
1108	u_int16_t			 h, p;
1109
1110	NG_HCI_M_PULLUP(event, sizeof(*ep));
1111	if (event == NULL)
1112		return (ENOBUFS);
1113
1114	ep = mtod(event, ng_hci_num_compl_pkts_ep *);
1115	m_adj(event, sizeof(*ep));
1116
1117	for (; ep->num_con_handles > 0; ep->num_con_handles --) {
1118		/* Get connection handle */
1119		m_copydata(event, 0, sizeof(h), (caddr_t) &h);
1120		m_adj(event, sizeof(h));
1121		h = NG_HCI_CON_HANDLE(le16toh(h));
1122
1123		/* Get number of completed packets */
1124		m_copydata(event, 0, sizeof(p), (caddr_t) &p);
1125		m_adj(event, sizeof(p));
1126		p = le16toh(p);
1127
1128		/* Check if we have this connection handle */
1129		con = ng_hci_con_by_handle(unit, h);
1130		if (con != NULL) {
1131			con->pending -= p;
1132			if (con->pending < 0) {
1133				NG_HCI_WARN(
1134"%s: %s - pending packet counter is out of sync! " \
1135"handle=%d, pending=%d, ncp=%d\n",	__func__, NG_NODE_NAME(unit->node),
1136					con->con_handle, con->pending, p);
1137
1138				con->pending = 0;
1139			}
1140
1141			/* Update buffer descriptor */
1142			if (con->link_type != NG_HCI_LINK_SCO)
1143				NG_HCI_BUFF_ACL_FREE(unit->buffer, p);
1144			else
1145				NG_HCI_BUFF_SCO_FREE(unit->buffer, p);
1146		} else
1147			NG_HCI_ALERT(
1148"%s: %s - invalid connection handle=%d\n",
1149				__func__, NG_NODE_NAME(unit->node), h);
1150	}
1151
1152	NG_FREE_M(event);
1153
1154	/* Send more data */
1155	ng_hci_send_data(unit);
1156
1157	return (0);
1158} /* num_compl_pkts */
1159
1160/* Mode change event */
1161static int
1162mode_change(ng_hci_unit_p unit, struct mbuf *event)
1163{
1164	ng_hci_mode_change_ep	*ep = NULL;
1165	ng_hci_unit_con_p	 con = NULL;
1166	int			 error = 0;
1167
1168	NG_HCI_M_PULLUP(event, sizeof(*ep));
1169	if (event == NULL)
1170		return (ENOBUFS);
1171
1172	ep = mtod(event, ng_hci_mode_change_ep *);
1173
1174	if (ep->status == 0) {
1175		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1176
1177		con = ng_hci_con_by_handle(unit, h);
1178		if (con == NULL) {
1179			NG_HCI_ALERT(
1180"%s: %s - invalid connection handle=%d\n",
1181				__func__, NG_NODE_NAME(unit->node), h);
1182			error = ENOENT;
1183		} else if (con->link_type != NG_HCI_LINK_ACL) {
1184			NG_HCI_ALERT(
1185"%s: %s - invalid link type=%d\n",
1186				__func__, NG_NODE_NAME(unit->node),
1187				con->link_type);
1188			error = EINVAL;
1189		} else
1190			con->mode = ep->unit_mode;
1191	} else
1192		NG_HCI_ERR(
1193"%s: %s - failed to change mode, status=%d\n",
1194			__func__, NG_NODE_NAME(unit->node), ep->status);
1195
1196	NG_FREE_M(event);
1197
1198	return (error);
1199} /* mode_change */
1200
1201/* Data buffer overflow event */
1202static int
1203data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event)
1204{
1205	NG_HCI_ALERT(
1206"%s: %s - %s data buffer overflow\n",
1207		__func__, NG_NODE_NAME(unit->node),
1208		(*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO");
1209
1210	NG_FREE_M(event);
1211
1212	return (0);
1213} /* data_buffer_overflow */
1214
1215/* Read clock offset complete event */
1216static int
1217read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event)
1218{
1219	ng_hci_read_clock_offset_compl_ep	*ep = NULL;
1220	ng_hci_unit_con_p			 con = NULL;
1221	ng_hci_neighbor_p			 n = NULL;
1222	int					 error = 0;
1223
1224	NG_HCI_M_PULLUP(event, sizeof(*ep));
1225	if (event == NULL)
1226		return (ENOBUFS);
1227
1228	ep = mtod(event, ng_hci_read_clock_offset_compl_ep *);
1229
1230	if (ep->status == 0) {
1231		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1232
1233		con = ng_hci_con_by_handle(unit, h);
1234		if (con == NULL) {
1235			NG_HCI_ALERT(
1236"%s: %s - invalid connection handle=%d\n",
1237				__func__, NG_NODE_NAME(unit->node), h);
1238			error = ENOENT;
1239			goto out;
1240		}
1241
1242		/* Update cache entry */
1243		n = ng_hci_get_neighbor(unit, &con->bdaddr, NG_HCI_LINK_ACL);
1244		if (n == NULL) {
1245			n = ng_hci_new_neighbor(unit);
1246			if (n == NULL) {
1247				error = ENOMEM;
1248				goto out;
1249			}
1250
1251			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1252			n->addrtype = NG_HCI_LINK_ACL;
1253		} else
1254			getmicrotime(&n->updated);
1255
1256		n->clock_offset = le16toh(ep->clock_offset);
1257	} else
1258		NG_HCI_ERR(
1259"%s: %s - failed to Read Remote Clock Offset, status=%d\n",
1260			__func__, NG_NODE_NAME(unit->node), ep->status);
1261out:
1262	NG_FREE_M(event);
1263
1264	return (error);
1265} /* read_clock_offset_compl */
1266
1267/* QoS violation event */
1268static int
1269qos_violation(ng_hci_unit_p unit, struct mbuf *event)
1270{
1271	ng_hci_qos_violation_ep	*ep = NULL;
1272	ng_hci_unit_con_p	 con = NULL;
1273	u_int16_t		 h;
1274	int			 error = 0;
1275
1276	NG_HCI_M_PULLUP(event, sizeof(*ep));
1277	if (event == NULL)
1278		return (ENOBUFS);
1279
1280	ep = mtod(event, ng_hci_qos_violation_ep *);
1281
1282	/* Check if we have this connection handle */
1283	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1284	con = ng_hci_con_by_handle(unit, h);
1285	if (con == NULL) {
1286		NG_HCI_ALERT(
1287"%s: %s - invalid connection handle=%d\n",
1288			__func__, NG_NODE_NAME(unit->node), h);
1289		error = ENOENT;
1290	} else if (con->link_type != NG_HCI_LINK_ACL) {
1291		NG_HCI_ALERT(
1292"%s: %s - invalid link type=%d\n",
1293			__func__, NG_NODE_NAME(unit->node), con->link_type);
1294		error = EINVAL;
1295	} else if (con->state != NG_HCI_CON_OPEN) {
1296		NG_HCI_ALERT(
1297"%s: %s - invalid connection state=%d, handle=%d\n",
1298			__func__, NG_NODE_NAME(unit->node), con->state, h);
1299		error = EINVAL;
1300	} else /* Notify upper layer */
1301		error = ng_hci_lp_qos_ind(con);
1302
1303	NG_FREE_M(event);
1304
1305	return (error);
1306} /* qos_violation */
1307
1308/* Page scan mode change event */
1309static int
1310page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1311{
1312	ng_hci_page_scan_mode_change_ep	*ep = NULL;
1313	ng_hci_neighbor_p		 n = NULL;
1314	int				 error = 0;
1315
1316	NG_HCI_M_PULLUP(event, sizeof(*ep));
1317	if (event == NULL)
1318		return (ENOBUFS);
1319
1320	ep = mtod(event, ng_hci_page_scan_mode_change_ep *);
1321
1322	/* Update cache entry */
1323	n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
1324	if (n == NULL) {
1325		n = ng_hci_new_neighbor(unit);
1326		if (n == NULL) {
1327			error = ENOMEM;
1328			goto out;
1329		}
1330
1331		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1332		n->addrtype = NG_HCI_LINK_ACL;
1333	} else
1334		getmicrotime(&n->updated);
1335
1336	n->page_scan_mode = ep->page_scan_mode;
1337out:
1338	NG_FREE_M(event);
1339
1340	return (error);
1341} /* page_scan_mode_change */
1342
1343/* Page scan repetition mode change event */
1344static int
1345page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1346{
1347	ng_hci_page_scan_rep_mode_change_ep	*ep = NULL;
1348	ng_hci_neighbor_p			 n = NULL;
1349	int					 error = 0;
1350
1351	NG_HCI_M_PULLUP(event, sizeof(*ep));
1352	if (event == NULL)
1353		return (ENOBUFS);
1354
1355	ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *);
1356
1357	/* Update cache entry */
1358	n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
1359	if (n == NULL) {
1360		n = ng_hci_new_neighbor(unit);
1361		if (n == NULL) {
1362			error = ENOMEM;
1363			goto out;
1364		}
1365
1366		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1367		n->addrtype = NG_HCI_LINK_ACL;
1368	} else
1369		getmicrotime(&n->updated);
1370
1371	n->page_scan_rep_mode = ep->page_scan_rep_mode;
1372out:
1373	NG_FREE_M(event);
1374
1375	return (error);
1376} /* page_scan_rep_mode_change */
1377
1378