xref: /freebsd/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
1 /*
2  * ng_hci_ulpi.c
3  *
4  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $Id: ng_hci_ulpi.c,v 1.14 2002/11/12 22:35:40 max Exp $
29  * $FreeBSD$
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/endian.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/queue.h>
39 #include <netgraph/ng_message.h>
40 #include <netgraph/netgraph.h>
41 #include "ng_bluetooth.h"
42 #include "ng_hci.h"
43 #include "ng_hci_var.h"
44 #include "ng_hci_cmds.h"
45 #include "ng_hci_evnt.h"
46 #include "ng_hci_ulpi.h"
47 #include "ng_hci_misc.h"
48 
49 /******************************************************************************
50  ******************************************************************************
51  **                 Upper Layer Protocol Interface module
52  ******************************************************************************
53  ******************************************************************************/
54 
55 static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
56 static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
57 
58 /*
59  * Process LP_ConnectReq event from the upper layer protocol
60  */
61 
62 int
63 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
64 {
65 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
66 		NG_HCI_WARN(
67 "%s: %s - unit is not ready, state=%#x\n",
68 			__func__, NG_NODE_NAME(unit->node), unit->state);
69 
70 		NG_FREE_ITEM(item);
71 
72 		return (ENXIO);
73 	}
74 
75 	if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
76 		NG_HCI_ALERT(
77 "%s: %s - invalid LP_ConnectReq message size=%d\n",
78 			__func__, NG_NODE_NAME(unit->node),
79 			NGI_MSG(item)->header.arglen);
80 
81 		NG_FREE_ITEM(item);
82 
83 		return (EMSGSIZE);
84 	}
85 
86 	if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL)
87 		return (ng_hci_lp_acl_con_req(unit, item, hook));
88 
89 	if (hook != unit->sco) {
90 		NG_HCI_WARN(
91 "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
92 			__func__, NG_NODE_NAME(unit->node), hook);
93 
94 		NG_FREE_ITEM(item);
95 
96 		return (EINVAL);
97 	}
98 
99 	return (ng_hci_lp_sco_con_req(unit, item, hook));
100 } /* ng_hci_lp_con_req */
101 
102 /*
103  * Request to create new ACL connection
104  */
105 
106 static int
107 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
108 {
109 	struct acl_con_req {
110 		ng_hci_cmd_pkt_t	 hdr;
111 		ng_hci_create_con_cp	 cp;
112 	} __attribute__ ((packed))	*req = NULL;
113 	ng_hci_lp_con_req_ep		*ep = NULL;
114 	ng_hci_unit_con_p		 con = NULL;
115 	ng_hci_neighbor_t		*n = NULL;
116 	struct mbuf			*m = NULL;
117 	int				 error = 0;
118 
119 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
120 
121 	/*
122 	 * Only one ACL connection can exist between each pair of units.
123 	 * So try to find ACL connection descriptor (in any state) that
124 	 * has requested remote BD_ADDR.
125 	 *
126 	 * Two cases:
127 	 *
128 	 * 1) We do not have connection to the remote unit. This is simple.
129 	 *    Just create new connection descriptor and send HCI command to
130 	 *    create new connection.
131 	 *
132 	 * 2) We do have connection descriptor. We need to check connection
133 	 *    state:
134 	 *
135 	 * 2.1) NG_HCI_CON_CLOSED mean we are in the process of closing
136 	 *      connection to the remote unit. We will reject connection
137 	 *      request until connection is closed.
138 	 *
139 	 * 2.2) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
140 	 *      accepting connection from the remote unit. This is a race
141 	 *      condition. We will ignore this message.
142 	 *
143 	 * 2.3) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
144 	 *      requested connection or we just accepted it. In any case
145 	 *      all we need to do here is set appropriate notification bit
146 	 *      and wait.
147 	 *
148 	 * 2.4) NG_HCI_CON_OPEN means connection is open. Just reply back
149 	 *      and let upper layer know that we have connection already.
150 	 */
151 
152 	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
153 	if (con != NULL) {
154 		switch (con->state) {
155 		case NG_HCI_CON_CLOSED:
156 			error = EBUSY;
157 			break;
158 
159 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
160 			error = EALREADY;
161 			break;
162 
163 		case NG_HCI_CON_W4_CONN_COMPLETE:
164 			if (hook == unit->acl)
165 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
166 			else
167 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
168 			break;
169 
170 		case NG_HCI_CON_OPEN: {
171 			struct ng_mesg		*msg = NULL;
172 			ng_hci_lp_con_cfm_ep	*cfm = NULL;
173 
174 			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
175 				NGI_GET_MSG(item, msg);
176 				NG_FREE_MSG(msg);
177 
178 				NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
179 					NGM_HCI_LP_CON_CFM, sizeof(*cfm),
180 					M_NOWAIT);
181 				if (msg != NULL) {
182 					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
183 					cfm->status = 0;
184 					cfm->link_type = con->link_type;
185 					cfm->con_handle = con->con_handle;
186 					bcopy(&con->bdaddr, &cfm->bdaddr,
187 						sizeof(cfm->bdaddr));
188 
189 					/*
190 					 * This will forward item back to
191 					 * sender and set item to NULL
192 					 */
193 
194 					_NGI_MSG(item) = msg;
195 					NG_FWD_ITEM_HOOK(error, item, hook);
196 				} else
197 					error = ENOMEM;
198 			} else
199 				NG_HCI_INFO(
200 "%s: %s - Source hook is not valid, hook=%p\n",
201 					__func__, NG_NODE_NAME(unit->node),
202 					hook);
203 			} break;
204 
205 		default:
206 			KASSERT(0,
207 ("%s: %s - Invalid connection state=%d\n",
208 				__func__, NG_NODE_NAME(unit->node),con->state));
209 
210 			error = EINVAL;
211 			break;
212 		}
213 
214 		goto out;
215 	}
216 
217 	/*
218 	 * If we got here then we need to create new ACL connection descriptor
219 	 * and submit HCI command. First create new connection desriptor, set
220 	 * bdaddr and notification flags.
221 	 */
222 
223 	con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
224 	if (con == NULL) {
225 		error = ENOMEM;
226 		goto out;
227 	}
228 
229 	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
230 
231 	/*
232 	 * Create HCI command
233 	 */
234 
235 	MGETHDR(m, M_DONTWAIT, MT_DATA);
236 	if (m == NULL) {
237 		ng_hci_free_con(con);
238 		error = ENOBUFS;
239 		goto out;
240 	}
241 
242 	m->m_pkthdr.len = m->m_len = sizeof(*req);
243 	req = mtod(m, struct acl_con_req *);
244 	req->hdr.type = NG_HCI_CMD_PKT;
245 	req->hdr.length = sizeof(req->cp);
246 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
247 					NG_HCI_OCF_CREATE_CON));
248 
249 	bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
250 
251 	req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
252 	if (unit->features[0] & NG_HCI_LMP_3SLOT)
253 		req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
254 	if (unit->features[0] & NG_HCI_LMP_5SLOT)
255 		req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
256 
257 	req->cp.pkt_type &= unit->packet_mask;
258 	if (req->cp.pkt_type == 0)
259 		req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
260 
261 	req->cp.pkt_type = htole16(req->cp.pkt_type);
262 
263 	if (unit->features[0] & NG_HCI_LMP_SWITCH)
264 		req->cp.accept_role_switch = 1;
265 	else
266 		req->cp.accept_role_switch = 0;
267 
268 	/*
269 	 * We may speed up connect by specifying valid parameters.
270 	 * So check the neighbor cache.
271 	 */
272 
273 	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
274 	if (n == NULL) {
275 		req->cp.page_scan_rep_mode = 0;
276 		req->cp.page_scan_mode = 0;
277 		req->cp.clock_offset = 0;
278 	} else {
279 		req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
280 		req->cp.page_scan_mode = n->page_scan_mode;
281 		req->cp.clock_offset = htole16(n->clock_offset);
282 	}
283 
284 	/*
285 	 * Adust connection state
286 	 */
287 
288 	if (hook == unit->acl)
289 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
290 	else
291 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
292 
293 	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
294 	ng_hci_con_timeout(con);
295 
296 	/*
297 	 * Queue and send HCI command
298 	 */
299 
300 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
301 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
302 		error = ng_hci_send_command(unit);
303 out:
304 	if (item != NULL)
305 		NG_FREE_ITEM(item);
306 
307 	return (error);
308 } /* ng_hci_lp_acl_con_req */
309 
310 /*
311  * Request to create new SCO connection
312  */
313 
314 static int
315 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
316 {
317 	struct sco_con_req {
318 		ng_hci_cmd_pkt_t	 hdr;
319 		ng_hci_add_sco_con_cp	 cp;
320 	} __attribute__ ((packed))	*req = NULL;
321 	ng_hci_lp_con_req_ep		*ep = NULL;
322 	ng_hci_unit_con_p		 acl_con = NULL, sco_con = NULL;
323 	struct mbuf			*m = NULL;
324 	int				 error = 0;
325 
326 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
327 
328 	/*
329 	 * SCO connection without ACL link
330 	 *
331 	 * If upper layer requests SCO connection and there is no open ACL
332 	 * connection to the desired remote unit, we will reject the request.
333 	 */
334 
335 	LIST_FOREACH(acl_con, &unit->con_list, next)
336 		if (acl_con->link_type == NG_HCI_LINK_ACL &&
337 		    acl_con->state == NG_HCI_CON_OPEN &&
338 		    bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
339 			break;
340 
341 	if (acl_con == NULL) {
342 		NG_HCI_INFO(
343 "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
344 			__func__, NG_NODE_NAME(unit->node),
345 			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
346 			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
347 
348 		error = ENOENT;
349 		goto out;
350 	}
351 
352 	/*
353 	 * Multiple SCO connections can exist between the same pair of units.
354 	 * We assume that multiple SCO connections have to be opened one after
355 	 * another.
356 	 *
357 	 * Try to find SCO connection descriptor that matches the following:
358 	 *
359 	 * 1) sco_con->link_type == NG_HCI_LINK_SCO
360 	 *
361 	 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
362 	 *    sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
363 	 *
364 	 * 3) sco_con->bdaddr == ep->bdaddr
365 	 *
366 	 * Two cases:
367 	 *
368 	 * 1) We do not have connection descriptor. This is simple. Just
369 	 *    create new connection and submit Add_SCO_Connection command.
370 	 *
371 	 * 2) We do have connection descriptor. We need to check the state.
372 	 *
373 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
374 	 *      connection from the remote unit. This is a race condition and
375 	 *      we will ignore the request.
376 	 *
377 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
378 	 *      connection or we just accepted it.
379 	 *
380 	 * XXX FIXME what to do with connection(s) in CLOSED state?
381 	 */
382 
383 	LIST_FOREACH(sco_con, &unit->con_list, next)
384 		if (sco_con->link_type == NG_HCI_LINK_SCO &&
385 		    (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
386 		     sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
387 		    bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
388 			break;
389 
390 	if (sco_con != NULL) {
391 		switch (sco_con->state) {
392 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
393 			error = EALREADY;
394 			break;
395 
396 		case NG_HCI_CON_W4_CONN_COMPLETE:
397 			sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
398 			break;
399 
400 		default:
401 			KASSERT(0,
402 ("%s: %s - Inavalid connection state=%d\n",
403 				__func__, NG_NODE_NAME(unit->node),
404 				sco_con->state));
405 
406 			error = EINVAL;
407 			break;
408 		}
409 
410 		goto out;
411 	}
412 
413 	/*
414 	 * If we got here then we need to create new SCO connection descriptor
415 	 * and submit HCI command.
416 	 */
417 
418 	sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
419 	if (sco_con == NULL) {
420 		error = ENOMEM;
421 		goto out;
422 	}
423 
424 	bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
425 
426 	/*
427 	 * Create HCI command
428 	 */
429 
430 	MGETHDR(m, M_DONTWAIT, MT_DATA);
431 	if (m == NULL) {
432 		ng_hci_free_con(sco_con);
433 		error = ENOBUFS;
434 		goto out;
435 	}
436 
437 	m->m_pkthdr.len = m->m_len = sizeof(*req);
438 	req = mtod(m, struct sco_con_req *);
439 	req->hdr.type = NG_HCI_CMD_PKT;
440 	req->hdr.length = sizeof(req->cp);
441 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
442 					NG_HCI_OCF_ADD_SCO_CON));
443 
444 	req->cp.con_handle = htole16(acl_con->con_handle);
445 
446 	req->cp.pkt_type = NG_HCI_PKT_HV1;
447 	if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
448 		req->cp.pkt_type |= NG_HCI_PKT_HV2;
449 	if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
450 		req->cp.pkt_type |= NG_HCI_PKT_HV3;
451 
452 	req->cp.pkt_type &= unit->packet_mask;
453 	if (req->cp.pkt_type == 0)
454 		req->cp.pkt_type = NG_HCI_PKT_HV1;
455 
456 	req->cp.pkt_type = htole16(req->cp.pkt_type);
457 
458 	/*
459 	 * Adust connection state
460 	 */
461 
462 	sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
463 
464 	sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
465 	ng_hci_con_timeout(sco_con);
466 
467 	/*
468 	 * Queue and send HCI command
469 	 */
470 
471 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
472 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
473 		error = ng_hci_send_command(unit);
474 out:
475 	NG_FREE_ITEM(item);
476 
477 	return (error);
478 } /* ng_hci_lp_sco_con_req */
479 
480 /*
481  * Process LP_DisconnectReq event from the upper layer protocol
482  *
483  * XXX XXX XXX
484  *
485  * NOTE: This is NOT defined by Bluetooth specification (why?) But i think
486  * this might be useful (at least for testing), so please do not depend on
487  * this interface.
488  */
489 
490 int
491 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
492 {
493 	struct discon_req {
494 		ng_hci_cmd_pkt_t	 hdr;
495 		ng_hci_discon_cp	 cp;
496 	} __attribute__ ((packed))	*req = NULL;
497 	ng_hci_lp_discon_req_ep		*ep = NULL;
498 	ng_hci_unit_con_p		 con = NULL;
499 	struct mbuf			*m = NULL;
500 	int				 error = 0;
501 
502 	/* Check if unit is ready */
503 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
504 		NG_HCI_WARN(
505 "%s: %s - unit is not ready, state=%#x\n",
506 			__func__, NG_NODE_NAME(unit->node), unit->state);
507 
508 		error = ENXIO;
509 		goto out;
510 	}
511 
512 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
513 		NG_HCI_ALERT(
514 "%s: %s - invalid LP_DisconnectReq message size=%d\n",
515 			__func__, NG_NODE_NAME(unit->node),
516 			NGI_MSG(item)->header.arglen);
517 
518 		error = EMSGSIZE;
519 		goto out;
520 	}
521 
522 	ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
523 
524 	con = ng_hci_con_by_handle(unit, ep->con_handle);
525 	if (con == NULL) {
526 		NG_HCI_ERR(
527 "%s: %s - invalid connection handle=%d\n",
528 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
529 
530 		error = ENOENT;
531 		goto out;
532 	}
533 
534 	if (con->state != NG_HCI_CON_OPEN) {
535 		NG_HCI_ERR(
536 "%s: %s - invalid connection state=%d, handle=%d\n",
537 			__func__, NG_NODE_NAME(unit->node), con->state,
538 			ep->con_handle);
539 
540 		error = EINVAL;
541 		goto out;
542 	}
543 
544 	/*
545 	 * Create HCI command
546 	 */
547 
548 	MGETHDR(m, M_DONTWAIT, MT_DATA);
549 	if (m == NULL) {
550 		error = ENOBUFS;
551 		goto out;
552 	}
553 
554 	m->m_pkthdr.len = m->m_len = sizeof(*req);
555 	req = mtod(m, struct discon_req *);
556 	req->hdr.type = NG_HCI_CMD_PKT;
557 	req->hdr.length = sizeof(req->cp);
558 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
559 							NG_HCI_OCF_DISCON));
560 
561 	req->cp.con_handle = htole16(ep->con_handle);
562 	req->cp.reason = ep->reason;
563 
564 	/*
565 	 * Adjust connection state
566 	 */
567 
568 	con->state = NG_HCI_CON_CLOSED;
569 	ng_hci_con_timeout(con);
570 
571 	/*
572 	 * Queue and send HCI command
573 	 */
574 
575 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
576 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
577 		error = ng_hci_send_command(unit);
578 out:
579 	NG_FREE_ITEM(item);
580 
581 	return (error);
582 } /* ng_hci_lp_discon_req */
583 
584 /*
585  * Send LP_ConnectCfm event to the upper layer protocol
586  */
587 
588 int
589 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
590 {
591 	ng_hci_unit_p		 unit = con->unit;
592 	struct ng_mesg		*msg = NULL;
593 	ng_hci_lp_con_cfm_ep	*ep = NULL;
594 	int			 error;
595 
596 	/*
597 	 * Check who wants to be notified. For ACL links both ACL and SCO
598 	 * upstream hooks will be notified (if required). For SCO links
599 	 * only SCO upstream hook will receive notification
600 	 */
601 
602 	if (con->link_type == NG_HCI_LINK_ACL &&
603 	    con->flags & NG_HCI_CON_NOTIFY_ACL) {
604 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
605 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
606 				sizeof(*ep), M_NOWAIT);
607 			if (msg != NULL) {
608 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
609 				ep->status = status;
610 				ep->link_type = con->link_type;
611 				ep->con_handle = con->con_handle;
612 				bcopy(&con->bdaddr, &ep->bdaddr,
613 					sizeof(ep->bdaddr));
614 
615 				NG_SEND_MSG_HOOK(error, unit->node, msg,
616 					unit->acl, NULL);
617 			}
618 		} else
619 			NG_HCI_INFO(
620 "%s: %s - ACL hook not valid, hook=%p\n",
621 				__func__, NG_NODE_NAME(unit->node), unit->acl);
622 
623 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
624 	}
625 
626 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
627 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
628 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
629 				sizeof(*ep), M_NOWAIT);
630 			if (msg != NULL) {
631 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
632 				ep->status = status;
633 				ep->link_type = con->link_type;
634 				ep->con_handle = con->con_handle;
635 				bcopy(&con->bdaddr, &ep->bdaddr,
636 					sizeof(ep->bdaddr));
637 
638 				NG_SEND_MSG_HOOK(error, unit->node, msg,
639 					unit->sco, NULL);
640 			}
641 		} else
642 			NG_HCI_INFO(
643 "%s: %s - SCO hook not valid, hook=%p\n",
644 				__func__, NG_NODE_NAME(unit->node), unit->acl);
645 
646 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
647 	}
648 
649 	return (0);
650 } /* ng_hci_lp_con_cfm */
651 
652 /*
653  * Send LP_ConnectInd event to the upper layer protocol
654  */
655 
656 int
657 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
658 {
659 	ng_hci_unit_p		 unit = con->unit;
660 	struct ng_mesg		*msg = NULL;
661 	ng_hci_lp_con_ind_ep	*ep = NULL;
662 	hook_p			 hook = NULL;
663 	int			 error = 0;
664 
665 	/*
666 	 * Connection_Request event is generated for specific link type.
667 	 * Use link_type to select upstream hook.
668 	 */
669 
670 	if (con->link_type == NG_HCI_LINK_ACL)
671 		hook = unit->acl;
672 	else
673 		hook = unit->sco;
674 
675 	if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
676 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
677 			sizeof(*ep), M_NOWAIT);
678 		if (msg == NULL)
679 			return (ENOMEM);
680 
681 		ep = (ng_hci_lp_con_ind_ep *)(msg->data);
682 		ep->link_type = con->link_type;
683 		bcopy(uclass, ep->uclass, sizeof(ep->uclass));
684 		bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
685 
686 		NG_SEND_MSG_HOOK(error, unit->node, msg, hook, NULL);
687 	} else {
688 		NG_HCI_WARN(
689 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
690 			__func__, NG_NODE_NAME(unit->node), hook);
691 
692 		error = ENOTCONN;
693 	}
694 
695 	return (error);
696 } /* ng_hci_lp_con_ind */
697 
698 /*
699  * Process LP_ConnectRsp event from the upper layer protocol
700  */
701 
702 int
703 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
704 {
705 	struct con_rsp_req {
706 		ng_hci_cmd_pkt_t		 hdr;
707 		union {
708 			ng_hci_accept_con_cp	 acc;
709 			ng_hci_reject_con_cp	 rej;
710 		} __attribute__ ((packed))	 cp;
711 	} __attribute__ ((packed))		*req = NULL;
712 	ng_hci_lp_con_rsp_ep			*ep = NULL;
713 	ng_hci_unit_con_p			 con = NULL;
714 	struct mbuf				*m = NULL;
715 	int					 error = 0;
716 
717 	/* Check if unit is ready */
718 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
719 		NG_HCI_WARN(
720 "%s: %s - unit is not ready, state=%#x\n",
721 			__func__, NG_NODE_NAME(unit->node), unit->state);
722 
723 		error = ENXIO;
724 		goto out;
725 	}
726 
727 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
728 		NG_HCI_ALERT(
729 "%s: %s - invalid LP_ConnectRsp message size=%d\n",
730 			__func__, NG_NODE_NAME(unit->node),
731 			NGI_MSG(item)->header.arglen);
732 
733 		error = EMSGSIZE;
734 		goto out;
735 	}
736 
737 	ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
738 
739 	/*
740 	 * Here we have to deal with race. Upper layers might send conflicting
741 	 * requests. One might send Accept and other Reject. We will not try
742 	 * to solve all the problems, so first request will always win.
743 	 *
744 	 * Try to find connection that matches the following:
745 	 *
746 	 * 1) con->link_type == ep->link_type
747 	 *
748 	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
749 	 *    con->state == NG_HCI_CON_W4_CONN_COMPLETE
750 	 *
751 	 * 3) con->bdaddr == ep->bdaddr
752 	 *
753 	 * Two cases:
754 	 *
755 	 * 1) We do not have connection descriptor. Could be bogus request or
756 	 *    we have rejected connection already.
757 	 *
758 	 * 2) We do have connection descriptor. Then we need to check state:
759 	 *
760 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
761 	 *      connection and it is a first response from the upper layer.
762 	 *      if "status == 0" (Accept) then we will send Accept_Connection
763 	 *      command and change connection state to W4_CONN_COMPLETE, else
764 	 *      send reject and delete connection.
765 	 *
766 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
767 	 *      connection. If "status == 0" we just need to link request
768 	 *      and wait, else ignore Reject request.
769 	 */
770 
771 	LIST_FOREACH(con, &unit->con_list, next)
772 		if (con->link_type == ep->link_type &&
773 		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
774 		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
775 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
776 			break;
777 
778 	if (con == NULL) {
779 		/* Reject for non-existing connection is fine */
780 		error = (ep->status == 0)? ENOENT : 0;
781 		goto out;
782 	}
783 
784 	/*
785 	 * Remove connection timeout and check connection state
786 	 */
787 
788 	ng_hci_con_untimeout(con);
789 
790 	switch (con->state) {
791 	case NG_HCI_CON_W4_LP_CON_RSP:
792 
793 		/*
794 		 * Create HCI command
795 		 */
796 
797 		MGETHDR(m, M_DONTWAIT, MT_DATA);
798 		if (m == NULL) {
799 			error = ENOBUFS;
800 			goto out;
801 		}
802 
803 		req = mtod(m, struct con_rsp_req *);
804 		req->hdr.type = NG_HCI_CMD_PKT;
805 
806 		if (ep->status == 0) {
807 			req->hdr.length = sizeof(req->cp.acc);
808 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
809 							NG_HCI_OGF_LINK_CONTROL,
810 							NG_HCI_OCF_ACCEPT_CON));
811 
812 			bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
813 				sizeof(req->cp.acc.bdaddr));
814 
815 			/*
816 			 * XXX should be configurable?
817 			 *
818 			 * We are accepting connection, so if we support role
819 			 * switch then set role to NG_HCI_ROLE_MASTER and let
820 			 * LM peform role switch. Otherwise it is probably
821 			 * makes sense to remain slave. In this case LM WILL
822 			 * NOT perform role switch.
823 			 */
824 
825 			if (unit->features[0] & NG_HCI_LMP_SWITCH)
826 				req->cp.acc.role = NG_HCI_ROLE_MASTER;
827 			else
828 				req->cp.acc.role = NG_HCI_ROLE_SLAVE;
829 
830 			/*
831 			 * Adjust connection state
832 			 */
833 
834 			if (hook == unit->acl)
835 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
836 			else
837 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
838 
839 			con->state = NG_HCI_CON_W4_CONN_COMPLETE;
840 			ng_hci_con_timeout(con);
841 		} else {
842 			req->hdr.length = sizeof(req->cp.rej);
843 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
844 							NG_HCI_OGF_LINK_CONTROL,
845 							NG_HCI_OCF_REJECT_CON));
846 
847 			bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
848 				sizeof(req->cp.rej.bdaddr));
849 
850 			req->cp.rej.reason = ep->status;
851 
852 			/*
853 			 * Free connection descritor
854 			 * Item will be deleted just before return.
855 			 */
856 
857 			ng_hci_free_con(con);
858 		}
859 
860 		m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
861 
862 		/* Queue and send HCI command */
863 		NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
864 		if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
865 			error = ng_hci_send_command(unit);
866 		break;
867 
868 	case NG_HCI_CON_W4_CONN_COMPLETE:
869 		if (ep->status == 0) {
870 			if (hook == unit->acl)
871 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
872 			else
873 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
874 		} else
875 			error = EPERM;
876 		break;
877 
878 	default:
879 		KASSERT(0,
880 ("%s: %s - Invalid connection state=%d\n",
881 			__func__, NG_NODE_NAME(unit->node), con->state));
882 
883 		error = EINVAL;
884 		break;
885 	}
886 out:
887 	NG_FREE_ITEM(item);
888 
889 	return (error);
890 } /* ng_hci_lp_con_rsp */
891 
892 /*
893  * Send LP_DisconnectInd to the upper layer protocol
894  */
895 
896 int
897 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
898 {
899 	ng_hci_unit_p		 unit = con->unit;
900 	struct ng_mesg		*msg = NULL;
901 	ng_hci_lp_discon_ind_ep	*ep = NULL;
902 	int			 error = 0;
903 
904 	/*
905 	 * Disconnect_Complete event is generated for specific connection
906 	 * handle. For ACL connection handles both ACL and SCO upstream
907 	 * hooks will receive notification. For SCO connection handles
908 	 * only SCO upstream hook will receive notification.
909 	 */
910 
911 	if (con->link_type == NG_HCI_LINK_ACL) {
912 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
913 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
914 				NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
915 			if (msg == NULL)
916 				return (ENOMEM);
917 
918 			ep = (ng_hci_lp_discon_ind_ep *) msg->data;
919 			ep->reason = reason;
920 			ep->link_type = con->link_type;
921 			ep->con_handle = con->con_handle;
922 
923 			NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,NULL);
924 		} else
925 			NG_HCI_INFO(
926 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
927 				__func__, NG_NODE_NAME(unit->node), unit->acl);
928 	}
929 
930 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
931 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
932 			sizeof(*ep), M_NOWAIT);
933 		if (msg == NULL)
934 			return (ENOMEM);
935 
936 		ep = (ng_hci_lp_discon_ind_ep *) msg->data;
937 		ep->reason = reason;
938 		ep->link_type = con->link_type;
939 		ep->con_handle = con->con_handle;
940 
941 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL);
942 	} else
943 		NG_HCI_INFO(
944 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
945 			__func__, NG_NODE_NAME(unit->node), unit->sco);
946 
947 	return (0);
948 } /* ng_hci_lp_discon_ind */
949 
950 /*
951  * Process LP_QoSReq action from the upper layer protocol
952  */
953 
954 int
955 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
956 {
957 	struct qos_setup_req {
958 		ng_hci_cmd_pkt_t	 hdr;
959 		ng_hci_qos_setup_cp	 cp;
960 	} __attribute__ ((packed))	*req = NULL;
961 	ng_hci_lp_qos_req_ep		*ep = NULL;
962 	ng_hci_unit_con_p		 con = NULL;
963 	struct mbuf			*m = NULL;
964 	int				 error = 0;
965 
966 	/* Check if unit is ready */
967 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
968 		NG_HCI_WARN(
969 "%s: %s - unit is not ready, state=%#x\n",
970 			__func__, NG_NODE_NAME(unit->node), unit->state);
971 
972 		error = ENXIO;
973 		goto out;
974 	}
975 
976 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
977 		NG_HCI_ALERT(
978 "%s: %s - invalid LP_QoSSetupReq message size=%d\n",
979 			__func__, NG_NODE_NAME(unit->node),
980 			NGI_MSG(item)->header.arglen);
981 
982 		error = EMSGSIZE;
983 		goto out;
984 	}
985 
986 	ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
987 
988 	con = ng_hci_con_by_handle(unit, ep->con_handle);
989 	if (con == NULL) {
990 		NG_HCI_ERR(
991 "%s: %s - invalid connection handle=%d\n",
992 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
993 
994 		error = EINVAL;
995 		goto out;
996 	}
997 
998 	if (con->link_type != NG_HCI_LINK_ACL) {
999 		NG_HCI_ERR("%s: %s - invalid link type=%d\n",
1000 			__func__, NG_NODE_NAME(unit->node), con->link_type);
1001 
1002 		error = EINVAL;
1003 		goto out;
1004 	}
1005 
1006 	if (con->state != NG_HCI_CON_OPEN) {
1007 		NG_HCI_ERR(
1008 "%s: %s - invalid connection state=%d, handle=%d\n",
1009 			__func__, NG_NODE_NAME(unit->node), con->state,
1010 			con->con_handle);
1011 
1012 		error = EINVAL;
1013 		goto out;
1014 	}
1015 
1016 	/*
1017 	 * Create HCI command
1018 	 */
1019 
1020 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1021 	if (m == NULL) {
1022 		error = ENOBUFS;
1023 		goto out;
1024 	}
1025 
1026 	m->m_pkthdr.len = m->m_len = sizeof(*req);
1027 	req = mtod(m, struct qos_setup_req *);
1028 	req->hdr.type = NG_HCI_CMD_PKT;
1029 	req->hdr.length = sizeof(req->cp);
1030 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1031 			NG_HCI_OCF_QOS_SETUP));
1032 
1033 	req->cp.con_handle = htole16(ep->con_handle);
1034 	req->cp.flags = ep->flags;
1035 	req->cp.service_type = ep->service_type;
1036 	req->cp.token_rate = htole32(ep->token_rate);
1037 	req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1038 	req->cp.latency = htole32(ep->latency);
1039 	req->cp.delay_variation = htole32(ep->delay_variation);
1040 
1041 	/*
1042 	 * Adjust connection state
1043  	 */
1044 
1045 	if (hook == unit->acl)
1046 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
1047 	else
1048 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
1049 
1050 	/*
1051 	 * Queue and send HCI command
1052 	 */
1053 
1054 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1055 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1056 		error = ng_hci_send_command(unit);
1057 out:
1058 	NG_FREE_ITEM(item);
1059 
1060 	return (error);
1061 } /* ng_hci_lp_qos_req */
1062 
1063 /*
1064  * Send LP_QoSCfm event to the upper layer protocol
1065  */
1066 
1067 int
1068 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1069 {
1070 	ng_hci_unit_p		 unit = con->unit;
1071 	struct ng_mesg		*msg = NULL;
1072 	ng_hci_lp_qos_cfm_ep	*ep = NULL;
1073 	int			 error;
1074 
1075 	if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1076 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1077 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1078 				sizeof(*ep), M_NOWAIT);
1079 			if (msg != NULL) {
1080 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1081 				ep->status = status;
1082 				ep->con_handle = con->con_handle;
1083 
1084 				NG_SEND_MSG_HOOK(error, unit->node, msg,
1085 					unit->acl, NULL);
1086 			}
1087 		} else
1088 			NG_HCI_INFO(
1089 "%s: %s - ACL hook not valid, hook=%p\n",
1090 				__func__, NG_NODE_NAME(unit->node), unit->acl);
1091 
1092 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1093 	}
1094 
1095 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1096 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1097 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1098 				sizeof(*ep), M_NOWAIT);
1099 			if (msg != NULL) {
1100 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1101 				ep->status = status;
1102 				ep->con_handle = con->con_handle;
1103 
1104 				NG_SEND_MSG_HOOK(error, unit->node, msg,
1105 					unit->sco, NULL);
1106 			}
1107 		} else
1108 			NG_HCI_INFO(
1109 "%s: %s - SCO hook not valid, hook=%p\n",
1110 				 __func__, NG_NODE_NAME(unit->node), unit->sco);
1111 
1112 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1113 	}
1114 
1115 	return (0);
1116 } /* ng_hci_lp_qos_cfm */
1117 
1118 /*
1119  * Send LP_QoSViolationInd event to the upper layer protocol
1120  */
1121 
1122 int
1123 ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1124 {
1125 	ng_hci_unit_p		 unit = con->unit;
1126 	struct ng_mesg		*msg = NULL;
1127 	ng_hci_lp_qos_ind_ep	*ep = NULL;
1128 	int			 error;
1129 
1130 	/*
1131 	 * QoS Violation can only be generated for ACL connection handles.
1132 	 * Both ACL and SCO upstream hooks will receive notification.
1133 	 */
1134 
1135 	if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1136 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1137 			sizeof(*ep), M_NOWAIT);
1138 		if (msg == NULL)
1139 			return (ENOMEM);
1140 
1141 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1142 		ep->con_handle = con->con_handle;
1143 
1144 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, NULL);
1145 	} else
1146 		NG_HCI_INFO(
1147 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1148 			__func__, NG_NODE_NAME(unit->node), unit->acl);
1149 
1150 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1151 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1152 			sizeof(*ep), M_NOWAIT);
1153 		if (msg == NULL)
1154 			return (ENOMEM);
1155 
1156 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1157 		ep->con_handle = con->con_handle;
1158 
1159 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL);
1160 	} else
1161 		NG_HCI_INFO(
1162 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1163 			__func__, NG_NODE_NAME(unit->node), unit->sco);
1164 
1165 	return (0);
1166 } /* ng_hci_lp_qos_ind */
1167 
1168 /*
1169  * Process connection timeout
1170  */
1171 
1172 void
1173 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1174 {
1175 	ng_hci_unit_con_p	con = (ng_hci_unit_con_p) arg1;
1176 
1177 	KASSERT((con->flags & NG_HCI_CON_TIMEOUT_PENDING),
1178 ("%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(node)));
1179 
1180 	con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1181 
1182 	/*
1183 	 * We expect to receive connection timeout in one of the following
1184 	 * states:
1185 	 *
1186 	 * 1) NG_HCI_CON_CLOSED means that upper layer has requested disconnect
1187 	 *    via LP_DISCON_REQ and we have not received Disconnect_Complete
1188 	 *    event. In this case we will send LP_DISCON_IND to upper layer.
1189 	 *
1190 	 * 2) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1191 	 *    to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1192 	 *    most likely already gave up on us.
1193 	 *
1194 	 * 3) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1195 	 *    (or we in the process of accepting it) and baseband has timedout
1196 	 *    on us. Inform upper layers and send LP_CON_CFM.
1197 	 */
1198 
1199 	switch (con->state) {
1200 	case NG_HCI_CON_CLOSED:
1201 		ng_hci_lp_discon_ind(con, 0x16);
1202 		break;
1203 
1204 	case NG_HCI_CON_W4_LP_CON_RSP:
1205 		break;
1206 
1207 	case NG_HCI_CON_W4_CONN_COMPLETE:
1208 		ng_hci_lp_con_cfm(con, 0xee);
1209 		break;
1210 
1211 	default:
1212 		KASSERT(0,
1213 ("%s: %s - Invalid connection state=%d\n",
1214 			__func__, NG_NODE_NAME(node), con->state));
1215 		break;
1216 	}
1217 
1218 	ng_hci_free_con(con);
1219 } /* ng_hci_process_con_timeout */
1220 
1221 /*
1222  * Process connection watchdog timeout
1223  */
1224 
1225 void
1226 ng_hci_process_con_watchdog_timeout(node_p node, hook_p hook,
1227 		void *arg1, int arg2)
1228 {
1229 	ng_hci_unit_con_p		 con = (ng_hci_unit_con_p) arg1;
1230 	struct discon_req {
1231 		ng_hci_cmd_pkt_t	 hdr;
1232 		ng_hci_discon_cp	 cp;
1233 	} __attribute__ ((packed))	*req = NULL;
1234 	struct mbuf			*m = NULL;
1235 
1236 	KASSERT((con->state == NG_HCI_CON_OPEN),
1237 ("%s: %s - invalid connection state=%d, handle=%d\n",
1238 		__func__, NG_NODE_NAME(node), con->state, con->con_handle));
1239 
1240 	KASSERT((con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING),
1241 ("%s: %s - No connection watchdog timeout!\n",
1242 		__func__, NG_NODE_NAME(node)));
1243 
1244 	con->flags &= ~NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING;
1245 
1246 	/*
1247 	 * Create HCI command
1248 	 */
1249 
1250 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1251 	if (m == NULL)
1252 		return; /* XXX this is bad */
1253 
1254 	m->m_pkthdr.len = m->m_len = sizeof(*req);
1255 	req = mtod(m, struct discon_req *);
1256 	req->hdr.type = NG_HCI_CMD_PKT;
1257 	req->hdr.length = sizeof(req->cp);
1258 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
1259 							NG_HCI_OCF_DISCON));
1260 
1261 	req->cp.con_handle = htole16(con->con_handle);
1262 	req->cp.reason = 0x13; /* User ended connection */
1263 
1264 	/*
1265 	 * Queue and send HCI command
1266 	 */
1267 
1268 	NG_BT_MBUFQ_ENQUEUE(&con->unit->cmdq, m);
1269 	if (!(con->unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1270 		ng_hci_send_command(con->unit);
1271 
1272 	/*
1273 	 * Send LP_DISCON_IND to the upper layers
1274 	 * Connection terminated by local host
1275 	 */
1276 
1277 	ng_hci_lp_discon_ind(con, 0x16);
1278 	ng_hci_free_con(con);
1279 } /* ng_hci_process_con_watchdog_timeout */
1280 
1281