From c17da94fafe8e0fa1a467f0c8fb7922fec8386e5 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Wed, 26 Feb 2014 19:27:05 +0100 Subject: [PATCH] move xbee user code in a separate file --- Makefile | 1 + commands.c | 3 +- main.c | 511 +----------------------------------------------- main.h | 34 +--- rc_proto.c | 1 + xbee_user.c | 553 ++++++++++++++++++++++++++++++++++++++++++++++++++++ xbee_user.h | 66 +++++++ 7 files changed, 625 insertions(+), 544 deletions(-) create mode 100644 xbee_user.c create mode 100644 xbee_user.h diff --git a/Makefile b/Makefile index e049893..3ff895f 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ AVERSIVE_DIR ?= ../.. # List C source files here. (C dependencies are automatically generated.) SRC = $(TARGET).c +SRC += xbee_user.c SRC += spi_servo.c SRC += commands.c SRC += commands_gen.c diff --git a/commands.c b/commands.c index 10b62cc..446d92d 100644 --- a/commands.c +++ b/commands.c @@ -36,14 +36,15 @@ #include #include #include +#include -#include "callout.h" #include "parse_atcmd.h" #include "parse_neighbor.h" #include "parse_monitor.h" #include "spi_servo.h" #include "rc_proto.h" +#include "xbee_user.h" #include "main.h" #include "cmdline.h" #include "beep.h" diff --git a/main.c b/main.c index b81e0a8..c438575 100644 --- a/main.c +++ b/main.c @@ -52,483 +52,15 @@ #include "eeprom_config.h" #include "beep.h" +#include "xbee_user.h" #include "main.h" struct xbeeboard xbeeboard; volatile uint32_t global_ms; -#define TIMEOUT_MS 1000 - /* global xbee device */ struct xbee_dev *xbee_dev; -/* events */ -//static struct event stdin_read_event, xbee_read_event; - -/* parameters */ -int xbee_raw = 0; -int xbee_hexdump = 0; -int xbee_debug = 0; - -int xbee_cmdline_input_enabled = 1; - -static struct xbee_ctx xbee_ctx[XBEE_MAX_CHANNEL]; - -static void hexdump(const char *title, const void *buf, unsigned int len) -{ - unsigned int i, out, ofs; - const unsigned char *data = buf; -#define LINE_LEN 80 - char line[LINE_LEN]; /* space needed 8+16*3+3+16 == 75 */ - - printf_P(PSTR("%s at [%p], len=%d\r\n"), title, data, len); - ofs = 0; - while (ofs < len) { - /* format 1 line in the buffer, then use printk to print them */ - out = snprintf_P(line, LINE_LEN, PSTR("%08X"), ofs); - for (i=0; ofs+i < len && i<16; i++) - out += snprintf_P(line+out, LINE_LEN - out, - PSTR(" %02X"), - data[ofs+i]&0xff); - for (;i<=16;i++) - out += snprintf(line+out, LINE_LEN - out, " "); - for (i=0; ofs < len && i<16; i++, ofs++) { - unsigned char c = data[ofs]; - if (!isascii(c) || !isprint(c)) - c = '.'; - out += snprintf_P(line+out, - LINE_LEN - out, - PSTR("%c"), c); - } - printf_P(PSTR("%s\r\n"), line); - } -} - -static int parse_xmit_status(struct xbee_ctx *ctx, - struct xbee_xmit_status_hdr *frame, unsigned len) -{ - (void)len; - - if (ctx == NULL) { - printf_P(PSTR("no context\r\n")); - return -1; - } - - /* see if it matches a xmit query (atcmd_query must be NULL) */ - if (ctx->atcmd_query[0] != '\0') { - printf_P(PSTR("invalid response 2\r\n")); - return -1; - } - - /* XXX use defines for these values */ - if (frame->delivery_status == 0x00) - printf_P(PSTR("Success\r\n")); - else if (frame->delivery_status == 0x01) - printf_P(PSTR("MAC ACK Failure\r\n")); - else if (frame->delivery_status == 0x15) - printf_P(PSTR("Invalid destination endpoint\r\n")); - else if (frame->delivery_status == 0x21) - printf_P(PSTR("Network ACK Failure\r\n")); - else if (frame->delivery_status == 0x25) - printf_P(PSTR("Route Not Found\r\n")); - - return 0; -} - -static int dump_atcmd(struct xbee_ctx *ctx, struct xbee_atresp_hdr *frame, - unsigned len) -{ - char atcmd_str[3]; - const struct xbee_atcmd *cmd_pgm; - struct xbee_atcmd cmd; - union { - uint8_t u8; - uint16_t u16; - uint32_t u32; - int16_t s16; - } __attribute__((packed)) *result; - - if (ctx == NULL) { - printf_P(PSTR("no context\r\n")); - return -1; - } - - /* get AT command from frame */ - memcpy(atcmd_str, &frame->cmd, 2); - atcmd_str[2] = '\0'; - - /* see if it matches query */ - if (memcmp(atcmd_str, ctx->atcmd_query, 2)) { - printf_P(PSTR("invalid response <%c%c><%s><%s>\r\n"), - frame->cmd & 0xFF, - (frame->cmd >> 8) & 0xFF, - atcmd_str, ctx->atcmd_query); - return -1; - } - - /* see if it exists */ - cmd_pgm = xbee_atcmd_lookup_name(atcmd_str); - if (cmd_pgm == NULL) { - printf_P(PSTR("unknown response\r\n")); - return -1; - } - memcpy_P(&cmd, cmd_pgm, sizeof(cmd)); - - /* bad status */ - if (frame->status == 1) { - printf_P(PSTR("Status is error\r\n")); - return -1; - } - else if (frame->status == 2) { - printf_P(PSTR("Invalid command\r\n")); - return -1; - } - else if (frame->status == 3) { - printf_P(PSTR("Invalid parameter\r\n")); - return -1; - } - else if (frame->status != 0) { - printf_P(PSTR("Unknown status error %d\r\n"), frame->status); - return -1; - } - - /* callback */ - if (ctx->func != NULL) - ctx->func(frame, len, ctx->arg); - - /* dump frame */ - result = (void *)frame->data; - len -= offsetof(struct xbee_atresp_hdr, data); - if (cmd.flags & XBEE_ATCMD_F_PARAM_U8 && len == sizeof(uint8_t)) - printf_P(PSTR("<%s> is 0x%x (%d)\r\n"), atcmd_str, result->u8, - result->u8); - else if (cmd.flags & XBEE_ATCMD_F_PARAM_U16 && len == sizeof(uint16_t)) - printf_P(PSTR("<%s> is 0x%x (%d)\r\n"), - atcmd_str, - ntohs(result->u16), ntohs(result->u16)); - else if (cmd.flags & XBEE_ATCMD_F_PARAM_U32 && len == sizeof(uint32_t)) - printf_P(PSTR("<%s> is 0x%"PRIx32" (%"PRIu32")\r\n"), - atcmd_str, - ntohl(result->u32), ntohs(result->u32)); - else if (cmd.flags & XBEE_ATCMD_F_PARAM_S16 && len == sizeof(int16_t)) - printf_P(PSTR("<%s> is %d\r\n"), atcmd_str, ntohs(result->s16)); - else if (len == 0) - printf_P(PSTR("no data, status ok\r\n")); - else - hexdump("atcmd answer", frame->data, len); - - - return 0; -} - - -int xbee_recv_data(struct xbee_recv_hdr *recvframe, unsigned len) -{ - unsigned int datalen; - struct rc_proto_hdr *rch = (struct rc_proto_hdr *) &recvframe->data; - - if (len < sizeof(*recvframe)) - return -1; - - datalen = len - sizeof(*recvframe); - if (datalen < sizeof(struct rc_proto_hdr)) - return -1; - - switch (rch->type) { -#if 0 - case RC_PROTO_TYPE_CHANNEL: { - struct rc_proto_channel *rcc = - (struct rc_proto_channel *) recvframe->data; - int16_t val; - if (datalen != sizeof(struct rc_proto_channel)) - return -1; - val = ntohs(rcc->axis[0]); - val >>= 6; - val += 512; - spi_servo_set(0, val); - break; - } - case RC_PROTO_TYPE_RANGE: { - struct rc_proto_range *rcr = - (struct rc_proto_range *) recvframe->data; - - if (datalen != sizeof(struct rc_proto_range)) - return -1; - - if (rcr->power_level >= MAX_POWER_LEVEL) - return -1; - - rc_proto_rx_range(rcr->power_level); - - break; - } -#endif - case RC_PROTO_HELLO: { - struct rc_proto_hello *rch = - (struct rc_proto_hello *) recvframe->data; - - if (xbee_debug) - printf_P(PSTR("recv hello len=%d\r\n"), - rch->datalen); - - break; - } - default: - return -1; - } - - return 0; -} - -/* socat /dev/ttyUSB0,raw,echo=0,b115200 /dev/ttyACM1,raw,echo=0,b115200 */ -int8_t xbeeapp_rx(struct xbee_dev *dev, int channel, int type, - void *frame, unsigned len, void *opaque) -{ - struct xbee_ctx *ctx = opaque; - int8_t ret = 0; - - if (xbee_debug) - printf_P(PSTR("type=0x%x, channel=%d, ctx=%p\r\n"), - type, channel, ctx); - - /* if ctx is !NULL, it is an answer to a query */ - if (ctx != NULL) { - /* XXX only delete timeout if answer matched */ - xbee_unload_timeout(ctx); - if (xbee_debug && ctx->atcmd_query) - printf_P(PSTR("Received answer to query <%c%c>\r\n"), - ctx->atcmd_query[0], ctx->atcmd_query[1]); - } - - /* some additional checks before sending */ - switch (type) { - case XBEE_TYPE_MODEM_STATUS: { - printf_P(PSTR("Received Modem Status frame\r\n")); - break; - } - - case XBEE_TYPE_RMT_ATRESP: { - union { - uint64_t u64; - struct { -#if BYTE_ORDER == LITTLE_ENDIAN - uint32_t low; - uint32_t high; -#else - uint32_t high; - uint32_t low; -#endif - } u32; - } addr; - memcpy(&addr, frame, sizeof(addr)); - addr.u64 = ntohll(addr.u64); - printf_P(PSTR("from remote address %"PRIx32"%"PRIx32"\r\n"), - addr.u32.high, addr.u32.low); - - /* this answer contains an atcmd answer at offset 10 */ - if (dump_atcmd(ctx, frame + 10, len - 10) < 0) - ret = -1; - - break; - } - case XBEE_TYPE_ATRESP: { - if (dump_atcmd(ctx, frame, len) < 0) - ret = -1; - - break; - } - - case XBEE_TYPE_XMIT_STATUS: { - if (parse_xmit_status(ctx, frame, len) < 0) - ret = -1; - - break; - } - - case XBEE_TYPE_RECV: { - if (xbee_recv_data(frame, len) < 0) - ret = -1; - - break; - } - - case XBEE_TYPE_ATCMD: - case XBEE_TYPE_ATCMD_Q: - case XBEE_TYPE_XMIT: - case XBEE_TYPE_EXPL_XMIT: - case XBEE_TYPE_RMT_ATCMD: - case XBEE_TYPE_EXPL_RECV: - case XBEE_TYPE_NODE_ID: - default: - printf_P(PSTR("Invalid frame\r\n")); - ret = -1; - break; - } - - if (ret < 0) - hexdump("undecoded rx frame", frame, len); - else if (xbee_hexdump) - hexdump("undecoded rx frame", frame, len); - - /* restart command line if it was a blocking query */ - if (ctx != NULL) { - xbee_unregister_channel(dev, channel); - if (ctx->foreground) { - xbee_stdin_enable(); - rdline_newline(&xbeeboard.rdl, xbeeboard.prompt); - } - } -} - -static int xbeeapp_send(struct xbee_ctx *ctx, int type, void *buf, unsigned len, - int foreground) -{ - int ret; - int channel; - - if (len > XBEE_MAX_FRAME_LEN) { - printf_P(PSTR("frame too large\r\n")); - return -1; - } - - /* register a channel */ - channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY, - xbeeapp_rx, NULL); - if (channel < 0) { - printf_P(PSTR("cannot send: no free channel\r\n")); - return -1; - } - - /* copy context in the static struct table (avoiding a malloc) */ - memcpy(&xbee_ctx[channel], ctx, sizeof(*ctx)); - ctx = &xbee_ctx[channel]; - xbee_set_opaque(xbee_dev, channel, ctx); - - if (xbee_debug) - printf_P(PSTR("send frame channel=%d type=0x%x len=%d\r\n"), - channel, type, len); - if (xbee_hexdump) - hexdump("xmit frame", buf, len); - - /* transmit the frame on this channel */ - ret = xbee_tx(xbee_dev, channel, type, buf, len); - if (ret < 0) { - printf_P(PSTR("cannot send\r\n")); - xbee_unregister_channel(xbee_dev, channel); - return -1; - } - - ctx->channel = channel; - xbee_load_timeout(ctx); /* load a timeout event */ - - /* suspend command line until we have answer or timeout */ - if (foreground) { - ctx->foreground = 1; - rdline_stop(&xbeeboard.rdl); /* don't display prompt when return */ - xbee_stdin_disable(); /* unload file descriptor polling */ - } - - return 0; -} - -/* send an AT command with parameters filled by caller. Disable - * command line until we get the answer or until a timeout occurs */ -int xbeeapp_send_atcmd(const char *atcmd_str, - void *param, unsigned param_len, int foreground, - int (*func)(void *frame, unsigned len, void *arg), void *arg) -{ - struct xbee_ctx ctx; - struct { - struct xbee_atcmd_hdr atcmd; - char buf[XBEE_MAX_FRAME_LEN]; - } __attribute__((packed)) frame; - - memset(&ctx, 0, sizeof(ctx)); - ctx.atcmd_query[0] = atcmd_str[0]; - ctx.atcmd_query[1] = atcmd_str[1]; - ctx.func = func; - ctx.arg = arg; - - memcpy(&frame.atcmd.cmd, atcmd_str, 2); - memcpy(&frame.buf, param, param_len); - - if (xbeeapp_send(&ctx, XBEE_TYPE_ATCMD, &frame, - sizeof(struct xbee_atcmd_hdr) + - param_len, foreground) < 0) { - return -1; - } - - return 0; -} - -int xbeeapp_send_msg(uint64_t addr, void *data, - unsigned data_len, int foreground) -{ - struct xbee_ctx ctx; - struct { - struct xbee_xmit_hdr xmit; - char buf[XBEE_MAX_FRAME_LEN]; - } __attribute__((packed)) frame; - - memset(&ctx, 0, sizeof(ctx)); - ctx.atcmd_query[0] = '\0'; - - frame.xmit.dstaddr = htonll(addr); - frame.xmit.reserved = htons(0xFFFE); - frame.xmit.bcast_radius = 0; - frame.xmit.opts = 0; - memcpy(&frame.buf, data, data_len); - - if (xbeeapp_send(&ctx, XBEE_TYPE_XMIT, &frame, - sizeof(struct xbee_xmit_hdr) + - data_len, foreground) < 0) { - return -1; - } - - return 0; -} - -void xbee_stdin_enable(void) -{ - xbee_cmdline_input_enabled = 1; -} - -void xbee_stdin_disable(void) -{ - xbee_cmdline_input_enabled = 0; -} - -static void evt_timeout(struct callout_mgr *cm, struct callout *clt, - void *arg) -{ - struct xbee_ctx *ctx = arg; - - (void)cm; - (void)clt; - - printf_P(PSTR("Timeout\r\n")); - - /* restart command line */ - xbee_stdin_enable(); - rdline_newline(&xbeeboard.rdl, xbeeboard.prompt); - - /* free event */ - xbee_unregister_channel(xbee_dev, ctx->channel); - - callout_stop(cm, clt); -} - -void xbee_load_timeout(struct xbee_ctx *ctx) -{ - callout_init(&ctx->timeout, evt_timeout, ctx, 0); - callout_schedule(&xbeeboard.mainloop_cm, &ctx->timeout, TIMEOUT_MS); -} - -void xbee_unload_timeout(struct xbee_ctx *ctx) -{ - callout_stop(&xbeeboard.mainloop_cm, &ctx->timeout); -} - void bootloader(void) { #define BOOTLOADER_ADDR 0x3f000 @@ -556,47 +88,6 @@ void bootloader(void) /* __asm__ __volatile__ ("eijmp\n"); */ } -void xbee_mainloop(void) -{ - while (1) { - callout_manage(&xbeeboard.mainloop_cm); - - if (xbee_raw) { - int16_t c; - - /* from xbee to cmdline */ - c = xbee_dev_recv(NULL); - if (c >= 0) - cmdline_dev_send((uint8_t)c, NULL); - - /* from cmdline to xbee */ - c = cmdline_dev_recv(NULL); - if (c == 4) { /* CTRL-d */ - xbee_dev_send('A', NULL); - xbee_dev_send('T', NULL); - xbee_dev_send('C', NULL); - xbee_dev_send('N', NULL); - xbee_dev_send('\n', NULL); - xbee_raw = 0; - rdline_newline(&xbeeboard.rdl, - xbeeboard.prompt); - } - else if (c >= 0) { - /* send to xbee */ - xbee_dev_send((uint8_t)c, NULL); - - /* echo on cmdline */ - cmdline_dev_send((uint8_t)c, NULL); - } - } - else { - if (xbee_cmdline_input_enabled) - cmdline_poll(); - xbee_rx(xbee_dev); - } - } -} - /* return time in milliseconds on unsigned 16 bits */ static uint16_t get_time_ms(void) { diff --git a/main.h b/main.h index 30a9262..001ecc0 100644 --- a/main.h +++ b/main.h @@ -35,9 +35,9 @@ #include #include #include +#include #include "cmdline.h" -#include "callout.h" #include "rc_proto.h" #include "spi_servo.h" @@ -84,39 +84,7 @@ struct xbeeboard { }; extern struct xbeeboard xbeeboard; - -/* used for timeouts and xbee rx callback */ -struct xbee_ctx { - int foreground; - int channel; - char atcmd_query[2]; - int (*func)(void *frame, unsigned len, void *arg); - void *arg; - struct callout timeout; -}; - -//extern cmdline_parse_ctx_t main_ctx; -extern struct xbee_dev *xbee_dev; -extern int xbee_raw; -extern int xbee_hexdump; -extern int xbee_debug; extern volatile uint32_t global_ms; void bootloader(void); -int8_t xbeeapp_rx(struct xbee_dev *dev, int channel, int type, - void *frame, unsigned len, void *opaque); -int xbeeapp_send_atcmd(const char *atcmd_str, - void *param, unsigned param_len, int foreground, - int (*func)(void *frame, unsigned len, void *arg), - void *arg); -int xbeeapp_send_msg(uint64_t addr, void *data, - unsigned data_len, int foreground); - -void xbee_stdin_enable(void); -void xbee_stdin_disable(void); - -void xbee_load_timeout(struct xbee_ctx *ctx); -void xbee_unload_timeout(struct xbee_ctx *ctx); - -void xbee_mainloop(void); diff --git a/rc_proto.c b/rc_proto.c index be30c39..22b6f28 100644 --- a/rc_proto.c +++ b/rc_proto.c @@ -41,6 +41,7 @@ #include "callout.h" #include "rc_proto.h" +#include "xbee_user.h" #include "main.h" /* */ diff --git a/xbee_user.c b/xbee_user.c new file mode 100644 index 0000000..81f2fb6 --- /dev/null +++ b/xbee_user.c @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2014, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "rc_proto.h" +#include "xbee_user.h" +#include "main.h" + +#define XBEE_TIMEOUT_MS 1000 + +static struct xbee_ctx xbee_ctx[XBEE_MAX_CHANNEL]; + +int xbee_cmdline_input_enabled = 1; + +/* parameters */ +int xbee_raw = 0; +int xbee_hexdump = 0; +int xbee_debug = 0; + +static void hexdump(const char *title, const void *buf, unsigned int len) +{ + unsigned int i, out, ofs; + const unsigned char *data = buf; +#define LINE_LEN 80 + char line[LINE_LEN]; /* space needed 8+16*3+3+16 == 75 */ + + printf_P(PSTR("%s at [%p], len=%d\r\n"), title, data, len); + ofs = 0; + while (ofs < len) { + /* format 1 line in the buffer, then use printk to print them */ + out = snprintf_P(line, LINE_LEN, PSTR("%08X"), ofs); + for (i=0; ofs+i < len && i<16; i++) + out += snprintf_P(line+out, LINE_LEN - out, + PSTR(" %02X"), + data[ofs+i]&0xff); + for (;i<=16;i++) + out += snprintf(line+out, LINE_LEN - out, " "); + for (i=0; ofs < len && i<16; i++, ofs++) { + unsigned char c = data[ofs]; + if (!isascii(c) || !isprint(c)) + c = '.'; + out += snprintf_P(line+out, + LINE_LEN - out, + PSTR("%c"), c); + } + printf_P(PSTR("%s\r\n"), line); + } +} + +static int parse_xmit_status(struct xbee_ctx *ctx, + struct xbee_xmit_status_hdr *frame, unsigned len) +{ + (void)len; + + if (ctx == NULL) { + printf_P(PSTR("no context\r\n")); + return -1; + } + + /* see if it matches a xmit query (atcmd_query must be NULL) */ + if (ctx->atcmd_query[0] != '\0') { + printf_P(PSTR("invalid response 2\r\n")); + return -1; + } + + /* XXX use defines for these values */ + if (frame->delivery_status == 0x00) + printf_P(PSTR("Success\r\n")); + else if (frame->delivery_status == 0x01) + printf_P(PSTR("MAC ACK Failure\r\n")); + else if (frame->delivery_status == 0x15) + printf_P(PSTR("Invalid destination endpoint\r\n")); + else if (frame->delivery_status == 0x21) + printf_P(PSTR("Network ACK Failure\r\n")); + else if (frame->delivery_status == 0x25) + printf_P(PSTR("Route Not Found\r\n")); + + return 0; +} + +static int dump_atcmd(struct xbee_ctx *ctx, struct xbee_atresp_hdr *frame, + unsigned len) +{ + char atcmd_str[3]; + const struct xbee_atcmd *cmd_pgm; + struct xbee_atcmd cmd; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + int16_t s16; + } __attribute__((packed)) *result; + + if (ctx == NULL) { + printf_P(PSTR("no context\r\n")); + return -1; + } + + /* get AT command from frame */ + memcpy(atcmd_str, &frame->cmd, 2); + atcmd_str[2] = '\0'; + + /* see if it matches query */ + if (memcmp(atcmd_str, ctx->atcmd_query, 2)) { + printf_P(PSTR("invalid response <%c%c><%s><%s>\r\n"), + frame->cmd & 0xFF, + (frame->cmd >> 8) & 0xFF, + atcmd_str, ctx->atcmd_query); + return -1; + } + + /* see if it exists */ + cmd_pgm = xbee_atcmd_lookup_name(atcmd_str); + if (cmd_pgm == NULL) { + printf_P(PSTR("unknown response\r\n")); + return -1; + } + memcpy_P(&cmd, cmd_pgm, sizeof(cmd)); + + /* bad status */ + if (frame->status == 1) { + printf_P(PSTR("Status is error\r\n")); + return -1; + } + else if (frame->status == 2) { + printf_P(PSTR("Invalid command\r\n")); + return -1; + } + else if (frame->status == 3) { + printf_P(PSTR("Invalid parameter\r\n")); + return -1; + } + else if (frame->status != 0) { + printf_P(PSTR("Unknown status error %d\r\n"), frame->status); + return -1; + } + + /* callback */ + if (ctx->func != NULL) + ctx->func(frame, len, ctx->arg); + + /* dump frame */ + result = (void *)frame->data; + len -= offsetof(struct xbee_atresp_hdr, data); + if (cmd.flags & XBEE_ATCMD_F_PARAM_U8 && len == sizeof(uint8_t)) + printf_P(PSTR("<%s> is 0x%x (%d)\r\n"), atcmd_str, result->u8, + result->u8); + else if (cmd.flags & XBEE_ATCMD_F_PARAM_U16 && len == sizeof(uint16_t)) + printf_P(PSTR("<%s> is 0x%x (%d)\r\n"), + atcmd_str, + ntohs(result->u16), ntohs(result->u16)); + else if (cmd.flags & XBEE_ATCMD_F_PARAM_U32 && len == sizeof(uint32_t)) + printf_P(PSTR("<%s> is 0x%"PRIx32" (%"PRIu32")\r\n"), + atcmd_str, + ntohl(result->u32), ntohs(result->u32)); + else if (cmd.flags & XBEE_ATCMD_F_PARAM_S16 && len == sizeof(int16_t)) + printf_P(PSTR("<%s> is %d\r\n"), atcmd_str, ntohs(result->s16)); + else if (len == 0) + printf_P(PSTR("no data, status ok\r\n")); + else + hexdump("atcmd answer", frame->data, len); + + + return 0; +} + + +int xbee_recv_data(struct xbee_recv_hdr *recvframe, unsigned len) +{ + unsigned int datalen; + struct rc_proto_hdr *rch = (struct rc_proto_hdr *) &recvframe->data; + + if (len < sizeof(*recvframe)) + return -1; + + datalen = len - sizeof(*recvframe); + if (datalen < sizeof(struct rc_proto_hdr)) + return -1; + + switch (rch->type) { +#if 0 + case RC_PROTO_TYPE_CHANNEL: { + struct rc_proto_channel *rcc = + (struct rc_proto_channel *) recvframe->data; + int16_t val; + if (datalen != sizeof(struct rc_proto_channel)) + return -1; + val = ntohs(rcc->axis[0]); + val >>= 6; + val += 512; + spi_servo_set(0, val); + break; + } + case RC_PROTO_TYPE_RANGE: { + struct rc_proto_range *rcr = + (struct rc_proto_range *) recvframe->data; + + if (datalen != sizeof(struct rc_proto_range)) + return -1; + + if (rcr->power_level >= MAX_POWER_LEVEL) + return -1; + + rc_proto_rx_range(rcr->power_level); + + break; + } +#endif + case RC_PROTO_HELLO: { + struct rc_proto_hello *rch = + (struct rc_proto_hello *) recvframe->data; + + if (xbee_debug) + printf_P(PSTR("recv hello len=%d\r\n"), + rch->datalen); + + break; + } + default: + return -1; + } + + return 0; +} + +/* socat /dev/ttyUSB0,raw,echo=0,b115200 /dev/ttyACM1,raw,echo=0,b115200 */ +int8_t xbeeapp_rx(struct xbee_dev *dev, int channel, int type, + void *frame, unsigned len, void *opaque) +{ + struct xbee_ctx *ctx = opaque; + int8_t ret = 0; + + if (xbee_debug) + printf_P(PSTR("type=0x%x, channel=%d, ctx=%p\r\n"), + type, channel, ctx); + + /* if ctx is !NULL, it is an answer to a query */ + if (ctx != NULL) { + /* XXX only delete timeout if answer matched */ + xbee_unload_timeout(ctx); + if (xbee_debug && ctx->atcmd_query) + printf_P(PSTR("Received answer to query <%c%c>\r\n"), + ctx->atcmd_query[0], ctx->atcmd_query[1]); + } + + /* some additional checks before sending */ + switch (type) { + case XBEE_TYPE_MODEM_STATUS: { + printf_P(PSTR("Received Modem Status frame\r\n")); + break; + } + + case XBEE_TYPE_RMT_ATRESP: { + union { + uint64_t u64; + struct { +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t low; + uint32_t high; +#else + uint32_t high; + uint32_t low; +#endif + } u32; + } addr; + memcpy(&addr, frame, sizeof(addr)); + addr.u64 = ntohll(addr.u64); + printf_P(PSTR("from remote address %"PRIx32"%"PRIx32"\r\n"), + addr.u32.high, addr.u32.low); + + /* this answer contains an atcmd answer at offset 10 */ + if (dump_atcmd(ctx, frame + 10, len - 10) < 0) + ret = -1; + + break; + } + case XBEE_TYPE_ATRESP: { + if (dump_atcmd(ctx, frame, len) < 0) + ret = -1; + + break; + } + + case XBEE_TYPE_XMIT_STATUS: { + if (parse_xmit_status(ctx, frame, len) < 0) + ret = -1; + + break; + } + + case XBEE_TYPE_RECV: { + if (xbee_recv_data(frame, len) < 0) + ret = -1; + + break; + } + + case XBEE_TYPE_ATCMD: + case XBEE_TYPE_ATCMD_Q: + case XBEE_TYPE_XMIT: + case XBEE_TYPE_EXPL_XMIT: + case XBEE_TYPE_RMT_ATCMD: + case XBEE_TYPE_EXPL_RECV: + case XBEE_TYPE_NODE_ID: + default: + printf_P(PSTR("Invalid frame\r\n")); + ret = -1; + break; + } + + if (ret < 0) + hexdump("undecoded rx frame", frame, len); + else if (xbee_hexdump) + hexdump("undecoded rx frame", frame, len); + + /* restart command line if it was a blocking query */ + if (ctx != NULL) { + xbee_unregister_channel(dev, channel); + if (ctx->foreground) { + xbee_stdin_enable(); + rdline_newline(&xbeeboard.rdl, xbeeboard.prompt); + } + } + + return ret; +} + +static int xbeeapp_send(struct xbee_ctx *ctx, int type, void *buf, unsigned len, + int foreground) +{ + int ret; + int channel; + + if (len > XBEE_MAX_FRAME_LEN) { + printf_P(PSTR("frame too large\r\n")); + return -1; + } + + /* register a channel */ + channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY, + xbeeapp_rx, NULL); + if (channel < 0) { + printf_P(PSTR("cannot send: no free channel\r\n")); + return -1; + } + + /* copy context in the static struct table (avoiding a malloc) */ + memcpy(&xbee_ctx[channel], ctx, sizeof(*ctx)); + ctx = &xbee_ctx[channel]; + xbee_set_opaque(xbee_dev, channel, ctx); + + if (xbee_debug) + printf_P(PSTR("send frame channel=%d type=0x%x len=%d\r\n"), + channel, type, len); + if (xbee_hexdump) + hexdump("xmit frame", buf, len); + + /* transmit the frame on this channel */ + ret = xbee_tx(xbee_dev, channel, type, buf, len); + if (ret < 0) { + printf_P(PSTR("cannot send\r\n")); + xbee_unregister_channel(xbee_dev, channel); + return -1; + } + + ctx->channel = channel; + xbee_load_timeout(ctx); /* load a timeout event */ + + /* suspend command line until we have answer or timeout */ + if (foreground) { + ctx->foreground = 1; + rdline_stop(&xbeeboard.rdl); /* don't display prompt when return */ + xbee_stdin_disable(); /* unload file descriptor polling */ + } + + return 0; +} + +/* send an AT command with parameters filled by caller. Disable + * command line until we get the answer or until a timeout occurs */ +int xbeeapp_send_atcmd(const char *atcmd_str, + void *param, unsigned param_len, int foreground, + int (*func)(void *frame, unsigned len, void *arg), void *arg) +{ + struct xbee_ctx ctx; + struct { + struct xbee_atcmd_hdr atcmd; + char buf[XBEE_MAX_FRAME_LEN]; + } __attribute__((packed)) frame; + + memset(&ctx, 0, sizeof(ctx)); + ctx.atcmd_query[0] = atcmd_str[0]; + ctx.atcmd_query[1] = atcmd_str[1]; + ctx.func = func; + ctx.arg = arg; + + memcpy(&frame.atcmd.cmd, atcmd_str, 2); + memcpy(&frame.buf, param, param_len); + + if (xbeeapp_send(&ctx, XBEE_TYPE_ATCMD, &frame, + sizeof(struct xbee_atcmd_hdr) + + param_len, foreground) < 0) { + return -1; + } + + return 0; +} + +int xbeeapp_send_msg(uint64_t addr, void *data, + unsigned data_len, int foreground) +{ + struct xbee_ctx ctx; + struct { + struct xbee_xmit_hdr xmit; + char buf[XBEE_MAX_FRAME_LEN]; + } __attribute__((packed)) frame; + + memset(&ctx, 0, sizeof(ctx)); + ctx.atcmd_query[0] = '\0'; + + frame.xmit.dstaddr = htonll(addr); + frame.xmit.reserved = htons(0xFFFE); + frame.xmit.bcast_radius = 0; + frame.xmit.opts = 0; + memcpy(&frame.buf, data, data_len); + + if (xbeeapp_send(&ctx, XBEE_TYPE_XMIT, &frame, + sizeof(struct xbee_xmit_hdr) + + data_len, foreground) < 0) { + return -1; + } + + return 0; +} + +static void evt_timeout(struct callout_mgr *cm, struct callout *clt, + void *arg) +{ + struct xbee_ctx *ctx = arg; + + (void)cm; + (void)clt; + + printf_P(PSTR("Timeout\r\n")); + + /* restart command line */ + xbee_stdin_enable(); + rdline_newline(&xbeeboard.rdl, xbeeboard.prompt); + + /* free event */ + xbee_unregister_channel(xbee_dev, ctx->channel); + + callout_stop(cm, clt); +} + +void xbee_load_timeout(struct xbee_ctx *ctx) +{ + callout_init(&ctx->timeout, evt_timeout, ctx, 0); + callout_schedule(&xbeeboard.mainloop_cm, &ctx->timeout, XBEE_TIMEOUT_MS); +} + +void xbee_unload_timeout(struct xbee_ctx *ctx) +{ + callout_stop(&xbeeboard.mainloop_cm, &ctx->timeout); +} + +void xbee_mainloop(void) +{ + while (1) { + callout_manage(&xbeeboard.mainloop_cm); + + if (xbee_raw) { + int16_t c; + + /* from xbee to cmdline */ + c = xbee_dev_recv(NULL); + if (c >= 0) + cmdline_dev_send((uint8_t)c, NULL); + + /* from cmdline to xbee */ + c = cmdline_dev_recv(NULL); + if (c == 4) { /* CTRL-d */ + xbee_dev_send('A', NULL); + xbee_dev_send('T', NULL); + xbee_dev_send('C', NULL); + xbee_dev_send('N', NULL); + xbee_dev_send('\n', NULL); + xbee_raw = 0; + rdline_newline(&xbeeboard.rdl, + xbeeboard.prompt); + } + else if (c >= 0) { + /* send to xbee */ + xbee_dev_send((uint8_t)c, NULL); + + /* echo on cmdline */ + cmdline_dev_send((uint8_t)c, NULL); + } + } + else { + if (xbee_cmdline_input_enabled) + cmdline_poll(); + xbee_rx(xbee_dev); + } + } +} + +void xbee_stdin_enable(void) +{ + xbee_cmdline_input_enabled = 1; +} + +void xbee_stdin_disable(void) +{ + xbee_cmdline_input_enabled = 0; +} diff --git a/xbee_user.h b/xbee_user.h new file mode 100644 index 0000000..ced5363 --- /dev/null +++ b/xbee_user.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XBEE_USER_H_ +#define _XBEE_USER_H_ + +#include + +/* used for timeouts and xbee rx callback */ +struct xbee_ctx { + int foreground; + int channel; + char atcmd_query[2]; + int (*func)(void *frame, unsigned len, void *arg); + void *arg; + struct callout timeout; +}; + +//extern cmdline_parse_ctx_t main_ctx; +extern struct xbee_dev *xbee_dev; +extern int xbee_raw; +extern int xbee_hexdump; +extern int xbee_debug; + +int8_t xbeeapp_rx(struct xbee_dev *dev, int channel, int type, + void *frame, unsigned len, void *opaque); +int xbeeapp_send_atcmd(const char *atcmd_str, + void *param, unsigned param_len, int foreground, + int (*func)(void *frame, unsigned len, void *arg), + void *arg); +int xbeeapp_send_msg(uint64_t addr, void *data, + unsigned data_len, int foreground); + +void xbee_stdin_enable(void); +void xbee_stdin_disable(void); + +void xbee_load_timeout(struct xbee_ctx *ctx); +void xbee_unload_timeout(struct xbee_ctx *ctx); + +void xbee_mainloop(void); + +#endif /* _XBEE_USER_H_ */ -- 2.20.1