[PATCH 2/8] barebox remote control

Sascha Hauer s.hauer at pengutronix.de
Fri Jan 8 03:13:49 PST 2016


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 at pengutronix.de>
---
 common/Kconfig   |  10 ++
 common/Makefile  |   2 +
 common/console.c |  26 ++-
 common/ratp.c    | 511 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 crypto/Kconfig   |   1 +
 fs/Makefile      |   1 +
 include/ratp.h   |   2 +-
 lib/readline.c   |   7 +
 8 files changed, 556 insertions(+), 4 deletions(-)
 create mode 100644 common/ratp.c

diff --git a/common/Kconfig b/common/Kconfig
index 8e79509..2b5943b 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 56e6bec..5eb3c96 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 4a1d257..9924964 100644
--- a/common/console.c
+++ b/common/console.c
@@ -303,6 +303,8 @@ int console_unregister(struct console_device *cdev)
 }
 EXPORT_SYMBOL(console_unregister);
 
+int barebox_ratp(struct console_device *cdev);
+
 static int getc_raw(void)
 {
 	struct console_device *cdev;
@@ -313,8 +315,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 (ch == 0x01) {
+					barebox_ratp(cdev);
+					return -1;
+				}
+
+				return ch;
+			}
 		}
 		if (!active)
 			/* no active console found. bail out */
@@ -349,16 +359,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 0000000..2fef3cc
--- /dev/null
+++ b/common/ratp.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2015 Sascha Hauer <s.hauer at 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
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 41145a3..fcf92c9 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -4,6 +4,7 @@ config CRC32
 	bool
 
 config CRC16
+	default y
 	bool
 
 config CRC7
diff --git a/fs/Makefile b/fs/Makefile
index 4693205..befbdf2 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_FS_UIMAGEFS)	+= uimagefs.o
 obj-$(CONFIG_FS_EFI)	 += efi.o
 obj-$(CONFIG_FS_EFIVARFS) += efivarfs.o
 obj-$(CONFIG_FS_SMHFS) += smhfs.o
+obj-$(CONFIG_RATP)	+= ratpfs.o
diff --git a/include/ratp.h b/include/ratp.h
index b91d305..94fd004 100644
--- a/include/ratp.h
+++ b/include/ratp.h
@@ -19,4 +19,4 @@ bool ratp_busy(struct ratp *ratp);
 
 void ratp_run_command(void);
 
-#endif /* __RATP_H */
+#endif /* __RATP_H */
\ No newline at end of file
diff --git a/lib/readline.c b/lib/readline.c
index c007e10..681f125 100644
--- a/lib/readline.c
+++ b/lib/readline.c
@@ -1,6 +1,8 @@
 #include <common.h>
 #include <readkey.h>
 #include <init.h>
+#include <poller.h>
+#include <ratp.h>
 #include <xfuncs.h>
 #include <complete.h>
 #include <linux/ctype.h>
@@ -197,6 +199,11 @@ int readline(const char *prompt, char *buf, int len)
 	puts (prompt);
 
 	while (1) {
+		while (!tstc()) {
+			poller_call();
+			ratp_run_command();
+		}
+
 		ichar = read_key();
 
 		if ((ichar == '\n') || (ichar == '\r')) {
-- 
2.6.4




More information about the barebox mailing list