1107120Sjulian/* 2107120Sjulian * ng_l2cap_cmds.c 3139823Simp */ 4139823Simp 5139823Simp/*- 6107120Sjulian * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 7107120Sjulian * All rights reserved. 8107120Sjulian * 9107120Sjulian * Redistribution and use in source and binary forms, with or without 10107120Sjulian * modification, are permitted provided that the following conditions 11107120Sjulian * are met: 12107120Sjulian * 1. Redistributions of source code must retain the above copyright 13107120Sjulian * notice, this list of conditions and the following disclaimer. 14107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright 15107120Sjulian * notice, this list of conditions and the following disclaimer in the 16107120Sjulian * documentation and/or other materials provided with the distribution. 17107120Sjulian * 18107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28107120Sjulian * SUCH DAMAGE. 29107120Sjulian * 30121054Semax * $Id: ng_l2cap_cmds.c,v 1.2 2003/09/08 19:11:45 max Exp $ 31107120Sjulian * $FreeBSD$ 32107120Sjulian */ 33107120Sjulian 34107120Sjulian#include <sys/param.h> 35107120Sjulian#include <sys/systm.h> 36107120Sjulian#include <sys/kernel.h> 37107120Sjulian#include <sys/endian.h> 38107120Sjulian#include <sys/malloc.h> 39107120Sjulian#include <sys/mbuf.h> 40107120Sjulian#include <sys/queue.h> 41107120Sjulian#include <netgraph/ng_message.h> 42107120Sjulian#include <netgraph/netgraph.h> 43128688Semax#include <netgraph/bluetooth/include/ng_bluetooth.h> 44128688Semax#include <netgraph/bluetooth/include/ng_hci.h> 45128688Semax#include <netgraph/bluetooth/include/ng_l2cap.h> 46128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h> 47128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h> 48128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h> 49128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h> 50128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h> 51128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h> 52107120Sjulian 53107120Sjulian/****************************************************************************** 54107120Sjulian ****************************************************************************** 55107120Sjulian ** L2CAP commands processing module 56107120Sjulian ****************************************************************************** 57107120Sjulian ******************************************************************************/ 58107120Sjulian 59107120Sjulian/* 60107120Sjulian * Process L2CAP command queue on connection 61107120Sjulian */ 62107120Sjulian 63107120Sjulianvoid 64107120Sjulianng_l2cap_con_wakeup(ng_l2cap_con_p con) 65107120Sjulian{ 66107120Sjulian ng_l2cap_cmd_p cmd = NULL; 67107120Sjulian struct mbuf *m = NULL; 68107120Sjulian int error = 0; 69107120Sjulian 70107120Sjulian /* Find first non-pending command in the queue */ 71107120Sjulian TAILQ_FOREACH(cmd, &con->cmd_list, next) { 72107120Sjulian KASSERT((cmd->con == con), 73107120Sjulian("%s: %s - invalid connection pointer!\n", 74121054Semax __func__, NG_NODE_NAME(con->l2cap->node))); 75107120Sjulian 76107120Sjulian if (!(cmd->flags & NG_L2CAP_CMD_PENDING)) 77107120Sjulian break; 78107120Sjulian } 79107120Sjulian 80107120Sjulian if (cmd == NULL) 81107120Sjulian return; 82107120Sjulian 83107120Sjulian /* Detach command packet */ 84107120Sjulian m = cmd->aux; 85107120Sjulian cmd->aux = NULL; 86107120Sjulian 87107120Sjulian /* Process command */ 88107120Sjulian switch (cmd->code) { 89107120Sjulian case NG_L2CAP_CMD_REJ: 90107120Sjulian case NG_L2CAP_DISCON_RSP: 91107120Sjulian case NG_L2CAP_ECHO_RSP: 92107120Sjulian case NG_L2CAP_INFO_RSP: 93168009Semax /* 94168009Semax * Do not check return ng_l2cap_lp_send() value, because 95168009Semax * in these cases we do not really have a graceful way out. 96168009Semax * ECHO and INFO responses are internal to the stack and not 97168009Semax * visible to user. REJect is just being nice to remote end 98168009Semax * (otherwise remote end will timeout anyway). DISCON is 99168009Semax * probably most interesting here, however, if it fails 100168009Semax * there is nothing we can do anyway. 101168009Semax */ 102168009Semax 103168009Semax (void) ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 104107120Sjulian ng_l2cap_unlink_cmd(cmd); 105107120Sjulian ng_l2cap_free_cmd(cmd); 106107120Sjulian break; 107107120Sjulian 108107120Sjulian case NG_L2CAP_CON_REQ: 109107120Sjulian error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 110107120Sjulian if (error != 0) { 111107120Sjulian ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, 112107120Sjulian NG_L2CAP_NO_RESOURCES, 0); 113107120Sjulian ng_l2cap_free_chan(cmd->ch); /* will free commands */ 114107120Sjulian } else 115107120Sjulian ng_l2cap_command_timeout(cmd, 116107120Sjulian bluetooth_l2cap_rtx_timeout()); 117107120Sjulian break; 118107120Sjulian 119107120Sjulian case NG_L2CAP_CON_RSP: 120107120Sjulian error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 121107120Sjulian ng_l2cap_unlink_cmd(cmd); 122107120Sjulian if (cmd->ch != NULL) { 123107120Sjulian ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token, 124107120Sjulian (error == 0)? NG_L2CAP_SUCCESS : 125107120Sjulian NG_L2CAP_NO_RESOURCES); 126107120Sjulian if (error != 0) 127107120Sjulian ng_l2cap_free_chan(cmd->ch); 128107120Sjulian } 129107120Sjulian ng_l2cap_free_cmd(cmd); 130107120Sjulian break; 131107120Sjulian 132107120Sjulian case NG_L2CAP_CFG_REQ: 133107120Sjulian error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 134107120Sjulian if (error != 0) { 135107120Sjulian ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, 136107120Sjulian NG_L2CAP_NO_RESOURCES); 137107120Sjulian ng_l2cap_unlink_cmd(cmd); 138107120Sjulian ng_l2cap_free_cmd(cmd); 139107120Sjulian } else 140107120Sjulian ng_l2cap_command_timeout(cmd, 141107120Sjulian bluetooth_l2cap_rtx_timeout()); 142107120Sjulian break; 143107120Sjulian 144107120Sjulian case NG_L2CAP_CFG_RSP: 145107120Sjulian error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 146107120Sjulian ng_l2cap_unlink_cmd(cmd); 147107120Sjulian if (cmd->ch != NULL) 148107120Sjulian ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token, 149107120Sjulian (error == 0)? NG_L2CAP_SUCCESS : 150107120Sjulian NG_L2CAP_NO_RESOURCES); 151107120Sjulian ng_l2cap_free_cmd(cmd); 152107120Sjulian break; 153107120Sjulian 154107120Sjulian case NG_L2CAP_DISCON_REQ: 155107120Sjulian error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 156107120Sjulian ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, 157107120Sjulian (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES); 158107120Sjulian if (error != 0) 159107120Sjulian ng_l2cap_free_chan(cmd->ch); /* XXX free channel */ 160107120Sjulian else 161107120Sjulian ng_l2cap_command_timeout(cmd, 162107120Sjulian bluetooth_l2cap_rtx_timeout()); 163107120Sjulian break; 164107120Sjulian 165107120Sjulian case NG_L2CAP_ECHO_REQ: 166107120Sjulian error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 167107120Sjulian if (error != 0) { 168107120Sjulian ng_l2cap_l2ca_ping_rsp(con, cmd->token, 169107120Sjulian NG_L2CAP_NO_RESOURCES, NULL); 170107120Sjulian ng_l2cap_unlink_cmd(cmd); 171107120Sjulian ng_l2cap_free_cmd(cmd); 172107120Sjulian } else 173107120Sjulian ng_l2cap_command_timeout(cmd, 174107120Sjulian bluetooth_l2cap_rtx_timeout()); 175107120Sjulian break; 176107120Sjulian 177107120Sjulian case NG_L2CAP_INFO_REQ: 178107120Sjulian error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 179107120Sjulian if (error != 0) { 180107120Sjulian ng_l2cap_l2ca_get_info_rsp(con, cmd->token, 181107120Sjulian NG_L2CAP_NO_RESOURCES, NULL); 182107120Sjulian ng_l2cap_unlink_cmd(cmd); 183107120Sjulian ng_l2cap_free_cmd(cmd); 184107120Sjulian } else 185107120Sjulian ng_l2cap_command_timeout(cmd, 186107120Sjulian bluetooth_l2cap_rtx_timeout()); 187107120Sjulian break; 188107120Sjulian 189107120Sjulian case NGM_L2CAP_L2CA_WRITE: { 190107120Sjulian int length = m->m_pkthdr.len; 191107120Sjulian 192107120Sjulian if (cmd->ch->dcid == NG_L2CAP_CLT_CID) { 193107120Sjulian m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t)); 194107120Sjulian if (m == NULL) 195107120Sjulian error = ENOBUFS; 196107120Sjulian else 197107120Sjulian mtod(m, ng_l2cap_clt_hdr_t *)->psm = 198107120Sjulian htole16(cmd->ch->psm); 199107120Sjulian } 200107120Sjulian 201107120Sjulian if (error == 0) 202107120Sjulian error = ng_l2cap_lp_send(con, cmd->ch->dcid, m); 203107120Sjulian 204107120Sjulian ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token, 205107120Sjulian (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES, 206107120Sjulian length); 207107120Sjulian 208107120Sjulian ng_l2cap_unlink_cmd(cmd); 209107120Sjulian ng_l2cap_free_cmd(cmd); 210107120Sjulian } break; 211107120Sjulian 212107120Sjulian /* XXX FIXME add other commands */ 213107120Sjulian 214107120Sjulian default: 215121054Semax panic( 216121054Semax"%s: %s - unknown command code=%d\n", 217121054Semax __func__, NG_NODE_NAME(con->l2cap->node), cmd->code); 218107120Sjulian break; 219107120Sjulian } 220107120Sjulian} /* ng_l2cap_con_wakeup */ 221107120Sjulian 222107120Sjulian/* 223107120Sjulian * We have failed to open ACL connection to the remote unit. Could be negative 224107120Sjulian * confirmation or timeout. So fail any "delayed" commands, notify upper layer, 225107120Sjulian * remove all channels and remove connection descriptor. 226107120Sjulian */ 227107120Sjulian 228107120Sjulianvoid 229107120Sjulianng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result) 230107120Sjulian{ 231107120Sjulian ng_l2cap_p l2cap = con->l2cap; 232107120Sjulian ng_l2cap_cmd_p cmd = NULL; 233107120Sjulian ng_l2cap_chan_p ch = NULL; 234107120Sjulian 235107120Sjulian NG_L2CAP_INFO( 236107120Sjulian"%s: %s - ACL connection failed, result=%d\n", 237107120Sjulian __func__, NG_NODE_NAME(l2cap->node), result); 238107120Sjulian 239149679Semax /* Connection is dying */ 240149679Semax con->flags |= NG_L2CAP_CON_DYING; 241149679Semax 242107120Sjulian /* Clean command queue */ 243107120Sjulian while (!TAILQ_EMPTY(&con->cmd_list)) { 244107120Sjulian cmd = TAILQ_FIRST(&con->cmd_list); 245107120Sjulian 246107120Sjulian ng_l2cap_unlink_cmd(cmd); 247107120Sjulian if(cmd->flags & NG_L2CAP_CMD_PENDING) 248107120Sjulian ng_l2cap_command_untimeout(cmd); 249107120Sjulian 250107120Sjulian KASSERT((cmd->con == con), 251107120Sjulian("%s: %s - invalid connection pointer!\n", 252121054Semax __func__, NG_NODE_NAME(l2cap->node))); 253107120Sjulian 254107120Sjulian switch (cmd->code) { 255107120Sjulian case NG_L2CAP_CMD_REJ: 256107120Sjulian case NG_L2CAP_DISCON_RSP: 257107120Sjulian case NG_L2CAP_ECHO_RSP: 258107120Sjulian case NG_L2CAP_INFO_RSP: 259107120Sjulian break; 260107120Sjulian 261107120Sjulian case NG_L2CAP_CON_REQ: 262107120Sjulian ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0); 263107120Sjulian break; 264107120Sjulian 265107120Sjulian case NG_L2CAP_CON_RSP: 266107120Sjulian if (cmd->ch != NULL) 267107120Sjulian ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token, 268107120Sjulian result); 269107120Sjulian break; 270107120Sjulian 271107120Sjulian case NG_L2CAP_CFG_REQ: 272107120Sjulian case NG_L2CAP_CFG_RSP: 273107120Sjulian case NGM_L2CAP_L2CA_WRITE: 274107120Sjulian ng_l2cap_l2ca_discon_ind(cmd->ch); 275107120Sjulian break; 276107120Sjulian 277107120Sjulian case NG_L2CAP_DISCON_REQ: 278107120Sjulian ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, 279107120Sjulian NG_L2CAP_SUCCESS); 280107120Sjulian break; 281107120Sjulian 282107120Sjulian case NG_L2CAP_ECHO_REQ: 283107120Sjulian ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 284107120Sjulian result, NULL); 285107120Sjulian break; 286107120Sjulian 287107120Sjulian case NG_L2CAP_INFO_REQ: 288107120Sjulian ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 289107120Sjulian result, NULL); 290107120Sjulian break; 291107120Sjulian 292107120Sjulian /* XXX FIXME add other commands */ 293107120Sjulian 294107120Sjulian default: 295121054Semax panic( 296121054Semax"%s: %s - unexpected command code=%d\n", 297121054Semax __func__, NG_NODE_NAME(l2cap->node), cmd->code); 298107120Sjulian break; 299107120Sjulian } 300107120Sjulian 301107120Sjulian if (cmd->ch != NULL) 302107120Sjulian ng_l2cap_free_chan(cmd->ch); 303107120Sjulian 304107120Sjulian ng_l2cap_free_cmd(cmd); 305107120Sjulian } 306107120Sjulian 307107120Sjulian /* 308107120Sjulian * There still might be channels (in OPEN state?) that 309250460Seadler * did not submit any commands, so disconnect them 310107120Sjulian */ 311107120Sjulian 312107120Sjulian LIST_FOREACH(ch, &l2cap->chan_list, next) 313107120Sjulian if (ch->con == con) 314107120Sjulian ng_l2cap_l2ca_discon_ind(ch); 315107120Sjulian 316107120Sjulian /* Free connection descriptor */ 317107120Sjulian ng_l2cap_free_con(con); 318107120Sjulian} /* ng_l2cap_con_fail */ 319107120Sjulian 320107120Sjulian/* 321107120Sjulian * Process L2CAP command timeout. In general - notify upper layer and destroy 322107120Sjulian * channel. Do not pay much attension to return code, just do our best. 323107120Sjulian */ 324107120Sjulian 325107120Sjulianvoid 326107120Sjulianng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2) 327107120Sjulian{ 328121054Semax ng_l2cap_p l2cap = NULL; 329121054Semax ng_l2cap_con_p con = NULL; 330121054Semax ng_l2cap_cmd_p cmd = NULL; 331121054Semax u_int16_t con_handle = (arg2 & 0x0ffff); 332121054Semax u_int8_t ident = ((arg2 >> 16) & 0xff); 333107120Sjulian 334121054Semax if (NG_NODE_NOT_VALID(node)) { 335121054Semax printf("%s: Netgraph node is not valid\n", __func__); 336121054Semax return; 337121054Semax } 338107120Sjulian 339121054Semax l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); 340121054Semax 341121054Semax con = ng_l2cap_con_by_handle(l2cap, con_handle); 342121054Semax if (con == NULL) { 343121054Semax NG_L2CAP_ALERT( 344121054Semax"%s: %s - could not find connection, con_handle=%d\n", 345121054Semax __func__, NG_NODE_NAME(node), con_handle); 346121054Semax return; 347121054Semax } 348121054Semax 349121054Semax cmd = ng_l2cap_cmd_by_ident(con, ident); 350121054Semax if (cmd == NULL) { 351121054Semax NG_L2CAP_ALERT( 352121054Semax"%s: %s - could not find command, con_handle=%d, ident=%d\n", 353121054Semax __func__, NG_NODE_NAME(node), con_handle, ident); 354121054Semax return; 355121054Semax } 356121054Semax 357107120Sjulian cmd->flags &= ~NG_L2CAP_CMD_PENDING; 358107120Sjulian ng_l2cap_unlink_cmd(cmd); 359107120Sjulian 360107120Sjulian switch (cmd->code) { 361107120Sjulian case NG_L2CAP_CON_REQ: 362107120Sjulian ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0); 363107120Sjulian ng_l2cap_free_chan(cmd->ch); 364107120Sjulian break; 365107120Sjulian 366107120Sjulian case NG_L2CAP_CFG_REQ: 367107120Sjulian ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT); 368107120Sjulian break; 369107120Sjulian 370107120Sjulian case NG_L2CAP_DISCON_REQ: 371107120Sjulian ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT); 372107120Sjulian ng_l2cap_free_chan(cmd->ch); /* XXX free channel */ 373107120Sjulian break; 374107120Sjulian 375107120Sjulian case NG_L2CAP_ECHO_REQ: 376107120Sjulian /* Echo request timed out. Let the upper layer know */ 377121054Semax ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 378107120Sjulian NG_L2CAP_TIMEOUT, NULL); 379107120Sjulian break; 380107120Sjulian 381107120Sjulian case NG_L2CAP_INFO_REQ: 382107120Sjulian /* Info request timed out. Let the upper layer know */ 383107120Sjulian ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 384107120Sjulian NG_L2CAP_TIMEOUT, NULL); 385107120Sjulian break; 386107120Sjulian 387107120Sjulian /* XXX FIXME add other commands */ 388107120Sjulian 389107120Sjulian default: 390121054Semax panic( 391121054Semax"%s: %s - unexpected command code=%d\n", 392121054Semax __func__, NG_NODE_NAME(l2cap->node), cmd->code); 393107120Sjulian break; 394107120Sjulian } 395107120Sjulian 396107120Sjulian ng_l2cap_free_cmd(cmd); 397107120Sjulian} /* ng_l2cap_process_command_timeout */ 398107120Sjulian 399