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