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