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