xref: /freebsd/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c (revision 878ed22696d5402cabd6f90932cc970ab912b7b0)
1878ed226SJulian Elischer /*
2878ed226SJulian Elischer  * ng_hci_ulpi.c
3878ed226SJulian Elischer  *
4878ed226SJulian Elischer  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
5878ed226SJulian Elischer  * All rights reserved.
6878ed226SJulian Elischer  *
7878ed226SJulian Elischer  * Redistribution and use in source and binary forms, with or without
8878ed226SJulian Elischer  * modification, are permitted provided that the following conditions
9878ed226SJulian Elischer  * are met:
10878ed226SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
11878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
12878ed226SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
13878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
14878ed226SJulian Elischer  *    documentation and/or other materials provided with the distribution.
15878ed226SJulian Elischer  *
16878ed226SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17878ed226SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18878ed226SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19878ed226SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20878ed226SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21878ed226SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22878ed226SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23878ed226SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24878ed226SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25878ed226SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26878ed226SJulian Elischer  * SUCH DAMAGE.
27878ed226SJulian Elischer  *
28878ed226SJulian Elischer  * $Id: ng_hci_ulpi.c,v 1.14 2002/11/12 22:35:40 max Exp $
29878ed226SJulian Elischer  * $FreeBSD$
30878ed226SJulian Elischer  */
31878ed226SJulian Elischer 
32878ed226SJulian Elischer #include <sys/param.h>
33878ed226SJulian Elischer #include <sys/systm.h>
34878ed226SJulian Elischer #include <sys/kernel.h>
35878ed226SJulian Elischer #include <sys/endian.h>
36878ed226SJulian Elischer #include <sys/malloc.h>
37878ed226SJulian Elischer #include <sys/mbuf.h>
38878ed226SJulian Elischer #include <sys/queue.h>
39878ed226SJulian Elischer #include <netgraph/ng_message.h>
40878ed226SJulian Elischer #include <netgraph/netgraph.h>
41878ed226SJulian Elischer #include "ng_bluetooth.h"
42878ed226SJulian Elischer #include "ng_hci.h"
43878ed226SJulian Elischer #include "ng_hci_var.h"
44878ed226SJulian Elischer #include "ng_hci_cmds.h"
45878ed226SJulian Elischer #include "ng_hci_evnt.h"
46878ed226SJulian Elischer #include "ng_hci_ulpi.h"
47878ed226SJulian Elischer #include "ng_hci_misc.h"
48878ed226SJulian Elischer 
49878ed226SJulian Elischer /******************************************************************************
50878ed226SJulian Elischer  ******************************************************************************
51878ed226SJulian Elischer  **                 Upper Layer Protocol Interface module
52878ed226SJulian Elischer  ******************************************************************************
53878ed226SJulian Elischer  ******************************************************************************/
54878ed226SJulian Elischer 
55878ed226SJulian Elischer static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
56878ed226SJulian Elischer static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
57878ed226SJulian Elischer 
58878ed226SJulian Elischer /*
59878ed226SJulian Elischer  * Process LP_ConnectReq event from the upper layer protocol
60878ed226SJulian Elischer  */
61878ed226SJulian Elischer 
62878ed226SJulian Elischer int
63878ed226SJulian Elischer ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
64878ed226SJulian Elischer {
65878ed226SJulian Elischer 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
66878ed226SJulian Elischer 		NG_HCI_WARN(
67878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n",
68878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->state);
69878ed226SJulian Elischer 
70878ed226SJulian Elischer 		NG_FREE_ITEM(item);
71878ed226SJulian Elischer 
72878ed226SJulian Elischer 		return (ENXIO);
73878ed226SJulian Elischer 	}
74878ed226SJulian Elischer 
75878ed226SJulian Elischer 	if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
76878ed226SJulian Elischer 		NG_HCI_ALERT(
77878ed226SJulian Elischer "%s: %s - invalid LP_ConnectReq message size=%d\n",
78878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
79878ed226SJulian Elischer 			NGI_MSG(item)->header.arglen);
80878ed226SJulian Elischer 
81878ed226SJulian Elischer 		NG_FREE_ITEM(item);
82878ed226SJulian Elischer 
83878ed226SJulian Elischer 		return (EMSGSIZE);
84878ed226SJulian Elischer 	}
85878ed226SJulian Elischer 
86878ed226SJulian Elischer 	if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL)
87878ed226SJulian Elischer 		return (ng_hci_lp_acl_con_req(unit, item, hook));
88878ed226SJulian Elischer 
89878ed226SJulian Elischer 	if (hook != unit->sco) {
90878ed226SJulian Elischer 		NG_HCI_WARN(
91878ed226SJulian Elischer "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
92878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), hook);
93878ed226SJulian Elischer 
94878ed226SJulian Elischer 		NG_FREE_ITEM(item);
95878ed226SJulian Elischer 
96878ed226SJulian Elischer 		return (EINVAL);
97878ed226SJulian Elischer 	}
98878ed226SJulian Elischer 
99878ed226SJulian Elischer 	return (ng_hci_lp_sco_con_req(unit, item, hook));
100878ed226SJulian Elischer } /* ng_hci_lp_con_req */
101878ed226SJulian Elischer 
102878ed226SJulian Elischer /*
103878ed226SJulian Elischer  * Request to create new ACL connection
104878ed226SJulian Elischer  */
105878ed226SJulian Elischer 
106878ed226SJulian Elischer static int
107878ed226SJulian Elischer ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
108878ed226SJulian Elischer {
109878ed226SJulian Elischer 	struct acl_con_req {
110878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
111878ed226SJulian Elischer 		ng_hci_create_con_cp	 cp;
112878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
113878ed226SJulian Elischer 	ng_hci_lp_con_req_ep		*ep = NULL;
114878ed226SJulian Elischer 	ng_hci_unit_con_p		 con = NULL;
115878ed226SJulian Elischer 	ng_hci_neighbor_t		*n = NULL;
116878ed226SJulian Elischer 	struct mbuf			*m = NULL;
117878ed226SJulian Elischer 	int				 error = 0;
118878ed226SJulian Elischer 
119878ed226SJulian Elischer 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
120878ed226SJulian Elischer 
121878ed226SJulian Elischer 	/*
122878ed226SJulian Elischer 	 * Only one ACL connection can exist between each pair of units.
123878ed226SJulian Elischer 	 * So try to find ACL connection descriptor (in any state) that
124878ed226SJulian Elischer 	 * has requested remote BD_ADDR.
125878ed226SJulian Elischer 	 *
126878ed226SJulian Elischer 	 * Two cases:
127878ed226SJulian Elischer 	 *
128878ed226SJulian Elischer 	 * 1) We do not have connection to the remote unit. This is simple.
129878ed226SJulian Elischer 	 *    Just create new connection descriptor and send HCI command to
130878ed226SJulian Elischer 	 *    create new connection.
131878ed226SJulian Elischer 	 *
132878ed226SJulian Elischer 	 * 2) We do have connection descriptor. We need to check connection
133878ed226SJulian Elischer 	 *    state:
134878ed226SJulian Elischer 	 *
135878ed226SJulian Elischer 	 * 2.1) NG_HCI_CON_CLOSED mean we are in the process of closing
136878ed226SJulian Elischer 	 *      connection to the remote unit. We will reject connection
137878ed226SJulian Elischer 	 *      request until connection is closed.
138878ed226SJulian Elischer 	 *
139878ed226SJulian Elischer 	 * 2.2) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
140878ed226SJulian Elischer 	 *      accepting connection from the remote unit. This is a race
141878ed226SJulian Elischer 	 *      condition. We will ignore this message.
142878ed226SJulian Elischer 	 *
143878ed226SJulian Elischer 	 * 2.3) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
144878ed226SJulian Elischer 	 *      requested connection or we just accepted it. In any case
145878ed226SJulian Elischer 	 *      all we need to do here is set appropriate notification bit
146878ed226SJulian Elischer 	 *      and wait.
147878ed226SJulian Elischer 	 *
148878ed226SJulian Elischer 	 * 2.4) NG_HCI_CON_OPEN means connection is open. Just reply back
149878ed226SJulian Elischer 	 *      and let upper layer know that we have connection already.
150878ed226SJulian Elischer 	 */
151878ed226SJulian Elischer 
152878ed226SJulian Elischer 	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
153878ed226SJulian Elischer 	if (con != NULL) {
154878ed226SJulian Elischer 		switch (con->state) {
155878ed226SJulian Elischer 		case NG_HCI_CON_CLOSED:
156878ed226SJulian Elischer 			error = EBUSY;
157878ed226SJulian Elischer 			break;
158878ed226SJulian Elischer 
159878ed226SJulian Elischer 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
160878ed226SJulian Elischer 			error = EALREADY;
161878ed226SJulian Elischer 			break;
162878ed226SJulian Elischer 
163878ed226SJulian Elischer 		case NG_HCI_CON_W4_CONN_COMPLETE:
164878ed226SJulian Elischer 			if (hook == unit->acl)
165878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
166878ed226SJulian Elischer 			else
167878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
168878ed226SJulian Elischer 			break;
169878ed226SJulian Elischer 
170878ed226SJulian Elischer 		case NG_HCI_CON_OPEN: {
171878ed226SJulian Elischer 			struct ng_mesg		*msg = NULL;
172878ed226SJulian Elischer 			ng_hci_lp_con_cfm_ep	*cfm = NULL;
173878ed226SJulian Elischer 
174878ed226SJulian Elischer 			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
175878ed226SJulian Elischer 				NGI_GET_MSG(item, msg);
176878ed226SJulian Elischer 				NG_FREE_MSG(msg);
177878ed226SJulian Elischer 
178878ed226SJulian Elischer 				NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
179878ed226SJulian Elischer 					NGM_HCI_LP_CON_CFM, sizeof(*cfm),
180878ed226SJulian Elischer 					M_NOWAIT);
181878ed226SJulian Elischer 				if (msg != NULL) {
182878ed226SJulian Elischer 					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
183878ed226SJulian Elischer 					cfm->status = 0;
184878ed226SJulian Elischer 					cfm->link_type = con->link_type;
185878ed226SJulian Elischer 					cfm->con_handle = con->con_handle;
186878ed226SJulian Elischer 					bcopy(&con->bdaddr, &cfm->bdaddr,
187878ed226SJulian Elischer 						sizeof(cfm->bdaddr));
188878ed226SJulian Elischer 
189878ed226SJulian Elischer 					/*
190878ed226SJulian Elischer 					 * This will forward item back to
191878ed226SJulian Elischer 					 * sender and set item to NULL
192878ed226SJulian Elischer 					 */
193878ed226SJulian Elischer 
194878ed226SJulian Elischer 					_NGI_MSG(item) = msg;
195878ed226SJulian Elischer 					NG_FWD_ITEM_HOOK(error, item, hook);
196878ed226SJulian Elischer 				} else
197878ed226SJulian Elischer 					error = ENOMEM;
198878ed226SJulian Elischer 			} else
199878ed226SJulian Elischer 				NG_HCI_INFO(
200878ed226SJulian Elischer "%s: %s - Source hook is not valid, hook=%p\n",
201878ed226SJulian Elischer 					__func__, NG_NODE_NAME(unit->node),
202878ed226SJulian Elischer 					hook);
203878ed226SJulian Elischer 			} break;
204878ed226SJulian Elischer 
205878ed226SJulian Elischer 		default:
206878ed226SJulian Elischer 			KASSERT(0,
207878ed226SJulian Elischer ("%s: %s - Invalid connection state=%d\n",
208878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node),con->state));
209878ed226SJulian Elischer 
210878ed226SJulian Elischer 			error = EINVAL;
211878ed226SJulian Elischer 			break;
212878ed226SJulian Elischer 		}
213878ed226SJulian Elischer 
214878ed226SJulian Elischer 		goto out;
215878ed226SJulian Elischer 	}
216878ed226SJulian Elischer 
217878ed226SJulian Elischer 	/*
218878ed226SJulian Elischer 	 * If we got here then we need to create new ACL connection descriptor
219878ed226SJulian Elischer 	 * and submit HCI command. First create new connection desriptor, set
220878ed226SJulian Elischer 	 * bdaddr and notification flags.
221878ed226SJulian Elischer 	 */
222878ed226SJulian Elischer 
223878ed226SJulian Elischer 	con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
224878ed226SJulian Elischer 	if (con == NULL) {
225878ed226SJulian Elischer 		error = ENOMEM;
226878ed226SJulian Elischer 		goto out;
227878ed226SJulian Elischer 	}
228878ed226SJulian Elischer 
229878ed226SJulian Elischer 	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
230878ed226SJulian Elischer 
231878ed226SJulian Elischer 	/*
232878ed226SJulian Elischer 	 * Create HCI command
233878ed226SJulian Elischer 	 */
234878ed226SJulian Elischer 
235878ed226SJulian Elischer 	MGETHDR(m, M_DONTWAIT, MT_DATA);
236878ed226SJulian Elischer 	if (m == NULL) {
237878ed226SJulian Elischer 		ng_hci_free_con(con);
238878ed226SJulian Elischer 		error = ENOBUFS;
239878ed226SJulian Elischer 		goto out;
240878ed226SJulian Elischer 	}
241878ed226SJulian Elischer 
242878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
243878ed226SJulian Elischer 	req = mtod(m, struct acl_con_req *);
244878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
245878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
246878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
247878ed226SJulian Elischer 					NG_HCI_OCF_CREATE_CON));
248878ed226SJulian Elischer 
249878ed226SJulian Elischer 	bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
250878ed226SJulian Elischer 
251878ed226SJulian Elischer 	req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
252878ed226SJulian Elischer 	if (unit->features[0] & NG_HCI_LMP_3SLOT)
253878ed226SJulian Elischer 		req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
254878ed226SJulian Elischer 	if (unit->features[0] & NG_HCI_LMP_5SLOT)
255878ed226SJulian Elischer 		req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
256878ed226SJulian Elischer 
257878ed226SJulian Elischer 	req->cp.pkt_type &= unit->packet_mask;
258878ed226SJulian Elischer 	if (req->cp.pkt_type == 0)
259878ed226SJulian Elischer 		req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
260878ed226SJulian Elischer 
261878ed226SJulian Elischer 	req->cp.pkt_type = htole16(req->cp.pkt_type);
262878ed226SJulian Elischer 
263878ed226SJulian Elischer 	if (unit->features[0] & NG_HCI_LMP_SWITCH)
264878ed226SJulian Elischer 		req->cp.accept_role_switch = 1;
265878ed226SJulian Elischer 	else
266878ed226SJulian Elischer 		req->cp.accept_role_switch = 0;
267878ed226SJulian Elischer 
268878ed226SJulian Elischer 	/*
269878ed226SJulian Elischer 	 * We may speed up connect by specifying valid parameters.
270878ed226SJulian Elischer 	 * So check the neighbor cache.
271878ed226SJulian Elischer 	 */
272878ed226SJulian Elischer 
273878ed226SJulian Elischer 	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
274878ed226SJulian Elischer 	if (n == NULL) {
275878ed226SJulian Elischer 		req->cp.page_scan_rep_mode = 0;
276878ed226SJulian Elischer 		req->cp.page_scan_mode = 0;
277878ed226SJulian Elischer 		req->cp.clock_offset = 0;
278878ed226SJulian Elischer 	} else {
279878ed226SJulian Elischer 		req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
280878ed226SJulian Elischer 		req->cp.page_scan_mode = n->page_scan_mode;
281878ed226SJulian Elischer 		req->cp.clock_offset = htole16(n->clock_offset);
282878ed226SJulian Elischer 	}
283878ed226SJulian Elischer 
284878ed226SJulian Elischer 	/*
285878ed226SJulian Elischer 	 * Adust connection state
286878ed226SJulian Elischer 	 */
287878ed226SJulian Elischer 
288878ed226SJulian Elischer 	if (hook == unit->acl)
289878ed226SJulian Elischer 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
290878ed226SJulian Elischer 	else
291878ed226SJulian Elischer 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
292878ed226SJulian Elischer 
293878ed226SJulian Elischer 	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
294878ed226SJulian Elischer 	ng_hci_con_timeout(con);
295878ed226SJulian Elischer 
296878ed226SJulian Elischer 	/*
297878ed226SJulian Elischer 	 * Queue and send HCI command
298878ed226SJulian Elischer 	 */
299878ed226SJulian Elischer 
300878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
301878ed226SJulian Elischer 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
302878ed226SJulian Elischer 		error = ng_hci_send_command(unit);
303878ed226SJulian Elischer out:
304878ed226SJulian Elischer 	if (item != NULL)
305878ed226SJulian Elischer 		NG_FREE_ITEM(item);
306878ed226SJulian Elischer 
307878ed226SJulian Elischer 	return (error);
308878ed226SJulian Elischer } /* ng_hci_lp_acl_con_req */
309878ed226SJulian Elischer 
310878ed226SJulian Elischer /*
311878ed226SJulian Elischer  * Request to create new SCO connection
312878ed226SJulian Elischer  */
313878ed226SJulian Elischer 
314878ed226SJulian Elischer static int
315878ed226SJulian Elischer ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
316878ed226SJulian Elischer {
317878ed226SJulian Elischer 	struct sco_con_req {
318878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
319878ed226SJulian Elischer 		ng_hci_add_sco_con_cp	 cp;
320878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
321878ed226SJulian Elischer 	ng_hci_lp_con_req_ep		*ep = NULL;
322878ed226SJulian Elischer 	ng_hci_unit_con_p		 acl_con = NULL, sco_con = NULL;
323878ed226SJulian Elischer 	struct mbuf			*m = NULL;
324878ed226SJulian Elischer 	int				 error = 0;
325878ed226SJulian Elischer 
326878ed226SJulian Elischer 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
327878ed226SJulian Elischer 
328878ed226SJulian Elischer 	/*
329878ed226SJulian Elischer 	 * SCO connection without ACL link
330878ed226SJulian Elischer 	 *
331878ed226SJulian Elischer 	 * If upper layer requests SCO connection and there is no open ACL
332878ed226SJulian Elischer 	 * connection to the desired remote unit, we will reject the request.
333878ed226SJulian Elischer 	 */
334878ed226SJulian Elischer 
335878ed226SJulian Elischer 	LIST_FOREACH(acl_con, &unit->con_list, next)
336878ed226SJulian Elischer 		if (acl_con->link_type == NG_HCI_LINK_ACL &&
337878ed226SJulian Elischer 		    acl_con->state == NG_HCI_CON_OPEN &&
338878ed226SJulian Elischer 		    bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
339878ed226SJulian Elischer 			break;
340878ed226SJulian Elischer 
341878ed226SJulian Elischer 	if (acl_con == NULL) {
342878ed226SJulian Elischer 		NG_HCI_INFO(
343878ed226SJulian Elischer "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
344878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
345878ed226SJulian Elischer 			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
346878ed226SJulian Elischer 			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
347878ed226SJulian Elischer 
348878ed226SJulian Elischer 		error = ENOENT;
349878ed226SJulian Elischer 		goto out;
350878ed226SJulian Elischer 	}
351878ed226SJulian Elischer 
352878ed226SJulian Elischer 	/*
353878ed226SJulian Elischer 	 * Multiple SCO connections can exist between the same pair of units.
354878ed226SJulian Elischer 	 * We assume that multiple SCO connections have to be opened one after
355878ed226SJulian Elischer 	 * another.
356878ed226SJulian Elischer 	 *
357878ed226SJulian Elischer 	 * Try to find SCO connection descriptor that matches the following:
358878ed226SJulian Elischer 	 *
359878ed226SJulian Elischer 	 * 1) sco_con->link_type == NG_HCI_LINK_SCO
360878ed226SJulian Elischer 	 *
361878ed226SJulian Elischer 	 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
362878ed226SJulian Elischer 	 *    sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
363878ed226SJulian Elischer 	 *
364878ed226SJulian Elischer 	 * 3) sco_con->bdaddr == ep->bdaddr
365878ed226SJulian Elischer 	 *
366878ed226SJulian Elischer 	 * Two cases:
367878ed226SJulian Elischer 	 *
368878ed226SJulian Elischer 	 * 1) We do not have connection descriptor. This is simple. Just
369878ed226SJulian Elischer 	 *    create new connection and submit Add_SCO_Connection command.
370878ed226SJulian Elischer 	 *
371878ed226SJulian Elischer 	 * 2) We do have connection descriptor. We need to check the state.
372878ed226SJulian Elischer 	 *
373878ed226SJulian Elischer 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
374878ed226SJulian Elischer 	 *      connection from the remote unit. This is a race condition and
375878ed226SJulian Elischer 	 *      we will ignore the request.
376878ed226SJulian Elischer 	 *
377878ed226SJulian Elischer 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
378878ed226SJulian Elischer 	 *      connection or we just accepted it.
379878ed226SJulian Elischer 	 *
380878ed226SJulian Elischer 	 * XXX FIXME what to do with connection(s) in CLOSED state?
381878ed226SJulian Elischer 	 */
382878ed226SJulian Elischer 
383878ed226SJulian Elischer 	LIST_FOREACH(sco_con, &unit->con_list, next)
384878ed226SJulian Elischer 		if (sco_con->link_type == NG_HCI_LINK_SCO &&
385878ed226SJulian Elischer 		    (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
386878ed226SJulian Elischer 		     sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
387878ed226SJulian Elischer 		    bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
388878ed226SJulian Elischer 			break;
389878ed226SJulian Elischer 
390878ed226SJulian Elischer 	if (sco_con != NULL) {
391878ed226SJulian Elischer 		switch (sco_con->state) {
392878ed226SJulian Elischer 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
393878ed226SJulian Elischer 			error = EALREADY;
394878ed226SJulian Elischer 			break;
395878ed226SJulian Elischer 
396878ed226SJulian Elischer 		case NG_HCI_CON_W4_CONN_COMPLETE:
397878ed226SJulian Elischer 			sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
398878ed226SJulian Elischer 			break;
399878ed226SJulian Elischer 
400878ed226SJulian Elischer 		default:
401878ed226SJulian Elischer 			KASSERT(0,
402878ed226SJulian Elischer ("%s: %s - Inavalid connection state=%d\n",
403878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node),
404878ed226SJulian Elischer 				sco_con->state));
405878ed226SJulian Elischer 
406878ed226SJulian Elischer 			error = EINVAL;
407878ed226SJulian Elischer 			break;
408878ed226SJulian Elischer 		}
409878ed226SJulian Elischer 
410878ed226SJulian Elischer 		goto out;
411878ed226SJulian Elischer 	}
412878ed226SJulian Elischer 
413878ed226SJulian Elischer 	/*
414878ed226SJulian Elischer 	 * If we got here then we need to create new SCO connection descriptor
415878ed226SJulian Elischer 	 * and submit HCI command.
416878ed226SJulian Elischer 	 */
417878ed226SJulian Elischer 
418878ed226SJulian Elischer 	sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
419878ed226SJulian Elischer 	if (sco_con == NULL) {
420878ed226SJulian Elischer 		error = ENOMEM;
421878ed226SJulian Elischer 		goto out;
422878ed226SJulian Elischer 	}
423878ed226SJulian Elischer 
424878ed226SJulian Elischer 	bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
425878ed226SJulian Elischer 
426878ed226SJulian Elischer 	/*
427878ed226SJulian Elischer 	 * Create HCI command
428878ed226SJulian Elischer 	 */
429878ed226SJulian Elischer 
430878ed226SJulian Elischer 	MGETHDR(m, M_DONTWAIT, MT_DATA);
431878ed226SJulian Elischer 	if (m == NULL) {
432878ed226SJulian Elischer 		ng_hci_free_con(sco_con);
433878ed226SJulian Elischer 		error = ENOBUFS;
434878ed226SJulian Elischer 		goto out;
435878ed226SJulian Elischer 	}
436878ed226SJulian Elischer 
437878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
438878ed226SJulian Elischer 	req = mtod(m, struct sco_con_req *);
439878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
440878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
441878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
442878ed226SJulian Elischer 					NG_HCI_OCF_ADD_SCO_CON));
443878ed226SJulian Elischer 
444878ed226SJulian Elischer 	req->cp.con_handle = htole16(acl_con->con_handle);
445878ed226SJulian Elischer 
446878ed226SJulian Elischer 	req->cp.pkt_type = NG_HCI_PKT_HV1;
447878ed226SJulian Elischer 	if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
448878ed226SJulian Elischer 		req->cp.pkt_type |= NG_HCI_PKT_HV2;
449878ed226SJulian Elischer 	if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
450878ed226SJulian Elischer 		req->cp.pkt_type |= NG_HCI_PKT_HV3;
451878ed226SJulian Elischer 
452878ed226SJulian Elischer 	req->cp.pkt_type &= unit->packet_mask;
453878ed226SJulian Elischer 	if (req->cp.pkt_type == 0)
454878ed226SJulian Elischer 		req->cp.pkt_type = NG_HCI_PKT_HV1;
455878ed226SJulian Elischer 
456878ed226SJulian Elischer 	req->cp.pkt_type = htole16(req->cp.pkt_type);
457878ed226SJulian Elischer 
458878ed226SJulian Elischer 	/*
459878ed226SJulian Elischer 	 * Adust connection state
460878ed226SJulian Elischer 	 */
461878ed226SJulian Elischer 
462878ed226SJulian Elischer 	sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
463878ed226SJulian Elischer 
464878ed226SJulian Elischer 	sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
465878ed226SJulian Elischer 	ng_hci_con_timeout(sco_con);
466878ed226SJulian Elischer 
467878ed226SJulian Elischer 	/*
468878ed226SJulian Elischer 	 * Queue and send HCI command
469878ed226SJulian Elischer 	 */
470878ed226SJulian Elischer 
471878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
472878ed226SJulian Elischer 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
473878ed226SJulian Elischer 		error = ng_hci_send_command(unit);
474878ed226SJulian Elischer out:
475878ed226SJulian Elischer 	NG_FREE_ITEM(item);
476878ed226SJulian Elischer 
477878ed226SJulian Elischer 	return (error);
478878ed226SJulian Elischer } /* ng_hci_lp_sco_con_req */
479878ed226SJulian Elischer 
480878ed226SJulian Elischer /*
481878ed226SJulian Elischer  * Process LP_DisconnectReq event from the upper layer protocol
482878ed226SJulian Elischer  *
483878ed226SJulian Elischer  * XXX XXX XXX
484878ed226SJulian Elischer  *
485878ed226SJulian Elischer  * NOTE: This is NOT defined by Bluetooth specification (why?) But i think
486878ed226SJulian Elischer  * this might be useful (at least for testing), so please do not depend on
487878ed226SJulian Elischer  * this interface.
488878ed226SJulian Elischer  */
489878ed226SJulian Elischer 
490878ed226SJulian Elischer int
491878ed226SJulian Elischer ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
492878ed226SJulian Elischer {
493878ed226SJulian Elischer 	struct discon_req {
494878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
495878ed226SJulian Elischer 		ng_hci_discon_cp	 cp;
496878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
497878ed226SJulian Elischer 	ng_hci_lp_discon_req_ep		*ep = NULL;
498878ed226SJulian Elischer 	ng_hci_unit_con_p		 con = NULL;
499878ed226SJulian Elischer 	struct mbuf			*m = NULL;
500878ed226SJulian Elischer 	int				 error = 0;
501878ed226SJulian Elischer 
502878ed226SJulian Elischer 	/* Check if unit is ready */
503878ed226SJulian Elischer 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
504878ed226SJulian Elischer 		NG_HCI_WARN(
505878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n",
506878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->state);
507878ed226SJulian Elischer 
508878ed226SJulian Elischer 		error = ENXIO;
509878ed226SJulian Elischer 		goto out;
510878ed226SJulian Elischer 	}
511878ed226SJulian Elischer 
512878ed226SJulian Elischer 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
513878ed226SJulian Elischer 		NG_HCI_ALERT(
514878ed226SJulian Elischer "%s: %s - invalid LP_DisconnectReq message size=%d\n",
515878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
516878ed226SJulian Elischer 			NGI_MSG(item)->header.arglen);
517878ed226SJulian Elischer 
518878ed226SJulian Elischer 		error = EMSGSIZE;
519878ed226SJulian Elischer 		goto out;
520878ed226SJulian Elischer 	}
521878ed226SJulian Elischer 
522878ed226SJulian Elischer 	ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
523878ed226SJulian Elischer 
524878ed226SJulian Elischer 	con = ng_hci_con_by_handle(unit, ep->con_handle);
525878ed226SJulian Elischer 	if (con == NULL) {
526878ed226SJulian Elischer 		NG_HCI_ERR(
527878ed226SJulian Elischer "%s: %s - invalid connection handle=%d\n",
528878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
529878ed226SJulian Elischer 
530878ed226SJulian Elischer 		error = ENOENT;
531878ed226SJulian Elischer 		goto out;
532878ed226SJulian Elischer 	}
533878ed226SJulian Elischer 
534878ed226SJulian Elischer 	if (con->state != NG_HCI_CON_OPEN) {
535878ed226SJulian Elischer 		NG_HCI_ERR(
536878ed226SJulian Elischer "%s: %s - invalid connection state=%d, handle=%d\n",
537878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), con->state,
538878ed226SJulian Elischer 			ep->con_handle);
539878ed226SJulian Elischer 
540878ed226SJulian Elischer 		error = EINVAL;
541878ed226SJulian Elischer 		goto out;
542878ed226SJulian Elischer 	}
543878ed226SJulian Elischer 
544878ed226SJulian Elischer 	/*
545878ed226SJulian Elischer 	 * Create HCI command
546878ed226SJulian Elischer 	 */
547878ed226SJulian Elischer 
548878ed226SJulian Elischer 	MGETHDR(m, M_DONTWAIT, MT_DATA);
549878ed226SJulian Elischer 	if (m == NULL) {
550878ed226SJulian Elischer 		error = ENOBUFS;
551878ed226SJulian Elischer 		goto out;
552878ed226SJulian Elischer 	}
553878ed226SJulian Elischer 
554878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
555878ed226SJulian Elischer 	req = mtod(m, struct discon_req *);
556878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
557878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
558878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
559878ed226SJulian Elischer 							NG_HCI_OCF_DISCON));
560878ed226SJulian Elischer 
561878ed226SJulian Elischer 	req->cp.con_handle = htole16(ep->con_handle);
562878ed226SJulian Elischer 	req->cp.reason = ep->reason;
563878ed226SJulian Elischer 
564878ed226SJulian Elischer 	/*
565878ed226SJulian Elischer 	 * Adjust connection state
566878ed226SJulian Elischer 	 */
567878ed226SJulian Elischer 
568878ed226SJulian Elischer 	con->state = NG_HCI_CON_CLOSED;
569878ed226SJulian Elischer 	ng_hci_con_timeout(con);
570878ed226SJulian Elischer 
571878ed226SJulian Elischer 	/*
572878ed226SJulian Elischer 	 * Queue and send HCI command
573878ed226SJulian Elischer 	 */
574878ed226SJulian Elischer 
575878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
576878ed226SJulian Elischer 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
577878ed226SJulian Elischer 		error = ng_hci_send_command(unit);
578878ed226SJulian Elischer out:
579878ed226SJulian Elischer 	NG_FREE_ITEM(item);
580878ed226SJulian Elischer 
581878ed226SJulian Elischer 	return (error);
582878ed226SJulian Elischer } /* ng_hci_lp_discon_req */
583878ed226SJulian Elischer 
584878ed226SJulian Elischer /*
585878ed226SJulian Elischer  * Send LP_ConnectCfm event to the upper layer protocol
586878ed226SJulian Elischer  */
587878ed226SJulian Elischer 
588878ed226SJulian Elischer int
589878ed226SJulian Elischer ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
590878ed226SJulian Elischer {
591878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
592878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
593878ed226SJulian Elischer 	ng_hci_lp_con_cfm_ep	*ep = NULL;
594878ed226SJulian Elischer 	int			 error;
595878ed226SJulian Elischer 
596878ed226SJulian Elischer 	/*
597878ed226SJulian Elischer 	 * Check who wants to be notified. For ACL links both ACL and SCO
598878ed226SJulian Elischer 	 * upstream hooks will be notified (if required). For SCO links
599878ed226SJulian Elischer 	 * only SCO upstream hook will receive notification
600878ed226SJulian Elischer 	 */
601878ed226SJulian Elischer 
602878ed226SJulian Elischer 	if (con->link_type == NG_HCI_LINK_ACL &&
603878ed226SJulian Elischer 	    con->flags & NG_HCI_CON_NOTIFY_ACL) {
604878ed226SJulian Elischer 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
605878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
606878ed226SJulian Elischer 				sizeof(*ep), M_NOWAIT);
607878ed226SJulian Elischer 			if (msg != NULL) {
608878ed226SJulian Elischer 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
609878ed226SJulian Elischer 				ep->status = status;
610878ed226SJulian Elischer 				ep->link_type = con->link_type;
611878ed226SJulian Elischer 				ep->con_handle = con->con_handle;
612878ed226SJulian Elischer 				bcopy(&con->bdaddr, &ep->bdaddr,
613878ed226SJulian Elischer 					sizeof(ep->bdaddr));
614878ed226SJulian Elischer 
615878ed226SJulian Elischer 				NG_SEND_MSG_HOOK(error, unit->node, msg,
616878ed226SJulian Elischer 					unit->acl, NULL);
617878ed226SJulian Elischer 			}
618878ed226SJulian Elischer 		} else
619878ed226SJulian Elischer 			NG_HCI_INFO(
620878ed226SJulian Elischer "%s: %s - ACL hook not valid, hook=%p\n",
621878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), unit->acl);
622878ed226SJulian Elischer 
623878ed226SJulian Elischer 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
624878ed226SJulian Elischer 	}
625878ed226SJulian Elischer 
626878ed226SJulian Elischer 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
627878ed226SJulian Elischer 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
628878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
629878ed226SJulian Elischer 				sizeof(*ep), M_NOWAIT);
630878ed226SJulian Elischer 			if (msg != NULL) {
631878ed226SJulian Elischer 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
632878ed226SJulian Elischer 				ep->status = status;
633878ed226SJulian Elischer 				ep->link_type = con->link_type;
634878ed226SJulian Elischer 				ep->con_handle = con->con_handle;
635878ed226SJulian Elischer 				bcopy(&con->bdaddr, &ep->bdaddr,
636878ed226SJulian Elischer 					sizeof(ep->bdaddr));
637878ed226SJulian Elischer 
638878ed226SJulian Elischer 				NG_SEND_MSG_HOOK(error, unit->node, msg,
639878ed226SJulian Elischer 					unit->sco, NULL);
640878ed226SJulian Elischer 			}
641878ed226SJulian Elischer 		} else
642878ed226SJulian Elischer 			NG_HCI_INFO(
643878ed226SJulian Elischer "%s: %s - SCO hook not valid, hook=%p\n",
644878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), unit->acl);
645878ed226SJulian Elischer 
646878ed226SJulian Elischer 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
647878ed226SJulian Elischer 	}
648878ed226SJulian Elischer 
649878ed226SJulian Elischer 	return (0);
650878ed226SJulian Elischer } /* ng_hci_lp_con_cfm */
651878ed226SJulian Elischer 
652878ed226SJulian Elischer /*
653878ed226SJulian Elischer  * Send LP_ConnectInd event to the upper layer protocol
654878ed226SJulian Elischer  */
655878ed226SJulian Elischer 
656878ed226SJulian Elischer int
657878ed226SJulian Elischer ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
658878ed226SJulian Elischer {
659878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
660878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
661878ed226SJulian Elischer 	ng_hci_lp_con_ind_ep	*ep = NULL;
662878ed226SJulian Elischer 	hook_p			 hook = NULL;
663878ed226SJulian Elischer 	int			 error = 0;
664878ed226SJulian Elischer 
665878ed226SJulian Elischer 	/*
666878ed226SJulian Elischer 	 * Connection_Request event is generated for specific link type.
667878ed226SJulian Elischer 	 * Use link_type to select upstream hook.
668878ed226SJulian Elischer 	 */
669878ed226SJulian Elischer 
670878ed226SJulian Elischer 	if (con->link_type == NG_HCI_LINK_ACL)
671878ed226SJulian Elischer 		hook = unit->acl;
672878ed226SJulian Elischer 	else
673878ed226SJulian Elischer 		hook = unit->sco;
674878ed226SJulian Elischer 
675878ed226SJulian Elischer 	if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
676878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
677878ed226SJulian Elischer 			sizeof(*ep), M_NOWAIT);
678878ed226SJulian Elischer 		if (msg == NULL)
679878ed226SJulian Elischer 			return (ENOMEM);
680878ed226SJulian Elischer 
681878ed226SJulian Elischer 		ep = (ng_hci_lp_con_ind_ep *)(msg->data);
682878ed226SJulian Elischer 		ep->link_type = con->link_type;
683878ed226SJulian Elischer 		bcopy(uclass, ep->uclass, sizeof(ep->uclass));
684878ed226SJulian Elischer 		bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
685878ed226SJulian Elischer 
686878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, unit->node, msg, hook, NULL);
687878ed226SJulian Elischer 	} else {
688878ed226SJulian Elischer 		NG_HCI_WARN(
689878ed226SJulian Elischer "%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
690878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), hook);
691878ed226SJulian Elischer 
692878ed226SJulian Elischer 		error = ENOTCONN;
693878ed226SJulian Elischer 	}
694878ed226SJulian Elischer 
695878ed226SJulian Elischer 	return (error);
696878ed226SJulian Elischer } /* ng_hci_lp_con_ind */
697878ed226SJulian Elischer 
698878ed226SJulian Elischer /*
699878ed226SJulian Elischer  * Process LP_ConnectRsp event from the upper layer protocol
700878ed226SJulian Elischer  */
701878ed226SJulian Elischer 
702878ed226SJulian Elischer int
703878ed226SJulian Elischer ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
704878ed226SJulian Elischer {
705878ed226SJulian Elischer 	struct con_rsp_req {
706878ed226SJulian Elischer 		ng_hci_cmd_pkt_t		 hdr;
707878ed226SJulian Elischer 		union {
708878ed226SJulian Elischer 			ng_hci_accept_con_cp	 acc;
709878ed226SJulian Elischer 			ng_hci_reject_con_cp	 rej;
710878ed226SJulian Elischer 		} __attribute__ ((packed))	 cp;
711878ed226SJulian Elischer 	} __attribute__ ((packed))		*req = NULL;
712878ed226SJulian Elischer 	ng_hci_lp_con_rsp_ep			*ep = NULL;
713878ed226SJulian Elischer 	ng_hci_unit_con_p			 con = NULL;
714878ed226SJulian Elischer 	struct mbuf				*m = NULL;
715878ed226SJulian Elischer 	int					 error = 0;
716878ed226SJulian Elischer 
717878ed226SJulian Elischer 	/* Check if unit is ready */
718878ed226SJulian Elischer 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
719878ed226SJulian Elischer 		NG_HCI_WARN(
720878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n",
721878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->state);
722878ed226SJulian Elischer 
723878ed226SJulian Elischer 		error = ENXIO;
724878ed226SJulian Elischer 		goto out;
725878ed226SJulian Elischer 	}
726878ed226SJulian Elischer 
727878ed226SJulian Elischer 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
728878ed226SJulian Elischer 		NG_HCI_ALERT(
729878ed226SJulian Elischer "%s: %s - invalid LP_ConnectRsp message size=%d\n",
730878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
731878ed226SJulian Elischer 			NGI_MSG(item)->header.arglen);
732878ed226SJulian Elischer 
733878ed226SJulian Elischer 		error = EMSGSIZE;
734878ed226SJulian Elischer 		goto out;
735878ed226SJulian Elischer 	}
736878ed226SJulian Elischer 
737878ed226SJulian Elischer 	ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
738878ed226SJulian Elischer 
739878ed226SJulian Elischer 	/*
740878ed226SJulian Elischer 	 * Here we have to deal with race. Upper layers might send conflicting
741878ed226SJulian Elischer 	 * requests. One might send Accept and other Reject. We will not try
742878ed226SJulian Elischer 	 * to solve all the problems, so first request will always win.
743878ed226SJulian Elischer 	 *
744878ed226SJulian Elischer 	 * Try to find connection that matches the following:
745878ed226SJulian Elischer 	 *
746878ed226SJulian Elischer 	 * 1) con->link_type == ep->link_type
747878ed226SJulian Elischer 	 *
748878ed226SJulian Elischer 	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
749878ed226SJulian Elischer 	 *    con->state == NG_HCI_CON_W4_CONN_COMPLETE
750878ed226SJulian Elischer 	 *
751878ed226SJulian Elischer 	 * 3) con->bdaddr == ep->bdaddr
752878ed226SJulian Elischer 	 *
753878ed226SJulian Elischer 	 * Two cases:
754878ed226SJulian Elischer 	 *
755878ed226SJulian Elischer 	 * 1) We do not have connection descriptor. Could be bogus request or
756878ed226SJulian Elischer 	 *    we have rejected connection already.
757878ed226SJulian Elischer 	 *
758878ed226SJulian Elischer 	 * 2) We do have connection descriptor. Then we need to check state:
759878ed226SJulian Elischer 	 *
760878ed226SJulian Elischer 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
761878ed226SJulian Elischer 	 *      connection and it is a first response from the upper layer.
762878ed226SJulian Elischer 	 *      if "status == 0" (Accept) then we will send Accept_Connection
763878ed226SJulian Elischer 	 *      command and change connection state to W4_CONN_COMPLETE, else
764878ed226SJulian Elischer 	 *      send reject and delete connection.
765878ed226SJulian Elischer 	 *
766878ed226SJulian Elischer 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
767878ed226SJulian Elischer 	 *      connection. If "status == 0" we just need to link request
768878ed226SJulian Elischer 	 *      and wait, else ignore Reject request.
769878ed226SJulian Elischer 	 */
770878ed226SJulian Elischer 
771878ed226SJulian Elischer 	LIST_FOREACH(con, &unit->con_list, next)
772878ed226SJulian Elischer 		if (con->link_type == ep->link_type &&
773878ed226SJulian Elischer 		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
774878ed226SJulian Elischer 		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
775878ed226SJulian Elischer 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
776878ed226SJulian Elischer 			break;
777878ed226SJulian Elischer 
778878ed226SJulian Elischer 	if (con == NULL) {
779878ed226SJulian Elischer 		/* Reject for non-existing connection is fine */
780878ed226SJulian Elischer 		error = (ep->status == 0)? ENOENT : 0;
781878ed226SJulian Elischer 		goto out;
782878ed226SJulian Elischer 	}
783878ed226SJulian Elischer 
784878ed226SJulian Elischer 	/*
785878ed226SJulian Elischer 	 * Remove connection timeout and check connection state
786878ed226SJulian Elischer 	 */
787878ed226SJulian Elischer 
788878ed226SJulian Elischer 	ng_hci_con_untimeout(con);
789878ed226SJulian Elischer 
790878ed226SJulian Elischer 	switch (con->state) {
791878ed226SJulian Elischer 	case NG_HCI_CON_W4_LP_CON_RSP:
792878ed226SJulian Elischer 
793878ed226SJulian Elischer 		/*
794878ed226SJulian Elischer 		 * Create HCI command
795878ed226SJulian Elischer 		 */
796878ed226SJulian Elischer 
797878ed226SJulian Elischer 		MGETHDR(m, M_DONTWAIT, MT_DATA);
798878ed226SJulian Elischer 		if (m == NULL) {
799878ed226SJulian Elischer 			error = ENOBUFS;
800878ed226SJulian Elischer 			goto out;
801878ed226SJulian Elischer 		}
802878ed226SJulian Elischer 
803878ed226SJulian Elischer 		req = mtod(m, struct con_rsp_req *);
804878ed226SJulian Elischer 		req->hdr.type = NG_HCI_CMD_PKT;
805878ed226SJulian Elischer 
806878ed226SJulian Elischer 		if (ep->status == 0) {
807878ed226SJulian Elischer 			req->hdr.length = sizeof(req->cp.acc);
808878ed226SJulian Elischer 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
809878ed226SJulian Elischer 							NG_HCI_OGF_LINK_CONTROL,
810878ed226SJulian Elischer 							NG_HCI_OCF_ACCEPT_CON));
811878ed226SJulian Elischer 
812878ed226SJulian Elischer 			bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
813878ed226SJulian Elischer 				sizeof(req->cp.acc.bdaddr));
814878ed226SJulian Elischer 
815878ed226SJulian Elischer 			/*
816878ed226SJulian Elischer 			 * XXX should be configurable?
817878ed226SJulian Elischer 			 *
818878ed226SJulian Elischer 			 * We are accepting connection, so if we support role
819878ed226SJulian Elischer 			 * switch then set role to NG_HCI_ROLE_MASTER and let
820878ed226SJulian Elischer 			 * LM peform role switch. Otherwise it is probably
821878ed226SJulian Elischer 			 * makes sense to remain slave. In this case LM WILL
822878ed226SJulian Elischer 			 * NOT perform role switch.
823878ed226SJulian Elischer 			 */
824878ed226SJulian Elischer 
825878ed226SJulian Elischer 			if (unit->features[0] & NG_HCI_LMP_SWITCH)
826878ed226SJulian Elischer 				req->cp.acc.role = NG_HCI_ROLE_MASTER;
827878ed226SJulian Elischer 			else
828878ed226SJulian Elischer 				req->cp.acc.role = NG_HCI_ROLE_SLAVE;
829878ed226SJulian Elischer 
830878ed226SJulian Elischer 			/*
831878ed226SJulian Elischer 			 * Adjust connection state
832878ed226SJulian Elischer 			 */
833878ed226SJulian Elischer 
834878ed226SJulian Elischer 			if (hook == unit->acl)
835878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
836878ed226SJulian Elischer 			else
837878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
838878ed226SJulian Elischer 
839878ed226SJulian Elischer 			con->state = NG_HCI_CON_W4_CONN_COMPLETE;
840878ed226SJulian Elischer 			ng_hci_con_timeout(con);
841878ed226SJulian Elischer 		} else {
842878ed226SJulian Elischer 			req->hdr.length = sizeof(req->cp.rej);
843878ed226SJulian Elischer 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
844878ed226SJulian Elischer 							NG_HCI_OGF_LINK_CONTROL,
845878ed226SJulian Elischer 							NG_HCI_OCF_REJECT_CON));
846878ed226SJulian Elischer 
847878ed226SJulian Elischer 			bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
848878ed226SJulian Elischer 				sizeof(req->cp.rej.bdaddr));
849878ed226SJulian Elischer 
850878ed226SJulian Elischer 			req->cp.rej.reason = ep->status;
851878ed226SJulian Elischer 
852878ed226SJulian Elischer 			/*
853878ed226SJulian Elischer 			 * Free connection descritor
854878ed226SJulian Elischer 			 * Item will be deleted just before return.
855878ed226SJulian Elischer 			 */
856878ed226SJulian Elischer 
857878ed226SJulian Elischer 			ng_hci_free_con(con);
858878ed226SJulian Elischer 		}
859878ed226SJulian Elischer 
860878ed226SJulian Elischer 		m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
861878ed226SJulian Elischer 
862878ed226SJulian Elischer 		/* Queue and send HCI command */
863878ed226SJulian Elischer 		NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
864878ed226SJulian Elischer 		if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
865878ed226SJulian Elischer 			error = ng_hci_send_command(unit);
866878ed226SJulian Elischer 		break;
867878ed226SJulian Elischer 
868878ed226SJulian Elischer 	case NG_HCI_CON_W4_CONN_COMPLETE:
869878ed226SJulian Elischer 		if (ep->status == 0) {
870878ed226SJulian Elischer 			if (hook == unit->acl)
871878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
872878ed226SJulian Elischer 			else
873878ed226SJulian Elischer 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
874878ed226SJulian Elischer 		} else
875878ed226SJulian Elischer 			error = EPERM;
876878ed226SJulian Elischer 		break;
877878ed226SJulian Elischer 
878878ed226SJulian Elischer 	default:
879878ed226SJulian Elischer 		KASSERT(0,
880878ed226SJulian Elischer ("%s: %s - Invalid connection state=%d\n",
881878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), con->state));
882878ed226SJulian Elischer 
883878ed226SJulian Elischer 		error = EINVAL;
884878ed226SJulian Elischer 		break;
885878ed226SJulian Elischer 	}
886878ed226SJulian Elischer out:
887878ed226SJulian Elischer 	NG_FREE_ITEM(item);
888878ed226SJulian Elischer 
889878ed226SJulian Elischer 	return (error);
890878ed226SJulian Elischer } /* ng_hci_lp_con_rsp */
891878ed226SJulian Elischer 
892878ed226SJulian Elischer /*
893878ed226SJulian Elischer  * Send LP_DisconnectInd to the upper layer protocol
894878ed226SJulian Elischer  */
895878ed226SJulian Elischer 
896878ed226SJulian Elischer int
897878ed226SJulian Elischer ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
898878ed226SJulian Elischer {
899878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
900878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
901878ed226SJulian Elischer 	ng_hci_lp_discon_ind_ep	*ep = NULL;
902878ed226SJulian Elischer 	int			 error = 0;
903878ed226SJulian Elischer 
904878ed226SJulian Elischer 	/*
905878ed226SJulian Elischer 	 * Disconnect_Complete event is generated for specific connection
906878ed226SJulian Elischer 	 * handle. For ACL connection handles both ACL and SCO upstream
907878ed226SJulian Elischer 	 * hooks will receive notification. For SCO connection handles
908878ed226SJulian Elischer 	 * only SCO upstream hook will receive notification.
909878ed226SJulian Elischer 	 */
910878ed226SJulian Elischer 
911878ed226SJulian Elischer 	if (con->link_type == NG_HCI_LINK_ACL) {
912878ed226SJulian Elischer 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
913878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
914878ed226SJulian Elischer 				NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
915878ed226SJulian Elischer 			if (msg == NULL)
916878ed226SJulian Elischer 				return (ENOMEM);
917878ed226SJulian Elischer 
918878ed226SJulian Elischer 			ep = (ng_hci_lp_discon_ind_ep *) msg->data;
919878ed226SJulian Elischer 			ep->reason = reason;
920878ed226SJulian Elischer 			ep->link_type = con->link_type;
921878ed226SJulian Elischer 			ep->con_handle = con->con_handle;
922878ed226SJulian Elischer 
923878ed226SJulian Elischer 			NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,NULL);
924878ed226SJulian Elischer 		} else
925878ed226SJulian Elischer 			NG_HCI_INFO(
926878ed226SJulian Elischer "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
927878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), unit->acl);
928878ed226SJulian Elischer 	}
929878ed226SJulian Elischer 
930878ed226SJulian Elischer 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
931878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
932878ed226SJulian Elischer 			sizeof(*ep), M_NOWAIT);
933878ed226SJulian Elischer 		if (msg == NULL)
934878ed226SJulian Elischer 			return (ENOMEM);
935878ed226SJulian Elischer 
936878ed226SJulian Elischer 		ep = (ng_hci_lp_discon_ind_ep *) msg->data;
937878ed226SJulian Elischer 		ep->reason = reason;
938878ed226SJulian Elischer 		ep->link_type = con->link_type;
939878ed226SJulian Elischer 		ep->con_handle = con->con_handle;
940878ed226SJulian Elischer 
941878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL);
942878ed226SJulian Elischer 	} else
943878ed226SJulian Elischer 		NG_HCI_INFO(
944878ed226SJulian Elischer "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
945878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->sco);
946878ed226SJulian Elischer 
947878ed226SJulian Elischer 	return (0);
948878ed226SJulian Elischer } /* ng_hci_lp_discon_ind */
949878ed226SJulian Elischer 
950878ed226SJulian Elischer /*
951878ed226SJulian Elischer  * Process LP_QoSReq action from the upper layer protocol
952878ed226SJulian Elischer  */
953878ed226SJulian Elischer 
954878ed226SJulian Elischer int
955878ed226SJulian Elischer ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
956878ed226SJulian Elischer {
957878ed226SJulian Elischer 	struct qos_setup_req {
958878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
959878ed226SJulian Elischer 		ng_hci_qos_setup_cp	 cp;
960878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
961878ed226SJulian Elischer 	ng_hci_lp_qos_req_ep		*ep = NULL;
962878ed226SJulian Elischer 	ng_hci_unit_con_p		 con = NULL;
963878ed226SJulian Elischer 	struct mbuf			*m = NULL;
964878ed226SJulian Elischer 	int				 error = 0;
965878ed226SJulian Elischer 
966878ed226SJulian Elischer 	/* Check if unit is ready */
967878ed226SJulian Elischer 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
968878ed226SJulian Elischer 		NG_HCI_WARN(
969878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n",
970878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->state);
971878ed226SJulian Elischer 
972878ed226SJulian Elischer 		error = ENXIO;
973878ed226SJulian Elischer 		goto out;
974878ed226SJulian Elischer 	}
975878ed226SJulian Elischer 
976878ed226SJulian Elischer 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
977878ed226SJulian Elischer 		NG_HCI_ALERT(
978878ed226SJulian Elischer "%s: %s - invalid LP_QoSSetupReq message size=%d\n",
979878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node),
980878ed226SJulian Elischer 			NGI_MSG(item)->header.arglen);
981878ed226SJulian Elischer 
982878ed226SJulian Elischer 		error = EMSGSIZE;
983878ed226SJulian Elischer 		goto out;
984878ed226SJulian Elischer 	}
985878ed226SJulian Elischer 
986878ed226SJulian Elischer 	ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
987878ed226SJulian Elischer 
988878ed226SJulian Elischer 	con = ng_hci_con_by_handle(unit, ep->con_handle);
989878ed226SJulian Elischer 	if (con == NULL) {
990878ed226SJulian Elischer 		NG_HCI_ERR(
991878ed226SJulian Elischer "%s: %s - invalid connection handle=%d\n",
992878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
993878ed226SJulian Elischer 
994878ed226SJulian Elischer 		error = EINVAL;
995878ed226SJulian Elischer 		goto out;
996878ed226SJulian Elischer 	}
997878ed226SJulian Elischer 
998878ed226SJulian Elischer 	if (con->link_type != NG_HCI_LINK_ACL) {
999878ed226SJulian Elischer 		NG_HCI_ERR("%s: %s - invalid link type=%d\n",
1000878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), con->link_type);
1001878ed226SJulian Elischer 
1002878ed226SJulian Elischer 		error = EINVAL;
1003878ed226SJulian Elischer 		goto out;
1004878ed226SJulian Elischer 	}
1005878ed226SJulian Elischer 
1006878ed226SJulian Elischer 	if (con->state != NG_HCI_CON_OPEN) {
1007878ed226SJulian Elischer 		NG_HCI_ERR(
1008878ed226SJulian Elischer "%s: %s - invalid connection state=%d, handle=%d\n",
1009878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), con->state,
1010878ed226SJulian Elischer 			con->con_handle);
1011878ed226SJulian Elischer 
1012878ed226SJulian Elischer 		error = EINVAL;
1013878ed226SJulian Elischer 		goto out;
1014878ed226SJulian Elischer 	}
1015878ed226SJulian Elischer 
1016878ed226SJulian Elischer 	/*
1017878ed226SJulian Elischer 	 * Create HCI command
1018878ed226SJulian Elischer 	 */
1019878ed226SJulian Elischer 
1020878ed226SJulian Elischer 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1021878ed226SJulian Elischer 	if (m == NULL) {
1022878ed226SJulian Elischer 		error = ENOBUFS;
1023878ed226SJulian Elischer 		goto out;
1024878ed226SJulian Elischer 	}
1025878ed226SJulian Elischer 
1026878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
1027878ed226SJulian Elischer 	req = mtod(m, struct qos_setup_req *);
1028878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
1029878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
1030878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1031878ed226SJulian Elischer 			NG_HCI_OCF_QOS_SETUP));
1032878ed226SJulian Elischer 
1033878ed226SJulian Elischer 	req->cp.con_handle = htole16(ep->con_handle);
1034878ed226SJulian Elischer 	req->cp.flags = ep->flags;
1035878ed226SJulian Elischer 	req->cp.service_type = ep->service_type;
1036878ed226SJulian Elischer 	req->cp.token_rate = htole32(ep->token_rate);
1037878ed226SJulian Elischer 	req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1038878ed226SJulian Elischer 	req->cp.latency = htole32(ep->latency);
1039878ed226SJulian Elischer 	req->cp.delay_variation = htole32(ep->delay_variation);
1040878ed226SJulian Elischer 
1041878ed226SJulian Elischer 	/*
1042878ed226SJulian Elischer 	 * Adjust connection state
1043878ed226SJulian Elischer  	 */
1044878ed226SJulian Elischer 
1045878ed226SJulian Elischer 	if (hook == unit->acl)
1046878ed226SJulian Elischer 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
1047878ed226SJulian Elischer 	else
1048878ed226SJulian Elischer 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
1049878ed226SJulian Elischer 
1050878ed226SJulian Elischer 	/*
1051878ed226SJulian Elischer 	 * Queue and send HCI command
1052878ed226SJulian Elischer 	 */
1053878ed226SJulian Elischer 
1054878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1055878ed226SJulian Elischer 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1056878ed226SJulian Elischer 		error = ng_hci_send_command(unit);
1057878ed226SJulian Elischer out:
1058878ed226SJulian Elischer 	NG_FREE_ITEM(item);
1059878ed226SJulian Elischer 
1060878ed226SJulian Elischer 	return (error);
1061878ed226SJulian Elischer } /* ng_hci_lp_qos_req */
1062878ed226SJulian Elischer 
1063878ed226SJulian Elischer /*
1064878ed226SJulian Elischer  * Send LP_QoSCfm event to the upper layer protocol
1065878ed226SJulian Elischer  */
1066878ed226SJulian Elischer 
1067878ed226SJulian Elischer int
1068878ed226SJulian Elischer ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1069878ed226SJulian Elischer {
1070878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
1071878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
1072878ed226SJulian Elischer 	ng_hci_lp_qos_cfm_ep	*ep = NULL;
1073878ed226SJulian Elischer 	int			 error;
1074878ed226SJulian Elischer 
1075878ed226SJulian Elischer 	if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1076878ed226SJulian Elischer 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1077878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1078878ed226SJulian Elischer 				sizeof(*ep), M_NOWAIT);
1079878ed226SJulian Elischer 			if (msg != NULL) {
1080878ed226SJulian Elischer 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1081878ed226SJulian Elischer 				ep->status = status;
1082878ed226SJulian Elischer 				ep->con_handle = con->con_handle;
1083878ed226SJulian Elischer 
1084878ed226SJulian Elischer 				NG_SEND_MSG_HOOK(error, unit->node, msg,
1085878ed226SJulian Elischer 					unit->acl, NULL);
1086878ed226SJulian Elischer 			}
1087878ed226SJulian Elischer 		} else
1088878ed226SJulian Elischer 			NG_HCI_INFO(
1089878ed226SJulian Elischer "%s: %s - ACL hook not valid, hook=%p\n",
1090878ed226SJulian Elischer 				__func__, NG_NODE_NAME(unit->node), unit->acl);
1091878ed226SJulian Elischer 
1092878ed226SJulian Elischer 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1093878ed226SJulian Elischer 	}
1094878ed226SJulian Elischer 
1095878ed226SJulian Elischer 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1096878ed226SJulian Elischer 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1097878ed226SJulian Elischer 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1098878ed226SJulian Elischer 				sizeof(*ep), M_NOWAIT);
1099878ed226SJulian Elischer 			if (msg != NULL) {
1100878ed226SJulian Elischer 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1101878ed226SJulian Elischer 				ep->status = status;
1102878ed226SJulian Elischer 				ep->con_handle = con->con_handle;
1103878ed226SJulian Elischer 
1104878ed226SJulian Elischer 				NG_SEND_MSG_HOOK(error, unit->node, msg,
1105878ed226SJulian Elischer 					unit->sco, NULL);
1106878ed226SJulian Elischer 			}
1107878ed226SJulian Elischer 		} else
1108878ed226SJulian Elischer 			NG_HCI_INFO(
1109878ed226SJulian Elischer "%s: %s - SCO hook not valid, hook=%p\n",
1110878ed226SJulian Elischer 				 __func__, NG_NODE_NAME(unit->node), unit->sco);
1111878ed226SJulian Elischer 
1112878ed226SJulian Elischer 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1113878ed226SJulian Elischer 	}
1114878ed226SJulian Elischer 
1115878ed226SJulian Elischer 	return (0);
1116878ed226SJulian Elischer } /* ng_hci_lp_qos_cfm */
1117878ed226SJulian Elischer 
1118878ed226SJulian Elischer /*
1119878ed226SJulian Elischer  * Send LP_QoSViolationInd event to the upper layer protocol
1120878ed226SJulian Elischer  */
1121878ed226SJulian Elischer 
1122878ed226SJulian Elischer int
1123878ed226SJulian Elischer ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1124878ed226SJulian Elischer {
1125878ed226SJulian Elischer 	ng_hci_unit_p		 unit = con->unit;
1126878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
1127878ed226SJulian Elischer 	ng_hci_lp_qos_ind_ep	*ep = NULL;
1128878ed226SJulian Elischer 	int			 error;
1129878ed226SJulian Elischer 
1130878ed226SJulian Elischer 	/*
1131878ed226SJulian Elischer 	 * QoS Violation can only be generated for ACL connection handles.
1132878ed226SJulian Elischer 	 * Both ACL and SCO upstream hooks will receive notification.
1133878ed226SJulian Elischer 	 */
1134878ed226SJulian Elischer 
1135878ed226SJulian Elischer 	if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1136878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1137878ed226SJulian Elischer 			sizeof(*ep), M_NOWAIT);
1138878ed226SJulian Elischer 		if (msg == NULL)
1139878ed226SJulian Elischer 			return (ENOMEM);
1140878ed226SJulian Elischer 
1141878ed226SJulian Elischer 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1142878ed226SJulian Elischer 		ep->con_handle = con->con_handle;
1143878ed226SJulian Elischer 
1144878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, NULL);
1145878ed226SJulian Elischer 	} else
1146878ed226SJulian Elischer 		NG_HCI_INFO(
1147878ed226SJulian Elischer "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1148878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->acl);
1149878ed226SJulian Elischer 
1150878ed226SJulian Elischer 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1151878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1152878ed226SJulian Elischer 			sizeof(*ep), M_NOWAIT);
1153878ed226SJulian Elischer 		if (msg == NULL)
1154878ed226SJulian Elischer 			return (ENOMEM);
1155878ed226SJulian Elischer 
1156878ed226SJulian Elischer 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1157878ed226SJulian Elischer 		ep->con_handle = con->con_handle;
1158878ed226SJulian Elischer 
1159878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL);
1160878ed226SJulian Elischer 	} else
1161878ed226SJulian Elischer 		NG_HCI_INFO(
1162878ed226SJulian Elischer "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1163878ed226SJulian Elischer 			__func__, NG_NODE_NAME(unit->node), unit->sco);
1164878ed226SJulian Elischer 
1165878ed226SJulian Elischer 	return (0);
1166878ed226SJulian Elischer } /* ng_hci_lp_qos_ind */
1167878ed226SJulian Elischer 
1168878ed226SJulian Elischer /*
1169878ed226SJulian Elischer  * Process connection timeout
1170878ed226SJulian Elischer  */
1171878ed226SJulian Elischer 
1172878ed226SJulian Elischer void
1173878ed226SJulian Elischer ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1174878ed226SJulian Elischer {
1175878ed226SJulian Elischer 	ng_hci_unit_con_p	con = (ng_hci_unit_con_p) arg1;
1176878ed226SJulian Elischer 
1177878ed226SJulian Elischer 	KASSERT((con->flags & NG_HCI_CON_TIMEOUT_PENDING),
1178878ed226SJulian Elischer ("%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(node)));
1179878ed226SJulian Elischer 
1180878ed226SJulian Elischer 	con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1181878ed226SJulian Elischer 
1182878ed226SJulian Elischer 	/*
1183878ed226SJulian Elischer 	 * We expect to receive connection timeout in one of the following
1184878ed226SJulian Elischer 	 * states:
1185878ed226SJulian Elischer 	 *
1186878ed226SJulian Elischer 	 * 1) NG_HCI_CON_CLOSED means that upper layer has requested disconnect
1187878ed226SJulian Elischer 	 *    via LP_DISCON_REQ and we have not received Disconnect_Complete
1188878ed226SJulian Elischer 	 *    event. In this case we will send LP_DISCON_IND to upper layer.
1189878ed226SJulian Elischer 	 *
1190878ed226SJulian Elischer 	 * 2) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1191878ed226SJulian Elischer 	 *    to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1192878ed226SJulian Elischer 	 *    most likely already gave up on us.
1193878ed226SJulian Elischer 	 *
1194878ed226SJulian Elischer 	 * 3) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1195878ed226SJulian Elischer 	 *    (or we in the process of accepting it) and baseband has timedout
1196878ed226SJulian Elischer 	 *    on us. Inform upper layers and send LP_CON_CFM.
1197878ed226SJulian Elischer 	 */
1198878ed226SJulian Elischer 
1199878ed226SJulian Elischer 	switch (con->state) {
1200878ed226SJulian Elischer 	case NG_HCI_CON_CLOSED:
1201878ed226SJulian Elischer 		ng_hci_lp_discon_ind(con, 0x16);
1202878ed226SJulian Elischer 		break;
1203878ed226SJulian Elischer 
1204878ed226SJulian Elischer 	case NG_HCI_CON_W4_LP_CON_RSP:
1205878ed226SJulian Elischer 		break;
1206878ed226SJulian Elischer 
1207878ed226SJulian Elischer 	case NG_HCI_CON_W4_CONN_COMPLETE:
1208878ed226SJulian Elischer 		ng_hci_lp_con_cfm(con, 0xee);
1209878ed226SJulian Elischer 		break;
1210878ed226SJulian Elischer 
1211878ed226SJulian Elischer 	default:
1212878ed226SJulian Elischer 		KASSERT(0,
1213878ed226SJulian Elischer ("%s: %s - Invalid connection state=%d\n",
1214878ed226SJulian Elischer 			__func__, NG_NODE_NAME(node), con->state));
1215878ed226SJulian Elischer 		break;
1216878ed226SJulian Elischer 	}
1217878ed226SJulian Elischer 
1218878ed226SJulian Elischer 	ng_hci_free_con(con);
1219878ed226SJulian Elischer } /* ng_hci_process_con_timeout */
1220878ed226SJulian Elischer 
1221878ed226SJulian Elischer /*
1222878ed226SJulian Elischer  * Process connection watchdog timeout
1223878ed226SJulian Elischer  */
1224878ed226SJulian Elischer 
1225878ed226SJulian Elischer void
1226878ed226SJulian Elischer ng_hci_process_con_watchdog_timeout(node_p node, hook_p hook,
1227878ed226SJulian Elischer 		void *arg1, int arg2)
1228878ed226SJulian Elischer {
1229878ed226SJulian Elischer 	ng_hci_unit_con_p		 con = (ng_hci_unit_con_p) arg1;
1230878ed226SJulian Elischer 	struct discon_req {
1231878ed226SJulian Elischer 		ng_hci_cmd_pkt_t	 hdr;
1232878ed226SJulian Elischer 		ng_hci_discon_cp	 cp;
1233878ed226SJulian Elischer 	} __attribute__ ((packed))	*req = NULL;
1234878ed226SJulian Elischer 	struct mbuf			*m = NULL;
1235878ed226SJulian Elischer 
1236878ed226SJulian Elischer 	KASSERT((con->state == NG_HCI_CON_OPEN),
1237878ed226SJulian Elischer ("%s: %s - invalid connection state=%d, handle=%d\n",
1238878ed226SJulian Elischer 		__func__, NG_NODE_NAME(node), con->state, con->con_handle));
1239878ed226SJulian Elischer 
1240878ed226SJulian Elischer 	KASSERT((con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING),
1241878ed226SJulian Elischer ("%s: %s - No connection watchdog timeout!\n",
1242878ed226SJulian Elischer 		__func__, NG_NODE_NAME(node)));
1243878ed226SJulian Elischer 
1244878ed226SJulian Elischer 	con->flags &= ~NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING;
1245878ed226SJulian Elischer 
1246878ed226SJulian Elischer 	/*
1247878ed226SJulian Elischer 	 * Create HCI command
1248878ed226SJulian Elischer 	 */
1249878ed226SJulian Elischer 
1250878ed226SJulian Elischer 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1251878ed226SJulian Elischer 	if (m == NULL)
1252878ed226SJulian Elischer 		return; /* XXX this is bad */
1253878ed226SJulian Elischer 
1254878ed226SJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*req);
1255878ed226SJulian Elischer 	req = mtod(m, struct discon_req *);
1256878ed226SJulian Elischer 	req->hdr.type = NG_HCI_CMD_PKT;
1257878ed226SJulian Elischer 	req->hdr.length = sizeof(req->cp);
1258878ed226SJulian Elischer 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
1259878ed226SJulian Elischer 							NG_HCI_OCF_DISCON));
1260878ed226SJulian Elischer 
1261878ed226SJulian Elischer 	req->cp.con_handle = htole16(con->con_handle);
1262878ed226SJulian Elischer 	req->cp.reason = 0x13; /* User ended connection */
1263878ed226SJulian Elischer 
1264878ed226SJulian Elischer 	/*
1265878ed226SJulian Elischer 	 * Queue and send HCI command
1266878ed226SJulian Elischer 	 */
1267878ed226SJulian Elischer 
1268878ed226SJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&con->unit->cmdq, m);
1269878ed226SJulian Elischer 	if (!(con->unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1270878ed226SJulian Elischer 		ng_hci_send_command(con->unit);
1271878ed226SJulian Elischer 
1272878ed226SJulian Elischer 	/*
1273878ed226SJulian Elischer 	 * Send LP_DISCON_IND to the upper layers
1274878ed226SJulian Elischer 	 * Connection terminated by local host
1275878ed226SJulian Elischer 	 */
1276878ed226SJulian Elischer 
1277878ed226SJulian Elischer 	ng_hci_lp_discon_ind(con, 0x16);
1278878ed226SJulian Elischer 	ng_hci_free_con(con);
1279878ed226SJulian Elischer } /* ng_hci_process_con_watchdog_timeout */
1280878ed226SJulian Elischer 
1281