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 */ 186 187 if ((con->refcnt == 0) && 188 (con->state == NG_L2CAP_CON_OPEN) && 189 (con->flags & NG_L2CAP_CON_OUTGOING) && 190 (con->l2cap->discon_timo > 0)) 191 ng_l2cap_discon_timeout(con); 192 } /* ng_l2cap_con_unref */ 193 194 /* 195 * Set auto disconnect timeout 196 * XXX FIXME: check return code from ng_callout 197 */ 198 199 int 200 ng_l2cap_discon_timeout(ng_l2cap_con_p con) 201 { 202 if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO)) 203 panic( 204 "%s: %s - invalid timeout, state=%d, flags=%#x\n", 205 __func__, NG_NODE_NAME(con->l2cap->node), 206 con->state, con->flags); 207 208 con->flags |= NG_L2CAP_CON_AUTO_DISCON_TIMO; 209 ng_callout(&con->con_timo, con->l2cap->node, NULL, 210 con->l2cap->discon_timo * hz, 211 ng_l2cap_process_discon_timeout, NULL, 212 con->con_handle); 213 214 return (0); 215 } /* ng_l2cap_discon_timeout */ 216 217 /* 218 * Unset auto disconnect timeout 219 */ 220 221 int 222 ng_l2cap_discon_untimeout(ng_l2cap_con_p con) 223 { 224 if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) 225 panic( 226 "%s: %s - no disconnect timeout, state=%d, flags=%#x\n", 227 __func__, NG_NODE_NAME(con->l2cap->node), 228 con->state, con->flags); 229 230 if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0) 231 return (ETIMEDOUT); 232 233 con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO; 234 235 return (0); 236 } /* ng_l2cap_discon_untimeout */ 237 238 /* 239 * Free connection descriptor. Will unlink connection and free everything. 240 */ 241 242 void 243 ng_l2cap_free_con(ng_l2cap_con_p con) 244 { 245 ng_l2cap_chan_p f = NULL, n = NULL; 246 247 con->state = NG_L2CAP_CON_CLOSED; 248 249 while (con->tx_pkt != NULL) { 250 struct mbuf *m = con->tx_pkt->m_nextpkt; 251 252 m_freem(con->tx_pkt); 253 con->tx_pkt = m; 254 } 255 256 NG_FREE_M(con->rx_pkt); 257 258 for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) { 259 n = LIST_NEXT(f, next); 260 261 if (f->con == con) 262 ng_l2cap_free_chan(f); 263 264 f = n; 265 } 266 267 while (!TAILQ_EMPTY(&con->cmd_list)) { 268 ng_l2cap_cmd_p cmd = TAILQ_FIRST(&con->cmd_list); 269 270 ng_l2cap_unlink_cmd(cmd); 271 if (cmd->flags & NG_L2CAP_CMD_PENDING) 272 ng_l2cap_command_untimeout(cmd); 273 ng_l2cap_free_cmd(cmd); 274 } 275 276 LIST_REMOVE(con, next); 277 bzero(con, sizeof(*con)); 278 FREE(con, M_NETGRAPH_L2CAP); 279 } /* ng_l2cap_free_con */ 280 281 /* 282 * Get connection by "remote" address 283 */ 284 285 ng_l2cap_con_p 286 ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr) 287 { 288 ng_l2cap_con_p con = NULL; 289 290 LIST_FOREACH(con, &l2cap->con_list, next) 291 if (bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0) 292 break; 293 294 return (con); 295 } /* ng_l2cap_con_by_addr */ 296 297 /* 298 * Get connection by "handle" 299 */ 300 301 ng_l2cap_con_p 302 ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle) 303 { 304 ng_l2cap_con_p con = NULL; 305 306 LIST_FOREACH(con, &l2cap->con_list, next) 307 if (con->con_handle == con_handle) 308 break; 309 310 return (con); 311 } /* ng_l2cap_con_by_handle */ 312 313 /* 314 * Allocate new L2CAP channel descriptor on "con" conection with "psm". 315 * Will link the channel to the l2cap node 316 */ 317 318 ng_l2cap_chan_p 319 ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm) 320 { 321 ng_l2cap_chan_p ch = NULL; 322 323 MALLOC(ch, ng_l2cap_chan_p, sizeof(*ch), M_NETGRAPH_L2CAP, 324 M_NOWAIT|M_ZERO); 325 if (ch == NULL) 326 return (NULL); 327 328 ch->scid = ng_l2cap_get_cid(l2cap); 329 330 if (ch->scid != NG_L2CAP_NULL_CID) { 331 /* Initialize channel */ 332 ch->psm = psm; 333 ch->con = con; 334 ch->state = NG_L2CAP_CLOSED; 335 336 /* Set MTU and flow control settings to defaults */ 337 ch->imtu = NG_L2CAP_MTU_DEFAULT; 338 bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow)); 339 340 ch->omtu = NG_L2CAP_MTU_DEFAULT; 341 bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)); 342 343 ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT; 344 ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT; 345 346 LIST_INSERT_HEAD(&l2cap->chan_list, ch, next); 347 348 ng_l2cap_con_ref(con); 349 } else { 350 bzero(ch, sizeof(*ch)); 351 FREE(ch, M_NETGRAPH_L2CAP); 352 ch = NULL; 353 } 354 355 return (ch); 356 } /* ng_l2cap_new_chan */ 357 358 /* 359 * Get channel by source (local) channel ID 360 */ 361 362 ng_l2cap_chan_p 363 ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid) 364 { 365 ng_l2cap_chan_p ch = NULL; 366 367 LIST_FOREACH(ch, &l2cap->chan_list, next) 368 if (ch->scid == scid) 369 break; 370 371 return (ch); 372 } /* ng_l2cap_chan_by_scid */ 373 374 /* 375 * Free channel descriptor. 376 */ 377 378 void 379 ng_l2cap_free_chan(ng_l2cap_chan_p ch) 380 { 381 ng_l2cap_cmd_p f = NULL, n = NULL; 382 383 f = TAILQ_FIRST(&ch->con->cmd_list); 384 while (f != NULL) { 385 n = TAILQ_NEXT(f, next); 386 387 if (f->ch == ch) { 388 ng_l2cap_unlink_cmd(f); 389 if (f->flags & NG_L2CAP_CMD_PENDING) 390 ng_l2cap_command_untimeout(f); 391 ng_l2cap_free_cmd(f); 392 } 393 394 f = n; 395 } 396 397 LIST_REMOVE(ch, next); 398 399 ng_l2cap_con_unref(ch->con); 400 401 bzero(ch, sizeof(*ch)); 402 FREE(ch, M_NETGRAPH_L2CAP); 403 } /* ng_l2cap_free_chan */ 404 405 /* 406 * Create new L2CAP command descriptor. WILL NOT add command to the queue. 407 */ 408 409 ng_l2cap_cmd_p 410 ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident, 411 u_int8_t code, u_int32_t token) 412 { 413 ng_l2cap_cmd_p cmd = NULL; 414 415 KASSERT((ch == NULL || ch->con == con), 416 ("%s: %s - invalid channel pointer!\n", 417 __func__, NG_NODE_NAME(con->l2cap->node))); 418 419 MALLOC(cmd, ng_l2cap_cmd_p, sizeof(*cmd), M_NETGRAPH_L2CAP, 420 M_NOWAIT|M_ZERO); 421 if (cmd == NULL) 422 return (NULL); 423 424 cmd->con = con; 425 cmd->ch = ch; 426 cmd->ident = ident; 427 cmd->code = code; 428 cmd->token = token; 429 ng_callout_init(&cmd->timo); 430 431 return (cmd); 432 } /* ng_l2cap_new_cmd */ 433 434 /* 435 * Get pending (i.e. initiated by local side) L2CAP command descriptor by ident 436 */ 437 438 ng_l2cap_cmd_p 439 ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident) 440 { 441 ng_l2cap_cmd_p cmd = NULL; 442 443 TAILQ_FOREACH(cmd, &con->cmd_list, next) { 444 if ((cmd->flags & NG_L2CAP_CMD_PENDING) && cmd->ident == ident) { 445 KASSERT((cmd->con == con), 446 ("%s: %s - invalid connection pointer!\n", 447 __func__, NG_NODE_NAME(con->l2cap->node))); 448 449 break; 450 } 451 } 452 453 return (cmd); 454 } /* ng_l2cap_cmd_by_ident */ 455 456 /* 457 * Set LP timeout 458 * XXX FIXME: check return code from ng_callout 459 */ 460 461 int 462 ng_l2cap_lp_timeout(ng_l2cap_con_p con) 463 { 464 if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO)) 465 panic( 466 "%s: %s - invalid timeout, state=%d, flags=%#x\n", 467 __func__, NG_NODE_NAME(con->l2cap->node), 468 con->state, con->flags); 469 470 con->flags |= NG_L2CAP_CON_LP_TIMO; 471 ng_callout(&con->con_timo, con->l2cap->node, NULL, 472 bluetooth_hci_connect_timeout(), 473 ng_l2cap_process_lp_timeout, NULL, 474 con->con_handle); 475 476 return (0); 477 } /* ng_l2cap_lp_timeout */ 478 479 /* 480 * Unset LP timeout 481 */ 482 483 int 484 ng_l2cap_lp_untimeout(ng_l2cap_con_p con) 485 { 486 if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) 487 panic( 488 "%s: %s - no LP connection timeout, state=%d, flags=%#x\n", 489 __func__, NG_NODE_NAME(con->l2cap->node), 490 con->state, con->flags); 491 492 if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0) 493 return (ETIMEDOUT); 494 495 con->flags &= ~NG_L2CAP_CON_LP_TIMO; 496 497 return (0); 498 } /* ng_l2cap_lp_untimeout */ 499 500 /* 501 * Set L2CAP command timeout 502 * XXX FIXME: check return code from ng_callout 503 */ 504 505 int 506 ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo) 507 { 508 int arg; 509 510 if (cmd->flags & NG_L2CAP_CMD_PENDING) 511 panic( 512 "%s: %s - duplicated command timeout, code=%#x, flags=%#x\n", 513 __func__, NG_NODE_NAME(cmd->con->l2cap->node), 514 cmd->code, cmd->flags); 515 516 arg = ((cmd->ident << 16) | cmd->con->con_handle); 517 cmd->flags |= NG_L2CAP_CMD_PENDING; 518 ng_callout(&cmd->timo, cmd->con->l2cap->node, NULL, timo, 519 ng_l2cap_process_command_timeout, NULL, arg); 520 521 return (0); 522 } /* ng_l2cap_command_timeout */ 523 524 /* 525 * Unset L2CAP command timeout 526 */ 527 528 int 529 ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd) 530 { 531 if (!(cmd->flags & NG_L2CAP_CMD_PENDING)) 532 panic( 533 "%s: %s - no command timeout, code=%#x, flags=%#x\n", 534 __func__, NG_NODE_NAME(cmd->con->l2cap->node), 535 cmd->code, cmd->flags); 536 537 if (ng_uncallout(&cmd->timo, cmd->con->l2cap->node) == 0) 538 return (ETIMEDOUT); 539 540 cmd->flags &= ~NG_L2CAP_CMD_PENDING; 541 542 return (0); 543 } /* ng_l2cap_command_untimeout */ 544 545 /* 546 * Prepend "m"buf with "size" bytes 547 */ 548 549 struct mbuf * 550 ng_l2cap_prepend(struct mbuf *m, int size) 551 { 552 M_PREPEND(m, size, M_DONTWAIT); 553 if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL)) 554 return (NULL); 555 556 return (m); 557 } /* ng_l2cap_prepend */ 558 559 /* 560 * Default flow settings 561 */ 562 563 ng_l2cap_flow_p 564 ng_l2cap_default_flow(void) 565 { 566 static ng_l2cap_flow_t default_flow = { 567 /* flags */ 0x0, 568 /* service_type */ NG_HCI_SERVICE_TYPE_BEST_EFFORT, 569 /* token_rate */ 0xffffffff, /* maximum */ 570 /* token_bucket_size */ 0xffffffff, /* maximum */ 571 /* peak_bandwidth */ 0x00000000, /* maximum */ 572 /* latency */ 0xffffffff, /* don't care */ 573 /* delay_variation */ 0xffffffff /* don't care */ 574 }; 575 576 return (&default_flow); 577 } /* ng_l2cap_default_flow */ 578 579 /* 580 * Get next available channel ID 581 * XXX FIXME this is *UGLY* but will do for now 582 */ 583 584 static u_int16_t 585 ng_l2cap_get_cid(ng_l2cap_p l2cap) 586 { 587 u_int16_t cid = l2cap->cid + 1; 588 589 if (cid < NG_L2CAP_FIRST_CID) 590 cid = NG_L2CAP_FIRST_CID; 591 592 while (cid != l2cap->cid) { 593 if (ng_l2cap_chan_by_scid(l2cap, cid) == NULL) { 594 l2cap->cid = cid; 595 596 return (cid); 597 } 598 599 cid ++; 600 if (cid < NG_L2CAP_FIRST_CID) 601 cid = NG_L2CAP_FIRST_CID; 602 } 603 604 return (NG_L2CAP_NULL_CID); 605 } /* ng_l2cap_get_cid */ 606 607 /* 608 * Get next available command ident 609 * XXX FIXME this is *UGLY* but will do for now 610 */ 611 612 u_int8_t 613 ng_l2cap_get_ident(ng_l2cap_con_p con) 614 { 615 u_int8_t ident = con->ident + 1; 616 617 if (ident < NG_L2CAP_FIRST_IDENT) 618 ident = NG_L2CAP_FIRST_IDENT; 619 620 while (ident != con->ident) { 621 if (ng_l2cap_cmd_by_ident(con, ident) == NULL) { 622 con->ident = ident; 623 624 return (ident); 625 } 626 627 ident ++; 628 if (ident < NG_L2CAP_FIRST_IDENT) 629 ident = NG_L2CAP_FIRST_IDENT; 630 } 631 632 return (NG_L2CAP_NULL_IDENT); 633 } /* ng_l2cap_get_ident */ 634 635