xref: /freebsd/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c (revision 77a44875e59b239c696cca17b4cc946105ade612)
1878ed226SJulian Elischer /*
2878ed226SJulian Elischer  * ng_hci_ulpi.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
6fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7fe267a55SPedro F. Giffuni  *
8878ed226SJulian Elischer  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
9878ed226SJulian Elischer  * All rights reserved.
10878ed226SJulian Elischer  *
11878ed226SJulian Elischer  * Redistribution and use in source and binary forms, with or without
12878ed226SJulian Elischer  * modification, are permitted provided that the following conditions
13878ed226SJulian Elischer  * are met:
14878ed226SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
15878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
16878ed226SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
17878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
18878ed226SJulian Elischer  *    documentation and/or other materials provided with the distribution.
19878ed226SJulian Elischer  *
20878ed226SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21878ed226SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22878ed226SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23878ed226SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24878ed226SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25878ed226SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26878ed226SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27878ed226SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28878ed226SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29878ed226SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30878ed226SJulian Elischer  * SUCH DAMAGE.
31878ed226SJulian Elischer  *
320986ab12SMaksim Yevmenkin  * $Id: ng_hci_ulpi.c,v 1.7 2003/09/08 18:57:51 max Exp $
33878ed226SJulian Elischer  * $FreeBSD$
34878ed226SJulian Elischer  */
35878ed226SJulian Elischer 
36878ed226SJulian Elischer #include <sys/param.h>
37878ed226SJulian Elischer #include <sys/systm.h>
38878ed226SJulian Elischer #include <sys/kernel.h>
39878ed226SJulian Elischer #include <sys/endian.h>
40878ed226SJulian Elischer #include <sys/malloc.h>
41878ed226SJulian Elischer #include <sys/mbuf.h>
42878ed226SJulian Elischer #include <sys/queue.h>
43878ed226SJulian Elischer #include <netgraph/ng_message.h>
44878ed226SJulian Elischer #include <netgraph/netgraph.h>
45b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h>
46b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h>
47b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_var.h>
48b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_cmds.h>
49b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_evnt.h>
50b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_ulpi.h>
51b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_misc.h>
52878ed226SJulian Elischer 
53878ed226SJulian Elischer /******************************************************************************
54878ed226SJulian Elischer  ******************************************************************************
55878ed226SJulian Elischer  **                 Upper Layer Protocol Interface module
56878ed226SJulian Elischer  ******************************************************************************
57878ed226SJulian Elischer  ******************************************************************************/
58878ed226SJulian Elischer 
59878ed226SJulian Elischer static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
60878ed226SJulian Elischer static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
61fbc48c2bSTakanori Watanabe static int ng_hci_lp_le_con_req (ng_hci_unit_p, item_p, hook_p, int);
62878ed226SJulian Elischer 
63878ed226SJulian Elischer /*
64878ed226SJulian Elischer  * Process LP_ConnectReq event from the upper layer protocol
65878ed226SJulian Elischer  */
66878ed226SJulian Elischer 
67878ed226SJulian Elischer int
68878ed226SJulian Elischer ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
69878ed226SJulian Elischer {
70fbc48c2bSTakanori Watanabe 	int link_type;
71fbc48c2bSTakanori Watanabe 
72878ed226SJulian Elischer 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
73878ed226SJulian Elischer 		NG_HCI_WARN(
74878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n",
75878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->state);
76878ed226SJulian Elischer 
77878ed226SJulian Elischer 		NG_FREE_ITEM(item);
78878ed226SJulian Elischer 
79878ed226SJulian Elischer 		return (ENXIO);
80878ed226SJulian Elischer 	}
81878ed226SJulian Elischer 
82878ed226SJulian Elischer 	if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
83878ed226SJulian Elischer 		NG_HCI_ALERT(
84878ed226SJulian Elischer "%s: %s - invalid LP_ConnectReq message size=%d\n",
85878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
86878ed226SJulian Elischer 			NGI_MSG(item)->header.arglen);
87878ed226SJulian Elischer 
88878ed226SJulian Elischer 		NG_FREE_ITEM(item);
89878ed226SJulian Elischer 
90878ed226SJulian Elischer 		return (EMSGSIZE);
91878ed226SJulian Elischer 	}
92fbc48c2bSTakanori Watanabe 	link_type = ((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type;
93fbc48c2bSTakanori Watanabe 	switch(link_type){
94fbc48c2bSTakanori Watanabe 	case NG_HCI_LINK_ACL:
95878ed226SJulian Elischer 		return (ng_hci_lp_acl_con_req(unit, item, hook));
96fbc48c2bSTakanori Watanabe 	case NG_HCI_LINK_SCO:
97878ed226SJulian Elischer 		if (hook != unit->sco ) {
98878ed226SJulian Elischer 			NG_HCI_WARN(
99878ed226SJulian Elischer 				"%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
100878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), hook);
101878ed226SJulian Elischer 
102878ed226SJulian Elischer 			NG_FREE_ITEM(item);
103878ed226SJulian Elischer 
104878ed226SJulian Elischer 			return (EINVAL);
105878ed226SJulian Elischer 		}
106878ed226SJulian Elischer 
107878ed226SJulian Elischer 		return (ng_hci_lp_sco_con_req(unit, item, hook));
108fbc48c2bSTakanori Watanabe 	case NG_HCI_LINK_LE_PUBLIC:
109fbc48c2bSTakanori Watanabe 	case NG_HCI_LINK_LE_RANDOM:
110fbc48c2bSTakanori Watanabe 		return (ng_hci_lp_le_con_req(unit, item, hook, link_type));
111fbc48c2bSTakanori Watanabe 	default:
112fbc48c2bSTakanori Watanabe 		panic("%s: link_type invalid.", __func__);
113fbc48c2bSTakanori Watanabe 	}
114fbc48c2bSTakanori Watanabe 
115fbc48c2bSTakanori Watanabe 	return (EINVAL);
116878ed226SJulian Elischer } /* ng_hci_lp_con_req */
117878ed226SJulian Elischer 
118878ed226SJulian Elischer /*
119878ed226SJulian Elischer  * Request to create new ACL connection
120878ed226SJulian Elischer  */
121878ed226SJulian Elischer 
122878ed226SJulian Elischer static int
123878ed226SJulian Elischer ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
124878ed226SJulian Elischer {
125878ed226SJulian Elischer 	struct acl_con_req {
126878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
127878ed226SJulian Elischer 		ng_hci_create_con_cp	 cp;
128878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
129878ed226SJulian Elischer 	ng_hci_lp_con_req_ep		*ep = NULL;
130878ed226SJulian Elischer 	ng_hci_unit_con_p		 con = NULL;
131878ed226SJulian Elischer 	ng_hci_neighbor_t		*n = NULL;
132878ed226SJulian Elischer 	struct mbuf			*m = NULL;
133878ed226SJulian Elischer 	int				 error = 0;
134878ed226SJulian Elischer 
135878ed226SJulian Elischer 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
136878ed226SJulian Elischer 
137878ed226SJulian Elischer 	/*
138878ed226SJulian Elischer 	 * Only one ACL connection can exist between each pair of units.
139878ed226SJulian Elischer 	 * So try to find ACL connection descriptor (in any state) that
140878ed226SJulian Elischer 	 * has requested remote BD_ADDR.
141878ed226SJulian Elischer 	 *
142878ed226SJulian Elischer 	 * Two cases:
143878ed226SJulian Elischer 	 *
144878ed226SJulian Elischer 	 * 1) We do not have connection to the remote unit. This is simple.
145878ed226SJulian Elischer 	 *    Just create new connection descriptor and send HCI command to
146878ed226SJulian Elischer 	 *    create new connection.
147878ed226SJulian Elischer 	 *
148878ed226SJulian Elischer 	 * 2) We do have connection descriptor. We need to check connection
149878ed226SJulian Elischer 	 *    state:
150878ed226SJulian Elischer 	 *
151f2bb1caeSJulian Elischer 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
152878ed226SJulian Elischer 	 *      accepting connection from the remote unit. This is a race
153878ed226SJulian Elischer 	 *      condition. We will ignore this message.
154878ed226SJulian Elischer 	 *
155f2bb1caeSJulian Elischer 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
156878ed226SJulian Elischer 	 *      requested connection or we just accepted it. In any case
157878ed226SJulian Elischer 	 *      all we need to do here is set appropriate notification bit
158878ed226SJulian Elischer 	 *      and wait.
159878ed226SJulian Elischer 	 *
160f2bb1caeSJulian Elischer 	 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
161878ed226SJulian Elischer 	 *      and let upper layer know that we have connection already.
162878ed226SJulian Elischer 	 */
163878ed226SJulian Elischer 
164878ed226SJulian Elischer 	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
165878ed226SJulian Elischer 	if (con != NULL) {
166878ed226SJulian Elischer 		switch (con->state) {
167878ed226SJulian Elischer 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
168878ed226SJulian Elischer 			error = EALREADY;
169878ed226SJulian Elischer 			break;
170878ed226SJulian Elischer 
171878ed226SJulian Elischer 		case NG_HCI_CON_W4_CONN_COMPLETE:
172878ed226SJulian Elischer 			if (hook == unit->acl)
173878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
174878ed226SJulian Elischer 			else
175878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
176878ed226SJulian Elischer 			break;
177878ed226SJulian Elischer 
178878ed226SJulian Elischer 		case NG_HCI_CON_OPEN: {
179878ed226SJulian Elischer 			struct ng_mesg		*msg = NULL;
180878ed226SJulian Elischer 			ng_hci_lp_con_cfm_ep	*cfm = NULL;
181878ed226SJulian Elischer 
182878ed226SJulian Elischer 			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
183878ed226SJulian Elischer 				NGI_GET_MSG(item, msg);
184878ed226SJulian Elischer 				NG_FREE_MSG(msg);
185878ed226SJulian Elischer 
186878ed226SJulian Elischer 				NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
187878ed226SJulian Elischer 					NGM_HCI_LP_CON_CFM, sizeof(*cfm),
188878ed226SJulian Elischer 					M_NOWAIT);
189878ed226SJulian Elischer 				if (msg != NULL) {
190878ed226SJulian Elischer 					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
191878ed226SJulian Elischer 					cfm->status = 0;
192878ed226SJulian Elischer 					cfm->link_type = con->link_type;
193878ed226SJulian Elischer 					cfm->con_handle = con->con_handle;
194878ed226SJulian Elischer 					bcopy(&con->bdaddr, &cfm->bdaddr,
195878ed226SJulian Elischer 						sizeof(cfm->bdaddr));
196878ed226SJulian Elischer 
197878ed226SJulian Elischer 					/*
198878ed226SJulian Elischer 					 * This will forward item back to
199878ed226SJulian Elischer 					 * sender and set item to NULL
200878ed226SJulian Elischer 					 */
201878ed226SJulian Elischer 
202878ed226SJulian Elischer 					_NGI_MSG(item) = msg;
203878ed226SJulian Elischer 					NG_FWD_ITEM_HOOK(error, item, hook);
204878ed226SJulian Elischer 				} else
205878ed226SJulian Elischer 					error = ENOMEM;
206878ed226SJulian Elischer 			} else
207878ed226SJulian Elischer 				NG_HCI_INFO(
208878ed226SJulian Elischer "%s: %s - Source hook is not valid, hook=%p\n",
209878ed226SJulian Elischer 					__func__, NG_NODE_NAME(unit->node),
210878ed226SJulian Elischer 					hook);
211878ed226SJulian Elischer 			} break;
212878ed226SJulian Elischer 
213878ed226SJulian Elischer 		default:
2140986ab12SMaksim Yevmenkin 			panic(
2150986ab12SMaksim Yevmenkin "%s: %s - Invalid connection state=%d\n",
2160986ab12SMaksim Yevmenkin 				__func__, NG_NODE_NAME(unit->node), con->state);
217878ed226SJulian Elischer 			break;
218878ed226SJulian Elischer 		}
219878ed226SJulian Elischer 
220878ed226SJulian Elischer 		goto out;
221878ed226SJulian Elischer 	}
222878ed226SJulian Elischer 
223878ed226SJulian Elischer 	/*
224878ed226SJulian Elischer 	 * If we got here then we need to create new ACL connection descriptor
225878ed226SJulian Elischer 	 * and submit HCI command. First create new connection desriptor, set
226878ed226SJulian Elischer 	 * bdaddr and notification flags.
227878ed226SJulian Elischer 	 */
228878ed226SJulian Elischer 
229878ed226SJulian Elischer 	con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
230878ed226SJulian Elischer 	if (con == NULL) {
231878ed226SJulian Elischer 		error = ENOMEM;
232878ed226SJulian Elischer 		goto out;
233878ed226SJulian Elischer 	}
234878ed226SJulian Elischer 
235878ed226SJulian Elischer 	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
236878ed226SJulian Elischer 
237878ed226SJulian Elischer 	/*
238878ed226SJulian Elischer 	 * Create HCI command
239878ed226SJulian Elischer 	 */
240878ed226SJulian Elischer 
241eb1b1807SGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
242878ed226SJulian Elischer 	if (m == NULL) {
243878ed226SJulian Elischer 		ng_hci_free_con(con);
244878ed226SJulian Elischer 		error = ENOBUFS;
245878ed226SJulian Elischer 		goto out;
246878ed226SJulian Elischer 	}
247878ed226SJulian Elischer 
248878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
249878ed226SJulian Elischer 	req = mtod(m, struct acl_con_req *);
250878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
251878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
252878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
253878ed226SJulian Elischer 					NG_HCI_OCF_CREATE_CON));
254878ed226SJulian Elischer 
255878ed226SJulian Elischer 	bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
256878ed226SJulian Elischer 
257878ed226SJulian Elischer 	req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
258878ed226SJulian Elischer 	if (unit->features[0] & NG_HCI_LMP_3SLOT)
259878ed226SJulian Elischer 		req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
260878ed226SJulian Elischer 	if (unit->features[0] & NG_HCI_LMP_5SLOT)
261878ed226SJulian Elischer 		req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
262878ed226SJulian Elischer 
263878ed226SJulian Elischer 	req->cp.pkt_type &= unit->packet_mask;
264f2bb1caeSJulian Elischer 	if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|
265f2bb1caeSJulian Elischer 				 NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|
266f2bb1caeSJulian Elischer 				 NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)
267878ed226SJulian Elischer 		req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
268878ed226SJulian Elischer 
269878ed226SJulian Elischer 	req->cp.pkt_type = htole16(req->cp.pkt_type);
270878ed226SJulian Elischer 
271f2bb1caeSJulian Elischer 	if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)
272878ed226SJulian Elischer 		req->cp.accept_role_switch = 1;
273878ed226SJulian Elischer 	else
274878ed226SJulian Elischer 		req->cp.accept_role_switch = 0;
275878ed226SJulian Elischer 
276878ed226SJulian Elischer 	/*
277878ed226SJulian Elischer 	 * We may speed up connect by specifying valid parameters.
278878ed226SJulian Elischer 	 * So check the neighbor cache.
279878ed226SJulian Elischer 	 */
280878ed226SJulian Elischer 
281fbc48c2bSTakanori Watanabe 	n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
282878ed226SJulian Elischer 	if (n == NULL) {
283878ed226SJulian Elischer 		req->cp.page_scan_rep_mode = 0;
284878ed226SJulian Elischer 		req->cp.page_scan_mode = 0;
285878ed226SJulian Elischer 		req->cp.clock_offset = 0;
286878ed226SJulian Elischer 	} else {
287878ed226SJulian Elischer 		req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
288878ed226SJulian Elischer 		req->cp.page_scan_mode = n->page_scan_mode;
289878ed226SJulian Elischer 		req->cp.clock_offset = htole16(n->clock_offset);
290878ed226SJulian Elischer 	}
291878ed226SJulian Elischer 
292878ed226SJulian Elischer 	/*
293878ed226SJulian Elischer 	 * Adust connection state
294878ed226SJulian Elischer 	 */
295878ed226SJulian Elischer 
296878ed226SJulian Elischer 	if (hook == unit->acl)
297878ed226SJulian Elischer 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
298878ed226SJulian Elischer 	else
299878ed226SJulian Elischer 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
300878ed226SJulian Elischer 
301878ed226SJulian Elischer 	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
302878ed226SJulian Elischer 	ng_hci_con_timeout(con);
303878ed226SJulian Elischer 
304878ed226SJulian Elischer 	/*
305878ed226SJulian Elischer 	 * Queue and send HCI command
306878ed226SJulian Elischer 	 */
307878ed226SJulian Elischer 
308878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
309878ed226SJulian Elischer 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
310878ed226SJulian Elischer 		error = ng_hci_send_command(unit);
311878ed226SJulian Elischer out:
312878ed226SJulian Elischer 	if (item != NULL)
313878ed226SJulian Elischer 		NG_FREE_ITEM(item);
314878ed226SJulian Elischer 
315878ed226SJulian Elischer 	return (error);
316878ed226SJulian Elischer } /* ng_hci_lp_acl_con_req */
317878ed226SJulian Elischer 
318878ed226SJulian Elischer /*
319878ed226SJulian Elischer  * Request to create new SCO connection
320878ed226SJulian Elischer  */
321878ed226SJulian Elischer 
322878ed226SJulian Elischer static int
323878ed226SJulian Elischer ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
324878ed226SJulian Elischer {
325878ed226SJulian Elischer 	struct sco_con_req {
326878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
327878ed226SJulian Elischer 		ng_hci_add_sco_con_cp	 cp;
328878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
329878ed226SJulian Elischer 	ng_hci_lp_con_req_ep		*ep = NULL;
330878ed226SJulian Elischer 	ng_hci_unit_con_p		 acl_con = NULL, sco_con = NULL;
331878ed226SJulian Elischer 	struct mbuf			*m = NULL;
332878ed226SJulian Elischer 	int				 error = 0;
333878ed226SJulian Elischer 
334878ed226SJulian Elischer 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
335878ed226SJulian Elischer 
336878ed226SJulian Elischer 	/*
337878ed226SJulian Elischer 	 * SCO connection without ACL link
338878ed226SJulian Elischer 	 *
339878ed226SJulian Elischer 	 * If upper layer requests SCO connection and there is no open ACL
340878ed226SJulian Elischer 	 * connection to the desired remote unit, we will reject the request.
341878ed226SJulian Elischer 	 */
342878ed226SJulian Elischer 
343878ed226SJulian Elischer 	LIST_FOREACH(acl_con, &unit->con_list, next)
344878ed226SJulian Elischer 		if (acl_con->link_type == NG_HCI_LINK_ACL &&
345878ed226SJulian Elischer 		    acl_con->state == NG_HCI_CON_OPEN &&
346878ed226SJulian Elischer 		    bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
347878ed226SJulian Elischer 			break;
348878ed226SJulian Elischer 
349878ed226SJulian Elischer 	if (acl_con == NULL) {
350878ed226SJulian Elischer 		NG_HCI_INFO(
351878ed226SJulian Elischer "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
352878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
353878ed226SJulian Elischer 			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
354878ed226SJulian Elischer 			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
355878ed226SJulian Elischer 
356878ed226SJulian Elischer 		error = ENOENT;
357878ed226SJulian Elischer 		goto out;
358878ed226SJulian Elischer 	}
359878ed226SJulian Elischer 
360878ed226SJulian Elischer 	/*
361878ed226SJulian Elischer 	 * Multiple SCO connections can exist between the same pair of units.
362878ed226SJulian Elischer 	 * We assume that multiple SCO connections have to be opened one after
363878ed226SJulian Elischer 	 * another.
364878ed226SJulian Elischer 	 *
365878ed226SJulian Elischer 	 * Try to find SCO connection descriptor that matches the following:
366878ed226SJulian Elischer 	 *
367878ed226SJulian Elischer 	 * 1) sco_con->link_type == NG_HCI_LINK_SCO
368878ed226SJulian Elischer 	 *
369878ed226SJulian Elischer 	 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
370878ed226SJulian Elischer 	 *    sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
371878ed226SJulian Elischer 	 *
372878ed226SJulian Elischer 	 * 3) sco_con->bdaddr == ep->bdaddr
373878ed226SJulian Elischer 	 *
374878ed226SJulian Elischer 	 * Two cases:
375878ed226SJulian Elischer 	 *
376878ed226SJulian Elischer 	 * 1) We do not have connection descriptor. This is simple. Just
377878ed226SJulian Elischer 	 *    create new connection and submit Add_SCO_Connection command.
378878ed226SJulian Elischer 	 *
379878ed226SJulian Elischer 	 * 2) We do have connection descriptor. We need to check the state.
380878ed226SJulian Elischer 	 *
381878ed226SJulian Elischer 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
382878ed226SJulian Elischer 	 *      connection from the remote unit. This is a race condition and
383878ed226SJulian Elischer 	 *      we will ignore the request.
384878ed226SJulian Elischer 	 *
385878ed226SJulian Elischer 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
386878ed226SJulian Elischer 	 *      connection or we just accepted it.
387878ed226SJulian Elischer 	 */
388878ed226SJulian Elischer 
389878ed226SJulian Elischer 	LIST_FOREACH(sco_con, &unit->con_list, next)
390878ed226SJulian Elischer 		if (sco_con->link_type == NG_HCI_LINK_SCO &&
391878ed226SJulian Elischer 		    (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
392878ed226SJulian Elischer 		     sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
393878ed226SJulian Elischer 		    bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
394878ed226SJulian Elischer 			break;
395878ed226SJulian Elischer 
396878ed226SJulian Elischer 	if (sco_con != NULL) {
397878ed226SJulian Elischer 		switch (sco_con->state) {
398878ed226SJulian Elischer 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
399878ed226SJulian Elischer 			error = EALREADY;
400878ed226SJulian Elischer 			break;
401878ed226SJulian Elischer 
402878ed226SJulian Elischer 		case NG_HCI_CON_W4_CONN_COMPLETE:
403878ed226SJulian Elischer 			sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
404878ed226SJulian Elischer 			break;
405878ed226SJulian Elischer 
406878ed226SJulian Elischer 		default:
4070986ab12SMaksim Yevmenkin 			panic(
408a164074fSEitan Adler "%s: %s - Invalid connection state=%d\n",
409878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node),
4100986ab12SMaksim Yevmenkin 				sco_con->state);
411878ed226SJulian Elischer 			break;
412878ed226SJulian Elischer 		}
413878ed226SJulian Elischer 
414878ed226SJulian Elischer 		goto out;
415878ed226SJulian Elischer 	}
416878ed226SJulian Elischer 
417878ed226SJulian Elischer 	/*
418878ed226SJulian Elischer 	 * If we got here then we need to create new SCO connection descriptor
419878ed226SJulian Elischer 	 * and submit HCI command.
420878ed226SJulian Elischer 	 */
421878ed226SJulian Elischer 
422878ed226SJulian Elischer 	sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
423878ed226SJulian Elischer 	if (sco_con == NULL) {
424878ed226SJulian Elischer 		error = ENOMEM;
425878ed226SJulian Elischer 		goto out;
426878ed226SJulian Elischer 	}
427878ed226SJulian Elischer 
428878ed226SJulian Elischer 	bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
429878ed226SJulian Elischer 
430878ed226SJulian Elischer 	/*
431878ed226SJulian Elischer 	 * Create HCI command
432878ed226SJulian Elischer 	 */
433878ed226SJulian Elischer 
434eb1b1807SGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
435878ed226SJulian Elischer 	if (m == NULL) {
436878ed226SJulian Elischer 		ng_hci_free_con(sco_con);
437878ed226SJulian Elischer 		error = ENOBUFS;
438878ed226SJulian Elischer 		goto out;
439878ed226SJulian Elischer 	}
440878ed226SJulian Elischer 
441878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
442878ed226SJulian Elischer 	req = mtod(m, struct sco_con_req *);
443878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
444878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
445878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
446878ed226SJulian Elischer 					NG_HCI_OCF_ADD_SCO_CON));
447878ed226SJulian Elischer 
448878ed226SJulian Elischer 	req->cp.con_handle = htole16(acl_con->con_handle);
449878ed226SJulian Elischer 
450878ed226SJulian Elischer 	req->cp.pkt_type = NG_HCI_PKT_HV1;
451878ed226SJulian Elischer 	if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
452878ed226SJulian Elischer 		req->cp.pkt_type |= NG_HCI_PKT_HV2;
453878ed226SJulian Elischer 	if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
454878ed226SJulian Elischer 		req->cp.pkt_type |= NG_HCI_PKT_HV3;
455878ed226SJulian Elischer 
456878ed226SJulian Elischer 	req->cp.pkt_type &= unit->packet_mask;
457f2bb1caeSJulian Elischer 	if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|
458f2bb1caeSJulian Elischer 				 NG_HCI_PKT_HV2|
459f2bb1caeSJulian Elischer 				 NG_HCI_PKT_HV3)) == 0)
460878ed226SJulian Elischer 		req->cp.pkt_type = NG_HCI_PKT_HV1;
461878ed226SJulian Elischer 
462878ed226SJulian Elischer 	req->cp.pkt_type = htole16(req->cp.pkt_type);
463878ed226SJulian Elischer 
464878ed226SJulian Elischer 	/*
465878ed226SJulian Elischer 	 * Adust connection state
466878ed226SJulian Elischer 	 */
467878ed226SJulian Elischer 
468878ed226SJulian Elischer 	sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
469878ed226SJulian Elischer 
470878ed226SJulian Elischer 	sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
471878ed226SJulian Elischer 	ng_hci_con_timeout(sco_con);
472878ed226SJulian Elischer 
473878ed226SJulian Elischer 	/*
474878ed226SJulian Elischer 	 * Queue and send HCI command
475878ed226SJulian Elischer 	 */
476878ed226SJulian Elischer 
477878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
478878ed226SJulian Elischer 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
479878ed226SJulian Elischer 		error = ng_hci_send_command(unit);
480878ed226SJulian Elischer out:
481878ed226SJulian Elischer 	NG_FREE_ITEM(item);
482878ed226SJulian Elischer 
483878ed226SJulian Elischer 	return (error);
484878ed226SJulian Elischer } /* ng_hci_lp_sco_con_req */
485878ed226SJulian Elischer 
486fbc48c2bSTakanori Watanabe static int
487fbc48c2bSTakanori Watanabe ng_hci_lp_le_con_req(ng_hci_unit_p unit, item_p item, hook_p hook, int link_type)
488fbc48c2bSTakanori Watanabe {
489fbc48c2bSTakanori Watanabe 	struct acl_con_req {
490fbc48c2bSTakanori Watanabe 		ng_hci_cmd_pkt_t	 hdr;
491fbc48c2bSTakanori Watanabe 		ng_hci_le_create_connection_cp	 cp;
492fbc48c2bSTakanori Watanabe 	} __attribute__ ((packed))	*req = NULL;
493fbc48c2bSTakanori Watanabe 	ng_hci_lp_con_req_ep		*ep = NULL;
494fbc48c2bSTakanori Watanabe 	ng_hci_unit_con_p		 con = NULL;
495fbc48c2bSTakanori Watanabe 	struct mbuf			*m = NULL;
496fbc48c2bSTakanori Watanabe 	int				 error = 0;
497fbc48c2bSTakanori Watanabe 
498fbc48c2bSTakanori Watanabe 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
499fbc48c2bSTakanori Watanabe 	if((link_type != NG_HCI_LINK_LE_PUBLIC)&&
500fbc48c2bSTakanori Watanabe 	   (link_type != NG_HCI_LINK_LE_RANDOM)){
501fbc48c2bSTakanori Watanabe 		printf("%s: Link type %d Cannot be here \n", __func__,
502fbc48c2bSTakanori Watanabe 		       link_type);
503fbc48c2bSTakanori Watanabe 	}
504fbc48c2bSTakanori Watanabe 	/*
505fbc48c2bSTakanori Watanabe 	 * Only one ACL connection can exist between each pair of units.
506fbc48c2bSTakanori Watanabe 	 * So try to find ACL connection descriptor (in any state) that
507fbc48c2bSTakanori Watanabe 	 * has requested remote BD_ADDR.
508fbc48c2bSTakanori Watanabe 	 *
509fbc48c2bSTakanori Watanabe 	 * Two cases:
510fbc48c2bSTakanori Watanabe 	 *
511fbc48c2bSTakanori Watanabe 	 * 1) We do not have connection to the remote unit. This is simple.
512fbc48c2bSTakanori Watanabe 	 *    Just create new connection descriptor and send HCI command to
513fbc48c2bSTakanori Watanabe 	 *    create new connection.
514fbc48c2bSTakanori Watanabe 	 *
515fbc48c2bSTakanori Watanabe 	 * 2) We do have connection descriptor. We need to check connection
516fbc48c2bSTakanori Watanabe 	 *    state:
517fbc48c2bSTakanori Watanabe 	 *
518fbc48c2bSTakanori Watanabe 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
519fbc48c2bSTakanori Watanabe 	 *      accepting connection from the remote unit. This is a race
520fbc48c2bSTakanori Watanabe 	 *      condition. We will ignore this message.
521fbc48c2bSTakanori Watanabe 	 *
522fbc48c2bSTakanori Watanabe 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
523fbc48c2bSTakanori Watanabe 	 *      requested connection or we just accepted it. In any case
524fbc48c2bSTakanori Watanabe 	 *      all we need to do here is set appropriate notification bit
525fbc48c2bSTakanori Watanabe 	 *      and wait.
526fbc48c2bSTakanori Watanabe 	 *
527fbc48c2bSTakanori Watanabe 	 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
528fbc48c2bSTakanori Watanabe 	 *      and let upper layer know that we have connection already.
529fbc48c2bSTakanori Watanabe 	 */
530fbc48c2bSTakanori Watanabe 
531fbc48c2bSTakanori Watanabe 	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, link_type);
532fbc48c2bSTakanori Watanabe 	if (con != NULL) {
533fbc48c2bSTakanori Watanabe 		switch (con->state) {
534fbc48c2bSTakanori Watanabe 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
535fbc48c2bSTakanori Watanabe 			error = EALREADY;
536fbc48c2bSTakanori Watanabe 			break;
537fbc48c2bSTakanori Watanabe 
538fbc48c2bSTakanori Watanabe 		case NG_HCI_CON_W4_CONN_COMPLETE:
539fbc48c2bSTakanori Watanabe 			if (hook != unit->sco)
540fbc48c2bSTakanori Watanabe 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
541fbc48c2bSTakanori Watanabe 			else
542fbc48c2bSTakanori Watanabe 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
543fbc48c2bSTakanori Watanabe 			break;
544fbc48c2bSTakanori Watanabe 
545fbc48c2bSTakanori Watanabe 		case NG_HCI_CON_OPEN: {
546fbc48c2bSTakanori Watanabe 			struct ng_mesg		*msg = NULL;
547fbc48c2bSTakanori Watanabe 			ng_hci_lp_con_cfm_ep	*cfm = NULL;
548fbc48c2bSTakanori Watanabe 
549fbc48c2bSTakanori Watanabe 			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
550fbc48c2bSTakanori Watanabe 				NGI_GET_MSG(item, msg);
551fbc48c2bSTakanori Watanabe 				NG_FREE_MSG(msg);
552fbc48c2bSTakanori Watanabe 
553fbc48c2bSTakanori Watanabe 				NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
554fbc48c2bSTakanori Watanabe 					NGM_HCI_LP_CON_CFM, sizeof(*cfm),
555fbc48c2bSTakanori Watanabe 					M_NOWAIT);
556fbc48c2bSTakanori Watanabe 				if (msg != NULL) {
557fbc48c2bSTakanori Watanabe 					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
558fbc48c2bSTakanori Watanabe 					cfm->status = 0;
559fbc48c2bSTakanori Watanabe 					cfm->link_type = con->link_type;
560fbc48c2bSTakanori Watanabe 					cfm->con_handle = con->con_handle;
561fbc48c2bSTakanori Watanabe 					bcopy(&con->bdaddr, &cfm->bdaddr,
562fbc48c2bSTakanori Watanabe 						sizeof(cfm->bdaddr));
563fbc48c2bSTakanori Watanabe 
564fbc48c2bSTakanori Watanabe 					/*
565fbc48c2bSTakanori Watanabe 					 * This will forward item back to
566fbc48c2bSTakanori Watanabe 					 * sender and set item to NULL
567fbc48c2bSTakanori Watanabe 					 */
568fbc48c2bSTakanori Watanabe 
569fbc48c2bSTakanori Watanabe 					_NGI_MSG(item) = msg;
570fbc48c2bSTakanori Watanabe 					NG_FWD_ITEM_HOOK(error, item, hook);
571fbc48c2bSTakanori Watanabe 				} else
572fbc48c2bSTakanori Watanabe 					error = ENOMEM;
573fbc48c2bSTakanori Watanabe 			} else
574fbc48c2bSTakanori Watanabe 				NG_HCI_INFO(
575fbc48c2bSTakanori Watanabe "%s: %s - Source hook is not valid, hook=%p\n",
576fbc48c2bSTakanori Watanabe 					__func__, NG_NODE_NAME(unit->node),
577fbc48c2bSTakanori Watanabe 					hook);
578fbc48c2bSTakanori Watanabe 			} break;
579fbc48c2bSTakanori Watanabe 
580fbc48c2bSTakanori Watanabe 		default:
581fbc48c2bSTakanori Watanabe 			panic(
582fbc48c2bSTakanori Watanabe "%s: %s - Invalid connection state=%d\n",
583fbc48c2bSTakanori Watanabe 				__func__, NG_NODE_NAME(unit->node), con->state);
584fbc48c2bSTakanori Watanabe 			break;
585fbc48c2bSTakanori Watanabe 		}
586fbc48c2bSTakanori Watanabe 
587fbc48c2bSTakanori Watanabe 		goto out;
588fbc48c2bSTakanori Watanabe 	}
589fbc48c2bSTakanori Watanabe 
590fbc48c2bSTakanori Watanabe 	/*
591fbc48c2bSTakanori Watanabe 	 * If we got here then we need to create new ACL connection descriptor
592fbc48c2bSTakanori Watanabe 	 * and submit HCI command. First create new connection desriptor, set
593fbc48c2bSTakanori Watanabe 	 * bdaddr and notification flags.
594fbc48c2bSTakanori Watanabe 	 */
595fbc48c2bSTakanori Watanabe 
596fbc48c2bSTakanori Watanabe 	con = ng_hci_new_con(unit, link_type);
597fbc48c2bSTakanori Watanabe 	if (con == NULL) {
598fbc48c2bSTakanori Watanabe 		error = ENOMEM;
599fbc48c2bSTakanori Watanabe 		goto out;
600fbc48c2bSTakanori Watanabe 	}
601fbc48c2bSTakanori Watanabe 
602fbc48c2bSTakanori Watanabe 	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
603fbc48c2bSTakanori Watanabe 
604fbc48c2bSTakanori Watanabe 	/*
605fbc48c2bSTakanori Watanabe 	 * Create HCI command
606fbc48c2bSTakanori Watanabe 	 */
607fbc48c2bSTakanori Watanabe 
608fbc48c2bSTakanori Watanabe 	MGETHDR(m, M_NOWAIT, MT_DATA);
609fbc48c2bSTakanori Watanabe 	if (m == NULL) {
610fbc48c2bSTakanori Watanabe 		ng_hci_free_con(con);
611fbc48c2bSTakanori Watanabe 		error = ENOBUFS;
612fbc48c2bSTakanori Watanabe 		goto out;
613fbc48c2bSTakanori Watanabe 	}
614fbc48c2bSTakanori Watanabe 
615fbc48c2bSTakanori Watanabe 	m->m_pkthdr.len = m->m_len = sizeof(*req);
616fbc48c2bSTakanori Watanabe 	req = mtod(m, struct acl_con_req *);
617fbc48c2bSTakanori Watanabe 	req->hdr.type = NG_HCI_CMD_PKT;
618fbc48c2bSTakanori Watanabe 	req->hdr.length = sizeof(req->cp);
619fbc48c2bSTakanori Watanabe 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LE,
620fbc48c2bSTakanori Watanabe 					NG_HCI_OCF_LE_CREATE_CONNECTION));
621fbc48c2bSTakanori Watanabe 
622fbc48c2bSTakanori Watanabe 	bcopy(&ep->bdaddr, &req->cp.peer_addr, sizeof(req->cp.peer_addr));
623fbc48c2bSTakanori Watanabe 	req->cp.own_address_type = 0;
624fbc48c2bSTakanori Watanabe 	req->cp.peer_addr_type = (link_type == NG_HCI_LINK_LE_RANDOM)? 1:0;
625fbc48c2bSTakanori Watanabe 	req->cp.scan_interval = htole16(4);
626fbc48c2bSTakanori Watanabe 	req->cp.scan_window = htole16(4);
627fbc48c2bSTakanori Watanabe 	req->cp.filter_policy = 0;
628fbc48c2bSTakanori Watanabe 	req->cp.conn_interval_min = htole16(0xf);
629fbc48c2bSTakanori Watanabe 	req->cp.conn_interval_max = htole16(0xf);
630fbc48c2bSTakanori Watanabe 	req->cp.conn_latency = htole16(0);
631fbc48c2bSTakanori Watanabe 	req->cp.supervision_timeout = htole16(0xc80);
632fbc48c2bSTakanori Watanabe 	req->cp.min_ce_length = htole16(1);
633fbc48c2bSTakanori Watanabe 	req->cp.max_ce_length = htole16(1);
634fbc48c2bSTakanori Watanabe 	/*
635fbc48c2bSTakanori Watanabe 	 * Adust connection state
636fbc48c2bSTakanori Watanabe 	 */
637fbc48c2bSTakanori Watanabe 
638fbc48c2bSTakanori Watanabe 	if (hook != unit->sco)
639fbc48c2bSTakanori Watanabe 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
640fbc48c2bSTakanori Watanabe 	else
641fbc48c2bSTakanori Watanabe 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
642fbc48c2bSTakanori Watanabe 
643fbc48c2bSTakanori Watanabe 	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
644fbc48c2bSTakanori Watanabe 	ng_hci_con_timeout(con);
645fbc48c2bSTakanori Watanabe 
646fbc48c2bSTakanori Watanabe 	/*
647fbc48c2bSTakanori Watanabe 	 * Queue and send HCI command
648fbc48c2bSTakanori Watanabe 	 */
649fbc48c2bSTakanori Watanabe 
650fbc48c2bSTakanori Watanabe 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
651fbc48c2bSTakanori Watanabe 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
652fbc48c2bSTakanori Watanabe 		error = ng_hci_send_command(unit);
653fbc48c2bSTakanori Watanabe out:
654fbc48c2bSTakanori Watanabe 	if (item != NULL)
655fbc48c2bSTakanori Watanabe 		NG_FREE_ITEM(item);
656fbc48c2bSTakanori Watanabe 
657fbc48c2bSTakanori Watanabe 	return (error);
658fbc48c2bSTakanori Watanabe } /* ng_hci_lp_acl_con_req */
659fbc48c2bSTakanori Watanabe 
660878ed226SJulian Elischer /*
661878ed226SJulian Elischer  * Process LP_DisconnectReq event from the upper layer protocol
662878ed226SJulian Elischer  */
663878ed226SJulian Elischer 
664878ed226SJulian Elischer int
665878ed226SJulian Elischer ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
666878ed226SJulian Elischer {
667878ed226SJulian Elischer 	struct discon_req {
668878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
669878ed226SJulian Elischer 		ng_hci_discon_cp	 cp;
670878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
671878ed226SJulian Elischer 	ng_hci_lp_discon_req_ep		*ep = NULL;
672878ed226SJulian Elischer 	ng_hci_unit_con_p		 con = NULL;
673878ed226SJulian Elischer 	struct mbuf			*m = NULL;
674878ed226SJulian Elischer 	int				 error = 0;
675878ed226SJulian Elischer 
676878ed226SJulian Elischer 	/* Check if unit is ready */
677878ed226SJulian Elischer 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
678878ed226SJulian Elischer 		NG_HCI_WARN(
679878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n",
680878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->state);
681878ed226SJulian Elischer 
682878ed226SJulian Elischer 		error = ENXIO;
683878ed226SJulian Elischer 		goto out;
684878ed226SJulian Elischer 	}
685878ed226SJulian Elischer 
686878ed226SJulian Elischer 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
687878ed226SJulian Elischer 		NG_HCI_ALERT(
688878ed226SJulian Elischer "%s: %s - invalid LP_DisconnectReq message size=%d\n",
689878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
690878ed226SJulian Elischer 			NGI_MSG(item)->header.arglen);
691878ed226SJulian Elischer 
692878ed226SJulian Elischer 		error = EMSGSIZE;
693878ed226SJulian Elischer 		goto out;
694878ed226SJulian Elischer 	}
695878ed226SJulian Elischer 
696878ed226SJulian Elischer 	ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
697878ed226SJulian Elischer 
698878ed226SJulian Elischer 	con = ng_hci_con_by_handle(unit, ep->con_handle);
699878ed226SJulian Elischer 	if (con == NULL) {
700878ed226SJulian Elischer 		NG_HCI_ERR(
701878ed226SJulian Elischer "%s: %s - invalid connection handle=%d\n",
702878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
703878ed226SJulian Elischer 
704878ed226SJulian Elischer 		error = ENOENT;
705878ed226SJulian Elischer 		goto out;
706878ed226SJulian Elischer 	}
707878ed226SJulian Elischer 
708878ed226SJulian Elischer 	if (con->state != NG_HCI_CON_OPEN) {
709878ed226SJulian Elischer 		NG_HCI_ERR(
710878ed226SJulian Elischer "%s: %s - invalid connection state=%d, handle=%d\n",
711878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), con->state,
712878ed226SJulian Elischer 			ep->con_handle);
713878ed226SJulian Elischer 
714878ed226SJulian Elischer 		error = EINVAL;
715878ed226SJulian Elischer 		goto out;
716878ed226SJulian Elischer 	}
717878ed226SJulian Elischer 
718878ed226SJulian Elischer 	/*
719878ed226SJulian Elischer 	 * Create HCI command
720878ed226SJulian Elischer 	 */
721878ed226SJulian Elischer 
722eb1b1807SGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
723878ed226SJulian Elischer 	if (m == NULL) {
724878ed226SJulian Elischer 		error = ENOBUFS;
725878ed226SJulian Elischer 		goto out;
726878ed226SJulian Elischer 	}
727878ed226SJulian Elischer 
728878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
729878ed226SJulian Elischer 	req = mtod(m, struct discon_req *);
730878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
731878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
732878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
733878ed226SJulian Elischer 							NG_HCI_OCF_DISCON));
734878ed226SJulian Elischer 
735878ed226SJulian Elischer 	req->cp.con_handle = htole16(ep->con_handle);
736878ed226SJulian Elischer 	req->cp.reason = ep->reason;
737878ed226SJulian Elischer 
738878ed226SJulian Elischer 	/*
739878ed226SJulian Elischer 	 * Queue and send HCI command
740878ed226SJulian Elischer 	 */
741878ed226SJulian Elischer 
742878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
743878ed226SJulian Elischer 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
744878ed226SJulian Elischer 		error = ng_hci_send_command(unit);
745878ed226SJulian Elischer out:
746878ed226SJulian Elischer 	NG_FREE_ITEM(item);
747878ed226SJulian Elischer 
748878ed226SJulian Elischer 	return (error);
749878ed226SJulian Elischer } /* ng_hci_lp_discon_req */
750878ed226SJulian Elischer 
751878ed226SJulian Elischer /*
752878ed226SJulian Elischer  * Send LP_ConnectCfm event to the upper layer protocol
753878ed226SJulian Elischer  */
754878ed226SJulian Elischer 
755878ed226SJulian Elischer int
756878ed226SJulian Elischer ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
757878ed226SJulian Elischer {
758878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
759878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
760878ed226SJulian Elischer 	ng_hci_lp_con_cfm_ep	*ep = NULL;
761878ed226SJulian Elischer 	int			 error;
762878ed226SJulian Elischer 
763878ed226SJulian Elischer 	/*
764878ed226SJulian Elischer 	 * Check who wants to be notified. For ACL links both ACL and SCO
765878ed226SJulian Elischer 	 * upstream hooks will be notified (if required). For SCO links
766878ed226SJulian Elischer 	 * only SCO upstream hook will receive notification
767878ed226SJulian Elischer 	 */
768878ed226SJulian Elischer 
769fbc48c2bSTakanori Watanabe 	if (con->link_type != NG_HCI_LINK_SCO &&
770878ed226SJulian Elischer 	    con->flags & NG_HCI_CON_NOTIFY_ACL) {
771878ed226SJulian Elischer 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
772878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
773878ed226SJulian Elischer 				sizeof(*ep), M_NOWAIT);
774878ed226SJulian Elischer 			if (msg != NULL) {
775878ed226SJulian Elischer 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
776878ed226SJulian Elischer 				ep->status = status;
777878ed226SJulian Elischer 				ep->link_type = con->link_type;
778878ed226SJulian Elischer 				ep->con_handle = con->con_handle;
779878ed226SJulian Elischer 				bcopy(&con->bdaddr, &ep->bdaddr,
780878ed226SJulian Elischer 					sizeof(ep->bdaddr));
781878ed226SJulian Elischer 
782878ed226SJulian Elischer 				NG_SEND_MSG_HOOK(error, unit->node, msg,
7834ae439a3SMaksim Yevmenkin 					unit->acl, 0);
784878ed226SJulian Elischer 			}
785878ed226SJulian Elischer 		} else
786878ed226SJulian Elischer 			NG_HCI_INFO(
787878ed226SJulian Elischer "%s: %s - ACL hook not valid, hook=%p\n",
788878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), unit->acl);
789878ed226SJulian Elischer 
790878ed226SJulian Elischer 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
791878ed226SJulian Elischer 	}
792878ed226SJulian Elischer 
793878ed226SJulian Elischer 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
794878ed226SJulian Elischer 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
795878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
796878ed226SJulian Elischer 				sizeof(*ep), M_NOWAIT);
797878ed226SJulian Elischer 			if (msg != NULL) {
798878ed226SJulian Elischer 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
799878ed226SJulian Elischer 				ep->status = status;
800878ed226SJulian Elischer 				ep->link_type = con->link_type;
801878ed226SJulian Elischer 				ep->con_handle = con->con_handle;
802878ed226SJulian Elischer 				bcopy(&con->bdaddr, &ep->bdaddr,
803878ed226SJulian Elischer 					sizeof(ep->bdaddr));
804878ed226SJulian Elischer 
805878ed226SJulian Elischer 				NG_SEND_MSG_HOOK(error, unit->node, msg,
8064ae439a3SMaksim Yevmenkin 					unit->sco, 0);
807878ed226SJulian Elischer 			}
808878ed226SJulian Elischer 		} else
809878ed226SJulian Elischer 			NG_HCI_INFO(
810878ed226SJulian Elischer "%s: %s - SCO hook not valid, hook=%p\n",
811878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), unit->acl);
812878ed226SJulian Elischer 
813878ed226SJulian Elischer 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
814878ed226SJulian Elischer 	}
815878ed226SJulian Elischer 
816878ed226SJulian Elischer 	return (0);
817878ed226SJulian Elischer } /* ng_hci_lp_con_cfm */
818878ed226SJulian Elischer 
8193a601a23STakanori Watanabe int
8203a601a23STakanori Watanabe ng_hci_lp_enc_change(ng_hci_unit_con_p con, int status)
8213a601a23STakanori Watanabe {
8223a601a23STakanori Watanabe 	ng_hci_unit_p		 unit = con->unit;
8233a601a23STakanori Watanabe 	struct ng_mesg		*msg = NULL;
8243a601a23STakanori Watanabe 	ng_hci_lp_enc_change_ep	*ep = NULL;
8253a601a23STakanori Watanabe 	int			 error;
8263a601a23STakanori Watanabe 
8273a601a23STakanori Watanabe 	if (con->link_type != NG_HCI_LINK_SCO) {
8283a601a23STakanori Watanabe 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
8293a601a23STakanori Watanabe 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_ENC_CHG,
8303a601a23STakanori Watanabe 				sizeof(*ep), M_NOWAIT);
8313a601a23STakanori Watanabe 			if (msg != NULL) {
8323a601a23STakanori Watanabe 				ep = (ng_hci_lp_enc_change_ep *) msg->data;
8333a601a23STakanori Watanabe 				ep->status = status;
8343a601a23STakanori Watanabe 				ep->link_type = con->link_type;
8353a601a23STakanori Watanabe 				ep->con_handle = con->con_handle;
8363a601a23STakanori Watanabe 
8373a601a23STakanori Watanabe 				NG_SEND_MSG_HOOK(error, unit->node, msg,
8383a601a23STakanori Watanabe 					unit->acl, 0);
8393a601a23STakanori Watanabe 			}
8403a601a23STakanori Watanabe 		} else
8413a601a23STakanori Watanabe 			NG_HCI_INFO(
8423a601a23STakanori Watanabe "%s: %s - ACL hook not valid, hook=%p\n",
8433a601a23STakanori Watanabe 				__func__, NG_NODE_NAME(unit->node), unit->acl);
8443a601a23STakanori Watanabe 	}
8453a601a23STakanori Watanabe 	return (0);
8463a601a23STakanori Watanabe } /* ng_hci_lp_con_cfm */
8473a601a23STakanori Watanabe 
848878ed226SJulian Elischer /*
849878ed226SJulian Elischer  * Send LP_ConnectInd event to the upper layer protocol
850878ed226SJulian Elischer  */
851878ed226SJulian Elischer 
852878ed226SJulian Elischer int
853878ed226SJulian Elischer ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
854878ed226SJulian Elischer {
855878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
856878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
857878ed226SJulian Elischer 	ng_hci_lp_con_ind_ep	*ep = NULL;
858878ed226SJulian Elischer 	hook_p			 hook = NULL;
859878ed226SJulian Elischer 	int			 error = 0;
860878ed226SJulian Elischer 
861878ed226SJulian Elischer 	/*
862878ed226SJulian Elischer 	 * Connection_Request event is generated for specific link type.
863878ed226SJulian Elischer 	 * Use link_type to select upstream hook.
864878ed226SJulian Elischer 	 */
865878ed226SJulian Elischer 
866fbc48c2bSTakanori Watanabe 	if (con->link_type != NG_HCI_LINK_SCO)
867878ed226SJulian Elischer 		hook = unit->acl;
868878ed226SJulian Elischer 	else
869878ed226SJulian Elischer 		hook = unit->sco;
870878ed226SJulian Elischer 
871878ed226SJulian Elischer 	if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
872878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
873878ed226SJulian Elischer 			sizeof(*ep), M_NOWAIT);
874878ed226SJulian Elischer 		if (msg == NULL)
875878ed226SJulian Elischer 			return (ENOMEM);
876878ed226SJulian Elischer 
877878ed226SJulian Elischer 		ep = (ng_hci_lp_con_ind_ep *)(msg->data);
878878ed226SJulian Elischer 		ep->link_type = con->link_type;
879878ed226SJulian Elischer 		bcopy(uclass, ep->uclass, sizeof(ep->uclass));
880878ed226SJulian Elischer 		bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
881878ed226SJulian Elischer 
8824ae439a3SMaksim Yevmenkin 		NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
883878ed226SJulian Elischer 	} else {
884878ed226SJulian Elischer 		NG_HCI_WARN(
885878ed226SJulian Elischer "%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
886878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), hook);
887878ed226SJulian Elischer 
888878ed226SJulian Elischer 		error = ENOTCONN;
889878ed226SJulian Elischer 	}
890878ed226SJulian Elischer 
891878ed226SJulian Elischer 	return (error);
892878ed226SJulian Elischer } /* ng_hci_lp_con_ind */
893878ed226SJulian Elischer 
894878ed226SJulian Elischer /*
895878ed226SJulian Elischer  * Process LP_ConnectRsp event from the upper layer protocol
896878ed226SJulian Elischer  */
897878ed226SJulian Elischer 
898878ed226SJulian Elischer int
899878ed226SJulian Elischer ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
900878ed226SJulian Elischer {
901878ed226SJulian Elischer 	struct con_rsp_req {
902878ed226SJulian Elischer 		ng_hci_cmd_pkt_t		 hdr;
903878ed226SJulian Elischer 		union {
904878ed226SJulian Elischer 			ng_hci_accept_con_cp	 acc;
905878ed226SJulian Elischer 			ng_hci_reject_con_cp	 rej;
906878ed226SJulian Elischer 		} __attribute__ ((packed))	 cp;
907878ed226SJulian Elischer 	} __attribute__ ((packed))		*req = NULL;
908878ed226SJulian Elischer 	ng_hci_lp_con_rsp_ep			*ep = NULL;
909878ed226SJulian Elischer 	ng_hci_unit_con_p			 con = NULL;
910878ed226SJulian Elischer 	struct mbuf				*m = NULL;
911878ed226SJulian Elischer 	int					 error = 0;
912878ed226SJulian Elischer 
913878ed226SJulian Elischer 	/* Check if unit is ready */
914878ed226SJulian Elischer 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
915878ed226SJulian Elischer 		NG_HCI_WARN(
916878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n",
917878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->state);
918878ed226SJulian Elischer 
919878ed226SJulian Elischer 		error = ENXIO;
920878ed226SJulian Elischer 		goto out;
921878ed226SJulian Elischer 	}
922878ed226SJulian Elischer 
923878ed226SJulian Elischer 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
924878ed226SJulian Elischer 		NG_HCI_ALERT(
925878ed226SJulian Elischer "%s: %s - invalid LP_ConnectRsp message size=%d\n",
926878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
927878ed226SJulian Elischer 			NGI_MSG(item)->header.arglen);
928878ed226SJulian Elischer 
929878ed226SJulian Elischer 		error = EMSGSIZE;
930878ed226SJulian Elischer 		goto out;
931878ed226SJulian Elischer 	}
932878ed226SJulian Elischer 
933878ed226SJulian Elischer 	ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
934878ed226SJulian Elischer 
935878ed226SJulian Elischer 	/*
936878ed226SJulian Elischer 	 * Here we have to deal with race. Upper layers might send conflicting
937878ed226SJulian Elischer 	 * requests. One might send Accept and other Reject. We will not try
938878ed226SJulian Elischer 	 * to solve all the problems, so first request will always win.
939878ed226SJulian Elischer 	 *
940878ed226SJulian Elischer 	 * Try to find connection that matches the following:
941878ed226SJulian Elischer 	 *
942878ed226SJulian Elischer 	 * 1) con->link_type == ep->link_type
943878ed226SJulian Elischer 	 *
944878ed226SJulian Elischer 	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
945878ed226SJulian Elischer 	 *    con->state == NG_HCI_CON_W4_CONN_COMPLETE
946878ed226SJulian Elischer 	 *
947878ed226SJulian Elischer 	 * 3) con->bdaddr == ep->bdaddr
948878ed226SJulian Elischer 	 *
949878ed226SJulian Elischer 	 * Two cases:
950878ed226SJulian Elischer 	 *
951878ed226SJulian Elischer 	 * 1) We do not have connection descriptor. Could be bogus request or
952878ed226SJulian Elischer 	 *    we have rejected connection already.
953878ed226SJulian Elischer 	 *
954878ed226SJulian Elischer 	 * 2) We do have connection descriptor. Then we need to check state:
955878ed226SJulian Elischer 	 *
956878ed226SJulian Elischer 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
957878ed226SJulian Elischer 	 *      connection and it is a first response from the upper layer.
958878ed226SJulian Elischer 	 *      if "status == 0" (Accept) then we will send Accept_Connection
959878ed226SJulian Elischer 	 *      command and change connection state to W4_CONN_COMPLETE, else
960878ed226SJulian Elischer 	 *      send reject and delete connection.
961878ed226SJulian Elischer 	 *
962878ed226SJulian Elischer 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
963878ed226SJulian Elischer 	 *      connection. If "status == 0" we just need to link request
964878ed226SJulian Elischer 	 *      and wait, else ignore Reject request.
965878ed226SJulian Elischer 	 */
966878ed226SJulian Elischer 
967878ed226SJulian Elischer 	LIST_FOREACH(con, &unit->con_list, next)
968878ed226SJulian Elischer 		if (con->link_type == ep->link_type &&
969878ed226SJulian Elischer 		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
970878ed226SJulian Elischer 		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
971878ed226SJulian Elischer 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
972878ed226SJulian Elischer 			break;
973878ed226SJulian Elischer 
974878ed226SJulian Elischer 	if (con == NULL) {
975878ed226SJulian Elischer 		/* Reject for non-existing connection is fine */
976878ed226SJulian Elischer 		error = (ep->status == 0)? ENOENT : 0;
977878ed226SJulian Elischer 		goto out;
978878ed226SJulian Elischer 	}
979878ed226SJulian Elischer 
980878ed226SJulian Elischer 	/*
9810986ab12SMaksim Yevmenkin 	 * Remove connection timeout and check connection state.
9820986ab12SMaksim Yevmenkin 	 * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then
9830986ab12SMaksim Yevmenkin 	 * timeout already happened and event went into node's queue.
984878ed226SJulian Elischer 	 */
985878ed226SJulian Elischer 
9860986ab12SMaksim Yevmenkin 	if ((error = ng_hci_con_untimeout(con)) != 0)
9870986ab12SMaksim Yevmenkin 		goto out;
988878ed226SJulian Elischer 
989878ed226SJulian Elischer 	switch (con->state) {
990878ed226SJulian Elischer 	case NG_HCI_CON_W4_LP_CON_RSP:
991878ed226SJulian Elischer 
992878ed226SJulian Elischer 		/*
993878ed226SJulian Elischer 		 * Create HCI command
994878ed226SJulian Elischer 		 */
995878ed226SJulian Elischer 
996eb1b1807SGleb Smirnoff 		MGETHDR(m, M_NOWAIT, MT_DATA);
997878ed226SJulian Elischer 		if (m == NULL) {
998878ed226SJulian Elischer 			error = ENOBUFS;
999878ed226SJulian Elischer 			goto out;
1000878ed226SJulian Elischer 		}
1001878ed226SJulian Elischer 
1002878ed226SJulian Elischer 		req = mtod(m, struct con_rsp_req *);
1003878ed226SJulian Elischer 		req->hdr.type = NG_HCI_CMD_PKT;
1004878ed226SJulian Elischer 
1005878ed226SJulian Elischer 		if (ep->status == 0) {
1006878ed226SJulian Elischer 			req->hdr.length = sizeof(req->cp.acc);
1007878ed226SJulian Elischer 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
1008878ed226SJulian Elischer 							NG_HCI_OGF_LINK_CONTROL,
1009878ed226SJulian Elischer 							NG_HCI_OCF_ACCEPT_CON));
1010878ed226SJulian Elischer 
1011878ed226SJulian Elischer 			bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
1012878ed226SJulian Elischer 				sizeof(req->cp.acc.bdaddr));
1013878ed226SJulian Elischer 
1014878ed226SJulian Elischer 			/*
1015878ed226SJulian Elischer 			 * We are accepting connection, so if we support role
1016f2bb1caeSJulian Elischer 			 * switch and role switch was enabled then set role to
1017*77a44875SGordon Bergling 			 * NG_HCI_ROLE_MASTER and let LM perform role switch.
1018f2bb1caeSJulian Elischer 			 * Otherwise we remain slave. In this case LM WILL NOT
1019f2bb1caeSJulian Elischer 			 * perform role switch.
1020878ed226SJulian Elischer 			 */
1021878ed226SJulian Elischer 
1022f2bb1caeSJulian Elischer 			if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
1023f2bb1caeSJulian Elischer 			    unit->role_switch)
1024878ed226SJulian Elischer 				req->cp.acc.role = NG_HCI_ROLE_MASTER;
1025878ed226SJulian Elischer 			else
1026878ed226SJulian Elischer 				req->cp.acc.role = NG_HCI_ROLE_SLAVE;
1027878ed226SJulian Elischer 
1028878ed226SJulian Elischer 			/*
1029878ed226SJulian Elischer 			 * Adjust connection state
1030878ed226SJulian Elischer 			 */
1031878ed226SJulian Elischer 
1032878ed226SJulian Elischer 			if (hook == unit->acl)
1033878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
1034878ed226SJulian Elischer 			else
1035878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
1036878ed226SJulian Elischer 
1037878ed226SJulian Elischer 			con->state = NG_HCI_CON_W4_CONN_COMPLETE;
1038878ed226SJulian Elischer 			ng_hci_con_timeout(con);
1039878ed226SJulian Elischer 		} else {
1040878ed226SJulian Elischer 			req->hdr.length = sizeof(req->cp.rej);
1041878ed226SJulian Elischer 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
1042878ed226SJulian Elischer 							NG_HCI_OGF_LINK_CONTROL,
1043878ed226SJulian Elischer 							NG_HCI_OCF_REJECT_CON));
1044878ed226SJulian Elischer 
1045878ed226SJulian Elischer 			bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
1046878ed226SJulian Elischer 				sizeof(req->cp.rej.bdaddr));
1047878ed226SJulian Elischer 
1048878ed226SJulian Elischer 			req->cp.rej.reason = ep->status;
1049878ed226SJulian Elischer 
1050878ed226SJulian Elischer 			/*
1051878ed226SJulian Elischer 			 * Free connection descritor
1052878ed226SJulian Elischer 			 * Item will be deleted just before return.
1053878ed226SJulian Elischer 			 */
1054878ed226SJulian Elischer 
1055878ed226SJulian Elischer 			ng_hci_free_con(con);
1056878ed226SJulian Elischer 		}
1057878ed226SJulian Elischer 
1058878ed226SJulian Elischer 		m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
1059878ed226SJulian Elischer 
1060878ed226SJulian Elischer 		/* Queue and send HCI command */
1061878ed226SJulian Elischer 		NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1062878ed226SJulian Elischer 		if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1063878ed226SJulian Elischer 			error = ng_hci_send_command(unit);
1064878ed226SJulian Elischer 		break;
1065878ed226SJulian Elischer 
1066878ed226SJulian Elischer 	case NG_HCI_CON_W4_CONN_COMPLETE:
1067878ed226SJulian Elischer 		if (ep->status == 0) {
1068878ed226SJulian Elischer 			if (hook == unit->acl)
1069878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
1070878ed226SJulian Elischer 			else
1071878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
1072878ed226SJulian Elischer 		} else
1073878ed226SJulian Elischer 			error = EPERM;
1074878ed226SJulian Elischer 		break;
1075878ed226SJulian Elischer 
1076878ed226SJulian Elischer 	default:
10770986ab12SMaksim Yevmenkin 		panic(
10780986ab12SMaksim Yevmenkin "%s: %s - Invalid connection state=%d\n",
10790986ab12SMaksim Yevmenkin 			__func__, NG_NODE_NAME(unit->node), con->state);
1080878ed226SJulian Elischer 		break;
1081878ed226SJulian Elischer 	}
1082878ed226SJulian Elischer out:
1083878ed226SJulian Elischer 	NG_FREE_ITEM(item);
1084878ed226SJulian Elischer 
1085878ed226SJulian Elischer 	return (error);
1086878ed226SJulian Elischer } /* ng_hci_lp_con_rsp */
1087878ed226SJulian Elischer 
1088878ed226SJulian Elischer /*
1089878ed226SJulian Elischer  * Send LP_DisconnectInd to the upper layer protocol
1090878ed226SJulian Elischer  */
1091878ed226SJulian Elischer 
1092878ed226SJulian Elischer int
1093878ed226SJulian Elischer ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
1094878ed226SJulian Elischer {
1095878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
1096878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
1097878ed226SJulian Elischer 	ng_hci_lp_discon_ind_ep	*ep = NULL;
1098878ed226SJulian Elischer 	int			 error = 0;
1099878ed226SJulian Elischer 
1100878ed226SJulian Elischer 	/*
1101878ed226SJulian Elischer 	 * Disconnect_Complete event is generated for specific connection
1102878ed226SJulian Elischer 	 * handle. For ACL connection handles both ACL and SCO upstream
1103878ed226SJulian Elischer 	 * hooks will receive notification. For SCO connection handles
1104878ed226SJulian Elischer 	 * only SCO upstream hook will receive notification.
1105878ed226SJulian Elischer 	 */
1106878ed226SJulian Elischer 
1107fbc48c2bSTakanori Watanabe 	if (con->link_type != NG_HCI_LINK_SCO) {
1108878ed226SJulian Elischer 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1109878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
1110878ed226SJulian Elischer 				NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
1111878ed226SJulian Elischer 			if (msg == NULL)
1112878ed226SJulian Elischer 				return (ENOMEM);
1113878ed226SJulian Elischer 
1114878ed226SJulian Elischer 			ep = (ng_hci_lp_discon_ind_ep *) msg->data;
1115878ed226SJulian Elischer 			ep->reason = reason;
1116878ed226SJulian Elischer 			ep->link_type = con->link_type;
1117878ed226SJulian Elischer 			ep->con_handle = con->con_handle;
1118878ed226SJulian Elischer 
11194ae439a3SMaksim Yevmenkin 			NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0);
1120878ed226SJulian Elischer 		} else
1121878ed226SJulian Elischer 			NG_HCI_INFO(
1122878ed226SJulian Elischer "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1123878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), unit->acl);
1124878ed226SJulian Elischer 	}
1125878ed226SJulian Elischer 
1126878ed226SJulian Elischer 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1127878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
1128878ed226SJulian Elischer 			sizeof(*ep), M_NOWAIT);
1129878ed226SJulian Elischer 		if (msg == NULL)
1130878ed226SJulian Elischer 			return (ENOMEM);
1131878ed226SJulian Elischer 
1132878ed226SJulian Elischer 		ep = (ng_hci_lp_discon_ind_ep *) msg->data;
1133878ed226SJulian Elischer 		ep->reason = reason;
1134878ed226SJulian Elischer 		ep->link_type = con->link_type;
1135878ed226SJulian Elischer 		ep->con_handle = con->con_handle;
1136878ed226SJulian Elischer 
11374ae439a3SMaksim Yevmenkin 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
1138878ed226SJulian Elischer 	} else
1139878ed226SJulian Elischer 		NG_HCI_INFO(
1140878ed226SJulian Elischer "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1141878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->sco);
1142878ed226SJulian Elischer 
1143878ed226SJulian Elischer 	return (0);
1144878ed226SJulian Elischer } /* ng_hci_lp_discon_ind */
1145878ed226SJulian Elischer 
1146878ed226SJulian Elischer /*
1147878ed226SJulian Elischer  * Process LP_QoSReq action from the upper layer protocol
1148878ed226SJulian Elischer  */
1149878ed226SJulian Elischer 
1150878ed226SJulian Elischer int
1151878ed226SJulian Elischer ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
1152878ed226SJulian Elischer {
1153878ed226SJulian Elischer 	struct qos_setup_req {
1154878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
1155878ed226SJulian Elischer 		ng_hci_qos_setup_cp	 cp;
1156878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
1157878ed226SJulian Elischer 	ng_hci_lp_qos_req_ep		*ep = NULL;
1158878ed226SJulian Elischer 	ng_hci_unit_con_p		 con = NULL;
1159878ed226SJulian Elischer 	struct mbuf			*m = NULL;
1160878ed226SJulian Elischer 	int				 error = 0;
1161878ed226SJulian Elischer 
1162878ed226SJulian Elischer 	/* Check if unit is ready */
1163878ed226SJulian Elischer 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
1164878ed226SJulian Elischer 		NG_HCI_WARN(
1165878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n",
1166878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->state);
1167878ed226SJulian Elischer 
1168878ed226SJulian Elischer 		error = ENXIO;
1169878ed226SJulian Elischer 		goto out;
1170878ed226SJulian Elischer 	}
1171878ed226SJulian Elischer 
1172878ed226SJulian Elischer 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
1173878ed226SJulian Elischer 		NG_HCI_ALERT(
1174878ed226SJulian Elischer "%s: %s - invalid LP_QoSSetupReq message size=%d\n",
1175878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
1176878ed226SJulian Elischer 			NGI_MSG(item)->header.arglen);
1177878ed226SJulian Elischer 
1178878ed226SJulian Elischer 		error = EMSGSIZE;
1179878ed226SJulian Elischer 		goto out;
1180878ed226SJulian Elischer 	}
1181878ed226SJulian Elischer 
1182878ed226SJulian Elischer 	ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
1183878ed226SJulian Elischer 
1184878ed226SJulian Elischer 	con = ng_hci_con_by_handle(unit, ep->con_handle);
1185878ed226SJulian Elischer 	if (con == NULL) {
1186878ed226SJulian Elischer 		NG_HCI_ERR(
1187878ed226SJulian Elischer "%s: %s - invalid connection handle=%d\n",
1188878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
1189878ed226SJulian Elischer 
1190878ed226SJulian Elischer 		error = EINVAL;
1191878ed226SJulian Elischer 		goto out;
1192878ed226SJulian Elischer 	}
1193878ed226SJulian Elischer 
1194878ed226SJulian Elischer 	if (con->link_type != NG_HCI_LINK_ACL) {
1195878ed226SJulian Elischer 		NG_HCI_ERR("%s: %s - invalid link type=%d\n",
1196878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), con->link_type);
1197878ed226SJulian Elischer 
1198878ed226SJulian Elischer 		error = EINVAL;
1199878ed226SJulian Elischer 		goto out;
1200878ed226SJulian Elischer 	}
1201878ed226SJulian Elischer 
1202878ed226SJulian Elischer 	if (con->state != NG_HCI_CON_OPEN) {
1203878ed226SJulian Elischer 		NG_HCI_ERR(
1204878ed226SJulian Elischer "%s: %s - invalid connection state=%d, handle=%d\n",
1205878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), con->state,
1206878ed226SJulian Elischer 			con->con_handle);
1207878ed226SJulian Elischer 
1208878ed226SJulian Elischer 		error = EINVAL;
1209878ed226SJulian Elischer 		goto out;
1210878ed226SJulian Elischer 	}
1211878ed226SJulian Elischer 
1212878ed226SJulian Elischer 	/*
1213878ed226SJulian Elischer 	 * Create HCI command
1214878ed226SJulian Elischer 	 */
1215878ed226SJulian Elischer 
1216eb1b1807SGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
1217878ed226SJulian Elischer 	if (m == NULL) {
1218878ed226SJulian Elischer 		error = ENOBUFS;
1219878ed226SJulian Elischer 		goto out;
1220878ed226SJulian Elischer 	}
1221878ed226SJulian Elischer 
1222878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
1223878ed226SJulian Elischer 	req = mtod(m, struct qos_setup_req *);
1224878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
1225878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
1226878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1227878ed226SJulian Elischer 			NG_HCI_OCF_QOS_SETUP));
1228878ed226SJulian Elischer 
1229878ed226SJulian Elischer 	req->cp.con_handle = htole16(ep->con_handle);
1230878ed226SJulian Elischer 	req->cp.flags = ep->flags;
1231878ed226SJulian Elischer 	req->cp.service_type = ep->service_type;
1232878ed226SJulian Elischer 	req->cp.token_rate = htole32(ep->token_rate);
1233878ed226SJulian Elischer 	req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1234878ed226SJulian Elischer 	req->cp.latency = htole32(ep->latency);
1235878ed226SJulian Elischer 	req->cp.delay_variation = htole32(ep->delay_variation);
1236878ed226SJulian Elischer 
1237878ed226SJulian Elischer 	/*
1238878ed226SJulian Elischer 	 * Adjust connection state
1239878ed226SJulian Elischer  	 */
1240878ed226SJulian Elischer 
1241878ed226SJulian Elischer 	if (hook == unit->acl)
1242878ed226SJulian Elischer 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
1243878ed226SJulian Elischer 	else
1244878ed226SJulian Elischer 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
1245878ed226SJulian Elischer 
1246878ed226SJulian Elischer 	/*
1247878ed226SJulian Elischer 	 * Queue and send HCI command
1248878ed226SJulian Elischer 	 */
1249878ed226SJulian Elischer 
1250878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1251878ed226SJulian Elischer 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1252878ed226SJulian Elischer 		error = ng_hci_send_command(unit);
1253878ed226SJulian Elischer out:
1254878ed226SJulian Elischer 	NG_FREE_ITEM(item);
1255878ed226SJulian Elischer 
1256878ed226SJulian Elischer 	return (error);
1257878ed226SJulian Elischer } /* ng_hci_lp_qos_req */
1258878ed226SJulian Elischer 
1259878ed226SJulian Elischer /*
1260878ed226SJulian Elischer  * Send LP_QoSCfm event to the upper layer protocol
1261878ed226SJulian Elischer  */
1262878ed226SJulian Elischer 
1263878ed226SJulian Elischer int
1264878ed226SJulian Elischer ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1265878ed226SJulian Elischer {
1266878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
1267878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
1268878ed226SJulian Elischer 	ng_hci_lp_qos_cfm_ep	*ep = NULL;
1269878ed226SJulian Elischer 	int			 error;
1270878ed226SJulian Elischer 
1271878ed226SJulian Elischer 	if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1272878ed226SJulian Elischer 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1273878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1274878ed226SJulian Elischer 				sizeof(*ep), M_NOWAIT);
1275878ed226SJulian Elischer 			if (msg != NULL) {
1276878ed226SJulian Elischer 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1277878ed226SJulian Elischer 				ep->status = status;
1278878ed226SJulian Elischer 				ep->con_handle = con->con_handle;
1279878ed226SJulian Elischer 
1280878ed226SJulian Elischer 				NG_SEND_MSG_HOOK(error, unit->node, msg,
12814ae439a3SMaksim Yevmenkin 					unit->acl, 0);
1282878ed226SJulian Elischer 			}
1283878ed226SJulian Elischer 		} else
1284878ed226SJulian Elischer 			NG_HCI_INFO(
1285878ed226SJulian Elischer "%s: %s - ACL hook not valid, hook=%p\n",
1286878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), unit->acl);
1287878ed226SJulian Elischer 
1288878ed226SJulian Elischer 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1289878ed226SJulian Elischer 	}
1290878ed226SJulian Elischer 
1291878ed226SJulian Elischer 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1292878ed226SJulian Elischer 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1293878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1294878ed226SJulian Elischer 				sizeof(*ep), M_NOWAIT);
1295878ed226SJulian Elischer 			if (msg != NULL) {
1296878ed226SJulian Elischer 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1297878ed226SJulian Elischer 				ep->status = status;
1298878ed226SJulian Elischer 				ep->con_handle = con->con_handle;
1299878ed226SJulian Elischer 
1300878ed226SJulian Elischer 				NG_SEND_MSG_HOOK(error, unit->node, msg,
13014ae439a3SMaksim Yevmenkin 					unit->sco, 0);
1302878ed226SJulian Elischer 			}
1303878ed226SJulian Elischer 		} else
1304878ed226SJulian Elischer 			NG_HCI_INFO(
1305878ed226SJulian Elischer "%s: %s - SCO hook not valid, hook=%p\n",
1306878ed226SJulian Elischer 				 __func__, NG_NODE_NAME(unit->node), unit->sco);
1307878ed226SJulian Elischer 
1308878ed226SJulian Elischer 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1309878ed226SJulian Elischer 	}
1310878ed226SJulian Elischer 
1311878ed226SJulian Elischer 	return (0);
1312878ed226SJulian Elischer } /* ng_hci_lp_qos_cfm */
1313878ed226SJulian Elischer 
1314878ed226SJulian Elischer /*
1315878ed226SJulian Elischer  * Send LP_QoSViolationInd event to the upper layer protocol
1316878ed226SJulian Elischer  */
1317878ed226SJulian Elischer 
1318878ed226SJulian Elischer int
1319878ed226SJulian Elischer ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1320878ed226SJulian Elischer {
1321878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
1322878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
1323878ed226SJulian Elischer 	ng_hci_lp_qos_ind_ep	*ep = NULL;
1324878ed226SJulian Elischer 	int			 error;
1325878ed226SJulian Elischer 
1326878ed226SJulian Elischer 	/*
1327878ed226SJulian Elischer 	 * QoS Violation can only be generated for ACL connection handles.
1328878ed226SJulian Elischer 	 * Both ACL and SCO upstream hooks will receive notification.
1329878ed226SJulian Elischer 	 */
1330878ed226SJulian Elischer 
1331878ed226SJulian Elischer 	if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1332878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1333878ed226SJulian Elischer 			sizeof(*ep), M_NOWAIT);
1334878ed226SJulian Elischer 		if (msg == NULL)
1335878ed226SJulian Elischer 			return (ENOMEM);
1336878ed226SJulian Elischer 
1337878ed226SJulian Elischer 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1338878ed226SJulian Elischer 		ep->con_handle = con->con_handle;
1339878ed226SJulian Elischer 
13404ae439a3SMaksim Yevmenkin 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0);
1341878ed226SJulian Elischer 	} else
1342878ed226SJulian Elischer 		NG_HCI_INFO(
1343878ed226SJulian Elischer "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1344878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->acl);
1345878ed226SJulian Elischer 
1346878ed226SJulian Elischer 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1347878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1348878ed226SJulian Elischer 			sizeof(*ep), M_NOWAIT);
1349878ed226SJulian Elischer 		if (msg == NULL)
1350878ed226SJulian Elischer 			return (ENOMEM);
1351878ed226SJulian Elischer 
1352878ed226SJulian Elischer 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1353878ed226SJulian Elischer 		ep->con_handle = con->con_handle;
1354878ed226SJulian Elischer 
13554ae439a3SMaksim Yevmenkin 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
1356878ed226SJulian Elischer 	} else
1357878ed226SJulian Elischer 		NG_HCI_INFO(
1358878ed226SJulian Elischer "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1359878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->sco);
1360878ed226SJulian Elischer 
1361878ed226SJulian Elischer 	return (0);
1362878ed226SJulian Elischer } /* ng_hci_lp_qos_ind */
1363878ed226SJulian Elischer 
1364878ed226SJulian Elischer /*
1365878ed226SJulian Elischer  * Process connection timeout
1366878ed226SJulian Elischer  */
1367878ed226SJulian Elischer 
1368878ed226SJulian Elischer void
13690986ab12SMaksim Yevmenkin ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
1370878ed226SJulian Elischer {
13710986ab12SMaksim Yevmenkin 	ng_hci_unit_p		unit = NULL;
13720986ab12SMaksim Yevmenkin 	ng_hci_unit_con_p	con = NULL;
1373878ed226SJulian Elischer 
13740986ab12SMaksim Yevmenkin 	if (NG_NODE_NOT_VALID(node)) {
13750986ab12SMaksim Yevmenkin 		printf("%s: Netgraph node is not valid\n", __func__);
13760986ab12SMaksim Yevmenkin 		return;
13770986ab12SMaksim Yevmenkin 	}
13780986ab12SMaksim Yevmenkin 
13790986ab12SMaksim Yevmenkin 	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
13800986ab12SMaksim Yevmenkin 	con = ng_hci_con_by_handle(unit, con_handle);
13810986ab12SMaksim Yevmenkin 
13820986ab12SMaksim Yevmenkin 	if (con == NULL) {
13830986ab12SMaksim Yevmenkin 		NG_HCI_ALERT(
13840986ab12SMaksim Yevmenkin "%s: %s - could not find connection, handle=%d\n",
13850986ab12SMaksim Yevmenkin 			__func__, NG_NODE_NAME(node), con_handle);
13860986ab12SMaksim Yevmenkin 		return;
13870986ab12SMaksim Yevmenkin 	}
13880986ab12SMaksim Yevmenkin 
13890986ab12SMaksim Yevmenkin 	if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {
13900986ab12SMaksim Yevmenkin 		NG_HCI_ALERT(
13910986ab12SMaksim Yevmenkin "%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n",
13920986ab12SMaksim Yevmenkin 			__func__, NG_NODE_NAME(node), con_handle, con->state,
13930986ab12SMaksim Yevmenkin 			con->flags);
13940986ab12SMaksim Yevmenkin 		return;
13950986ab12SMaksim Yevmenkin 	}
1396878ed226SJulian Elischer 
1397878ed226SJulian Elischer 	con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1398878ed226SJulian Elischer 
1399878ed226SJulian Elischer 	/*
1400878ed226SJulian Elischer 	 * We expect to receive connection timeout in one of the following
1401878ed226SJulian Elischer 	 * states:
1402878ed226SJulian Elischer 	 *
1403f2bb1caeSJulian Elischer 	 * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1404878ed226SJulian Elischer 	 *    to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1405878ed226SJulian Elischer 	 *    most likely already gave up on us.
1406878ed226SJulian Elischer 	 *
1407f2bb1caeSJulian Elischer 	 * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1408878ed226SJulian Elischer 	 *    (or we in the process of accepting it) and baseband has timedout
1409878ed226SJulian Elischer 	 *    on us. Inform upper layers and send LP_CON_CFM.
1410878ed226SJulian Elischer 	 */
1411878ed226SJulian Elischer 
1412878ed226SJulian Elischer 	switch (con->state) {
1413878ed226SJulian Elischer 	case NG_HCI_CON_W4_LP_CON_RSP:
1414878ed226SJulian Elischer 		break;
1415878ed226SJulian Elischer 
1416878ed226SJulian Elischer 	case NG_HCI_CON_W4_CONN_COMPLETE:
1417878ed226SJulian Elischer 		ng_hci_lp_con_cfm(con, 0xee);
1418878ed226SJulian Elischer 		break;
1419878ed226SJulian Elischer 
1420878ed226SJulian Elischer 	default:
14210986ab12SMaksim Yevmenkin 		panic(
14220986ab12SMaksim Yevmenkin "%s: %s - Invalid connection state=%d\n",
14230986ab12SMaksim Yevmenkin 			__func__, NG_NODE_NAME(node), con->state);
1424878ed226SJulian Elischer 		break;
1425878ed226SJulian Elischer 	}
1426878ed226SJulian Elischer 
1427878ed226SJulian Elischer 	ng_hci_free_con(con);
1428878ed226SJulian Elischer } /* ng_hci_process_con_timeout */
1429