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