1 /* 2 * ng_hci_ulpi.c 3 */ 4 5 /*- 6 * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 7 * All rights reserved. 8 * 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. 17 * 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. 29 * 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 $ 33 */ 34 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" 51 52 /****************************************************************************** 53 ****************************************************************************** 54 ** Upper Layer Protocol Interface module 55 ****************************************************************************** 56 ******************************************************************************/ 57 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); 60 61 /* 62 * Process LP_ConnectReq event from the upper layer protocol 63 */ 64 65 int 66 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 67 { 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); 72 73 NG_FREE_ITEM(item); 74 75 return (ENXIO); 76 } 77 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); 83 84 NG_FREE_ITEM(item); 85 86 return (EMSGSIZE); 87 } 88 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)); 91 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); 96 97 NG_FREE_ITEM(item); 98 99 return (EINVAL); 100 } 101 102 return (ng_hci_lp_sco_con_req(unit, item, hook)); 103 } /* ng_hci_lp_con_req */ 104 105 /* 106 * Request to create new ACL connection 107 */ 108 109 static int 110 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 111 { 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; 121 122 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 123 124 /* 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. 128 * 129 * Two cases: 130 * 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. 134 * 135 * 2) We do have connection descriptor. We need to check connection 136 * state: 137 * 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. 141 * 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. 146 * 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. 149 */ 150 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; 157 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; 164 165 case NG_HCI_CON_OPEN: { 166 struct ng_mesg *msg = NULL; 167 ng_hci_lp_con_cfm_ep *cfm = NULL; 168 169 if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 170 NGI_GET_MSG(item, msg); 171 NG_FREE_MSG(msg); 172 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)); 183 184 /* 185 * This will forward item back to 186 * sender and set item to NULL 187 */ 188 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; 199 200 default: 201 panic( 202 "%s: %s - Invalid connection state=%d\n", 203 __func__, NG_NODE_NAME(unit->node), con->state); 204 break; 205 } 206 207 goto out; 208 } 209 210 /* 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. 214 */ 215 216 con = ng_hci_new_con(unit, NG_HCI_LINK_ACL); 217 if (con == NULL) { 218 error = ENOMEM; 219 goto out; 220 } 221 222 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 223 224 /* 225 * Create HCI command 226 */ 227 228 MGETHDR(m, MB_DONTWAIT, MT_DATA); 229 if (m == NULL) { 230 ng_hci_free_con(con); 231 error = ENOBUFS; 232 goto out; 233 } 234 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)); 241 242 bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr)); 243 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); 249 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); 255 256 req->cp.pkt_type = htole16(req->cp.pkt_type); 257 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; 262 263 /* 264 * We may speed up connect by specifying valid parameters. 265 * So check the neighbor cache. 266 */ 267 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); 277 } 278 279 /* 280 * Adust connection state 281 */ 282 283 if (hook == unit->acl) 284 con->flags |= NG_HCI_CON_NOTIFY_ACL; 285 else 286 con->flags |= NG_HCI_CON_NOTIFY_SCO; 287 288 con->state = NG_HCI_CON_W4_CONN_COMPLETE; 289 ng_hci_con_timeout(con); 290 291 /* 292 * Queue and send HCI command 293 */ 294 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); 301 302 return (error); 303 } /* ng_hci_lp_acl_con_req */ 304 305 /* 306 * Request to create new SCO connection 307 */ 308 309 static int 310 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 311 { 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; 320 321 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 322 323 /* 324 * SCO connection without ACL link 325 * 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. 328 */ 329 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; 335 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]); 342 343 error = ENOENT; 344 goto out; 345 } 346 347 /* 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. 351 * 352 * Try to find SCO connection descriptor that matches the following: 353 * 354 * 1) sco_con->link_type == NG_HCI_LINK_SCO 355 * 356 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 357 * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE 358 * 359 * 3) sco_con->bdaddr == ep->bdaddr 360 * 361 * Two cases: 362 * 363 * 1) We do not have connection descriptor. This is simple. Just 364 * create new connection and submit Add_SCO_Connection command. 365 * 366 * 2) We do have connection descriptor. We need to check the state. 367 * 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. 371 * 372 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested 373 * connection or we just accepted it. 374 */ 375 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; 382 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; 388 389 case NG_HCI_CON_W4_CONN_COMPLETE: 390 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 391 break; 392 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; 399 } 400 401 goto out; 402 } 403 404 /* 405 * If we got here then we need to create new SCO connection descriptor 406 * and submit HCI command. 407 */ 408 409 sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO); 410 if (sco_con == NULL) { 411 error = ENOMEM; 412 goto out; 413 } 414 415 bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr)); 416 417 /* 418 * Create HCI command 419 */ 420 421 MGETHDR(m, MB_DONTWAIT, MT_DATA); 422 if (m == NULL) { 423 ng_hci_free_con(sco_con); 424 error = ENOBUFS; 425 goto out; 426 } 427 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)); 434 435 req->cp.con_handle = htole16(acl_con->con_handle); 436 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; 442 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; 448 449 req->cp.pkt_type = htole16(req->cp.pkt_type); 450 451 /* 452 * Adust connection state 453 */ 454 455 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 456 457 sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE; 458 ng_hci_con_timeout(sco_con); 459 460 /* 461 * Queue and send HCI command 462 */ 463 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); 469 470 return (error); 471 } /* ng_hci_lp_sco_con_req */ 472 473 /* 474 * Process LP_DisconnectReq event from the upper layer protocol 475 */ 476 477 int 478 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook) 479 { 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; 488 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); 494 495 error = ENXIO; 496 goto out; 497 } 498 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); 504 505 error = EMSGSIZE; 506 goto out; 507 } 508 509 ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data); 510 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); 516 517 error = ENOENT; 518 goto out; 519 } 520 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); 526 527 error = EINVAL; 528 goto out; 529 } 530 531 /* 532 * Create HCI command 533 */ 534 535 MGETHDR(m, MB_DONTWAIT, MT_DATA); 536 if (m == NULL) { 537 error = ENOBUFS; 538 goto out; 539 } 540 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)); 547 548 req->cp.con_handle = htole16(ep->con_handle); 549 req->cp.reason = ep->reason; 550 551 /* 552 * Queue and send HCI command 553 */ 554 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); 560 561 return (error); 562 } /* ng_hci_lp_discon_req */ 563 564 /* 565 * Send LP_ConnectCfm event to the upper layer protocol 566 */ 567 568 int 569 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status) 570 { 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; 575 576 /* 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 580 */ 581 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)); 594 595 NG_SEND_MSG_HOOK(error, unit->node, msg, 596 unit->acl, 0); 597 } 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); 602 603 con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 604 } 605 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)); 617 618 NG_SEND_MSG_HOOK(error, unit->node, msg, 619 unit->sco, 0); 620 } 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); 625 626 con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 627 } 628 629 return (0); 630 } /* ng_hci_lp_con_cfm */ 631 632 /* 633 * Send LP_ConnectInd event to the upper layer protocol 634 */ 635 636 int 637 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass) 638 { 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; 644 645 /* 646 * Connection_Request event is generated for specific link type. 647 * Use link_type to select upstream hook. 648 */ 649 650 if (con->link_type == NG_HCI_LINK_ACL) 651 hook = unit->acl; 652 else 653 hook = unit->sco; 654 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); 660 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)); 665 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); 671 672 error = ENOTCONN; 673 } 674 675 return (error); 676 } /* ng_hci_lp_con_ind */ 677 678 /* 679 * Process LP_ConnectRsp event from the upper layer protocol 680 */ 681 682 int 683 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook) 684 { 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; 696 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); 702 703 error = ENXIO; 704 goto out; 705 } 706 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); 712 713 error = EMSGSIZE; 714 goto out; 715 } 716 717 ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data); 718 719 /* 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. 723 * 724 * Try to find connection that matches the following: 725 * 726 * 1) con->link_type == ep->link_type 727 * 728 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || 729 * con->state == NG_HCI_CON_W4_CONN_COMPLETE 730 * 731 * 3) con->bdaddr == ep->bdaddr 732 * 733 * Two cases: 734 * 735 * 1) We do not have connection descriptor. Could be bogus request or 736 * we have rejected connection already. 737 * 738 * 2) We do have connection descriptor. Then we need to check state: 739 * 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. 745 * 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. 749 */ 750 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; 757 758 if (con == NULL) { 759 /* Reject for non-existing connection is fine */ 760 error = (ep->status == 0)? ENOENT : 0; 761 goto out; 762 } 763 764 /* 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. 768 */ 769 770 if ((error = ng_hci_con_untimeout(con)) != 0) 771 goto out; 772 773 switch (con->state) { 774 case NG_HCI_CON_W4_LP_CON_RSP: 775 776 /* 777 * Create HCI command 778 */ 779 780 MGETHDR(m, MB_DONTWAIT, MT_DATA); 781 if (m == NULL) { 782 error = ENOBUFS; 783 goto out; 784 } 785 786 req = mtod(m, struct con_rsp_req *); 787 req->hdr.type = NG_HCI_CMD_PKT; 788 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)); 794 795 bcopy(&ep->bdaddr, &req->cp.acc.bdaddr, 796 sizeof(req->cp.acc.bdaddr)); 797 798 /* 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. 804 */ 805 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; 811 812 /* 813 * Adjust connection state 814 */ 815 816 if (hook == unit->acl) 817 con->flags |= NG_HCI_CON_NOTIFY_ACL; 818 else 819 con->flags |= NG_HCI_CON_NOTIFY_SCO; 820 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)); 828 829 bcopy(&ep->bdaddr, &req->cp.rej.bdaddr, 830 sizeof(req->cp.rej.bdaddr)); 831 832 req->cp.rej.reason = ep->status; 833 834 /* 835 * Free connection descritor 836 * Item will be deleted just before return. 837 */ 838 839 ng_hci_free_con(con); 840 } 841 842 m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length; 843 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; 849 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; 859 860 default: 861 panic( 862 "%s: %s - Invalid connection state=%d\n", 863 __func__, NG_NODE_NAME(unit->node), con->state); 864 break; 865 } 866 out: 867 NG_FREE_ITEM(item); 868 869 return (error); 870 } /* ng_hci_lp_con_rsp */ 871 872 /* 873 * Send LP_DisconnectInd to the upper layer protocol 874 */ 875 876 int 877 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason) 878 { 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; 883 884 /* 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. 889 */ 890 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); 897 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; 902 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); 908 } 909 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); 915 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; 920 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); 926 927 return (0); 928 } /* ng_hci_lp_discon_ind */ 929 930 /* 931 * Process LP_QoSReq action from the upper layer protocol 932 */ 933 934 int 935 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook) 936 { 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; 945 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); 951 952 error = ENXIO; 953 goto out; 954 } 955 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); 961 962 error = EMSGSIZE; 963 goto out; 964 } 965 966 ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data); 967 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); 973 974 error = EINVAL; 975 goto out; 976 } 977 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); 981 982 error = EINVAL; 983 goto out; 984 } 985 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); 991 992 error = EINVAL; 993 goto out; 994 } 995 996 /* 997 * Create HCI command 998 */ 999 1000 MGETHDR(m, MB_DONTWAIT, MT_DATA); 1001 if (m == NULL) { 1002 error = ENOBUFS; 1003 goto out; 1004 } 1005 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)); 1012 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); 1020 1021 /* 1022 * Adjust connection state 1023 */ 1024 1025 if (hook == unit->acl) 1026 con->flags |= NG_HCI_CON_NOTIFY_ACL; 1027 else 1028 con->flags |= NG_HCI_CON_NOTIFY_SCO; 1029 1030 /* 1031 * Queue and send HCI command 1032 */ 1033 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); 1039 1040 return (error); 1041 } /* ng_hci_lp_qos_req */ 1042 1043 /* 1044 * Send LP_QoSCfm event to the upper layer protocol 1045 */ 1046 1047 int 1048 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status) 1049 { 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; 1054 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; 1063 1064 NG_SEND_MSG_HOOK(error, unit->node, msg, 1065 unit->acl, 0); 1066 } 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); 1071 1072 con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 1073 } 1074 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; 1083 1084 NG_SEND_MSG_HOOK(error, unit->node, msg, 1085 unit->sco, 0); 1086 } 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); 1091 1092 con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 1093 } 1094 1095 return (0); 1096 } /* ng_hci_lp_qos_cfm */ 1097 1098 /* 1099 * Send LP_QoSViolationInd event to the upper layer protocol 1100 */ 1101 1102 int 1103 ng_hci_lp_qos_ind(ng_hci_unit_con_p con) 1104 { 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; 1109 1110 /* 1111 * QoS Violation can only be generated for ACL connection handles. 1112 * Both ACL and SCO upstream hooks will receive notification. 1113 */ 1114 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); 1120 1121 ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1122 ep->con_handle = con->con_handle; 1123 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); 1129 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); 1135 1136 ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1137 ep->con_handle = con->con_handle; 1138 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); 1144 1145 return (0); 1146 } /* ng_hci_lp_qos_ind */ 1147 1148 /* 1149 * Process connection timeout 1150 */ 1151 1152 void 1153 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle) 1154 { 1155 ng_hci_unit_p unit = NULL; 1156 ng_hci_unit_con_p con = NULL; 1157 1158 if (NG_NODE_NOT_VALID(node)) { 1159 printf("%s: Netgraph node is not valid\n", __func__); 1160 return; 1161 } 1162 1163 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 1164 con = ng_hci_con_by_handle(unit, con_handle); 1165 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; 1171 } 1172 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; 1179 } 1180 1181 con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; 1182 1183 /* 1184 * We expect to receive connection timeout in one of the following 1185 * states: 1186 * 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. 1190 * 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. 1194 */ 1195 1196 switch (con->state) { 1197 case NG_HCI_CON_W4_LP_CON_RSP: 1198 break; 1199 1200 case NG_HCI_CON_W4_CONN_COMPLETE: 1201 ng_hci_lp_con_cfm(con, 0xee); 1202 break; 1203 1204 default: 1205 panic( 1206 "%s: %s - Invalid connection state=%d\n", 1207 __func__, NG_NODE_NAME(node), con->state); 1208 break; 1209 } 1210 1211 ng_hci_free_con(con); 1212 } /* ng_hci_process_con_timeout */ 1213 1214