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