summaryrefslogtreecommitdiffstats
path: root/common/ratp.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/ratp.c')
-rw-r--r--common/ratp.c511
1 files changed, 511 insertions, 0 deletions
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