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>
511 lines
9.8 KiB
C
511 lines
9.8 KiB
C
/*
|
|
* 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); |