1 /* 2 * ng_hci_evnt.c 3 * 4 * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $Id: ng_hci_evnt.c,v 1.18 2002/11/12 22:35:39 max Exp $ 29 * $FreeBSD$ 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/endian.h> 36 #include <sys/malloc.h> 37 #include <sys/mbuf.h> 38 #include <sys/queue.h> 39 #include <netgraph/ng_message.h> 40 #include <netgraph/netgraph.h> 41 #include "ng_bluetooth.h" 42 #include "ng_hci.h" 43 #include "ng_hci_var.h" 44 #include "ng_hci_cmds.h" 45 #include "ng_hci_evnt.h" 46 #include "ng_hci_ulpi.h" 47 #include "ng_hci_misc.h" 48 49 /****************************************************************************** 50 ****************************************************************************** 51 ** HCI event processing module 52 ****************************************************************************** 53 ******************************************************************************/ 54 55 /* 56 * Event processing routines 57 */ 58 59 static int inquiry_result (ng_hci_unit_p, struct mbuf *); 60 static int con_compl (ng_hci_unit_p, struct mbuf *); 61 static int con_req (ng_hci_unit_p, struct mbuf *); 62 static int discon_compl (ng_hci_unit_p, struct mbuf *); 63 static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *); 64 static int qos_setup_compl (ng_hci_unit_p, struct mbuf *); 65 static int hardware_error (ng_hci_unit_p, struct mbuf *); 66 static int role_change (ng_hci_unit_p, struct mbuf *); 67 static int num_compl_pkts (ng_hci_unit_p, struct mbuf *); 68 static int mode_change (ng_hci_unit_p, struct mbuf *); 69 static int data_buffer_overflow (ng_hci_unit_p, struct mbuf *); 70 static int read_clock_offset_compl (ng_hci_unit_p, struct mbuf *); 71 static int qos_violation (ng_hci_unit_p, struct mbuf *); 72 static int page_scan_mode_change (ng_hci_unit_p, struct mbuf *); 73 static int page_scan_rep_mode_change (ng_hci_unit_p, struct mbuf *); 74 static int sync_con_queue (ng_hci_unit_p, ng_hci_unit_con_p, int); 75 static int send_data_packets (ng_hci_unit_p, int, int); 76 77 /* 78 * Process HCI event packet 79 */ 80 81 int 82 ng_hci_process_event(ng_hci_unit_p unit, struct mbuf *event) 83 { 84 ng_hci_event_pkt_t *hdr = NULL; 85 int error = 0; 86 87 /* Get event packet header */ 88 NG_HCI_M_PULLUP(event, sizeof(*hdr)); 89 if (event == NULL) 90 return (ENOBUFS); 91 92 hdr = mtod(event, ng_hci_event_pkt_t *); 93 94 NG_HCI_INFO( 95 "%s: %s - got HCI event=%#x, length=%d\n", 96 __func__, NG_NODE_NAME(unit->node), hdr->event, hdr->length); 97 98 /* Get rid of event header and process event */ 99 m_adj(event, sizeof(*hdr)); 100 101 switch (hdr->event) { 102 case NG_HCI_EVENT_INQUIRY_COMPL: 103 /* case NG_HCI_EVENT_MODE_CHANGE: */ 104 case NG_HCI_EVENT_RETURN_LINK_KEYS: 105 case NG_HCI_EVENT_PIN_CODE_REQ: 106 case NG_HCI_EVENT_LINK_KEY_REQ: 107 case NG_HCI_EVENT_LINK_KEY_NOTIFICATION: 108 case NG_HCI_EVENT_LOOPBACK_COMMAND: 109 case NG_HCI_EVENT_AUTH_COMPL: 110 case NG_HCI_EVENT_ENCRYPTION_CHANGE: 111 case NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL: 112 case NG_HCI_EVENT_MASTER_LINK_KEY_COMPL: 113 case NG_HCI_EVENT_FLUSH_OCCUR: /* XXX Do we have to handle it? */ 114 /* case NG_HCI_EVENT_ROLE_CHANGE: */ 115 case NG_HCI_EVENT_MAX_SLOT_CHANGE: 116 case NG_HCI_EVENT_CON_PKT_TYPE_CHANGED: 117 case NG_HCI_EVENT_BT_LOGO: 118 case NG_HCI_EVENT_VENDOR: 119 case NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL: 120 case NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL: 121 /* These do not need post processing */ 122 NG_FREE_M(event); 123 break; 124 125 case NG_HCI_EVENT_INQUIRY_RESULT: 126 error = inquiry_result(unit, event); 127 break; 128 129 case NG_HCI_EVENT_CON_COMPL: 130 error = con_compl(unit, event); 131 break; 132 133 case NG_HCI_EVENT_CON_REQ: 134 error = con_req(unit, event); 135 break; 136 137 case NG_HCI_EVENT_DISCON_COMPL: 138 error = discon_compl(unit, event); 139 break; 140 141 case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL: 142 error = read_remote_features_compl(unit, event); 143 break; 144 145 case NG_HCI_EVENT_QOS_SETUP_COMPL: 146 error = qos_setup_compl(unit, event); 147 break; 148 149 case NG_HCI_EVENT_COMMAND_COMPL: 150 error = ng_hci_process_command_complete(unit, event); 151 break; 152 153 case NG_HCI_EVENT_COMMAND_STATUS: 154 error = ng_hci_process_command_status(unit, event); 155 break; 156 157 case NG_HCI_EVENT_HARDWARE_ERROR: 158 error = hardware_error(unit, event); 159 break; 160 161 case NG_HCI_EVENT_ROLE_CHANGE: 162 error = role_change(unit, event); 163 break; 164 165 case NG_HCI_EVENT_NUM_COMPL_PKTS: 166 error = num_compl_pkts(unit, event); 167 break; 168 169 case NG_HCI_EVENT_MODE_CHANGE: 170 error = mode_change(unit, event); 171 break; 172 173 case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW: 174 error = data_buffer_overflow(unit, event); 175 break; 176 177 case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL: 178 error = read_clock_offset_compl(unit, event); 179 break; 180 181 case NG_HCI_EVENT_QOS_VIOLATION: 182 error = qos_violation(unit, event); 183 break; 184 185 case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE: 186 error = page_scan_mode_change(unit, event); 187 break; 188 189 case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE: 190 error = page_scan_rep_mode_change(unit, event); 191 break; 192 193 default: 194 NG_FREE_M(event); 195 error = EINVAL; 196 break; 197 } 198 199 return (error); 200 } /* ng_hci_process_event */ 201 202 /* 203 * Send ACL and/or SCO data to the unit driver 204 */ 205 206 void 207 ng_hci_send_data(ng_hci_unit_p unit) 208 { 209 int count; 210 211 /* Send ACL data */ 212 NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count); 213 214 NG_HCI_INFO( 215 "%s: %s - sending ACL data packets, count=%d\n", 216 __func__, NG_NODE_NAME(unit->node), count); 217 218 if (count > 0) { 219 count = send_data_packets(unit, NG_HCI_LINK_ACL, count); 220 NG_HCI_STAT_ACL_SENT(unit->stat, count); 221 NG_HCI_BUFF_ACL_USE(unit->buffer, count); 222 } 223 224 /* Send SCO data */ 225 NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count); 226 227 NG_HCI_INFO( 228 "%s: %s - sending SCO data packets, count=%d\n", 229 __func__, NG_NODE_NAME(unit->node), count); 230 231 if (count > 0) { 232 count = send_data_packets(unit, NG_HCI_LINK_SCO, count); 233 NG_HCI_STAT_SCO_SENT(unit->stat, count); 234 NG_HCI_BUFF_SCO_USE(unit->buffer, count); 235 } 236 } /* ng_hci_send_data */ 237 238 /* 239 * Send data packets to the lower layer. 240 */ 241 242 static int 243 send_data_packets(ng_hci_unit_p unit, int link_type, int limit) 244 { 245 ng_hci_unit_con_p con = NULL, winner = NULL; 246 item_p item = NULL; 247 int min_pending, total_sent, sent, error, v; 248 249 for (total_sent = 0; limit > 0; ) { 250 min_pending = 0x0fffffff; 251 winner = NULL; 252 253 /* 254 * Find the connection that has has data to send 255 * and the smallest number of pending packets 256 */ 257 258 LIST_FOREACH(con, &unit->con_list, next) { 259 if (con->link_type != link_type) 260 continue; 261 if (NG_BT_ITEMQ_LEN(&con->conq) == 0) 262 continue; 263 264 if (con->pending < min_pending) { 265 winner = con; 266 min_pending = con->pending; 267 } 268 } 269 270 if (winner == NULL) 271 break; 272 273 /* 274 * OK, we have a winner now send as much packets as we can 275 * Count the number of packets we have sent and then sync 276 * winner connection queue. 277 */ 278 279 for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) { 280 NG_BT_ITEMQ_DEQUEUE(&winner->conq, item); 281 if (item == NULL) 282 break; 283 284 NG_HCI_INFO( 285 "%s: %s - sending data packet, handle=%d, len=%d\n", 286 __func__, NG_NODE_NAME(unit->node), 287 winner->con_handle, NGI_M(item)->m_pkthdr.len); 288 289 /* Check if driver hook still there */ 290 v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv)); 291 if (!v || (unit->state & NG_HCI_UNIT_READY) != 292 NG_HCI_UNIT_READY) { 293 NG_HCI_ERR( 294 "%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n", 295 __func__, NG_NODE_NAME(unit->node), 296 NG_HCI_HOOK_DRV, ((v)? "" : "not "), 297 unit->state); 298 299 NG_FREE_ITEM(item); 300 error = ENOTCONN; 301 } else { 302 v = NGI_M(item)->m_pkthdr.len; 303 304 /* Give packet to raw hook */ 305 ng_hci_mtap(unit, NGI_M(item)); 306 307 /* ... and forward item to the driver */ 308 NG_FWD_ITEM_HOOK(error, item, unit->drv); 309 } 310 311 if (error != 0) { 312 NG_HCI_ERR( 313 "%s: %s - could not send data packet, handle=%d, error=%d\n", 314 __func__, NG_NODE_NAME(unit->node), 315 winner->con_handle, error); 316 break; 317 } 318 319 winner->pending ++; 320 NG_HCI_STAT_BYTES_SENT(unit->stat, v); 321 } 322 323 /* 324 * Sync connection queue for the winner 325 */ 326 327 sync_con_queue(unit, winner, sent); 328 } 329 330 return (total_sent); 331 } /* send_data_packets */ 332 333 /* 334 * Send flow control messages to the upper layer 335 */ 336 337 static int 338 sync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed) 339 { 340 hook_p hook = NULL; 341 struct ng_mesg *msg = NULL; 342 ng_hci_sync_con_queue_ep *state = NULL; 343 int error; 344 345 hook = (con->link_type == NG_HCI_LINK_ACL)? unit->acl : unit->sco; 346 if (hook == NULL || NG_HOOK_NOT_VALID(hook)) 347 return (ENOTCONN); 348 349 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE, 350 sizeof(*state), M_NOWAIT); 351 if (msg == NULL) 352 return (ENOMEM); 353 354 state = (ng_hci_sync_con_queue_ep *)(msg->data); 355 state->con_handle = con->con_handle; 356 state->completed = completed; 357 358 NG_SEND_MSG_HOOK(error, unit->node, msg, hook, NULL); 359 360 return (error); 361 } /* sync_con_queue */ 362 363 /* Inquiry result event */ 364 static int 365 inquiry_result(ng_hci_unit_p unit, struct mbuf *event) 366 { 367 ng_hci_inquiry_result_ep *ep = NULL; 368 ng_hci_neighbor_p n = NULL; 369 bdaddr_t bdaddr; 370 int error = 0; 371 372 NG_HCI_M_PULLUP(event, sizeof(*ep)); 373 if (event == NULL) 374 return (ENOBUFS); 375 376 ep = mtod(event, ng_hci_inquiry_result_ep *); 377 m_adj(event, sizeof(*ep)); 378 379 for (; ep->num_responses > 0; ep->num_responses --) { 380 /* Get remote unit address */ 381 m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr); 382 m_adj(event, sizeof(bdaddr)); 383 384 /* Lookup entry in the cache */ 385 n = ng_hci_get_neighbor(unit, &bdaddr); 386 if (n == NULL) { 387 /* Create new entry */ 388 n = ng_hci_new_neighbor(unit); 389 if (n == NULL) { 390 error = ENOMEM; 391 break; 392 } 393 } else 394 getmicrotime(&n->updated); 395 396 bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 397 398 /* XXX call m_pullup here? */ 399 400 n->page_scan_rep_mode = *mtod(event, u_int8_t *); 401 m_adj(event, sizeof(u_int8_t)); 402 403 /* page_scan_period_mode */ 404 m_adj(event, sizeof(u_int8_t)); 405 406 n->page_scan_mode = *mtod(event, u_int8_t *); 407 m_adj(event, sizeof(u_int8_t)); 408 409 /* class */ 410 m_adj(event, NG_HCI_CLASS_SIZE); 411 412 /* clock offset */ 413 m_copydata(event, 0, sizeof(n->clock_offset), 414 (caddr_t) &n->clock_offset); 415 n->clock_offset = le16toh(n->clock_offset); 416 } 417 418 NG_FREE_M(event); 419 420 return (error); 421 } /* inquiry_result */ 422 423 /* Connection complete event */ 424 static int 425 con_compl(ng_hci_unit_p unit, struct mbuf *event) 426 { 427 ng_hci_con_compl_ep *ep = NULL; 428 ng_hci_unit_con_p con = NULL; 429 int error = 0; 430 431 NG_HCI_M_PULLUP(event, sizeof(*ep)); 432 if (event == NULL) 433 return (ENOBUFS); 434 435 ep = mtod(event, ng_hci_con_compl_ep *); 436 437 /* 438 * Find the first connection descriptor that matches the following: 439 * 440 * 1) con->link_type == ep->link_type 441 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE 442 * 3) con->bdaddr == ep->bdaddr 443 */ 444 445 LIST_FOREACH(con, &unit->con_list, next) 446 if (con->link_type == ep->link_type && 447 con->state == NG_HCI_CON_W4_CONN_COMPLETE && 448 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 449 break; 450 451 /* 452 * Two possible cases: 453 * 454 * 1) We have found connection descriptor. That means upper layer has 455 * requested this connection via LP_CON_REQ message 456 * 457 * 2) We do not have connection descriptor. That means upper layer 458 * nas not requested this connection or (less likely) we gave up 459 * on this connection (timeout). The most likely scenario is that 460 * we have received Create_Connection/Add_SCO_Connection command 461 * from the RAW hook 462 */ 463 464 if (con == NULL) { 465 if (ep->status != 0) 466 goto out; 467 468 con = ng_hci_new_con(unit, ep->link_type); 469 if (con == NULL) { 470 error = ENOMEM; 471 goto out; 472 } 473 474 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 475 } else 476 ng_hci_con_untimeout(con); 477 478 /* 479 * Update connection descriptor and send notification 480 * to the upper layers. 481 */ 482 483 con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 484 con->encryption_mode = ep->encryption_mode; 485 486 ng_hci_lp_con_cfm(con, ep->status); 487 488 /* Adjust connection state */ 489 if (ep->status != 0) 490 ng_hci_free_con(con); 491 else { 492 con->state = NG_HCI_CON_OPEN; 493 494 /* 495 * Change link policy for the ACL connections. Enable all 496 * supported link modes. Enable Role switch as well if 497 * device supports it. 498 */ 499 500 if (ep->link_type == NG_HCI_LINK_ACL) { 501 struct __link_policy { 502 ng_hci_cmd_pkt_t hdr; 503 ng_hci_write_link_policy_settings_cp cp; 504 } __attribute__ ((packed)) *lp; 505 struct mbuf *m; 506 507 MGETHDR(m, M_DONTWAIT, MT_DATA); 508 if (m != NULL) { 509 m->m_pkthdr.len = m->m_len = sizeof(*lp); 510 lp = mtod(m, struct __link_policy *); 511 512 lp->hdr.type = NG_HCI_CMD_PKT; 513 lp->hdr.opcode = htole16(NG_HCI_OPCODE( 514 NG_HCI_OGF_LINK_POLICY, 515 NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS)); 516 lp->hdr.length = sizeof(lp->cp); 517 518 lp->cp.con_handle = ep->con_handle; 519 520 lp->cp.settings = 0; 521 if (unit->features[0] & NG_HCI_LMP_SWITCH) 522 lp->cp.settings |= 0x1; 523 if (unit->features[0] & NG_HCI_LMP_HOLD_MODE) 524 lp->cp.settings |= 0x2; 525 if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE) 526 lp->cp.settings |= 0x4; 527 if (unit->features[1] & NG_HCI_LMP_PARK_MODE) 528 lp->cp.settings |= 0x8; 529 530 lp->cp.settings &= unit->link_policy_mask; 531 lp->cp.settings = htole16(lp->cp.settings); 532 533 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 534 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 535 ng_hci_send_command(unit); 536 } 537 } 538 } 539 out: 540 NG_FREE_M(event); 541 542 return (error); 543 } /* com_compl */ 544 545 /* Connection request event */ 546 static int 547 con_req(ng_hci_unit_p unit, struct mbuf *event) 548 { 549 ng_hci_con_req_ep *ep = NULL; 550 ng_hci_unit_con_p con = NULL; 551 int error = 0; 552 553 NG_HCI_M_PULLUP(event, sizeof(*ep)); 554 if (event == NULL) 555 return (ENOBUFS); 556 557 ep = mtod(event, ng_hci_con_req_ep *); 558 559 /* 560 * Find the first connection descriptor that matches the following: 561 * 562 * 1) con->link_type == ep->link_type 563 * 564 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || 565 * con->state == NG_HCI_CON_W4_CONN_COMPL 566 * 567 * 3) con->bdaddr == ep->bdaddr 568 * 569 * Possible cases: 570 * 571 * 1) We do not have connection descriptor. This is simple. Create 572 * new fresh connection descriptor and send notification to the 573 * appropriate upstream hook (based on link_type). 574 * 575 * 2) We found connection handle. This is more complicated. 576 * 577 * 2.1) ACL links 578 * 579 * Since only one ACL link can exist between each pair of 580 * units then we have a race. Our upper layer has requested 581 * an ACL connection to the remote unit, but we did not send 582 * command yet. At the same time the remote unit has requested 583 * an ACL connection from us. In this case we will ignore 584 * Connection_Request event. This probably will cause connect 585 * failure on both units. 586 * 587 * 2.2) SCO links 588 * 589 * The spec on page 45 says : 590 * 591 * "The master can support up to three SCO links to the same 592 * slave or to different slaves. A slave can support up to 593 * three SCO links from the same master, or two SCO links if 594 * the links originate from different masters." 595 * 596 * The only problem is how to handle multiple SCO links between 597 * matster and slave. For now we will assume that multiple SCO 598 * links MUST be opened one after another. 599 */ 600 601 LIST_FOREACH(con, &unit->con_list, next) 602 if (con->link_type == ep->link_type && 603 (con->state == NG_HCI_CON_W4_LP_CON_RSP || 604 con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 605 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 606 break; 607 608 if (con == NULL) { 609 con = ng_hci_new_con(unit, ep->link_type); 610 if (con != NULL) { 611 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 612 613 con->state = NG_HCI_CON_W4_LP_CON_RSP; 614 ng_hci_con_timeout(con); 615 616 error = ng_hci_lp_con_ind(con, ep->uclass); 617 if (error != 0) 618 ng_hci_free_con(con); 619 } else 620 error = ENOMEM; 621 } 622 623 NG_FREE_M(event); 624 625 return (error); 626 } /* con_req */ 627 628 /* Disconnect complete event */ 629 static int 630 discon_compl(ng_hci_unit_p unit, struct mbuf *event) 631 { 632 ng_hci_discon_compl_ep *ep = NULL; 633 ng_hci_unit_con_p con = NULL; 634 int error = 0; 635 u_int16_t h; 636 637 NG_HCI_M_PULLUP(event, sizeof(*ep)); 638 if (event == NULL) 639 return (ENOBUFS); 640 641 ep = mtod(event, ng_hci_discon_compl_ep *); 642 643 /* 644 * XXX 645 * Do we have to send notification if ep->status != 0? 646 * For now we will send notification for both ACL and SCO connections 647 * ONLY if ep->status == 0. 648 */ 649 650 if (ep->status == 0) { 651 h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 652 con = ng_hci_con_by_handle(unit, h); 653 if (con != NULL) { 654 error = ng_hci_lp_discon_ind(con, ep->reason); 655 ng_hci_free_con(con); 656 } else { 657 NG_HCI_ALERT( 658 "%s: %s - invalid connection handle=%d\n", 659 __func__, NG_NODE_NAME(unit->node), h); 660 error = ENOENT; 661 } 662 } 663 664 NG_FREE_M(event); 665 666 return (error); 667 } /* discon_compl */ 668 669 /* Read remote feature complete event */ 670 static int 671 read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event) 672 { 673 ng_hci_read_remote_features_compl_ep *ep = NULL; 674 ng_hci_unit_con_p con = NULL; 675 ng_hci_neighbor_p n = NULL; 676 u_int16_t h; 677 int error = 0; 678 679 NG_HCI_M_PULLUP(event, sizeof(*ep)); 680 if (event == NULL) 681 return (ENOBUFS); 682 683 ep = mtod(event, ng_hci_read_remote_features_compl_ep *); 684 685 if (ep->status == 0) { 686 /* Check if we have this connection handle */ 687 h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 688 con = ng_hci_con_by_handle(unit, h); 689 if (con == NULL) { 690 NG_HCI_ALERT( 691 "%s: %s - invalid connection handle=%d\n", 692 __func__, NG_NODE_NAME(unit->node), h); 693 error = ENOENT; 694 goto out; 695 } 696 697 /* Update cache entry */ 698 n = ng_hci_get_neighbor(unit, &con->bdaddr); 699 if (n == NULL) { 700 n = ng_hci_new_neighbor(unit); 701 if (n == NULL) { 702 error = ENOMEM; 703 goto out; 704 } 705 706 bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 707 } else 708 getmicrotime(&n->updated); 709 710 bcopy(ep->features, n->features, sizeof(n->features)); 711 } else 712 NG_HCI_ERR( 713 "%s: %s - failed to read remote unit features, status=%d\n", 714 __func__, NG_NODE_NAME(unit->node), ep->status); 715 out: 716 NG_FREE_M(event); 717 718 return (error); 719 } /* read_remote_features_compl */ 720 721 /* QoS setup complete event */ 722 static int 723 qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event) 724 { 725 ng_hci_qos_setup_compl_ep *ep = NULL; 726 ng_hci_unit_con_p con = NULL; 727 u_int16_t h; 728 int error = 0; 729 730 NG_HCI_M_PULLUP(event, sizeof(*ep)); 731 if (event == NULL) 732 return (ENOBUFS); 733 734 ep = mtod(event, ng_hci_qos_setup_compl_ep *); 735 736 /* Check if we have this connection handle */ 737 h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 738 con = ng_hci_con_by_handle(unit, h); 739 if (con == NULL) { 740 NG_HCI_ALERT( 741 "%s: %s - invalid connection handle=%d\n", 742 __func__, NG_NODE_NAME(unit->node), h); 743 error = ENOENT; 744 } else if (con->link_type != NG_HCI_LINK_ACL) { 745 NG_HCI_ALERT( 746 "%s: %s - invalid link type=%d, handle=%d\n", 747 __func__, NG_NODE_NAME(unit->node), con->link_type, h); 748 error = EINVAL; 749 } else if (con->state != NG_HCI_CON_OPEN) { 750 NG_HCI_ALERT( 751 "%s: %s - invalid connection state=%d, handle=%d\n", 752 __func__, NG_NODE_NAME(unit->node), 753 con->state, h); 754 error = EINVAL; 755 } else /* Notify upper layer */ 756 error = ng_hci_lp_qos_cfm(con, ep->status); 757 758 NG_FREE_M(event); 759 760 return (error); 761 } /* qos_setup_compl */ 762 763 /* Hardware error event */ 764 static int 765 hardware_error(ng_hci_unit_p unit, struct mbuf *event) 766 { 767 NG_HCI_ALERT( 768 "%s: %s - hardware error %#x\n", 769 __func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *)); 770 771 NG_FREE_M(event); 772 773 return (0); 774 } /* hardware_error */ 775 776 /* Role change event */ 777 static int 778 role_change(ng_hci_unit_p unit, struct mbuf *event) 779 { 780 ng_hci_role_change_ep *ep = NULL; 781 ng_hci_unit_con_p con = NULL; 782 783 NG_HCI_M_PULLUP(event, sizeof(*ep)); 784 if (event == NULL) 785 return (ENOBUFS); 786 787 ep = mtod(event, ng_hci_role_change_ep *); 788 789 if (ep->status == 0) { 790 /* XXX shoud we also change "role" for SCO connections? */ 791 con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL); 792 if (con != NULL) 793 con->role = ep->role; 794 else 795 NG_HCI_ALERT( 796 "%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n", 797 __func__, NG_NODE_NAME(unit->node), 798 ep->bdaddr.b[5], ep->bdaddr.b[4], 799 ep->bdaddr.b[3], ep->bdaddr.b[2], 800 ep->bdaddr.b[1], ep->bdaddr.b[0]); 801 } else 802 NG_HCI_ERR( 803 "%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n", 804 __func__, NG_NODE_NAME(unit->node), ep->status, 805 ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], 806 ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); 807 808 NG_FREE_M(event); 809 810 return (0); 811 } /* role_change */ 812 813 /* Number of completed packets event */ 814 static int 815 num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event) 816 { 817 ng_hci_num_compl_pkts_ep *ep = NULL; 818 ng_hci_unit_con_p con = NULL; 819 u_int16_t h, p; 820 821 NG_HCI_M_PULLUP(event, sizeof(*ep)); 822 if (event == NULL) 823 return (ENOBUFS); 824 825 ep = mtod(event, ng_hci_num_compl_pkts_ep *); 826 m_adj(event, sizeof(*ep)); 827 828 for (; ep->num_con_handles > 0; ep->num_con_handles --) { 829 /* Get connection handle */ 830 m_copydata(event, 0, sizeof(h), (caddr_t) &h); 831 m_adj(event, sizeof(h)); 832 h = NG_HCI_CON_HANDLE(le16toh(h)); 833 834 /* Get number of completed packets */ 835 m_copydata(event, 0, sizeof(p), (caddr_t) &p); 836 m_adj(event, sizeof(p)); 837 p = le16toh(p); 838 839 /* Check if we have this connection handle */ 840 con = ng_hci_con_by_handle(unit, h); 841 if (con != NULL) { 842 con->pending -= p; 843 if (con->pending < 0) { 844 NG_HCI_WARN( 845 "%s: %s - pending packet counter is out of sync! " \ 846 "handle=%d, pending=%d, ncp=%d\n", __func__, NG_NODE_NAME(unit->node), 847 con->con_handle, con->pending, p); 848 849 con->pending = 0; 850 } 851 852 /* Update buffer descriptor */ 853 if (con->link_type == NG_HCI_LINK_ACL) 854 NG_HCI_BUFF_ACL_FREE(unit->buffer, p); 855 else 856 NG_HCI_BUFF_SCO_FREE(unit->buffer, p); 857 } else 858 NG_HCI_ALERT( 859 "%s: %s - invalid connection handle=%d\n", 860 __func__, NG_NODE_NAME(unit->node), h); 861 } 862 863 NG_FREE_M(event); 864 865 /* Send more data */ 866 ng_hci_send_data(unit); 867 868 return (0); 869 } /* num_compl_pkts */ 870 871 /* Mode change event */ 872 static int 873 mode_change(ng_hci_unit_p unit, struct mbuf *event) 874 { 875 ng_hci_mode_change_ep *ep = NULL; 876 ng_hci_unit_con_p con = NULL; 877 int error = 0; 878 879 NG_HCI_M_PULLUP(event, sizeof(*ep)); 880 if (event == NULL) 881 return (ENOBUFS); 882 883 ep = mtod(event, ng_hci_mode_change_ep *); 884 885 if (ep->status == 0) { 886 u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 887 888 con = ng_hci_con_by_handle(unit, h); 889 if (con == NULL) { 890 NG_HCI_ALERT( 891 "%s: %s - invalid connection handle=%d\n", 892 __func__, NG_NODE_NAME(unit->node), h); 893 error = ENOENT; 894 } else if (con->link_type != NG_HCI_LINK_ACL) { 895 NG_HCI_ALERT( 896 "%s: %s - invalid link type=%d\n", 897 __func__, NG_NODE_NAME(unit->node), 898 con->link_type); 899 error = EINVAL; 900 } else 901 con->mode = ep->unit_mode; 902 } else 903 NG_HCI_ERR( 904 "%s: %s - failed to change mode, status=%d\n", 905 __func__, NG_NODE_NAME(unit->node), ep->status); 906 907 NG_FREE_M(event); 908 909 return (error); 910 } /* mode_change */ 911 912 /* Data buffer overflow event */ 913 static int 914 data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event) 915 { 916 NG_HCI_ALERT( 917 "%s: %s - %s data buffer overflow\n", 918 __func__, NG_NODE_NAME(unit->node), 919 (*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO"); 920 921 NG_FREE_M(event); 922 923 return (0); 924 } /* data_buffer_overflow */ 925 926 /* Read clock offset complete event */ 927 static int 928 read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event) 929 { 930 ng_hci_read_clock_offset_compl_ep *ep = NULL; 931 ng_hci_unit_con_p con = NULL; 932 ng_hci_neighbor_p n = NULL; 933 int error = 0; 934 935 NG_HCI_M_PULLUP(event, sizeof(*ep)); 936 if (event == NULL) 937 return (ENOBUFS); 938 939 ep = mtod(event, ng_hci_read_clock_offset_compl_ep *); 940 941 if (ep->status == 0) { 942 u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 943 944 con = ng_hci_con_by_handle(unit, h); 945 if (con == NULL) { 946 NG_HCI_ALERT( 947 "%s: %s - invalid connection handle=%d\n", 948 __func__, NG_NODE_NAME(unit->node), h); 949 error = ENOENT; 950 goto out; 951 } 952 953 /* Update cache entry */ 954 n = ng_hci_get_neighbor(unit, &con->bdaddr); 955 if (n == NULL) { 956 n = ng_hci_new_neighbor(unit); 957 if (n == NULL) { 958 error = ENOMEM; 959 goto out; 960 } 961 962 bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 963 } else 964 getmicrotime(&n->updated); 965 966 n->clock_offset = le16toh(ep->clock_offset); 967 } else 968 NG_HCI_ERR( 969 "%s: %s - failed to Read Remote Clock Offset, status=%d\n", 970 __func__, NG_NODE_NAME(unit->node), ep->status); 971 out: 972 NG_FREE_M(event); 973 974 return (error); 975 } /* read_clock_offset_compl */ 976 977 /* QoS violation event */ 978 static int 979 qos_violation(ng_hci_unit_p unit, struct mbuf *event) 980 { 981 ng_hci_qos_violation_ep *ep = NULL; 982 ng_hci_unit_con_p con = NULL; 983 u_int16_t h; 984 int error = 0; 985 986 NG_HCI_M_PULLUP(event, sizeof(*ep)); 987 if (event == NULL) 988 return (ENOBUFS); 989 990 ep = mtod(event, ng_hci_qos_violation_ep *); 991 992 /* Check if we have this connection handle */ 993 h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 994 con = ng_hci_con_by_handle(unit, h); 995 if (con == NULL) { 996 NG_HCI_ALERT( 997 "%s: %s - invalid connection handle=%d\n", 998 __func__, NG_NODE_NAME(unit->node), h); 999 error = ENOENT; 1000 } else if (con->link_type != NG_HCI_LINK_ACL) { 1001 NG_HCI_ALERT( 1002 "%s: %s - invalid link type=%d\n", 1003 __func__, NG_NODE_NAME(unit->node), con->link_type); 1004 error = EINVAL; 1005 } else if (con->state != NG_HCI_CON_OPEN) { 1006 NG_HCI_ALERT( 1007 "%s: %s - invalid connection state=%d, handle=%d\n", 1008 __func__, NG_NODE_NAME(unit->node), con->state, h); 1009 error = EINVAL; 1010 } else /* Notify upper layer */ 1011 error = ng_hci_lp_qos_ind(con); 1012 1013 NG_FREE_M(event); 1014 1015 return (error); 1016 } /* qos_violation */ 1017 1018 /* Page scan mode change event */ 1019 static int 1020 page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event) 1021 { 1022 ng_hci_page_scan_mode_change_ep *ep = NULL; 1023 ng_hci_neighbor_p n = NULL; 1024 int error = 0; 1025 1026 NG_HCI_M_PULLUP(event, sizeof(*ep)); 1027 if (event == NULL) 1028 return (ENOBUFS); 1029 1030 ep = mtod(event, ng_hci_page_scan_mode_change_ep *); 1031 1032 /* Update cache entry */ 1033 n = ng_hci_get_neighbor(unit, &ep->bdaddr); 1034 if (n == NULL) { 1035 n = ng_hci_new_neighbor(unit); 1036 if (n == NULL) { 1037 error = ENOMEM; 1038 goto out; 1039 } 1040 1041 bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 1042 } else 1043 getmicrotime(&n->updated); 1044 1045 n->page_scan_mode = ep->page_scan_mode; 1046 out: 1047 NG_FREE_M(event); 1048 1049 return (error); 1050 } /* page_scan_mode_change */ 1051 1052 /* Page scan repetition mode change event */ 1053 static int 1054 page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event) 1055 { 1056 ng_hci_page_scan_rep_mode_change_ep *ep = NULL; 1057 ng_hci_neighbor_p n = NULL; 1058 int error = 0; 1059 1060 NG_HCI_M_PULLUP(event, sizeof(*ep)); 1061 if (event == NULL) 1062 return (ENOBUFS); 1063 1064 ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *); 1065 1066 /* Update cache entry */ 1067 n = ng_hci_get_neighbor(unit, &ep->bdaddr); 1068 if (n == NULL) { 1069 n = ng_hci_new_neighbor(unit); 1070 if (n == NULL) { 1071 error = ENOMEM; 1072 goto out; 1073 } 1074 1075 bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 1076 } else 1077 getmicrotime(&n->updated); 1078 1079 n->page_scan_rep_mode = ep->page_scan_rep_mode; 1080 out: 1081 NG_FREE_M(event); 1082 1083 return (error); 1084 } /* page_scan_rep_mode_change */ 1085 1086