1*b06ebda0SMatthew Dillon /* 2*b06ebda0SMatthew Dillon * ng_l2cap_evnt.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_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $ 31*b06ebda0SMatthew Dillon * $FreeBSD: src/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c,v 1.8 2005/01/07 01:45:43 imp 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/endian.h> 38*b06ebda0SMatthew Dillon #include <sys/malloc.h> 39*b06ebda0SMatthew Dillon #include <sys/mbuf.h> 40*b06ebda0SMatthew Dillon #include <sys/queue.h> 41*b06ebda0SMatthew Dillon #include <netgraph/ng_message.h> 42*b06ebda0SMatthew Dillon #include <netgraph/netgraph.h> 43*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/include/ng_bluetooth.h> 44*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/include/ng_hci.h> 45*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/include/ng_l2cap.h> 46*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h> 47*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h> 48*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h> 49*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h> 50*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h> 51*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h> 52*b06ebda0SMatthew Dillon 53*b06ebda0SMatthew Dillon /****************************************************************************** 54*b06ebda0SMatthew Dillon ****************************************************************************** 55*b06ebda0SMatthew Dillon ** L2CAP events processing module 56*b06ebda0SMatthew Dillon ****************************************************************************** 57*b06ebda0SMatthew Dillon ******************************************************************************/ 58*b06ebda0SMatthew Dillon 59*b06ebda0SMatthew Dillon static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p); 60*b06ebda0SMatthew Dillon static int ng_l2cap_process_cmd_rej (ng_l2cap_con_p, u_int8_t); 61*b06ebda0SMatthew Dillon static int ng_l2cap_process_con_req (ng_l2cap_con_p, u_int8_t); 62*b06ebda0SMatthew Dillon static int ng_l2cap_process_con_rsp (ng_l2cap_con_p, u_int8_t); 63*b06ebda0SMatthew Dillon static int ng_l2cap_process_cfg_req (ng_l2cap_con_p, u_int8_t); 64*b06ebda0SMatthew Dillon static int ng_l2cap_process_cfg_rsp (ng_l2cap_con_p, u_int8_t); 65*b06ebda0SMatthew Dillon static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t); 66*b06ebda0SMatthew Dillon static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t); 67*b06ebda0SMatthew Dillon static int ng_l2cap_process_echo_req (ng_l2cap_con_p, u_int8_t); 68*b06ebda0SMatthew Dillon static int ng_l2cap_process_echo_rsp (ng_l2cap_con_p, u_int8_t); 69*b06ebda0SMatthew Dillon static int ng_l2cap_process_info_req (ng_l2cap_con_p, u_int8_t); 70*b06ebda0SMatthew Dillon static int ng_l2cap_process_info_rsp (ng_l2cap_con_p, u_int8_t); 71*b06ebda0SMatthew Dillon static int send_l2cap_reject 72*b06ebda0SMatthew Dillon (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t); 73*b06ebda0SMatthew Dillon static int send_l2cap_con_rej 74*b06ebda0SMatthew Dillon (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t); 75*b06ebda0SMatthew Dillon static int send_l2cap_cfg_rsp 76*b06ebda0SMatthew Dillon (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *); 77*b06ebda0SMatthew Dillon static int get_next_l2cap_opt 78*b06ebda0SMatthew Dillon (struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p); 79*b06ebda0SMatthew Dillon 80*b06ebda0SMatthew Dillon /* 81*b06ebda0SMatthew Dillon * Receive L2CAP packet. First get L2CAP header and verify packet. Than 82*b06ebda0SMatthew Dillon * get destination channel and process packet. 83*b06ebda0SMatthew Dillon */ 84*b06ebda0SMatthew Dillon 85*b06ebda0SMatthew Dillon int 86*b06ebda0SMatthew Dillon ng_l2cap_receive(ng_l2cap_con_p con) 87*b06ebda0SMatthew Dillon { 88*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 89*b06ebda0SMatthew Dillon ng_l2cap_hdr_t *hdr = NULL; 90*b06ebda0SMatthew Dillon int error = 0; 91*b06ebda0SMatthew Dillon 92*b06ebda0SMatthew Dillon /* Check packet */ 93*b06ebda0SMatthew Dillon if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { 94*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 95*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP packet. Packet too small, len=%d\n", 96*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 97*b06ebda0SMatthew Dillon con->rx_pkt->m_pkthdr.len); 98*b06ebda0SMatthew Dillon error = EMSGSIZE; 99*b06ebda0SMatthew Dillon goto drop; 100*b06ebda0SMatthew Dillon } 101*b06ebda0SMatthew Dillon 102*b06ebda0SMatthew Dillon /* Get L2CAP header */ 103*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); 104*b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) 105*b06ebda0SMatthew Dillon return (ENOBUFS); 106*b06ebda0SMatthew Dillon 107*b06ebda0SMatthew Dillon hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *); 108*b06ebda0SMatthew Dillon hdr->length = le16toh(hdr->length); 109*b06ebda0SMatthew Dillon hdr->dcid = le16toh(hdr->dcid); 110*b06ebda0SMatthew Dillon 111*b06ebda0SMatthew Dillon /* Check payload size */ 112*b06ebda0SMatthew Dillon if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) { 113*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 114*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n", 115*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), hdr->length, 116*b06ebda0SMatthew Dillon con->rx_pkt->m_pkthdr.len - sizeof(*hdr)); 117*b06ebda0SMatthew Dillon error = EMSGSIZE; 118*b06ebda0SMatthew Dillon goto drop; 119*b06ebda0SMatthew Dillon } 120*b06ebda0SMatthew Dillon 121*b06ebda0SMatthew Dillon /* Process packet */ 122*b06ebda0SMatthew Dillon switch (hdr->dcid) { 123*b06ebda0SMatthew Dillon case NG_L2CAP_SIGNAL_CID: /* L2CAP command */ 124*b06ebda0SMatthew Dillon m_adj(con->rx_pkt, sizeof(*hdr)); 125*b06ebda0SMatthew Dillon error = ng_l2cap_process_signal_cmd(con); 126*b06ebda0SMatthew Dillon break; 127*b06ebda0SMatthew Dillon 128*b06ebda0SMatthew Dillon case NG_L2CAP_CLT_CID: /* Connectionless packet */ 129*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_clt_receive(con); 130*b06ebda0SMatthew Dillon break; 131*b06ebda0SMatthew Dillon 132*b06ebda0SMatthew Dillon default: /* Data packet */ 133*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_receive(con); 134*b06ebda0SMatthew Dillon break; 135*b06ebda0SMatthew Dillon } 136*b06ebda0SMatthew Dillon 137*b06ebda0SMatthew Dillon return (error); 138*b06ebda0SMatthew Dillon drop: 139*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 140*b06ebda0SMatthew Dillon 141*b06ebda0SMatthew Dillon return (error); 142*b06ebda0SMatthew Dillon } /* ng_l2cap_receive */ 143*b06ebda0SMatthew Dillon 144*b06ebda0SMatthew Dillon /* 145*b06ebda0SMatthew Dillon * Process L2CAP signaling command. We already know that destination channel ID 146*b06ebda0SMatthew Dillon * is 0x1 that means we have received signaling command from peer's L2CAP layer. 147*b06ebda0SMatthew Dillon * So get command header, decode and process it. 148*b06ebda0SMatthew Dillon * 149*b06ebda0SMatthew Dillon * XXX do we need to check signaling MTU here? 150*b06ebda0SMatthew Dillon */ 151*b06ebda0SMatthew Dillon 152*b06ebda0SMatthew Dillon static int 153*b06ebda0SMatthew Dillon ng_l2cap_process_signal_cmd(ng_l2cap_con_p con) 154*b06ebda0SMatthew Dillon { 155*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 156*b06ebda0SMatthew Dillon ng_l2cap_cmd_hdr_t *hdr = NULL; 157*b06ebda0SMatthew Dillon struct mbuf *m = NULL; 158*b06ebda0SMatthew Dillon 159*b06ebda0SMatthew Dillon while (con->rx_pkt != NULL) { 160*b06ebda0SMatthew Dillon /* Verify packet length */ 161*b06ebda0SMatthew Dillon if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { 162*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 163*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n", 164*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 165*b06ebda0SMatthew Dillon con->rx_pkt->m_pkthdr.len); 166*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 167*b06ebda0SMatthew Dillon 168*b06ebda0SMatthew Dillon return (EMSGSIZE); 169*b06ebda0SMatthew Dillon } 170*b06ebda0SMatthew Dillon 171*b06ebda0SMatthew Dillon /* Get signaling command */ 172*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); 173*b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) 174*b06ebda0SMatthew Dillon return (ENOBUFS); 175*b06ebda0SMatthew Dillon 176*b06ebda0SMatthew Dillon hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); 177*b06ebda0SMatthew Dillon hdr->length = le16toh(hdr->length); 178*b06ebda0SMatthew Dillon m_adj(con->rx_pkt, sizeof(*hdr)); 179*b06ebda0SMatthew Dillon 180*b06ebda0SMatthew Dillon /* Verify command length */ 181*b06ebda0SMatthew Dillon if (con->rx_pkt->m_pkthdr.len < hdr->length) { 182*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 183*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \ 184*b06ebda0SMatthew Dillon "Invalid command length=%d, m_pkthdr.len=%d\n", 185*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 186*b06ebda0SMatthew Dillon hdr->code, hdr->ident, hdr->length, 187*b06ebda0SMatthew Dillon con->rx_pkt->m_pkthdr.len); 188*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 189*b06ebda0SMatthew Dillon 190*b06ebda0SMatthew Dillon return (EMSGSIZE); 191*b06ebda0SMatthew Dillon } 192*b06ebda0SMatthew Dillon 193*b06ebda0SMatthew Dillon /* Get the command, save the rest (if any) */ 194*b06ebda0SMatthew Dillon if (con->rx_pkt->m_pkthdr.len > hdr->length) 195*b06ebda0SMatthew Dillon m = m_split(con->rx_pkt, hdr->length, M_DONTWAIT); 196*b06ebda0SMatthew Dillon else 197*b06ebda0SMatthew Dillon m = NULL; 198*b06ebda0SMatthew Dillon 199*b06ebda0SMatthew Dillon /* Process command */ 200*b06ebda0SMatthew Dillon switch (hdr->code) { 201*b06ebda0SMatthew Dillon case NG_L2CAP_CMD_REJ: 202*b06ebda0SMatthew Dillon ng_l2cap_process_cmd_rej(con, hdr->ident); 203*b06ebda0SMatthew Dillon break; 204*b06ebda0SMatthew Dillon 205*b06ebda0SMatthew Dillon case NG_L2CAP_CON_REQ: 206*b06ebda0SMatthew Dillon ng_l2cap_process_con_req(con, hdr->ident); 207*b06ebda0SMatthew Dillon break; 208*b06ebda0SMatthew Dillon 209*b06ebda0SMatthew Dillon case NG_L2CAP_CON_RSP: 210*b06ebda0SMatthew Dillon ng_l2cap_process_con_rsp(con, hdr->ident); 211*b06ebda0SMatthew Dillon break; 212*b06ebda0SMatthew Dillon 213*b06ebda0SMatthew Dillon case NG_L2CAP_CFG_REQ: 214*b06ebda0SMatthew Dillon ng_l2cap_process_cfg_req(con, hdr->ident); 215*b06ebda0SMatthew Dillon break; 216*b06ebda0SMatthew Dillon 217*b06ebda0SMatthew Dillon case NG_L2CAP_CFG_RSP: 218*b06ebda0SMatthew Dillon ng_l2cap_process_cfg_rsp(con, hdr->ident); 219*b06ebda0SMatthew Dillon break; 220*b06ebda0SMatthew Dillon 221*b06ebda0SMatthew Dillon case NG_L2CAP_DISCON_REQ: 222*b06ebda0SMatthew Dillon ng_l2cap_process_discon_req(con, hdr->ident); 223*b06ebda0SMatthew Dillon break; 224*b06ebda0SMatthew Dillon 225*b06ebda0SMatthew Dillon case NG_L2CAP_DISCON_RSP: 226*b06ebda0SMatthew Dillon ng_l2cap_process_discon_rsp(con, hdr->ident); 227*b06ebda0SMatthew Dillon break; 228*b06ebda0SMatthew Dillon 229*b06ebda0SMatthew Dillon case NG_L2CAP_ECHO_REQ: 230*b06ebda0SMatthew Dillon ng_l2cap_process_echo_req(con, hdr->ident); 231*b06ebda0SMatthew Dillon break; 232*b06ebda0SMatthew Dillon 233*b06ebda0SMatthew Dillon case NG_L2CAP_ECHO_RSP: 234*b06ebda0SMatthew Dillon ng_l2cap_process_echo_rsp(con, hdr->ident); 235*b06ebda0SMatthew Dillon break; 236*b06ebda0SMatthew Dillon 237*b06ebda0SMatthew Dillon case NG_L2CAP_INFO_REQ: 238*b06ebda0SMatthew Dillon ng_l2cap_process_info_req(con, hdr->ident); 239*b06ebda0SMatthew Dillon break; 240*b06ebda0SMatthew Dillon 241*b06ebda0SMatthew Dillon case NG_L2CAP_INFO_RSP: 242*b06ebda0SMatthew Dillon ng_l2cap_process_info_rsp(con, hdr->ident); 243*b06ebda0SMatthew Dillon break; 244*b06ebda0SMatthew Dillon 245*b06ebda0SMatthew Dillon default: 246*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 247*b06ebda0SMatthew Dillon "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n", 248*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 249*b06ebda0SMatthew Dillon hdr->code, hdr->ident, hdr->length); 250*b06ebda0SMatthew Dillon 251*b06ebda0SMatthew Dillon /* 252*b06ebda0SMatthew Dillon * Send L2CAP_CommandRej. Do not really care 253*b06ebda0SMatthew Dillon * about the result 254*b06ebda0SMatthew Dillon */ 255*b06ebda0SMatthew Dillon 256*b06ebda0SMatthew Dillon send_l2cap_reject(con, hdr->ident, 257*b06ebda0SMatthew Dillon NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0); 258*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 259*b06ebda0SMatthew Dillon break; 260*b06ebda0SMatthew Dillon } 261*b06ebda0SMatthew Dillon 262*b06ebda0SMatthew Dillon con->rx_pkt = m; 263*b06ebda0SMatthew Dillon } 264*b06ebda0SMatthew Dillon 265*b06ebda0SMatthew Dillon return (0); 266*b06ebda0SMatthew Dillon } /* ng_l2cap_process_signal_cmd */ 267*b06ebda0SMatthew Dillon 268*b06ebda0SMatthew Dillon /* 269*b06ebda0SMatthew Dillon * Process L2CAP_CommandRej command 270*b06ebda0SMatthew Dillon */ 271*b06ebda0SMatthew Dillon 272*b06ebda0SMatthew Dillon static int 273*b06ebda0SMatthew Dillon ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident) 274*b06ebda0SMatthew Dillon { 275*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 276*b06ebda0SMatthew Dillon ng_l2cap_cmd_rej_cp *cp = NULL; 277*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 278*b06ebda0SMatthew Dillon 279*b06ebda0SMatthew Dillon /* Get command parameters */ 280*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 281*b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) 282*b06ebda0SMatthew Dillon return (ENOBUFS); 283*b06ebda0SMatthew Dillon 284*b06ebda0SMatthew Dillon cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *); 285*b06ebda0SMatthew Dillon cp->reason = le16toh(cp->reason); 286*b06ebda0SMatthew Dillon 287*b06ebda0SMatthew Dillon /* Check if we have pending command descriptor */ 288*b06ebda0SMatthew Dillon cmd = ng_l2cap_cmd_by_ident(con, ident); 289*b06ebda0SMatthew Dillon if (cmd != NULL) { 290*b06ebda0SMatthew Dillon /* If command timeout already happened then ignore reject */ 291*b06ebda0SMatthew Dillon if (ng_l2cap_command_untimeout(cmd) != 0) { 292*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 293*b06ebda0SMatthew Dillon return (ETIMEDOUT); 294*b06ebda0SMatthew Dillon } 295*b06ebda0SMatthew Dillon 296*b06ebda0SMatthew Dillon ng_l2cap_unlink_cmd(cmd); 297*b06ebda0SMatthew Dillon 298*b06ebda0SMatthew Dillon switch (cmd->code) { 299*b06ebda0SMatthew Dillon case NG_L2CAP_CON_REQ: 300*b06ebda0SMatthew Dillon ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0); 301*b06ebda0SMatthew Dillon ng_l2cap_free_chan(cmd->ch); 302*b06ebda0SMatthew Dillon break; 303*b06ebda0SMatthew Dillon 304*b06ebda0SMatthew Dillon case NG_L2CAP_CFG_REQ: 305*b06ebda0SMatthew Dillon ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason); 306*b06ebda0SMatthew Dillon break; 307*b06ebda0SMatthew Dillon 308*b06ebda0SMatthew Dillon case NG_L2CAP_DISCON_REQ: 309*b06ebda0SMatthew Dillon ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason); 310*b06ebda0SMatthew Dillon ng_l2cap_free_chan(cmd->ch); /* XXX free channel */ 311*b06ebda0SMatthew Dillon break; 312*b06ebda0SMatthew Dillon 313*b06ebda0SMatthew Dillon case NG_L2CAP_ECHO_REQ: 314*b06ebda0SMatthew Dillon ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 315*b06ebda0SMatthew Dillon cp->reason, NULL); 316*b06ebda0SMatthew Dillon break; 317*b06ebda0SMatthew Dillon 318*b06ebda0SMatthew Dillon case NG_L2CAP_INFO_REQ: 319*b06ebda0SMatthew Dillon ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 320*b06ebda0SMatthew Dillon cp->reason, NULL); 321*b06ebda0SMatthew Dillon break; 322*b06ebda0SMatthew Dillon 323*b06ebda0SMatthew Dillon default: 324*b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 325*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n", 326*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), cmd->code); 327*b06ebda0SMatthew Dillon break; 328*b06ebda0SMatthew Dillon } 329*b06ebda0SMatthew Dillon 330*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 331*b06ebda0SMatthew Dillon } else 332*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 333*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_CommandRej command. " \ 334*b06ebda0SMatthew Dillon "Requested ident does not exist, ident=%d\n", 335*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ident); 336*b06ebda0SMatthew Dillon 337*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 338*b06ebda0SMatthew Dillon 339*b06ebda0SMatthew Dillon return (0); 340*b06ebda0SMatthew Dillon } /* ng_l2cap_process_cmd_rej */ 341*b06ebda0SMatthew Dillon 342*b06ebda0SMatthew Dillon /* 343*b06ebda0SMatthew Dillon * Process L2CAP_ConnectReq command 344*b06ebda0SMatthew Dillon */ 345*b06ebda0SMatthew Dillon 346*b06ebda0SMatthew Dillon static int 347*b06ebda0SMatthew Dillon ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident) 348*b06ebda0SMatthew Dillon { 349*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 350*b06ebda0SMatthew Dillon struct mbuf *m = con->rx_pkt; 351*b06ebda0SMatthew Dillon ng_l2cap_con_req_cp *cp = NULL; 352*b06ebda0SMatthew Dillon ng_l2cap_chan_p ch = NULL; 353*b06ebda0SMatthew Dillon int error = 0; 354*b06ebda0SMatthew Dillon u_int16_t dcid, psm; 355*b06ebda0SMatthew Dillon 356*b06ebda0SMatthew Dillon /* Get command parameters */ 357*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 358*b06ebda0SMatthew Dillon if (m == NULL) 359*b06ebda0SMatthew Dillon return (ENOBUFS); 360*b06ebda0SMatthew Dillon 361*b06ebda0SMatthew Dillon cp = mtod(m, ng_l2cap_con_req_cp *); 362*b06ebda0SMatthew Dillon psm = le16toh(cp->psm); 363*b06ebda0SMatthew Dillon dcid = le16toh(cp->scid); 364*b06ebda0SMatthew Dillon 365*b06ebda0SMatthew Dillon NG_FREE_M(m); 366*b06ebda0SMatthew Dillon con->rx_pkt = NULL; 367*b06ebda0SMatthew Dillon 368*b06ebda0SMatthew Dillon /* 369*b06ebda0SMatthew Dillon * Create new channel and send L2CA_ConnectInd notification 370*b06ebda0SMatthew Dillon * to the upper layer protocol. 371*b06ebda0SMatthew Dillon */ 372*b06ebda0SMatthew Dillon 373*b06ebda0SMatthew Dillon ch = ng_l2cap_new_chan(l2cap, con, psm); 374*b06ebda0SMatthew Dillon if (ch == NULL) 375*b06ebda0SMatthew Dillon return (send_l2cap_con_rej(con, ident, 0, dcid, 376*b06ebda0SMatthew Dillon NG_L2CAP_NO_RESOURCES)); 377*b06ebda0SMatthew Dillon 378*b06ebda0SMatthew Dillon /* Update channel IDs */ 379*b06ebda0SMatthew Dillon ch->dcid = dcid; 380*b06ebda0SMatthew Dillon 381*b06ebda0SMatthew Dillon /* Sent L2CA_ConnectInd notification to the upper layer */ 382*b06ebda0SMatthew Dillon ch->ident = ident; 383*b06ebda0SMatthew Dillon ch->state = NG_L2CAP_W4_L2CA_CON_RSP; 384*b06ebda0SMatthew Dillon 385*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_con_ind(ch); 386*b06ebda0SMatthew Dillon if (error != 0) { 387*b06ebda0SMatthew Dillon send_l2cap_con_rej(con, ident, ch->scid, dcid, 388*b06ebda0SMatthew Dillon (error == ENOMEM)? NG_L2CAP_NO_RESOURCES : 389*b06ebda0SMatthew Dillon NG_L2CAP_PSM_NOT_SUPPORTED); 390*b06ebda0SMatthew Dillon ng_l2cap_free_chan(ch); 391*b06ebda0SMatthew Dillon } 392*b06ebda0SMatthew Dillon 393*b06ebda0SMatthew Dillon return (error); 394*b06ebda0SMatthew Dillon } /* ng_l2cap_process_con_req */ 395*b06ebda0SMatthew Dillon 396*b06ebda0SMatthew Dillon /* 397*b06ebda0SMatthew Dillon * Process L2CAP_ConnectRsp command 398*b06ebda0SMatthew Dillon */ 399*b06ebda0SMatthew Dillon 400*b06ebda0SMatthew Dillon static int 401*b06ebda0SMatthew Dillon ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident) 402*b06ebda0SMatthew Dillon { 403*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 404*b06ebda0SMatthew Dillon struct mbuf *m = con->rx_pkt; 405*b06ebda0SMatthew Dillon ng_l2cap_con_rsp_cp *cp = NULL; 406*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 407*b06ebda0SMatthew Dillon u_int16_t scid, dcid, result, status; 408*b06ebda0SMatthew Dillon int error = 0; 409*b06ebda0SMatthew Dillon 410*b06ebda0SMatthew Dillon /* Get command parameters */ 411*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 412*b06ebda0SMatthew Dillon if (m == NULL) 413*b06ebda0SMatthew Dillon return (ENOBUFS); 414*b06ebda0SMatthew Dillon 415*b06ebda0SMatthew Dillon cp = mtod(m, ng_l2cap_con_rsp_cp *); 416*b06ebda0SMatthew Dillon dcid = le16toh(cp->dcid); 417*b06ebda0SMatthew Dillon scid = le16toh(cp->scid); 418*b06ebda0SMatthew Dillon result = le16toh(cp->result); 419*b06ebda0SMatthew Dillon status = le16toh(cp->status); 420*b06ebda0SMatthew Dillon 421*b06ebda0SMatthew Dillon NG_FREE_M(m); 422*b06ebda0SMatthew Dillon con->rx_pkt = NULL; 423*b06ebda0SMatthew Dillon 424*b06ebda0SMatthew Dillon /* Check if we have pending command descriptor */ 425*b06ebda0SMatthew Dillon cmd = ng_l2cap_cmd_by_ident(con, ident); 426*b06ebda0SMatthew Dillon if (cmd == NULL) { 427*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 428*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n", 429*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ident, 430*b06ebda0SMatthew Dillon con->con_handle); 431*b06ebda0SMatthew Dillon 432*b06ebda0SMatthew Dillon return (ENOENT); 433*b06ebda0SMatthew Dillon } 434*b06ebda0SMatthew Dillon 435*b06ebda0SMatthew Dillon /* Verify channel state, if invalid - do nothing */ 436*b06ebda0SMatthew Dillon if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) { 437*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 438*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConnectRsp. " \ 439*b06ebda0SMatthew Dillon "Invalid channel state, cid=%d, state=%d\n", 440*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), scid, 441*b06ebda0SMatthew Dillon cmd->ch->state); 442*b06ebda0SMatthew Dillon goto reject; 443*b06ebda0SMatthew Dillon } 444*b06ebda0SMatthew Dillon 445*b06ebda0SMatthew Dillon /* Verify CIDs and send reject if does not match */ 446*b06ebda0SMatthew Dillon if (cmd->ch->scid != scid) { 447*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 448*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n", 449*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 450*b06ebda0SMatthew Dillon scid); 451*b06ebda0SMatthew Dillon goto reject; 452*b06ebda0SMatthew Dillon } 453*b06ebda0SMatthew Dillon 454*b06ebda0SMatthew Dillon /* 455*b06ebda0SMatthew Dillon * Looks good. We got confirmation from our peer. Now process 456*b06ebda0SMatthew Dillon * it. First disable RTX timer. Then check the result and send 457*b06ebda0SMatthew Dillon * notification to the upper layer. If command timeout already 458*b06ebda0SMatthew Dillon * happened then ignore response. 459*b06ebda0SMatthew Dillon */ 460*b06ebda0SMatthew Dillon 461*b06ebda0SMatthew Dillon if ((error = ng_l2cap_command_untimeout(cmd)) != 0) 462*b06ebda0SMatthew Dillon return (error); 463*b06ebda0SMatthew Dillon 464*b06ebda0SMatthew Dillon if (result == NG_L2CAP_PENDING) { 465*b06ebda0SMatthew Dillon /* 466*b06ebda0SMatthew Dillon * Our peer wants more time to complete connection. We shall 467*b06ebda0SMatthew Dillon * start ERTX timer and wait. Keep command in the list. 468*b06ebda0SMatthew Dillon */ 469*b06ebda0SMatthew Dillon 470*b06ebda0SMatthew Dillon cmd->ch->dcid = dcid; 471*b06ebda0SMatthew Dillon ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout()); 472*b06ebda0SMatthew Dillon 473*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, 474*b06ebda0SMatthew Dillon result, status); 475*b06ebda0SMatthew Dillon if (error != 0) 476*b06ebda0SMatthew Dillon ng_l2cap_free_chan(cmd->ch); 477*b06ebda0SMatthew Dillon } else { 478*b06ebda0SMatthew Dillon ng_l2cap_unlink_cmd(cmd); 479*b06ebda0SMatthew Dillon 480*b06ebda0SMatthew Dillon if (result == NG_L2CAP_SUCCESS) { 481*b06ebda0SMatthew Dillon /* 482*b06ebda0SMatthew Dillon * Channel is open. Complete command and move to CONFIG 483*b06ebda0SMatthew Dillon * state. Since we have sent positive confirmation we 484*b06ebda0SMatthew Dillon * expect to receive L2CA_Config request from the upper 485*b06ebda0SMatthew Dillon * layer protocol. 486*b06ebda0SMatthew Dillon */ 487*b06ebda0SMatthew Dillon 488*b06ebda0SMatthew Dillon cmd->ch->dcid = dcid; 489*b06ebda0SMatthew Dillon cmd->ch->state = NG_L2CAP_CONFIG; 490*b06ebda0SMatthew Dillon } else 491*b06ebda0SMatthew Dillon /* There was an error, so close the channel */ 492*b06ebda0SMatthew Dillon NG_L2CAP_INFO( 493*b06ebda0SMatthew Dillon "%s: %s - failed to open L2CAP channel, result=%d, status=%d\n", 494*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), result, 495*b06ebda0SMatthew Dillon status); 496*b06ebda0SMatthew Dillon 497*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, 498*b06ebda0SMatthew Dillon result, status); 499*b06ebda0SMatthew Dillon 500*b06ebda0SMatthew Dillon /* XXX do we have to remove the channel on error? */ 501*b06ebda0SMatthew Dillon if (error != 0 || result != NG_L2CAP_SUCCESS) 502*b06ebda0SMatthew Dillon ng_l2cap_free_chan(cmd->ch); 503*b06ebda0SMatthew Dillon 504*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 505*b06ebda0SMatthew Dillon } 506*b06ebda0SMatthew Dillon 507*b06ebda0SMatthew Dillon return (error); 508*b06ebda0SMatthew Dillon 509*b06ebda0SMatthew Dillon reject: 510*b06ebda0SMatthew Dillon /* Send reject. Do not really care about the result */ 511*b06ebda0SMatthew Dillon send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid); 512*b06ebda0SMatthew Dillon 513*b06ebda0SMatthew Dillon return (0); 514*b06ebda0SMatthew Dillon } /* ng_l2cap_process_con_rsp */ 515*b06ebda0SMatthew Dillon 516*b06ebda0SMatthew Dillon /* 517*b06ebda0SMatthew Dillon * Process L2CAP_ConfigReq command 518*b06ebda0SMatthew Dillon */ 519*b06ebda0SMatthew Dillon 520*b06ebda0SMatthew Dillon static int 521*b06ebda0SMatthew Dillon ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident) 522*b06ebda0SMatthew Dillon { 523*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 524*b06ebda0SMatthew Dillon struct mbuf *m = con->rx_pkt; 525*b06ebda0SMatthew Dillon ng_l2cap_cfg_req_cp *cp = NULL; 526*b06ebda0SMatthew Dillon ng_l2cap_chan_p ch = NULL; 527*b06ebda0SMatthew Dillon u_int16_t dcid, respond, result; 528*b06ebda0SMatthew Dillon ng_l2cap_cfg_opt_t hdr; 529*b06ebda0SMatthew Dillon ng_l2cap_cfg_opt_val_t val; 530*b06ebda0SMatthew Dillon int off, error = 0; 531*b06ebda0SMatthew Dillon 532*b06ebda0SMatthew Dillon /* Get command parameters */ 533*b06ebda0SMatthew Dillon con->rx_pkt = NULL; 534*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 535*b06ebda0SMatthew Dillon if (m == NULL) 536*b06ebda0SMatthew Dillon return (ENOBUFS); 537*b06ebda0SMatthew Dillon 538*b06ebda0SMatthew Dillon cp = mtod(m, ng_l2cap_cfg_req_cp *); 539*b06ebda0SMatthew Dillon dcid = le16toh(cp->dcid); 540*b06ebda0SMatthew Dillon respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags)); 541*b06ebda0SMatthew Dillon m_adj(m, sizeof(*cp)); 542*b06ebda0SMatthew Dillon 543*b06ebda0SMatthew Dillon /* Check if we have this channel and it is in valid state */ 544*b06ebda0SMatthew Dillon ch = ng_l2cap_chan_by_scid(l2cap, dcid); 545*b06ebda0SMatthew Dillon if (ch == NULL) { 546*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 547*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigReq command. " \ 548*b06ebda0SMatthew Dillon "Channel does not exist, cid=%d\n", 549*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), dcid); 550*b06ebda0SMatthew Dillon goto reject; 551*b06ebda0SMatthew Dillon } 552*b06ebda0SMatthew Dillon 553*b06ebda0SMatthew Dillon /* Verify channel state */ 554*b06ebda0SMatthew Dillon if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) { 555*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 556*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigReq. " \ 557*b06ebda0SMatthew Dillon "Invalid channel state, cid=%d, state=%d\n", 558*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state); 559*b06ebda0SMatthew Dillon goto reject; 560*b06ebda0SMatthew Dillon } 561*b06ebda0SMatthew Dillon 562*b06ebda0SMatthew Dillon if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */ 563*b06ebda0SMatthew Dillon ch->cfg_state = 0; 564*b06ebda0SMatthew Dillon ch->state = NG_L2CAP_CONFIG; 565*b06ebda0SMatthew Dillon } 566*b06ebda0SMatthew Dillon 567*b06ebda0SMatthew Dillon for (result = 0, off = 0; ; ) { 568*b06ebda0SMatthew Dillon error = get_next_l2cap_opt(m, &off, &hdr, &val); 569*b06ebda0SMatthew Dillon if (error == 0) { /* We done with this packet */ 570*b06ebda0SMatthew Dillon NG_FREE_M(m); 571*b06ebda0SMatthew Dillon break; 572*b06ebda0SMatthew Dillon } else if (error > 0) { /* Got option */ 573*b06ebda0SMatthew Dillon switch (hdr.type) { 574*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_MTU: 575*b06ebda0SMatthew Dillon ch->omtu = val.mtu; 576*b06ebda0SMatthew Dillon break; 577*b06ebda0SMatthew Dillon 578*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_FLUSH_TIMO: 579*b06ebda0SMatthew Dillon ch->flush_timo = val.flush_timo; 580*b06ebda0SMatthew Dillon break; 581*b06ebda0SMatthew Dillon 582*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_QOS: 583*b06ebda0SMatthew Dillon bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow)); 584*b06ebda0SMatthew Dillon break; 585*b06ebda0SMatthew Dillon 586*b06ebda0SMatthew Dillon default: /* Ignore unknown hint option */ 587*b06ebda0SMatthew Dillon break; 588*b06ebda0SMatthew Dillon } 589*b06ebda0SMatthew Dillon } else { /* Oops, something is wrong */ 590*b06ebda0SMatthew Dillon respond = 1; 591*b06ebda0SMatthew Dillon 592*b06ebda0SMatthew Dillon if (error == -3) { 593*b06ebda0SMatthew Dillon 594*b06ebda0SMatthew Dillon /* 595*b06ebda0SMatthew Dillon * Adjust mbuf so we can get to the start 596*b06ebda0SMatthew Dillon * of the first option we did not like. 597*b06ebda0SMatthew Dillon */ 598*b06ebda0SMatthew Dillon 599*b06ebda0SMatthew Dillon m_adj(m, off - sizeof(hdr)); 600*b06ebda0SMatthew Dillon m->m_pkthdr.len = sizeof(hdr) + hdr.length; 601*b06ebda0SMatthew Dillon 602*b06ebda0SMatthew Dillon result = NG_L2CAP_UNKNOWN_OPTION; 603*b06ebda0SMatthew Dillon } else { 604*b06ebda0SMatthew Dillon /* XXX FIXME Send other reject codes? */ 605*b06ebda0SMatthew Dillon NG_FREE_M(m); 606*b06ebda0SMatthew Dillon result = NG_L2CAP_REJECT; 607*b06ebda0SMatthew Dillon } 608*b06ebda0SMatthew Dillon 609*b06ebda0SMatthew Dillon break; 610*b06ebda0SMatthew Dillon } 611*b06ebda0SMatthew Dillon } 612*b06ebda0SMatthew Dillon 613*b06ebda0SMatthew Dillon /* 614*b06ebda0SMatthew Dillon * Now check and see if we have to respond. If everything was OK then 615*b06ebda0SMatthew Dillon * respond contain "C flag" and (if set) we will respond with empty 616*b06ebda0SMatthew Dillon * packet and will wait for more options. 617*b06ebda0SMatthew Dillon * 618*b06ebda0SMatthew Dillon * Other case is that we did not like peer's options and will respond 619*b06ebda0SMatthew Dillon * with L2CAP_Config response command with Reject error code. 620*b06ebda0SMatthew Dillon * 621*b06ebda0SMatthew Dillon * When "respond == 0" than we have received all options and we will 622*b06ebda0SMatthew Dillon * sent L2CA_ConfigInd event to the upper layer protocol. 623*b06ebda0SMatthew Dillon */ 624*b06ebda0SMatthew Dillon 625*b06ebda0SMatthew Dillon if (respond) { 626*b06ebda0SMatthew Dillon error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m); 627*b06ebda0SMatthew Dillon if (error != 0) { 628*b06ebda0SMatthew Dillon ng_l2cap_l2ca_discon_ind(ch); 629*b06ebda0SMatthew Dillon ng_l2cap_free_chan(ch); 630*b06ebda0SMatthew Dillon } 631*b06ebda0SMatthew Dillon } else { 632*b06ebda0SMatthew Dillon /* Send L2CA_ConfigInd event to the upper layer protocol */ 633*b06ebda0SMatthew Dillon ch->ident = ident; 634*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_cfg_ind(ch); 635*b06ebda0SMatthew Dillon if (error != 0) 636*b06ebda0SMatthew Dillon ng_l2cap_free_chan(ch); 637*b06ebda0SMatthew Dillon } 638*b06ebda0SMatthew Dillon 639*b06ebda0SMatthew Dillon return (error); 640*b06ebda0SMatthew Dillon 641*b06ebda0SMatthew Dillon reject: 642*b06ebda0SMatthew Dillon /* Send reject. Do not really care about the result */ 643*b06ebda0SMatthew Dillon NG_FREE_M(m); 644*b06ebda0SMatthew Dillon 645*b06ebda0SMatthew Dillon send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid); 646*b06ebda0SMatthew Dillon 647*b06ebda0SMatthew Dillon return (0); 648*b06ebda0SMatthew Dillon } /* ng_l2cap_process_cfg_req */ 649*b06ebda0SMatthew Dillon 650*b06ebda0SMatthew Dillon /* 651*b06ebda0SMatthew Dillon * Process L2CAP_ConfigRsp command 652*b06ebda0SMatthew Dillon */ 653*b06ebda0SMatthew Dillon 654*b06ebda0SMatthew Dillon static int 655*b06ebda0SMatthew Dillon ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident) 656*b06ebda0SMatthew Dillon { 657*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 658*b06ebda0SMatthew Dillon struct mbuf *m = con->rx_pkt; 659*b06ebda0SMatthew Dillon ng_l2cap_cfg_rsp_cp *cp = NULL; 660*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 661*b06ebda0SMatthew Dillon u_int16_t scid, cflag, result; 662*b06ebda0SMatthew Dillon ng_l2cap_cfg_opt_t hdr; 663*b06ebda0SMatthew Dillon ng_l2cap_cfg_opt_val_t val; 664*b06ebda0SMatthew Dillon int off, error = 0; 665*b06ebda0SMatthew Dillon 666*b06ebda0SMatthew Dillon /* Get command parameters */ 667*b06ebda0SMatthew Dillon con->rx_pkt = NULL; 668*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 669*b06ebda0SMatthew Dillon if (m == NULL) 670*b06ebda0SMatthew Dillon return (ENOBUFS); 671*b06ebda0SMatthew Dillon 672*b06ebda0SMatthew Dillon cp = mtod(m, ng_l2cap_cfg_rsp_cp *); 673*b06ebda0SMatthew Dillon scid = le16toh(cp->scid); 674*b06ebda0SMatthew Dillon cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags)); 675*b06ebda0SMatthew Dillon result = le16toh(cp->result); 676*b06ebda0SMatthew Dillon m_adj(m, sizeof(*cp)); 677*b06ebda0SMatthew Dillon 678*b06ebda0SMatthew Dillon /* Check if we have this command */ 679*b06ebda0SMatthew Dillon cmd = ng_l2cap_cmd_by_ident(con, ident); 680*b06ebda0SMatthew Dillon if (cmd == NULL) { 681*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 682*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n", 683*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ident, 684*b06ebda0SMatthew Dillon con->con_handle); 685*b06ebda0SMatthew Dillon NG_FREE_M(m); 686*b06ebda0SMatthew Dillon 687*b06ebda0SMatthew Dillon return (ENOENT); 688*b06ebda0SMatthew Dillon } 689*b06ebda0SMatthew Dillon 690*b06ebda0SMatthew Dillon /* Verify CIDs and send reject if does not match */ 691*b06ebda0SMatthew Dillon if (cmd->ch->scid != scid) { 692*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 693*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigRsp. " \ 694*b06ebda0SMatthew Dillon "Channel ID does not match, scid=%d(%d)\n", 695*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 696*b06ebda0SMatthew Dillon scid); 697*b06ebda0SMatthew Dillon goto reject; 698*b06ebda0SMatthew Dillon } 699*b06ebda0SMatthew Dillon 700*b06ebda0SMatthew Dillon /* Verify channel state and reject if invalid */ 701*b06ebda0SMatthew Dillon if (cmd->ch->state != NG_L2CAP_CONFIG) { 702*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 703*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigRsp. " \ 704*b06ebda0SMatthew Dillon "Invalid channel state, scid=%d, state=%d\n", 705*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 706*b06ebda0SMatthew Dillon cmd->ch->state); 707*b06ebda0SMatthew Dillon goto reject; 708*b06ebda0SMatthew Dillon } 709*b06ebda0SMatthew Dillon 710*b06ebda0SMatthew Dillon /* 711*b06ebda0SMatthew Dillon * Looks like it is our response, so process it. First parse options, 712*b06ebda0SMatthew Dillon * then verify C flag. If it is set then we shall expect more 713*b06ebda0SMatthew Dillon * configuration options from the peer and we will wait. Otherwise we 714*b06ebda0SMatthew Dillon * have received all options and we will send L2CA_ConfigRsp event to 715*b06ebda0SMatthew Dillon * the upper layer protocol. If command timeout already happened then 716*b06ebda0SMatthew Dillon * ignore response. 717*b06ebda0SMatthew Dillon */ 718*b06ebda0SMatthew Dillon 719*b06ebda0SMatthew Dillon if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 720*b06ebda0SMatthew Dillon NG_FREE_M(m); 721*b06ebda0SMatthew Dillon return (error); 722*b06ebda0SMatthew Dillon } 723*b06ebda0SMatthew Dillon 724*b06ebda0SMatthew Dillon for (off = 0; ; ) { 725*b06ebda0SMatthew Dillon error = get_next_l2cap_opt(m, &off, &hdr, &val); 726*b06ebda0SMatthew Dillon if (error == 0) /* We done with this packet */ 727*b06ebda0SMatthew Dillon break; 728*b06ebda0SMatthew Dillon else if (error > 0) { /* Got option */ 729*b06ebda0SMatthew Dillon switch (hdr.type) { 730*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_MTU: 731*b06ebda0SMatthew Dillon cmd->ch->imtu = val.mtu; 732*b06ebda0SMatthew Dillon break; 733*b06ebda0SMatthew Dillon 734*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_FLUSH_TIMO: 735*b06ebda0SMatthew Dillon cmd->ch->flush_timo = val.flush_timo; 736*b06ebda0SMatthew Dillon break; 737*b06ebda0SMatthew Dillon 738*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_QOS: 739*b06ebda0SMatthew Dillon bcopy(&val.flow, &cmd->ch->oflow, 740*b06ebda0SMatthew Dillon sizeof(cmd->ch->oflow)); 741*b06ebda0SMatthew Dillon break; 742*b06ebda0SMatthew Dillon 743*b06ebda0SMatthew Dillon default: /* Ignore unknown hint option */ 744*b06ebda0SMatthew Dillon break; 745*b06ebda0SMatthew Dillon } 746*b06ebda0SMatthew Dillon } else { 747*b06ebda0SMatthew Dillon /* 748*b06ebda0SMatthew Dillon * XXX FIXME What to do here? 749*b06ebda0SMatthew Dillon * 750*b06ebda0SMatthew Dillon * This is really BAD :( options packet was broken, or 751*b06ebda0SMatthew Dillon * peer sent us option that we did not understand. Let 752*b06ebda0SMatthew Dillon * upper layer know and do not wait for more options. 753*b06ebda0SMatthew Dillon */ 754*b06ebda0SMatthew Dillon 755*b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 756*b06ebda0SMatthew Dillon "%s: %s - failed to parse configuration options, error=%d\n", 757*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), error); 758*b06ebda0SMatthew Dillon 759*b06ebda0SMatthew Dillon result = NG_L2CAP_UNKNOWN; 760*b06ebda0SMatthew Dillon cflag = 0; 761*b06ebda0SMatthew Dillon 762*b06ebda0SMatthew Dillon break; 763*b06ebda0SMatthew Dillon } 764*b06ebda0SMatthew Dillon } 765*b06ebda0SMatthew Dillon 766*b06ebda0SMatthew Dillon NG_FREE_M(m); 767*b06ebda0SMatthew Dillon 768*b06ebda0SMatthew Dillon if (cflag) /* Restart timer and wait for more options */ 769*b06ebda0SMatthew Dillon ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout()); 770*b06ebda0SMatthew Dillon else { 771*b06ebda0SMatthew Dillon ng_l2cap_unlink_cmd(cmd); 772*b06ebda0SMatthew Dillon 773*b06ebda0SMatthew Dillon /* Send L2CA_Config response to the upper layer protocol */ 774*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result); 775*b06ebda0SMatthew Dillon if (error != 0) { 776*b06ebda0SMatthew Dillon /* 777*b06ebda0SMatthew Dillon * XXX FIXME what to do here? we were not able to send 778*b06ebda0SMatthew Dillon * response to the upper layer protocol, so for now 779*b06ebda0SMatthew Dillon * just close the channel. Send L2CAP_Disconnect to 780*b06ebda0SMatthew Dillon * remote peer? 781*b06ebda0SMatthew Dillon */ 782*b06ebda0SMatthew Dillon 783*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 784*b06ebda0SMatthew Dillon "%s: %s - failed to send L2CA_Config response, error=%d\n", 785*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), error); 786*b06ebda0SMatthew Dillon 787*b06ebda0SMatthew Dillon ng_l2cap_free_chan(cmd->ch); 788*b06ebda0SMatthew Dillon } 789*b06ebda0SMatthew Dillon 790*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 791*b06ebda0SMatthew Dillon } 792*b06ebda0SMatthew Dillon 793*b06ebda0SMatthew Dillon return (error); 794*b06ebda0SMatthew Dillon 795*b06ebda0SMatthew Dillon reject: 796*b06ebda0SMatthew Dillon /* Send reject. Do not really care about the result */ 797*b06ebda0SMatthew Dillon NG_FREE_M(m); 798*b06ebda0SMatthew Dillon 799*b06ebda0SMatthew Dillon send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0); 800*b06ebda0SMatthew Dillon 801*b06ebda0SMatthew Dillon return (0); 802*b06ebda0SMatthew Dillon } /* ng_l2cap_process_cfg_rsp */ 803*b06ebda0SMatthew Dillon 804*b06ebda0SMatthew Dillon /* 805*b06ebda0SMatthew Dillon * Process L2CAP_DisconnectReq command 806*b06ebda0SMatthew Dillon */ 807*b06ebda0SMatthew Dillon 808*b06ebda0SMatthew Dillon static int 809*b06ebda0SMatthew Dillon ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident) 810*b06ebda0SMatthew Dillon { 811*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 812*b06ebda0SMatthew Dillon ng_l2cap_discon_req_cp *cp = NULL; 813*b06ebda0SMatthew Dillon ng_l2cap_chan_p ch = NULL; 814*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 815*b06ebda0SMatthew Dillon u_int16_t scid, dcid; 816*b06ebda0SMatthew Dillon 817*b06ebda0SMatthew Dillon /* Get command parameters */ 818*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 819*b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) 820*b06ebda0SMatthew Dillon return (ENOBUFS); 821*b06ebda0SMatthew Dillon 822*b06ebda0SMatthew Dillon cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *); 823*b06ebda0SMatthew Dillon dcid = le16toh(cp->dcid); 824*b06ebda0SMatthew Dillon scid = le16toh(cp->scid); 825*b06ebda0SMatthew Dillon 826*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 827*b06ebda0SMatthew Dillon 828*b06ebda0SMatthew Dillon /* Check if we have this channel and it is in valid state */ 829*b06ebda0SMatthew Dillon ch = ng_l2cap_chan_by_scid(l2cap, dcid); 830*b06ebda0SMatthew Dillon if (ch == NULL) { 831*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 832*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectReq message. " \ 833*b06ebda0SMatthew Dillon "Channel does not exist, cid=%d\n", 834*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), dcid); 835*b06ebda0SMatthew Dillon goto reject; 836*b06ebda0SMatthew Dillon } 837*b06ebda0SMatthew Dillon 838*b06ebda0SMatthew Dillon /* XXX Verify channel state and reject if invalid -- is that true? */ 839*b06ebda0SMatthew Dillon if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG && 840*b06ebda0SMatthew Dillon ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 841*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 842*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectReq. " \ 843*b06ebda0SMatthew Dillon "Invalid channel state, cid=%d, state=%d\n", 844*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state); 845*b06ebda0SMatthew Dillon goto reject; 846*b06ebda0SMatthew Dillon } 847*b06ebda0SMatthew Dillon 848*b06ebda0SMatthew Dillon /* Match destination channel ID */ 849*b06ebda0SMatthew Dillon if (ch->dcid != scid || ch->scid != dcid) { 850*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 851*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectReq. " \ 852*b06ebda0SMatthew Dillon "Channel IDs does not match, channel: scid=%d, dcid=%d, " \ 853*b06ebda0SMatthew Dillon "request: scid=%d, dcid=%d\n", 854*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid, 855*b06ebda0SMatthew Dillon scid, dcid); 856*b06ebda0SMatthew Dillon goto reject; 857*b06ebda0SMatthew Dillon } 858*b06ebda0SMatthew Dillon 859*b06ebda0SMatthew Dillon /* 860*b06ebda0SMatthew Dillon * Looks good, so notify upper layer protocol that channel is about 861*b06ebda0SMatthew Dillon * to be disconnected and send L2CA_DisconnectInd message. Then respond 862*b06ebda0SMatthew Dillon * with L2CAP_DisconnectRsp. 863*b06ebda0SMatthew Dillon */ 864*b06ebda0SMatthew Dillon 865*b06ebda0SMatthew Dillon if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 866*b06ebda0SMatthew Dillon ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */ 867*b06ebda0SMatthew Dillon ng_l2cap_free_chan(ch); 868*b06ebda0SMatthew Dillon } 869*b06ebda0SMatthew Dillon 870*b06ebda0SMatthew Dillon /* Send L2CAP_DisconnectRsp */ 871*b06ebda0SMatthew Dillon cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0); 872*b06ebda0SMatthew Dillon if (cmd == NULL) 873*b06ebda0SMatthew Dillon return (ENOMEM); 874*b06ebda0SMatthew Dillon 875*b06ebda0SMatthew Dillon _ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid); 876*b06ebda0SMatthew Dillon if (cmd->aux == NULL) { 877*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 878*b06ebda0SMatthew Dillon 879*b06ebda0SMatthew Dillon return (ENOBUFS); 880*b06ebda0SMatthew Dillon } 881*b06ebda0SMatthew Dillon 882*b06ebda0SMatthew Dillon /* Link command to the queue */ 883*b06ebda0SMatthew Dillon ng_l2cap_link_cmd(con, cmd); 884*b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(con); 885*b06ebda0SMatthew Dillon 886*b06ebda0SMatthew Dillon return (0); 887*b06ebda0SMatthew Dillon 888*b06ebda0SMatthew Dillon reject: 889*b06ebda0SMatthew Dillon /* Send reject. Do not really care about the result */ 890*b06ebda0SMatthew Dillon send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid); 891*b06ebda0SMatthew Dillon 892*b06ebda0SMatthew Dillon return (0); 893*b06ebda0SMatthew Dillon } /* ng_l2cap_process_discon_req */ 894*b06ebda0SMatthew Dillon 895*b06ebda0SMatthew Dillon /* 896*b06ebda0SMatthew Dillon * Process L2CAP_DisconnectRsp command 897*b06ebda0SMatthew Dillon */ 898*b06ebda0SMatthew Dillon 899*b06ebda0SMatthew Dillon static int 900*b06ebda0SMatthew Dillon ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident) 901*b06ebda0SMatthew Dillon { 902*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 903*b06ebda0SMatthew Dillon ng_l2cap_discon_rsp_cp *cp = NULL; 904*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 905*b06ebda0SMatthew Dillon u_int16_t scid, dcid; 906*b06ebda0SMatthew Dillon int error = 0; 907*b06ebda0SMatthew Dillon 908*b06ebda0SMatthew Dillon /* Get command parameters */ 909*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 910*b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) 911*b06ebda0SMatthew Dillon return (ENOBUFS); 912*b06ebda0SMatthew Dillon 913*b06ebda0SMatthew Dillon cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *); 914*b06ebda0SMatthew Dillon dcid = le16toh(cp->dcid); 915*b06ebda0SMatthew Dillon scid = le16toh(cp->scid); 916*b06ebda0SMatthew Dillon 917*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 918*b06ebda0SMatthew Dillon 919*b06ebda0SMatthew Dillon /* Check if we have pending command descriptor */ 920*b06ebda0SMatthew Dillon cmd = ng_l2cap_cmd_by_ident(con, ident); 921*b06ebda0SMatthew Dillon if (cmd == NULL) { 922*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 923*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n", 924*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ident, 925*b06ebda0SMatthew Dillon con->con_handle); 926*b06ebda0SMatthew Dillon goto out; 927*b06ebda0SMatthew Dillon } 928*b06ebda0SMatthew Dillon 929*b06ebda0SMatthew Dillon /* Verify channel state, do nothing if invalid */ 930*b06ebda0SMatthew Dillon if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 931*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 932*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectRsp. " \ 933*b06ebda0SMatthew Dillon "Invalid channel state, cid=%d, state=%d\n", 934*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), scid, 935*b06ebda0SMatthew Dillon cmd->ch->state); 936*b06ebda0SMatthew Dillon goto out; 937*b06ebda0SMatthew Dillon } 938*b06ebda0SMatthew Dillon 939*b06ebda0SMatthew Dillon /* Verify CIDs and send reject if does not match */ 940*b06ebda0SMatthew Dillon if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) { 941*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 942*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectRsp. " \ 943*b06ebda0SMatthew Dillon "Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n", 944*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 945*b06ebda0SMatthew Dillon scid, cmd->ch->dcid, dcid); 946*b06ebda0SMatthew Dillon goto out; 947*b06ebda0SMatthew Dillon } 948*b06ebda0SMatthew Dillon 949*b06ebda0SMatthew Dillon /* 950*b06ebda0SMatthew Dillon * Looks like we have successfuly disconnected channel, so notify 951*b06ebda0SMatthew Dillon * upper layer. If command timeout already happened then ignore 952*b06ebda0SMatthew Dillon * response. 953*b06ebda0SMatthew Dillon */ 954*b06ebda0SMatthew Dillon 955*b06ebda0SMatthew Dillon if ((error = ng_l2cap_command_untimeout(cmd)) != 0) 956*b06ebda0SMatthew Dillon goto out; 957*b06ebda0SMatthew Dillon 958*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS); 959*b06ebda0SMatthew Dillon ng_l2cap_free_chan(cmd->ch); /* this will free commands too */ 960*b06ebda0SMatthew Dillon out: 961*b06ebda0SMatthew Dillon return (error); 962*b06ebda0SMatthew Dillon } /* ng_l2cap_process_discon_rsp */ 963*b06ebda0SMatthew Dillon 964*b06ebda0SMatthew Dillon /* 965*b06ebda0SMatthew Dillon * Process L2CAP_EchoReq command 966*b06ebda0SMatthew Dillon */ 967*b06ebda0SMatthew Dillon 968*b06ebda0SMatthew Dillon static int 969*b06ebda0SMatthew Dillon ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident) 970*b06ebda0SMatthew Dillon { 971*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 972*b06ebda0SMatthew Dillon ng_l2cap_cmd_hdr_t *hdr = NULL; 973*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 974*b06ebda0SMatthew Dillon 975*b06ebda0SMatthew Dillon con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr)); 976*b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) { 977*b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 978*b06ebda0SMatthew Dillon "%s: %s - ng_l2cap_prepend() failed, size=%zd\n", 979*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr)); 980*b06ebda0SMatthew Dillon 981*b06ebda0SMatthew Dillon return (ENOBUFS); 982*b06ebda0SMatthew Dillon } 983*b06ebda0SMatthew Dillon 984*b06ebda0SMatthew Dillon hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); 985*b06ebda0SMatthew Dillon hdr->code = NG_L2CAP_ECHO_RSP; 986*b06ebda0SMatthew Dillon hdr->ident = ident; 987*b06ebda0SMatthew Dillon hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr)); 988*b06ebda0SMatthew Dillon 989*b06ebda0SMatthew Dillon cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0); 990*b06ebda0SMatthew Dillon if (cmd == NULL) { 991*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 992*b06ebda0SMatthew Dillon 993*b06ebda0SMatthew Dillon return (ENOBUFS); 994*b06ebda0SMatthew Dillon } 995*b06ebda0SMatthew Dillon 996*b06ebda0SMatthew Dillon /* Attach data and link command to the queue */ 997*b06ebda0SMatthew Dillon cmd->aux = con->rx_pkt; 998*b06ebda0SMatthew Dillon con->rx_pkt = NULL; 999*b06ebda0SMatthew Dillon ng_l2cap_link_cmd(con, cmd); 1000*b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(con); 1001*b06ebda0SMatthew Dillon 1002*b06ebda0SMatthew Dillon return (0); 1003*b06ebda0SMatthew Dillon } /* ng_l2cap_process_echo_req */ 1004*b06ebda0SMatthew Dillon 1005*b06ebda0SMatthew Dillon /* 1006*b06ebda0SMatthew Dillon * Process L2CAP_EchoRsp command 1007*b06ebda0SMatthew Dillon */ 1008*b06ebda0SMatthew Dillon 1009*b06ebda0SMatthew Dillon static int 1010*b06ebda0SMatthew Dillon ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident) 1011*b06ebda0SMatthew Dillon { 1012*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 1013*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 1014*b06ebda0SMatthew Dillon int error = 0; 1015*b06ebda0SMatthew Dillon 1016*b06ebda0SMatthew Dillon /* Check if we have this command */ 1017*b06ebda0SMatthew Dillon cmd = ng_l2cap_cmd_by_ident(con, ident); 1018*b06ebda0SMatthew Dillon if (cmd != NULL) { 1019*b06ebda0SMatthew Dillon /* If command timeout already happened then ignore response */ 1020*b06ebda0SMatthew Dillon if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 1021*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 1022*b06ebda0SMatthew Dillon return (error); 1023*b06ebda0SMatthew Dillon } 1024*b06ebda0SMatthew Dillon 1025*b06ebda0SMatthew Dillon ng_l2cap_unlink_cmd(cmd); 1026*b06ebda0SMatthew Dillon 1027*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 1028*b06ebda0SMatthew Dillon NG_L2CAP_SUCCESS, con->rx_pkt); 1029*b06ebda0SMatthew Dillon 1030*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 1031*b06ebda0SMatthew Dillon con->rx_pkt = NULL; 1032*b06ebda0SMatthew Dillon } else { 1033*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 1034*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_EchoRsp command. " \ 1035*b06ebda0SMatthew Dillon "Requested ident does not exist, ident=%d\n", 1036*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ident); 1037*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 1038*b06ebda0SMatthew Dillon } 1039*b06ebda0SMatthew Dillon 1040*b06ebda0SMatthew Dillon return (error); 1041*b06ebda0SMatthew Dillon } /* ng_l2cap_process_echo_rsp */ 1042*b06ebda0SMatthew Dillon 1043*b06ebda0SMatthew Dillon /* 1044*b06ebda0SMatthew Dillon * Process L2CAP_InfoReq command 1045*b06ebda0SMatthew Dillon */ 1046*b06ebda0SMatthew Dillon 1047*b06ebda0SMatthew Dillon static int 1048*b06ebda0SMatthew Dillon ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident) 1049*b06ebda0SMatthew Dillon { 1050*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 1051*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 1052*b06ebda0SMatthew Dillon u_int16_t type; 1053*b06ebda0SMatthew Dillon 1054*b06ebda0SMatthew Dillon /* Get command parameters */ 1055*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp)); 1056*b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) 1057*b06ebda0SMatthew Dillon return (ENOBUFS); 1058*b06ebda0SMatthew Dillon 1059*b06ebda0SMatthew Dillon type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type); 1060*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 1061*b06ebda0SMatthew Dillon 1062*b06ebda0SMatthew Dillon cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0); 1063*b06ebda0SMatthew Dillon if (cmd == NULL) 1064*b06ebda0SMatthew Dillon return (ENOMEM); 1065*b06ebda0SMatthew Dillon 1066*b06ebda0SMatthew Dillon switch (type) { 1067*b06ebda0SMatthew Dillon case NG_L2CAP_CONNLESS_MTU: 1068*b06ebda0SMatthew Dillon _ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU, 1069*b06ebda0SMatthew Dillon NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT); 1070*b06ebda0SMatthew Dillon break; 1071*b06ebda0SMatthew Dillon 1072*b06ebda0SMatthew Dillon default: 1073*b06ebda0SMatthew Dillon _ng_l2cap_info_rsp(cmd->aux, ident, type, 1074*b06ebda0SMatthew Dillon NG_L2CAP_NOT_SUPPORTED, 0); 1075*b06ebda0SMatthew Dillon break; 1076*b06ebda0SMatthew Dillon } 1077*b06ebda0SMatthew Dillon 1078*b06ebda0SMatthew Dillon if (cmd->aux == NULL) { 1079*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 1080*b06ebda0SMatthew Dillon 1081*b06ebda0SMatthew Dillon return (ENOBUFS); 1082*b06ebda0SMatthew Dillon } 1083*b06ebda0SMatthew Dillon 1084*b06ebda0SMatthew Dillon /* Link command to the queue */ 1085*b06ebda0SMatthew Dillon ng_l2cap_link_cmd(con, cmd); 1086*b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(con); 1087*b06ebda0SMatthew Dillon 1088*b06ebda0SMatthew Dillon return (0); 1089*b06ebda0SMatthew Dillon } /* ng_l2cap_process_info_req */ 1090*b06ebda0SMatthew Dillon 1091*b06ebda0SMatthew Dillon /* 1092*b06ebda0SMatthew Dillon * Process L2CAP_InfoRsp command 1093*b06ebda0SMatthew Dillon */ 1094*b06ebda0SMatthew Dillon 1095*b06ebda0SMatthew Dillon static int 1096*b06ebda0SMatthew Dillon ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident) 1097*b06ebda0SMatthew Dillon { 1098*b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 1099*b06ebda0SMatthew Dillon ng_l2cap_info_rsp_cp *cp = NULL; 1100*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 1101*b06ebda0SMatthew Dillon int error = 0; 1102*b06ebda0SMatthew Dillon 1103*b06ebda0SMatthew Dillon /* Get command parameters */ 1104*b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 1105*b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) 1106*b06ebda0SMatthew Dillon return (ENOBUFS); 1107*b06ebda0SMatthew Dillon 1108*b06ebda0SMatthew Dillon cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *); 1109*b06ebda0SMatthew Dillon cp->type = le16toh(cp->type); 1110*b06ebda0SMatthew Dillon cp->result = le16toh(cp->result); 1111*b06ebda0SMatthew Dillon m_adj(con->rx_pkt, sizeof(*cp)); 1112*b06ebda0SMatthew Dillon 1113*b06ebda0SMatthew Dillon /* Check if we have pending command descriptor */ 1114*b06ebda0SMatthew Dillon cmd = ng_l2cap_cmd_by_ident(con, ident); 1115*b06ebda0SMatthew Dillon if (cmd == NULL) { 1116*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 1117*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_InfoRsp command. " \ 1118*b06ebda0SMatthew Dillon "Requested ident does not exist, ident=%d\n", 1119*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ident); 1120*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 1121*b06ebda0SMatthew Dillon 1122*b06ebda0SMatthew Dillon return (ENOENT); 1123*b06ebda0SMatthew Dillon } 1124*b06ebda0SMatthew Dillon 1125*b06ebda0SMatthew Dillon /* If command timeout already happened then ignore response */ 1126*b06ebda0SMatthew Dillon if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 1127*b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 1128*b06ebda0SMatthew Dillon return (error); 1129*b06ebda0SMatthew Dillon } 1130*b06ebda0SMatthew Dillon 1131*b06ebda0SMatthew Dillon ng_l2cap_unlink_cmd(cmd); 1132*b06ebda0SMatthew Dillon 1133*b06ebda0SMatthew Dillon if (cp->result == NG_L2CAP_SUCCESS) { 1134*b06ebda0SMatthew Dillon switch (cp->type) { 1135*b06ebda0SMatthew Dillon case NG_L2CAP_CONNLESS_MTU: 1136*b06ebda0SMatthew Dillon if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t)) 1137*b06ebda0SMatthew Dillon *mtod(con->rx_pkt, u_int16_t *) = 1138*b06ebda0SMatthew Dillon le16toh(*mtod(con->rx_pkt,u_int16_t *)); 1139*b06ebda0SMatthew Dillon else { 1140*b06ebda0SMatthew Dillon cp->result = NG_L2CAP_UNKNOWN; /* XXX */ 1141*b06ebda0SMatthew Dillon 1142*b06ebda0SMatthew Dillon NG_L2CAP_ERR( 1143*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP_InfoRsp command. " \ 1144*b06ebda0SMatthew Dillon "Bad connectionless MTU parameter, len=%d\n", 1145*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 1146*b06ebda0SMatthew Dillon con->rx_pkt->m_pkthdr.len); 1147*b06ebda0SMatthew Dillon } 1148*b06ebda0SMatthew Dillon break; 1149*b06ebda0SMatthew Dillon 1150*b06ebda0SMatthew Dillon default: 1151*b06ebda0SMatthew Dillon NG_L2CAP_WARN( 1152*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n", 1153*b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), cp->type); 1154*b06ebda0SMatthew Dillon break; 1155*b06ebda0SMatthew Dillon } 1156*b06ebda0SMatthew Dillon } 1157*b06ebda0SMatthew Dillon 1158*b06ebda0SMatthew Dillon error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 1159*b06ebda0SMatthew Dillon cp->result, con->rx_pkt); 1160*b06ebda0SMatthew Dillon 1161*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 1162*b06ebda0SMatthew Dillon con->rx_pkt = NULL; 1163*b06ebda0SMatthew Dillon 1164*b06ebda0SMatthew Dillon return (error); 1165*b06ebda0SMatthew Dillon } /* ng_l2cap_process_info_rsp */ 1166*b06ebda0SMatthew Dillon 1167*b06ebda0SMatthew Dillon /* 1168*b06ebda0SMatthew Dillon * Send L2CAP reject 1169*b06ebda0SMatthew Dillon */ 1170*b06ebda0SMatthew Dillon 1171*b06ebda0SMatthew Dillon static int 1172*b06ebda0SMatthew Dillon send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason, 1173*b06ebda0SMatthew Dillon u_int16_t mtu, u_int16_t scid, u_int16_t dcid) 1174*b06ebda0SMatthew Dillon { 1175*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 1176*b06ebda0SMatthew Dillon 1177*b06ebda0SMatthew Dillon cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0); 1178*b06ebda0SMatthew Dillon if (cmd == NULL) 1179*b06ebda0SMatthew Dillon return (ENOMEM); 1180*b06ebda0SMatthew Dillon 1181*b06ebda0SMatthew Dillon _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid); 1182*b06ebda0SMatthew Dillon if (cmd->aux == NULL) { 1183*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 1184*b06ebda0SMatthew Dillon 1185*b06ebda0SMatthew Dillon return (ENOBUFS); 1186*b06ebda0SMatthew Dillon } 1187*b06ebda0SMatthew Dillon 1188*b06ebda0SMatthew Dillon /* Link command to the queue */ 1189*b06ebda0SMatthew Dillon ng_l2cap_link_cmd(con, cmd); 1190*b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(con); 1191*b06ebda0SMatthew Dillon 1192*b06ebda0SMatthew Dillon return (0); 1193*b06ebda0SMatthew Dillon } /* send_l2cap_reject */ 1194*b06ebda0SMatthew Dillon 1195*b06ebda0SMatthew Dillon /* 1196*b06ebda0SMatthew Dillon * Send L2CAP connection reject 1197*b06ebda0SMatthew Dillon */ 1198*b06ebda0SMatthew Dillon 1199*b06ebda0SMatthew Dillon static int 1200*b06ebda0SMatthew Dillon send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid, 1201*b06ebda0SMatthew Dillon u_int16_t dcid, u_int16_t result) 1202*b06ebda0SMatthew Dillon { 1203*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 1204*b06ebda0SMatthew Dillon 1205*b06ebda0SMatthew Dillon cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0); 1206*b06ebda0SMatthew Dillon if (cmd == NULL) 1207*b06ebda0SMatthew Dillon return (ENOMEM); 1208*b06ebda0SMatthew Dillon 1209*b06ebda0SMatthew Dillon _ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0); 1210*b06ebda0SMatthew Dillon if (cmd->aux == NULL) { 1211*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 1212*b06ebda0SMatthew Dillon 1213*b06ebda0SMatthew Dillon return (ENOBUFS); 1214*b06ebda0SMatthew Dillon } 1215*b06ebda0SMatthew Dillon 1216*b06ebda0SMatthew Dillon /* Link command to the queue */ 1217*b06ebda0SMatthew Dillon ng_l2cap_link_cmd(con, cmd); 1218*b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(con); 1219*b06ebda0SMatthew Dillon 1220*b06ebda0SMatthew Dillon return (0); 1221*b06ebda0SMatthew Dillon } /* send_l2cap_con_rej */ 1222*b06ebda0SMatthew Dillon 1223*b06ebda0SMatthew Dillon /* 1224*b06ebda0SMatthew Dillon * Send L2CAP config response 1225*b06ebda0SMatthew Dillon */ 1226*b06ebda0SMatthew Dillon 1227*b06ebda0SMatthew Dillon static int 1228*b06ebda0SMatthew Dillon send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid, 1229*b06ebda0SMatthew Dillon u_int16_t result, struct mbuf *opt) 1230*b06ebda0SMatthew Dillon { 1231*b06ebda0SMatthew Dillon ng_l2cap_cmd_p cmd = NULL; 1232*b06ebda0SMatthew Dillon 1233*b06ebda0SMatthew Dillon cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0); 1234*b06ebda0SMatthew Dillon if (cmd == NULL) { 1235*b06ebda0SMatthew Dillon NG_FREE_M(opt); 1236*b06ebda0SMatthew Dillon 1237*b06ebda0SMatthew Dillon return (ENOMEM); 1238*b06ebda0SMatthew Dillon } 1239*b06ebda0SMatthew Dillon 1240*b06ebda0SMatthew Dillon _ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt); 1241*b06ebda0SMatthew Dillon if (cmd->aux == NULL) { 1242*b06ebda0SMatthew Dillon ng_l2cap_free_cmd(cmd); 1243*b06ebda0SMatthew Dillon 1244*b06ebda0SMatthew Dillon return (ENOBUFS); 1245*b06ebda0SMatthew Dillon } 1246*b06ebda0SMatthew Dillon 1247*b06ebda0SMatthew Dillon /* Link command to the queue */ 1248*b06ebda0SMatthew Dillon ng_l2cap_link_cmd(con, cmd); 1249*b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(con); 1250*b06ebda0SMatthew Dillon 1251*b06ebda0SMatthew Dillon return (0); 1252*b06ebda0SMatthew Dillon } /* send_l2cap_cfg_rsp */ 1253*b06ebda0SMatthew Dillon 1254*b06ebda0SMatthew Dillon /* 1255*b06ebda0SMatthew Dillon * Get next L2CAP configuration option 1256*b06ebda0SMatthew Dillon * 1257*b06ebda0SMatthew Dillon * Return codes: 1258*b06ebda0SMatthew Dillon * 0 no option 1259*b06ebda0SMatthew Dillon * 1 we have got option 1260*b06ebda0SMatthew Dillon * -1 header too short 1261*b06ebda0SMatthew Dillon * -2 bad option value or length 1262*b06ebda0SMatthew Dillon * -3 unknown option 1263*b06ebda0SMatthew Dillon */ 1264*b06ebda0SMatthew Dillon 1265*b06ebda0SMatthew Dillon static int 1266*b06ebda0SMatthew Dillon get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr, 1267*b06ebda0SMatthew Dillon ng_l2cap_cfg_opt_val_p val) 1268*b06ebda0SMatthew Dillon { 1269*b06ebda0SMatthew Dillon int hint, len = m->m_pkthdr.len - (*off); 1270*b06ebda0SMatthew Dillon 1271*b06ebda0SMatthew Dillon if (len == 0) 1272*b06ebda0SMatthew Dillon return (0); 1273*b06ebda0SMatthew Dillon if (len < 0 || len < sizeof(*hdr)) 1274*b06ebda0SMatthew Dillon return (-1); 1275*b06ebda0SMatthew Dillon 1276*b06ebda0SMatthew Dillon m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr); 1277*b06ebda0SMatthew Dillon *off += sizeof(*hdr); 1278*b06ebda0SMatthew Dillon len -= sizeof(*hdr); 1279*b06ebda0SMatthew Dillon 1280*b06ebda0SMatthew Dillon hint = NG_L2CAP_OPT_HINT(hdr->type); 1281*b06ebda0SMatthew Dillon hdr->type &= NG_L2CAP_OPT_HINT_MASK; 1282*b06ebda0SMatthew Dillon 1283*b06ebda0SMatthew Dillon switch (hdr->type) { 1284*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_MTU: 1285*b06ebda0SMatthew Dillon if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length) 1286*b06ebda0SMatthew Dillon return (-2); 1287*b06ebda0SMatthew Dillon 1288*b06ebda0SMatthew Dillon m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val); 1289*b06ebda0SMatthew Dillon val->mtu = le16toh(val->mtu); 1290*b06ebda0SMatthew Dillon *off += NG_L2CAP_OPT_MTU_SIZE; 1291*b06ebda0SMatthew Dillon break; 1292*b06ebda0SMatthew Dillon 1293*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_FLUSH_TIMO: 1294*b06ebda0SMatthew Dillon if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE || 1295*b06ebda0SMatthew Dillon len < hdr->length) 1296*b06ebda0SMatthew Dillon return (-2); 1297*b06ebda0SMatthew Dillon 1298*b06ebda0SMatthew Dillon m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val); 1299*b06ebda0SMatthew Dillon val->flush_timo = le16toh(val->flush_timo); 1300*b06ebda0SMatthew Dillon *off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE; 1301*b06ebda0SMatthew Dillon break; 1302*b06ebda0SMatthew Dillon 1303*b06ebda0SMatthew Dillon case NG_L2CAP_OPT_QOS: 1304*b06ebda0SMatthew Dillon if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length) 1305*b06ebda0SMatthew Dillon return (-2); 1306*b06ebda0SMatthew Dillon 1307*b06ebda0SMatthew Dillon m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val); 1308*b06ebda0SMatthew Dillon val->flow.token_rate = le32toh(val->flow.token_rate); 1309*b06ebda0SMatthew Dillon val->flow.token_bucket_size = 1310*b06ebda0SMatthew Dillon le32toh(val->flow.token_bucket_size); 1311*b06ebda0SMatthew Dillon val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth); 1312*b06ebda0SMatthew Dillon val->flow.latency = le32toh(val->flow.latency); 1313*b06ebda0SMatthew Dillon val->flow.delay_variation = le32toh(val->flow.delay_variation); 1314*b06ebda0SMatthew Dillon *off += NG_L2CAP_OPT_QOS_SIZE; 1315*b06ebda0SMatthew Dillon break; 1316*b06ebda0SMatthew Dillon 1317*b06ebda0SMatthew Dillon default: 1318*b06ebda0SMatthew Dillon if (hint) 1319*b06ebda0SMatthew Dillon *off += hdr->length; 1320*b06ebda0SMatthew Dillon else 1321*b06ebda0SMatthew Dillon return (-3); 1322*b06ebda0SMatthew Dillon break; 1323*b06ebda0SMatthew Dillon } 1324*b06ebda0SMatthew Dillon 1325*b06ebda0SMatthew Dillon return (1); 1326*b06ebda0SMatthew Dillon } /* get_next_l2cap_opt */ 1327*b06ebda0SMatthew Dillon 1328