1878ed226SJulian Elischer /* 2878ed226SJulian Elischer * ng_l2cap_evnt.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 6878ed226SJulian Elischer * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 7878ed226SJulian Elischer * All rights reserved. 8878ed226SJulian Elischer * 9878ed226SJulian Elischer * Redistribution and use in source and binary forms, with or without 10878ed226SJulian Elischer * modification, are permitted provided that the following conditions 11878ed226SJulian Elischer * are met: 12878ed226SJulian Elischer * 1. Redistributions of source code must retain the above copyright 13878ed226SJulian Elischer * notice, this list of conditions and the following disclaimer. 14878ed226SJulian Elischer * 2. Redistributions in binary form must reproduce the above copyright 15878ed226SJulian Elischer * notice, this list of conditions and the following disclaimer in the 16878ed226SJulian Elischer * documentation and/or other materials provided with the distribution. 17878ed226SJulian Elischer * 18878ed226SJulian Elischer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19878ed226SJulian Elischer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20878ed226SJulian Elischer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21878ed226SJulian Elischer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22878ed226SJulian Elischer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23878ed226SJulian Elischer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24878ed226SJulian Elischer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25878ed226SJulian Elischer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26878ed226SJulian Elischer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27878ed226SJulian Elischer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28878ed226SJulian Elischer * SUCH DAMAGE. 29878ed226SJulian Elischer * 300986ab12SMaksim Yevmenkin * $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $ 31878ed226SJulian Elischer * $FreeBSD$ 32878ed226SJulian Elischer */ 33878ed226SJulian Elischer 34878ed226SJulian Elischer #include <sys/param.h> 35878ed226SJulian Elischer #include <sys/systm.h> 36878ed226SJulian Elischer #include <sys/kernel.h> 37878ed226SJulian Elischer #include <sys/endian.h> 38878ed226SJulian Elischer #include <sys/malloc.h> 39878ed226SJulian Elischer #include <sys/mbuf.h> 40878ed226SJulian Elischer #include <sys/queue.h> 41878ed226SJulian Elischer #include <netgraph/ng_message.h> 42878ed226SJulian Elischer #include <netgraph/netgraph.h> 43b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h> 44b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h> 45b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_l2cap.h> 46b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h> 47b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h> 48b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h> 49b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h> 50b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h> 51b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h> 52878ed226SJulian Elischer 53878ed226SJulian Elischer /****************************************************************************** 54878ed226SJulian Elischer ****************************************************************************** 55878ed226SJulian Elischer ** L2CAP events processing module 56878ed226SJulian Elischer ****************************************************************************** 57878ed226SJulian Elischer ******************************************************************************/ 58878ed226SJulian Elischer 59878ed226SJulian Elischer static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p); 60878ed226SJulian Elischer static int ng_l2cap_process_cmd_rej (ng_l2cap_con_p, u_int8_t); 61878ed226SJulian Elischer static int ng_l2cap_process_con_req (ng_l2cap_con_p, u_int8_t); 62878ed226SJulian Elischer static int ng_l2cap_process_con_rsp (ng_l2cap_con_p, u_int8_t); 63878ed226SJulian Elischer static int ng_l2cap_process_cfg_req (ng_l2cap_con_p, u_int8_t); 64878ed226SJulian Elischer static int ng_l2cap_process_cfg_rsp (ng_l2cap_con_p, u_int8_t); 65878ed226SJulian Elischer static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t); 66878ed226SJulian Elischer static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t); 67878ed226SJulian Elischer static int ng_l2cap_process_echo_req (ng_l2cap_con_p, u_int8_t); 68878ed226SJulian Elischer static int ng_l2cap_process_echo_rsp (ng_l2cap_con_p, u_int8_t); 69878ed226SJulian Elischer static int ng_l2cap_process_info_req (ng_l2cap_con_p, u_int8_t); 70878ed226SJulian Elischer static int ng_l2cap_process_info_rsp (ng_l2cap_con_p, u_int8_t); 71878ed226SJulian Elischer static int send_l2cap_reject 72878ed226SJulian Elischer (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t); 73878ed226SJulian Elischer static int send_l2cap_con_rej 74878ed226SJulian Elischer (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t); 75878ed226SJulian Elischer static int send_l2cap_cfg_rsp 76878ed226SJulian Elischer (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *); 77878ed226SJulian Elischer static int get_next_l2cap_opt 78878ed226SJulian Elischer (struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p); 79878ed226SJulian Elischer 80878ed226SJulian Elischer /* 81878ed226SJulian Elischer * Receive L2CAP packet. First get L2CAP header and verify packet. Than 82878ed226SJulian Elischer * get destination channel and process packet. 83878ed226SJulian Elischer */ 84878ed226SJulian Elischer 85878ed226SJulian Elischer int 86878ed226SJulian Elischer ng_l2cap_receive(ng_l2cap_con_p con) 87878ed226SJulian Elischer { 88878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 89878ed226SJulian Elischer ng_l2cap_hdr_t *hdr = NULL; 90878ed226SJulian Elischer int error = 0; 91878ed226SJulian Elischer 92878ed226SJulian Elischer /* Check packet */ 93878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { 94878ed226SJulian Elischer NG_L2CAP_ERR( 95878ed226SJulian Elischer "%s: %s - invalid L2CAP packet. Packet too small, len=%d\n", 96878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), 97878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len); 98878ed226SJulian Elischer error = EMSGSIZE; 99878ed226SJulian Elischer goto drop; 100878ed226SJulian Elischer } 101878ed226SJulian Elischer 102878ed226SJulian Elischer /* Get L2CAP header */ 103878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); 104878ed226SJulian Elischer if (con->rx_pkt == NULL) 105878ed226SJulian Elischer return (ENOBUFS); 106878ed226SJulian Elischer 107878ed226SJulian Elischer hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *); 108878ed226SJulian Elischer hdr->length = le16toh(hdr->length); 109878ed226SJulian Elischer hdr->dcid = le16toh(hdr->dcid); 110878ed226SJulian Elischer 111878ed226SJulian Elischer /* Check payload size */ 112878ed226SJulian Elischer if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) { 113878ed226SJulian Elischer NG_L2CAP_ERR( 1144ae439a3SMaksim Yevmenkin "%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n", 115878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), hdr->length, 116878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len - sizeof(*hdr)); 117878ed226SJulian Elischer error = EMSGSIZE; 118878ed226SJulian Elischer goto drop; 119878ed226SJulian Elischer } 120878ed226SJulian Elischer 121878ed226SJulian Elischer /* Process packet */ 122878ed226SJulian Elischer switch (hdr->dcid) { 123878ed226SJulian Elischer case NG_L2CAP_SIGNAL_CID: /* L2CAP command */ 124878ed226SJulian Elischer m_adj(con->rx_pkt, sizeof(*hdr)); 125878ed226SJulian Elischer error = ng_l2cap_process_signal_cmd(con); 126878ed226SJulian Elischer break; 127878ed226SJulian Elischer 128878ed226SJulian Elischer case NG_L2CAP_CLT_CID: /* Connectionless packet */ 129878ed226SJulian Elischer error = ng_l2cap_l2ca_clt_receive(con); 130878ed226SJulian Elischer break; 131878ed226SJulian Elischer 132878ed226SJulian Elischer default: /* Data packet */ 133878ed226SJulian Elischer error = ng_l2cap_l2ca_receive(con); 134878ed226SJulian Elischer break; 135878ed226SJulian Elischer } 136878ed226SJulian Elischer 137878ed226SJulian Elischer return (error); 138878ed226SJulian Elischer drop: 139878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 140878ed226SJulian Elischer 141878ed226SJulian Elischer return (error); 142878ed226SJulian Elischer } /* ng_l2cap_receive */ 143878ed226SJulian Elischer 144878ed226SJulian Elischer /* 145878ed226SJulian Elischer * Process L2CAP signaling command. We already know that destination channel ID 146878ed226SJulian Elischer * is 0x1 that means we have received signaling command from peer's L2CAP layer. 147878ed226SJulian Elischer * So get command header, decode and process it. 148878ed226SJulian Elischer * 149878ed226SJulian Elischer * XXX do we need to check signaling MTU here? 150878ed226SJulian Elischer */ 151878ed226SJulian Elischer 152878ed226SJulian Elischer static int 153878ed226SJulian Elischer ng_l2cap_process_signal_cmd(ng_l2cap_con_p con) 154878ed226SJulian Elischer { 155878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 156878ed226SJulian Elischer ng_l2cap_cmd_hdr_t *hdr = NULL; 157878ed226SJulian Elischer struct mbuf *m = NULL; 158878ed226SJulian Elischer 159878ed226SJulian Elischer while (con->rx_pkt != NULL) { 160878ed226SJulian Elischer /* Verify packet length */ 161878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { 162878ed226SJulian Elischer NG_L2CAP_ERR( 163878ed226SJulian Elischer "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n", 164878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), 165878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len); 166878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 167878ed226SJulian Elischer 168878ed226SJulian Elischer return (EMSGSIZE); 169878ed226SJulian Elischer } 170878ed226SJulian Elischer 171878ed226SJulian Elischer /* Get signaling command */ 172878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); 173878ed226SJulian Elischer if (con->rx_pkt == NULL) 174878ed226SJulian Elischer return (ENOBUFS); 175878ed226SJulian Elischer 176878ed226SJulian Elischer hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); 177878ed226SJulian Elischer hdr->length = le16toh(hdr->length); 178878ed226SJulian Elischer m_adj(con->rx_pkt, sizeof(*hdr)); 179878ed226SJulian Elischer 180878ed226SJulian Elischer /* Verify command length */ 181878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len < hdr->length) { 182878ed226SJulian Elischer NG_L2CAP_ERR( 183878ed226SJulian Elischer "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \ 184878ed226SJulian Elischer "Invalid command length=%d, m_pkthdr.len=%d\n", 185878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), 186878ed226SJulian Elischer hdr->code, hdr->ident, hdr->length, 187878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len); 188878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 189878ed226SJulian Elischer 190878ed226SJulian Elischer return (EMSGSIZE); 191878ed226SJulian Elischer } 192878ed226SJulian Elischer 193878ed226SJulian Elischer /* Get the command, save the rest (if any) */ 194878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len > hdr->length) 195a163d034SWarner Losh m = m_split(con->rx_pkt, hdr->length, M_DONTWAIT); 196878ed226SJulian Elischer else 197878ed226SJulian Elischer m = NULL; 198878ed226SJulian Elischer 199878ed226SJulian Elischer /* Process command */ 200878ed226SJulian Elischer switch (hdr->code) { 201878ed226SJulian Elischer case NG_L2CAP_CMD_REJ: 202878ed226SJulian Elischer ng_l2cap_process_cmd_rej(con, hdr->ident); 203878ed226SJulian Elischer break; 204878ed226SJulian Elischer 205878ed226SJulian Elischer case NG_L2CAP_CON_REQ: 206878ed226SJulian Elischer ng_l2cap_process_con_req(con, hdr->ident); 207878ed226SJulian Elischer break; 208878ed226SJulian Elischer 209878ed226SJulian Elischer case NG_L2CAP_CON_RSP: 210878ed226SJulian Elischer ng_l2cap_process_con_rsp(con, hdr->ident); 211878ed226SJulian Elischer break; 212878ed226SJulian Elischer 213878ed226SJulian Elischer case NG_L2CAP_CFG_REQ: 214878ed226SJulian Elischer ng_l2cap_process_cfg_req(con, hdr->ident); 215878ed226SJulian Elischer break; 216878ed226SJulian Elischer 217878ed226SJulian Elischer case NG_L2CAP_CFG_RSP: 218878ed226SJulian Elischer ng_l2cap_process_cfg_rsp(con, hdr->ident); 219878ed226SJulian Elischer break; 220878ed226SJulian Elischer 221878ed226SJulian Elischer case NG_L2CAP_DISCON_REQ: 222878ed226SJulian Elischer ng_l2cap_process_discon_req(con, hdr->ident); 223878ed226SJulian Elischer break; 224878ed226SJulian Elischer 225878ed226SJulian Elischer case NG_L2CAP_DISCON_RSP: 226878ed226SJulian Elischer ng_l2cap_process_discon_rsp(con, hdr->ident); 227878ed226SJulian Elischer break; 228878ed226SJulian Elischer 229878ed226SJulian Elischer case NG_L2CAP_ECHO_REQ: 230878ed226SJulian Elischer ng_l2cap_process_echo_req(con, hdr->ident); 231878ed226SJulian Elischer break; 232878ed226SJulian Elischer 233878ed226SJulian Elischer case NG_L2CAP_ECHO_RSP: 234878ed226SJulian Elischer ng_l2cap_process_echo_rsp(con, hdr->ident); 235878ed226SJulian Elischer break; 236878ed226SJulian Elischer 237878ed226SJulian Elischer case NG_L2CAP_INFO_REQ: 238878ed226SJulian Elischer ng_l2cap_process_info_req(con, hdr->ident); 239878ed226SJulian Elischer break; 240878ed226SJulian Elischer 241878ed226SJulian Elischer case NG_L2CAP_INFO_RSP: 242878ed226SJulian Elischer ng_l2cap_process_info_rsp(con, hdr->ident); 243878ed226SJulian Elischer break; 244878ed226SJulian Elischer 245878ed226SJulian Elischer default: 246878ed226SJulian Elischer NG_L2CAP_ERR( 247878ed226SJulian Elischer "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n", 248878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), 249878ed226SJulian Elischer hdr->code, hdr->ident, hdr->length); 250878ed226SJulian Elischer 251878ed226SJulian Elischer /* 252878ed226SJulian Elischer * Send L2CAP_CommandRej. Do not really care 253878ed226SJulian Elischer * about the result 254878ed226SJulian Elischer */ 255878ed226SJulian Elischer 256878ed226SJulian Elischer send_l2cap_reject(con, hdr->ident, 257878ed226SJulian Elischer NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0); 258878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 259878ed226SJulian Elischer break; 260878ed226SJulian Elischer } 261878ed226SJulian Elischer 262878ed226SJulian Elischer con->rx_pkt = m; 263878ed226SJulian Elischer } 264878ed226SJulian Elischer 265878ed226SJulian Elischer return (0); 266878ed226SJulian Elischer } /* ng_l2cap_process_signal_cmd */ 267878ed226SJulian Elischer 268878ed226SJulian Elischer /* 269878ed226SJulian Elischer * Process L2CAP_CommandRej command 270878ed226SJulian Elischer */ 271878ed226SJulian Elischer 272878ed226SJulian Elischer static int 273878ed226SJulian Elischer ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident) 274878ed226SJulian Elischer { 275878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 276878ed226SJulian Elischer ng_l2cap_cmd_rej_cp *cp = NULL; 277878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 278878ed226SJulian Elischer 279878ed226SJulian Elischer /* Get command parameters */ 280878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 281878ed226SJulian Elischer if (con->rx_pkt == NULL) 282878ed226SJulian Elischer return (ENOBUFS); 283878ed226SJulian Elischer 284878ed226SJulian Elischer cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *); 285878ed226SJulian Elischer cp->reason = le16toh(cp->reason); 286878ed226SJulian Elischer 287878ed226SJulian Elischer /* Check if we have pending command descriptor */ 288878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident); 289878ed226SJulian Elischer if (cmd != NULL) { 2900986ab12SMaksim Yevmenkin /* If command timeout already happened then ignore reject */ 2910986ab12SMaksim Yevmenkin if (ng_l2cap_command_untimeout(cmd) != 0) { 2920986ab12SMaksim Yevmenkin NG_FREE_M(con->rx_pkt); 2930986ab12SMaksim Yevmenkin return (ETIMEDOUT); 2940986ab12SMaksim Yevmenkin } 295878ed226SJulian Elischer 296878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd); 297878ed226SJulian Elischer 298878ed226SJulian Elischer switch (cmd->code) { 299878ed226SJulian Elischer case NG_L2CAP_CON_REQ: 300878ed226SJulian Elischer ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0); 301878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch); 302878ed226SJulian Elischer break; 303878ed226SJulian Elischer 304878ed226SJulian Elischer case NG_L2CAP_CFG_REQ: 305878ed226SJulian Elischer ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason); 306878ed226SJulian Elischer break; 307878ed226SJulian Elischer 308878ed226SJulian Elischer case NG_L2CAP_DISCON_REQ: 309878ed226SJulian Elischer ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason); 310878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch); /* XXX free channel */ 311878ed226SJulian Elischer break; 312878ed226SJulian Elischer 313878ed226SJulian Elischer case NG_L2CAP_ECHO_REQ: 314878ed226SJulian Elischer ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 315878ed226SJulian Elischer cp->reason, NULL); 316878ed226SJulian Elischer break; 317878ed226SJulian Elischer 318878ed226SJulian Elischer case NG_L2CAP_INFO_REQ: 319878ed226SJulian Elischer ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 320878ed226SJulian Elischer cp->reason, NULL); 321878ed226SJulian Elischer break; 322878ed226SJulian Elischer 323878ed226SJulian Elischer default: 324878ed226SJulian Elischer NG_L2CAP_ALERT( 325878ed226SJulian Elischer "%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n", 326878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->code); 327878ed226SJulian Elischer break; 328878ed226SJulian Elischer } 329878ed226SJulian Elischer 330878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 331878ed226SJulian Elischer } else 332878ed226SJulian Elischer NG_L2CAP_ERR( 333878ed226SJulian Elischer "%s: %s - unexpected L2CAP_CommandRej command. " \ 334878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n", 335878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident); 336878ed226SJulian Elischer 337878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 338878ed226SJulian Elischer 339878ed226SJulian Elischer return (0); 340878ed226SJulian Elischer } /* ng_l2cap_process_cmd_rej */ 341878ed226SJulian Elischer 342878ed226SJulian Elischer /* 343878ed226SJulian Elischer * Process L2CAP_ConnectReq command 344878ed226SJulian Elischer */ 345878ed226SJulian Elischer 346878ed226SJulian Elischer static int 347878ed226SJulian Elischer ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident) 348878ed226SJulian Elischer { 349878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 350878ed226SJulian Elischer struct mbuf *m = con->rx_pkt; 351878ed226SJulian Elischer ng_l2cap_con_req_cp *cp = NULL; 352878ed226SJulian Elischer ng_l2cap_chan_p ch = NULL; 353878ed226SJulian Elischer int error = 0; 354878ed226SJulian Elischer u_int16_t dcid, psm; 355878ed226SJulian Elischer 356878ed226SJulian Elischer /* Get command parameters */ 357878ed226SJulian Elischer NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 358878ed226SJulian Elischer if (m == NULL) 359878ed226SJulian Elischer return (ENOBUFS); 360878ed226SJulian Elischer 361878ed226SJulian Elischer cp = mtod(m, ng_l2cap_con_req_cp *); 362878ed226SJulian Elischer psm = le16toh(cp->psm); 363878ed226SJulian Elischer dcid = le16toh(cp->scid); 364878ed226SJulian Elischer 365878ed226SJulian Elischer NG_FREE_M(m); 366878ed226SJulian Elischer con->rx_pkt = NULL; 367878ed226SJulian Elischer 368878ed226SJulian Elischer /* 369878ed226SJulian Elischer * Create new channel and send L2CA_ConnectInd notification 370878ed226SJulian Elischer * to the upper layer protocol. 371878ed226SJulian Elischer */ 372878ed226SJulian Elischer 373878ed226SJulian Elischer ch = ng_l2cap_new_chan(l2cap, con, psm); 374878ed226SJulian Elischer if (ch == NULL) 375878ed226SJulian Elischer return (send_l2cap_con_rej(con, ident, 0, dcid, 376878ed226SJulian Elischer NG_L2CAP_NO_RESOURCES)); 377878ed226SJulian Elischer 378878ed226SJulian Elischer /* Update channel IDs */ 379878ed226SJulian Elischer ch->dcid = dcid; 380878ed226SJulian Elischer 381878ed226SJulian Elischer /* Sent L2CA_ConnectInd notification to the upper layer */ 382878ed226SJulian Elischer ch->ident = ident; 383878ed226SJulian Elischer ch->state = NG_L2CAP_W4_L2CA_CON_RSP; 384878ed226SJulian Elischer 385878ed226SJulian Elischer error = ng_l2cap_l2ca_con_ind(ch); 386878ed226SJulian Elischer if (error != 0) { 387878ed226SJulian Elischer send_l2cap_con_rej(con, ident, ch->scid, dcid, 388878ed226SJulian Elischer (error == ENOMEM)? NG_L2CAP_NO_RESOURCES : 389878ed226SJulian Elischer NG_L2CAP_PSM_NOT_SUPPORTED); 390878ed226SJulian Elischer ng_l2cap_free_chan(ch); 391878ed226SJulian Elischer } 392878ed226SJulian Elischer 393878ed226SJulian Elischer return (error); 394878ed226SJulian Elischer } /* ng_l2cap_process_con_req */ 395878ed226SJulian Elischer 396878ed226SJulian Elischer /* 397878ed226SJulian Elischer * Process L2CAP_ConnectRsp command 398878ed226SJulian Elischer */ 399878ed226SJulian Elischer 400878ed226SJulian Elischer static int 401878ed226SJulian Elischer ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident) 402878ed226SJulian Elischer { 403878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 404878ed226SJulian Elischer struct mbuf *m = con->rx_pkt; 405878ed226SJulian Elischer ng_l2cap_con_rsp_cp *cp = NULL; 406878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 407878ed226SJulian Elischer u_int16_t scid, dcid, result, status; 408878ed226SJulian Elischer int error = 0; 409878ed226SJulian Elischer 410878ed226SJulian Elischer /* Get command parameters */ 411878ed226SJulian Elischer NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 412878ed226SJulian Elischer if (m == NULL) 413878ed226SJulian Elischer return (ENOBUFS); 414878ed226SJulian Elischer 415878ed226SJulian Elischer cp = mtod(m, ng_l2cap_con_rsp_cp *); 416878ed226SJulian Elischer dcid = le16toh(cp->dcid); 417878ed226SJulian Elischer scid = le16toh(cp->scid); 418878ed226SJulian Elischer result = le16toh(cp->result); 419878ed226SJulian Elischer status = le16toh(cp->status); 420878ed226SJulian Elischer 421878ed226SJulian Elischer NG_FREE_M(m); 422878ed226SJulian Elischer con->rx_pkt = NULL; 423878ed226SJulian Elischer 424878ed226SJulian Elischer /* Check if we have pending command descriptor */ 425878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident); 426878ed226SJulian Elischer if (cmd == NULL) { 427878ed226SJulian Elischer NG_L2CAP_ERR( 428878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n", 429878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident, 430878ed226SJulian Elischer con->con_handle); 431878ed226SJulian Elischer 432878ed226SJulian Elischer return (ENOENT); 433878ed226SJulian Elischer } 434878ed226SJulian Elischer 435878ed226SJulian Elischer /* Verify channel state, if invalid - do nothing */ 436878ed226SJulian Elischer if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) { 437878ed226SJulian Elischer NG_L2CAP_ERR( 438878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp. " \ 439878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n", 440878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), scid, 441878ed226SJulian Elischer cmd->ch->state); 442878ed226SJulian Elischer goto reject; 443878ed226SJulian Elischer } 444878ed226SJulian Elischer 445878ed226SJulian Elischer /* Verify CIDs and send reject if does not match */ 446878ed226SJulian Elischer if (cmd->ch->scid != scid) { 447878ed226SJulian Elischer NG_L2CAP_ERR( 448878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n", 449878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 450878ed226SJulian Elischer scid); 451878ed226SJulian Elischer goto reject; 452878ed226SJulian Elischer } 453878ed226SJulian Elischer 454878ed226SJulian Elischer /* 455878ed226SJulian Elischer * Looks good. We got confirmation from our peer. Now process 456878ed226SJulian Elischer * it. First disable RTX timer. Then check the result and send 4570986ab12SMaksim Yevmenkin * notification to the upper layer. If command timeout already 4580986ab12SMaksim Yevmenkin * happened then ignore response. 459878ed226SJulian Elischer */ 460878ed226SJulian Elischer 4610986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0) 4620986ab12SMaksim Yevmenkin return (error); 463878ed226SJulian Elischer 464878ed226SJulian Elischer if (result == NG_L2CAP_PENDING) { 465878ed226SJulian Elischer /* 466878ed226SJulian Elischer * Our peer wants more time to complete connection. We shall 467878ed226SJulian Elischer * start ERTX timer and wait. Keep command in the list. 468878ed226SJulian Elischer */ 469878ed226SJulian Elischer 470878ed226SJulian Elischer cmd->ch->dcid = dcid; 471878ed226SJulian Elischer ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout()); 472878ed226SJulian Elischer 473878ed226SJulian Elischer error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, 474878ed226SJulian Elischer result, status); 475878ed226SJulian Elischer if (error != 0) 476878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch); 477878ed226SJulian Elischer } else { 478878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd); 479878ed226SJulian Elischer 480878ed226SJulian Elischer if (result == NG_L2CAP_SUCCESS) { 481878ed226SJulian Elischer /* 482878ed226SJulian Elischer * Channel is open. Complete command and move to CONFIG 483878ed226SJulian Elischer * state. Since we have sent positive confirmation we 484878ed226SJulian Elischer * expect to receive L2CA_Config request from the upper 485878ed226SJulian Elischer * layer protocol. 486878ed226SJulian Elischer */ 487878ed226SJulian Elischer 488878ed226SJulian Elischer cmd->ch->dcid = dcid; 489878ed226SJulian Elischer cmd->ch->state = NG_L2CAP_CONFIG; 490878ed226SJulian Elischer } else 491878ed226SJulian Elischer /* There was an error, so close the channel */ 492878ed226SJulian Elischer NG_L2CAP_INFO( 493878ed226SJulian Elischer "%s: %s - failed to open L2CAP channel, result=%d, status=%d\n", 494878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), result, 495878ed226SJulian Elischer status); 496878ed226SJulian Elischer 497878ed226SJulian Elischer error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, 498878ed226SJulian Elischer result, status); 499878ed226SJulian Elischer 500878ed226SJulian Elischer /* XXX do we have to remove the channel on error? */ 501878ed226SJulian Elischer if (error != 0 || result != NG_L2CAP_SUCCESS) 502878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch); 503878ed226SJulian Elischer 504878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 505878ed226SJulian Elischer } 506878ed226SJulian Elischer 507878ed226SJulian Elischer return (error); 508878ed226SJulian Elischer 509878ed226SJulian Elischer reject: 510878ed226SJulian Elischer /* Send reject. Do not really care about the result */ 511878ed226SJulian Elischer send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid); 512878ed226SJulian Elischer 513878ed226SJulian Elischer return (0); 514878ed226SJulian Elischer } /* ng_l2cap_process_con_rsp */ 515878ed226SJulian Elischer 516878ed226SJulian Elischer /* 517878ed226SJulian Elischer * Process L2CAP_ConfigReq command 518878ed226SJulian Elischer */ 519878ed226SJulian Elischer 520878ed226SJulian Elischer static int 521878ed226SJulian Elischer ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident) 522878ed226SJulian Elischer { 523878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 524878ed226SJulian Elischer struct mbuf *m = con->rx_pkt; 525878ed226SJulian Elischer ng_l2cap_cfg_req_cp *cp = NULL; 526878ed226SJulian Elischer ng_l2cap_chan_p ch = NULL; 527878ed226SJulian Elischer u_int16_t dcid, respond, result; 528878ed226SJulian Elischer ng_l2cap_cfg_opt_t hdr; 529878ed226SJulian Elischer ng_l2cap_cfg_opt_val_t val; 530878ed226SJulian Elischer int off, error = 0; 531878ed226SJulian Elischer 532878ed226SJulian Elischer /* Get command parameters */ 533878ed226SJulian Elischer con->rx_pkt = NULL; 534878ed226SJulian Elischer NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 535878ed226SJulian Elischer if (m == NULL) 536878ed226SJulian Elischer return (ENOBUFS); 537878ed226SJulian Elischer 538878ed226SJulian Elischer cp = mtod(m, ng_l2cap_cfg_req_cp *); 539878ed226SJulian Elischer dcid = le16toh(cp->dcid); 540878ed226SJulian Elischer respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags)); 541878ed226SJulian Elischer m_adj(m, sizeof(*cp)); 542878ed226SJulian Elischer 543878ed226SJulian Elischer /* Check if we have this channel and it is in valid state */ 544878ed226SJulian Elischer ch = ng_l2cap_chan_by_scid(l2cap, dcid); 545878ed226SJulian Elischer if (ch == NULL) { 546878ed226SJulian Elischer NG_L2CAP_ERR( 547878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigReq command. " \ 548878ed226SJulian Elischer "Channel does not exist, cid=%d\n", 549878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), dcid); 550878ed226SJulian Elischer goto reject; 551878ed226SJulian Elischer } 552878ed226SJulian Elischer 553878ed226SJulian Elischer /* Verify channel state */ 554878ed226SJulian Elischer if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) { 555878ed226SJulian Elischer NG_L2CAP_ERR( 556878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigReq. " \ 557878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n", 558878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state); 559878ed226SJulian Elischer goto reject; 560878ed226SJulian Elischer } 561878ed226SJulian Elischer 562878ed226SJulian Elischer if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */ 563878ed226SJulian Elischer ch->cfg_state = 0; 564878ed226SJulian Elischer ch->state = NG_L2CAP_CONFIG; 565878ed226SJulian Elischer } 566878ed226SJulian Elischer 567878ed226SJulian Elischer for (result = 0, off = 0; ; ) { 568878ed226SJulian Elischer error = get_next_l2cap_opt(m, &off, &hdr, &val); 569878ed226SJulian Elischer if (error == 0) { /* We done with this packet */ 570878ed226SJulian Elischer NG_FREE_M(m); 571878ed226SJulian Elischer break; 572878ed226SJulian Elischer } else if (error > 0) { /* Got option */ 573878ed226SJulian Elischer switch (hdr.type) { 574878ed226SJulian Elischer case NG_L2CAP_OPT_MTU: 575878ed226SJulian Elischer ch->omtu = val.mtu; 576878ed226SJulian Elischer break; 577878ed226SJulian Elischer 578878ed226SJulian Elischer case NG_L2CAP_OPT_FLUSH_TIMO: 579878ed226SJulian Elischer ch->flush_timo = val.flush_timo; 580878ed226SJulian Elischer break; 581878ed226SJulian Elischer 582878ed226SJulian Elischer case NG_L2CAP_OPT_QOS: 583878ed226SJulian Elischer bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow)); 584878ed226SJulian Elischer break; 585878ed226SJulian Elischer 5860986ab12SMaksim Yevmenkin default: /* Ignore unknown hint option */ 587878ed226SJulian Elischer break; 588878ed226SJulian Elischer } 589878ed226SJulian Elischer } else { /* Oops, something is wrong */ 590878ed226SJulian Elischer respond = 1; 591878ed226SJulian Elischer 592878ed226SJulian Elischer if (error == -3) { 593878ed226SJulian Elischer 594878ed226SJulian Elischer /* 595878ed226SJulian Elischer * Adjust mbuf so we can get to the start 596878ed226SJulian Elischer * of the first option we did not like. 597878ed226SJulian Elischer */ 598878ed226SJulian Elischer 599878ed226SJulian Elischer m_adj(m, off - sizeof(hdr)); 600878ed226SJulian Elischer m->m_pkthdr.len = sizeof(hdr) + hdr.length; 601878ed226SJulian Elischer 602878ed226SJulian Elischer result = NG_L2CAP_UNKNOWN_OPTION; 603878ed226SJulian Elischer } else { 604878ed226SJulian Elischer /* XXX FIXME Send other reject codes? */ 605878ed226SJulian Elischer NG_FREE_M(m); 606878ed226SJulian Elischer result = NG_L2CAP_REJECT; 607878ed226SJulian Elischer } 608878ed226SJulian Elischer 609878ed226SJulian Elischer break; 610878ed226SJulian Elischer } 611878ed226SJulian Elischer } 612878ed226SJulian Elischer 613878ed226SJulian Elischer /* 614878ed226SJulian Elischer * Now check and see if we have to respond. If everything was OK then 615878ed226SJulian Elischer * respond contain "C flag" and (if set) we will respond with empty 616878ed226SJulian Elischer * packet and will wait for more options. 617878ed226SJulian Elischer * 618878ed226SJulian Elischer * Other case is that we did not like peer's options and will respond 619878ed226SJulian Elischer * with L2CAP_Config response command with Reject error code. 620878ed226SJulian Elischer * 621878ed226SJulian Elischer * When "respond == 0" than we have received all options and we will 622878ed226SJulian Elischer * sent L2CA_ConfigInd event to the upper layer protocol. 623878ed226SJulian Elischer */ 624878ed226SJulian Elischer 625878ed226SJulian Elischer if (respond) { 626878ed226SJulian Elischer error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m); 627878ed226SJulian Elischer if (error != 0) { 628878ed226SJulian Elischer ng_l2cap_l2ca_discon_ind(ch); 629878ed226SJulian Elischer ng_l2cap_free_chan(ch); 630878ed226SJulian Elischer } 631878ed226SJulian Elischer } else { 632878ed226SJulian Elischer /* Send L2CA_ConfigInd event to the upper layer protocol */ 633878ed226SJulian Elischer ch->ident = ident; 634878ed226SJulian Elischer error = ng_l2cap_l2ca_cfg_ind(ch); 635878ed226SJulian Elischer if (error != 0) 636878ed226SJulian Elischer ng_l2cap_free_chan(ch); 637878ed226SJulian Elischer } 638878ed226SJulian Elischer 639878ed226SJulian Elischer return (error); 640878ed226SJulian Elischer 641878ed226SJulian Elischer reject: 642878ed226SJulian Elischer /* Send reject. Do not really care about the result */ 643878ed226SJulian Elischer NG_FREE_M(m); 644878ed226SJulian Elischer 645878ed226SJulian Elischer send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid); 646878ed226SJulian Elischer 647878ed226SJulian Elischer return (0); 648878ed226SJulian Elischer } /* ng_l2cap_process_cfg_req */ 649878ed226SJulian Elischer 650878ed226SJulian Elischer /* 651878ed226SJulian Elischer * Process L2CAP_ConfigRsp command 652878ed226SJulian Elischer */ 653878ed226SJulian Elischer 654878ed226SJulian Elischer static int 655878ed226SJulian Elischer ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident) 656878ed226SJulian Elischer { 657878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 658878ed226SJulian Elischer struct mbuf *m = con->rx_pkt; 659878ed226SJulian Elischer ng_l2cap_cfg_rsp_cp *cp = NULL; 660878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 661878ed226SJulian Elischer u_int16_t scid, cflag, result; 662878ed226SJulian Elischer ng_l2cap_cfg_opt_t hdr; 663878ed226SJulian Elischer ng_l2cap_cfg_opt_val_t val; 664878ed226SJulian Elischer int off, error = 0; 665878ed226SJulian Elischer 666878ed226SJulian Elischer /* Get command parameters */ 667878ed226SJulian Elischer con->rx_pkt = NULL; 668878ed226SJulian Elischer NG_L2CAP_M_PULLUP(m, sizeof(*cp)); 669878ed226SJulian Elischer if (m == NULL) 670878ed226SJulian Elischer return (ENOBUFS); 671878ed226SJulian Elischer 672878ed226SJulian Elischer cp = mtod(m, ng_l2cap_cfg_rsp_cp *); 673878ed226SJulian Elischer scid = le16toh(cp->scid); 674878ed226SJulian Elischer cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags)); 675878ed226SJulian Elischer result = le16toh(cp->result); 676878ed226SJulian Elischer m_adj(m, sizeof(*cp)); 677878ed226SJulian Elischer 678878ed226SJulian Elischer /* Check if we have this command */ 679878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident); 680878ed226SJulian Elischer if (cmd == NULL) { 681878ed226SJulian Elischer NG_L2CAP_ERR( 682878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n", 683878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident, 684878ed226SJulian Elischer con->con_handle); 685878ed226SJulian Elischer NG_FREE_M(m); 686878ed226SJulian Elischer 687878ed226SJulian Elischer return (ENOENT); 688878ed226SJulian Elischer } 689878ed226SJulian Elischer 690878ed226SJulian Elischer /* Verify CIDs and send reject if does not match */ 691878ed226SJulian Elischer if (cmd->ch->scid != scid) { 692878ed226SJulian Elischer NG_L2CAP_ERR( 693878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp. " \ 694878ed226SJulian Elischer "Channel ID does not match, scid=%d(%d)\n", 695878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 696878ed226SJulian Elischer scid); 697878ed226SJulian Elischer goto reject; 698878ed226SJulian Elischer } 699878ed226SJulian Elischer 700878ed226SJulian Elischer /* Verify channel state and reject if invalid */ 701878ed226SJulian Elischer if (cmd->ch->state != NG_L2CAP_CONFIG) { 702878ed226SJulian Elischer NG_L2CAP_ERR( 703878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp. " \ 704878ed226SJulian Elischer "Invalid channel state, scid=%d, state=%d\n", 705878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 706878ed226SJulian Elischer cmd->ch->state); 707878ed226SJulian Elischer goto reject; 708878ed226SJulian Elischer } 709878ed226SJulian Elischer 710878ed226SJulian Elischer /* 711878ed226SJulian Elischer * Looks like it is our response, so process it. First parse options, 712878ed226SJulian Elischer * then verify C flag. If it is set then we shall expect more 713878ed226SJulian Elischer * configuration options from the peer and we will wait. Otherwise we 714878ed226SJulian Elischer * have received all options and we will send L2CA_ConfigRsp event to 7150986ab12SMaksim Yevmenkin * the upper layer protocol. If command timeout already happened then 7160986ab12SMaksim Yevmenkin * ignore response. 717878ed226SJulian Elischer */ 718878ed226SJulian Elischer 7190986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 7200986ab12SMaksim Yevmenkin NG_FREE_M(m); 7210986ab12SMaksim Yevmenkin return (error); 7220986ab12SMaksim Yevmenkin } 723878ed226SJulian Elischer 724878ed226SJulian Elischer for (off = 0; ; ) { 725878ed226SJulian Elischer error = get_next_l2cap_opt(m, &off, &hdr, &val); 726878ed226SJulian Elischer if (error == 0) /* We done with this packet */ 727878ed226SJulian Elischer break; 728878ed226SJulian Elischer else if (error > 0) { /* Got option */ 729878ed226SJulian Elischer switch (hdr.type) { 730878ed226SJulian Elischer case NG_L2CAP_OPT_MTU: 731878ed226SJulian Elischer cmd->ch->imtu = val.mtu; 732878ed226SJulian Elischer break; 733878ed226SJulian Elischer 734878ed226SJulian Elischer case NG_L2CAP_OPT_FLUSH_TIMO: 735878ed226SJulian Elischer cmd->ch->flush_timo = val.flush_timo; 736878ed226SJulian Elischer break; 737878ed226SJulian Elischer 738878ed226SJulian Elischer case NG_L2CAP_OPT_QOS: 739878ed226SJulian Elischer bcopy(&val.flow, &cmd->ch->oflow, 740878ed226SJulian Elischer sizeof(cmd->ch->oflow)); 741878ed226SJulian Elischer break; 742878ed226SJulian Elischer 7430986ab12SMaksim Yevmenkin default: /* Ignore unknown hint option */ 744878ed226SJulian Elischer break; 745878ed226SJulian Elischer } 746878ed226SJulian Elischer } else { 747878ed226SJulian Elischer /* 748878ed226SJulian Elischer * XXX FIXME What to do here? 749878ed226SJulian Elischer * 7500986ab12SMaksim Yevmenkin * This is really BAD :( options packet was broken, or 7510986ab12SMaksim Yevmenkin * peer sent us option that we did not understand. Let 7520986ab12SMaksim Yevmenkin * upper layer know and do not wait for more options. 753878ed226SJulian Elischer */ 754878ed226SJulian Elischer 755878ed226SJulian Elischer NG_L2CAP_ALERT( 756878ed226SJulian Elischer "%s: %s - failed to parse configuration options, error=%d\n", 757878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), error); 758878ed226SJulian Elischer 759878ed226SJulian Elischer result = NG_L2CAP_UNKNOWN; 760878ed226SJulian Elischer cflag = 0; 761878ed226SJulian Elischer 762878ed226SJulian Elischer break; 763878ed226SJulian Elischer } 764878ed226SJulian Elischer } 765878ed226SJulian Elischer 766878ed226SJulian Elischer NG_FREE_M(m); 767878ed226SJulian Elischer 768878ed226SJulian Elischer if (cflag) /* Restart timer and wait for more options */ 769878ed226SJulian Elischer ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout()); 770878ed226SJulian Elischer else { 771878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd); 772878ed226SJulian Elischer 773878ed226SJulian Elischer /* Send L2CA_Config response to the upper layer protocol */ 774878ed226SJulian Elischer error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result); 775878ed226SJulian Elischer if (error != 0) { 776878ed226SJulian Elischer /* 777878ed226SJulian Elischer * XXX FIXME what to do here? we were not able to send 778878ed226SJulian Elischer * response to the upper layer protocol, so for now 779878ed226SJulian Elischer * just close the channel. Send L2CAP_Disconnect to 780878ed226SJulian Elischer * remote peer? 781878ed226SJulian Elischer */ 782878ed226SJulian Elischer 783878ed226SJulian Elischer NG_L2CAP_ERR( 784878ed226SJulian Elischer "%s: %s - failed to send L2CA_Config response, error=%d\n", 785878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), error); 786878ed226SJulian Elischer 787878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch); 788878ed226SJulian Elischer } 789878ed226SJulian Elischer 790878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 791878ed226SJulian Elischer } 792878ed226SJulian Elischer 793878ed226SJulian Elischer return (error); 794878ed226SJulian Elischer 795878ed226SJulian Elischer reject: 796878ed226SJulian Elischer /* Send reject. Do not really care about the result */ 797878ed226SJulian Elischer NG_FREE_M(m); 798878ed226SJulian Elischer 799878ed226SJulian Elischer send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0); 800878ed226SJulian Elischer 801878ed226SJulian Elischer return (0); 802878ed226SJulian Elischer } /* ng_l2cap_process_cfg_rsp */ 803878ed226SJulian Elischer 804878ed226SJulian Elischer /* 805878ed226SJulian Elischer * Process L2CAP_DisconnectReq command 806878ed226SJulian Elischer */ 807878ed226SJulian Elischer 808878ed226SJulian Elischer static int 809878ed226SJulian Elischer ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident) 810878ed226SJulian Elischer { 811878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 812878ed226SJulian Elischer ng_l2cap_discon_req_cp *cp = NULL; 813878ed226SJulian Elischer ng_l2cap_chan_p ch = NULL; 814878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 815878ed226SJulian Elischer u_int16_t scid, dcid; 816878ed226SJulian Elischer 817878ed226SJulian Elischer /* Get command parameters */ 818878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 819878ed226SJulian Elischer if (con->rx_pkt == NULL) 820878ed226SJulian Elischer return (ENOBUFS); 821878ed226SJulian Elischer 822878ed226SJulian Elischer cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *); 823878ed226SJulian Elischer dcid = le16toh(cp->dcid); 824878ed226SJulian Elischer scid = le16toh(cp->scid); 825878ed226SJulian Elischer 826878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 827878ed226SJulian Elischer 828878ed226SJulian Elischer /* Check if we have this channel and it is in valid state */ 829878ed226SJulian Elischer ch = ng_l2cap_chan_by_scid(l2cap, dcid); 830878ed226SJulian Elischer if (ch == NULL) { 831878ed226SJulian Elischer NG_L2CAP_ERR( 832878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq message. " \ 833878ed226SJulian Elischer "Channel does not exist, cid=%d\n", 834878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), dcid); 835878ed226SJulian Elischer goto reject; 836878ed226SJulian Elischer } 837878ed226SJulian Elischer 838878ed226SJulian Elischer /* XXX Verify channel state and reject if invalid -- is that true? */ 839f2bb1caeSJulian Elischer if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG && 840f2bb1caeSJulian Elischer ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 841878ed226SJulian Elischer NG_L2CAP_ERR( 842878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq. " \ 843878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n", 844878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state); 845878ed226SJulian Elischer goto reject; 846878ed226SJulian Elischer } 847878ed226SJulian Elischer 848878ed226SJulian Elischer /* Match destination channel ID */ 849878ed226SJulian Elischer if (ch->dcid != scid || ch->scid != dcid) { 850878ed226SJulian Elischer NG_L2CAP_ERR( 851878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq. " \ 852878ed226SJulian Elischer "Channel IDs does not match, channel: scid=%d, dcid=%d, " \ 853878ed226SJulian Elischer "request: scid=%d, dcid=%d\n", 854878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid, 855878ed226SJulian Elischer scid, dcid); 856878ed226SJulian Elischer goto reject; 857878ed226SJulian Elischer } 858878ed226SJulian Elischer 859878ed226SJulian Elischer /* 860878ed226SJulian Elischer * Looks good, so notify upper layer protocol that channel is about 861878ed226SJulian Elischer * to be disconnected and send L2CA_DisconnectInd message. Then respond 862878ed226SJulian Elischer * with L2CAP_DisconnectRsp. 863878ed226SJulian Elischer */ 864878ed226SJulian Elischer 865f2bb1caeSJulian Elischer if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 866878ed226SJulian Elischer ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */ 867878ed226SJulian Elischer ng_l2cap_free_chan(ch); 868f2bb1caeSJulian Elischer } 869878ed226SJulian Elischer 870878ed226SJulian Elischer /* Send L2CAP_DisconnectRsp */ 871878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0); 872878ed226SJulian Elischer if (cmd == NULL) 873878ed226SJulian Elischer return (ENOMEM); 874878ed226SJulian Elischer 875878ed226SJulian Elischer _ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid); 876878ed226SJulian Elischer if (cmd->aux == NULL) { 877878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 878878ed226SJulian Elischer 879878ed226SJulian Elischer return (ENOBUFS); 880878ed226SJulian Elischer } 881878ed226SJulian Elischer 882878ed226SJulian Elischer /* Link command to the queue */ 883878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd); 884878ed226SJulian Elischer ng_l2cap_lp_deliver(con); 885878ed226SJulian Elischer 886878ed226SJulian Elischer return (0); 887878ed226SJulian Elischer 888878ed226SJulian Elischer reject: 889878ed226SJulian Elischer /* Send reject. Do not really care about the result */ 890878ed226SJulian Elischer send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid); 891878ed226SJulian Elischer 892878ed226SJulian Elischer return (0); 893878ed226SJulian Elischer } /* ng_l2cap_process_discon_req */ 894878ed226SJulian Elischer 895878ed226SJulian Elischer /* 896878ed226SJulian Elischer * Process L2CAP_DisconnectRsp command 897878ed226SJulian Elischer */ 898878ed226SJulian Elischer 899878ed226SJulian Elischer static int 900878ed226SJulian Elischer ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident) 901878ed226SJulian Elischer { 902878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 903878ed226SJulian Elischer ng_l2cap_discon_rsp_cp *cp = NULL; 904878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 905878ed226SJulian Elischer u_int16_t scid, dcid; 906878ed226SJulian Elischer int error = 0; 907878ed226SJulian Elischer 908878ed226SJulian Elischer /* Get command parameters */ 909878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 910878ed226SJulian Elischer if (con->rx_pkt == NULL) 911878ed226SJulian Elischer return (ENOBUFS); 912878ed226SJulian Elischer 913878ed226SJulian Elischer cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *); 914878ed226SJulian Elischer dcid = le16toh(cp->dcid); 915878ed226SJulian Elischer scid = le16toh(cp->scid); 916878ed226SJulian Elischer 917878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 918878ed226SJulian Elischer 919878ed226SJulian Elischer /* Check if we have pending command descriptor */ 920878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident); 921878ed226SJulian Elischer if (cmd == NULL) { 922878ed226SJulian Elischer NG_L2CAP_ERR( 923878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n", 924878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident, 925878ed226SJulian Elischer con->con_handle); 926878ed226SJulian Elischer goto out; 927878ed226SJulian Elischer } 928878ed226SJulian Elischer 929878ed226SJulian Elischer /* Verify channel state, do nothing if invalid */ 930878ed226SJulian Elischer if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { 931878ed226SJulian Elischer NG_L2CAP_ERR( 932878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp. " \ 933878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n", 934878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), scid, 935878ed226SJulian Elischer cmd->ch->state); 936878ed226SJulian Elischer goto out; 937878ed226SJulian Elischer } 938878ed226SJulian Elischer 939878ed226SJulian Elischer /* Verify CIDs and send reject if does not match */ 940878ed226SJulian Elischer if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) { 941878ed226SJulian Elischer NG_L2CAP_ERR( 942878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp. " \ 943878ed226SJulian Elischer "Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n", 944878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, 945878ed226SJulian Elischer scid, cmd->ch->dcid, dcid); 946878ed226SJulian Elischer goto out; 947878ed226SJulian Elischer } 948878ed226SJulian Elischer 949878ed226SJulian Elischer /* 9500986ab12SMaksim Yevmenkin * Looks like we have successfuly disconnected channel, so notify 9510986ab12SMaksim Yevmenkin * upper layer. If command timeout already happened then ignore 9520986ab12SMaksim Yevmenkin * response. 953878ed226SJulian Elischer */ 954878ed226SJulian Elischer 9550986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0) 9560986ab12SMaksim Yevmenkin goto out; 9570986ab12SMaksim Yevmenkin 958878ed226SJulian Elischer error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS); 959878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch); /* this will free commands too */ 960878ed226SJulian Elischer out: 961878ed226SJulian Elischer return (error); 962878ed226SJulian Elischer } /* ng_l2cap_process_discon_rsp */ 963878ed226SJulian Elischer 964878ed226SJulian Elischer /* 965878ed226SJulian Elischer * Process L2CAP_EchoReq command 966878ed226SJulian Elischer */ 967878ed226SJulian Elischer 968878ed226SJulian Elischer static int 969878ed226SJulian Elischer ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident) 970878ed226SJulian Elischer { 971878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 972878ed226SJulian Elischer ng_l2cap_cmd_hdr_t *hdr = NULL; 973878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 974878ed226SJulian Elischer 975878ed226SJulian Elischer con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr)); 976878ed226SJulian Elischer if (con->rx_pkt == NULL) { 977878ed226SJulian Elischer NG_L2CAP_ALERT( 9784ae439a3SMaksim Yevmenkin "%s: %s - ng_l2cap_prepend() failed, size=%zd\n", 979878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr)); 980878ed226SJulian Elischer 981878ed226SJulian Elischer return (ENOBUFS); 982878ed226SJulian Elischer } 983878ed226SJulian Elischer 984878ed226SJulian Elischer hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); 985878ed226SJulian Elischer hdr->code = NG_L2CAP_ECHO_RSP; 986878ed226SJulian Elischer hdr->ident = ident; 987878ed226SJulian Elischer hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr)); 988878ed226SJulian Elischer 989878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0); 990878ed226SJulian Elischer if (cmd == NULL) { 991878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 992878ed226SJulian Elischer 993878ed226SJulian Elischer return (ENOBUFS); 994878ed226SJulian Elischer } 995878ed226SJulian Elischer 996878ed226SJulian Elischer /* Attach data and link command to the queue */ 997878ed226SJulian Elischer cmd->aux = con->rx_pkt; 998878ed226SJulian Elischer con->rx_pkt = NULL; 999878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd); 1000878ed226SJulian Elischer ng_l2cap_lp_deliver(con); 1001878ed226SJulian Elischer 1002878ed226SJulian Elischer return (0); 1003878ed226SJulian Elischer } /* ng_l2cap_process_echo_req */ 1004878ed226SJulian Elischer 1005878ed226SJulian Elischer /* 1006878ed226SJulian Elischer * Process L2CAP_EchoRsp command 1007878ed226SJulian Elischer */ 1008878ed226SJulian Elischer 1009878ed226SJulian Elischer static int 1010878ed226SJulian Elischer ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident) 1011878ed226SJulian Elischer { 1012878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 1013878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 1014878ed226SJulian Elischer int error = 0; 1015878ed226SJulian Elischer 1016878ed226SJulian Elischer /* Check if we have this command */ 1017878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident); 1018878ed226SJulian Elischer if (cmd != NULL) { 10190986ab12SMaksim Yevmenkin /* If command timeout already happened then ignore response */ 10200986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 10210986ab12SMaksim Yevmenkin NG_FREE_M(con->rx_pkt); 10220986ab12SMaksim Yevmenkin return (error); 10230986ab12SMaksim Yevmenkin } 1024878ed226SJulian Elischer 1025878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd); 1026878ed226SJulian Elischer 1027878ed226SJulian Elischer error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 1028878ed226SJulian Elischer NG_L2CAP_SUCCESS, con->rx_pkt); 1029878ed226SJulian Elischer 1030878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 1031878ed226SJulian Elischer con->rx_pkt = NULL; 1032878ed226SJulian Elischer } else { 1033878ed226SJulian Elischer NG_L2CAP_ERR( 1034878ed226SJulian Elischer "%s: %s - unexpected L2CAP_EchoRsp command. " \ 1035878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n", 1036878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident); 1037878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 1038878ed226SJulian Elischer } 1039878ed226SJulian Elischer 1040878ed226SJulian Elischer return (error); 1041878ed226SJulian Elischer } /* ng_l2cap_process_echo_rsp */ 1042878ed226SJulian Elischer 1043878ed226SJulian Elischer /* 1044878ed226SJulian Elischer * Process L2CAP_InfoReq command 1045878ed226SJulian Elischer */ 1046878ed226SJulian Elischer 1047878ed226SJulian Elischer static int 1048878ed226SJulian Elischer ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident) 1049878ed226SJulian Elischer { 1050878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 1051878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 1052878ed226SJulian Elischer u_int16_t type; 1053878ed226SJulian Elischer 1054878ed226SJulian Elischer /* Get command parameters */ 1055878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp)); 1056878ed226SJulian Elischer if (con->rx_pkt == NULL) 1057878ed226SJulian Elischer return (ENOBUFS); 1058878ed226SJulian Elischer 1059878ed226SJulian Elischer type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type); 1060878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 1061878ed226SJulian Elischer 1062878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0); 1063878ed226SJulian Elischer if (cmd == NULL) 1064878ed226SJulian Elischer return (ENOMEM); 1065878ed226SJulian Elischer 1066878ed226SJulian Elischer switch (type) { 1067878ed226SJulian Elischer case NG_L2CAP_CONNLESS_MTU: 1068878ed226SJulian Elischer _ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU, 1069878ed226SJulian Elischer NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT); 1070878ed226SJulian Elischer break; 1071878ed226SJulian Elischer 1072878ed226SJulian Elischer default: 1073878ed226SJulian Elischer _ng_l2cap_info_rsp(cmd->aux, ident, type, 1074878ed226SJulian Elischer NG_L2CAP_NOT_SUPPORTED, 0); 1075878ed226SJulian Elischer break; 1076878ed226SJulian Elischer } 1077878ed226SJulian Elischer 1078878ed226SJulian Elischer if (cmd->aux == NULL) { 1079878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 1080878ed226SJulian Elischer 1081878ed226SJulian Elischer return (ENOBUFS); 1082878ed226SJulian Elischer } 1083878ed226SJulian Elischer 1084878ed226SJulian Elischer /* Link command to the queue */ 1085878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd); 1086878ed226SJulian Elischer ng_l2cap_lp_deliver(con); 1087878ed226SJulian Elischer 1088878ed226SJulian Elischer return (0); 1089878ed226SJulian Elischer } /* ng_l2cap_process_info_req */ 1090878ed226SJulian Elischer 1091878ed226SJulian Elischer /* 1092878ed226SJulian Elischer * Process L2CAP_InfoRsp command 1093878ed226SJulian Elischer */ 1094878ed226SJulian Elischer 1095878ed226SJulian Elischer static int 1096878ed226SJulian Elischer ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident) 1097878ed226SJulian Elischer { 1098878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap; 1099878ed226SJulian Elischer ng_l2cap_info_rsp_cp *cp = NULL; 1100878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 1101878ed226SJulian Elischer int error = 0; 1102878ed226SJulian Elischer 1103878ed226SJulian Elischer /* Get command parameters */ 1104878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); 1105878ed226SJulian Elischer if (con->rx_pkt == NULL) 1106878ed226SJulian Elischer return (ENOBUFS); 1107878ed226SJulian Elischer 1108878ed226SJulian Elischer cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *); 1109878ed226SJulian Elischer cp->type = le16toh(cp->type); 1110878ed226SJulian Elischer cp->result = le16toh(cp->result); 1111878ed226SJulian Elischer m_adj(con->rx_pkt, sizeof(*cp)); 1112878ed226SJulian Elischer 1113878ed226SJulian Elischer /* Check if we have pending command descriptor */ 1114878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident); 1115878ed226SJulian Elischer if (cmd == NULL) { 1116878ed226SJulian Elischer NG_L2CAP_ERR( 1117878ed226SJulian Elischer "%s: %s - unexpected L2CAP_InfoRsp command. " \ 1118878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n", 1119878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident); 1120878ed226SJulian Elischer NG_FREE_M(con->rx_pkt); 1121878ed226SJulian Elischer 1122878ed226SJulian Elischer return (ENOENT); 1123878ed226SJulian Elischer } 1124878ed226SJulian Elischer 11250986ab12SMaksim Yevmenkin /* If command timeout already happened then ignore response */ 11260986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { 11270986ab12SMaksim Yevmenkin NG_FREE_M(con->rx_pkt); 11280986ab12SMaksim Yevmenkin return (error); 11290986ab12SMaksim Yevmenkin } 1130878ed226SJulian Elischer 1131878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd); 1132878ed226SJulian Elischer 1133878ed226SJulian Elischer if (cp->result == NG_L2CAP_SUCCESS) { 1134878ed226SJulian Elischer switch (cp->type) { 1135878ed226SJulian Elischer case NG_L2CAP_CONNLESS_MTU: 1136878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t)) 1137878ed226SJulian Elischer *mtod(con->rx_pkt, u_int16_t *) = 1138878ed226SJulian Elischer le16toh(*mtod(con->rx_pkt,u_int16_t *)); 1139878ed226SJulian Elischer else { 1140878ed226SJulian Elischer cp->result = NG_L2CAP_UNKNOWN; /* XXX */ 1141878ed226SJulian Elischer 1142878ed226SJulian Elischer NG_L2CAP_ERR( 1143878ed226SJulian Elischer "%s: %s - invalid L2CAP_InfoRsp command. " \ 1144878ed226SJulian Elischer "Bad connectionless MTU parameter, len=%d\n", 1145878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), 1146878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len); 1147878ed226SJulian Elischer } 1148878ed226SJulian Elischer break; 1149878ed226SJulian Elischer 1150878ed226SJulian Elischer default: 1151878ed226SJulian Elischer NG_L2CAP_WARN( 1152878ed226SJulian Elischer "%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n", 1153878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cp->type); 1154878ed226SJulian Elischer break; 1155878ed226SJulian Elischer } 1156878ed226SJulian Elischer } 1157878ed226SJulian Elischer 1158878ed226SJulian Elischer error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 1159878ed226SJulian Elischer cp->result, con->rx_pkt); 1160878ed226SJulian Elischer 1161878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 1162878ed226SJulian Elischer con->rx_pkt = NULL; 1163878ed226SJulian Elischer 1164878ed226SJulian Elischer return (error); 1165878ed226SJulian Elischer } /* ng_l2cap_process_info_rsp */ 1166878ed226SJulian Elischer 1167878ed226SJulian Elischer /* 1168878ed226SJulian Elischer * Send L2CAP reject 1169878ed226SJulian Elischer */ 1170878ed226SJulian Elischer 1171878ed226SJulian Elischer static int 1172878ed226SJulian Elischer send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason, 1173878ed226SJulian Elischer u_int16_t mtu, u_int16_t scid, u_int16_t dcid) 1174878ed226SJulian Elischer { 1175878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 1176878ed226SJulian Elischer 1177878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0); 1178878ed226SJulian Elischer if (cmd == NULL) 1179878ed226SJulian Elischer return (ENOMEM); 1180878ed226SJulian Elischer 1181878ed226SJulian Elischer _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid); 1182878ed226SJulian Elischer if (cmd->aux == NULL) { 1183878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 1184878ed226SJulian Elischer 1185878ed226SJulian Elischer return (ENOBUFS); 1186878ed226SJulian Elischer } 1187878ed226SJulian Elischer 1188878ed226SJulian Elischer /* Link command to the queue */ 1189878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd); 1190878ed226SJulian Elischer ng_l2cap_lp_deliver(con); 1191878ed226SJulian Elischer 1192878ed226SJulian Elischer return (0); 1193878ed226SJulian Elischer } /* send_l2cap_reject */ 1194878ed226SJulian Elischer 1195878ed226SJulian Elischer /* 1196878ed226SJulian Elischer * Send L2CAP connection reject 1197878ed226SJulian Elischer */ 1198878ed226SJulian Elischer 1199878ed226SJulian Elischer static int 1200878ed226SJulian Elischer send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid, 1201878ed226SJulian Elischer u_int16_t dcid, u_int16_t result) 1202878ed226SJulian Elischer { 1203878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 1204878ed226SJulian Elischer 1205878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0); 1206878ed226SJulian Elischer if (cmd == NULL) 1207878ed226SJulian Elischer return (ENOMEM); 1208878ed226SJulian Elischer 1209878ed226SJulian Elischer _ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0); 1210878ed226SJulian Elischer if (cmd->aux == NULL) { 1211878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 1212878ed226SJulian Elischer 1213878ed226SJulian Elischer return (ENOBUFS); 1214878ed226SJulian Elischer } 1215878ed226SJulian Elischer 1216878ed226SJulian Elischer /* Link command to the queue */ 1217878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd); 1218878ed226SJulian Elischer ng_l2cap_lp_deliver(con); 1219878ed226SJulian Elischer 1220878ed226SJulian Elischer return (0); 1221878ed226SJulian Elischer } /* send_l2cap_con_rej */ 1222878ed226SJulian Elischer 1223878ed226SJulian Elischer /* 1224878ed226SJulian Elischer * Send L2CAP config response 1225878ed226SJulian Elischer */ 1226878ed226SJulian Elischer 1227878ed226SJulian Elischer static int 1228878ed226SJulian Elischer send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid, 1229878ed226SJulian Elischer u_int16_t result, struct mbuf *opt) 1230878ed226SJulian Elischer { 1231878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL; 1232878ed226SJulian Elischer 1233878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0); 1234878ed226SJulian Elischer if (cmd == NULL) { 1235878ed226SJulian Elischer NG_FREE_M(opt); 1236878ed226SJulian Elischer 1237878ed226SJulian Elischer return (ENOMEM); 1238878ed226SJulian Elischer } 1239878ed226SJulian Elischer 1240878ed226SJulian Elischer _ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt); 1241878ed226SJulian Elischer if (cmd->aux == NULL) { 1242878ed226SJulian Elischer ng_l2cap_free_cmd(cmd); 1243878ed226SJulian Elischer 1244878ed226SJulian Elischer return (ENOBUFS); 1245878ed226SJulian Elischer } 1246878ed226SJulian Elischer 1247878ed226SJulian Elischer /* Link command to the queue */ 1248878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd); 1249878ed226SJulian Elischer ng_l2cap_lp_deliver(con); 1250878ed226SJulian Elischer 1251878ed226SJulian Elischer return (0); 1252878ed226SJulian Elischer } /* send_l2cap_cfg_rsp */ 1253878ed226SJulian Elischer 1254878ed226SJulian Elischer /* 1255878ed226SJulian Elischer * Get next L2CAP configuration option 1256878ed226SJulian Elischer * 1257878ed226SJulian Elischer * Return codes: 1258878ed226SJulian Elischer * 0 no option 1259878ed226SJulian Elischer * 1 we have got option 1260878ed226SJulian Elischer * -1 header too short 1261878ed226SJulian Elischer * -2 bad option value or length 1262878ed226SJulian Elischer * -3 unknown option 1263878ed226SJulian Elischer */ 1264878ed226SJulian Elischer 1265878ed226SJulian Elischer static int 1266878ed226SJulian Elischer get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr, 1267878ed226SJulian Elischer ng_l2cap_cfg_opt_val_p val) 1268878ed226SJulian Elischer { 1269878ed226SJulian Elischer int hint, len = m->m_pkthdr.len - (*off); 1270878ed226SJulian Elischer 1271878ed226SJulian Elischer if (len == 0) 1272878ed226SJulian Elischer return (0); 1273878ed226SJulian Elischer if (len < 0 || len < sizeof(*hdr)) 1274878ed226SJulian Elischer return (-1); 1275878ed226SJulian Elischer 1276878ed226SJulian Elischer m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr); 1277878ed226SJulian Elischer *off += sizeof(*hdr); 1278878ed226SJulian Elischer len -= sizeof(*hdr); 1279878ed226SJulian Elischer 1280878ed226SJulian Elischer hint = NG_L2CAP_OPT_HINT(hdr->type); 1281878ed226SJulian Elischer hdr->type &= NG_L2CAP_OPT_HINT_MASK; 1282878ed226SJulian Elischer 1283878ed226SJulian Elischer switch (hdr->type) { 1284878ed226SJulian Elischer case NG_L2CAP_OPT_MTU: 1285878ed226SJulian Elischer if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length) 1286878ed226SJulian Elischer return (-2); 1287878ed226SJulian Elischer 1288878ed226SJulian Elischer m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val); 1289878ed226SJulian Elischer val->mtu = le16toh(val->mtu); 1290878ed226SJulian Elischer *off += NG_L2CAP_OPT_MTU_SIZE; 1291878ed226SJulian Elischer break; 1292878ed226SJulian Elischer 1293878ed226SJulian Elischer case NG_L2CAP_OPT_FLUSH_TIMO: 1294878ed226SJulian Elischer if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE || 1295878ed226SJulian Elischer len < hdr->length) 1296878ed226SJulian Elischer return (-2); 1297878ed226SJulian Elischer 1298878ed226SJulian Elischer m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val); 1299878ed226SJulian Elischer val->flush_timo = le16toh(val->flush_timo); 1300878ed226SJulian Elischer *off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE; 1301878ed226SJulian Elischer break; 1302878ed226SJulian Elischer 1303878ed226SJulian Elischer case NG_L2CAP_OPT_QOS: 1304878ed226SJulian Elischer if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length) 1305878ed226SJulian Elischer return (-2); 1306878ed226SJulian Elischer 1307878ed226SJulian Elischer m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val); 1308878ed226SJulian Elischer val->flow.token_rate = le32toh(val->flow.token_rate); 1309878ed226SJulian Elischer val->flow.token_bucket_size = 1310878ed226SJulian Elischer le32toh(val->flow.token_bucket_size); 1311878ed226SJulian Elischer val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth); 1312878ed226SJulian Elischer val->flow.latency = le32toh(val->flow.latency); 1313878ed226SJulian Elischer val->flow.delay_variation = le32toh(val->flow.delay_variation); 1314878ed226SJulian Elischer *off += NG_L2CAP_OPT_QOS_SIZE; 1315878ed226SJulian Elischer break; 1316878ed226SJulian Elischer 1317878ed226SJulian Elischer default: 1318878ed226SJulian Elischer if (hint) 1319878ed226SJulian Elischer *off += hdr->length; 1320878ed226SJulian Elischer else 1321878ed226SJulian Elischer return (-3); 1322878ed226SJulian Elischer break; 1323878ed226SJulian Elischer } 1324878ed226SJulian Elischer 1325878ed226SJulian Elischer return (1); 1326878ed226SJulian Elischer } /* get_next_l2cap_opt */ 1327878ed226SJulian Elischer 1328