1 /* 2 * ng_l2cap_llpi.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_l2cap_llpi.c,v 1.5 2003/09/08 19:11:45 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 <netgraph/bluetooth/include/ng_bluetooth.h> 42 #include <netgraph/bluetooth/include/ng_hci.h> 43 #include <netgraph/bluetooth/include/ng_l2cap.h> 44 #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h> 45 #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h> 46 #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h> 47 #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h> 48 #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h> 49 #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h> 50 51 /****************************************************************************** 52 ****************************************************************************** 53 ** Lower Layer Protocol (HCI) Interface module 54 ****************************************************************************** 55 ******************************************************************************/ 56 57 /* 58 * Send LP_ConnectReq event to the lower layer protocol. Create new connection 59 * descriptor and initialize it. Create LP_ConnectReq event and send it to the 60 * lower layer, then adjust connection state and start timer. The function WILL 61 * FAIL if connection to the remote unit already exists. 62 */ 63 64 int 65 ng_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr) 66 { 67 struct ng_mesg *msg = NULL; 68 ng_hci_lp_con_req_ep *ep = NULL; 69 ng_l2cap_con_p con = NULL; 70 int error = 0; 71 72 /* Verify that we DO NOT have connection to the remote unit */ 73 con = ng_l2cap_con_by_addr(l2cap, bdaddr); 74 if (con != NULL) { 75 NG_L2CAP_ALERT( 76 "%s: %s - unexpected LP_ConnectReq event. " \ 77 "Connection already exists, state=%d, con_handle=%d\n", 78 __func__, NG_NODE_NAME(l2cap->node), con->state, 79 con->con_handle); 80 81 return (EEXIST); 82 } 83 84 /* Check if lower layer protocol is still connected */ 85 if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 86 NG_L2CAP_ERR( 87 "%s: %s - hook \"%s\" is not connected or valid\n", 88 __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 89 90 return (ENOTCONN); 91 } 92 93 /* Create and intialize new connection descriptor */ 94 con = ng_l2cap_new_con(l2cap, bdaddr); 95 if (con == NULL) 96 return (ENOMEM); 97 98 /* Create and send LP_ConnectReq event */ 99 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ, 100 sizeof(*ep), M_NOWAIT); 101 if (msg == NULL) { 102 ng_l2cap_free_con(con); 103 104 return (ENOMEM); 105 } 106 107 ep = (ng_hci_lp_con_req_ep *) (msg->data); 108 bcopy(bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); 109 ep->link_type = NG_HCI_LINK_ACL; 110 111 con->flags |= NG_L2CAP_CON_OUTGOING; 112 con->state = NG_L2CAP_W4_LP_CON_CFM; 113 ng_l2cap_lp_timeout(con); 114 115 NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); 116 if (error != 0) { 117 if ((error = ng_l2cap_lp_untimeout(con)) != 0) 118 return (error); 119 120 ng_l2cap_free_con(con); 121 } 122 123 return (error); 124 } /* ng_l2cap_lp_con_req */ 125 126 /* 127 * Process LP_ConnectCfm event from the lower layer protocol. It could be 128 * positive or negative. Verify remote unit address then stop the timer and 129 * process event. 130 */ 131 132 int 133 ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg) 134 { 135 ng_hci_lp_con_cfm_ep *ep = NULL; 136 ng_l2cap_con_p con = NULL; 137 int error = 0; 138 139 /* Check message */ 140 if (msg->header.arglen != sizeof(*ep)) { 141 NG_L2CAP_ALERT( 142 "%s: %s - invalid LP_ConnectCfm[Neg] message size\n", 143 __func__, NG_NODE_NAME(l2cap->node)); 144 error = EMSGSIZE; 145 goto out; 146 } 147 148 ep = (ng_hci_lp_con_cfm_ep *) (msg->data); 149 150 /* Check if we have requested/accepted this connection */ 151 con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr); 152 if (con == NULL) { 153 NG_L2CAP_ERR( 154 "%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n", 155 __func__, NG_NODE_NAME(l2cap->node)); 156 error = ENOENT; 157 goto out; 158 } 159 160 /* Check connection state */ 161 if (con->state != NG_L2CAP_W4_LP_CON_CFM) { 162 NG_L2CAP_ALERT( 163 "%s: %s - unexpected LP_ConnectCfm event. " \ 164 "Invalid connection state, state=%d, con_handle=%d\n", 165 __func__, NG_NODE_NAME(l2cap->node), con->state, 166 con->con_handle); 167 error = EINVAL; 168 goto out; 169 } 170 171 /* 172 * Looks like it is our confirmation. It is safe now to cancel 173 * connection timer and notify upper layer. If timeout already 174 * happened then ignore connection confirmation and let timeout 175 * handle that. 176 */ 177 178 if ((error = ng_l2cap_lp_untimeout(con)) != 0) 179 goto out; 180 181 if (ep->status == 0) { 182 con->state = NG_L2CAP_CON_OPEN; 183 con->con_handle = ep->con_handle; 184 ng_l2cap_lp_deliver(con); 185 } else /* Negative confirmation - remove connection descriptor */ 186 ng_l2cap_con_fail(con, ep->status); 187 out: 188 return (error); 189 } /* ng_l2cap_lp_con_cfm */ 190 191 /* 192 * Process LP_ConnectInd event from the lower layer protocol. This is a good 193 * place to put some extra check on remote unit address and/or class. We could 194 * even forward this information to control hook (or check against internal 195 * black list) and thus implement some kind of firewall. But for now be simple 196 * and create new connection descriptor, start timer and send LP_ConnectRsp 197 * event (i.e. accept connection). 198 */ 199 200 int 201 ng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) 202 { 203 ng_hci_lp_con_ind_ep *ep = NULL; 204 ng_hci_lp_con_rsp_ep *rp = NULL; 205 struct ng_mesg *rsp = NULL; 206 ng_l2cap_con_p con = NULL; 207 int error = 0; 208 209 /* Check message */ 210 if (msg->header.arglen != sizeof(*ep)) { 211 NG_L2CAP_ALERT( 212 "%s: %s - invalid LP_ConnectInd message size\n", 213 __func__, NG_NODE_NAME(l2cap->node)); 214 error = EMSGSIZE; 215 goto out; 216 } 217 218 ep = (ng_hci_lp_con_ind_ep *) (msg->data); 219 220 /* Make sure we have only one connection to the remote unit */ 221 con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr); 222 if (con != NULL) { 223 NG_L2CAP_ALERT( 224 "%s: %s - unexpected LP_ConnectInd event. " \ 225 "Connection already exists, state=%d, con_handle=%d\n", 226 __func__, NG_NODE_NAME(l2cap->node), con->state, 227 con->con_handle); 228 error = EEXIST; 229 goto out; 230 } 231 232 /* Check if lower layer protocol is still connected */ 233 if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 234 NG_L2CAP_ERR( 235 "%s: %s - hook \"%s\" is not connected or valid", 236 __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 237 error = ENOTCONN; 238 goto out; 239 } 240 241 /* Create and intialize new connection descriptor */ 242 con = ng_l2cap_new_con(l2cap, &ep->bdaddr); 243 if (con == NULL) { 244 error = ENOMEM; 245 goto out; 246 } 247 248 /* Create and send LP_ConnectRsp event */ 249 NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP, 250 sizeof(*rp), M_NOWAIT); 251 if (msg == NULL) { 252 ng_l2cap_free_con(con); 253 error = ENOMEM; 254 goto out; 255 } 256 257 rp = (ng_hci_lp_con_rsp_ep *)(rsp->data); 258 rp->status = 0x00; /* accept connection */ 259 rp->link_type = NG_HCI_LINK_ACL; 260 bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr)); 261 262 con->state = NG_L2CAP_W4_LP_CON_CFM; 263 ng_l2cap_lp_timeout(con); 264 265 NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, 0); 266 if (error != 0) { 267 if ((error = ng_l2cap_lp_untimeout(con)) != 0) 268 goto out; 269 270 ng_l2cap_free_con(con); 271 } 272 out: 273 return (error); 274 } /* ng_hci_lp_con_ind */ 275 276 /* 277 * Process LP_DisconnectInd event from the lower layer protocol. We have been 278 * disconnected from the remote unit. So notify the upper layer protocol. 279 */ 280 281 int 282 ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) 283 { 284 ng_hci_lp_discon_ind_ep *ep = NULL; 285 ng_l2cap_con_p con = NULL; 286 int error = 0; 287 288 /* Check message */ 289 if (msg->header.arglen != sizeof(*ep)) { 290 NG_L2CAP_ALERT( 291 "%s: %s - invalid LP_DisconnectInd message size\n", 292 __func__, NG_NODE_NAME(l2cap->node)); 293 error = EMSGSIZE; 294 goto out; 295 } 296 297 ep = (ng_hci_lp_discon_ind_ep *) (msg->data); 298 299 /* Check if we have this connection */ 300 con = ng_l2cap_con_by_handle(l2cap, ep->con_handle); 301 if (con == NULL) { 302 NG_L2CAP_ERR( 303 "%s: %s - unexpected LP_DisconnectInd event. " \ 304 "Connection does not exist, con_handle=%d\n", 305 __func__, NG_NODE_NAME(l2cap->node), ep->con_handle); 306 error = ENOENT; 307 goto out; 308 } 309 310 /* XXX Verify connection state -- do we need to check this? */ 311 if (con->state != NG_L2CAP_CON_OPEN) { 312 NG_L2CAP_ERR( 313 "%s: %s - unexpected LP_DisconnectInd event. " \ 314 "Invalid connection state, state=%d, con_handle=%d\n", 315 __func__, NG_NODE_NAME(l2cap->node), con->state, 316 con->con_handle); 317 error = EINVAL; 318 goto out; 319 } 320 321 /* 322 * Notify upper layer and remove connection 323 * Note: The connection could have auto disconnect timeout set. Try 324 * to remove it. If auto disconnect timeout happened then ignore 325 * disconnect indication and let timeout handle that. 326 */ 327 328 if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) 329 if ((error = ng_l2cap_discon_untimeout(con)) != 0) 330 return (error); 331 332 ng_l2cap_con_fail(con, ep->reason); 333 out: 334 return (error); 335 } /* ng_l2cap_lp_discon_ind */ 336 337 /* 338 * Send LP_QoSSetupReq event to the lower layer protocol 339 */ 340 341 int 342 ng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle, 343 ng_l2cap_flow_p flow) 344 { 345 struct ng_mesg *msg = NULL; 346 ng_hci_lp_qos_req_ep *ep = NULL; 347 ng_l2cap_con_p con = NULL; 348 int error = 0; 349 350 /* Verify that we have this connection */ 351 con = ng_l2cap_con_by_handle(l2cap, con_handle); 352 if (con == NULL) { 353 NG_L2CAP_ERR( 354 "%s: %s - unexpected LP_QoSSetupReq event. " \ 355 "Connection does not exist, con_handle=%d\n", 356 __func__, NG_NODE_NAME(l2cap->node), con_handle); 357 358 return (ENOENT); 359 } 360 361 /* Verify connection state */ 362 if (con->state != NG_L2CAP_CON_OPEN) { 363 NG_L2CAP_ERR( 364 "%s: %s - unexpected LP_QoSSetupReq event. " \ 365 "Invalid connection state, state=%d, con_handle=%d\n", 366 __func__, NG_NODE_NAME(l2cap->node), con->state, 367 con->con_handle); 368 369 return (EINVAL); 370 } 371 372 /* Check if lower layer protocol is still connected */ 373 if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 374 NG_L2CAP_ERR( 375 "%s: %s - hook \"%s\" is not connected or valid", 376 __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 377 378 return (ENOTCONN); 379 } 380 381 /* Create and send LP_QoSSetupReq event */ 382 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ, 383 sizeof(*ep), M_NOWAIT); 384 if (msg == NULL) 385 return (ENOMEM); 386 387 ep = (ng_hci_lp_qos_req_ep *) (msg->data); 388 ep->con_handle = con_handle; 389 ep->flags = flow->flags; 390 ep->service_type = flow->service_type; 391 ep->token_rate = flow->token_rate; 392 ep->peak_bandwidth = flow->peak_bandwidth; 393 ep->latency = flow->latency; 394 ep->delay_variation = flow->delay_variation; 395 396 NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); 397 398 return (error); 399 } /* ng_l2cap_lp_con_req */ 400 401 /* 402 * Process LP_QoSSetupCfm from the lower layer protocol 403 */ 404 405 int 406 ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg) 407 { 408 ng_hci_lp_qos_cfm_ep *ep = NULL; 409 int error = 0; 410 411 /* Check message */ 412 if (msg->header.arglen != sizeof(*ep)) { 413 NG_L2CAP_ALERT( 414 "%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n", 415 __func__, NG_NODE_NAME(l2cap->node)); 416 error = EMSGSIZE; 417 goto out; 418 } 419 420 ep = (ng_hci_lp_qos_cfm_ep *) (msg->data); 421 /* XXX FIXME do something */ 422 out: 423 return (error); 424 } /* ng_l2cap_lp_qos_cfm */ 425 426 /* 427 * Process LP_QoSViolationInd event from the lower layer protocol. Lower 428 * layer protocol has detected QoS Violation, so we MUST notify the 429 * upper layer. 430 */ 431 432 int 433 ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) 434 { 435 ng_hci_lp_qos_ind_ep *ep = NULL; 436 ng_l2cap_con_p con = NULL; 437 int error = 0; 438 439 /* Check message */ 440 if (msg->header.arglen != sizeof(*ep)) { 441 NG_L2CAP_ALERT( 442 "%s: %s - invalid LP_QoSViolation message size\n", 443 __func__, NG_NODE_NAME(l2cap->node)); 444 error = EMSGSIZE; 445 goto out; 446 } 447 448 ep = (ng_hci_lp_qos_ind_ep *) (msg->data); 449 450 /* Check if we have this connection */ 451 con = ng_l2cap_con_by_handle(l2cap, ep->con_handle); 452 if (con == NULL) { 453 NG_L2CAP_ERR( 454 "%s: %s - unexpected LP_QoSViolationInd event. " \ 455 "Connection does not exist, con_handle=%d\n", 456 __func__, NG_NODE_NAME(l2cap->node), ep->con_handle); 457 error = ENOENT; 458 goto out; 459 } 460 461 /* Verify connection state */ 462 if (con->state != NG_L2CAP_CON_OPEN) { 463 NG_L2CAP_ERR( 464 "%s: %s - unexpected LP_QoSViolationInd event. " \ 465 "Invalid connection state, state=%d, con_handle=%d\n", 466 __func__, NG_NODE_NAME(l2cap->node), con->state, 467 con->con_handle); 468 error = EINVAL; 469 goto out; 470 } 471 472 /* XXX FIXME Notify upper layer and terminate channels if required */ 473 out: 474 return (error); 475 } /* ng_l2cap_qos_ind */ 476 477 /* 478 * Prepare L2CAP packet. Prepend packet with L2CAP packet header and then 479 * segment it according to HCI MTU. 480 */ 481 482 int 483 ng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0) 484 { 485 ng_l2cap_p l2cap = con->l2cap; 486 ng_l2cap_hdr_t *l2cap_hdr = NULL; 487 ng_hci_acldata_pkt_t *acl_hdr = NULL; 488 struct mbuf *m_last = NULL, *m = NULL; 489 int len, flag = NG_HCI_PACKET_START; 490 491 KASSERT((con->tx_pkt == NULL), 492 ("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node))); 493 KASSERT((l2cap->pkt_size > 0), 494 ("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node))); 495 496 /* Prepend mbuf with L2CAP header */ 497 m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr)); 498 if (m0 == NULL) { 499 NG_L2CAP_ALERT( 500 "%s: %s - ng_l2cap_prepend(%zd) failed\n", 501 __func__, NG_NODE_NAME(l2cap->node), 502 sizeof(*l2cap_hdr)); 503 504 goto fail; 505 } 506 507 l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *); 508 l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr)); 509 l2cap_hdr->dcid = htole16(dcid); 510 511 /* 512 * Segment single L2CAP packet according to the HCI layer MTU. Convert 513 * each segment into ACL data packet and prepend it with ACL data packet 514 * header. Link all segments together via m_nextpkt link. 515 * 516 * XXX BC (Broadcast flag) will always be 0 (zero). 517 */ 518 519 while (m0 != NULL) { 520 /* Check length of the packet against HCI MTU */ 521 len = m0->m_pkthdr.len; 522 if (len > l2cap->pkt_size) { 523 m = m_split(m0, l2cap->pkt_size, M_DONTWAIT); 524 if (m == NULL) { 525 NG_L2CAP_ALERT( 526 "%s: %s - m_split(%d) failed\n", __func__, NG_NODE_NAME(l2cap->node), 527 l2cap->pkt_size); 528 goto fail; 529 } 530 531 len = l2cap->pkt_size; 532 } 533 534 /* Convert packet fragment into ACL data packet */ 535 m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr)); 536 if (m0 == NULL) { 537 NG_L2CAP_ALERT( 538 "%s: %s - ng_l2cap_prepend(%zd) failed\n", 539 __func__, NG_NODE_NAME(l2cap->node), 540 sizeof(*acl_hdr)); 541 goto fail; 542 } 543 544 acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *); 545 acl_hdr->type = NG_HCI_ACL_DATA_PKT; 546 acl_hdr->length = htole16(len); 547 acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE( 548 con->con_handle, flag, 0)); 549 550 /* Add fragment to the chain */ 551 m0->m_nextpkt = NULL; 552 553 if (con->tx_pkt == NULL) 554 con->tx_pkt = m_last = m0; 555 else { 556 m_last->m_nextpkt = m0; 557 m_last = m0; 558 } 559 560 NG_L2CAP_INFO( 561 "%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n", 562 __func__, NG_NODE_NAME(l2cap->node), con->con_handle, 563 flag, len); 564 565 m0 = m; 566 m = NULL; 567 flag = NG_HCI_PACKET_FRAGMENT; 568 } 569 570 return (0); 571 fail: 572 NG_FREE_M(m0); 573 NG_FREE_M(m); 574 575 while (con->tx_pkt != NULL) { 576 m = con->tx_pkt->m_nextpkt; 577 m_freem(con->tx_pkt); 578 con->tx_pkt = m; 579 } 580 581 return (ENOBUFS); 582 } /* ng_l2cap_lp_send */ 583 584 /* 585 * Receive ACL data packet from the HCI layer. First strip ACL packet header 586 * and get connection handle, PB (Packet Boundary) flag and payload length. 587 * Then find connection descriptor and verify its state. Then process ACL 588 * packet as follows. 589 * 590 * 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP 591 * header and get total length of the L2CAP packet. Then start new L2CAP 592 * packet. 593 * 594 * 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT) 595 * then add segment to the packet. 596 */ 597 598 int 599 ng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m) 600 { 601 ng_hci_acldata_pkt_t *acl_hdr = NULL; 602 ng_l2cap_hdr_t *l2cap_hdr = NULL; 603 ng_l2cap_con_p con = NULL; 604 u_int16_t con_handle, length, pb; 605 int error = 0; 606 607 /* Check ACL data packet */ 608 if (m->m_pkthdr.len < sizeof(*acl_hdr)) { 609 NG_L2CAP_ERR( 610 "%s: %s - invalid ACL data packet. Packet too small, length=%d\n", 611 __func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len); 612 error = EMSGSIZE; 613 goto drop; 614 } 615 616 /* Strip ACL data packet header */ 617 NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr)); 618 if (m == NULL) 619 return (ENOBUFS); 620 621 acl_hdr = mtod(m, ng_hci_acldata_pkt_t *); 622 m_adj(m, sizeof(*acl_hdr)); 623 624 /* Get ACL connection handle, PB flag and payload length */ 625 acl_hdr->con_handle = le16toh(acl_hdr->con_handle); 626 con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle); 627 pb = NG_HCI_PB_FLAG(acl_hdr->con_handle); 628 length = le16toh(acl_hdr->length); 629 630 NG_L2CAP_INFO( 631 "%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n", 632 __func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length); 633 634 /* Get connection descriptor */ 635 con = ng_l2cap_con_by_handle(l2cap, con_handle); 636 if (con == NULL) { 637 NG_L2CAP_ERR( 638 "%s: %s - unexpected ACL data packet. " \ 639 "Connection does not exist, con_handle=%d\n", 640 __func__, NG_NODE_NAME(l2cap->node), con_handle); 641 error = ENOENT; 642 goto drop; 643 } 644 645 /* Verify connection state */ 646 if (con->state != NG_L2CAP_CON_OPEN) { 647 NG_L2CAP_ERR( 648 "%s: %s - unexpected ACL data packet. Invalid connection state=%d\n", 649 __func__, NG_NODE_NAME(l2cap->node), con->state); 650 error = EHOSTDOWN; 651 goto drop; 652 } 653 654 /* Process packet */ 655 if (pb == NG_HCI_PACKET_START) { 656 if (con->rx_pkt != NULL) { 657 NG_L2CAP_ERR( 658 "%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n", 659 __func__, NG_NODE_NAME(l2cap->node), 660 con->rx_pkt->m_pkthdr.len, con->rx_pkt_len); 661 NG_FREE_M(con->rx_pkt); 662 con->rx_pkt_len = 0; 663 } 664 665 /* Get L2CAP header */ 666 if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) { 667 NG_L2CAP_ERR( 668 "%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n", 669 __func__, NG_NODE_NAME(l2cap->node), 670 m->m_pkthdr.len); 671 error = EMSGSIZE; 672 goto drop; 673 } 674 675 NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr)); 676 if (m == NULL) 677 return (ENOBUFS); 678 679 l2cap_hdr = mtod(m, ng_l2cap_hdr_t *); 680 681 NG_L2CAP_INFO( 682 "%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n", 683 __func__, NG_NODE_NAME(l2cap->node), con_handle, 684 le16toh(l2cap_hdr->length)); 685 686 /* Start new L2CAP packet */ 687 con->rx_pkt = m; 688 con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr); 689 } else if (pb == NG_HCI_PACKET_FRAGMENT) { 690 if (con->rx_pkt == NULL) { 691 NG_L2CAP_ERR( 692 "%s: %s - unexpected ACL data packet fragment, con_handle=%d\n", 693 __func__, NG_NODE_NAME(l2cap->node), 694 con->con_handle); 695 goto drop; 696 } 697 698 /* Add fragment to the L2CAP packet */ 699 m_cat(con->rx_pkt, m); 700 con->rx_pkt->m_pkthdr.len += length; 701 } else { 702 NG_L2CAP_ERR( 703 "%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n", 704 __func__, NG_NODE_NAME(l2cap->node), pb); 705 error = EINVAL; 706 goto drop; 707 } 708 709 con->rx_pkt_len -= length; 710 if (con->rx_pkt_len < 0) { 711 NG_L2CAP_ALERT( 712 "%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n", 713 __func__, NG_NODE_NAME(l2cap->node), 714 con->rx_pkt->m_pkthdr.len, con->rx_pkt_len); 715 NG_FREE_M(con->rx_pkt); 716 con->rx_pkt_len = 0; 717 } else if (con->rx_pkt_len == 0) { 718 /* OK, we have got complete L2CAP packet, so process it */ 719 error = ng_l2cap_receive(con); 720 con->rx_pkt = NULL; 721 con->rx_pkt_len = 0; 722 } 723 724 return (error); 725 726 drop: 727 NG_FREE_M(m); 728 729 return (error); 730 } /* ng_l2cap_lp_receive */ 731 732 /* 733 * Send queued ACL packets to the HCI layer 734 */ 735 736 void 737 ng_l2cap_lp_deliver(ng_l2cap_con_p con) 738 { 739 ng_l2cap_p l2cap = con->l2cap; 740 struct mbuf *m = NULL; 741 int error; 742 743 /* Check connection */ 744 if (con->state != NG_L2CAP_CON_OPEN) 745 return; 746 747 if (con->tx_pkt == NULL) 748 ng_l2cap_con_wakeup(con); 749 750 if (con->tx_pkt == NULL) 751 return; 752 753 /* Check if lower layer protocol is still connected */ 754 if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 755 NG_L2CAP_ERR( 756 "%s: %s - hook \"%s\" is not connected or valid", 757 __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 758 759 goto drop; /* XXX what to do with "pending"? */ 760 } 761 762 /* Send ACL data packets */ 763 while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) { 764 m = con->tx_pkt; 765 con->tx_pkt = con->tx_pkt->m_nextpkt; 766 m->m_nextpkt = NULL; 767 768 NG_L2CAP_INFO( 769 "%s: %s - sending ACL packet, con_handle=%d, len=%d\n", 770 __func__, NG_NODE_NAME(l2cap->node), con->con_handle, 771 m->m_pkthdr.len); 772 773 NG_SEND_DATA_ONLY(error, l2cap->hci, m); 774 if (error != 0) { 775 NG_L2CAP_ERR( 776 "%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n", 777 __func__, NG_NODE_NAME(l2cap->node), 778 con->con_handle, error); 779 780 goto drop; /* XXX what to do with "pending"? */ 781 } 782 783 con->pending ++; 784 } 785 786 NG_L2CAP_INFO( 787 "%s: %s - %d ACL packets have been sent, con_handle=%d\n", 788 __func__, NG_NODE_NAME(l2cap->node), con->pending, 789 con->con_handle); 790 791 return; 792 793 drop: 794 while (con->tx_pkt != NULL) { 795 m = con->tx_pkt->m_nextpkt; 796 m_freem(con->tx_pkt); 797 con->tx_pkt = m; 798 } 799 } /* ng_l2cap_lp_deliver */ 800 801 /* 802 * Process connection timeout. Remove connection from the list. If there 803 * are any channels that wait for the connection then notify them. Free 804 * connection descriptor. 805 */ 806 807 void 808 ng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int con_handle) 809 { 810 ng_l2cap_p l2cap = NULL; 811 ng_l2cap_con_p con = NULL; 812 813 if (NG_NODE_NOT_VALID(node)) { 814 printf("%s: Netgraph node is not valid\n", __func__); 815 return; 816 } 817 818 l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); 819 con = ng_l2cap_con_by_handle(l2cap, con_handle); 820 821 if (con == NULL) { 822 NG_L2CAP_ALERT( 823 "%s: %s - could not find connection, con_handle=%d\n", 824 __func__, NG_NODE_NAME(node), con_handle); 825 return; 826 } 827 828 if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) { 829 NG_L2CAP_ALERT( 830 "%s: %s - no pending LP timeout, con_handle=%d, state=%d, flags=%#x\n", 831 __func__, NG_NODE_NAME(node), con_handle, con->state, 832 con->flags); 833 return; 834 } 835 836 /* 837 * Notify channels that connection has timed out. This will remove 838 * connection, channels and pending commands. 839 */ 840 841 con->flags &= ~NG_L2CAP_CON_LP_TIMO; 842 ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT); 843 } /* ng_l2cap_process_lp_timeout */ 844 845 /* 846 * Process auto disconnect timeout and send LP_DisconReq event to the 847 * lower layer protocol 848 */ 849 850 void 851 ng_l2cap_process_discon_timeout(node_p node, hook_p hook, void *arg1, int con_handle) 852 { 853 ng_l2cap_p l2cap = NULL; 854 ng_l2cap_con_p con = NULL; 855 struct ng_mesg *msg = NULL; 856 ng_hci_lp_discon_req_ep *ep = NULL; 857 int error; 858 859 if (NG_NODE_NOT_VALID(node)) { 860 printf("%s: Netgraph node is not valid\n", __func__); 861 return; 862 } 863 864 l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); 865 con = ng_l2cap_con_by_handle(l2cap, con_handle); 866 867 if (con == NULL) { 868 NG_L2CAP_ALERT( 869 "%s: %s - could not find connection, con_handle=%d\n", 870 __func__, NG_NODE_NAME(node), con_handle); 871 return; 872 } 873 874 if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) { 875 NG_L2CAP_ALERT( 876 "%s: %s - no pending disconnect timeout, con_handle=%d, state=%d, flags=%#x\n", 877 __func__, NG_NODE_NAME(node), con_handle, con->state, 878 con->flags); 879 return; 880 } 881 882 con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO; 883 884 /* Check if lower layer protocol is still connected */ 885 if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 886 NG_L2CAP_ERR( 887 "%s: %s - hook \"%s\" is not connected or valid\n", 888 __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 889 return; 890 } 891 892 /* Create and send LP_DisconReq event */ 893 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ, 894 sizeof(*ep), M_NOWAIT); 895 if (msg == NULL) 896 return; 897 898 ep = (ng_hci_lp_discon_req_ep *) (msg->data); 899 ep->con_handle = con->con_handle; 900 ep->reason = 0x13; /* User Ended Connection */ 901 902 NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); 903 } /* ng_l2cap_process_discon_timeout */ 904 905