1 /* 2 * ng_l2cap_misc.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_l2cap_misc.c,v 1.5 2003/09/08 19:11:45 max Exp $ 31 * $FreeBSD$ 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/malloc.h> 38 #include <sys/mbuf.h> 39 #include <sys/queue.h> 40 #include <netgraph/ng_message.h> 41 #include <netgraph/netgraph.h> 42 #include <netgraph/bluetooth/include/ng_bluetooth.h> 43 #include <netgraph/bluetooth/include/ng_hci.h> 44 #include <netgraph/bluetooth/include/ng_l2cap.h> 45 #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h> 46 #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h> 47 #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h> 48 #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h> 49 #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h> 50 #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h> 51 52 static u_int16_t ng_l2cap_get_cid (ng_l2cap_p); 53 54 /****************************************************************************** 55 ****************************************************************************** 56 ** Utility routines 57 ****************************************************************************** 58 ******************************************************************************/ 59 60 /* 61 * Send hook information to the upper layer 62 */ 63 64 void 65 ng_l2cap_send_hook_info(node_p node, hook_p hook, void *arg1, int arg2) 66 { 67 ng_l2cap_p l2cap = NULL; 68 struct ng_mesg *msg = NULL; 69 int error = 0; 70 71 if (node == NULL || NG_NODE_NOT_VALID(node) || 72 hook == NULL || NG_HOOK_NOT_VALID(hook)) 73 return; 74 75 l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); 76 if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci) || 77 bcmp(&l2cap->bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2cap->bdaddr)) == 0) 78 return; 79 80 NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_HOOK_INFO, 81 sizeof(bdaddr_t), M_NOWAIT); 82 if (msg != NULL) { 83 bcopy(&l2cap->bdaddr, msg->data, sizeof(bdaddr_t)); 84 NG_SEND_MSG_HOOK(error, node, msg, hook, 0); 85 } else 86 error = ENOMEM; 87 88 if (error != 0) 89 NG_L2CAP_INFO( 90 "%s: %s - failed to send HOOK_INFO message to hook \"%s\", error=%d\n", 91 __func__, NG_NODE_NAME(l2cap->node), NG_HOOK_NAME(hook), 92 error); 93 } /* ng_l2cap_send_hook_info */ 94 95 /* 96 * Create new connection descriptor for the "remote" unit. 97 * Will link connection descriptor to the l2cap node. 98 */ 99 100 ng_l2cap_con_p 101 ng_l2cap_new_con(ng_l2cap_p l2cap, bdaddr_p bdaddr) 102 { 103 static int fake_con_handle = 0x0f00; 104 ng_l2cap_con_p con = NULL; 105 106 /* Create new connection descriptor */ 107 MALLOC(con, ng_l2cap_con_p, sizeof(*con), M_NETGRAPH_L2CAP, 108 M_NOWAIT|M_ZERO); 109 if (con == NULL) 110 return (NULL); 111 112 con->l2cap = l2cap; 113 con->state = NG_L2CAP_CON_CLOSED; 114 115 /* 116 * XXX 117 * 118 * Assign fake connection handle to the connection descriptor. 119 * Bluetooth specification marks 0x0f00 - 0x0fff connection 120 * handles as reserved. We need this fake connection handles 121 * for timeouts. Connection handle will be passed as argument 122 * to timeout so when timeout happens we can find the right 123 * connection descriptor. We can not pass pointers, because 124 * timeouts are external (to Netgraph) events and there might 125 * be a race when node/hook goes down and timeout event already 126 * went into node's queue 127 */ 128 129 con->con_handle = fake_con_handle ++; 130 if (fake_con_handle > 0x0fff) 131 fake_con_handle = 0x0f00; 132 133 bcopy(bdaddr, &con->remote, sizeof(con->remote)); 134 ng_callout_init(&con->con_timo); 135 136 con->ident = NG_L2CAP_FIRST_IDENT - 1; 137 TAILQ_INIT(&con->cmd_list); 138 139 /* Link connection */ 140 LIST_INSERT_HEAD(&l2cap->con_list, con, next); 141 142 return (con); 143 } /* ng_l2cap_new_con */ 144 145 /* 146 * Add reference to the connection descriptor 147 */ 148 149 void 150 ng_l2cap_con_ref(ng_l2cap_con_p con) 151 { 152 con->refcnt ++; 153 154 if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) { 155 if ((con->state != NG_L2CAP_CON_OPEN) || 156 (con->flags & NG_L2CAP_CON_OUTGOING) == 0) 157 panic( 158 "%s: %s - bad auto disconnect timeout, state=%d, flags=%#x\n", 159 __func__, NG_NODE_NAME(con->l2cap->node), 160 con->state, con->flags); 161 162 ng_l2cap_discon_untimeout(con); 163 } 164 } /* ng_l2cap_con_ref */ 165 166 /* 167 * Remove reference from the connection descriptor 168 */ 169 170 void 171 ng_l2cap_con_unref(ng_l2cap_con_p con) 172 { 173 con->refcnt --; 174 175 if (con->refcnt < 0) 176 panic( 177 "%s: %s - con->refcnt < 0\n", __func__, NG_NODE_NAME(con->l2cap->node)); 178 179 /* 180 * Set auto disconnect timer only if the following conditions are met: 181 * 1) we have no reference on the connection 182 * 2) connection is in OPEN state 183 * 3) it is an outgoing connection 184 * 4) disconnect timeout > 0 185 * 5) connection is not dying 186 */ 187 188 if ((con->refcnt == 0) && 189 (con->state == NG_L2CAP_CON_OPEN) && 190 (con->flags & NG_L2CAP_CON_OUTGOING) && 191 (con->l2cap->discon_timo > 0) && 192 ((con->flags & NG_L2CAP_CON_DYING) == 0)) 193 ng_l2cap_discon_timeout(con); 194 } /* ng_l2cap_con_unref */ 195 196 /* 197 * Set auto disconnect timeout 198 * XXX FIXME: check return code from ng_callout 199 */ 200 201 int 202 ng_l2cap_discon_timeout(ng_l2cap_con_p con) 203 { 204 if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO)) 205 panic( 206 "%s: %s - invalid timeout, state=%d, flags=%#x\n", 207 __func__, NG_NODE_NAME(con->l2cap->node), 208 con->state, con->flags); 209 210 con->flags |= NG_L2CAP_CON_AUTO_DISCON_TIMO; 211 ng_callout(&con->con_timo, con->l2cap->node, NULL, 212 con->l2cap->discon_timo * hz, 213 ng_l2cap_process_discon_timeout, NULL, 214 con->con_handle); 215 216 return (0); 217 } /* ng_l2cap_discon_timeout */ 218 219 /* 220 * Unset auto disconnect timeout 221 */ 222 223 int 224 ng_l2cap_discon_untimeout(ng_l2cap_con_p con) 225 { 226 if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) 227 panic( 228 "%s: %s - no disconnect timeout, state=%d, flags=%#x\n", 229 __func__, NG_NODE_NAME(con->l2cap->node), 230 con->state, con->flags); 231 232 if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0) 233 return (ETIMEDOUT); 234 235 con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO; 236 237 return (0); 238 } /* ng_l2cap_discon_untimeout */ 239 240 /* 241 * Free connection descriptor. Will unlink connection and free everything. 242 */ 243 244 void 245 ng_l2cap_free_con(ng_l2cap_con_p con) 246 { 247 ng_l2cap_chan_p f = NULL, n = NULL; 248 249 con->state = NG_L2CAP_CON_CLOSED; 250 251 while (con->tx_pkt != NULL) { 252 struct mbuf *m = con->tx_pkt->m_nextpkt; 253 254 m_freem(con->tx_pkt); 255 con->tx_pkt = m; 256 } 257 258 NG_FREE_M(con->rx_pkt); 259 260 for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) { 261 n = LIST_NEXT(f, next); 262 263 if (f->con == con) 264 ng_l2cap_free_chan(f); 265 266 f = n; 267 } 268 269 while (!TAILQ_EMPTY(&con->cmd_list)) { 270 ng_l2cap_cmd_p cmd = TAILQ_FIRST(&con->cmd_list); 271 272 ng_l2cap_unlink_cmd(cmd); 273 if (cmd->flags & NG_L2CAP_CMD_PENDING) 274 ng_l2cap_command_untimeout(cmd); 275 ng_l2cap_free_cmd(cmd); 276 } 277 278 if (con->flags & (NG_L2CAP_CON_AUTO_DISCON_TIMO|NG_L2CAP_CON_LP_TIMO)) 279 panic( 280 "%s: %s - timeout pending! state=%d, flags=%#x\n", 281 __func__, NG_NODE_NAME(con->l2cap->node), 282 con->state, con->flags); 283 284 LIST_REMOVE(con, next); 285 286 bzero(con, sizeof(*con)); 287 FREE(con, M_NETGRAPH_L2CAP); 288 } /* ng_l2cap_free_con */ 289 290 /* 291 * Get connection by "remote" address 292 */ 293 294 ng_l2cap_con_p 295 ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr) 296 { 297 ng_l2cap_con_p con = NULL; 298 299 LIST_FOREACH(con, &l2cap->con_list, next) 300 if (bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0) 301 break; 302 303 return (con); 304 } /* ng_l2cap_con_by_addr */ 305 306 /* 307 * Get connection by "handle" 308 */ 309 310 ng_l2cap_con_p 311 ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle) 312 { 313 ng_l2cap_con_p con = NULL; 314 315 LIST_FOREACH(con, &l2cap->con_list, next) 316 if (con->con_handle == con_handle) 317 break; 318 319 return (con); 320 } /* ng_l2cap_con_by_handle */ 321 322 /* 323 * Allocate new L2CAP channel descriptor on "con" conection with "psm". 324 * Will link the channel to the l2cap node 325 */ 326 327 ng_l2cap_chan_p 328 ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm) 329 { 330 ng_l2cap_chan_p ch = NULL; 331 332 MALLOC(ch, ng_l2cap_chan_p, sizeof(*ch), M_NETGRAPH_L2CAP, 333 M_NOWAIT|M_ZERO); 334 if (ch == NULL) 335 return (NULL); 336 337 ch->scid = ng_l2cap_get_cid(l2cap); 338 339 if (ch->scid != NG_L2CAP_NULL_CID) { 340 /* Initialize channel */ 341 ch->psm = psm; 342 ch->con = con; 343 ch->state = NG_L2CAP_CLOSED; 344 345 /* Set MTU and flow control settings to defaults */ 346 ch->imtu = NG_L2CAP_MTU_DEFAULT; 347 bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow)); 348 349 ch->omtu = NG_L2CAP_MTU_DEFAULT; 350 bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)); 351 352 ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT; 353 ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT; 354 355 LIST_INSERT_HEAD(&l2cap->chan_list, ch, next); 356 357 ng_l2cap_con_ref(con); 358 } else { 359 bzero(ch, sizeof(*ch)); 360 FREE(ch, M_NETGRAPH_L2CAP); 361 ch = NULL; 362 } 363 364 return (ch); 365 } /* ng_l2cap_new_chan */ 366 367 /* 368 * Get channel by source (local) channel ID 369 */ 370 371 ng_l2cap_chan_p 372 ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid) 373 { 374 ng_l2cap_chan_p ch = NULL; 375 376 LIST_FOREACH(ch, &l2cap->chan_list, next) 377 if (ch->scid == scid) 378 break; 379 380 return (ch); 381 } /* ng_l2cap_chan_by_scid */ 382 383 /* 384 * Free channel descriptor. 385 */ 386 387 void 388 ng_l2cap_free_chan(ng_l2cap_chan_p ch) 389 { 390 ng_l2cap_cmd_p f = NULL, n = NULL; 391 392 f = TAILQ_FIRST(&ch->con->cmd_list); 393 while (f != NULL) { 394 n = TAILQ_NEXT(f, next); 395 396 if (f->ch == ch) { 397 ng_l2cap_unlink_cmd(f); 398 if (f->flags & NG_L2CAP_CMD_PENDING) 399 ng_l2cap_command_untimeout(f); 400 ng_l2cap_free_cmd(f); 401 } 402 403 f = n; 404 } 405 406 LIST_REMOVE(ch, next); 407 408 ng_l2cap_con_unref(ch->con); 409 410 bzero(ch, sizeof(*ch)); 411 FREE(ch, M_NETGRAPH_L2CAP); 412 } /* ng_l2cap_free_chan */ 413 414 /* 415 * Create new L2CAP command descriptor. WILL NOT add command to the queue. 416 */ 417 418 ng_l2cap_cmd_p 419 ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident, 420 u_int8_t code, u_int32_t token) 421 { 422 ng_l2cap_cmd_p cmd = NULL; 423 424 KASSERT((ch == NULL || ch->con == con), 425 ("%s: %s - invalid channel pointer!\n", 426 __func__, NG_NODE_NAME(con->l2cap->node))); 427 428 MALLOC(cmd, ng_l2cap_cmd_p, sizeof(*cmd), M_NETGRAPH_L2CAP, 429 M_NOWAIT|M_ZERO); 430 if (cmd == NULL) 431 return (NULL); 432 433 cmd->con = con; 434 cmd->ch = ch; 435 cmd->ident = ident; 436 cmd->code = code; 437 cmd->token = token; 438 ng_callout_init(&cmd->timo); 439 440 return (cmd); 441 } /* ng_l2cap_new_cmd */ 442 443 /* 444 * Get pending (i.e. initiated by local side) L2CAP command descriptor by ident 445 */ 446 447 ng_l2cap_cmd_p 448 ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident) 449 { 450 ng_l2cap_cmd_p cmd = NULL; 451 452 TAILQ_FOREACH(cmd, &con->cmd_list, next) { 453 if ((cmd->flags & NG_L2CAP_CMD_PENDING) && cmd->ident == ident) { 454 KASSERT((cmd->con == con), 455 ("%s: %s - invalid connection pointer!\n", 456 __func__, NG_NODE_NAME(con->l2cap->node))); 457 458 break; 459 } 460 } 461 462 return (cmd); 463 } /* ng_l2cap_cmd_by_ident */ 464 465 /* 466 * Set LP timeout 467 * XXX FIXME: check return code from ng_callout 468 */ 469 470 int 471 ng_l2cap_lp_timeout(ng_l2cap_con_p con) 472 { 473 if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO)) 474 panic( 475 "%s: %s - invalid timeout, state=%d, flags=%#x\n", 476 __func__, NG_NODE_NAME(con->l2cap->node), 477 con->state, con->flags); 478 479 con->flags |= NG_L2CAP_CON_LP_TIMO; 480 ng_callout(&con->con_timo, con->l2cap->node, NULL, 481 bluetooth_hci_connect_timeout(), 482 ng_l2cap_process_lp_timeout, NULL, 483 con->con_handle); 484 485 return (0); 486 } /* ng_l2cap_lp_timeout */ 487 488 /* 489 * Unset LP timeout 490 */ 491 492 int 493 ng_l2cap_lp_untimeout(ng_l2cap_con_p con) 494 { 495 if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) 496 panic( 497 "%s: %s - no LP connection timeout, state=%d, flags=%#x\n", 498 __func__, NG_NODE_NAME(con->l2cap->node), 499 con->state, con->flags); 500 501 if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0) 502 return (ETIMEDOUT); 503 504 con->flags &= ~NG_L2CAP_CON_LP_TIMO; 505 506 return (0); 507 } /* ng_l2cap_lp_untimeout */ 508 509 /* 510 * Set L2CAP command timeout 511 * XXX FIXME: check return code from ng_callout 512 */ 513 514 int 515 ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo) 516 { 517 int arg; 518 519 if (cmd->flags & NG_L2CAP_CMD_PENDING) 520 panic( 521 "%s: %s - duplicated command timeout, code=%#x, flags=%#x\n", 522 __func__, NG_NODE_NAME(cmd->con->l2cap->node), 523 cmd->code, cmd->flags); 524 525 arg = ((cmd->ident << 16) | cmd->con->con_handle); 526 cmd->flags |= NG_L2CAP_CMD_PENDING; 527 ng_callout(&cmd->timo, cmd->con->l2cap->node, NULL, timo, 528 ng_l2cap_process_command_timeout, NULL, arg); 529 530 return (0); 531 } /* ng_l2cap_command_timeout */ 532 533 /* 534 * Unset L2CAP command timeout 535 */ 536 537 int 538 ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd) 539 { 540 if (!(cmd->flags & NG_L2CAP_CMD_PENDING)) 541 panic( 542 "%s: %s - no command timeout, code=%#x, flags=%#x\n", 543 __func__, NG_NODE_NAME(cmd->con->l2cap->node), 544 cmd->code, cmd->flags); 545 546 if (ng_uncallout(&cmd->timo, cmd->con->l2cap->node) == 0) 547 return (ETIMEDOUT); 548 549 cmd->flags &= ~NG_L2CAP_CMD_PENDING; 550 551 return (0); 552 } /* ng_l2cap_command_untimeout */ 553 554 /* 555 * Prepend "m"buf with "size" bytes 556 */ 557 558 struct mbuf * 559 ng_l2cap_prepend(struct mbuf *m, int size) 560 { 561 M_PREPEND(m, size, M_DONTWAIT); 562 if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL)) 563 return (NULL); 564 565 return (m); 566 } /* ng_l2cap_prepend */ 567 568 /* 569 * Default flow settings 570 */ 571 572 ng_l2cap_flow_p 573 ng_l2cap_default_flow(void) 574 { 575 static ng_l2cap_flow_t default_flow = { 576 /* flags */ 0x0, 577 /* service_type */ NG_HCI_SERVICE_TYPE_BEST_EFFORT, 578 /* token_rate */ 0xffffffff, /* maximum */ 579 /* token_bucket_size */ 0xffffffff, /* maximum */ 580 /* peak_bandwidth */ 0x00000000, /* maximum */ 581 /* latency */ 0xffffffff, /* don't care */ 582 /* delay_variation */ 0xffffffff /* don't care */ 583 }; 584 585 return (&default_flow); 586 } /* ng_l2cap_default_flow */ 587 588 /* 589 * Get next available channel ID 590 * XXX FIXME this is *UGLY* but will do for now 591 */ 592 593 static u_int16_t 594 ng_l2cap_get_cid(ng_l2cap_p l2cap) 595 { 596 u_int16_t cid = l2cap->cid + 1; 597 598 if (cid < NG_L2CAP_FIRST_CID) 599 cid = NG_L2CAP_FIRST_CID; 600 601 while (cid != l2cap->cid) { 602 if (ng_l2cap_chan_by_scid(l2cap, cid) == NULL) { 603 l2cap->cid = cid; 604 605 return (cid); 606 } 607 608 cid ++; 609 if (cid < NG_L2CAP_FIRST_CID) 610 cid = NG_L2CAP_FIRST_CID; 611 } 612 613 return (NG_L2CAP_NULL_CID); 614 } /* ng_l2cap_get_cid */ 615 616 /* 617 * Get next available command ident 618 * XXX FIXME this is *UGLY* but will do for now 619 */ 620 621 u_int8_t 622 ng_l2cap_get_ident(ng_l2cap_con_p con) 623 { 624 u_int8_t ident = con->ident + 1; 625 626 if (ident < NG_L2CAP_FIRST_IDENT) 627 ident = NG_L2CAP_FIRST_IDENT; 628 629 while (ident != con->ident) { 630 if (ng_l2cap_cmd_by_ident(con, ident) == NULL) { 631 con->ident = ident; 632 633 return (ident); 634 } 635 636 ident ++; 637 if (ident < NG_L2CAP_FIRST_IDENT) 638 ident = NG_L2CAP_FIRST_IDENT; 639 } 640 641 return (NG_L2CAP_NULL_IDENT); 642 } /* ng_l2cap_get_ident */ 643 644