Vendor import of netgraph from FreeBSD-current 20080626
[dragonfly.git] / sys / netgraph7 / bluetooth / hci / ng_hci_ulpi.c
blob1da18d4f6a41a7c5b5688293f5df86646193c805
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 $
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/endian.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/queue.h>
41 #include <netgraph/ng_message.h>
42 #include <netgraph/netgraph.h>
43 #include <netgraph/bluetooth/include/ng_bluetooth.h>
44 #include <netgraph/bluetooth/include/ng_hci.h>
45 #include <netgraph/bluetooth/hci/ng_hci_var.h>
46 #include <netgraph/bluetooth/hci/ng_hci_cmds.h>
47 #include <netgraph/bluetooth/hci/ng_hci_evnt.h>
48 #include <netgraph/bluetooth/hci/ng_hci_ulpi.h>
49 #include <netgraph/bluetooth/hci/ng_hci_misc.h>
51 /******************************************************************************
52 ******************************************************************************
53 ** Upper Layer Protocol Interface module
54 ******************************************************************************
55 ******************************************************************************/
57 static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
58 static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
61 * Process LP_ConnectReq event from the upper layer protocol
64 int
65 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
67 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
68 NG_HCI_WARN(
69 "%s: %s - unit is not ready, state=%#x\n",
70 __func__, NG_NODE_NAME(unit->node), unit->state);
72 NG_FREE_ITEM(item);
74 return (ENXIO);
77 if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
78 NG_HCI_ALERT(
79 "%s: %s - invalid LP_ConnectReq message size=%d\n",
80 __func__, NG_NODE_NAME(unit->node),
81 NGI_MSG(item)->header.arglen);
83 NG_FREE_ITEM(item);
85 return (EMSGSIZE);
88 if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL)
89 return (ng_hci_lp_acl_con_req(unit, item, hook));
91 if (hook != unit->sco) {
92 NG_HCI_WARN(
93 "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
94 __func__, NG_NODE_NAME(unit->node), hook);
96 NG_FREE_ITEM(item);
98 return (EINVAL);
101 return (ng_hci_lp_sco_con_req(unit, item, hook));
102 } /* ng_hci_lp_con_req */
105 * Request to create new ACL connection
108 static int
109 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
111 struct acl_con_req {
112 ng_hci_cmd_pkt_t hdr;
113 ng_hci_create_con_cp cp;
114 } __attribute__ ((packed)) *req = NULL;
115 ng_hci_lp_con_req_ep *ep = NULL;
116 ng_hci_unit_con_p con = NULL;
117 ng_hci_neighbor_t *n = NULL;
118 struct mbuf *m = NULL;
119 int error = 0;
121 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
124 * Only one ACL connection can exist between each pair of units.
125 * So try to find ACL connection descriptor (in any state) that
126 * has requested remote BD_ADDR.
128 * Two cases:
130 * 1) We do not have connection to the remote unit. This is simple.
131 * Just create new connection descriptor and send HCI command to
132 * create new connection.
134 * 2) We do have connection descriptor. We need to check connection
135 * state:
137 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
138 * accepting connection from the remote unit. This is a race
139 * condition. We will ignore this message.
141 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
142 * requested connection or we just accepted it. In any case
143 * all we need to do here is set appropriate notification bit
144 * and wait.
146 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
147 * and let upper layer know that we have connection already.
150 con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
151 if (con != NULL) {
152 switch (con->state) {
153 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
154 error = EALREADY;
155 break;
157 case NG_HCI_CON_W4_CONN_COMPLETE:
158 if (hook == unit->acl)
159 con->flags |= NG_HCI_CON_NOTIFY_ACL;
160 else
161 con->flags |= NG_HCI_CON_NOTIFY_SCO;
162 break;
164 case NG_HCI_CON_OPEN: {
165 struct ng_mesg *msg = NULL;
166 ng_hci_lp_con_cfm_ep *cfm = NULL;
168 if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
169 NGI_GET_MSG(item, msg);
170 NG_FREE_MSG(msg);
172 NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
173 NGM_HCI_LP_CON_CFM, sizeof(*cfm),
174 M_NOWAIT);
175 if (msg != NULL) {
176 cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
177 cfm->status = 0;
178 cfm->link_type = con->link_type;
179 cfm->con_handle = con->con_handle;
180 bcopy(&con->bdaddr, &cfm->bdaddr,
181 sizeof(cfm->bdaddr));
184 * This will forward item back to
185 * sender and set item to NULL
188 _NGI_MSG(item) = msg;
189 NG_FWD_ITEM_HOOK(error, item, hook);
190 } else
191 error = ENOMEM;
192 } else
193 NG_HCI_INFO(
194 "%s: %s - Source hook is not valid, hook=%p\n",
195 __func__, NG_NODE_NAME(unit->node),
196 hook);
197 } break;
199 default:
200 panic(
201 "%s: %s - Invalid connection state=%d\n",
202 __func__, NG_NODE_NAME(unit->node), con->state);
203 break;
206 goto out;
210 * If we got here then we need to create new ACL connection descriptor
211 * and submit HCI command. First create new connection desriptor, set
212 * bdaddr and notification flags.
215 con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
216 if (con == NULL) {
217 error = ENOMEM;
218 goto out;
221 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
224 * Create HCI command
227 MGETHDR(m, M_DONTWAIT, MT_DATA);
228 if (m == NULL) {
229 ng_hci_free_con(con);
230 error = ENOBUFS;
231 goto out;
234 m->m_pkthdr.len = m->m_len = sizeof(*req);
235 req = mtod(m, struct acl_con_req *);
236 req->hdr.type = NG_HCI_CMD_PKT;
237 req->hdr.length = sizeof(req->cp);
238 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
239 NG_HCI_OCF_CREATE_CON));
241 bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
243 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
244 if (unit->features[0] & NG_HCI_LMP_3SLOT)
245 req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
246 if (unit->features[0] & NG_HCI_LMP_5SLOT)
247 req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
249 req->cp.pkt_type &= unit->packet_mask;
250 if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|
251 NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|
252 NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)
253 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
255 req->cp.pkt_type = htole16(req->cp.pkt_type);
257 if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)
258 req->cp.accept_role_switch = 1;
259 else
260 req->cp.accept_role_switch = 0;
263 * We may speed up connect by specifying valid parameters.
264 * So check the neighbor cache.
267 n = ng_hci_get_neighbor(unit, &ep->bdaddr);
268 if (n == NULL) {
269 req->cp.page_scan_rep_mode = 0;
270 req->cp.page_scan_mode = 0;
271 req->cp.clock_offset = 0;
272 } else {
273 req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
274 req->cp.page_scan_mode = n->page_scan_mode;
275 req->cp.clock_offset = htole16(n->clock_offset);
279 * Adust connection state
282 if (hook == unit->acl)
283 con->flags |= NG_HCI_CON_NOTIFY_ACL;
284 else
285 con->flags |= NG_HCI_CON_NOTIFY_SCO;
287 con->state = NG_HCI_CON_W4_CONN_COMPLETE;
288 ng_hci_con_timeout(con);
291 * Queue and send HCI command
294 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
295 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
296 error = ng_hci_send_command(unit);
297 out:
298 if (item != NULL)
299 NG_FREE_ITEM(item);
301 return (error);
302 } /* ng_hci_lp_acl_con_req */
305 * Request to create new SCO connection
308 static int
309 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
311 struct sco_con_req {
312 ng_hci_cmd_pkt_t hdr;
313 ng_hci_add_sco_con_cp cp;
314 } __attribute__ ((packed)) *req = NULL;
315 ng_hci_lp_con_req_ep *ep = NULL;
316 ng_hci_unit_con_p acl_con = NULL, sco_con = NULL;
317 struct mbuf *m = NULL;
318 int error = 0;
320 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
323 * SCO connection without ACL link
325 * If upper layer requests SCO connection and there is no open ACL
326 * connection to the desired remote unit, we will reject the request.
329 LIST_FOREACH(acl_con, &unit->con_list, next)
330 if (acl_con->link_type == NG_HCI_LINK_ACL &&
331 acl_con->state == NG_HCI_CON_OPEN &&
332 bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
333 break;
335 if (acl_con == NULL) {
336 NG_HCI_INFO(
337 "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
338 __func__, NG_NODE_NAME(unit->node),
339 ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
340 ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
342 error = ENOENT;
343 goto out;
347 * Multiple SCO connections can exist between the same pair of units.
348 * We assume that multiple SCO connections have to be opened one after
349 * another.
351 * Try to find SCO connection descriptor that matches the following:
353 * 1) sco_con->link_type == NG_HCI_LINK_SCO
355 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
356 * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
358 * 3) sco_con->bdaddr == ep->bdaddr
360 * Two cases:
362 * 1) We do not have connection descriptor. This is simple. Just
363 * create new connection and submit Add_SCO_Connection command.
365 * 2) We do have connection descriptor. We need to check the state.
367 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
368 * connection from the remote unit. This is a race condition and
369 * we will ignore the request.
371 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
372 * connection or we just accepted it.
375 LIST_FOREACH(sco_con, &unit->con_list, next)
376 if (sco_con->link_type == NG_HCI_LINK_SCO &&
377 (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
378 sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
379 bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
380 break;
382 if (sco_con != NULL) {
383 switch (sco_con->state) {
384 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
385 error = EALREADY;
386 break;
388 case NG_HCI_CON_W4_CONN_COMPLETE:
389 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
390 break;
392 default:
393 panic(
394 "%s: %s - Inavalid connection state=%d\n",
395 __func__, NG_NODE_NAME(unit->node),
396 sco_con->state);
397 break;
400 goto out;
404 * If we got here then we need to create new SCO connection descriptor
405 * and submit HCI command.
408 sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
409 if (sco_con == NULL) {
410 error = ENOMEM;
411 goto out;
414 bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
417 * Create HCI command
420 MGETHDR(m, M_DONTWAIT, MT_DATA);
421 if (m == NULL) {
422 ng_hci_free_con(sco_con);
423 error = ENOBUFS;
424 goto out;
427 m->m_pkthdr.len = m->m_len = sizeof(*req);
428 req = mtod(m, struct sco_con_req *);
429 req->hdr.type = NG_HCI_CMD_PKT;
430 req->hdr.length = sizeof(req->cp);
431 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
432 NG_HCI_OCF_ADD_SCO_CON));
434 req->cp.con_handle = htole16(acl_con->con_handle);
436 req->cp.pkt_type = NG_HCI_PKT_HV1;
437 if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
438 req->cp.pkt_type |= NG_HCI_PKT_HV2;
439 if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
440 req->cp.pkt_type |= NG_HCI_PKT_HV3;
442 req->cp.pkt_type &= unit->packet_mask;
443 if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|
444 NG_HCI_PKT_HV2|
445 NG_HCI_PKT_HV3)) == 0)
446 req->cp.pkt_type = NG_HCI_PKT_HV1;
448 req->cp.pkt_type = htole16(req->cp.pkt_type);
451 * Adust connection state
454 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
456 sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
457 ng_hci_con_timeout(sco_con);
460 * Queue and send HCI command
463 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
464 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
465 error = ng_hci_send_command(unit);
466 out:
467 NG_FREE_ITEM(item);
469 return (error);
470 } /* ng_hci_lp_sco_con_req */
473 * Process LP_DisconnectReq event from the upper layer protocol
477 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
479 struct discon_req {
480 ng_hci_cmd_pkt_t hdr;
481 ng_hci_discon_cp cp;
482 } __attribute__ ((packed)) *req = NULL;
483 ng_hci_lp_discon_req_ep *ep = NULL;
484 ng_hci_unit_con_p con = NULL;
485 struct mbuf *m = NULL;
486 int error = 0;
488 /* Check if unit is ready */
489 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
490 NG_HCI_WARN(
491 "%s: %s - unit is not ready, state=%#x\n",
492 __func__, NG_NODE_NAME(unit->node), unit->state);
494 error = ENXIO;
495 goto out;
498 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
499 NG_HCI_ALERT(
500 "%s: %s - invalid LP_DisconnectReq message size=%d\n",
501 __func__, NG_NODE_NAME(unit->node),
502 NGI_MSG(item)->header.arglen);
504 error = EMSGSIZE;
505 goto out;
508 ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
510 con = ng_hci_con_by_handle(unit, ep->con_handle);
511 if (con == NULL) {
512 NG_HCI_ERR(
513 "%s: %s - invalid connection handle=%d\n",
514 __func__, NG_NODE_NAME(unit->node), ep->con_handle);
516 error = ENOENT;
517 goto out;
520 if (con->state != NG_HCI_CON_OPEN) {
521 NG_HCI_ERR(
522 "%s: %s - invalid connection state=%d, handle=%d\n",
523 __func__, NG_NODE_NAME(unit->node), con->state,
524 ep->con_handle);
526 error = EINVAL;
527 goto out;
531 * Create HCI command
534 MGETHDR(m, M_DONTWAIT, MT_DATA);
535 if (m == NULL) {
536 error = ENOBUFS;
537 goto out;
540 m->m_pkthdr.len = m->m_len = sizeof(*req);
541 req = mtod(m, struct discon_req *);
542 req->hdr.type = NG_HCI_CMD_PKT;
543 req->hdr.length = sizeof(req->cp);
544 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
545 NG_HCI_OCF_DISCON));
547 req->cp.con_handle = htole16(ep->con_handle);
548 req->cp.reason = ep->reason;
551 * Queue and send HCI command
554 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
555 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
556 error = ng_hci_send_command(unit);
557 out:
558 NG_FREE_ITEM(item);
560 return (error);
561 } /* ng_hci_lp_discon_req */
564 * Send LP_ConnectCfm event to the upper layer protocol
568 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
570 ng_hci_unit_p unit = con->unit;
571 struct ng_mesg *msg = NULL;
572 ng_hci_lp_con_cfm_ep *ep = NULL;
573 int error;
576 * Check who wants to be notified. For ACL links both ACL and SCO
577 * upstream hooks will be notified (if required). For SCO links
578 * only SCO upstream hook will receive notification
581 if (con->link_type == NG_HCI_LINK_ACL &&
582 con->flags & NG_HCI_CON_NOTIFY_ACL) {
583 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
584 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
585 sizeof(*ep), M_NOWAIT);
586 if (msg != NULL) {
587 ep = (ng_hci_lp_con_cfm_ep *) msg->data;
588 ep->status = status;
589 ep->link_type = con->link_type;
590 ep->con_handle = con->con_handle;
591 bcopy(&con->bdaddr, &ep->bdaddr,
592 sizeof(ep->bdaddr));
594 NG_SEND_MSG_HOOK(error, unit->node, msg,
595 unit->acl, 0);
597 } else
598 NG_HCI_INFO(
599 "%s: %s - ACL hook not valid, hook=%p\n",
600 __func__, NG_NODE_NAME(unit->node), unit->acl);
602 con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
605 if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
606 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
607 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
608 sizeof(*ep), M_NOWAIT);
609 if (msg != NULL) {
610 ep = (ng_hci_lp_con_cfm_ep *) msg->data;
611 ep->status = status;
612 ep->link_type = con->link_type;
613 ep->con_handle = con->con_handle;
614 bcopy(&con->bdaddr, &ep->bdaddr,
615 sizeof(ep->bdaddr));
617 NG_SEND_MSG_HOOK(error, unit->node, msg,
618 unit->sco, 0);
620 } else
621 NG_HCI_INFO(
622 "%s: %s - SCO hook not valid, hook=%p\n",
623 __func__, NG_NODE_NAME(unit->node), unit->acl);
625 con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
628 return (0);
629 } /* ng_hci_lp_con_cfm */
632 * Send LP_ConnectInd event to the upper layer protocol
636 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
638 ng_hci_unit_p unit = con->unit;
639 struct ng_mesg *msg = NULL;
640 ng_hci_lp_con_ind_ep *ep = NULL;
641 hook_p hook = NULL;
642 int error = 0;
645 * Connection_Request event is generated for specific link type.
646 * Use link_type to select upstream hook.
649 if (con->link_type == NG_HCI_LINK_ACL)
650 hook = unit->acl;
651 else
652 hook = unit->sco;
654 if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
655 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
656 sizeof(*ep), M_NOWAIT);
657 if (msg == NULL)
658 return (ENOMEM);
660 ep = (ng_hci_lp_con_ind_ep *)(msg->data);
661 ep->link_type = con->link_type;
662 bcopy(uclass, ep->uclass, sizeof(ep->uclass));
663 bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
665 NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
666 } else {
667 NG_HCI_WARN(
668 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
669 __func__, NG_NODE_NAME(unit->node), hook);
671 error = ENOTCONN;
674 return (error);
675 } /* ng_hci_lp_con_ind */
678 * Process LP_ConnectRsp event from the upper layer protocol
682 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
684 struct con_rsp_req {
685 ng_hci_cmd_pkt_t hdr;
686 union {
687 ng_hci_accept_con_cp acc;
688 ng_hci_reject_con_cp rej;
689 } __attribute__ ((packed)) cp;
690 } __attribute__ ((packed)) *req = NULL;
691 ng_hci_lp_con_rsp_ep *ep = NULL;
692 ng_hci_unit_con_p con = NULL;
693 struct mbuf *m = NULL;
694 int error = 0;
696 /* Check if unit is ready */
697 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
698 NG_HCI_WARN(
699 "%s: %s - unit is not ready, state=%#x\n",
700 __func__, NG_NODE_NAME(unit->node), unit->state);
702 error = ENXIO;
703 goto out;
706 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
707 NG_HCI_ALERT(
708 "%s: %s - invalid LP_ConnectRsp message size=%d\n",
709 __func__, NG_NODE_NAME(unit->node),
710 NGI_MSG(item)->header.arglen);
712 error = EMSGSIZE;
713 goto out;
716 ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
719 * Here we have to deal with race. Upper layers might send conflicting
720 * requests. One might send Accept and other Reject. We will not try
721 * to solve all the problems, so first request will always win.
723 * Try to find connection that matches the following:
725 * 1) con->link_type == ep->link_type
727 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
728 * con->state == NG_HCI_CON_W4_CONN_COMPLETE
730 * 3) con->bdaddr == ep->bdaddr
732 * Two cases:
734 * 1) We do not have connection descriptor. Could be bogus request or
735 * we have rejected connection already.
737 * 2) We do have connection descriptor. Then we need to check state:
739 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
740 * connection and it is a first response from the upper layer.
741 * if "status == 0" (Accept) then we will send Accept_Connection
742 * command and change connection state to W4_CONN_COMPLETE, else
743 * send reject and delete connection.
745 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
746 * connection. If "status == 0" we just need to link request
747 * and wait, else ignore Reject request.
750 LIST_FOREACH(con, &unit->con_list, next)
751 if (con->link_type == ep->link_type &&
752 (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
753 con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
754 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
755 break;
757 if (con == NULL) {
758 /* Reject for non-existing connection is fine */
759 error = (ep->status == 0)? ENOENT : 0;
760 goto out;
764 * Remove connection timeout and check connection state.
765 * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then
766 * timeout already happened and event went into node's queue.
769 if ((error = ng_hci_con_untimeout(con)) != 0)
770 goto out;
772 switch (con->state) {
773 case NG_HCI_CON_W4_LP_CON_RSP:
776 * Create HCI command
779 MGETHDR(m, M_DONTWAIT, MT_DATA);
780 if (m == NULL) {
781 error = ENOBUFS;
782 goto out;
785 req = mtod(m, struct con_rsp_req *);
786 req->hdr.type = NG_HCI_CMD_PKT;
788 if (ep->status == 0) {
789 req->hdr.length = sizeof(req->cp.acc);
790 req->hdr.opcode = htole16(NG_HCI_OPCODE(
791 NG_HCI_OGF_LINK_CONTROL,
792 NG_HCI_OCF_ACCEPT_CON));
794 bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
795 sizeof(req->cp.acc.bdaddr));
798 * We are accepting connection, so if we support role
799 * switch and role switch was enabled then set role to
800 * NG_HCI_ROLE_MASTER and let LM peform role switch.
801 * Otherwise we remain slave. In this case LM WILL NOT
802 * perform role switch.
805 if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
806 unit->role_switch)
807 req->cp.acc.role = NG_HCI_ROLE_MASTER;
808 else
809 req->cp.acc.role = NG_HCI_ROLE_SLAVE;
812 * Adjust connection state
815 if (hook == unit->acl)
816 con->flags |= NG_HCI_CON_NOTIFY_ACL;
817 else
818 con->flags |= NG_HCI_CON_NOTIFY_SCO;
820 con->state = NG_HCI_CON_W4_CONN_COMPLETE;
821 ng_hci_con_timeout(con);
822 } else {
823 req->hdr.length = sizeof(req->cp.rej);
824 req->hdr.opcode = htole16(NG_HCI_OPCODE(
825 NG_HCI_OGF_LINK_CONTROL,
826 NG_HCI_OCF_REJECT_CON));
828 bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
829 sizeof(req->cp.rej.bdaddr));
831 req->cp.rej.reason = ep->status;
834 * Free connection descritor
835 * Item will be deleted just before return.
838 ng_hci_free_con(con);
841 m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
843 /* Queue and send HCI command */
844 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
845 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
846 error = ng_hci_send_command(unit);
847 break;
849 case NG_HCI_CON_W4_CONN_COMPLETE:
850 if (ep->status == 0) {
851 if (hook == unit->acl)
852 con->flags |= NG_HCI_CON_NOTIFY_ACL;
853 else
854 con->flags |= NG_HCI_CON_NOTIFY_SCO;
855 } else
856 error = EPERM;
857 break;
859 default:
860 panic(
861 "%s: %s - Invalid connection state=%d\n",
862 __func__, NG_NODE_NAME(unit->node), con->state);
863 break;
865 out:
866 NG_FREE_ITEM(item);
868 return (error);
869 } /* ng_hci_lp_con_rsp */
872 * Send LP_DisconnectInd to the upper layer protocol
876 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
878 ng_hci_unit_p unit = con->unit;
879 struct ng_mesg *msg = NULL;
880 ng_hci_lp_discon_ind_ep *ep = NULL;
881 int error = 0;
884 * Disconnect_Complete event is generated for specific connection
885 * handle. For ACL connection handles both ACL and SCO upstream
886 * hooks will receive notification. For SCO connection handles
887 * only SCO upstream hook will receive notification.
890 if (con->link_type == NG_HCI_LINK_ACL) {
891 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
892 NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
893 NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
894 if (msg == NULL)
895 return (ENOMEM);
897 ep = (ng_hci_lp_discon_ind_ep *) msg->data;
898 ep->reason = reason;
899 ep->link_type = con->link_type;
900 ep->con_handle = con->con_handle;
902 NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0);
903 } else
904 NG_HCI_INFO(
905 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
906 __func__, NG_NODE_NAME(unit->node), unit->acl);
909 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
910 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
911 sizeof(*ep), M_NOWAIT);
912 if (msg == NULL)
913 return (ENOMEM);
915 ep = (ng_hci_lp_discon_ind_ep *) msg->data;
916 ep->reason = reason;
917 ep->link_type = con->link_type;
918 ep->con_handle = con->con_handle;
920 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
921 } else
922 NG_HCI_INFO(
923 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
924 __func__, NG_NODE_NAME(unit->node), unit->sco);
926 return (0);
927 } /* ng_hci_lp_discon_ind */
930 * Process LP_QoSReq action from the upper layer protocol
934 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
936 struct qos_setup_req {
937 ng_hci_cmd_pkt_t hdr;
938 ng_hci_qos_setup_cp cp;
939 } __attribute__ ((packed)) *req = NULL;
940 ng_hci_lp_qos_req_ep *ep = NULL;
941 ng_hci_unit_con_p con = NULL;
942 struct mbuf *m = NULL;
943 int error = 0;
945 /* Check if unit is ready */
946 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
947 NG_HCI_WARN(
948 "%s: %s - unit is not ready, state=%#x\n",
949 __func__, NG_NODE_NAME(unit->node), unit->state);
951 error = ENXIO;
952 goto out;
955 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
956 NG_HCI_ALERT(
957 "%s: %s - invalid LP_QoSSetupReq message size=%d\n",
958 __func__, NG_NODE_NAME(unit->node),
959 NGI_MSG(item)->header.arglen);
961 error = EMSGSIZE;
962 goto out;
965 ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
967 con = ng_hci_con_by_handle(unit, ep->con_handle);
968 if (con == NULL) {
969 NG_HCI_ERR(
970 "%s: %s - invalid connection handle=%d\n",
971 __func__, NG_NODE_NAME(unit->node), ep->con_handle);
973 error = EINVAL;
974 goto out;
977 if (con->link_type != NG_HCI_LINK_ACL) {
978 NG_HCI_ERR("%s: %s - invalid link type=%d\n",
979 __func__, NG_NODE_NAME(unit->node), con->link_type);
981 error = EINVAL;
982 goto out;
985 if (con->state != NG_HCI_CON_OPEN) {
986 NG_HCI_ERR(
987 "%s: %s - invalid connection state=%d, handle=%d\n",
988 __func__, NG_NODE_NAME(unit->node), con->state,
989 con->con_handle);
991 error = EINVAL;
992 goto out;
996 * Create HCI command
999 MGETHDR(m, M_DONTWAIT, MT_DATA);
1000 if (m == NULL) {
1001 error = ENOBUFS;
1002 goto out;
1005 m->m_pkthdr.len = m->m_len = sizeof(*req);
1006 req = mtod(m, struct qos_setup_req *);
1007 req->hdr.type = NG_HCI_CMD_PKT;
1008 req->hdr.length = sizeof(req->cp);
1009 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1010 NG_HCI_OCF_QOS_SETUP));
1012 req->cp.con_handle = htole16(ep->con_handle);
1013 req->cp.flags = ep->flags;
1014 req->cp.service_type = ep->service_type;
1015 req->cp.token_rate = htole32(ep->token_rate);
1016 req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1017 req->cp.latency = htole32(ep->latency);
1018 req->cp.delay_variation = htole32(ep->delay_variation);
1021 * Adjust connection state
1024 if (hook == unit->acl)
1025 con->flags |= NG_HCI_CON_NOTIFY_ACL;
1026 else
1027 con->flags |= NG_HCI_CON_NOTIFY_SCO;
1030 * Queue and send HCI command
1033 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1034 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1035 error = ng_hci_send_command(unit);
1036 out:
1037 NG_FREE_ITEM(item);
1039 return (error);
1040 } /* ng_hci_lp_qos_req */
1043 * Send LP_QoSCfm event to the upper layer protocol
1047 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1049 ng_hci_unit_p unit = con->unit;
1050 struct ng_mesg *msg = NULL;
1051 ng_hci_lp_qos_cfm_ep *ep = NULL;
1052 int error;
1054 if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1055 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1056 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1057 sizeof(*ep), M_NOWAIT);
1058 if (msg != NULL) {
1059 ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1060 ep->status = status;
1061 ep->con_handle = con->con_handle;
1063 NG_SEND_MSG_HOOK(error, unit->node, msg,
1064 unit->acl, 0);
1066 } else
1067 NG_HCI_INFO(
1068 "%s: %s - ACL hook not valid, hook=%p\n",
1069 __func__, NG_NODE_NAME(unit->node), unit->acl);
1071 con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1074 if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1075 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1076 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1077 sizeof(*ep), M_NOWAIT);
1078 if (msg != NULL) {
1079 ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1080 ep->status = status;
1081 ep->con_handle = con->con_handle;
1083 NG_SEND_MSG_HOOK(error, unit->node, msg,
1084 unit->sco, 0);
1086 } else
1087 NG_HCI_INFO(
1088 "%s: %s - SCO hook not valid, hook=%p\n",
1089 __func__, NG_NODE_NAME(unit->node), unit->sco);
1091 con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1094 return (0);
1095 } /* ng_hci_lp_qos_cfm */
1098 * Send LP_QoSViolationInd event to the upper layer protocol
1102 ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1104 ng_hci_unit_p unit = con->unit;
1105 struct ng_mesg *msg = NULL;
1106 ng_hci_lp_qos_ind_ep *ep = NULL;
1107 int error;
1110 * QoS Violation can only be generated for ACL connection handles.
1111 * Both ACL and SCO upstream hooks will receive notification.
1114 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1115 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1116 sizeof(*ep), M_NOWAIT);
1117 if (msg == NULL)
1118 return (ENOMEM);
1120 ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1121 ep->con_handle = con->con_handle;
1123 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0);
1124 } else
1125 NG_HCI_INFO(
1126 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1127 __func__, NG_NODE_NAME(unit->node), unit->acl);
1129 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1130 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1131 sizeof(*ep), M_NOWAIT);
1132 if (msg == NULL)
1133 return (ENOMEM);
1135 ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1136 ep->con_handle = con->con_handle;
1138 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
1139 } else
1140 NG_HCI_INFO(
1141 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1142 __func__, NG_NODE_NAME(unit->node), unit->sco);
1144 return (0);
1145 } /* ng_hci_lp_qos_ind */
1148 * Process connection timeout
1151 void
1152 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
1154 ng_hci_unit_p unit = NULL;
1155 ng_hci_unit_con_p con = NULL;
1157 if (NG_NODE_NOT_VALID(node)) {
1158 printf("%s: Netgraph node is not valid\n", __func__);
1159 return;
1162 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
1163 con = ng_hci_con_by_handle(unit, con_handle);
1165 if (con == NULL) {
1166 NG_HCI_ALERT(
1167 "%s: %s - could not find connection, handle=%d\n",
1168 __func__, NG_NODE_NAME(node), con_handle);
1169 return;
1172 if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {
1173 NG_HCI_ALERT(
1174 "%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n",
1175 __func__, NG_NODE_NAME(node), con_handle, con->state,
1176 con->flags);
1177 return;
1180 con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1183 * We expect to receive connection timeout in one of the following
1184 * states:
1186 * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1187 * to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1188 * most likely already gave up on us.
1190 * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1191 * (or we in the process of accepting it) and baseband has timedout
1192 * on us. Inform upper layers and send LP_CON_CFM.
1195 switch (con->state) {
1196 case NG_HCI_CON_W4_LP_CON_RSP:
1197 break;
1199 case NG_HCI_CON_W4_CONN_COMPLETE:
1200 ng_hci_lp_con_cfm(con, 0xee);
1201 break;
1203 default:
1204 panic(
1205 "%s: %s - Invalid connection state=%d\n",
1206 __func__, NG_NODE_NAME(node), con->state);
1207 break;
1210 ng_hci_free_con(con);
1211 } /* ng_hci_process_con_timeout */