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