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