mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Andrey Smirnov <andrew.smirnov@gmail.com>
To: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Barebox List <barebox@lists.infradead.org>
Subject: Re: [PATCH 1/8] Add Reliable Asynchronous Transfer Protocol
Date: Sun, 17 Jan 2016 17:09:04 -0800	[thread overview]
Message-ID: <CAHQ1cqHrDCYi-GpagemTY4aWgyA7ybuMv3oz7sJfUN77mwbYbg@mail.gmail.com> (raw)
In-Reply-To: <1452251635-14689-2-git-send-email-s.hauer@pengutronix.de>

On Fri, Jan 8, 2016 at 3:13 AM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 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>

Tested-by: Andrey Smirnov <andrew.smirnov@gmail.com>

> ---
>  include/ratp.h |   22 +
>  lib/Kconfig    |    8 +
>  lib/Makefile   |    1 +
>  lib/ratp.c     | 1834 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 1865 insertions(+)
>  create mode 100644 include/ratp.h
>  create mode 100644 lib/ratp.c
>
> diff --git a/include/ratp.h b/include/ratp.h
> new file mode 100644
> index 0000000..b91d305
> --- /dev/null
> +++ b/include/ratp.h
> @@ -0,0 +1,22 @@
> +#ifndef __RATP_H
> +#define __RATP_H
> +
> +struct ratp {
> +       struct ratp_internal *internal;
> +       int (*send)(struct ratp *, void *pkt, int len);
> +       int (*recv)(struct ratp *, uint8_t *data);
> +};
> +
> +int ratp_establish(struct ratp *ratp, bool active, int timeout_ms);
> +void ratp_close(struct ratp *ratp);
> +int ratp_recv(struct ratp *ratp, void **data, size_t *len);
> +int ratp_send(struct ratp *ratp, const void *data, size_t len);
> +int ratp_send_complete(struct ratp *ratp, const void *data, size_t len,
> +                  void (*complete)(void *ctx, int status), void *complete_ctx);
> +int ratp_poll(struct ratp *ratp);
> +bool ratp_closed(struct ratp *ratp);
> +bool ratp_busy(struct ratp *ratp);
> +
> +void ratp_run_command(void);
> +
> +#endif /* __RATP_H */
> diff --git a/lib/Kconfig b/lib/Kconfig
> index fbf9f0f..a7e067e 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/fonts/Kconfig
> diff --git a/lib/Makefile b/lib/Makefile
> index abb34cf..0694721 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -56,3 +56,4 @@ obj-y                 += gcd.o
>  obj-y                  += hexdump.o
>  obj-$(CONFIG_FONTS)    += fonts/
>  obj-$(CONFIG_BAREBOX_LOGO)     += logo/
> +obj-$(CONFIG_RATP)     += ratp.o
> diff --git a/lib/ratp.c b/lib/ratp.c
> new file mode 100644
> index 0000000..d596a0e
> --- /dev/null
> +++ b/lib/ratp.c
> @@ -0,0 +1,1834 @@
> +/*
> + * 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        |
> + *        |                               |
> + *        +-------------------------------+
> + *
> + */
> +
> +struct ratp_header {
> +       uint8_t synch;
> +       uint8_t control;
> +       uint8_t data_length;
> +       uint8_t cksum;
> +};
> +
> +#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)
> +
> +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;
> +       void (*complete)(void *ctx, int status);
> +       void *complete_ctx;
> +       int eor;
> +};
> +
> +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",
> +};
> +
> +struct ratp_internal {
> +       struct ratp *ratp;
> +
> +       enum ratp_state state;
> +       int sn_sent;
> +       int sn_received;
> +       int active;
> +
> +       void *recvbuf;
> +       void *sendbuf;
> +       int sendbuf_len;
> +
> +       struct list_head recvmsg;
> +       struct list_head sendmsg;
> +
> +       struct ratp_message *sendmsg_current;
> +
> +       uint64_t timewait_timer_start;
> +       uint64_t retransmission_timer_start;
> +       int max_retransmission;
> +       int retransmission_count;
> +       int srtt;
> +       int rto;
> +
> +       int status;
> +
> +       int in_ratp;
> +};
> +
> +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_internal *ri, 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_internal *ri, 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);
> +
> +#ifdef VERBOSE_DEBUG
> +       if (hdr->data_length)
> +               memory_display(hdr + 1, 0, hdr->data_length, 1, 0);
> +#endif
> +}
> +
> +static void ratp_create_packet(struct ratp_internal *ri, 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_internal *ri, enum ratp_state state)
> +{
> +       pr_debug("state %-10s -> %-10s\n", ratp_state_str[ri->state],
> +                       ratp_state_str[state]);
> +
> +       ri->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_pkt(struct ratp_internal *ri, void *pkt, int length)
> +{
> +       struct ratp_header *hdr = (void *)pkt;
> +
> +       ratp_print_header(ri, hdr, "send");
> +
> +       if (ratp_has_data(hdr) ||
> +           (hdr->control & (RATP_CONTROL_SYN | RATP_CONTROL_RST | RATP_CONTROL_FIN))) {
> +               memcpy(ri->sendbuf, pkt, length);
> +               ri->sn_sent = ratp_sn(hdr);
> +               ri->sendbuf_len = length;
> +               ri->retransmission_timer_start = get_time_ns();
> +               ri->retransmission_count = 0;
> +       }
> +
> +       return ri->ratp->send(ri->ratp, pkt, length);
> +}
> +
> +static int ratp_send_hdr(struct ratp_internal *ri, uint8_t control)
> +{
> +       struct ratp_header hdr = {};
> +
> +       ratp_create_packet(ri, &hdr, control, 0);
> +
> +       return ratp_send_pkt(ri, &hdr, sizeof(hdr));
> +}
> +
> +static int ratp_recv_char(struct ratp_internal *ri, uint8_t *data, int poll_timeout_ms)
> +{
> +       uint64_t start;
> +       int ret;
> +
> +       start = get_time_ns();
> +
> +       while (1) {
> +               ret = ri->ratp->recv(ri->ratp, data);
> +               if (ret < 0 && ret != -EAGAIN)
> +                       return ret;
> +
> +               if (ret == 0)
> +                       return 0;
> +
> +               if (is_timeout(start, poll_timeout_ms * MSECOND))
> +                       return -EAGAIN;
> +       }
> +}
> +
> +static int ratp_recv_pkt_header(struct ratp_internal *ri, struct ratp_header *hdr,
> +               int poll_timeout_ms)
> +{
> +       int ret;
> +       uint8_t buf;
> +
> +       do {
> +               ret = ratp_recv_char(ri, &buf, 0);
> +               if (ret < 0)
> +                       return ret;
> +               hdr->synch = buf;
> +       } while (hdr->synch != 1);
> +       ret = ratp_recv_char(ri, &buf, poll_timeout_ms);
> +       if (ret < 0)
> +               return ret;
> +
> +       hdr->control = buf;
> +       ret = ratp_recv_char(ri, &buf, poll_timeout_ms);
> +       if (ret < 0)
> +               return ret;
> +
> +       hdr->data_length = buf;
> +
> +       ret = ratp_recv_char(ri, &buf, poll_timeout_ms);
> +       if (ret < 0)
> +               return ret;
> +
> +       hdr->cksum = buf;
> +
> +       if (!ratp_header_ok(ri, hdr))
> +               return -EAGAIN;
> +
> +       return 0;
> +}
> +
> +static int ratp_recv_pkt_data(struct ratp_internal *ri, 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_recv_char(ri, 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_pkt(struct ratp_internal *ri, void *pkt, int poll_timeout_ms)
> +{
> +       struct ratp_header *hdr = pkt;
> +       void *data = pkt + sizeof(struct ratp_header);
> +       int ret;
> +
> +       ret = ratp_recv_pkt_header(ri, hdr, poll_timeout_ms);
> +       if (ret < 0)
> +               return ret;
> +
> +       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(ri, data, hdr->data_length,
> +                               poll_timeout_ms);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static bool ratp_an_expected(struct ratp_internal *ri, struct ratp_header *hdr)
> +{
> +       return ratp_an(hdr) == (ri->sn_sent + 1) % 2;
> +}
> +
> +static bool ratp_sn_expected(struct ratp_internal *ri, struct ratp_header *hdr)
> +{
> +       return ratp_sn(hdr) != ri->sn_received;
> +}
> +
> +static int ratp_send_ack(struct ratp_internal *ri, struct ratp_header *hdr)
> +{
> +       uint8_t control = RATP_CONTROL_ACK;
> +       int ret;
> +
> +       if (hdr->control & RATP_CONTROL_SN)
> +               control |= RATP_CONTROL_AN;
> +       else
> +               control |= 0;
> +
> +       ret = ratp_send_hdr(ri, control);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +static int ratp_send_next_data(struct ratp_internal *ri)
> +{
> +       uint16_t crc;
> +       uint8_t control = RATP_CONTROL_ACK;
> +       struct ratp_header *hdr;
> +       int pktlen;
> +       struct ratp_message *msg;
> +       int len;
> +
> +       if (ri->sendmsg_current) {
> +               pr_err("%s: busy\n", __func__);
> +               return -EBUSY;
> +       }
> +
> +       if (list_empty(&ri->sendmsg))
> +               return 0;
> +
> +       msg = list_first_entry(&ri->sendmsg, struct ratp_message, list);
> +
> +       ri->sendmsg_current = msg;
> +
> +       list_del(&msg->list);
> +
> +       len = msg->len;
> +
> +       control = ratp_set_sn(ri->sn_sent + 1) |
> +               ratp_set_an(ri->sn_received + 1) |
> +               RATP_CONTROL_ACK;
> +
> +       hdr = msg->buf;
> +
> +       if (msg->eor)
> +               control |= RATP_CONTROL_EOR;
> +
> +       if (len > 1) {
> +               void *data = hdr + 1;
> +               pktlen = sizeof(*hdr) + len + 2;
> +               crc = cyg_crc16(data, len);
> +               put_unaligned_be16(crc, data + len);
> +       } else {
> +               pktlen = sizeof(struct ratp_header);
> +               control |= RATP_CONTROL_SO;
> +               len = 0;
> +       }
> +
> +       ratp_create_packet(ri, hdr, control, len);
> +
> +       ri->retransmission_count = 0;
> +
> +       ratp_send_pkt(ri, msg->buf, pktlen);
> +
> +       return 0;
> +}
> +
> +static void ratp_start_time_wait_timer(struct ratp_internal *ri)
> +{
> +       ri->timewait_timer_start = get_time_ns();
> +}
> +
> +static void ratp_msg_done(struct ratp_internal *ri, struct ratp_message *msg, int status)
> +{
> +       int alpha, beta, rtt;
> +
> +       if (!status) {
> +               rtt = (unsigned long)(get_time_ns() - ri->retransmission_timer_start) / MSECOND;
> +
> +               alpha = 8;
> +               beta = 15;
> +
> +               ri->srtt = (alpha * ri->srtt + (10 - alpha) * rtt) / 10;
> +               ri->rto = max(200, beta * ri->srtt / 10);
> +
> +               pr_debug("%s: done. SRTT: %dms RTO: %dms status: %d\n",
> +                       __func__, ri->srtt, ri->rto, ri->status);
> +       }
> +
> +       if (msg->complete)
> +               msg->complete(msg->complete_ctx, status);
> +
> +       free(msg->buf);
> +       free(msg);
> +}
> +
> +/*
> + * 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%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(ri, 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(ri, &synack, control, 255);
> +               ratp_send_pkt(ri, &synack, sizeof(synack));
> +
> +               ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if ((hdr->control & RATP_CONTROL_ACK) && !ratp_an_expected(ri, 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(ri, control);
> +               }
> +               return;
> +       }
> +
> +       if (hdr->control & RATP_CONTROL_RST) {
> +               if (hdr->control & RATP_CONTROL_ACK) {
> +                       ri->status = -ECONNREFUSED;
> +
> +                       pr_debug("Connection refused\n");
> +
> +                       ratp_state_change(ri, 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;
> +
> +               }
> +
> +               ri->sn_received = ratp_sn(hdr);
> +
> +               ratp_send_hdr(ri, control);
> +               ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       int ret;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (ratp_sn_expected(ri, 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(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       int ret;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (!ratp_has_data(hdr))
> +               return 0;
> +
> +       if (ratp_sn_expected(ri, hdr))
> +               return 0;
> +
> +       if ((hdr->control & RATP_CONTROL_RST) ||
> +                       (hdr->control & RATP_CONTROL_FIN))
> +               return 1;
> +
> +       if (hdr->control & RATP_CONTROL_SYN) {
> +               ri->status = -ECONNRESET;
> +               pr_debug("Error: Connection reset\n");
> +               ratp_state_change(ri, RATP_STATE_CLOSED);
> +               return 1;
> +       }
> +
> +       if (!ratp_has_data(hdr))
> +               return 1;
> +
> +       pr_debug("Sending ack for duplicate message\n");
> +       ret = ratp_send_ack(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (!(hdr->control & RATP_CONTROL_RST))
> +               return 0;
> +
> +       if (!(ri->active)) {
> +               ratp_state_change(ri, RATP_STATE_LISTEN);
> +               return 1;
> +       }
> +
> +       ri->status = -ECONNREFUSED;
> +
> +       pr_debug("Error: connection refused\n");
> +
> +       ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (!(hdr->control & RATP_CONTROL_RST))
> +               return 0;
> +
> +       ri->status = -ECONNRESET;
> +
> +       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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (!(hdr->control & RATP_CONTROL_RST))
> +               return 0;
> +
> +       ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       uint8_t control;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (!(hdr->control & RATP_CONTROL_SYN))
> +               return 0;
> +
> +       ri->status = -ECONNRESET;
> +
> +       control = RATP_CONTROL_RST;
> +
> +       if (hdr->control & RATP_CONTROL_ACK)
> +               control |= ratp_set_sn(ratp_an(hdr));
> +
> +       ratp_send_hdr(ri, control);
> +
> +       pr_debug("connection reset\n");
> +
> +       ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       uint8_t control;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (!(hdr->control & RATP_CONTROL_ACK))
> +               return 1;
> +
> +       if (ratp_an_expected(ri, hdr))
> +               return 0;
> +
> +       control = RATP_CONTROL_RST | ratp_set_sn(ratp_an(hdr));
> +       ratp_send_hdr(ri, control);
> +
> +       if (ri->active) {
> +               ratp_state_change(ri, RATP_STATE_CLOSED);
> +               ri->status = -ECONNREFUSED;
> +
> +               pr_debug("connection refused\n");
> +       } else {
> +               ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (!(hdr->control & RATP_CONTROL_ACK))
> +               return 1;
> +
> +       if (ratp_an_expected(ri, hdr)) {
> +               pr_debug("Data succesfully sent\n");
> +               if (ri->sendmsg_current)
> +                       ratp_msg_done(ri, ri->sendmsg_current, 0);
> +               ri->sendmsg_current = NULL;
> +               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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       uint8_t control;
> +
> +       pr_debug("%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(ri, 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_internal *ri, void *pkt)
> +{
> +       pr_debug("%s\n", __func__);
> +
> +       ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       uint8_t control;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (!(hdr->control & RATP_CONTROL_FIN))
> +               return 0;
> +
> +       ri->status = -ENETDOWN;
> +
> +       control = ratp_set_sn(ratp_an(hdr)) |
> +               ratp_set_an(ratp_sn(hdr) + 1) |
> +               RATP_CONTROL_FIN |
> +               RATP_CONTROL_ACK;
> +
> +       ratp_send_hdr(ri, control);
> +
> +       ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       uint8_t control;
> +       int expected;
> +
> +       pr_debug("%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(ri, control);
> +               ri->status = -ECONNRESET;
> +               pr_debug("Error: Connection reset\n");
> +               ratp_state_change(ri, RATP_STATE_CLOSED);
> +               return 1;
> +       }
> +
> +       control = ratp_set_sn(ratp_an(hdr)) |
> +               ratp_set_an(ratp_sn(hdr) + 1) |
> +               RATP_CONTROL_ACK;
> +
> +       expected = ratp_an_expected(ri, hdr);
> +
> +       ratp_send_hdr(ri, control);
> +
> +       if (expected) {
> +               ratp_state_change(ri, RATP_STATE_TIME_WAIT);
> +               ratp_start_time_wait_timer(ri);
> +       } else {
> +               ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (ratp_an_expected(ri, hdr))
> +               ratp_state_change(ri, 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +
> +       pr_debug("%s\n", __func__);
> +
> +       if (ratp_an_expected(ri, hdr)) {
> +               ratp_state_change(ri, RATP_STATE_TIME_WAIT);
> +               ratp_start_time_wait_timer(ri);
> +       }
> +
> +       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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       uint8_t control;
> +
> +       pr_debug("%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(ri, control);
> +
> +       ratp_start_time_wait_timer(ri);
> +
> +       return 0;
> +}
> +
> +static int msg_recv(struct ratp_internal *ri, 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->data_length;
> +       }
> +
> +       if (hdr->control & RATP_CONTROL_EOR)
> +               msg->eor = 1;
> +
> +       list_add_tail(&msg->list, &ri->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_internal *ri, 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);
> +
> +       ri->sn_received = ratp_sn(hdr);
> +
> +       msg_recv(ri, pkt);
> +
> +       if (list_empty(&ri->sendmsg) || ri->sendmsg_current) {
> +               control = ratp_set_sn(!ri->sn_sent) |
> +                       ratp_set_an(ri->sn_received + 1) |
> +                       RATP_CONTROL_ACK;
> +
> +               ratp_send_hdr(ri, control);
> +       } else {
> +               ratp_send_next_data(ri);
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * State machine as desribed in RFC916
> + *
> + * 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_internal *ri, void *pkt)
> +{
> +       struct ratp_header *hdr = pkt;
> +       int ret;
> +
> +       ratp_print_header(ri, hdr, "                      recv");
> +       pr_debug(" state %s\n", ratp_state_str[ri->state]);
> +
> +       switch (ri->state) {
> +       case RATP_STATE_LISTEN:
> +               ratp_behaviour_a(ri, pkt);
> +               break;
> +       case RATP_STATE_SYN_SENT:
> +               ratp_behaviour_b(ri, pkt);
> +               break;
> +       case RATP_STATE_SYN_RECEIVED:
> +               ret = ratp_behaviour_c1(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_d1(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_e(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_f1(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_h1(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               break;
> +       case RATP_STATE_ESTABLISHED:
> +               ret = ratp_behaviour_c2(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_d2(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_e(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_f2(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_h2(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_i1(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               break;
> +       case RATP_STATE_FIN_WAIT:
> +               ret = ratp_behaviour_c2(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_d2(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_e(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_f3(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_h3(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               break;
> +       case RATP_STATE_LAST_ACK:
> +               ret = ratp_behaviour_c2(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_d3(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_e(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_f3(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_h4(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               break;
> +       case RATP_STATE_CLOSING:
> +               ret = ratp_behaviour_c2(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_d3(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_e(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_f3(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_h5(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               break;
> +       case RATP_STATE_TIME_WAIT:
> +               ret = ratp_behaviour_d3(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_e(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_f3(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               ret = ratp_behaviour_h6(ri, pkt);
> +               if (ret)
> +                       return ret;
> +               break;
> +       case RATP_STATE_CLOSED:
> +               ratp_behaviour_g(ri, 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)
> +{
> +       struct ratp_internal *ri = ratp->internal;
> +
> +       if (!ri)
> +               return true;
> +
> +       return ri->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)
> +{
> +       struct ratp_internal *ri = ratp->internal;
> +
> +       if (!ri)
> +               return false;
> +
> +       return ri->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)
> +{
> +       struct ratp_internal *ri = ratp->internal;
> +       int ret;
> +
> +       if (!ri)
> +               return -ENETDOWN;
> +
> +       ri->in_ratp++;
> +
> +       ret = ratp_recv_pkt(ri, ri->recvbuf, 100);
> +       if (ret == 0) {
> +
> +               if (ri->state == RATP_STATE_TIME_WAIT &&
> +                   is_timeout(ri->timewait_timer_start, ri->srtt * 2 * MSECOND)) {
> +                       pr_debug("2*SRTT timer timed out\n");
> +                       ret = -ECONNRESET;
> +                       goto out;
> +               }
> +
> +               ret = ratp_state_machine(ri, ri->recvbuf);
> +               if (ret < 0)
> +                       goto out;
> +
> +               if (ri->status < 0) {
> +                       ret = ri->status;
> +                       goto out;
> +               }
> +       }
> +
> +       if (ri->sendmsg_current && is_timeout(ri->retransmission_timer_start,
> +           ri->rto * MSECOND)) {
> +
> +               ri->retransmission_count++;
> +               if (ri->retransmission_count == ri->max_retransmission) {
> +                       ri->status = ret = -ETIMEDOUT;
> +                       ri->state = RATP_STATE_CLOSED;
> +                       goto out;
> +               }
> +
> +               pr_debug("%s: retransmit\n", __func__);
> +
> +               ratp_print_header(ri, ri->sendbuf, "resend");
> +
> +               ri->retransmission_timer_start = get_time_ns();
> +
> +               ret = ri->ratp->send(ratp, ri->sendbuf, ri->sendbuf_len);
> +               if (ret)
> +                       goto out;
> +       }
> +
> +       if (!ri->sendmsg_current && !list_empty(&ri->sendmsg))
> +               ratp_send_next_data(ri);
> +
> +       ret = 0;
> +out:
> +       ri->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_internal 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)
> +{
> +       struct ratp_internal *ri;
> +       int ret;
> +       uint64_t start;
> +
> +       ri = xzalloc(sizeof(*ri));
> +       ri->ratp = ratp;
> +       ratp->internal = ri;
> +
> +       ri->recvbuf = xmalloc(512);
> +       ri->sendbuf = xmalloc(512);
> +       INIT_LIST_HEAD(&ri->recvmsg);
> +       INIT_LIST_HEAD(&ri->sendmsg);
> +       ri->max_retransmission = 100;
> +       ri->srtt = 100;
> +       ri->rto = 100;
> +       ri->active = active;
> +
> +       ri->in_ratp++;
> +
> +       if (ri->active) {
> +               ratp_send_hdr(ri, RATP_CONTROL_SYN);
> +
> +               ratp_state_change(ri, RATP_STATE_SYN_SENT);
> +       }
> +
> +       start = get_time_ns();
> +
> +       while (1) {
> +               ret = ratp_poll(ri->ratp);
> +               if (ret < 0)
> +                       goto out;
> +
> +               if (ri->state == RATP_STATE_ESTABLISHED) {
> +                       ret = 0;
> +                       goto out;
> +               }
> +
> +               if (timeout_ms && is_timeout(start, MSECOND * timeout_ms)) {
> +                       ret = -ETIMEDOUT;
> +                       goto out;
> +               }
> +       }
> +
> +out:
> +       if (ret) {
> +               free(ri->recvbuf);
> +               free(ri->sendbuf);
> +               free(ri);
> +               ratp->internal = NULL;
> +       }
> +
> +       ri->in_ratp--;
> +
> +       return ret;
> +}
> +
> +void ratp_close(struct ratp *ratp)
> +{
> +       struct ratp_internal *ri = ratp->internal;
> +       struct ratp_message *msg, *tmp;
> +       struct ratp_header fin = {};
> +
> +       if (!ri)
> +               return;
> +
> +       if (ri->state == RATP_STATE_ESTABLISHED) {
> +               uint64_t start;
> +               u8 control;
> +
> +               pr_debug("Closing...\n");
> +
> +               ratp_state_change(ri, RATP_STATE_FIN_WAIT);
> +
> +               control = ratp_set_sn(!ri->sn_sent) |
> +                       ratp_set_an(ri->sn_received + 1) |
> +                       RATP_CONTROL_FIN | RATP_CONTROL_ACK;
> +
> +               ratp_create_packet(ri, &fin, control, 0);
> +
> +               ratp_send_pkt(ri, &fin, sizeof(fin));
> +
> +               start = get_time_ns();
> +
> +               while (!is_timeout(start, ri->srtt * MSECOND * 2))
> +                       ratp_poll(ratp);
> +       }
> +
> +       list_for_each_entry_safe(msg, tmp, &ri->sendmsg, list)
> +               ratp_msg_done(ri, msg, -ECONNRESET);
> +
> +       free(ri);
> +       ratp->internal = NULL;
> +
> +       pr_info("Closed\n");
> +}
> +
> +/**
> + * ratp_send_complete(): Send data over a RATP link
> + * @ratp: The RATP link
> + * @data: The data buffer
> + * @len: The length of the message to send
> + * @complete: The completion callback for the message
> + * @complete_ctx: context pointer for the completion callback
> + *
> + * Queue a RATP message for transmission. This only queues the message,
> + * ratp_poll has to be called to actually transfer the message.
> + * @complete will be called upon completion of the message.
> + *
> + * Return: 0 if successful, a negative error code otherwise.
> + */
> +int ratp_send_complete(struct ratp *ratp, const void *data, size_t len,
> +                  void (*complete)(void *ctx, int status), void *complete_ctx)
> +{
> +       struct ratp_internal *ri = ratp->internal;
> +       struct ratp_message *msg;
> +
> +       if (!ri || ri->state != RATP_STATE_ESTABLISHED)
> +               return -ENETDOWN;
> +
> +       if (!len)
> +               return -EINVAL;
> +
> +       ri->in_ratp++;
> +
> +       while (len) {
> +               int now = min((int)len, 255);
> +
> +               msg = xzalloc(sizeof(*msg));
> +               msg->buf = xzalloc(sizeof(struct ratp_header) + now + 2);
> +               msg->len = now;
> +               memcpy(msg->buf + sizeof(struct ratp_header), data, now);
> +
> +               list_add_tail(&msg->list, &ri->sendmsg);
> +
> +               len -= now;
> +       }
> +
> +       msg->eor = 1;
> +       msg->complete = complete;
> +       msg->complete_ctx = complete_ctx;
> +
> +       ri->in_ratp--;
> +
> +       return 0;
> +}
> +
> +/**
> + * ratp_send(): Send data over a RATP link
> + * @ratp: The RATP link
> + * @data: The data buffer
> + * @len: The length of the message to send
> + *
> + * Queue a RATP message for transmission. This only queues the message,
> + * ratp_poll has to be called to actually transfer the message.
> + *
> + * Return: 0 if successful, a negative error code otherwise.
> + */
> +int ratp_send(struct ratp *ratp, const void *data, size_t len)
> +{
> +       return ratp_send_complete(ratp, data, len, NULL, NULL);
> +}
> +
> +/**
> + * ratp_recv() - Receive data from a RATP link
> + * @ratp: The RATP link
> + * @data: Pointer to data
> + * @len: The length of the data in bytes
> + *
> + * If a message is available it fills @data with a pointer to the data.
> + * This function does not wait for new messages. If no data is available
> + * -EAGAIN is returned. If data is received @data has to be freed by the
> + * caller.
> + *
> + * Return: 0 if successful, a negative error code otherwise.
> + */
> +int ratp_recv(struct ratp *ratp, void **data, size_t *len)
> +{
> +       struct ratp_internal *ri = ratp->internal;
> +       struct ratp_message *msg, *tmp;
> +       void *pos;
> +       int num = 0;
> +
> +       *len = 0;
> +
> +       if (!ri || ri->state != RATP_STATE_ESTABLISHED)
> +               return -ENETDOWN;
> +
> +       if (list_empty(&ri->recvmsg))
> +               return -EAGAIN;
> +
> +       list_for_each_entry(msg, &ri->recvmsg, list) {
> +               *len += msg->len;
> +               num++;
> +               if (msg->eor)
> +                       goto eor;
> +       }
> +
> +       return -EAGAIN;
> +
> +eor:
> +       *data = malloc(*len);
> +       if (!*data)
> +               return -ENOMEM;
> +
> +       pos = *data;
> +
> +       list_for_each_entry_safe(msg, tmp, &ri->recvmsg, list) {
> +               memcpy(pos, msg->buf, msg->len);
> +               pos += msg->len;
> +
> +               list_del(&msg->list);
> +
> +               free(msg->buf);
> +               free(msg);
> +       }
> +
> +       return 0;
> +}
> \ No newline at end of file
> --
> 2.6.4
>
>
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  reply	other threads:[~2016-01-18  1:09 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-08 11:13 [PATCH] remote control support Sascha Hauer
2016-01-08 11:13 ` [PATCH 1/8] Add Reliable Asynchronous Transfer Protocol Sascha Hauer
2016-01-18  1:09   ` Andrey Smirnov [this message]
2016-01-08 11:13 ` [PATCH 2/8] barebox remote control Sascha Hauer
2016-01-11  2:10   ` Andrey Smirnov
2016-01-11  7:52     ` Sascha Hauer
2016-01-18  1:04       ` Andrey Smirnov
2016-01-18  2:39   ` Andrey Smirnov
2016-01-08 11:13 ` [PATCH 3/8] fs: Add RATP fs support Sascha Hauer
2016-01-18  1:10   ` Andrey Smirnov
2016-01-08 11:13 ` [PATCH 4/8] include pyserial trunk Sascha Hauer
2016-01-08 11:57   ` Yegor Yefremov
2016-01-08 12:00     ` Sascha Hauer
2016-01-08 11:13 ` [PATCH 5/8] pyserial: decrease timeouts Sascha Hauer
2016-01-08 11:13 ` [PATCH 6/8] host side for barebox remote control Sascha Hauer
2016-01-18  1:07   ` Andrey Smirnov
2016-01-18  9:32     ` Jan Lübbe
2016-01-08 11:13 ` [PATCH 7/8] defaultenv2: Add automount for RATPFS Sascha Hauer
2016-01-08 11:13 ` [PATCH 8/8] barebox remote control: Documentation Sascha Hauer
2016-01-18  1:16   ` Andrey Smirnov
2016-01-18  9:25     ` 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=CAHQ1cqHrDCYi-GpagemTY4aWgyA7ybuMv3oz7sJfUN77mwbYbg@mail.gmail.com \
    --to=andrew.smirnov@gmail.com \
    --cc=barebox@lists.infradead.org \
    --cc=s.hauer@pengutronix.de \
    /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