summaryrefslogtreecommitdiffstats
path: root/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c')
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c365
1 files changed, 365 insertions, 0 deletions
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c b/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c
new file mode 100644
index 0000000..0d28f1a
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c
@@ -0,0 +1,365 @@
+/*
+ * ng_l2cap_cmds.c
+ *
+ * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ng_l2cap_cmds.c,v 1.14 2002/09/04 21:38:38 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_l2cap_var.h"
+#include "ng_l2cap_cmds.h"
+#include "ng_l2cap_evnt.h"
+#include "ng_l2cap_llpi.h"
+#include "ng_l2cap_ulpi.h"
+#include "ng_l2cap_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** L2CAP commands processing module
+ ******************************************************************************
+ ******************************************************************************/
+
+/*
+ * Process L2CAP command queue on connection
+ */
+
+void
+ng_l2cap_con_wakeup(ng_l2cap_con_p con)
+{
+ ng_l2cap_cmd_p cmd = NULL;
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ /* Find first non-pending command in the queue */
+ TAILQ_FOREACH(cmd, &con->cmd_list, next) {
+ KASSERT((cmd->con == con),
+("%s: %s - invalid connection pointer!\n",
+ __func__, NG_NODE_NAME(con->l2cap->node)));
+
+ if (!(cmd->flags & NG_L2CAP_CMD_PENDING))
+ break;
+ }
+
+ if (cmd == NULL)
+ return;
+
+ /* Detach command packet */
+ m = cmd->aux;
+ cmd->aux = NULL;
+
+ /* Process command */
+ switch (cmd->code) {
+ case NG_L2CAP_CMD_REJ:
+ case NG_L2CAP_DISCON_RSP:
+ case NG_L2CAP_ECHO_RSP:
+ case NG_L2CAP_INFO_RSP:
+ ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ break;
+
+ case NG_L2CAP_CON_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
+ NG_L2CAP_NO_RESOURCES, 0);
+ ng_l2cap_free_chan(cmd->ch); /* will free commands */
+ } else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NG_L2CAP_CON_RSP:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ ng_l2cap_unlink_cmd(cmd);
+ if (cmd->ch != NULL) {
+ ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
+ (error == 0)? NG_L2CAP_SUCCESS :
+ NG_L2CAP_NO_RESOURCES);
+ if (error != 0)
+ ng_l2cap_free_chan(cmd->ch);
+ }
+ ng_l2cap_free_cmd(cmd);
+ break;
+
+ case NG_L2CAP_CFG_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token,
+ NG_L2CAP_NO_RESOURCES);
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ } else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NG_L2CAP_CFG_RSP:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ ng_l2cap_unlink_cmd(cmd);
+ if (cmd->ch != NULL)
+ ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token,
+ (error == 0)? NG_L2CAP_SUCCESS :
+ NG_L2CAP_NO_RESOURCES);
+ ng_l2cap_free_cmd(cmd);
+ break;
+
+ case NG_L2CAP_DISCON_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
+ (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES);
+ if (error != 0)
+ ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
+ else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NG_L2CAP_ECHO_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_ping_rsp(con, cmd->token,
+ NG_L2CAP_NO_RESOURCES, NULL);
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ } else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NG_L2CAP_INFO_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_get_info_rsp(con, cmd->token,
+ NG_L2CAP_NO_RESOURCES, NULL);
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ } else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NGM_L2CAP_L2CA_WRITE: {
+ int length = m->m_pkthdr.len;
+
+ if (cmd->ch->dcid == NG_L2CAP_CLT_CID) {
+ m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t));
+ if (m == NULL)
+ error = ENOBUFS;
+ else
+ mtod(m, ng_l2cap_clt_hdr_t *)->psm =
+ htole16(cmd->ch->psm);
+ }
+
+ if (error == 0)
+ error = ng_l2cap_lp_send(con, cmd->ch->dcid, m);
+
+ ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token,
+ (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES,
+ length);
+
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ } break;
+
+ /* XXX FIXME add other commands */
+
+ default:
+ KASSERT(0,
+("%s: %s - unknown command code=%d\n",
+ __func__, NG_NODE_NAME(con->l2cap->node), cmd->code));
+
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ break;
+ }
+} /* ng_l2cap_con_wakeup */
+
+/*
+ * We have failed to open ACL connection to the remote unit. Could be negative
+ * confirmation or timeout. So fail any "delayed" commands, notify upper layer,
+ * remove all channels and remove connection descriptor.
+ */
+
+void
+ng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_cmd_p cmd = NULL;
+ ng_l2cap_chan_p ch = NULL;
+
+ NG_L2CAP_INFO(
+"%s: %s - ACL connection failed, result=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), result);
+
+ /* Clean command queue */
+ while (!TAILQ_EMPTY(&con->cmd_list)) {
+ cmd = TAILQ_FIRST(&con->cmd_list);
+
+ ng_l2cap_unlink_cmd(cmd);
+ if(cmd->flags & NG_L2CAP_CMD_PENDING)
+ ng_l2cap_command_untimeout(cmd);
+
+ KASSERT((cmd->con == con),
+("%s: %s - invalid connection pointer!\n",
+ __func__, NG_NODE_NAME(con->l2cap->node)));
+
+ switch (cmd->code) {
+ case NG_L2CAP_CMD_REJ:
+ case NG_L2CAP_DISCON_RSP:
+ case NG_L2CAP_ECHO_RSP:
+ case NG_L2CAP_INFO_RSP:
+ break;
+
+ case NG_L2CAP_CON_REQ:
+ ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0);
+ break;
+
+ case NG_L2CAP_CON_RSP:
+ if (cmd->ch != NULL)
+ ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
+ result);
+ break;
+
+ case NG_L2CAP_CFG_REQ:
+ case NG_L2CAP_CFG_RSP:
+ case NGM_L2CAP_L2CA_WRITE:
+ ng_l2cap_l2ca_discon_ind(cmd->ch);
+ break;
+
+ case NG_L2CAP_DISCON_REQ:
+ ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
+ NG_L2CAP_SUCCESS);
+ break;
+
+ case NG_L2CAP_ECHO_REQ:
+ ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
+ result, NULL);
+ break;
+
+ case NG_L2CAP_INFO_REQ:
+ ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
+ result, NULL);
+ break;
+
+ /* XXX FIXME add other commands */
+
+ default:
+ KASSERT(0,
+("%s: %s - unexpected command code=%d\n",
+ __func__, NG_NODE_NAME(con->l2cap->node),
+ cmd->code));
+ break;
+ }
+
+ if (cmd->ch != NULL)
+ ng_l2cap_free_chan(cmd->ch);
+
+ ng_l2cap_free_cmd(cmd);
+ }
+
+ /*
+ * There still might be channels (in OPEN state?) that
+ * did not submit any commands, so diconnect them
+ */
+
+ LIST_FOREACH(ch, &l2cap->chan_list, next)
+ if (ch->con == con)
+ ng_l2cap_l2ca_discon_ind(ch);
+
+ /* Free connection descriptor */
+ ng_l2cap_free_con(con);
+} /* ng_l2cap_con_fail */
+
+/*
+ * Process L2CAP command timeout. In general - notify upper layer and destroy
+ * channel. Do not pay much attension to return code, just do our best.
+ */
+
+void
+ng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_l2cap_cmd_p cmd = (ng_l2cap_cmd_p) arg1;
+
+ KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
+("%s: %s - invalid command flags flags=%#x!\n",
+ __func__, NG_NODE_NAME(cmd->con->l2cap->node), cmd->flags));
+
+ cmd->flags &= ~NG_L2CAP_CMD_PENDING;
+ ng_l2cap_unlink_cmd(cmd);
+
+ switch (cmd->code) {
+ case NG_L2CAP_CON_REQ:
+ ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0);
+ ng_l2cap_free_chan(cmd->ch);
+ break;
+
+ case NG_L2CAP_CFG_REQ:
+ ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
+ break;
+
+ case NG_L2CAP_DISCON_REQ:
+ ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
+ ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
+ break;
+
+ case NG_L2CAP_ECHO_REQ:
+ /* Echo request timed out. Let the upper layer know */
+ ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
+ NG_L2CAP_TIMEOUT, NULL);
+ break;
+
+ case NG_L2CAP_INFO_REQ:
+ /* Info request timed out. Let the upper layer know */
+ ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
+ NG_L2CAP_TIMEOUT, NULL);
+ break;
+
+ /* XXX FIXME add other commands */
+
+ default:
+ KASSERT(0,
+("%s: %s - unexpected command code=%d\n",
+ __func__, NG_NODE_NAME(cmd->con->l2cap->node),
+ cmd->code));
+ break;
+ }
+
+ ng_l2cap_free_cmd(cmd);
+} /* ng_l2cap_process_command_timeout */
+
OpenPOWER on IntegriCloud