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@xxxxxxxxxxxxxx> --- 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@xxxxxxxxxxxxxx> + * + * 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@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox