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