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