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