diff --git a/tools/atrf-proxy/Makefile b/tools/atrf-proxy/Makefile new file mode 100644 index 0000000..4ba765d --- /dev/null +++ b/tools/atrf-proxy/Makefile @@ -0,0 +1,16 @@ +# +# atrf-proxy/Makefile - ATRF network proxy +# +# Written 2011 by Werner Almesberger +# Copyright 2011 Werner Almesberger +# +# 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. +# + + +MAIN = atrf-proxy + +include ../Makefile.common diff --git a/tools/atrf-proxy/PROTOCOL b/tools/atrf-proxy/PROTOCOL new file mode 100644 index 0000000..8cdf53b --- /dev/null +++ b/tools/atrf-proxy/PROTOCOL @@ -0,0 +1,68 @@ +Protocol specification +====================== + +Messages +-------- + + ++[greeting] +-message + +RESET ++[comment] +-message + +RESET_RF ++[comment] +-message + +TEST ++[comment] +-message + +SLP_TR 0|1 ++[comment] +-message + +CLKM freq_mhz ++[comment] +-message + +SET register value ++[comment] +-message + +GET register ++value +-message + +WRITE length raw-data ++[comment] +-message + +READ ++length raw-data +-message + +POLL ++0|1 +-message + + +Message format +-------------- + +Commands are not case-sensitive. Messages and comments consist of printable +ASCII characters and spaces. + +Whitespace separating words is always exactly one space character. +Lines end with a * single newline, without trailing whitespace. +Numeric values are in C notation, i.e., 64, 0100, and 0x40 would all +represent the same value. + +"raw-data" is a string of binary data of the indicated length. The other +formatting conventions remain valid after this string, i.e., it must be +followed by a single newline. + +The device is implicitly opened when establishing a TCP session. The +device is implicitly closed when closing the TCP session. diff --git a/tools/atrf-proxy/atrf-proxy.c b/tools/atrf-proxy/atrf-proxy.c new file mode 100644 index 0000000..b9bb7b8 --- /dev/null +++ b/tools/atrf-proxy/atrf-proxy.c @@ -0,0 +1,309 @@ +/* + * atrf-proxy/atrf-proxy.c - ATRF network proxy + * + * Written 2011 by Werner Almesberger + * Copyright 2011 Werner Almesberger + * + * 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. + */ + + +#include +#include +#include +#include +#include /* for strcasecmp */ +#include +#include +#include +#include + +#include "atrf.h" +#include "netio.h" + + +#define DEFAULT_PORT 0x1540 /* 5440 */ + + +static int verbose = 0; + + +static int get_num(struct netio *netio, int last, int *ret) +{ + char buf[10]; + ssize_t got; + unsigned long n; + char *end; + + got = netio_read_until(netio, + last ? "\n" : " ", buf, sizeof(buf), NULL); + if (got < 0) { + *ret = -1; + return -1; + } + buf[got] = 0; + n = strtoul(buf, &end, 0); + if (*end) { + *ret = netio_printf(netio, "-bad number\n"); + return -1; + } + /* @@@ check against INT_MAX */ + return n; +} + + +static int cmd_more(struct atrf_dsc *dsc, struct netio *netio, const char *cmd) +{ + int n, ret; + + if (!strcasecmp(cmd, "set")) { + int val; + + n = get_num(netio, 0, &ret); + if (n < 0) + return ret; + if (n > 255) + return netio_printf(netio, "-bad argument\n"); + val = get_num(netio, 1, &ret); + if (val < 0) + return ret; + if (val > 255) + return netio_printf(netio, "-bad argument\n"); + atrf_reg_write(dsc, n, ret); + if (atrf_error(dsc)) + return netio_printf(netio, "-I/O error\n"); + return netio_printf(netio, "+\n"); + } + + n = get_num(netio, 1, &ret); + if (n < 0) + return ret; + + if (!strcasecmp(cmd, "slp_tr")) { + if (n > 1) + return netio_printf(netio, "-bad argument\n"); + if (atrf_slp_tr(dsc, n) < 0) + return netio_printf(netio, "-I/O error\n"); + return netio_printf(netio, "+\n"); + } + if (!strcasecmp(cmd, "get")) { + uint8_t res; + + if (n > 255) + return netio_printf(netio, "-bad argument\n"); + res = atrf_reg_read(dsc, n); + if (atrf_error(dsc)) + return netio_printf(netio, "-I/O error\n"); + return netio_printf(netio, "+0x%02x\n", res); + } + if (!strcasecmp(cmd, "write")) { + uint8_t *tmp; + ssize_t got; + + if (n > 128) + return netio_printf(netio, "-bad argument\n"); + tmp = malloc(n+1); + if (!tmp) + return netio_printf(netio, "-out of memory\n"); + got = netio_read(netio, tmp, n+1); + if (got != n+1) { + free(tmp); + return -1; + } + if (tmp[n] != '\n') { + free(tmp); + return netio_printf(netio, "-unterminated command\n"); + } + atrf_buf_write(dsc, tmp, n); + free(tmp); + if (atrf_error(dsc)) + return netio_printf(netio, "-I/O error\n"); + return netio_printf(netio, "+\n"); + } + return netio_printf(netio, "-unrecognized command\n"); +} + + +static int cmd_zero(struct atrf_dsc *dsc, struct netio *netio, const char *cmd) +{ + int res; + + if (!strcasecmp(cmd, "reset")) { + atrf_reset(dsc); + return netio_printf(netio, "+\n"); + } + if (!strcasecmp(cmd, "reset_rf")) { + atrf_reset_rf(dsc); + return netio_printf(netio, "+\n"); + } + if (!strcasecmp(cmd, "test")) { + atrf_test_mode(dsc); + return netio_printf(netio, "+\n"); + } + if (!strcasecmp(cmd, "read")) { + uint8_t buf[128+1]; /* one more for the trailing \n */ + int got; + + got = atrf_buf_read(dsc, buf, sizeof(buf)); + if (got < 0) + return netio_printf(netio, "-I/O error\n"); + if (netio_printf(netio, "+%d ", got) < 0) + return -1; + buf[got] = '\n'; + return netio_write(netio, buf, got+1); + } + if (!strcasecmp(cmd, "poll")) { + res = atrf_interrupt(dsc); + if (res < 0) + return netio_printf(netio, "-I/O error\n"); + return netio_printf(netio, "+%d\n", res); + } + return netio_printf(netio, "-unrecognized command\n"); +} + + +static void session(const char *driver, struct netio *netio) +{ + struct atrf_dsc *dsc; + + dsc = atrf_open(driver); + if (!dsc) { + netio_printf(netio, "-unable to open driver\n"); + return; + } + + if (netio_printf(netio, + "+connected to %s\n", driver ? driver : "default") < 0) + goto done; + + while (1) { + char buf[100]; + ssize_t got; + char last; + + got = netio_read_until(netio, " \n", buf, sizeof(buf), &last); + if (got < 0) + break; + if (!got && last) { + netio_printf(netio, "-empty input\n"); + continue; + } + buf[got] = 0; + switch (last) { + case ' ': + if (cmd_more(dsc, netio, buf) < 0) + goto done; + break; + case '\n': + if (cmd_zero(dsc, netio, buf) < 0) + goto done; + break; + case 0: + goto done; + default: + abort(); + } + } + +done: + atrf_close(dsc); +} + + +static void loop(const char *driver, int port) +{ + struct sockaddr_in addr; + int s; + + s = socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("socket"); + exit(1); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + exit(1); + } + + if (listen(s, 0) < 0) { + perror("listen"); + exit(1); + } + + while (1) { + socklen_t len = sizeof(addr); + int s2; + struct netio *netio; + + s2 = accept(s, (struct sockaddr *) &addr, &len); + if (s2 < 0) { + perror("accept"); + continue; + } + + if (verbose) + fprintf(stderr, "%s:%u\n", inet_ntoa(addr.sin_addr), + ntohs(addr.sin_port)); + + netio = netio_open(s2); + if (netio) { + session(driver, netio); + netio_close(netio); + } else { + if (close(s2) < 0) + perror("close"); + } + } +} + + +static void usage(const char *name) +{ + fprintf(stderr, "usage: %s [-d driver[:arg]] [-v ...] [port]\n", name); + exit(1); +} + + +int main(int argc, char **argv) +{ + unsigned long port = DEFAULT_PORT; + const char *driver = NULL; + char *end; + int c; + + while ((c = getopt(argc, argv, "d:v")) != EOF) + switch (c) { + case 'd': + driver = optarg; + break; + case 'v': + verbose++; + netio_verbose++; + break; + default: + usage(*argv); + } + + switch (argc-optind) { + case 0: + break; + case 1: + port = strtoul(argv[optind], &end, 0); + if (*end || !port || port > 0xffff) + usage(*argv); + break; + default: + usage(*argv); + } + + loop(driver, port); + + return 0; +}