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