summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2015-11-26 09:47:23 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2016-01-18 09:25:09 +0100
commit266057337402f45adf707ef59beebd2a6821a749 (patch)
tree522ebf43324c9af913035e3044c2397d747c8d2b /common
parentbbdbda0a7442545ab9022f2ec78e45f1d6750f38 (diff)
downloadbarebox-266057337402f45adf707ef59beebd2a6821a749.tar.gz
barebox-266057337402f45adf707ef59beebd2a6821a749.tar.xz
barebox remote control
This adds the ability to control barebox over serial lines. The regular console is designed for human input and is unsuitable for controlling barebox from scripts since characters can be lost on both ends, the data stream contains escape sequences and the prompt cannot be easily matched upon. This approach is based on the RATP protocol. RATP packages start with a binary 0x01 which does not occur in normal console data. Whenever a 0x01 character is detected in the console barebox goes into RATP mode. The RATP packets contain a simple structure with a command/respone type and data for that type. Currently defined types are: BB_RATP_TYPE_COMMAND (host->barebox): Execute a command in the shell BB_RATP_TYPE_COMMAND_RETURN (barebox->host) Sends return value of the command back to the host, also means barebox is ready for the next command BB_RATP_TYPE_CONSOLEMSG (barebox->host) Console message from barebox Planned but not yet implemented are: BB_RATP_TYPE_PING (host->barebox) BB_RATP_TYPE_PONG (barebox->host) For testing purposes BB_RATP_TYPE_GETENV (host->barebox) BB_RATP_TYPE_GETENV_RETURN (barebox->host) Get values of environment variables Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Tested-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Diffstat (limited to 'common')
-rw-r--r--common/Kconfig10
-rw-r--r--common/Makefile2
-rw-r--r--common/console.c25
-rw-r--r--common/ratp.c511
4 files changed, 545 insertions, 3 deletions
diff --git a/common/Kconfig b/common/Kconfig
index 8e7950968c..2b5943be8b 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -611,6 +611,16 @@ config PBL_CONSOLE
must be running at the address it's linked at and bss must
be cleared. On ARM that would be after setup_c().
+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 56e6becec0..5eb3c96f45 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -45,6 +45,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
@@ -54,6 +55,7 @@ obj-$(CONFIG_IMD) += imd.o
obj-$(CONFIG_FILE_LIST) += file-list.o
obj-$(CONFIG_FIRMWARE) += firmware.o
obj-$(CONFIG_BAREBOX_UPDATE_IMX_NAND_FCB) += imx-bbu-nand-fcb.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 4a1d2576d0..a541892583 100644
--- a/common/console.c
+++ b/common/console.c
@@ -31,6 +31,7 @@
#include <kfifo.h>
#include <module.h>
#include <poller.h>
+#include <ratp_bb.h>
#include <magicvar.h>
#include <globalvar.h>
#include <linux/list.h>
@@ -313,8 +314,16 @@ 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 (IS_ENABLED(CONFIG_RATP) && ch == 0x01) {
+ barebox_ratp(cdev);
+ return -1;
+ }
+
+ return ch;
+ }
}
if (!active)
/* no active console found. bail out */
@@ -349,16 +358,26 @@ int getc(void)
start = get_time_ns();
while (1) {
if (tstc_raw()) {
- kfifo_putc(console_input_fifo, getc_raw());
+ int c = getc_raw();
+
+ if (c < 0)
+ break;
+
+ kfifo_putc(console_input_fifo, c);
start = get_time_ns();
}
+
if (is_timeout(start, 100 * USECOND) &&
kfifo_len(console_input_fifo))
break;
}
+ if (!kfifo_len(console_input_fifo))
+ return -1;
+
kfifo_getc(console_input_fifo, &ch);
+
return ch;
}
EXPORT_SYMBOL(getc);
diff --git a/common/ratp.c b/common/ratp.c
new file mode 100644
index 0000000000..2fef3cc0fe
--- /dev/null
+++ b/common/ratp.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2015 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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) "barebox-ratp: " fmt
+
+#include <common.h>
+#include <command.h>
+#include <kfifo.h>
+#include <malloc.h>
+#include <init.h>
+#include <ratp.h>
+#include <command.h>
+#include <byteorder.h>
+#include <environment.h>
+#include <kfifo.h>
+#include <poller.h>
+#include <linux/sizes.h>
+#include <ratp_bb.h>
+#include <fs.h>
+
+#define BB_RATP_TYPE_COMMAND 1
+#define BB_RATP_TYPE_COMMAND_RETURN 2
+#define BB_RATP_TYPE_CONSOLEMSG 3
+#define BB_RATP_TYPE_PING 4
+#define BB_RATP_TYPE_PONG 5
+#define BB_RATP_TYPE_GETENV 6
+#define BB_RATP_TYPE_GETENV_RETURN 7
+#define BB_RATP_TYPE_FS 8
+#define BB_RATP_TYPE_FS_RETURN 9
+
+struct ratp_bb {
+ uint16_t type;
+ uint16_t flags;
+ uint8_t data[];
+};
+
+struct ratp_bb_command_return {
+ uint32_t errno;
+};
+
+struct ratp_ctx {
+ struct console_device *cdev;
+ struct ratp ratp;
+ int ratp_status;
+ struct console_device ratp_console;
+ int have_synch;
+ int in_ratp_console;
+
+ u8 sendbuf[256];
+ u8 sendbuf_len;
+
+ int old_active;
+
+ struct kfifo *console_recv_fifo;
+ struct kfifo *console_transmit_fifo;
+
+ struct ratp_bb_pkt *fs_rx;
+
+ struct poller_struct poller;
+};
+
+static int console_recv(struct ratp *r, uint8_t *data)
+{
+ struct ratp_ctx *ctx = container_of(r, struct ratp_ctx, ratp);
+ struct console_device *cdev = ctx->cdev;
+
+ if (ctx->have_synch) {
+ ctx->have_synch = 0;
+ *data = 0x01;
+ return 0;
+ }
+
+ if (!cdev->tstc(cdev))
+ return -EAGAIN;
+
+ *data = cdev->getc(cdev);
+
+ return 0;
+}
+
+static int console_send(struct ratp *r, void *pkt, int len)
+{
+ struct ratp_ctx *ctx = container_of(r, struct ratp_ctx, ratp);
+ struct console_device *cdev = ctx->cdev;
+ const uint8_t *buf = pkt;
+ int i;
+
+ for (i = 0; i < len; i++)
+ cdev->putc(cdev, buf[i]);
+
+ return 0;
+}
+
+static void *xmemdup_add_zero(const void *buf, int len)
+{
+ void *ret;
+
+ ret = xzalloc(len + 1);
+ *(uint8_t *)(ret + len) = 0;
+ memcpy(ret, buf, len);
+
+ return ret;
+}
+
+static void ratp_queue_console_tx(struct ratp_ctx *ctx)
+{
+ u8 buf[255];
+ struct ratp_bb *rbb = (void *)buf;
+ unsigned int now, maxlen = 255 - sizeof(*rbb);
+ int ret;
+
+ rbb->type = cpu_to_be16(BB_RATP_TYPE_CONSOLEMSG);
+
+ while (1) {
+ now = min(maxlen, kfifo_len(ctx->console_transmit_fifo));
+ if (!now)
+ break;
+
+ kfifo_get(ctx->console_transmit_fifo, rbb->data, now);
+
+ ret = ratp_send(&ctx->ratp, rbb, now + sizeof(*rbb));
+ if (ret)
+ return;
+ }
+}
+
+static int ratp_bb_send_command_return(struct ratp_ctx *ctx, uint32_t errno)
+{
+ void *buf;
+ struct ratp_bb *rbb;
+ struct ratp_bb_command_return *rbb_ret;
+ int len = sizeof(*rbb) + sizeof(*rbb_ret);
+ int ret;
+
+ ratp_queue_console_tx(ctx);
+
+ buf = xzalloc(len);
+ rbb = buf;
+ rbb_ret = buf + sizeof(*rbb);
+
+ rbb->type = cpu_to_be16(BB_RATP_TYPE_COMMAND_RETURN);
+ rbb_ret->errno = cpu_to_be32(errno);
+
+ ret = ratp_send(&ctx->ratp, buf, len);
+
+ free(buf);
+
+ return ret;
+}
+
+static int ratp_bb_send_pong(struct ratp_ctx *ctx)
+{
+ void *buf;
+ struct ratp_bb *rbb;
+ int len = sizeof(*rbb);
+ int ret;
+
+ buf = xzalloc(len);
+ rbb = buf;
+
+ rbb->type = cpu_to_be16(BB_RATP_TYPE_PONG);
+
+ ret = ratp_send(&ctx->ratp, buf, len);
+
+ free(buf);
+
+ return ret;
+}
+
+static int ratp_bb_send_getenv_return(struct ratp_ctx *ctx, const char *val)
+{
+ void *buf;
+ struct ratp_bb *rbb;
+ int len, ret;
+
+ if (!val)
+ val = "";
+
+ len = sizeof(*rbb) + strlen(val);
+ buf = xzalloc(len);
+ rbb = buf;
+ strcpy(rbb->data, val);
+
+ rbb->type = cpu_to_be16(BB_RATP_TYPE_GETENV_RETURN);
+
+ ret = ratp_send(&ctx->ratp, buf, len);
+
+ free(buf);
+
+ return ret;
+}
+
+static char *ratp_command;
+static struct ratp_ctx *ratp_command_ctx;
+
+static int ratp_bb_dispatch(struct ratp_ctx *ctx, const void *buf, int len)
+{
+ const struct ratp_bb *rbb = buf;
+ struct ratp_bb_pkt *pkt;
+ int dlen = len - sizeof(struct ratp_bb);
+ char *varname;
+ int ret = 0;
+
+ switch (be16_to_cpu(rbb->type)) {
+ case BB_RATP_TYPE_COMMAND:
+ if (ratp_command)
+ return 0;
+
+ ratp_command = xmemdup_add_zero(&rbb->data, dlen);
+ ratp_command_ctx = ctx;
+ pr_debug("got command: %s\n", ratp_command);
+
+ break;
+
+ case BB_RATP_TYPE_COMMAND_RETURN:
+ case BB_RATP_TYPE_PONG:
+ break;
+
+ case BB_RATP_TYPE_CONSOLEMSG:
+
+ kfifo_put(ctx->console_recv_fifo, rbb->data, dlen);
+ break;
+
+ case BB_RATP_TYPE_PING:
+ ret = ratp_bb_send_pong(ctx);
+ break;
+
+ case BB_RATP_TYPE_GETENV:
+ varname = xmemdup_add_zero(&rbb->data, dlen);
+
+ ret = ratp_bb_send_getenv_return(ctx, getenv(varname));
+ break;
+
+ case BB_RATP_TYPE_FS_RETURN:
+ pkt = xzalloc(sizeof(*pkt) + dlen);
+ pkt->len = dlen;
+ memcpy(pkt->data, &rbb->data, dlen);
+ ctx->fs_rx = pkt;
+ break;
+ default:
+ printf("%s: unhandled packet type 0x%04x\n", __func__, be16_to_cpu(rbb->type));
+ break;
+ }
+
+ return ret;
+}
+
+static int ratp_console_getc(struct console_device *cdev)
+{
+ struct ratp_ctx *ctx = container_of(cdev, struct ratp_ctx, ratp_console);
+ unsigned char c;
+
+ if (!kfifo_len(ctx->console_recv_fifo))
+ return -1;
+
+ kfifo_getc(ctx->console_recv_fifo, &c);
+
+ return c;
+}
+
+static int ratp_console_tstc(struct console_device *cdev)
+{
+ struct ratp_ctx *ctx = container_of(cdev, struct ratp_ctx, ratp_console);
+
+ return kfifo_len(ctx->console_recv_fifo) ? 1 : 0;
+}
+
+static int ratp_console_puts(struct console_device *cdev, const char *s)
+{
+ struct ratp_ctx *ctx = container_of(cdev, struct ratp_ctx, ratp_console);
+ int len = 0;
+
+ len = strlen(s);
+
+ if (ratp_busy(&ctx->ratp))
+ return len;
+
+ kfifo_put(ctx->console_transmit_fifo, s, len);
+
+ return len;
+}
+
+static void ratp_console_putc(struct console_device *cdev, char c)
+{
+ struct ratp_ctx *ctx = container_of(cdev, struct ratp_ctx, ratp_console);
+
+ if (ratp_busy(&ctx->ratp))
+ return;
+
+ kfifo_putc(ctx->console_transmit_fifo, c);
+}
+
+static int ratp_console_register(struct ratp_ctx *ctx)
+{
+ int ret;
+
+ ctx->ratp_console.tstc = ratp_console_tstc;
+ ctx->ratp_console.puts = ratp_console_puts;
+ ctx->ratp_console.putc = ratp_console_putc;
+ ctx->ratp_console.getc = ratp_console_getc;
+ ctx->ratp_console.devname = "ratpconsole";
+ ctx->ratp_console.devid = DEVICE_ID_SINGLE;
+
+ ret = console_register(&ctx->ratp_console);
+ if (ret) {
+ pr_err("registering failed with %s\n", strerror(-ret));
+ return ret;
+ }
+
+ return 0;
+}
+
+void ratp_run_command(void)
+{
+ int ret;
+
+ if (!ratp_command)
+ return;
+
+ pr_debug("running command: %s\n", ratp_command);
+
+ ret = run_command(ratp_command);
+
+ free(ratp_command);
+ ratp_command = NULL;
+
+ ratp_bb_send_command_return(ratp_command_ctx, ret);
+}
+
+static const char *ratpfs_mount_path;
+
+int barebox_ratp_fs_mount(const char *path)
+{
+ if (path && ratpfs_mount_path)
+ return -EBUSY;
+
+ ratpfs_mount_path = path;
+
+ return 0;
+}
+
+static void ratp_console_unregister(struct ratp_ctx *ctx)
+{
+ int ret;
+
+ console_set_active(&ctx->ratp_console, 0);
+ poller_unregister(&ctx->poller);
+ ratp_close(&ctx->ratp);
+ console_set_active(ctx->cdev, ctx->old_active);
+ ctx->cdev = NULL;
+
+ if (ratpfs_mount_path) {
+ ret = umount(ratpfs_mount_path);
+ if (!ret)
+ ratpfs_mount_path = NULL;
+ }
+}
+
+static void ratp_poller(struct poller_struct *poller)
+{
+ struct ratp_ctx *ctx = container_of(poller, struct ratp_ctx, poller);
+ int ret;
+ size_t len;
+ void *buf;
+
+ ratp_queue_console_tx(ctx);
+
+ ret = ratp_poll(&ctx->ratp);
+ if (ret == -EINTR)
+ goto out;
+ if (ratp_closed(&ctx->ratp))
+ goto out;
+
+ ret = ratp_recv(&ctx->ratp, &buf, &len);
+ if (ret < 0)
+ return;
+
+ ratp_bb_dispatch(ctx, buf, len);
+
+ free(buf);
+
+ return;
+
+out:
+ ratp_console_unregister(ctx);
+}
+
+static int do_ratp_close(int argc, char *argv[])
+{
+ if (ratp_command_ctx && ratp_command_ctx->cdev)
+ ratp_console_unregister(ratp_command_ctx);
+ else
+ printf("ratp is not active\n");
+
+ return 0;
+}
+
+BAREBOX_CMD_START(ratp_close)
+ .cmd = do_ratp_close,
+};
+
+int barebox_ratp_fs_call(struct ratp_bb_pkt *tx, struct ratp_bb_pkt **rx)
+{
+ struct ratp_ctx *ctx = ratp_command_ctx;
+ struct ratp_bb *rbb;
+ int len;
+ u64 start;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ctx->fs_rx = NULL;
+
+ len = sizeof(*rbb) + tx->len;
+ rbb = xzalloc(len);
+ rbb->type = cpu_to_be16(BB_RATP_TYPE_FS);
+ memcpy(rbb->data, tx->data, tx->len);
+
+ if (ratp_send(&ctx->ratp, rbb, len) != 0)
+ pr_debug("failed to send port pkt\n");
+
+ free(rbb);
+
+ start = get_time_ns();
+
+ while (!ctx->fs_rx) {
+ poller_call();
+ if (ratp_closed(&ctx->ratp))
+ return -EIO;
+ if (is_timeout(start, 10 * SECOND))
+ return -ETIMEDOUT;
+ }
+
+ *rx = ctx->fs_rx;
+
+ pr_debug("%s: len %i\n", __func__, ctx->fs_rx->len);
+
+ return 0;
+}
+
+int barebox_ratp(struct console_device *cdev)
+{
+ int ret;
+ struct ratp_ctx *ctx;
+ struct ratp *ratp;
+
+ if (ratp_command_ctx) {
+ ctx = ratp_command_ctx;
+ } else {
+ ctx = xzalloc(sizeof(*ctx));
+ ratp_command_ctx = ctx;
+ ctx->ratp.send = console_send;
+ ctx->ratp.recv = console_recv;
+ ctx->console_recv_fifo = kfifo_alloc(512);
+ ctx->console_transmit_fifo = kfifo_alloc(SZ_128K);
+ ctx->poller.func = ratp_poller;
+ ratp_console_register(ctx);
+ }
+
+ if (ctx->cdev)
+ return -EBUSY;
+
+ ratp = &ctx->ratp;
+
+ ctx->old_active = console_get_active(cdev);
+ console_set_active(cdev, 0);
+
+ ctx->cdev = cdev;
+ ctx->have_synch = 1;
+
+ ret = ratp_establish(ratp, false, 100);
+ if (ret < 0)
+ goto out;
+
+ ret = poller_register(&ctx->poller);
+ if (ret)
+ goto out1;
+
+ console_set_active(&ctx->ratp_console, CONSOLE_STDOUT | CONSOLE_STDERR |
+ CONSOLE_STDIN);
+
+ return 0;
+
+out1:
+ ratp_close(ratp);
+out:
+ console_set_active(ctx->cdev, ctx->old_active);
+ ctx->cdev = NULL;
+
+ return ret;
+}
+
+static void barebox_ratp_close(void)
+{
+ if (ratp_command_ctx && ratp_command_ctx->cdev)
+ ratp_console_unregister(ratp_command_ctx);
+}
+predevshutdown_exitcall(barebox_ratp_close); \ No newline at end of file