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