From: Sascha Hauer <s.hauer@pengutronix.de>
To: Barebox List <barebox@lists.infradead.org>
Subject: [PATCH 1/4] Add Reliable Asynchronous Transfer Protocol
Date: Thu, 11 Jun 2015 08:54:07 +0200 [thread overview]
Message-ID: <1434005650-28131-2-git-send-email-s.hauer@pengutronix.de> (raw)
In-Reply-To: <1434005650-28131-1-git-send-email-s.hauer@pengutronix.de>
This patch adds support for Reliable Asynchronous Transfer Protocol (RATP)
as described in RFC916.
Communication over RS232 is often unreliable as characters are lost or
misinterpreted. This protocol allows for a reliable packet based communication
over serial lines.
The implementation simply follows the state machine described in the RFC text.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/Kconfig | 10 +
common/Makefile | 2 +
common/console.c | 54 +-
include/ratp.h | 80 +++
lib/Kconfig | 8 +
lib/Makefile | 1 +
lib/ratp.c | 1612 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 1765 insertions(+), 2 deletions(-)
create mode 100644 include/ratp.h
create mode 100644 lib/ratp.c
diff --git a/common/Kconfig b/common/Kconfig
index 3dfb5ac..0c96ea0 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -576,6 +576,16 @@ config CONSOLE_ACTIVATE_NONE
endchoice
+config CONSOLE_RATP
+ bool
+ select RATP
+ prompt "RATP console support"
+ help
+ This option adds support for remote controlling barebox via serial
+ port. The regular console is designed for human interaction whereas
+ this option adds a machine readable interface for controlling barebox.
+ Say yes here if you want to control barebox from a remote host.
+
config PARTITION
bool
prompt "Enable Partitions"
diff --git a/common/Makefile b/common/Makefile
index 2738238..e413f0d 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_RESET_SOURCE) += reset_source.o
obj-$(CONFIG_SHELL_HUSH) += hush.o
obj-$(CONFIG_SHELL_SIMPLE) += parser.o
obj-$(CONFIG_STATE) += state.o
+obj-$(CONFIG_RATP) += ratp.o
obj-$(CONFIG_UIMAGE) += image.o uimage.o
obj-$(CONFIG_MENUTREE) += menutree.o
obj-$(CONFIG_EFI_GUID) += efi-guid.o
@@ -52,6 +53,7 @@ lwl-$(CONFIG_IMD) += imd-barebox.o
obj-$(CONFIG_IMD) += imd.o
obj-$(CONFIG_FILE_LIST) += file-list.o
obj-$(CONFIG_FIRMWARE) += firmware.o
+obj-$(CONFIG_CONSOLE_RATP) += ratp.o
quiet_cmd_pwd_h = PWDH $@
ifdef CONFIG_PASSWORD
diff --git a/common/console.c b/common/console.c
index 0a6fc3e..b63a075 100644
--- a/common/console.c
+++ b/common/console.c
@@ -303,6 +303,52 @@ int console_unregister(struct console_device *cdev)
}
EXPORT_SYMBOL(console_unregister);
+int barebox_ratp(struct console_device *cdev);
+
+static struct console_device *ratp_request;
+
+/**
+ * barebox_console_ratp_outstanding() - Check if there are outstanding RATP
+ * requests
+ *
+ * Return: true if there are outstanding RATP requests and false otherwise
+ */
+bool barebox_console_ratp_outstanding(void)
+{
+ if (!IS_ENABLED(CONFIG_CONSOLE_RATP))
+ return false;
+
+ return ratp_request != NULL;
+}
+
+/**
+ * barebox_console_ratp() - Check for outstanding RATP requests
+ *
+ * If there are outstanding RATP requests from the remote end
+ * enter RATP mode.
+ */
+void barebox_console_ratp(void)
+{
+ struct console_device *cdev;
+ unsigned active;
+
+ if (!IS_ENABLED(CONFIG_CONSOLE_RATP))
+ return;
+
+ if (!ratp_request)
+ return;
+
+ cdev = ratp_request;
+
+ pr_info("RATP request detected, entering RATP mode\n");
+ active = console_get_active(cdev);
+ console_set_active(cdev, 0);
+ barebox_ratp(cdev);
+ console_set_active(cdev, active);
+
+ ratp_request = NULL;
+}
+
static int getc_raw(void)
{
struct console_device *cdev;
@@ -313,8 +359,12 @@ static int getc_raw(void)
if (!(cdev->f_active & CONSOLE_STDIN))
continue;
active = 1;
- if (cdev->tstc(cdev))
- return cdev->getc(cdev);
+ if (cdev->tstc(cdev)) {
+ int ch = cdev->getc(cdev);
+ if (ch == 0x01)
+ ratp_request = cdev;
+ return ch;
+ }
}
if (!active)
/* no active console found. bail out */
diff --git a/include/ratp.h b/include/ratp.h
new file mode 100644
index 0000000..58098f9
--- /dev/null
+++ b/include/ratp.h
@@ -0,0 +1,80 @@
+#ifndef __RATP_H
+#define __RATP_H
+
+#include <linux/list.h>
+
+struct ratp_header {
+ uint8_t synch;
+ uint8_t control;
+ uint8_t data_length;
+ uint8_t cksum;
+};
+
+enum ratp_state {
+ RATP_STATE_LISTEN,
+ RATP_STATE_SYN_SENT,
+ RATP_STATE_SYN_RECEIVED,
+ RATP_STATE_ESTABLISHED,
+ RATP_STATE_FIN_WAIT,
+ RATP_STATE_LAST_ACK,
+ RATP_STATE_CLOSING,
+ RATP_STATE_TIME_WAIT,
+ RATP_STATE_CLOSED,
+};
+
+struct ratp_message {
+ void *buf;
+ size_t len;
+ struct list_head list;
+};
+
+struct ratp {
+ enum ratp_state state;
+ int sn_sent;
+ int an_sent;
+ int sn_received;
+ int an_received;
+ int active;
+ int (*send)(struct ratp *, void *pkt, int len);
+ int (*recv)(struct ratp *, uint8_t *data);
+
+ void *sendbuf;
+ int sendbuf_len;
+
+ void *recvbuf;
+
+ struct list_head recvmsg;
+
+ uint64_t timewait_timer_start;
+ uint64_t retransmission_timer_start;
+ int max_retransmission;
+ int retransmission_count;
+ int srtt;
+ int rto;
+
+ int status;
+
+ int wait_ack;
+
+ int in_ratp;
+};
+
+#define RATP_CONTROL_SO (1 << 0)
+#define RATP_CONTROL_EOR (1 << 1)
+#define RATP_CONTROL_AN (1 << 2)
+#define RATP_CONTROL_SN (1 << 3)
+#define RATP_CONTROL_RST (1 << 4)
+#define RATP_CONTROL_FIN (1 << 5)
+#define RATP_CONTROL_ACK (1 << 6)
+#define RATP_CONTROL_SYN (1 << 7)
+
+int ratp_establish(struct ratp *ratp, bool active, int timeout_ms);
+void ratp_debug(struct ratp *ratp, const char *fmt, ...);
+void ratp_sleep(int s);
+int ratp_recv_data(struct ratp *ratp, void *data, size_t *len);
+int ratp_send_data(struct ratp *ratp, const void *data, size_t len);
+int ratp_poll(struct ratp *ratp);
+bool ratp_closed(struct ratp *ratp);
+bool ratp_busy(struct ratp *ratp);
+
+#endif /* __RATP_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 62695f1..a805cfd 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -55,6 +55,14 @@ config LIBMTD
config STMP_DEVICE
bool
+config RATP
+ select CRC16
+ bool
+ help
+ Reliable Asynchronous Transfer Protocol (RATP) is a protocol for reliably
+ transferring packets over serial links described in RFC916. This implementation
+ is used for controlling barebox over serial ports.
+
source lib/gui/Kconfig
source lib/bootstrap/Kconfig
diff --git a/lib/Makefile b/lib/Makefile
index 6a3e9fd..cad03cb 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -54,3 +54,4 @@ obj-y += libfile.o
obj-y += bitmap.o
obj-y += gcd.o
obj-y += hexdump.o
+obj-$(CONFIG_RATP) += ratp.o
diff --git a/lib/ratp.c b/lib/ratp.c
new file mode 100644
index 0000000..2f4448a
--- /dev/null
+++ b/lib/ratp.c
@@ -0,0 +1,1612 @@
+/*
+ * barebox RATP implementation.
+ * This is the barebox implementation for the Reliable Asynchronous
+ * Transfer Protocol (RATP) as described in RFC916.
+ *
+ * Copyright (C) 2015 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "ratp: " fmt
+
+#include <common.h>
+#include <malloc.h>
+#include <getopt.h>
+#include <ratp.h>
+#include <crc.h>
+#include <clock.h>
+#include <asm/unaligned.h>
+
+/*
+ * RATP packet format:
+ *
+ * Byte No.
+ *
+ * +-------------------------------+
+ * | |
+ * 1 | Synch Leader | Hex 01
+ * | |
+ * +-------------------------------+
+ * | S | A | F | R | S | A | E | S |
+ * 2 | Y | C | I | S | N | N | O | O | Control
+ * | N | K | N | T | | | R | |
+ * +-------------------------------+
+ * | |
+ * 3 | Data length (0-255) |
+ * | |
+ * +-------------------------------+
+ * | |
+ * 4 | Header Checksum |
+ * | |
+ * +-------------------------------+
+ *
+ */
+
+static char *ratp_state_str[] = {
+ [RATP_STATE_LISTEN] = "LISTEN",
+ [RATP_STATE_SYN_SENT] = "SYN_SENT",
+ [RATP_STATE_SYN_RECEIVED] = "SYN_RECEIVED",
+ [RATP_STATE_ESTABLISHED] = "ESTABLISHED",
+ [RATP_STATE_FIN_WAIT] = "FIN_WAIT",
+ [RATP_STATE_LAST_ACK] = "LAST_ACK",
+ [RATP_STATE_CLOSING] = "CLOSING",
+ [RATP_STATE_TIME_WAIT] = "TIME_WAIT",
+ [RATP_STATE_CLOSED] = "CLOSED",
+};
+
+static bool ratp_sn(struct ratp_header *hdr)
+{
+ return hdr->control & RATP_CONTROL_SN ? 1 : 0;
+}
+
+static bool ratp_an(struct ratp_header *hdr)
+{
+ return hdr->control & RATP_CONTROL_AN ? 1 : 0;
+}
+
+#define ratp_set_sn(sn) (((sn) % 2) ? RATP_CONTROL_SN : 0)
+#define ratp_set_an(an) (((an) % 2) ? RATP_CONTROL_AN : 0)
+
+static inline int ratp_header_ok(struct ratp *ratp, struct ratp_header *h)
+{
+ uint8_t cksum;
+ int ret;
+
+ cksum = h->control;
+ cksum += h->data_length;
+ cksum += h->cksum;
+
+ ret = cksum == 0xff ? 1 : 0;
+
+ if (ret)
+ pr_vdebug("Header ok\n");
+ else
+ pr_vdebug("Header cksum failed: %02x\n", cksum);
+
+ return ret;
+}
+
+static bool ratp_has_data(struct ratp_header *hdr)
+{
+ if (hdr->control & RATP_CONTROL_SO)
+ return 1;
+ if (hdr->data_length)
+ return 1;
+ return 0;
+}
+
+static void ratp_print_header(struct ratp *ratp, struct ratp_header *hdr,
+ const char *prefix)
+{
+ uint8_t control = hdr->control;
+
+ pr_debug("%s>%s %s %s %s %s %s %s %s< len: %-3d\n",
+ prefix,
+ control & RATP_CONTROL_SO ? "so" : "--",
+ control & RATP_CONTROL_EOR ? "eor" : "---",
+ control & RATP_CONTROL_AN ? "an" : "--",
+ control & RATP_CONTROL_SN ? "sn" : "--",
+ control & RATP_CONTROL_RST ? "rst" : "---",
+ control & RATP_CONTROL_FIN ? "fin" : "---",
+ control & RATP_CONTROL_ACK ? "ack" : "---",
+ control & RATP_CONTROL_SYN ? "syn" : "---",
+ hdr->data_length);
+}
+
+static void ratp_create_packet(struct ratp *ratp, struct ratp_header *hdr,
+ uint8_t control, uint8_t length)
+{
+ hdr->synch = 0x1;
+ hdr->control = control;
+ hdr->data_length = length;
+ hdr->cksum = (control + length) ^ 0xff;
+}
+
+static void ratp_state_change(struct ratp *ratp, enum ratp_state state)
+{
+ pr_debug("state %-10s -> %-10s\n", ratp_state_str[ratp->state],
+ ratp_state_str[state]);
+
+ ratp->state = state;
+}
+
+#define RATP_CONTROL_SO (1 << 0)
+#define RATP_CONTROL_EOR (1 << 1)
+#define RATP_CONTROL_AN (1 << 2)
+#define RATP_CONTROL_SN (1 << 3)
+#define RATP_CONTROL_RST (1 << 4)
+#define RATP_CONTROL_FIN (1 << 5)
+#define RATP_CONTROL_ACK (1 << 6)
+#define RATP_CONTROL_SYN (1 << 7)
+
+static int ratp_send(struct ratp *ratp, void *pkt, int length)
+{
+ struct ratp_header *hdr = (void *)pkt;
+
+ ratp_print_header(ratp, hdr, "send");
+
+ ratp->sn_sent = ratp_sn(hdr);
+ ratp->an_sent = ratp_an(hdr);
+
+ if (ratp_has_data(hdr) ||
+ (hdr->control & (RATP_CONTROL_SYN | RATP_CONTROL_RST | RATP_CONTROL_FIN))) {
+ memcpy(ratp->sendbuf, pkt, length);
+ ratp->sendbuf_len = length;
+ ratp->retransmission_timer_start = get_time_ns();
+ ratp->retransmission_count = 0;
+ ratp->wait_ack = true;
+ }
+
+ return ratp->send(ratp, pkt, length);
+}
+
+static int ratp_send_hdr(struct ratp *ratp, uint8_t control)
+{
+ struct ratp_header hdr = {};
+
+ ratp_create_packet(ratp, &hdr, control, 0);
+
+ return ratp_send(ratp, &hdr, sizeof(hdr));
+}
+
+static int ratp_getc(struct ratp *ratp, uint8_t *data, int poll_timeout_ms)
+{
+ uint64_t start;
+ int ret;
+
+ start = get_time_ns();
+
+ while (1) {
+ ret = ratp->recv(ratp, data);
+ if (ret < 0)
+ return ret;
+
+ if (ret > 0)
+ return 0;
+
+ if (is_timeout(start, poll_timeout_ms * MSECOND))
+ return -ETIMEDOUT;
+ }
+}
+
+static int ratp_recv_header(struct ratp *ratp, struct ratp_header *hdr,
+ int poll_timeout_ms)
+{
+ int ret;
+ uint8_t buf;
+
+again:
+ do {
+ ret = ratp_getc(ratp, &buf, poll_timeout_ms);
+ if (ret < 0)
+ return ret;
+
+ hdr->synch = buf;
+ } while (hdr->synch != 1);
+
+ ret = ratp_getc(ratp, &buf, poll_timeout_ms);
+ if (ret < 0)
+ return ret;
+
+ hdr->control = buf;
+
+ ret = ratp_getc(ratp, &buf, poll_timeout_ms);
+ if (ret < 0)
+ return ret;
+
+ hdr->data_length = buf;
+
+ ret = ratp_getc(ratp, &buf, poll_timeout_ms);
+ if (ret < 0)
+ return ret;
+
+ hdr->cksum = buf;
+
+ if (!ratp_header_ok(ratp, hdr))
+ goto again;
+
+ return 0;
+}
+
+static int ratp_recv_pkt_data(struct ratp *ratp, void *data, uint8_t len,
+ int poll_timeout_ms)
+{
+ uint16_t crc_expect, crc_read;
+ int ret, i;
+
+ for (i = 0; i < len + 2; i++) {
+ ret = ratp_getc(ratp, data + i, poll_timeout_ms);
+ if (ret < 0)
+ return ret;
+ }
+
+ crc_expect = cyg_crc16(data, len);
+
+ crc_read = get_unaligned_be16(data + len);
+
+ if (crc_expect != crc_read) {
+ pr_vdebug("Wrong CRC: expected: 0x%04x, got 0x%04x\n",
+ crc_expect, crc_read);
+ return -EBADMSG;
+ } else {
+ pr_vdebug("correct CRC: 0x%04x\n", crc_expect);
+ }
+
+ return 0;
+}
+
+static int ratp_recv(struct ratp *ratp, void *pkt, int poll_timeout_ms)
+{
+ struct ratp_header *hdr = pkt;
+ void *data = pkt + sizeof(struct ratp_header);
+ int ret;
+
+ ret = ratp_recv_header(ratp, hdr, poll_timeout_ms);
+ if (ret < 0)
+ return ret;
+
+ ratp->sn_received = ratp_sn(hdr);
+ ratp->an_received = ratp_sn(hdr);
+
+ if (hdr->control & (RATP_CONTROL_SO | RATP_CONTROL_RST | RATP_CONTROL_SYN | RATP_CONTROL_FIN))
+ return 0;
+
+ if (hdr->data_length) {
+ ret = ratp_recv_pkt_data(ratp, data, hdr->data_length,
+ poll_timeout_ms);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool ratp_an_expected(struct ratp *ratp, struct ratp_header *hdr)
+{
+ return ratp_an(hdr) == (ratp->sn_sent + 1) % 2;
+}
+
+static bool ratp_sn_expected(struct ratp *ratp, struct ratp_header *hdr)
+{
+ return ratp_sn(hdr) == ratp->an_sent;
+}
+
+static int ratp_send_ack(struct ratp *ratp, struct ratp_header *hdr)
+{
+ uint8_t control = RATP_CONTROL_ACK;
+ int ret;
+
+ if (hdr->control & RATP_CONTROL_AN)
+ control |= RATP_CONTROL_SN;
+ else
+ control |= 0;
+
+ if (hdr->control & RATP_CONTROL_SN)
+ control |= 0;
+ else
+ control |= RATP_CONTROL_AN;
+
+ ret = ratp_send_hdr(ratp, control);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void ratp_start_time_wait_timer(struct ratp *ratp)
+{
+ ratp->timewait_timer_start = get_time_ns();
+}
+
+/*
+ * This procedure details the behavior of the LISTEN state. First
+ * check the packet for the RST flag. If it is set then packet is
+ * discarded and ignored, return and continue the processing
+ * associated with this state.
+ *
+ * We assume now that the RST flag was not set. Check the packet
+ * for the ACK flag. If it is set we have an illegal condition
+ * since no connection has yet been opened. Send a RST packet
+ * with the correct response SN value:
+ *
+ * <SN=received AN><CTL=RST>
+ *
+ * Return to the current state without any further processing.
+ *
+ * We assume now that neither the RST nor the ACK flags were set.
+ * Check the packet for a SYN flag. If it is set then an attempt
+ * is being made to open a connection. Create a TCB for this
+ * connection. The sender has placed its MDL in the LENGTH field,
+ * also specified is the sender's initial SN value. Retrieve and
+ * place them into the TCB. Note that the presence of the SO flag
+ * is ignored since it has no meaning when either of the SYN, RST,
+ * or FIN flags are set.
+ *
+ * Send a SYN packet which acknowledges the SYN received. Choose
+ * the initial SN value and the MDL for this end of the
+ * connection:
+ *
+ * <SN=0><AN=received SN+1 modulo 2><CTL=SYN, ACK><LENGTH=MDL>
+ *
+ * and go to the RATP_STATE_SYN_RECEIVED state without any further
+ * processing.
+ *
+ * Any packet not satisfying the above tests is discarded and
+ * ignored. Return to the current state without any further
+ * processing.
+ */
+static void ratp_behaviour_a(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (hdr->control & RATP_CONTROL_RST)
+ return;
+
+ if (hdr->control & RATP_CONTROL_ACK) {
+ uint8_t control = RATP_CONTROL_RST;
+
+ if (hdr->control & RATP_CONTROL_AN)
+ control |= RATP_CONTROL_SN;
+
+ ratp_send_hdr(ratp, control);
+
+ return;
+ }
+
+ if (hdr->control & RATP_CONTROL_SYN) {
+ struct ratp_header synack = {};
+ uint8_t control = RATP_CONTROL_SYN | RATP_CONTROL_ACK;
+
+ if (!(hdr->control & RATP_CONTROL_SN))
+ control |= RATP_CONTROL_AN;
+
+ ratp_create_packet(ratp, &synack, control, 255);
+ ratp_send(ratp, &synack, sizeof(struct ratp_header));
+
+ ratp_state_change(ratp, RATP_STATE_SYN_RECEIVED);
+ }
+}
+
+/*
+ * This procedure represents the behavior of the SYN-SENT state
+ * and is entered when this end of the connection decides to
+ * execute an active OPEN.
+ *
+ * First, check the packet for the ACK flag. If the ACK flag is
+ * set then check to see if the AN value was as expected. If it
+ * was continue below. Otherwise the AN value was unexpected. If
+ * the RST flag was set then discard the packet and return to the
+ * current state without any further processing, else send a
+ * reset:
+ *
+ * <SN=received AN><CTL=RST>
+ *
+ * Discard the packet and return to the current state without any
+ * further processing.
+ *
+ * At this point either the ACK flag was set and the AN value was
+ * as expected or ACK was not set. Second, check the RST flag.
+ * If the RST flag is set there are two cases:
+ *
+ * . If the ACK flag is set then discard the packet, flush the
+ * retransmission queue, inform the user "Error: Connection
+ * refused", delete the TCB, and go to the CLOSED state without
+ * any further processing.
+ *
+ * 2. If the ACK flag was not set then discard the packet and
+ * return to this state without any further processing.
+ *
+ * At this point we assume the packet contained an ACK which was
+ * Ok, or there was no ACK, and there was no RST. Now check the
+ * packet for the SYN flag. If the ACK flag was set then our SYN
+ * has been acknowledged. Store MDL received in the TCB. At this
+ * point we are technically in the ESTABLISHED state. Send an
+ * acknowledgment packet and any initial data which is queued to
+ * send:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK><DATA>
+ *
+ * Go to the ESTABLISHED state without any further processing.
+ *
+ * If the SYN flag was set but the ACK was not set then the other
+ * end of the connection has executed an active open also.
+ * Acknowledge the SYN, choose your MDL, and send:
+ *
+ * <SN=0><AN=received SN+1 modulo 2><CTL=SYN, ACK><LENGTH=MDL>
+ *
+ * Go to the SYN-RECEIVED state without any further processing.
+ *
+ * Any packet not satisfying the above tests is discarded and
+ * ignored. Return to the current state without any further
+ * processing.
+ */
+static void ratp_behaviour_b(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if ((hdr->control & RATP_CONTROL_ACK) && !ratp_an_expected(ratp, hdr)) {
+ if (!(hdr->control & RATP_CONTROL_RST)) {
+ uint8_t control = RATP_CONTROL_RST;
+
+ control = RATP_CONTROL_RST |
+ ratp_set_sn(ratp_an(hdr));
+
+ ratp_send_hdr(ratp, control);
+ }
+ return;
+ }
+
+ if (hdr->control & RATP_CONTROL_RST) {
+ if (hdr->control & RATP_CONTROL_ACK) {
+ ratp->wait_ack = false;
+ ratp->status = -ECONNREFUSED;
+
+ pr_debug("Connection refused\n");
+
+ ratp_state_change(ratp, RATP_STATE_CLOSED);
+
+ }
+ return;
+ }
+
+ if (hdr->control & RATP_CONTROL_SYN) {
+ uint8_t control;
+
+ if (hdr->control & RATP_CONTROL_ACK) {
+ control = ratp_set_sn(ratp_an(hdr)) |
+ ratp_set_an(!ratp_sn(hdr)) |
+ RATP_CONTROL_ACK;
+ } else {
+ control = ratp_set_an(!ratp_sn(hdr)) |
+ RATP_CONTROL_SYN |
+ RATP_CONTROL_ACK;
+
+ }
+
+ ratp_send_hdr(ratp, control);
+ ratp_state_change(ratp, RATP_STATE_ESTABLISHED);
+ }
+}
+
+/*
+ * Examine the received SN field value. If the SN value was
+ * expected then return and continue the processing associated
+ * with this state.
+ *
+ * We now assume the SN value was not what was expected.
+ *
+ * If either RST or FIN were set discard the packet and return to
+ * the current state without any further processing.
+ *
+ * If neither RST nor FIN flags were set it is assumed that this
+ * packet is a duplicate of one already received. Send an ACK
+ * back:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
+ *
+ * Discard the duplicate packet and return to the current state
+ * without any further processing.
+ */
+static int ratp_behaviour_c1(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ int ret;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (ratp_sn_expected(ratp, hdr)) {
+ pr_vdebug("%s: sn is expected\n", __func__);
+ return 0;
+ }
+
+ if (!(hdr->control & RATP_CONTROL_RST) &&
+ !(hdr->control & RATP_CONTROL_FIN)) {
+ ret = ratp_send_ack(ratp, hdr);
+ if (ret)
+ return ret;
+ }
+
+ return 1;
+
+}
+
+/*
+ * Examine the received SN field value. If the SN value was
+ * expected then return and continue the processing associated
+ * with this state.
+ *
+ * We now assume the SN value was not what was expected.
+ *
+ * If either RST or FIN were set discard the packet and return to
+ * the current state without any further processing.
+ *
+ * If SYN was set we assume that the other end crashed and has
+ * attempted to open a new connection. We respond by sending a
+ * legal reset:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=RST, ACK>
+ *
+ * This will cause the other end, currently in the SYN-SENT state,
+ * to close. Flush the retransmission queue, inform the user
+ * "Error: Connection reset", discard the packet, delete the TCB,
+ * and go to the CLOSED state without any further processing.
+ *
+ * If neither RST, FIN, nor SYN flags were set it is assumed that
+ * this packet is a duplicate of one already received. Send an
+ * ACK back:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
+ *
+ * Discard the duplicate packet and return to the current state
+ * without any further processing.
+ */
+static int ratp_behaviour_c2(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ int ret;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (ratp_sn_expected(ratp, hdr))
+ return 0;
+
+ if ((hdr->control & RATP_CONTROL_RST) ||
+ (hdr->control & RATP_CONTROL_FIN))
+ return 1;
+
+ if (hdr->control & RATP_CONTROL_SYN) {
+ ratp->status = -ECONNRESET;
+ ratp->wait_ack = false;
+ pr_debug("Error: Connection reset\n");
+ ratp_state_change(ratp, RATP_STATE_CLOSED);
+ return 1;
+ }
+
+ if (!ratp_has_data(hdr))
+ return 1;
+
+ pr_vdebug("Sending ack for duplicate message\n");
+ ret = ratp_send_ack(ratp, hdr);
+ if (ret)
+ return ret;
+
+ return 1;
+}
+
+/*
+ * The packet is examined for a RST flag. If RST is not set then
+ * return and continue the processing associated with this state.
+ *
+ * RST is now assumed to have been set. If the connection was
+ * originally initiated from the LISTEN state (it was passively
+ * opened) then flush the retransmission queue, discard the
+ * packet, and go to the LISTEN state without any further
+ * processing.
+ *
+ * If instead the connection was initiated actively (came from the
+ * SYN-SENT state) then flush the retransmission queue, inform the
+ * user "Error: Connection refused", discard the packet, delete
+ * the TCB, and go to the CLOSED state without any further
+ * processing.
+ */
+static int ratp_behaviour_d1(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_RST))
+ return 0;
+
+ if (!(ratp->active)) {
+ ratp_state_change(ratp, RATP_STATE_LISTEN);
+ return 1;
+ }
+
+ ratp->status = -ECONNREFUSED;
+ ratp->wait_ack = false;
+ pr_debug("Error: connection refused\n");
+
+ ratp_state_change(ratp, RATP_STATE_CLOSED);
+
+ return 1;
+}
+
+/*
+ * The packet is examined for a RST flag. If RST is not set then
+ * return and continue the processing associated with this state.
+ *
+ * RST is now assumed to have been set. Any data remaining to be
+ * sent is flushed. The retransmission queue is flushed, the user
+ * is informed "Error: Connection reset.", discard the packet,
+ * delete the TCB, and go to the CLOSED state without any further
+ * processing.
+ */
+static int ratp_behaviour_d2(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_RST))
+ return 0;
+
+ ratp->status = -ECONNRESET;
+ ratp->wait_ack = false;
+ pr_debug("connection reset\n");
+
+ return 0;
+}
+
+/*
+ * The packet is examined for a RST flag. If RST is not set then
+ * return and continue the processing associated with this state.
+ *
+ * RST is now assumed to have been set. Discard the packet,
+ * delete the TCB, and go to the CLOSED state without any further
+ * processing.
+ */
+static int ratp_behaviour_d3(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_RST))
+ return 0;
+
+ ratp_state_change(ratp, RATP_STATE_CLOSED);
+
+ return 1;
+}
+
+/*
+ * Check the presence of the SYN flag. If the SYN flag is not set
+ * then return and continue the processing associated with this
+ * state.
+ *
+ * We now assume that the SYN flag was set. The presence of a SYN
+ * here is an error. Flush the retransmission queue, send a legal
+ * RST packet.
+ *
+ * If the ACK flag was set then send:
+ *
+ * <SN=received AN><CTL=RST>
+ *
+ * If the ACK flag was not set then send:
+ *
+ * <SN=0><CTL=RST>
+ *
+ * The user should receive the message "Error: Connection reset.",
+ * then delete the TCB and go to the CLOSED state without any
+ * further processing.
+ */
+static int ratp_behaviour_e(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ uint8_t control;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_SYN))
+ return 0;
+
+ ratp->status = -ECONNRESET;
+ ratp->wait_ack = false;
+
+ control = RATP_CONTROL_RST;
+
+ if (hdr->control & RATP_CONTROL_ACK)
+ control |= ratp_set_sn(ratp_an(hdr));
+
+ ratp_send_hdr(ratp, control);
+
+ pr_debug("connection reset\n");
+
+ ratp_state_change(ratp, RATP_STATE_CLOSED);
+
+ return 1;
+}
+
+/*
+ * Check the presence of the ACK flag. If ACK is not set then
+ * discard the packet and return without any further processing.
+ *
+ * We now assume that the ACK flag was set. If the AN field value
+ * was as expected then return and continue the processing
+ * associated with this state.
+ *
+ * We now assume that the ACK flag was set and that the AN field
+ * value was unexpected. If the connection was originally
+ * initiated from the LISTEN state (it was passively opened) then
+ * flush the retransmission queue, discard the packet, and send a
+ * legal RST packet:
+ *
+ * <SN=received AN><CTL=RST>
+ *
+ * Then delete the TCB and go to the LISTEN state without any
+ * further processing.
+ *
+ * Otherwise the connection was initiated actively (came from the
+ * SYN-SENT state) then inform the user "Error: Connection
+ * refused", flush the retransmission queue, discard the packet,
+ * and send a legal RST packet:
+ *
+ * <SN=received AN><CTL=RST>
+ *
+ * Then delete the TCB and go to the CLOSED state without any
+ * further processing.
+ */
+static int ratp_behaviour_f1(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ uint8_t control;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_ACK))
+ return 1;
+
+ if (ratp_an_expected(ratp, hdr))
+ return 0;
+
+ control = RATP_CONTROL_RST | ratp_set_sn(ratp_an(hdr));
+ ratp_send_hdr(ratp, control);
+
+ if (ratp->active) {
+ ratp_state_change(ratp, RATP_STATE_CLOSED);
+ ratp->status = -ECONNREFUSED;
+ ratp->wait_ack = false;
+ pr_debug("connection refused\n");
+ } else {
+ ratp_state_change(ratp, RATP_STATE_LISTEN);
+ }
+
+ return 1;
+}
+
+/*
+ * Check the presence of the ACK flag. If ACK is not set then
+ * discard the packet and return without any further processing.
+ *
+ * We now assume that the ACK flag was set. If the AN field value
+ * was as expected then flush the retransmission queue and inform
+ * the user with an "Ok" if a buffer has been entirely
+ * acknowledged. Another packet containing data may now be sent.
+ * Return and continue the processing associated with this state.
+ *
+ * We now assume that the ACK flag was set and that the AN field
+ * value was unexpected. This is assumed to indicate a duplicate
+ * acknowledgment. It is ignored, return and continue the
+ * processing associated with this state.
+ */
+static int ratp_behaviour_f2(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_ACK))
+ return 1;
+
+ if (ratp_an_expected(ratp, hdr)) {
+ ratp->status = 0;
+ ratp->wait_ack = false;
+ pr_vdebug("Data succesfully sent\n");
+ return 0;
+ } else {
+ pr_vdebug("%s: an not expected\n", __func__);
+ }
+
+ return 0;
+}
+
+/*
+ * Check the presence of the ACK flag. If ACK is not set then
+ * discard the packet and return without any further processing.
+ *
+ * We now assume that the ACK flag was set. If the AN field value
+ * was as expected then continue the processing associated with
+ * this state.
+ *
+ * We now assume that the ACK flag was set and that the AN field
+ * value was unexpected. This is ignored, return and continue
+ * with the processing associated with this state.
+ */
+static int ratp_behaviour_f3(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_ACK))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * This procedure represents the behavior of the CLOSED state of a
+ * connection. All incoming packets are discarded. If the packet
+ * had the RST flag set take no action. Otherwise it is necessary
+ * to build a RST packet. Since this end is closed the other end
+ * of the connection has incorrect data about the state of the
+ * connection and should be so informed.
+ *
+ * If the ACK flag was set then send:
+ *
+ * <SN=received AN><CTL=RST>
+ *
+ * If the ACK flag was not set then send:
+ *
+ * <SN=0><AN=received SN+1 modulo 2><CTL=RST, ACK>
+ *
+ * After sending the reset packet return to the current state
+ * without any further processing.
+ */
+static int ratp_behaviour_g(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ uint8_t control;
+
+ pr_vdebug("%s\n", __func__);
+
+ control = RATP_CONTROL_RST;
+
+ if (hdr->control & RATP_CONTROL_ACK)
+ control |= ratp_set_sn(ratp_an(hdr));
+ else
+ control = ratp_set_an(ratp_sn(hdr) + 1) | RATP_CONTROL_ACK;
+
+ ratp_send_hdr(ratp, control);
+
+ return 0;
+}
+
+/*
+ * Our SYN has been acknowledged. At this point we are
+ * technically in the ESTABLISHED state. Send any initial data
+ * which is queued to send:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK><DATA>
+ *
+ * Go to the ESTABLISHED state and execute procedure I1 to process
+ * any data which might be in this packet.
+ *
+ * Any packet not satisfying the above tests is discarded and
+ * ignored. Return to the current state without any further
+ * processing.
+ */
+static int ratp_behaviour_h1(struct ratp *ratp, void *pkt)
+{
+ pr_vdebug("%s\n", __func__);
+
+ ratp->wait_ack = false;
+ ratp_state_change(ratp, RATP_STATE_ESTABLISHED);
+
+ return 0;
+}
+
+/*
+ * Check the presence of the FIN flag. If FIN is not set then
+ * continue the processing associated with this state.
+ *
+ * We now assume that the FIN flag was set. This means the other
+ * end has decided to close the connection. Flush the
+ * retransmission queue. If any data remains to be sent then
+ * inform the user "Warning: Data left unsent." The user must
+ * also be informed "Connection closing." An acknowledgment for
+ * the FIN must be sent which also indicates this end is closing:
+ *
+ * <SN=received AN><AN=received SN + 1 modulo 2><CTL=FIN, ACK>
+ *
+ * Go to the LAST-ACK state without any further processing.
+ */
+static int ratp_behaviour_h2(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ uint8_t control;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_FIN))
+ return 0;
+
+ ratp->status = -ENETDOWN;
+ ratp->wait_ack = false;
+
+ control = ratp_set_sn(ratp_an(hdr)) |
+ ratp_set_an(ratp_sn(hdr) + 1) |
+ RATP_CONTROL_FIN |
+ RATP_CONTROL_ACK;
+
+ ratp_send_hdr(ratp, control);
+
+ ratp_state_change(ratp, RATP_STATE_LAST_ACK);
+
+ return 1;
+}
+
+/*
+ * This state represents the final behavior of the FIN-WAIT state.
+ *
+ * If the packet did not contain a FIN we assume this packet is a
+ * duplicate and that the other end of the connection has not seen
+ * the FIN packet we sent earlier. Rely upon retransmission of
+ * our earlier FIN packet to inform the other end of our desire to
+ * close. Discard the packet and return without any further
+ * processing.
+ *
+ * At this point we have a packet which should contain a FIN. By
+ * the rules of this protocol an ACK of a FIN requires a FIN, ACK
+ * in response and no data. If the packet contains data we have
+ * detected an illegal condition. Send a reset:
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=RST, ACK>
+ *
+ * Discard the packet, flush the retransmission queue, inform the
+ * ser "Error: Connection reset.", delete the TCB, and go to the
+ * CLOSED state without any further processing.
+ *
+ * We now assume that the FIN flag was set and no data was
+ * contained in the packet. If the AN field value was expected
+ * then this packet acknowledges a previously sent FIN packet.
+ * The other end of the connection is then also assumed to be
+ * closing and expects an acknowledgment. Send an acknowledgment
+ * of the FIN:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
+ *
+ * Start the 2*SRTT timer associated with the TIME-WAIT state,
+ * discard the packet, and go to the TIME-WAIT state without any
+ * further processing.
+ *
+ * Otherwise the AN field value was unexpected. This indicates a
+ * simultaneous closing by both sides of the connection. Send an
+ * acknowledgment of the FIN:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
+ *
+ * Discard the packet, and go to the CLOSING state without any
+ * further processing.
+ */
+static int ratp_behaviour_h3(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ uint8_t control;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_FIN))
+ return 1;
+
+ if (ratp_has_data(hdr)) {
+ control = ratp_set_sn(ratp_an(hdr)) |
+ ratp_set_an(ratp_sn(hdr) + 1) |
+ RATP_CONTROL_RST |
+ RATP_CONTROL_ACK;
+ ratp_send_hdr(ratp, control);
+ ratp->status = -ECONNRESET;
+ ratp->wait_ack = false;
+ pr_debug("Error: Connection reset\n");
+ ratp_state_change(ratp, RATP_STATE_CLOSED);
+ return 1;
+ }
+
+ control = ratp_set_sn(ratp_an(hdr)) |
+ ratp_set_an(ratp_sn(hdr) + 1) |
+ RATP_CONTROL_ACK;
+ ratp_send_hdr(ratp, control);
+
+ if (ratp_an_expected(ratp, hdr)) {
+ ratp_state_change(ratp, RATP_STATE_TIME_WAIT);
+ ratp_start_time_wait_timer(ratp);
+ } else {
+ ratp_state_change(ratp, RATP_STATE_CLOSING);
+ }
+
+ return 1;
+}
+
+/*
+ * This state represents the final behavior of the LAST-ACK state.
+ *
+ * If the AN field value is expected then this ACK is in response
+ * to the FIN, ACK packet recently sent. This is the final
+ * acknowledging message indicating both side's agreement to close
+ * the connection. Discard the packet, flush all queues, delete
+ * the TCB, and go to the CLOSED state without any further
+ * processing.
+ *
+ * Otherwise the AN field value was unexpected. Discard the
+ * packet and remain in the current state without any further
+ * processing.
+ */
+static int ratp_behaviour_h4(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (ratp_an_expected(ratp, hdr))
+ ratp_state_change(ratp, RATP_STATE_CLOSED);
+
+ return 1;
+}
+
+/*
+ * This state represents the final behavior of the CLOSING state.
+ *
+ * If the AN field value was expected then this packet
+ * acknowledges the FIN packet recently sent. This is the final
+ * acknowledging message indicating both side's agreement to close
+ * the connection. Start the 2*SRTT timer associated with the
+ * TIME-WAIT state, discard the packet, and go to the TIME-WAIT
+ * state without any further processing.
+ *
+ * Otherwise the AN field value was unexpected. Discard the
+ * packet and remain in the current state without any further
+ * processing.
+ */
+static int ratp_behaviour_h5(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (ratp_an_expected(ratp, hdr)) {
+ ratp_state_change(ratp, RATP_STATE_TIME_WAIT);
+ ratp_start_time_wait_timer(ratp);
+ }
+
+ return 0;
+}
+
+/*
+ * This state represents the behavior of the TIME-WAIT state.
+ * Check the presence of the ACK flag. If ACK is not set then
+ * discard the packet and return without any further processing.
+ *
+ * Check the presence of the FIN flag. If FIN is not set then
+ * discard the packet and return without any further processing.
+ *
+ * We now assume that the FIN flag was set. This situation
+ * indicates that the last acknowledgment of the FIN packet sent
+ * by the other end of the connection did not arrive. Resend the
+ * acknowledgment:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
+ *
+ * Restart the 2*SRTT timer, discard the packet, and remain in the
+ * current state without any further processing.
+ */
+static int ratp_behaviour_h6(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ uint8_t control;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (!(hdr->control & RATP_CONTROL_ACK))
+ return 1;
+
+ if (!(hdr->control & RATP_CONTROL_FIN))
+ return 1;
+
+ control = ratp_set_sn(ratp_an(hdr) + 1) | RATP_CONTROL_ACK;
+
+ ratp_send_hdr(ratp, control);
+
+ ratp_start_time_wait_timer(ratp);
+
+ return 0;
+}
+
+static int msg_recv(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ struct ratp_message *msg;
+
+ pr_debug("%s: Put msg in receive queue\n", __func__);
+
+ msg = xzalloc(sizeof(*msg));
+ if (hdr->data_length) {
+ msg->len = hdr->data_length;
+ msg->buf = xzalloc(msg->len);
+ memcpy(msg->buf, pkt + sizeof(struct ratp_header), msg->len);
+ } else {
+ msg->len = 1;
+ msg->buf = xzalloc(1);
+ *(uint8_t *)msg->buf = hdr->cksum;
+ }
+
+ list_add_tail(&msg->list, &ratp->recvmsg);
+
+ return 0;
+}
+
+/*
+ * This represents that stage of processing in the ESTABLISHED
+ * state in which all the flag bits have been processed and only
+ * data may remain. The packet is examined to see if it contains
+ * data. If not the packet is now discarded, return to the
+ * current state without any further processing.
+ *
+ * We assume the packet contained data, that either the SO flag
+ * was set or LENGTH is positive. That data is placed into the
+ * user's receive buffers. As these become full the user should
+ * be informed "Receive buffer full." An acknowledgment is sent:
+ *
+ * <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
+ *
+ * If data is queued to send then it is most efficient to
+ * 'piggyback' this acknowledgment on that data packet.
+ *
+ * The packet is now discarded, return to the ESTABLISHED state
+ * without any further processing.
+ */
+static int ratp_behaviour_i1(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ uint8_t control = 0;
+
+ if (!hdr->data_length && !(hdr->control & RATP_CONTROL_SO))
+ return 1;
+
+ pr_vdebug("%s **received** %d\n", __func__, hdr->data_length);
+
+ if (hdr->control & RATP_CONTROL_AN)
+ control |= RATP_CONTROL_SN;
+
+ if (!(hdr->control & RATP_CONTROL_SN))
+ control |= RATP_CONTROL_AN;
+
+ control |= RATP_CONTROL_ACK;
+
+ ratp_send_hdr(ratp, control);
+
+ msg_recv(ratp, pkt);
+
+ return 0;
+}
+
+/*
+ * STATE BEHAVIOR
+ * =============+========================
+ * LISTEN | A
+ * -------------+------------------------
+ * SYN-SENT | B
+ * -------------+------------------------
+ * SYN-RECEIVED | C1 D1 E F1 H1
+ * -------------+------------------------
+ * ESTABLISHED | C2 D2 E F2 H2 I1
+ * -------------+------------------------
+ * FIN-WAIT | C2 D2 E F3 H3
+ * -------------+------------------------
+ * LAST-ACK | C2 D3 E F3 H4
+ * -------------+------------------------
+ * CLOSING | C2 D3 E F3 H5
+ * -------------+------------------------
+ * TIME-WAIT | D3 E F3 H6
+ * -------------+------------------------
+ * CLOSED | G
+ * -------------+------------------------
+ */
+
+static int __ratp_state_machine(struct ratp *ratp, void *pkt)
+{
+ struct ratp_header *hdr = pkt;
+ int ret;
+
+ ratp_print_header(ratp, hdr, "recv");
+ pr_debug(" state %s\n", ratp_state_str[ratp->state]);
+
+ switch (ratp->state) {
+ case RATP_STATE_LISTEN:
+ ratp_behaviour_a(ratp, pkt);
+ break;
+ case RATP_STATE_SYN_SENT:
+ ratp_behaviour_b(ratp, pkt);
+ break;
+ case RATP_STATE_SYN_RECEIVED:
+ ret = ratp_behaviour_c1(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_d1(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_e(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_f1(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_h1(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_i1(ratp, pkt);
+ if (ret)
+ return ret;
+ break;
+ case RATP_STATE_ESTABLISHED:
+ ret = ratp_behaviour_c2(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_d2(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_e(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_f2(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_h2(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_i1(ratp, pkt);
+ if (ret)
+ return ret;
+ break;
+ case RATP_STATE_FIN_WAIT:
+ ret = ratp_behaviour_c2(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_d2(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_e(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_f3(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_h3(ratp, pkt);
+ if (ret)
+ return ret;
+ break;
+ case RATP_STATE_LAST_ACK:
+ ret = ratp_behaviour_c2(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_d3(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_e(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_f3(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_h4(ratp, pkt);
+ if (ret)
+ return ret;
+ break;
+ case RATP_STATE_CLOSING:
+ ret = ratp_behaviour_c2(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_d3(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_e(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_f3(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_h5(ratp, pkt);
+ if (ret)
+ return ret;
+ break;
+ case RATP_STATE_TIME_WAIT:
+ ret = ratp_behaviour_d3(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_e(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_f3(ratp, pkt);
+ if (ret)
+ return ret;
+ ret = ratp_behaviour_h6(ratp, pkt);
+ if (ret)
+ return ret;
+ break;
+ case RATP_STATE_CLOSED:
+ ratp_behaviour_g(ratp, pkt);
+ break;
+ };
+
+ return 0;
+}
+
+/**
+ * ratp_closed() - Check if a connection is closed
+ *
+ * Return: true if a connection is closed, false otherwise
+ */
+bool ratp_closed(struct ratp *ratp)
+{
+ return ratp->state == RATP_STATE_CLOSED;
+}
+
+/**
+ * ratp_busy() - Check if we are inside the RATP code
+ *
+ * Needed for RATP debugging. The RATP console uses this to determine
+ * if it is called from inside the RATP code.
+ *
+ * Return: true if we are inside the RATP code, false otherwise
+ */
+bool ratp_busy(struct ratp *ratp)
+{
+ return ratp->in_ratp != 0;
+}
+
+/**
+ * ratp_poll() - Execute RATP state machine
+ * @ratp: The RATP link
+ *
+ * This function should be executed periodically to keep the RATP state
+ * machine going.
+ *
+ * Return: 0 if successful, a negative error code otherwise.
+ */
+int ratp_poll(struct ratp *ratp)
+{
+ int ret;
+
+ ratp->in_ratp++;
+
+ ret = ratp_recv(ratp, ratp->recvbuf, 500);
+ if (ret < 0)
+ goto out;
+
+ if (ratp->state == RATP_STATE_TIME_WAIT &&
+ is_timeout(ratp->timewait_timer_start, ratp->srtt * 2 * MSECOND)) {
+ pr_debug("2*SRTT timer timed out\n");
+ ret = -ECONNRESET;
+ goto out;
+ }
+
+ ret = __ratp_state_machine(ratp, ratp->recvbuf);
+ if (ret < 0)
+ goto out;
+
+ ratp->sn_received = ratp_sn(ratp->recvbuf);
+
+ if (ratp->wait_ack && is_timeout(ratp->retransmission_timer_start, ratp->rto * MSECOND)) {
+ ratp->retransmission_count++;
+ if (ratp->retransmission_count == ratp->max_retransmission) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ pr_debug("%s: retransmit\n", __func__);
+
+ ratp_print_header(ratp, ratp->sendbuf, "resend");
+
+ ret = ratp->send(ratp, ratp->sendbuf, ratp->sendbuf_len);
+ if (ret)
+ goto out;
+ }
+
+ ret = 0;
+out:
+ ratp->in_ratp--;
+
+ return ret;
+}
+
+/**
+ * ratp_establish(): Establish a RATP link
+ * @ratp: The RATP link
+ * @active: if true actively create a connection
+ * @timeout_ms: Timeout in ms to wait until a connection is established. If
+ * 0 wait forever.
+ *
+ * This function establishes a link with the remote end. It expects the
+ * send and receive functions to be set, all other struct ratp members can
+ * be left uninitialized.
+ *
+ * Return: 0 if successful, a negative error code otherwise.
+ */
+int ratp_establish(struct ratp *ratp, bool active, int timeout_ms)
+{
+ int ret;
+ uint64_t start;
+
+ ratp->sendbuf = xmalloc(512);
+ ratp->recvbuf = xmalloc(512);
+ INIT_LIST_HEAD(&ratp->recvmsg);
+ ratp->max_retransmission = 100;
+ ratp->srtt = 100;
+ ratp->rto = 100;
+ ratp->active = active;
+
+ ratp->in_ratp++;
+
+ if (ratp->active) {
+ ratp_send_hdr(ratp, RATP_CONTROL_SYN);
+
+ ratp_state_change(ratp, RATP_STATE_SYN_SENT);
+ }
+
+ start = get_time_ns();
+
+ while (1) {
+ ret = ratp_poll(ratp);
+ if (ret < 0)
+ goto out;
+
+ if (ratp->state == RATP_STATE_ESTABLISHED) {
+ ret = 0;
+ goto out;
+ }
+
+ if (timeout_ms && is_timeout(start, MSECOND * timeout_ms)) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ }
+
+out:
+ ratp->in_ratp--;
+
+ return ret;
+}
+
+/**
+ * ratp_send_data(): Send data over a RATP link
+ * @ratp: The RATP link
+ * @data: The data buffer
+ * @len: The length of the message to send
+ *
+ * This function sends data over a RATP link. This function blocks until
+ * the message has been send or an error occured.
+ *
+ * Return: 0 if successful, a negative error code otherwise.
+ */
+int ratp_send_data(struct ratp *ratp, const void *data, size_t len)
+{
+ uint16_t crc;
+ uint8_t control = RATP_CONTROL_ACK;
+ int ret;
+ uint64_t start_rtt;
+ struct ratp_header *hdr;
+ void *buf;
+ int alpha, beta, rtt;
+ void *pkt;
+ int pktlen;
+
+ ratp->in_ratp++;
+
+ pr_vdebug("%s\n", __func__);
+
+ if (ratp->status) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (!len) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ control = ratp_set_sn(ratp->an_received) |
+ ratp_set_an(ratp->sn_received + 1) |
+ RATP_CONTROL_ACK;
+
+ if (len > 1) {
+ pktlen = sizeof(struct ratp_header) + len + 2;
+ pkt = xzalloc(pktlen);
+ hdr = pkt;
+ buf = pkt + sizeof(struct ratp_header);
+ memcpy(buf, data, len);
+ crc = cyg_crc16(data, len);
+ put_unaligned_be16(crc, buf + len);
+ } else {
+ pktlen = sizeof(struct ratp_header);
+ pkt = xzalloc(pktlen);
+ hdr = pkt;
+ control |= RATP_CONTROL_SO;
+ len = 0;
+ }
+
+ ratp_create_packet(ratp, hdr, control, len);
+
+ ratp->status = -EINPROGRESS;
+ ratp->wait_ack = false;
+ ratp->retransmission_count = 0;
+
+ start_rtt = get_time_ns();
+
+ ratp_send(ratp, pkt, pktlen);
+
+ while (ratp->status == -EINPROGRESS) {
+ ret = ratp_poll(ratp);
+ if (ret == -EINTR)
+ goto out;
+ }
+
+ rtt = (unsigned long)(get_time_ns() - start_rtt) / MSECOND;
+
+ alpha = 8;
+ beta = 15;
+
+ ratp->srtt = (alpha * ratp->srtt + (10 - alpha) * rtt) / 10;
+ ratp->rto = max(100, beta * ratp->srtt / 10);
+
+ pr_debug("%s: done. SRTT: %dms RTO: %dms status: %d\n",
+ __func__, ratp->srtt, ratp->rto, ratp->status);
+
+ ret = ratp->status;
+
+out:
+ ratp->in_ratp--;
+
+ return ret;
+}
+
+/**
+ * ratp_recv_data() - Receive data from a RATP link
+ * @ratp: The RATP link
+ * @data: Pointer to buffer to be filled with data
+ * @len: On entry the length of the buffer, on exit the number of
+ * bytes received.
+ *
+ * If a message is available this function fills the buffer with the data.
+ * This function does not wait for new messages. If no data is available
+ * -EAGAIN is returned.
+ *
+ * Return: 0 if successful, a negative error code otherwise.
+ */
+int ratp_recv_data(struct ratp *ratp, void *data, size_t *retlen)
+{
+ struct ratp_message *msg;
+
+ if (list_empty(&ratp->recvmsg))
+ return -EAGAIN;
+
+ msg = list_first_entry(&ratp->recvmsg, struct ratp_message, list);
+ *retlen = min(*retlen, msg->len);
+ memcpy(data, msg->buf, msg->len);
+
+ free(msg->buf);
+ list_del(&msg->list);
+ free(msg);
+
+ return 0;
+}
--
2.1.4
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
next prev parent reply other threads:[~2015-06-11 6:54 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-06-11 6:54 Add barebox remote control Sascha Hauer
2015-06-11 6:54 ` Sascha Hauer [this message]
2015-06-12 12:05 ` [PATCH 1/4] Add Reliable Asynchronous Transfer Protocol Peter Korsgaard
2015-06-15 5:12 ` Sascha Hauer
2015-06-15 7:19 ` Peter Korsgaard
2015-06-17 5:45 ` Sascha Hauer
2015-06-11 6:54 ` [PATCH 2/4] barebox remote control Sascha Hauer
2015-06-12 10:36 ` Peter Korsgaard
2015-06-15 4:51 ` Sascha Hauer
2015-06-11 6:54 ` [PATCH 3/4] include pyserial trunk Sascha Hauer
2015-06-11 15:48 ` Uwe Kleine-König
2015-06-11 6:54 ` [PATCH 4/4] host side for barebox remote control Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1434005650-28131-2-git-send-email-s.hauer@pengutronix.de \
--to=s.hauer@pengutronix.de \
--cc=barebox@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox