Path: blob/main/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c
34814 views
/*1* ng_l2cap_cmds.c2*/34/*-5* SPDX-License-Identifier: BSD-2-Clause6*7* Copyright (c) Maksim Yevmenkin <[email protected]>8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*31* $Id: ng_l2cap_cmds.c,v 1.2 2003/09/08 19:11:45 max Exp $32*/3334#include <sys/param.h>35#include <sys/systm.h>36#include <sys/kernel.h>37#include <sys/endian.h>38#include <sys/malloc.h>39#include <sys/mbuf.h>40#include <sys/queue.h>41#include <netgraph/ng_message.h>42#include <netgraph/netgraph.h>43#include <netgraph/bluetooth/include/ng_bluetooth.h>44#include <netgraph/bluetooth/include/ng_hci.h>45#include <netgraph/bluetooth/include/ng_l2cap.h>46#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>47#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>48#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>49#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>50#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>51#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>5253/******************************************************************************54******************************************************************************55** L2CAP commands processing module56******************************************************************************57******************************************************************************/5859/*60* Process L2CAP command queue on connection61*/6263void64ng_l2cap_con_wakeup(ng_l2cap_con_p con)65{66ng_l2cap_cmd_p cmd = NULL;67struct mbuf *m = NULL;68int error = 0;6970/* Find first non-pending command in the queue */71TAILQ_FOREACH(cmd, &con->cmd_list, next) {72KASSERT((cmd->con == con),73("%s: %s - invalid connection pointer!\n",74__func__, NG_NODE_NAME(con->l2cap->node)));7576if (!(cmd->flags & NG_L2CAP_CMD_PENDING))77break;78}7980if (cmd == NULL)81return;8283/* Detach command packet */84m = cmd->aux;85cmd->aux = NULL;8687/* Process command */88switch (cmd->code) {89case NG_L2CAP_DISCON_RSP:90case NG_L2CAP_ECHO_RSP:91case NG_L2CAP_INFO_RSP:92/*93* Do not check return ng_l2cap_lp_send() value, because94* in these cases we do not really have a graceful way out.95* ECHO and INFO responses are internal to the stack and not96* visible to user. REJect is just being nice to remote end97* (otherwise remote end will timeout anyway). DISCON is98* probably most interesting here, however, if it fails99* there is nothing we can do anyway.100*/101102(void) ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);103ng_l2cap_unlink_cmd(cmd);104ng_l2cap_free_cmd(cmd);105break;106case NG_L2CAP_CMD_REJ:107(void) ng_l2cap_lp_send(con,108(con->linktype == NG_HCI_LINK_ACL)?109NG_L2CAP_SIGNAL_CID:110NG_L2CAP_LESIGNAL_CID111, m);112ng_l2cap_unlink_cmd(cmd);113ng_l2cap_free_cmd(cmd);114break;115116case NG_L2CAP_CON_REQ:117error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);118if (error != 0) {119ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,120NG_L2CAP_NO_RESOURCES, 0);121ng_l2cap_free_chan(cmd->ch); /* will free commands */122} else123ng_l2cap_command_timeout(cmd,124bluetooth_l2cap_rtx_timeout());125break;126case NG_L2CAP_CON_RSP:127error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);128ng_l2cap_unlink_cmd(cmd);129if (cmd->ch != NULL) {130ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,131(error == 0)? NG_L2CAP_SUCCESS :132NG_L2CAP_NO_RESOURCES);133if (error != 0)134ng_l2cap_free_chan(cmd->ch);135}136ng_l2cap_free_cmd(cmd);137break;138139case NG_L2CAP_CFG_REQ:140error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);141if (error != 0) {142ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token,143NG_L2CAP_NO_RESOURCES);144ng_l2cap_unlink_cmd(cmd);145ng_l2cap_free_cmd(cmd);146} else147ng_l2cap_command_timeout(cmd,148bluetooth_l2cap_rtx_timeout());149break;150151case NG_L2CAP_CFG_RSP:152error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);153ng_l2cap_unlink_cmd(cmd);154if (cmd->ch != NULL)155ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token,156(error == 0)? NG_L2CAP_SUCCESS :157NG_L2CAP_NO_RESOURCES);158ng_l2cap_free_cmd(cmd);159break;160161case NG_L2CAP_DISCON_REQ:162error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);163ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,164(error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES);165if (error != 0)166ng_l2cap_free_chan(cmd->ch); /* XXX free channel */167else168ng_l2cap_command_timeout(cmd,169bluetooth_l2cap_rtx_timeout());170break;171172case NG_L2CAP_ECHO_REQ:173error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);174if (error != 0) {175ng_l2cap_l2ca_ping_rsp(con, cmd->token,176NG_L2CAP_NO_RESOURCES, NULL);177ng_l2cap_unlink_cmd(cmd);178ng_l2cap_free_cmd(cmd);179} else180ng_l2cap_command_timeout(cmd,181bluetooth_l2cap_rtx_timeout());182break;183184case NG_L2CAP_INFO_REQ:185error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);186if (error != 0) {187ng_l2cap_l2ca_get_info_rsp(con, cmd->token,188NG_L2CAP_NO_RESOURCES, NULL);189ng_l2cap_unlink_cmd(cmd);190ng_l2cap_free_cmd(cmd);191} else192ng_l2cap_command_timeout(cmd,193bluetooth_l2cap_rtx_timeout());194break;195196case NGM_L2CAP_L2CA_WRITE: {197int length = m->m_pkthdr.len;198199if (cmd->ch->dcid == NG_L2CAP_CLT_CID) {200m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t));201if (m == NULL)202error = ENOBUFS;203else204mtod(m, ng_l2cap_clt_hdr_t *)->psm =205htole16(cmd->ch->psm);206}207208if (error == 0)209error = ng_l2cap_lp_send(con, cmd->ch->dcid, m);210211ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token,212(error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES,213length);214215ng_l2cap_unlink_cmd(cmd);216ng_l2cap_free_cmd(cmd);217} break;218case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:219error = ng_l2cap_lp_send(con, NG_L2CAP_LESIGNAL_CID, m);220ng_l2cap_unlink_cmd(cmd);221ng_l2cap_free_cmd(cmd);222break;223case NG_L2CAP_CMD_PARAM_UPDATE_REQUEST:224/*TBD.*/225/* XXX FIXME add other commands */226default:227panic(228"%s: %s - unknown command code=%d\n",229__func__, NG_NODE_NAME(con->l2cap->node), cmd->code);230break;231}232} /* ng_l2cap_con_wakeup */233234/*235* We have failed to open ACL connection to the remote unit. Could be negative236* confirmation or timeout. So fail any "delayed" commands, notify upper layer,237* remove all channels and remove connection descriptor.238*/239240void241ng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result)242{243ng_l2cap_p l2cap = con->l2cap;244ng_l2cap_cmd_p cmd = NULL;245ng_l2cap_chan_p ch = NULL;246247NG_L2CAP_INFO(248"%s: %s - ACL connection failed, result=%d\n",249__func__, NG_NODE_NAME(l2cap->node), result);250251/* Connection is dying */252con->flags |= NG_L2CAP_CON_DYING;253254/* Clean command queue */255while (!TAILQ_EMPTY(&con->cmd_list)) {256cmd = TAILQ_FIRST(&con->cmd_list);257258ng_l2cap_unlink_cmd(cmd);259if(cmd->flags & NG_L2CAP_CMD_PENDING)260ng_l2cap_command_untimeout(cmd);261262KASSERT((cmd->con == con),263("%s: %s - invalid connection pointer!\n",264__func__, NG_NODE_NAME(l2cap->node)));265266switch (cmd->code) {267case NG_L2CAP_CMD_REJ:268case NG_L2CAP_DISCON_RSP:269case NG_L2CAP_ECHO_RSP:270case NG_L2CAP_INFO_RSP:271case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:272break;273274case NG_L2CAP_CON_REQ:275ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0);276break;277278case NG_L2CAP_CON_RSP:279if (cmd->ch != NULL)280ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,281result);282break;283284case NG_L2CAP_CFG_REQ:285case NG_L2CAP_CFG_RSP:286case NGM_L2CAP_L2CA_WRITE:287ng_l2cap_l2ca_discon_ind(cmd->ch);288break;289290case NG_L2CAP_DISCON_REQ:291ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,292NG_L2CAP_SUCCESS);293break;294295case NG_L2CAP_ECHO_REQ:296ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,297result, NULL);298break;299300case NG_L2CAP_INFO_REQ:301ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,302result, NULL);303break;304305/* XXX FIXME add other commands */306307default:308panic(309"%s: %s - unexpected command code=%d\n",310__func__, NG_NODE_NAME(l2cap->node), cmd->code);311break;312}313314if (cmd->ch != NULL)315ng_l2cap_free_chan(cmd->ch);316317ng_l2cap_free_cmd(cmd);318}319320/*321* There still might be channels (in OPEN state?) that322* did not submit any commands, so disconnect them323*/324325LIST_FOREACH(ch, &l2cap->chan_list, next)326if (ch->con == con)327ng_l2cap_l2ca_discon_ind(ch);328329/* Free connection descriptor */330ng_l2cap_free_con(con);331} /* ng_l2cap_con_fail */332333/*334* Process L2CAP command timeout. In general - notify upper layer and destroy335* channel. Do not pay much attention to return code, just do our best.336*/337338void339ng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)340{341ng_l2cap_p l2cap = NULL;342ng_l2cap_con_p con = NULL;343ng_l2cap_cmd_p cmd = NULL;344u_int16_t con_handle = (arg2 & 0x0ffff);345u_int8_t ident = ((arg2 >> 16) & 0xff);346347if (NG_NODE_NOT_VALID(node)) {348printf("%s: Netgraph node is not valid\n", __func__);349return;350}351352l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);353354con = ng_l2cap_con_by_handle(l2cap, con_handle);355if (con == NULL) {356NG_L2CAP_ALERT(357"%s: %s - could not find connection, con_handle=%d\n",358__func__, NG_NODE_NAME(node), con_handle);359return;360}361362cmd = ng_l2cap_cmd_by_ident(con, ident);363if (cmd == NULL) {364NG_L2CAP_ALERT(365"%s: %s - could not find command, con_handle=%d, ident=%d\n",366__func__, NG_NODE_NAME(node), con_handle, ident);367return;368}369370cmd->flags &= ~NG_L2CAP_CMD_PENDING;371ng_l2cap_unlink_cmd(cmd);372373switch (cmd->code) {374case NG_L2CAP_CON_REQ:375ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0);376ng_l2cap_free_chan(cmd->ch);377break;378379case NG_L2CAP_CFG_REQ:380ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);381break;382383case NG_L2CAP_DISCON_REQ:384ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);385ng_l2cap_free_chan(cmd->ch); /* XXX free channel */386break;387388case NG_L2CAP_ECHO_REQ:389/* Echo request timed out. Let the upper layer know */390ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,391NG_L2CAP_TIMEOUT, NULL);392break;393394case NG_L2CAP_INFO_REQ:395/* Info request timed out. Let the upper layer know */396ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,397NG_L2CAP_TIMEOUT, NULL);398break;399400/* XXX FIXME add other commands */401402default:403panic(404"%s: %s - unexpected command code=%d\n",405__func__, NG_NODE_NAME(l2cap->node), cmd->code);406break;407}408409ng_l2cap_free_cmd(cmd);410} /* ng_l2cap_process_command_timeout */411412413