Netgraph port from FreeBSD - initial porting work
[dragonfly.git] / sys / netgraph7 / bluetooth / hci / ng_hci_ulpi.c
blobec6ed461dae72b7cbad44df8fa3076da489d8303
1 /*
2 * ng_hci_ulpi.c
3 */
5 /*-
6 * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
30 * $Id: ng_hci_ulpi.c,v 1.7 2003/09/08 18:57:51 max Exp $
31 * $FreeBSD: src/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c,v 1.8 2005/01/07 01:45:43 imp Exp $
32 * $DragonFly: src/sys/netgraph7/bluetooth/hci/ng_hci_ulpi.c,v 1.2 2008/06/26 23:05:40 dillon Exp $
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/endian.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/queue.h>
42 #include "ng_message.h"
43 #include "netgraph.h"
44 #include "bluetooth/include/ng_bluetooth.h"
45 #include "bluetooth/include/ng_hci.h"
46 #include "bluetooth/hci/ng_hci_var.h"
47 #include "bluetooth/hci/ng_hci_cmds.h"
48 #include "bluetooth/hci/ng_hci_evnt.h"
49 #include "bluetooth/hci/ng_hci_ulpi.h"
50 #include "bluetooth/hci/ng_hci_misc.h"
52 /******************************************************************************
53 ******************************************************************************
54 ** Upper Layer Protocol Interface module
55 ******************************************************************************
56 ******************************************************************************/
58 static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
59 static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
62 * Process LP_ConnectReq event from the upper layer protocol
65 int
66 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
68 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
69 NG_HCI_WARN(
70 "%s: %s - unit is not ready, state=%#x\n",
71 __func__, NG_NODE_NAME(unit->node), unit->state);
73 NG_FREE_ITEM(item);
75 return (ENXIO);
78 if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
79 NG_HCI_ALERT(
80 "%s: %s - invalid LP_ConnectReq message size=%d\n",
81 __func__, NG_NODE_NAME(unit->node),
82 NGI_MSG(item)->header.arglen);
84 NG_FREE_ITEM(item);
86 return (EMSGSIZE);
89 if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL)
90 return (ng_hci_lp_acl_con_req(unit, item, hook));
92 if (hook != unit->sco) {
93 NG_HCI_WARN(
94 "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
95 __func__, NG_NODE_NAME(unit->node), hook);
97 NG_FREE_ITEM(item);
99 return (EINVAL);
102 return (ng_hci_lp_sco_con_req(unit, item, hook));
103 } /* ng_hci_lp_con_req */
106 * Request to create new ACL connection
109 static int
110 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
112 struct acl_con_req {
113 ng_hci_cmd_pkt_t hdr;
114 ng_hci_create_con_cp cp;
115 } __attribute__ ((packed)) *req = NULL;
116 ng_hci_lp_con_req_ep *ep = NULL;
117 ng_hci_unit_con_p con = NULL;
118 ng_hci_neighbor_t *n = NULL;
119 struct mbuf *m = NULL;
120 int error = 0;
122 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
125 * Only one ACL connection can exist between each pair of units.
126 * So try to find ACL connection descriptor (in any state) that
127 * has requested remote BD_ADDR.
129 * Two cases:
131 * 1) We do not have connection to the remote unit. This is simple.
132 * Just create new connection descriptor and send HCI command to
133 * create new connection.
135 * 2) We do have connection descriptor. We need to check connection
136 * state:
138 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
139 * accepting connection from the remote unit. This is a race
140 * condition. We will ignore this message.
142 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
143 * requested connection or we just accepted it. In any case
144 * all we need to do here is set appropriate notification bit
145 * and wait.
147 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
148 * and let upper layer know that we have connection already.
151 con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
152 if (con != NULL) {
153 switch (con->state) {
154 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
155 error = EALREADY;
156 break;
158 case NG_HCI_CON_W4_CONN_COMPLETE:
159 if (hook == unit->acl)
160 con->flags |= NG_HCI_CON_NOTIFY_ACL;
161 else
162 con->flags |= NG_HCI_CON_NOTIFY_SCO;
163 break;
165 case NG_HCI_CON_OPEN: {
166 struct ng_mesg *msg = NULL;
167 ng_hci_lp_con_cfm_ep *cfm = NULL;
169 if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
170 NGI_GET_MSG(item, msg);
171 NG_FREE_MSG(msg);
173 NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
174 NGM_HCI_LP_CON_CFM, sizeof(*cfm),
175 M_WAITOK | M_NULLOK);
176 if (msg != NULL) {
177 cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
178 cfm->status = 0;
179 cfm->link_type = con->link_type;
180 cfm->con_handle = con->con_handle;
181 bcopy(&con->bdaddr, &cfm->bdaddr,
182 sizeof(cfm->bdaddr));
185 * This will forward item back to
186 * sender and set item to NULL
189 _NGI_MSG(item) = msg;
190 NG_FWD_ITEM_HOOK(error, item, hook);
191 } else
192 error = ENOMEM;
193 } else
194 NG_HCI_INFO(
195 "%s: %s - Source hook is not valid, hook=%p\n",
196 __func__, NG_NODE_NAME(unit->node),
197 hook);
198 } break;
200 default:
201 panic(
202 "%s: %s - Invalid connection state=%d\n",
203 __func__, NG_NODE_NAME(unit->node), con->state);
204 break;
207 goto out;
211 * If we got here then we need to create new ACL connection descriptor
212 * and submit HCI command. First create new connection desriptor, set
213 * bdaddr and notification flags.
216 con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
217 if (con == NULL) {
218 error = ENOMEM;
219 goto out;
222 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
225 * Create HCI command
228 MGETHDR(m, MB_DONTWAIT, MT_DATA);
229 if (m == NULL) {
230 ng_hci_free_con(con);
231 error = ENOBUFS;
232 goto out;
235 m->m_pkthdr.len = m->m_len = sizeof(*req);
236 req = mtod(m, struct acl_con_req *);
237 req->hdr.type = NG_HCI_CMD_PKT;
238 req->hdr.length = sizeof(req->cp);
239 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
240 NG_HCI_OCF_CREATE_CON));
242 bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
244 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
245 if (unit->features[0] & NG_HCI_LMP_3SLOT)
246 req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
247 if (unit->features[0] & NG_HCI_LMP_5SLOT)
248 req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
250 req->cp.pkt_type &= unit->packet_mask;
251 if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|
252 NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|
253 NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)
254 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
256 req->cp.pkt_type = htole16(req->cp.pkt_type);
258 if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)
259 req->cp.accept_role_switch = 1;
260 else
261 req->cp.accept_role_switch = 0;
264 * We may speed up connect by specifying valid parameters.
265 * So check the neighbor cache.
268 n = ng_hci_get_neighbor(unit, &ep->bdaddr);
269 if (n == NULL) {
270 req->cp.page_scan_rep_mode = 0;
271 req->cp.page_scan_mode = 0;
272 req->cp.clock_offset = 0;
273 } else {
274 req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
275 req->cp.page_scan_mode = n->page_scan_mode;
276 req->cp.clock_offset = htole16(n->clock_offset);
280 * Adust connection state
283 if (hook == unit->acl)
284 con->flags |= NG_HCI_CON_NOTIFY_ACL;
285 else
286 con->flags |= NG_HCI_CON_NOTIFY_SCO;
288 con->state = NG_HCI_CON_W4_CONN_COMPLETE;
289 ng_hci_con_timeout(con);
292 * Queue and send HCI command
295 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
296 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
297 error = ng_hci_send_command(unit);
298 out:
299 if (item != NULL)
300 NG_FREE_ITEM(item);
302 return (error);
303 } /* ng_hci_lp_acl_con_req */
306 * Request to create new SCO connection
309 static int
310 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
312 struct sco_con_req {
313 ng_hci_cmd_pkt_t hdr;
314 ng_hci_add_sco_con_cp cp;
315 } __attribute__ ((packed)) *req = NULL;
316 ng_hci_lp_con_req_ep *ep = NULL;
317 ng_hci_unit_con_p acl_con = NULL, sco_con = NULL;
318 struct mbuf *m = NULL;
319 int error = 0;
321 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
324 * SCO connection without ACL link
326 * If upper layer requests SCO connection and there is no open ACL
327 * connection to the desired remote unit, we will reject the request.
330 LIST_FOREACH(acl_con, &unit->con_list, next)
331 if (acl_con->link_type == NG_HCI_LINK_ACL &&
332 acl_con->state == NG_HCI_CON_OPEN &&
333 bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
334 break;
336 if (acl_con == NULL) {
337 NG_HCI_INFO(
338 "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
339 __func__, NG_NODE_NAME(unit->node),
340 ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
341 ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
343 error = ENOENT;
344 goto out;
348 * Multiple SCO connections can exist between the same pair of units.
349 * We assume that multiple SCO connections have to be opened one after
350 * another.
352 * Try to find SCO connection descriptor that matches the following:
354 * 1) sco_con->link_type == NG_HCI_LINK_SCO
356 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
357 * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
359 * 3) sco_con->bdaddr == ep->bdaddr
361 * Two cases:
363 * 1) We do not have connection descriptor. This is simple. Just
364 * create new connection and submit Add_SCO_Connection command.
366 * 2) We do have connection descriptor. We need to check the state.
368 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
369 * connection from the remote unit. This is a race condition and
370 * we will ignore the request.
372 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
373 * connection or we just accepted it.
376 LIST_FOREACH(sco_con, &unit->con_list, next)
377 if (sco_con->link_type == NG_HCI_LINK_SCO &&
378 (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
379 sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
380 bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
381 break;
383 if (sco_con != NULL) {
384 switch (sco_con->state) {
385 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
386 error = EALREADY;
387 break;
389 case NG_HCI_CON_W4_CONN_COMPLETE:
390 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
391 break;
393 default:
394 panic(
395 "%s: %s - Inavalid connection state=%d\n",
396 __func__, NG_NODE_NAME(unit->node),
397 sco_con->state);
398 break;
401 goto out;
405 * If we got here then we need to create new SCO connection descriptor
406 * and submit HCI command.
409 sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
410 if (sco_con == NULL) {
411 error = ENOMEM;
412 goto out;
415 bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
418 * Create HCI command
421 MGETHDR(m, MB_DONTWAIT, MT_DATA);
422 if (m == NULL) {
423 ng_hci_free_con(sco_con);
424 error = ENOBUFS;
425 goto out;
428 m->m_pkthdr.len = m->m_len = sizeof(*req);
429 req = mtod(m, struct sco_con_req *);
430 req->hdr.type = NG_HCI_CMD_PKT;
431 req->hdr.length = sizeof(req->cp);
432 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
433 NG_HCI_OCF_ADD_SCO_CON));
435 req->cp.con_handle = htole16(acl_con->con_handle);
437 req->cp.pkt_type = NG_HCI_PKT_HV1;
438 if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
439 req->cp.pkt_type |= NG_HCI_PKT_HV2;
440 if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
441 req->cp.pkt_type |= NG_HCI_PKT_HV3;
443 req->cp.pkt_type &= unit->packet_mask;
444 if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|
445 NG_HCI_PKT_HV2|
446 NG_HCI_PKT_HV3)) == 0)
447 req->cp.pkt_type = NG_HCI_PKT_HV1;
449 req->cp.pkt_type = htole16(req->cp.pkt_type);
452 * Adust connection state
455 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
457 sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
458 ng_hci_con_timeout(sco_con);
461 * Queue and send HCI command
464 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
465 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
466 error = ng_hci_send_command(unit);
467 out:
468 NG_FREE_ITEM(item);
470 return (error);
471 } /* ng_hci_lp_sco_con_req */
474 * Process LP_DisconnectReq event from the upper layer protocol
478 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
480 struct discon_req {
481 ng_hci_cmd_pkt_t hdr;
482 ng_hci_discon_cp cp;
483 } __attribute__ ((packed)) *req = NULL;
484 ng_hci_lp_discon_req_ep *ep = NULL;
485 ng_hci_unit_con_p con = NULL;
486 struct mbuf *m = NULL;
487 int error = 0;
489 /* Check if unit is ready */
490 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
491 NG_HCI_WARN(
492 "%s: %s - unit is not ready, state=%#x\n",
493 __func__, NG_NODE_NAME(unit->node), unit->state);
495 error = ENXIO;
496 goto out;
499 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
500 NG_HCI_ALERT(
501 "%s: %s - invalid LP_DisconnectReq message size=%d\n",
502 __func__, NG_NODE_NAME(unit->node),
503 NGI_MSG(item)->header.arglen);
505 error = EMSGSIZE;
506 goto out;
509 ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
511 con = ng_hci_con_by_handle(unit, ep->con_handle);
512 if (con == NULL) {
513 NG_HCI_ERR(
514 "%s: %s - invalid connection handle=%d\n",
515 __func__, NG_NODE_NAME(unit->node), ep->con_handle);
517 error = ENOENT;
518 goto out;
521 if (con->state != NG_HCI_CON_OPEN) {
522 NG_HCI_ERR(
523 "%s: %s - invalid connection state=%d, handle=%d\n",
524 __func__, NG_NODE_NAME(unit->node), con->state,
525 ep->con_handle);
527 error = EINVAL;
528 goto out;
532 * Create HCI command
535 MGETHDR(m, MB_DONTWAIT, MT_DATA);
536 if (m == NULL) {
537 error = ENOBUFS;
538 goto out;
541 m->m_pkthdr.len = m->m_len = sizeof(*req);
542 req = mtod(m, struct discon_req *);
543 req->hdr.type = NG_HCI_CMD_PKT;
544 req->hdr.length = sizeof(req->cp);
545 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
546 NG_HCI_OCF_DISCON));
548 req->cp.con_handle = htole16(ep->con_handle);
549 req->cp.reason = ep->reason;
552 * Queue and send HCI command
555 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
556 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
557 error = ng_hci_send_command(unit);
558 out:
559 NG_FREE_ITEM(item);
561 return (error);
562 } /* ng_hci_lp_discon_req */
565 * Send LP_ConnectCfm event to the upper layer protocol
569 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
571 ng_hci_unit_p unit = con->unit;
572 struct ng_mesg *msg = NULL;
573 ng_hci_lp_con_cfm_ep *ep = NULL;
574 int error;
577 * Check who wants to be notified. For ACL links both ACL and SCO
578 * upstream hooks will be notified (if required). For SCO links
579 * only SCO upstream hook will receive notification
582 if (con->link_type == NG_HCI_LINK_ACL &&
583 con->flags & NG_HCI_CON_NOTIFY_ACL) {
584 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
585 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
586 sizeof(*ep), M_WAITOK | M_NULLOK);
587 if (msg != NULL) {
588 ep = (ng_hci_lp_con_cfm_ep *) msg->data;
589 ep->status = status;
590 ep->link_type = con->link_type;
591 ep->con_handle = con->con_handle;
592 bcopy(&con->bdaddr, &ep->bdaddr,
593 sizeof(ep->bdaddr));
595 NG_SEND_MSG_HOOK(error, unit->node, msg,
596 unit->acl, 0);
598 } else
599 NG_HCI_INFO(
600 "%s: %s - ACL hook not valid, hook=%p\n",
601 __func__, NG_NODE_NAME(unit->node), unit->acl);
603 con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
606 if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
607 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
608 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
609 sizeof(*ep), M_WAITOK | M_NULLOK);
610 if (msg != NULL) {
611 ep = (ng_hci_lp_con_cfm_ep *) msg->data;
612 ep->status = status;
613 ep->link_type = con->link_type;
614 ep->con_handle = con->con_handle;
615 bcopy(&con->bdaddr, &ep->bdaddr,
616 sizeof(ep->bdaddr));
618 NG_SEND_MSG_HOOK(error, unit->node, msg,
619 unit->sco, 0);
621 } else
622 NG_HCI_INFO(
623 "%s: %s - SCO hook not valid, hook=%p\n",
624 __func__, NG_NODE_NAME(unit->node), unit->acl);
626 con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
629 return (0);
630 } /* ng_hci_lp_con_cfm */
633 * Send LP_ConnectInd event to the upper layer protocol
637 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
639 ng_hci_unit_p unit = con->unit;
640 struct ng_mesg *msg = NULL;
641 ng_hci_lp_con_ind_ep *ep = NULL;
642 hook_p hook = NULL;
643 int error = 0;
646 * Connection_Request event is generated for specific link type.
647 * Use link_type to select upstream hook.
650 if (con->link_type == NG_HCI_LINK_ACL)
651 hook = unit->acl;
652 else
653 hook = unit->sco;
655 if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
656 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
657 sizeof(*ep), M_WAITOK | M_NULLOK);
658 if (msg == NULL)
659 return (ENOMEM);
661 ep = (ng_hci_lp_con_ind_ep *)(msg->data);
662 ep->link_type = con->link_type;
663 bcopy(uclass, ep->uclass, sizeof(ep->uclass));
664 bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
666 NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
667 } else {
668 NG_HCI_WARN(
669 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
670 __func__, NG_NODE_NAME(unit->node), hook);
672 error = ENOTCONN;
675 return (error);
676 } /* ng_hci_lp_con_ind */
679 * Process LP_ConnectRsp event from the upper layer protocol
683 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
685 struct con_rsp_req {
686 ng_hci_cmd_pkt_t hdr;
687 union {
688 ng_hci_accept_con_cp acc;
689 ng_hci_reject_con_cp rej;
690 } __attribute__ ((packed)) cp;
691 } __attribute__ ((packed)) *req = NULL;
692 ng_hci_lp_con_rsp_ep *ep = NULL;
693 ng_hci_unit_con_p con = NULL;
694 struct mbuf *m = NULL;
695 int error = 0;
697 /* Check if unit is ready */
698 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
699 NG_HCI_WARN(
700 "%s: %s - unit is not ready, state=%#x\n",
701 __func__, NG_NODE_NAME(unit->node), unit->state);
703 error = ENXIO;
704 goto out;
707 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
708 NG_HCI_ALERT(
709 "%s: %s - invalid LP_ConnectRsp message size=%d\n",
710 __func__, NG_NODE_NAME(unit->node),
711 NGI_MSG(item)->header.arglen);
713 error = EMSGSIZE;
714 goto out;
717 ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
720 * Here we have to deal with race. Upper layers might send conflicting
721 * requests. One might send Accept and other Reject. We will not try
722 * to solve all the problems, so first request will always win.
724 * Try to find connection that matches the following:
726 * 1) con->link_type == ep->link_type
728 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
729 * con->state == NG_HCI_CON_W4_CONN_COMPLETE
731 * 3) con->bdaddr == ep->bdaddr
733 * Two cases:
735 * 1) We do not have connection descriptor. Could be bogus request or
736 * we have rejected connection already.
738 * 2) We do have connection descriptor. Then we need to check state:
740 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
741 * connection and it is a first response from the upper layer.
742 * if "status == 0" (Accept) then we will send Accept_Connection
743 * command and change connection state to W4_CONN_COMPLETE, else
744 * send reject and delete connection.
746 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
747 * connection. If "status == 0" we just need to link request
748 * and wait, else ignore Reject request.
751 LIST_FOREACH(con, &unit->con_list, next)
752 if (con->link_type == ep->link_type &&
753 (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
754 con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
755 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
756 break;
758 if (con == NULL) {
759 /* Reject for non-existing connection is fine */
760 error = (ep->status == 0)? ENOENT : 0;
761 goto out;
765 * Remove connection timeout and check connection state.
766 * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then
767 * timeout already happened and event went into node's queue.
770 if ((error = ng_hci_con_untimeout(con)) != 0)
771 goto out;
773 switch (con->state) {
774 case NG_HCI_CON_W4_LP_CON_RSP:
777 * Create HCI command
780 MGETHDR(m, MB_DONTWAIT, MT_DATA);
781 if (m == NULL) {
782 error = ENOBUFS;
783 goto out;
786 req = mtod(m, struct con_rsp_req *);
787 req->hdr.type = NG_HCI_CMD_PKT;
789 if (ep->status == 0) {
790 req->hdr.length = sizeof(req->cp.acc);
791 req->hdr.opcode = htole16(NG_HCI_OPCODE(
792 NG_HCI_OGF_LINK_CONTROL,
793 NG_HCI_OCF_ACCEPT_CON));
795 bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
796 sizeof(req->cp.acc.bdaddr));
799 * We are accepting connection, so if we support role
800 * switch and role switch was enabled then set role to
801 * NG_HCI_ROLE_MASTER and let LM peform role switch.
802 * Otherwise we remain slave. In this case LM WILL NOT
803 * perform role switch.
806 if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
807 unit->role_switch)
808 req->cp.acc.role = NG_HCI_ROLE_MASTER;
809 else
810 req->cp.acc.role = NG_HCI_ROLE_SLAVE;
813 * Adjust connection state
816 if (hook == unit->acl)
817 con->flags |= NG_HCI_CON_NOTIFY_ACL;
818 else
819 con->flags |= NG_HCI_CON_NOTIFY_SCO;
821 con->state = NG_HCI_CON_W4_CONN_COMPLETE;
822 ng_hci_con_timeout(con);
823 } else {
824 req->hdr.length = sizeof(req->cp.rej);
825 req->hdr.opcode = htole16(NG_HCI_OPCODE(
826 NG_HCI_OGF_LINK_CONTROL,
827 NG_HCI_OCF_REJECT_CON));
829 bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
830 sizeof(req->cp.rej.bdaddr));
832 req->cp.rej.reason = ep->status;
835 * Free connection descritor
836 * Item will be deleted just before return.
839 ng_hci_free_con(con);
842 m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
844 /* Queue and send HCI command */
845 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
846 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
847 error = ng_hci_send_command(unit);
848 break;
850 case NG_HCI_CON_W4_CONN_COMPLETE:
851 if (ep->status == 0) {
852 if (hook == unit->acl)
853 con->flags |= NG_HCI_CON_NOTIFY_ACL;
854 else
855 con->flags |= NG_HCI_CON_NOTIFY_SCO;
856 } else
857 error = EPERM;
858 break;
860 default:
861 panic(
862 "%s: %s - Invalid connection state=%d\n",
863 __func__, NG_NODE_NAME(unit->node), con->state);
864 break;
866 out:
867 NG_FREE_ITEM(item);
869 return (error);
870 } /* ng_hci_lp_con_rsp */
873 * Send LP_DisconnectInd to the upper layer protocol
877 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
879 ng_hci_unit_p unit = con->unit;
880 struct ng_mesg *msg = NULL;
881 ng_hci_lp_discon_ind_ep *ep = NULL;
882 int error = 0;
885 * Disconnect_Complete event is generated for specific connection
886 * handle. For ACL connection handles both ACL and SCO upstream
887 * hooks will receive notification. For SCO connection handles
888 * only SCO upstream hook will receive notification.
891 if (con->link_type == NG_HCI_LINK_ACL) {
892 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
893 NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
894 NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_WAITOK | M_NULLOK);
895 if (msg == NULL)
896 return (ENOMEM);
898 ep = (ng_hci_lp_discon_ind_ep *) msg->data;
899 ep->reason = reason;
900 ep->link_type = con->link_type;
901 ep->con_handle = con->con_handle;
903 NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0);
904 } else
905 NG_HCI_INFO(
906 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
907 __func__, NG_NODE_NAME(unit->node), unit->acl);
910 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
911 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
912 sizeof(*ep), M_WAITOK | M_NULLOK);
913 if (msg == NULL)
914 return (ENOMEM);
916 ep = (ng_hci_lp_discon_ind_ep *) msg->data;
917 ep->reason = reason;
918 ep->link_type = con->link_type;
919 ep->con_handle = con->con_handle;
921 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
922 } else
923 NG_HCI_INFO(
924 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
925 __func__, NG_NODE_NAME(unit->node), unit->sco);
927 return (0);
928 } /* ng_hci_lp_discon_ind */
931 * Process LP_QoSReq action from the upper layer protocol
935 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
937 struct qos_setup_req {
938 ng_hci_cmd_pkt_t hdr;
939 ng_hci_qos_setup_cp cp;
940 } __attribute__ ((packed)) *req = NULL;
941 ng_hci_lp_qos_req_ep *ep = NULL;
942 ng_hci_unit_con_p con = NULL;
943 struct mbuf *m = NULL;
944 int error = 0;
946 /* Check if unit is ready */
947 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
948 NG_HCI_WARN(
949 "%s: %s - unit is not ready, state=%#x\n",
950 __func__, NG_NODE_NAME(unit->node), unit->state);
952 error = ENXIO;
953 goto out;
956 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
957 NG_HCI_ALERT(
958 "%s: %s - invalid LP_QoSSetupReq message size=%d\n",
959 __func__, NG_NODE_NAME(unit->node),
960 NGI_MSG(item)->header.arglen);
962 error = EMSGSIZE;
963 goto out;
966 ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
968 con = ng_hci_con_by_handle(unit, ep->con_handle);
969 if (con == NULL) {
970 NG_HCI_ERR(
971 "%s: %s - invalid connection handle=%d\n",
972 __func__, NG_NODE_NAME(unit->node), ep->con_handle);
974 error = EINVAL;
975 goto out;
978 if (con->link_type != NG_HCI_LINK_ACL) {
979 NG_HCI_ERR("%s: %s - invalid link type=%d\n",
980 __func__, NG_NODE_NAME(unit->node), con->link_type);
982 error = EINVAL;
983 goto out;
986 if (con->state != NG_HCI_CON_OPEN) {
987 NG_HCI_ERR(
988 "%s: %s - invalid connection state=%d, handle=%d\n",
989 __func__, NG_NODE_NAME(unit->node), con->state,
990 con->con_handle);
992 error = EINVAL;
993 goto out;
997 * Create HCI command
1000 MGETHDR(m, MB_DONTWAIT, MT_DATA);
1001 if (m == NULL) {
1002 error = ENOBUFS;
1003 goto out;
1006 m->m_pkthdr.len = m->m_len = sizeof(*req);
1007 req = mtod(m, struct qos_setup_req *);
1008 req->hdr.type = NG_HCI_CMD_PKT;
1009 req->hdr.length = sizeof(req->cp);
1010 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1011 NG_HCI_OCF_QOS_SETUP));
1013 req->cp.con_handle = htole16(ep->con_handle);
1014 req->cp.flags = ep->flags;
1015 req->cp.service_type = ep->service_type;
1016 req->cp.token_rate = htole32(ep->token_rate);
1017 req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1018 req->cp.latency = htole32(ep->latency);
1019 req->cp.delay_variation = htole32(ep->delay_variation);
1022 * Adjust connection state
1025 if (hook == unit->acl)
1026 con->flags |= NG_HCI_CON_NOTIFY_ACL;
1027 else
1028 con->flags |= NG_HCI_CON_NOTIFY_SCO;
1031 * Queue and send HCI command
1034 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1035 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1036 error = ng_hci_send_command(unit);
1037 out:
1038 NG_FREE_ITEM(item);
1040 return (error);
1041 } /* ng_hci_lp_qos_req */
1044 * Send LP_QoSCfm event to the upper layer protocol
1048 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1050 ng_hci_unit_p unit = con->unit;
1051 struct ng_mesg *msg = NULL;
1052 ng_hci_lp_qos_cfm_ep *ep = NULL;
1053 int error;
1055 if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1056 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1057 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1058 sizeof(*ep), M_WAITOK | M_NULLOK);
1059 if (msg != NULL) {
1060 ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1061 ep->status = status;
1062 ep->con_handle = con->con_handle;
1064 NG_SEND_MSG_HOOK(error, unit->node, msg,
1065 unit->acl, 0);
1067 } else
1068 NG_HCI_INFO(
1069 "%s: %s - ACL hook not valid, hook=%p\n",
1070 __func__, NG_NODE_NAME(unit->node), unit->acl);
1072 con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1075 if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1076 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1077 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1078 sizeof(*ep), M_WAITOK | M_NULLOK);
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;
1084 NG_SEND_MSG_HOOK(error, unit->node, msg,
1085 unit->sco, 0);
1087 } else
1088 NG_HCI_INFO(
1089 "%s: %s - SCO hook not valid, hook=%p\n",
1090 __func__, NG_NODE_NAME(unit->node), unit->sco);
1092 con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1095 return (0);
1096 } /* ng_hci_lp_qos_cfm */
1099 * Send LP_QoSViolationInd event to the upper layer protocol
1103 ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1105 ng_hci_unit_p unit = con->unit;
1106 struct ng_mesg *msg = NULL;
1107 ng_hci_lp_qos_ind_ep *ep = NULL;
1108 int error;
1111 * QoS Violation can only be generated for ACL connection handles.
1112 * Both ACL and SCO upstream hooks will receive notification.
1115 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1116 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1117 sizeof(*ep), M_WAITOK | M_NULLOK);
1118 if (msg == NULL)
1119 return (ENOMEM);
1121 ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1122 ep->con_handle = con->con_handle;
1124 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0);
1125 } else
1126 NG_HCI_INFO(
1127 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1128 __func__, NG_NODE_NAME(unit->node), unit->acl);
1130 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1131 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1132 sizeof(*ep), M_WAITOK | M_NULLOK);
1133 if (msg == NULL)
1134 return (ENOMEM);
1136 ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1137 ep->con_handle = con->con_handle;
1139 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
1140 } else
1141 NG_HCI_INFO(
1142 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1143 __func__, NG_NODE_NAME(unit->node), unit->sco);
1145 return (0);
1146 } /* ng_hci_lp_qos_ind */
1149 * Process connection timeout
1152 void
1153 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
1155 ng_hci_unit_p unit = NULL;
1156 ng_hci_unit_con_p con = NULL;
1158 if (NG_NODE_NOT_VALID(node)) {
1159 printf("%s: Netgraph node is not valid\n", __func__);
1160 return;
1163 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
1164 con = ng_hci_con_by_handle(unit, con_handle);
1166 if (con == NULL) {
1167 NG_HCI_ALERT(
1168 "%s: %s - could not find connection, handle=%d\n",
1169 __func__, NG_NODE_NAME(node), con_handle);
1170 return;
1173 if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {
1174 NG_HCI_ALERT(
1175 "%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n",
1176 __func__, NG_NODE_NAME(node), con_handle, con->state,
1177 con->flags);
1178 return;
1181 con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1184 * We expect to receive connection timeout in one of the following
1185 * states:
1187 * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1188 * to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1189 * most likely already gave up on us.
1191 * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1192 * (or we in the process of accepting it) and baseband has timedout
1193 * on us. Inform upper layers and send LP_CON_CFM.
1196 switch (con->state) {
1197 case NG_HCI_CON_W4_LP_CON_RSP:
1198 break;
1200 case NG_HCI_CON_W4_CONN_COMPLETE:
1201 ng_hci_lp_con_cfm(con, 0xee);
1202 break;
1204 default:
1205 panic(
1206 "%s: %s - Invalid connection state=%d\n",
1207 __func__, NG_NODE_NAME(node), con->state);
1208 break;
1211 ng_hci_free_con(con);
1212 } /* ng_hci_process_con_timeout */