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.16 2002/09/04 21:38:38 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_lp_timeout (void *); 52 static void ng_l2cap_queue_command_timeout (void *); 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, NULL); 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. Will create new 97 * connection descriptor and signal channel. Will link both connection and 98 * channel 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 * Free connection descriptor. Will unlink connection and free everything. 129 */ 130 131 void 132 ng_l2cap_free_con(ng_l2cap_con_p con) 133 { 134 ng_l2cap_chan_p f = NULL, n = NULL; 135 136 if (con->state == NG_L2CAP_W4_LP_CON_CFM) 137 ng_l2cap_lp_untimeout(con); 138 139 if (con->tx_pkt != NULL) { 140 while (con->tx_pkt != NULL) { 141 struct mbuf *m = con->tx_pkt->m_nextpkt; 142 143 m_freem(con->tx_pkt); 144 con->tx_pkt = m; 145 } 146 } 147 148 NG_FREE_M(con->rx_pkt); 149 150 for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) { 151 n = LIST_NEXT(f, next); 152 153 if (f->con == con) 154 ng_l2cap_free_chan(f); 155 156 f = n; 157 } 158 159 while (!TAILQ_EMPTY(&con->cmd_list)) { 160 ng_l2cap_cmd_p cmd = TAILQ_FIRST(&con->cmd_list); 161 162 ng_l2cap_unlink_cmd(cmd); 163 ng_l2cap_free_cmd(cmd); 164 } 165 166 LIST_REMOVE(con, next); 167 bzero(con, sizeof(*con)); 168 FREE(con, M_NETGRAPH_L2CAP); 169 } /* ng_l2cap_free_con */ 170 171 /* 172 * Get connection by "remote" address 173 */ 174 175 ng_l2cap_con_p 176 ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr) 177 { 178 ng_l2cap_con_p con = NULL; 179 180 LIST_FOREACH(con, &l2cap->con_list, next) 181 if (bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0) 182 break; 183 184 return (con); 185 } /* ng_l2cap_con_by_addr */ 186 187 /* 188 * Get connection by "handle" 189 */ 190 191 ng_l2cap_con_p 192 ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle) 193 { 194 ng_l2cap_con_p con = NULL; 195 196 LIST_FOREACH(con, &l2cap->con_list, next) 197 if (con->con_handle == con_handle) 198 break; 199 200 return (con); 201 } /* ng_l2cap_con_by_handle */ 202 203 /* 204 * Allocate new L2CAP channel descriptor on "con" conection with "psm". 205 * Will link the channel to the l2cap node 206 */ 207 208 ng_l2cap_chan_p 209 ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm) 210 { 211 ng_l2cap_chan_p ch = NULL; 212 213 MALLOC(ch, ng_l2cap_chan_p, sizeof(*ch), M_NETGRAPH_L2CAP, 214 M_NOWAIT|M_ZERO); 215 if (ch == NULL) 216 return (NULL); 217 218 ch->scid = ng_l2cap_get_cid(l2cap); 219 220 if (ch->scid != NG_L2CAP_NULL_CID) { 221 /* Initialize channel */ 222 ch->psm = psm; 223 ch->con = con; 224 ch->state = NG_L2CAP_CLOSED; 225 226 /* Set MTU and flow control settings to defaults */ 227 ch->imtu = NG_L2CAP_MTU_DEFAULT; 228 bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow)); 229 230 ch->omtu = NG_L2CAP_MTU_DEFAULT; 231 bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)); 232 233 ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT; 234 ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT; 235 236 LIST_INSERT_HEAD(&l2cap->chan_list, ch, next); 237 } else { 238 bzero(ch, sizeof(*ch)); 239 FREE(ch, M_NETGRAPH_L2CAP); 240 ch = NULL; 241 } 242 243 return (ch); 244 } /* ng_l2cap_new_chan */ 245 246 /* 247 * Get channel by source (local) channel ID 248 */ 249 250 ng_l2cap_chan_p 251 ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid) 252 { 253 ng_l2cap_chan_p ch = NULL; 254 255 LIST_FOREACH(ch, &l2cap->chan_list, next) 256 if (ch->scid == scid) 257 break; 258 259 return (ch); 260 } /* ng_l2cap_chan_by_scid */ 261 262 /* 263 * Free channel descriptor. 264 */ 265 266 void 267 ng_l2cap_free_chan(ng_l2cap_chan_p ch) 268 { 269 ng_l2cap_cmd_p f = NULL, n = NULL; 270 271 f = TAILQ_FIRST(&ch->con->cmd_list); 272 while (f != NULL) { 273 n = TAILQ_NEXT(f, next); 274 275 if (f->ch == ch) { 276 ng_l2cap_unlink_cmd(f); 277 ng_l2cap_free_cmd(f); 278 } 279 280 f = n; 281 } 282 283 LIST_REMOVE(ch, next); 284 bzero(ch, sizeof(*ch)); 285 FREE(ch, M_NETGRAPH_L2CAP); 286 } /* ng_l2cap_free_chan */ 287 288 /* 289 * Create new L2CAP command descriptor. WILL NOT add command to the queue. 290 */ 291 292 ng_l2cap_cmd_p 293 ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident, 294 u_int8_t code, u_int32_t token) 295 { 296 ng_l2cap_cmd_p cmd = NULL; 297 298 KASSERT((ch == NULL || ch->con == con), 299 ("%s: %s - invalid channel pointer!\n", 300 __func__, NG_NODE_NAME(con->l2cap->node))); 301 302 MALLOC(cmd, ng_l2cap_cmd_p, sizeof(*cmd), M_NETGRAPH_L2CAP, 303 M_NOWAIT|M_ZERO); 304 if (cmd == NULL) 305 return (NULL); 306 307 cmd->con = con; 308 cmd->ch = ch; 309 cmd->ident = ident; 310 cmd->code = code; 311 cmd->token = token; 312 callout_handle_init(&cmd->timo); 313 314 return (cmd); 315 } /* ng_l2cap_new_cmd */ 316 317 /* 318 * Get L2CAP command descriptor by ident 319 */ 320 321 ng_l2cap_cmd_p 322 ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident) 323 { 324 ng_l2cap_cmd_p cmd = NULL; 325 326 TAILQ_FOREACH(cmd, &con->cmd_list, next) 327 if (cmd->ident == ident) 328 break; 329 330 return (cmd); 331 } /* ng_l2cap_cmd_by_ident */ 332 333 /* 334 * Set LP timeout 335 */ 336 337 void 338 ng_l2cap_lp_timeout(ng_l2cap_con_p con) 339 { 340 NG_NODE_REF(con->l2cap->node); 341 con->con_timo = timeout(ng_l2cap_queue_lp_timeout, con, 342 bluetooth_hci_connect_timeout()); 343 } /* ng_l2cap_lp_timeout */ 344 345 /* 346 * Unset LP timeout 347 */ 348 349 void 350 ng_l2cap_lp_untimeout(ng_l2cap_con_p con) 351 { 352 untimeout(ng_l2cap_queue_lp_timeout, con, con->con_timo); 353 NG_NODE_UNREF(con->l2cap->node); 354 } /* ng_l2cap_lp_untimeout */ 355 356 /* 357 * OK, timeout has happend so queue LP timeout processing function 358 */ 359 360 static void 361 ng_l2cap_queue_lp_timeout(void *context) 362 { 363 ng_l2cap_con_p con = (ng_l2cap_con_p) context; 364 node_p node = con->l2cap->node; 365 366 /* 367 * We need to save node pointer here, because ng_send_fn() 368 * can execute ng_l2cap_process_lp_timeout() without putting 369 * item into node's queue (if node can be locked). Once 370 * ng_l2cap_process_lp_timeout() executed the con pointer 371 * is no longer valid. 372 */ 373 374 if (NG_NODE_IS_VALID(node)) 375 ng_send_fn(node, NULL, &ng_l2cap_process_lp_timeout, con, 0); 376 377 NG_NODE_UNREF(node); 378 } /* ng_l2cap_queue_lp_timeout */ 379 380 /* 381 * Set L2CAP command timeout 382 */ 383 384 void 385 ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo) 386 { 387 NG_NODE_REF(cmd->con->l2cap->node); 388 cmd->flags |= NG_L2CAP_CMD_PENDING; 389 cmd->timo = timeout(ng_l2cap_queue_command_timeout, cmd, timo); 390 } /* ng_l2cap_command_timeout */ 391 392 /* 393 * Unset L2CAP command timeout 394 */ 395 396 void 397 ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd) 398 { 399 cmd->flags &= ~NG_L2CAP_CMD_PENDING; 400 untimeout(ng_l2cap_queue_command_timeout, cmd, cmd->timo); 401 NG_NODE_UNREF(cmd->con->l2cap->node); 402 } /* ng_l2cap_command_untimeout */ 403 404 /* 405 * OK, timeout has happend so queue L2CAP command timeout processing function 406 */ 407 408 static void 409 ng_l2cap_queue_command_timeout(void *context) 410 { 411 ng_l2cap_cmd_p cmd = (ng_l2cap_cmd_p) context; 412 node_p node = cmd->con->l2cap->node; 413 414 /* 415 * We need to save node pointer here, because ng_send_fn() 416 * can execute ng_l2cap_process_command_timeout() without 417 * putting item into node's queue (if node can be locked). 418 * Once ng_l2cap_process_command_timeout() executed the 419 * cmd pointer is no longer valid. 420 */ 421 422 if (NG_NODE_IS_VALID(node)) 423 ng_send_fn(node,NULL,&ng_l2cap_process_command_timeout,cmd,0); 424 425 NG_NODE_UNREF(node); 426 } /* ng_l2cap_queue_command_timeout */ 427 428 /* 429 * Prepend "m"buf with "size" bytes 430 */ 431 432 struct mbuf * 433 ng_l2cap_prepend(struct mbuf *m, int size) 434 { 435 M_PREPEND(m, size, M_DONTWAIT); 436 if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL)) 437 return (NULL); 438 439 return (m); 440 } /* ng_l2cap_prepend */ 441 442 /* 443 * Default flow settings 444 */ 445 446 ng_l2cap_flow_p 447 ng_l2cap_default_flow(void) 448 { 449 static ng_l2cap_flow_t default_flow = { 450 /* flags */ 0x0, 451 /* service_type */ NG_HCI_SERVICE_TYPE_BEST_EFFORT, 452 /* token_rate */ 0xffffffff, /* maximum */ 453 /* token_bucket_size */ 0xffffffff, /* maximum */ 454 /* peak_bandwidth */ 0x00000000, /* maximum */ 455 /* latency */ 0xffffffff, /* don't care */ 456 /* delay_variation */ 0xffffffff /* don't care */ 457 }; 458 459 return (&default_flow); 460 } /* ng_l2cap_default_flow */ 461 462 /* 463 * Get next available channel ID 464 * XXX FIXME this is *UGLY* but will do for now 465 */ 466 467 static u_int16_t 468 ng_l2cap_get_cid(ng_l2cap_p l2cap) 469 { 470 u_int16_t cid = l2cap->cid + 1; 471 472 if (cid < NG_L2CAP_FIRST_CID) 473 cid = NG_L2CAP_FIRST_CID; 474 475 while (cid != l2cap->cid) { 476 if (ng_l2cap_chan_by_scid(l2cap, cid) == NULL) { 477 l2cap->cid = cid; 478 479 return (cid); 480 } 481 482 cid ++; 483 if (cid < NG_L2CAP_FIRST_CID) 484 cid = NG_L2CAP_FIRST_CID; 485 } 486 487 return (NG_L2CAP_NULL_CID); 488 } /* ng_l2cap_get_cid */ 489 490 /* 491 * Get next available command ident 492 * XXX FIXME this is *UGLY* but will do for now 493 */ 494 495 u_int8_t 496 ng_l2cap_get_ident(ng_l2cap_con_p con) 497 { 498 u_int8_t ident = con->ident + 1; 499 500 if (ident < NG_L2CAP_FIRST_IDENT) 501 ident = NG_L2CAP_FIRST_IDENT; 502 503 while (ident != con->ident) { 504 if (ng_l2cap_cmd_by_ident(con, ident) == NULL) { 505 con->ident = ident; 506 507 return (ident); 508 } 509 510 ident ++; 511 if (ident < NG_L2CAP_FIRST_IDENT) 512 ident = NG_L2CAP_FIRST_IDENT; 513 } 514 515 return (NG_L2CAP_NULL_IDENT); 516 } /* ng_l2cap_get_ident */ 517 518