From 1bbb5063d82e9dc21677eb2a7d1d8f7ef2da3b89 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Fri, 17 Feb 2012 18:21:19 +0100 Subject: [PATCH 1/1] init --- commands.c | 1339 ++++++++++++++++++++++++++++++++++++++++++++++ main.c | 829 ++++++++++++++++++++++++++++ main.h | 69 +++ parse_atcmd.c | 130 +++++ parse_atcmd.h | 60 +++ parse_monitor.c | 117 ++++ parse_monitor.h | 66 +++ parse_neighbor.c | 121 +++++ parse_neighbor.h | 54 ++ xbee.c | 264 +++++++++ xbee.h | 82 +++ xbee_atcmd.c | 684 +++++++++++++++++++++++ xbee_atcmd.h | 54 ++ xbee_buf.c | 185 +++++++ xbee_buf.h | 86 +++ xbee_neighbor.c | 99 ++++ xbee_neighbor.h | 58 ++ xbee_proto.c | 378 +++++++++++++ xbee_proto.h | 153 ++++++ xbee_stats.c | 78 +++ xbee_stats.h | 67 +++ 21 files changed, 4973 insertions(+) create mode 100644 commands.c create mode 100644 main.c create mode 100644 main.h create mode 100644 parse_atcmd.c create mode 100644 parse_atcmd.h create mode 100644 parse_monitor.c create mode 100644 parse_monitor.h create mode 100644 parse_neighbor.c create mode 100644 parse_neighbor.h create mode 100644 xbee.c create mode 100644 xbee.h create mode 100644 xbee_atcmd.c create mode 100644 xbee_atcmd.h create mode 100644 xbee_buf.c create mode 100644 xbee_buf.h create mode 100644 xbee_neighbor.c create mode 100644 xbee_neighbor.h create mode 100644 xbee_proto.c create mode 100644 xbee_proto.h create mode 100644 xbee_stats.c create mode 100644 xbee_stats.h diff --git a/commands.c b/commands.c new file mode 100644 index 0000000..a12ed42 --- /dev/null +++ b/commands.c @@ -0,0 +1,1339 @@ +/* + * Copyright (c) 2009, 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 +#include +#include +#include +#include + +#include "xbee_neighbor.h" +#include "xbee_atcmd.h" +#include "xbee_stats.h" +#include "xbee_buf.h" +#include "xbee_proto.h" +#include "xbee.h" +#include "parse_atcmd.h" +#include "parse_neighbor.h" +#include "parse_monitor.h" +#include "main.h" + +static struct monitor_reg_list monitor_list = LIST_HEAD_INITIALIZER(x/*XXX*/); +static int monitor_period_ms = 1000; +static int monitor_running = 0; +static int monitor_count = 0; +static struct event monitor_event; +struct monitor_reg *monitor_current; + +static int range_period_ms = 1000; +static int range_powermask = 0x1F; +static uint8_t range_power = 0; +static int range_running = 0; +static uint64_t range_dstaddr = 0xFFFF; /* broadcast by default */ +static struct event range_event; +static int range_count = 100; +static int range_cur_count = 0; + +static const char *xbee_logfilename = "/tmp/xbee.log"; + +static void monitor_cb(int s, short event, void *arg) +{ + struct timeval tv; + struct cmdline *cl = arg; + + if (monitor_current == NULL) + monitor_current = LIST_FIRST(&monitor_list); + + xbeeapp_send_atcmd(monitor_current->atcmd, NULL, 0, 0); + monitor_current = LIST_NEXT(monitor_current, next); + + evtimer_set(&monitor_event, monitor_cb, cl); + tv.tv_sec = 0; + tv.tv_usec = (1000 * monitor_period_ms) / monitor_count; + evtimer_add(&monitor_event, &tv); +} + +static void range_cb(int s, short event, void *arg) +{ + struct timeval tv; + struct cmdline *cl = arg; + char buf[16]; + uint8_t i, mask; + + range_cur_count--; + + /* get new xmit power */ + for (i = 1; i <= 8; i++) { + mask = 1 << ((range_power + i) & 0x7); + if (mask & range_powermask) + break; + } + range_power = ((range_power + i) & 0x7); + + xbeeapp_send_atcmd("PL", &range_power, sizeof(range_power), 0); + snprintf(buf, sizeof(buf), "range%d", range_power); + xbeeapp_send_msg(range_dstaddr, buf, strlen(buf), 0); + + if (range_cur_count == 0) { + range_running = 0; + return; + } + + evtimer_set(&range_event, range_cb, cl); + tv.tv_sec = 0; + tv.tv_usec = 1000 * range_period_ms; + evtimer_add(&range_event, &tv); +} + +/* ************* */ + +/* this structure is filled when cmd_stats is parsed successfully */ +struct cmd_stats_result { + cmdline_fixed_string_t stats; + cmdline_fixed_string_t action; +}; + +/* function called when cmd_stats is parsed successfully */ +static void cmd_stats_parsed(void *parsed_result, struct cmdline *cl, void *data) +{ + struct cmd_stats_result *res = parsed_result; + + if (!strcmp(res->action, "show")) { + xbee_dump_stats(stdout, xbee_dev); + if (xbee_logfile != NULL) + xbee_dump_stats(xbee_logfile, xbee_dev); + } + else if (!strcmp(res->action, "reset")) + xbee_reset_stats(xbee_dev); +} + +cmdline_parse_token_string_t cmd_stats_stats = + TOKEN_STRING_INITIALIZER(struct cmd_stats_result, stats, "stats"); +cmdline_parse_token_string_t cmd_stats_action = + TOKEN_STRING_INITIALIZER(struct cmd_stats_result, action, "show#reset"); + +cmdline_parse_inst_t cmd_stats = { + .f = cmd_stats_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Send a stats to the xbee device", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_stats_stats, + (void *)&cmd_stats_action, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_monitor is parsed successfully */ +struct cmd_monitor_result { + cmdline_fixed_string_t monitor; + cmdline_fixed_string_t action; +}; + +/* function called when cmd_monitor is parsed successfully */ +static void cmd_monitor_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_monitor_result *res = parsed_result; + struct monitor_reg *m; + + if (!strcmp(res->action, "show")) { + printf("monitor period is %d ms, %d regs in list\n", + monitor_period_ms, monitor_count); + LIST_FOREACH(m, &monitor_list, next) + printf(" %s\n", m->desc); + } + else if (!strcmp(res->action, "start")) { + struct timeval tv; + if (monitor_running) { + printf("already running\n"); + return; + } + if (monitor_count == 0) { + printf("no regs to be monitored\n"); + return; + } + evtimer_set(&monitor_event, monitor_cb, cl); + tv.tv_sec = 0; + tv.tv_usec = 0; + evtimer_add(&monitor_event, &tv); + monitor_running = 1; + monitor_current = LIST_FIRST(&monitor_list); + } + else if (!strcmp(res->action, "end")) { + if (monitor_running == 0) { + printf("not running\n"); + return; + } + monitor_running = 0; + evtimer_del(&monitor_event); + } +} + +cmdline_parse_token_string_t cmd_monitor_monitor = + TOKEN_STRING_INITIALIZER(struct cmd_monitor_result, monitor, "monitor"); +cmdline_parse_token_string_t cmd_monitor_action = + TOKEN_STRING_INITIALIZER(struct cmd_monitor_result, action, + "show#start#end"); + +cmdline_parse_inst_t cmd_monitor = { + .f = cmd_monitor_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "start/stop/show current monitoring", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_monitor_monitor, + (void *)&cmd_monitor_action, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_monitor_add is parsed successfully */ +struct cmd_monitor_add_result { + cmdline_fixed_string_t monitor; + cmdline_fixed_string_t action; + struct xbee_atcmd *cmd; +}; + +/* function called when cmd_monitor_add is parsed successfully */ +static void cmd_monitor_add_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_monitor_add_result *res = parsed_result; + struct monitor_reg *m; + + LIST_FOREACH(m, &monitor_list, next) { + if (!strcmp(m->desc, res->cmd->desc)) + break; + } + + if (m != NULL) { + printf("already exist\n"); + return; + } + + m = malloc(sizeof(*m)); + if (m == NULL) { + printf("no mem\n"); + return; + } + + m->desc = res->cmd->desc; + m->atcmd = res->cmd->name; + LIST_INSERT_HEAD(&monitor_list, m, next); + monitor_count ++; +} + +cmdline_parse_token_string_t cmd_monitor_add_monitor_add = + TOKEN_STRING_INITIALIZER(struct cmd_monitor_add_result, monitor, + "monitor"); +cmdline_parse_token_string_t cmd_monitor_add_action = + TOKEN_STRING_INITIALIZER(struct cmd_monitor_add_result, action, + "add"); +parse_token_atcmd_t cmd_monitor_add_atcmd = + TOKEN_ATCMD_INITIALIZER(struct cmd_monitor_add_result, cmd, &xbee_dev, + XBEE_ATCMD_F_READ, XBEE_ATCMD_F_READ); + + +cmdline_parse_inst_t cmd_monitor_add = { + .f = cmd_monitor_add_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "add a register in monitor list", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_monitor_add_monitor_add, + (void *)&cmd_monitor_add_action, + (void *)&cmd_monitor_add_atcmd, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_monitor_period is parsed successfully */ +struct cmd_monitor_period_result { + cmdline_fixed_string_t monitor; + cmdline_fixed_string_t action; + uint32_t period; +}; + +/* function called when cmd_monitor_period is parsed successfully */ +static void cmd_monitor_period_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_monitor_period_result *res = parsed_result; + + if (res->period < 100) { + printf("error, minimum period is 100 ms\n"); + return; + } + + monitor_period_ms = res->period; +} + +cmdline_parse_token_string_t cmd_monitor_period_monitor_period = + TOKEN_STRING_INITIALIZER(struct cmd_monitor_period_result, monitor, + "monitor"); +cmdline_parse_token_string_t cmd_monitor_period_action = + TOKEN_STRING_INITIALIZER(struct cmd_monitor_period_result, action, + "period"); +cmdline_parse_token_num_t cmd_monitor_period_period = + TOKEN_NUM_INITIALIZER(struct cmd_monitor_period_result, period, UINT32); + + +cmdline_parse_inst_t cmd_monitor_period = { + .f = cmd_monitor_period_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "set register monitoring period", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_monitor_period_monitor_period, + (void *)&cmd_monitor_period_action, + (void *)&cmd_monitor_period_period, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_monitor_del is parsed successfully */ +struct cmd_monitor_del_result { + cmdline_fixed_string_t monitor; + cmdline_fixed_string_t action; + struct monitor_reg *m; +}; + +/* function called when cmd_monitor_del is parsed successfully */ +static void cmd_monitor_del_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_monitor_del_result *res = parsed_result; + + monitor_current = LIST_NEXT(res->m, next); + LIST_REMOVE(res->m, next); + free(res->m); + monitor_count --; + if (monitor_count == 0) { + printf("Disable monitoring, no more event\n"); + evtimer_del(&monitor_event); + monitor_running = 0; + return; + } +} + +cmdline_parse_token_string_t cmd_monitor_del_monitor_del = + TOKEN_STRING_INITIALIZER(struct cmd_monitor_del_result, monitor, + "monitor"); +cmdline_parse_token_string_t cmd_monitor_del_action = + TOKEN_STRING_INITIALIZER(struct cmd_monitor_del_result, action, + "del"); +parse_token_monitor_t cmd_monitor_del_atcmd = + TOKEN_MONITOR_INITIALIZER(struct cmd_monitor_del_result, m, + &monitor_list); + + +cmdline_parse_inst_t cmd_monitor_del = { + .f = cmd_monitor_del_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "del a register in monitor list", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_monitor_del_monitor_del, + (void *)&cmd_monitor_del_action, + (void *)&cmd_monitor_del_atcmd, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_range is parsed successfully */ +struct cmd_range_result { + cmdline_fixed_string_t range; + cmdline_fixed_string_t action; +}; + +/* function called when cmd_range is parsed successfully */ +static void cmd_range_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_range_result *res = parsed_result; + + if (!strcmp(res->action, "show")) { + printf("range infos:\n"); + printf(" range period %d\n", range_period_ms); + printf(" range count %d\n", range_count); + printf(" range powermask 0x%x\n", range_powermask); + printf(" range dstaddr %"PRIx64"\n", range_dstaddr); + if (range_running) + printf(" range test is running\n"); + else + printf(" range test is not running\n"); + } + else if (!strcmp(res->action, "start")) { + struct timeval tv; + if (range_running) { + printf("already running\n"); + return; + } + range_cur_count = range_count; + evtimer_set(&range_event, range_cb, cl); + tv.tv_sec = 0; + tv.tv_usec = 0; + evtimer_add(&range_event, &tv); + range_running = 1; + } + else if (!strcmp(res->action, "end")) { + if (range_running == 0) { + printf("not running\n"); + return; + } + range_running = 0; + evtimer_del(&range_event); + } +} + +cmdline_parse_token_string_t cmd_range_range = + TOKEN_STRING_INITIALIZER(struct cmd_range_result, range, "range"); +cmdline_parse_token_string_t cmd_range_action = + TOKEN_STRING_INITIALIZER(struct cmd_range_result, action, + "show#start#end"); + +cmdline_parse_inst_t cmd_range = { + .f = cmd_range_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "start/stop/show current rangeing", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_range_range, + (void *)&cmd_range_action, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_range_period is parsed successfully */ +struct cmd_range_period_result { + cmdline_fixed_string_t range; + cmdline_fixed_string_t action; + uint32_t period; +}; + +/* function called when cmd_range_period is parsed successfully */ +static void cmd_range_period_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_range_period_result *res = parsed_result; + + if (res->period < 10) { + printf("error, minimum period is 10 ms\n"); + return; + } + + range_period_ms = res->period; +} + +cmdline_parse_token_string_t cmd_range_period_range_period = + TOKEN_STRING_INITIALIZER(struct cmd_range_period_result, range, + "range"); +cmdline_parse_token_string_t cmd_range_period_action = + TOKEN_STRING_INITIALIZER(struct cmd_range_period_result, action, + "period"); +cmdline_parse_token_num_t cmd_range_period_period = + TOKEN_NUM_INITIALIZER(struct cmd_range_period_result, period, UINT32); + + +cmdline_parse_inst_t cmd_range_period = { + .f = cmd_range_period_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "set range test period", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_range_period_range_period, + (void *)&cmd_range_period_action, + (void *)&cmd_range_period_period, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_range_count is parsed successfully */ +struct cmd_range_count_result { + cmdline_fixed_string_t range; + cmdline_fixed_string_t action; + uint32_t count; +}; + +/* function called when cmd_range_count is parsed successfully */ +static void cmd_range_count_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_range_count_result *res = parsed_result; + range_count = res->count; +} + +cmdline_parse_token_string_t cmd_range_count_range_count = + TOKEN_STRING_INITIALIZER(struct cmd_range_count_result, range, + "range"); +cmdline_parse_token_string_t cmd_range_count_action = + TOKEN_STRING_INITIALIZER(struct cmd_range_count_result, action, + "count"); +cmdline_parse_token_num_t cmd_range_count_count = + TOKEN_NUM_INITIALIZER(struct cmd_range_count_result, count, UINT32); + + +cmdline_parse_inst_t cmd_range_count = { + .f = cmd_range_count_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "set range test count", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_range_count_range_count, + (void *)&cmd_range_count_action, + (void *)&cmd_range_count_count, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_range_powermask is parsed successfully */ +struct cmd_range_powermask_result { + cmdline_fixed_string_t range; + cmdline_fixed_string_t action; + uint8_t powermask; +}; + +/* function called when cmd_range_powermask is parsed successfully */ +static void cmd_range_powermask_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_range_powermask_result *res = parsed_result; + range_powermask = res->powermask; +} + +cmdline_parse_token_string_t cmd_range_powermask_range_powermask = + TOKEN_STRING_INITIALIZER(struct cmd_range_powermask_result, range, + "range"); +cmdline_parse_token_string_t cmd_range_powermask_action = + TOKEN_STRING_INITIALIZER(struct cmd_range_powermask_result, action, + "powermask"); +cmdline_parse_token_num_t cmd_range_powermask_powermask = + TOKEN_NUM_INITIALIZER(struct cmd_range_powermask_result, powermask, + UINT8); + + +cmdline_parse_inst_t cmd_range_powermask = { + .f = cmd_range_powermask_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "set range test powermask", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_range_powermask_range_powermask, + (void *)&cmd_range_powermask_action, + (void *)&cmd_range_powermask_powermask, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_range_dstaddr is parsed successfully */ +struct cmd_range_dstaddr_result { + cmdline_fixed_string_t range; + cmdline_fixed_string_t action; + uint64_t dstaddr; +}; + +/* function called when cmd_range_dstaddr is parsed successfully */ +static void cmd_range_dstaddr_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_range_dstaddr_result *res = parsed_result; + + range_dstaddr = res->dstaddr; +} + +cmdline_parse_token_string_t cmd_range_dstaddr_range_dstaddr = + TOKEN_STRING_INITIALIZER(struct cmd_range_dstaddr_result, range, + "range"); +cmdline_parse_token_string_t cmd_range_dstaddr_action = + TOKEN_STRING_INITIALIZER(struct cmd_range_dstaddr_result, action, + "dstaddr"); +cmdline_parse_token_num_t cmd_range_dstaddr_dstaddr = + TOKEN_NUM_INITIALIZER(struct cmd_range_dstaddr_result, dstaddr, UINT64); + + +cmdline_parse_inst_t cmd_range_dstaddr = { + .f = cmd_range_dstaddr_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "set register rangeing dstaddr", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_range_dstaddr_range_dstaddr, + (void *)&cmd_range_dstaddr_action, + (void *)&cmd_range_dstaddr_dstaddr, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_ping is parsed successfully */ +struct cmd_ping_result { + cmdline_fixed_string_t ping; +}; + +/* function called when cmd_ping is parsed successfully */ +static void cmd_ping_parsed(void *parsed_result, struct cmdline *cl, void *data) +{ + xbeeapp_send_atcmd("VL", NULL, 0, 1); +} + +cmdline_parse_token_string_t cmd_ping_ping = + TOKEN_STRING_INITIALIZER(struct cmd_ping_result, ping, "ping"); + +cmdline_parse_inst_t cmd_ping = { + .f = cmd_ping_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Send a ping to the xbee device", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_ping_ping, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_raw is parsed successfully */ +struct cmd_raw_result { + cmdline_fixed_string_t raw; +}; + +/* function called when cmd_raw is parsed successfully */ +static void cmd_raw_parsed(void *parsed_result, struct cmdline *cl, void *data) +{ + printf("switched to raw mode, CTRL-D to exit\n"); + rdline_stop(&cl->rdl); /* don't display prompt when return */ + xbee_raw = 1; +} + +cmdline_parse_token_string_t cmd_raw_raw = + TOKEN_STRING_INITIALIZER(struct cmd_raw_result, raw, "raw"); + +cmdline_parse_inst_t cmd_raw = { + .f = cmd_raw_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Switch to raw mode", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_raw_raw, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_dump is parsed successfully */ +struct cmd_dump_result { + cmdline_fixed_string_t dump; + cmdline_fixed_string_t onoff; +}; + +/* function called when cmd_dump is parsed successfully */ +static void cmd_dump_parsed(void *parsed_result, struct cmdline *cl, void *data) +{ + struct cmd_dump_result *res = parsed_result; + if (!strcmp(res->onoff, "on")) + xbee_hexdump = 1; + else + xbee_hexdump = 0; +} + +cmdline_parse_token_string_t cmd_dump_dump = + TOKEN_STRING_INITIALIZER(struct cmd_dump_result, dump, "dump"); + +cmdline_parse_token_string_t cmd_dump_onoff = + TOKEN_STRING_INITIALIZER(struct cmd_dump_result, onoff, "on#off"); + +cmdline_parse_inst_t cmd_dump = { + .f = cmd_dump_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "enable/disable hexdump of received packets", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_dump_dump, + (void *)&cmd_dump_onoff, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_debug is parsed successfully */ +struct cmd_debug_result { + cmdline_fixed_string_t debug; + cmdline_fixed_string_t onoff; +}; + +/* function called when cmd_debug is parsed successfully */ +static void cmd_debug_parsed(void *parsed_result, struct cmdline *cl, void *data) +{ + struct cmd_debug_result *res = parsed_result; + if (!strcmp(res->onoff, "on")) + xbee_debug = 1; + else + xbee_debug = 0; +} + +cmdline_parse_token_string_t cmd_debug_debug = + TOKEN_STRING_INITIALIZER(struct cmd_debug_result, debug, "debug"); + +cmdline_parse_token_string_t cmd_debug_onoff = + TOKEN_STRING_INITIALIZER(struct cmd_debug_result, onoff, "on#off"); + +cmdline_parse_inst_t cmd_debug = { + .f = cmd_debug_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "enable/disable additionnal debug", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_debug_debug, + (void *)&cmd_debug_onoff, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_help is parsed successfully */ +struct cmd_help_result { + cmdline_fixed_string_t help; + struct xbee_atcmd *cmd; +}; + +/* function called when cmd_help is parsed successfully */ +static void cmd_help_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_help_result *res = parsed_result; + int type; + + type = (res->cmd->flags & (XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE)); + switch (type) { + case XBEE_ATCMD_F_READ: + printf("Read-only\n"); + break; + case XBEE_ATCMD_F_WRITE: + printf("Write-only\n"); + break; + default: + printf("Read-write\n"); + break; + } + if (res->cmd->flags & XBEE_ATCMD_F_PARAM_NONE) + printf("No argument\n"); + else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U8) + printf("Register is unsigned 8 bits\n"); + else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U16) + printf("Register is unsigned 16 bits\n"); + else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U32) + printf("Register is unsigned 32 bits\n"); + else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_S16) + printf("Register is signed 16 bits\n"); + else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_STRING_20B) + printf("Register is a 20 bytes string\n"); + else + printf("Unknown argument\n"); + + printf("%s\n", res->cmd->help); +} + +cmdline_parse_token_string_t cmd_help_help = + TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help"); + +parse_token_atcmd_t cmd_help_atcmd = + TOKEN_ATCMD_INITIALIZER(struct cmd_help_result, cmd, &xbee_dev, + 0, 0); + +cmdline_parse_inst_t cmd_help = { + .f = cmd_help_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Help a register using an AT command", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_help_help, + (void *)&cmd_help_atcmd, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_read is parsed successfully */ +struct cmd_read_result { + cmdline_fixed_string_t read; + struct xbee_atcmd *cmd; +}; + +/* function called when cmd_read is parsed successfully */ +static void cmd_read_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_read_result *res = parsed_result; + xbeeapp_send_atcmd(res->cmd->name, NULL, 0, 1); +} + +cmdline_parse_token_string_t cmd_read_read = + TOKEN_STRING_INITIALIZER(struct cmd_read_result, read, "read"); + +parse_token_atcmd_t cmd_read_atcmd = + TOKEN_ATCMD_INITIALIZER(struct cmd_read_result, cmd, &xbee_dev, + XBEE_ATCMD_F_READ, XBEE_ATCMD_F_READ); + +cmdline_parse_inst_t cmd_read = { + .f = cmd_read_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Read a register using an AT command", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_read_read, + (void *)&cmd_read_atcmd, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_write is parsed successfully */ +struct cmd_write_result { + cmdline_fixed_string_t write; + struct xbee_atcmd *cmd; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + }; +}; + +/* function called when cmd_write is parsed successfully */ +static void cmd_write_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_write_result *res = parsed_result; + int len; + void *param; + + if (res->cmd->flags & XBEE_ATCMD_F_PARAM_NONE) { + len = 0; + param = NULL; + } + else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U8) { + len = sizeof(res->u8); + param = &res->u8; + } + else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U16) { + len = sizeof(res->u16); + res->u16 = htons(res->u16); + param = &res->u16; + } + else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U32) { + len = sizeof(res->u32); + res->u32 = htonl(res->u32); + param = &res->u32; + } + else { + printf("Unknown argument type\n"); + return; + } + xbeeapp_send_atcmd(res->cmd->name, param, len, 1); +} + +cmdline_parse_token_string_t cmd_write_write = + TOKEN_STRING_INITIALIZER(struct cmd_write_result, write, + "write"); + +parse_token_atcmd_t cmd_write_none_atcmd = + TOKEN_ATCMD_INITIALIZER(struct cmd_write_result, cmd, + &xbee_dev, + XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_NONE, + XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_NONE); + +cmdline_parse_inst_t cmd_write_none = { + .f = cmd_write_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Send an AT command (no argument)", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_write_write, + (void *)&cmd_write_none_atcmd, + NULL, + }, +}; + +parse_token_atcmd_t cmd_write_u8_atcmd = + TOKEN_ATCMD_INITIALIZER(struct cmd_write_result, cmd, + &xbee_dev, + XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U8, + XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U8); + +cmdline_parse_token_num_t cmd_write_u8_u8 = + TOKEN_NUM_INITIALIZER(struct cmd_write_result, u8, UINT8); + +cmdline_parse_inst_t cmd_write_u8 = { + .f = cmd_write_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Write a 8 bits register using an AT command", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_write_write, + (void *)&cmd_write_u8_atcmd, + (void *)&cmd_write_u8_u8, + NULL, + }, +}; + +parse_token_atcmd_t cmd_write_u16_atcmd = + TOKEN_ATCMD_INITIALIZER(struct cmd_write_result, cmd, + &xbee_dev, + XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U16, + XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U16); + +cmdline_parse_token_num_t cmd_write_u16_u16 = + TOKEN_NUM_INITIALIZER(struct cmd_write_result, u16, UINT16); + +cmdline_parse_inst_t cmd_write_u16 = { + .f = cmd_write_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Write a 16 bits register using an AT command", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_write_write, + (void *)&cmd_write_u16_atcmd, + (void *)&cmd_write_u16_u16, + NULL, + }, +}; + +parse_token_atcmd_t cmd_write_u32_atcmd = + TOKEN_ATCMD_INITIALIZER(struct cmd_write_result, cmd, + &xbee_dev, + XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U32, + XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U32); + +cmdline_parse_token_num_t cmd_write_u32_u32 = + TOKEN_NUM_INITIALIZER(struct cmd_write_result, u32, UINT32); + +cmdline_parse_inst_t cmd_write_u32 = { + .f = cmd_write_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Write a 32 bits register using an AT command", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_write_write, + (void *)&cmd_write_u32_atcmd, + (void *)&cmd_write_u32_u32, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_sendmsg is parsed successfully */ +struct cmd_sendmsg_result { + cmdline_fixed_string_t sendmsg; + uint64_t addr; + cmdline_fixed_string_t data; +}; + +/* function called when cmd_sendmsg is parsed successfully */ +static void cmd_sendmsg_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_sendmsg_result *res = parsed_result; + xbeeapp_send_msg(res->addr, res->data, strlen(res->data), 1); +} + +cmdline_parse_token_string_t cmd_sendmsg_sendmsg = + TOKEN_STRING_INITIALIZER(struct cmd_sendmsg_result, sendmsg, "sendmsg"); + +cmdline_parse_token_num_t cmd_sendmsg_addr = + TOKEN_NUM_INITIALIZER(struct cmd_sendmsg_result, addr, UINT64); + +cmdline_parse_token_string_t cmd_sendmsg_data = + TOKEN_STRING_INITIALIZER(struct cmd_sendmsg_result, data, NULL); + +cmdline_parse_inst_t cmd_sendmsg = { + .f = cmd_sendmsg_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Send data to a node using its address", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_sendmsg_sendmsg, + (void *)&cmd_sendmsg_addr, + (void *)&cmd_sendmsg_data, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_sendmsg_name is parsed successfully */ +struct cmd_sendmsg_name_result { + cmdline_fixed_string_t sendmsg_name; + struct xbee_neigh *neigh; + cmdline_fixed_string_t data; +}; + +/* function called when cmd_sendmsg_name is parsed successfully */ +static void cmd_sendmsg_name_parsed(void *parsed_result, struct cmdline *cl, + void *data) +{ + struct cmd_sendmsg_name_result *res = parsed_result; + xbeeapp_send_msg(res->neigh->addr, res->data, strlen(res->data), 1); +} + +cmdline_parse_token_string_t cmd_sendmsg_name_sendmsg_name = + TOKEN_STRING_INITIALIZER(struct cmd_sendmsg_name_result, sendmsg_name, + "sendmsg"); + +parse_token_neighbor_t cmd_sendmsg_name_neigh = + TOKEN_NEIGHBOR_INITIALIZER(struct cmd_sendmsg_name_result, neigh, + &xbee_dev); + +cmdline_parse_token_string_t cmd_sendmsg_name_data = + TOKEN_STRING_INITIALIZER(struct cmd_sendmsg_name_result, data, NULL); + +cmdline_parse_inst_t cmd_sendmsg_name = { + .f = cmd_sendmsg_name_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Send data to a node using its name", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_sendmsg_name_sendmsg_name, + (void *)&cmd_sendmsg_name_neigh, + (void *)&cmd_sendmsg_name_data, + NULL, + }, +}; + +/* ************* */ + +struct cmd_neigh_del_result { + cmdline_fixed_string_t cmd; + cmdline_fixed_string_t action; + struct xbee_neigh *neigh; +}; + +static void cmd_neigh_del_parsed(void *parsed_result, + struct cmdline *cl, + void *data) +{ + struct cmd_neigh_del_result *res = parsed_result; + xbee_neigh_del(xbee_dev, res->neigh); +} + +cmdline_parse_token_string_t cmd_neigh_del_cmd = + TOKEN_STRING_INITIALIZER(struct cmd_neigh_del_result, cmd, "neigh"); +cmdline_parse_token_string_t cmd_neigh_del_action = + TOKEN_STRING_INITIALIZER(struct cmd_neigh_del_result, action, "del"); +parse_token_neighbor_t cmd_neigh_del_neigh = + TOKEN_NEIGHBOR_INITIALIZER(struct cmd_neigh_del_result, neigh, + &xbee_dev); + +cmdline_parse_inst_t cmd_neigh_del = { + .f = cmd_neigh_del_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "delete a neighbor", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_neigh_del_cmd, + (void *)&cmd_neigh_del_action, + (void *)&cmd_neigh_del_neigh, + NULL, + }, +}; + +/* ************* */ + +struct cmd_neigh_add_result { + cmdline_fixed_string_t cmd; + cmdline_fixed_string_t action; + cmdline_fixed_string_t name; + uint64_t addr; +}; + +static void cmd_neigh_add_parsed(void *parsed_result, + struct cmdline *cl, + void *data) +{ + struct cmd_neigh_add_result *res = parsed_result; + if (xbee_neigh_add(xbee_dev, res->name, res->addr) == NULL) + printf("name or addr already exist\n"); +} + +cmdline_parse_token_string_t cmd_neigh_add_cmd = + TOKEN_STRING_INITIALIZER(struct cmd_neigh_add_result, cmd, "neigh"); +cmdline_parse_token_string_t cmd_neigh_add_action = + TOKEN_STRING_INITIALIZER(struct cmd_neigh_add_result, action, "add"); +cmdline_parse_token_string_t cmd_neigh_add_name = + TOKEN_STRING_INITIALIZER(struct cmd_neigh_add_result, name, NULL); +cmdline_parse_token_num_t cmd_neigh_add_addr = + TOKEN_NUM_INITIALIZER(struct cmd_neigh_add_result, addr, UINT64); + +cmdline_parse_inst_t cmd_neigh_add = { + .f = cmd_neigh_add_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "add a neighbor", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_neigh_add_cmd, + (void *)&cmd_neigh_add_action, + (void *)&cmd_neigh_add_name, + (void *)&cmd_neigh_add_addr, + NULL, + }, +}; + +/* ************* */ + +struct cmd_neigh_list_result { + cmdline_fixed_string_t cmd; + cmdline_fixed_string_t action; +}; + +static void cmd_neigh_list_parsed(void *parsed_result, + struct cmdline *cl, + void *data) +{ + struct xbee_neigh *neigh; + + LIST_FOREACH(neigh, &xbee_dev->neigh_list, next) { + printf(" %s: 0x%"PRIx64"\n", neigh->name, neigh->addr); + } +} + +cmdline_parse_token_string_t cmd_neigh_list_cmd = + TOKEN_STRING_INITIALIZER(struct cmd_neigh_list_result, cmd, "neigh"); +cmdline_parse_token_string_t cmd_neigh_list_action = + TOKEN_STRING_INITIALIZER(struct cmd_neigh_list_result, action, "list"); + +cmdline_parse_inst_t cmd_neigh_list = { + .f = cmd_neigh_list_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "list all known neighbors", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_neigh_list_cmd, + (void *)&cmd_neigh_list_action, + NULL, + }, +}; + +/*******************/ + +struct cmd_logfile_result { + cmdline_fixed_string_t logfile; + cmdline_filename_t file; +}; + +static void cmd_logfile_parsed(void *parsed_result, + struct cmdline *cl, + void *data) +{ + if (xbee_logfile != NULL) + fclose(xbee_logfile); + xbee_logfile = fopen(xbee_logfilename, "a"); + if (xbee_logfile == NULL) + printf("cannot open file: %s\n", strerror(errno)); + fprintf(xbee_logfile, "-------------------start\n"); + printf("enabling log\n"); +} + +cmdline_parse_token_string_t cmd_logfile_logfile = + TOKEN_STRING_INITIALIZER(struct cmd_logfile_result, logfile, "logfile"); + +cmdline_parse_token_file_t cmd_logfile_file = + TOKEN_FILE_INITIALIZER(struct cmd_logfile_result, file, + PARSE_FILE_F_CREATE); + +cmdline_parse_inst_t cmd_logfile = { + .f = cmd_logfile_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = " set log file", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_logfile_logfile, + (void *)&cmd_logfile_file, + NULL, + }, +}; + +/* ************* */ + +/* this structure is filled when cmd_log is parsed successfully */ +struct cmd_log_result { + cmdline_fixed_string_t log; + cmdline_fixed_string_t onoff; +}; + +/* function called when cmd_log is parsed successfully */ +static void cmd_log_parsed(void *parsed_result, struct cmdline *cl, void *data) +{ + struct cmd_log_result *res = parsed_result; + if (!strcmp(res->onoff, "on") && xbee_logfile == NULL) { + xbee_logfile = fopen(xbee_logfilename, "a"); + if (xbee_logfile == NULL) + printf("cannot open file: %s\n", strerror(errno)); + fprintf(xbee_logfile, "-------------------start\n"); + } + else if (!strcmp(res->onoff, "off") && xbee_logfile != NULL) { + fclose(xbee_logfile); + xbee_logfile = NULL; + } +} + +cmdline_parse_token_string_t cmd_log_log = + TOKEN_STRING_INITIALIZER(struct cmd_log_result, log, "log"); + +cmdline_parse_token_string_t cmd_log_onoff = + TOKEN_STRING_INITIALIZER(struct cmd_log_result, onoff, "on#off"); + +cmdline_parse_inst_t cmd_log = { + .f = cmd_log_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "enable/disable hexlog of received packets", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_log_log, + (void *)&cmd_log_onoff, + NULL, + }, +}; + + +/*******************/ + +struct cmd_saveconfig_result { + cmdline_fixed_string_t saveconfig; + cmdline_filename_t file; +}; + +static void cmd_saveconfig_parsed(void *parsed_result, + struct cmdline *cl, + void *data) +{ + struct cmd_saveconfig_result *res = parsed_result; + + if (xbeeapp_dump_config(res->file) < 0) + printf("cannot save config\n"); +} + +cmdline_parse_token_string_t cmd_saveconfig_saveconfig = + TOKEN_STRING_INITIALIZER(struct cmd_saveconfig_result, saveconfig, + "saveconfig"); + +cmdline_parse_token_file_t cmd_saveconfig_file = + TOKEN_FILE_INITIALIZER(struct cmd_saveconfig_result, file, + PARSE_FILE_F_CREATE); + +cmdline_parse_inst_t cmd_saveconfig = { + .f = cmd_saveconfig_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = " set log file", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_saveconfig_saveconfig, + (void *)&cmd_saveconfig_file, + NULL, + }, +}; + +/*******************/ + +struct cmd_loadconfig_result { + cmdline_fixed_string_t loadconfig; + cmdline_filename_t file; +}; + +static void cmd_loadconfig_parsed(void *parsed_result, + struct cmdline *cl, + void *data) +{ +} + +cmdline_parse_token_string_t cmd_loadconfig_loadconfig = + TOKEN_STRING_INITIALIZER(struct cmd_loadconfig_result, loadconfig, + "loadconfig"); + +cmdline_parse_token_file_t cmd_loadconfig_file = + TOKEN_FILE_INITIALIZER(struct cmd_loadconfig_result, file, + PARSE_FILE_F_CREATE); + +cmdline_parse_inst_t cmd_loadconfig = { + .f = cmd_loadconfig_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = " set log file", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_loadconfig_loadconfig, + (void *)&cmd_loadconfig_file, + NULL, + }, +}; + +/**********************************************************/ +/**********************************************************/ +/****** CONTEXT (list of instruction) */ + +/* in progmem */ +cmdline_parse_ctx_t main_ctx = { + .name = "main", + .insts = { + &cmd_stats, + &cmd_monitor, + &cmd_monitor_period, + &cmd_monitor_add, + &cmd_monitor_del, + &cmd_range, + &cmd_range_period, + &cmd_range_count, + &cmd_range_powermask, + &cmd_range_dstaddr, + &cmd_ping, + &cmd_raw, + &cmd_dump, + &cmd_debug, + &cmd_help, + &cmd_read, + &cmd_write_none, + &cmd_write_u8, + &cmd_write_u16, + &cmd_write_u32, + &cmd_sendmsg, + &cmd_sendmsg_name, + &cmd_neigh_del, + &cmd_neigh_add, + &cmd_neigh_list, + &cmd_logfile, + &cmd_log, + &cmd_saveconfig, + &cmd_loadconfig, + NULL, + }, +}; diff --git a/main.c b/main.c new file mode 100644 index 0000000..52c0d80 --- /dev/null +++ b/main.c @@ -0,0 +1,829 @@ +/* + * Copyright (c) 2011, 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. + */ + +#define _BSD_SOURCE /* for be64toh, endian.h */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include "xbee_neighbor.h" +#include "xbee_atcmd.h" +#include "xbee_stats.h" +#include "xbee_buf.h" +#include "xbee_proto.h" +#include "xbee.h" +#include "main.h" + +#define TIMEOUT_US 1000000 + +/* XXX qd on deconnecte, prend 100% du cpu */ +/* XXX rmt at cmd */ +/* XXX neighbor discovery */ +/* XXX dump et restauration de la config */ + +/* global xbee device */ +static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx, + struct xbee_atresp_hdr *frame, + unsigned len); + +struct xbee_dev *xbee_dev; + +/* events */ +static struct event stdin_read_event, xbee_read_event; + +static struct cmdline *xbee_cl; + +/* parameters */ +static char *xbee_devname = NULL; +static int xbee_baud = 9600; +int xbee_raw = 0; +int xbee_hexdump = 0; +int xbee_debug = 0; +FILE *xbee_logfile; + +void xbeeapp_log(int always_on_stdout, const char *fmt, ...) +{ + va_list ap, ap2; + + va_start(ap, fmt); + va_copy(ap2, ap); + if (xbee_logfile != NULL) + vfprintf(xbee_logfile, fmt, ap); + if (always_on_stdout || xbee_debug) + vprintf(fmt, ap2); + va_end(ap2); + va_end(ap); +} + +static void hexdump(int always_on_stdout, 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 */ + struct timeval tv; + + gettimeofday(&tv, NULL); + + xbeeapp_log(always_on_stdout, "%"PRIi64".%.6d %s at [%p], len=%d\n", + (uint64_t)tv.tv_sec, (int)tv.tv_usec, title, data, len); + ofs = 0; + while (ofs < len) { + /* format 1 line in the buffer, then use printk to print them */ + out = snprintf(line, LINE_LEN, "%08X", ofs); + for (i=0; ofs+i < len && i<16; i++) + out += snprintf(line+out, LINE_LEN - out, " %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(line+out, LINE_LEN - out, "%c", c); + } + xbeeapp_log(always_on_stdout, "%s\n", line); + } +} + +static int parse_xmit_status(struct xbee_ctx *ctx, + struct xbee_xmit_status_hdr *frame, unsigned len) +{ + if (ctx == NULL) { + xbeeapp_log(1, "no context\n"); + return -1; + } + + /* see if it matches a xmit query */ + if (ctx->type != SEND_MSG) { + xbeeapp_log(ctx->foreground, "invalid response\n"); + return -1; + } + + /* XXX use defines for these values */ + if (frame->delivery_status == 0x00) + xbeeapp_log(ctx->foreground, "Success\n"); + else if (frame->delivery_status == 0x01) + xbeeapp_log(ctx->foreground, "MAC ACK Failure\n"); + else if (frame->delivery_status == 0x15) + xbeeapp_log(ctx->foreground, "Invalid destination endpoint\n"); + else if (frame->delivery_status == 0x21) + xbeeapp_log(ctx->foreground, "Network ACK Failure\n"); + else if (frame->delivery_status == 0x25) + xbeeapp_log(ctx->foreground, "Route Not Found\n"); + + return 0; +} + +static int atcmd_frame_status(struct xbee_atresp_hdr *frame, unsigned len) +{ + char atcmd_str[3]; + + /* get AT command from frame */ + memcpy(atcmd_str, &frame->cmd, 2); + atcmd_str[2] = '\0'; + + if (frame->status == 1) + xbeeapp_log(1, "<%s>: Status is error\n", atcmd_str); + else if (frame->status == 2) + xbeeapp_log(1, "<%s>: Invalid command\n", atcmd_str); + else if (frame->status == 3) + xbeeapp_log(1, "<%s>: Invalid parameter\n", atcmd_str); + else if (frame->status != 0) + xbeeapp_log(1, "<%s>: Unknown status error %d\n", atcmd_str, + frame->status); + return frame->status; +} + +/* assume frame->status is 0 */ +static int atcmd_frame_to_string(struct xbee_atresp_hdr *frame, unsigned len, + char *dstbuf, unsigned dstlen) +{ + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + int16_t s16; + } __attribute__((packed)) *result; + char atcmd_str[3]; + struct xbee_atcmd *cmd; + + /* get AT command from frame */ + memcpy(atcmd_str, &frame->cmd, 2); + atcmd_str[2] = '\0'; + + /* see if it exists */ + cmd = xbee_atcmd_lookup_name(atcmd_str); + if (cmd == NULL) { + xbeeapp_log(1, "unknown response\n"); + return -1; + } + + /* dump frame */ + result = (void *)frame->data; + len -= offsetof(struct xbee_atresp_hdr, data); + if (cmd->flags & XBEE_ATCMD_F_PARAM_NONE && len == 0) + snprintf(dstbuf, dstlen, "ok"); + if (cmd->flags & XBEE_ATCMD_F_PARAM_U8 && len == sizeof(uint8_t)) + snprintf(dstbuf, dstlen, "0x%x", result->u8); + else if (cmd->flags & XBEE_ATCMD_F_PARAM_U16 && len == sizeof(uint16_t)) + snprintf(dstbuf, dstlen, "0x%x", ntohs(result->u16)); + else if (cmd->flags & XBEE_ATCMD_F_PARAM_U32 && len == sizeof(uint32_t)) + snprintf(dstbuf, dstlen, "0x%x", ntohl(result->u32)); + else if (cmd->flags & XBEE_ATCMD_F_PARAM_S16 && len == sizeof(int16_t)) + snprintf(dstbuf, dstlen, "%d\n", ntohs(result->s16)); + else + return -1; + + return 0; +} + +static int dump_atcmd(struct xbee_ctx *ctx, struct xbee_atresp_hdr *frame, + unsigned len) +{ + char buf[32]; + + /* see if it matches query */ + if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) { + printf("invalid response\n"); + return -1; + } + + /* dump frame */ + if (atcmd_frame_status(frame, len) == 0) { + + if (len == sizeof(struct xbee_atresp_hdr)) + xbeeapp_log(ctx->foreground, "<%s>: ok\n", + ctx->atcmd_query->name); + else if (atcmd_frame_to_string(frame, len, buf, + sizeof(buf)) == 0) + xbeeapp_log(ctx->foreground, "<%s> is %s\n", + ctx->atcmd_query->name, buf); + else + hexdump(ctx->foreground, "atcmd answer", frame, len); + } + return 0; +} + +void xbee_rx(struct xbee_dev *dev, int channel, int type, + void *frame, unsigned len, void *opaque) +{ + struct xbee_ctx *ctx = opaque; + int do_hexdump = xbee_hexdump; + + xbeeapp_log(0, "type=0x%x, channel=%d, ctx=%p\n", type, channel, ctx); + hexdump(0, "rx", frame, len); + + /* 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 != NULL) + printf("Received answer to query <%s>\n", + ctx->atcmd_query->name); + xbee_unregister_channel(dev, channel); + } + + /* some additional checks before sending */ + switch (type) { + case XBEE_TYPE_MODEM_STATUS: { + printf("Received Modem Status frame\n"); + break; + } + + case XBEE_TYPE_RMT_ATRESP: { + uint64_t addr; + memcpy(&addr, frame, sizeof(addr)); + addr = be64toh(addr); + printf("from remote address %"PRIx64"\n", addr); + + /* this answer contains an atcmd answer at offset 10 */ + if (ctx != NULL && ctx->type == ATCMD_RMT) { + if (dump_atcmd(ctx, frame + 10, len - 10) < 0) + do_hexdump = 1; + } + else { + printf("invalid response\n"); + do_hexdump = 1; + } + break; + } + case XBEE_TYPE_ATRESP: { + /* we are currently dumping config, continue */ + if (ctx != NULL && ctx->type == DUMP_CONF) { + xbeeapp_dump_conf_continue(ctx, frame, len); + } + else if (ctx != NULL && ctx->type == ATCMD) { + if (dump_atcmd(ctx, frame, len) < 0) + do_hexdump = 1; + } + else { + printf("invalid response\n"); + do_hexdump = 1; + } + break; + } + + case XBEE_TYPE_XMIT_STATUS: { + if (parse_xmit_status(ctx, frame, len) < 0) + do_hexdump = 1; + break; + } + + case XBEE_TYPE_RECV: { + struct xbee_recv_hdr *recvframe = frame; + int recvlen = len - sizeof(*recvframe); + int on_stdout = 1; + + /* if we receive a range-test frame, ask for RSSI now */ + if (recvlen >= strlen("range") && + !strncmp((char *)recvframe->data, + "range", strlen("range"))) { + xbeeapp_send_atcmd("DB", NULL, 0, 0); + on_stdout = 0; + } + hexdump(on_stdout, "rx data", recvframe->data, + recvlen); + 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: + xbeeapp_log(0, "Invalid frame\n"); + do_hexdump = 1; + break; + } + + if (do_hexdump) { + xbeeapp_log(1, "rx frame type=0x%x, channel=%d, ctx=%p\n", + type, channel, ctx); + hexdump(1, "undecoded rx frame", frame, len); + } + + /* restart command line if it was a blocking query */ + if (ctx != NULL) { + if (ctx->foreground && !ctx->have_more_command) { + xbee_stdin_enable(); + rdline_newline(&xbee_cl->rdl, xbee_cl->prompt); + } + if (!ctx->have_more_command) + free(ctx); + } +} + +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("frame too large\n"); + return -1; + } + + /* register a channel */ + channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY, + xbee_rx, ctx); + if (channel < 0) { + printf("cannot send: no free channel\n"); + return -1; + } + + xbeeapp_log(0, "send frame ctx=%p channel=%d type=0x%x len=%d\n", + ctx, channel, type, len); + hexdump(0, "xmit frame", buf, len); + + /* transmit the frame on this channel */ + ret = xbee_proto_xmit(xbee_dev, channel, type, buf, + len); + if (ret < 0) { + printf("cannot send: %s\n", strerror(errno)); + 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(&xbee_cl->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) +{ + struct xbee_ctx *ctx; + struct xbee_atcmd *cmd; + struct { + struct xbee_atcmd_hdr atcmd; + char buf[XBEE_MAX_FRAME_LEN]; + } __attribute__((packed)) frame; + + cmd = xbee_atcmd_lookup_name(atcmd_str); + if (cmd == NULL) { + printf("no such at command\n"); + return -1; + } + + /* allocate memory to store the context for async cb */ + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("not enough memory\n"); + return -1; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->type = ATCMD; + ctx->atcmd_query = cmd; + + 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) { + free(ctx); + return -1; + } + + return 0; +} + +/* dump config */ +static int __xbeeapp_dump_config(struct xbee_ctx *ctx, struct xbee_atcmd *cmd) +{ + struct xbee_atcmd_hdr atcmd; + + /* find the first command that is readable and writable */ + for (; cmd->name != NULL; cmd++) { + if ((cmd->flags & (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE)) == + (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE)) + break; + } + if (cmd->name == NULL) { + printf("no register to dump\n"); + return -1; + } + + ctx->atcmd_query = cmd; + ctx->have_more_command = 1; + memcpy(&atcmd.cmd, cmd->name, 2); + + if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &atcmd, + sizeof(struct xbee_atcmd_hdr), 1) < 0) { + return -1; + } + + return 0; +} + +/* dump config */ +int xbeeapp_dump_config(const char *filename) +{ + struct xbee_ctx *ctx; + struct xbee_atcmd *cmd; + int ret; + + /* find the first command that is readable and writable */ + cmd = &xbee_atcmd_list[0]; + + /* allocate memory to store the context for async cb */ + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("not enough memory\n"); + return -1; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->type = DUMP_CONF; + ret = __xbeeapp_dump_config(ctx, cmd); + + if (ret < 0) + free(ctx); + return ret; +} + +/* continue a dump conf */ +static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx, + struct xbee_atresp_hdr *frame, + unsigned len) +{ + struct xbee_atcmd *cmd; + char buf[32]; + + /* see if it matches query */ + if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) { + printf("invalid response\n"); + ctx->have_more_command = 0; + return -1; + } + + /* dump register content in buf, skip on error */ + if (atcmd_frame_status(frame, len) == 0 && + atcmd_frame_to_string(frame, len, buf, sizeof(buf)) == 0) + printf("write %s %s\n", ctx->atcmd_query->desc, buf); + + cmd = ctx->atcmd_query + 1; + if (__xbeeapp_dump_config(ctx, cmd) < 0) { + ctx->have_more_command = 0; + //close(xbee_config_file); + printf("END\n"); /* XXX */ + } + return 0; +} + +/* send data message */ +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; + + /* allocate memory to store the context for async cb */ + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("not enough memory\n"); + return -1; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->type = SEND_MSG; + ctx->atcmd_query = NULL; + + frame.xmit.dstaddr = htobe64(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) { + free(ctx); + return -1; + } + + return 0; +} + +void xbee_stdin_enable(void) +{ + event_add(&stdin_read_event, NULL); +} + +void xbee_stdin_disable(void) +{ + event_del(&stdin_read_event); +} + +static void evt_timeout(int s, short event, void *arg) +{ + struct xbee_ctx *ctx = arg; + + xbeeapp_log(0, "Timeout\n"); + + /* restart command line */ + if (ctx->foreground) { + if (xbee_debug == 0) + printf("Timeout\n"); + xbee_stdin_enable(); + rdline_newline(&xbee_cl->rdl, xbee_cl->prompt); + } + /* free event */ + xbee_unregister_channel(xbee_dev, ctx->channel); + free(ctx); +} + +void xbee_load_timeout(struct xbee_ctx *ctx) +{ + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = TIMEOUT_US; + evtimer_set(&ctx->timeout, evt_timeout, ctx); + evtimer_add(&ctx->timeout, &tv); +} + +void xbee_unload_timeout(struct xbee_ctx *ctx) +{ + evtimer_del(&ctx->timeout); +} + +static void +evt_stdin_input(int s, short event, void *arg) +{ + char buf[2048]; + int n; + + n = read(s, buf, sizeof(buf)); + if (n < 0) { + event_loopbreak(); + return; + } + + if (xbee_raw) { + int i; + for (i = 0; i < n; i++) { + /* ctrl-d is pressed, back in cmdline mode XXX */ + if (buf[i] == 4) { + xbee_raw = 0; + n = i; + rdline_newline(&xbee_cl->rdl, xbee_cl->prompt); + break; + } + } + /* XXX bad hack */ + if (buf[0] == '\n') + write(xbee_dev->fd, "\r\n", 2); + else + write(xbee_dev->fd, buf, n); + write(s, buf, n); + + } + else { + if (cmdline_in(xbee_cl, buf, n) < 0) { + event_loopbreak(); + return; + } + } +} + +static void +evt_xbee_input(int s, short event, void *arg) +{ + if (xbee_raw) { + char buf[2048]; + int n; + + n = read(s, buf, sizeof(buf)); + if (n < 0) + return; + + write(1, buf, n); + } + else { + xbee_read(xbee_dev); + xbee_process_queue(xbee_dev); + } +} + +static void +usage(const char *prgname) +{ + printf("%s [-s BAUD] -d DEVNAME\n" + " -s BAUD: set baud rate of xbee device\n" + " -d DEVNAME: xbee char device\n" + " -r: start in raw mode\n" + "\n", + prgname); +} + +/* Parse the argument given in the command line of the application */ +static int +parse_args(int argc, char **argv) +{ + int opt, ret; + char **argvopt; + int option_index; + char *prgname = argv[0]; + static struct option lgopts[] = { + {0, 0, 0, 0} + }; + + argvopt = argv; + + while ((opt = getopt_long(argc, argvopt, "hd:s:", + lgopts, &option_index)) != EOF) { + + switch (opt) { + + case 'h': + usage(prgname); + exit(0); + break; + + case 'd': + xbee_devname = strdup(optarg); + break; + + case 's': + xbee_baud = atoi(optarg); + break; + + case 'r': + xbee_raw = 1; + break; + + /* long options */ + case 0: + /* if (!strcmp(lgopts[option_index].name, "option")) */ + break; + + default: + usage(prgname); + return -1; + } + } + + if (xbee_devname == NULL) { + fprintf(stderr, "xbee device argument missing\n"); + usage(prgname); + return -1; + } + + if (argc != optind) { + printf("Invalid argument\n"); + usage(prgname); + return -1; + } + + ret = optind-1; + + return ret; +} + +int main(int argc, char **argv) +{ + struct termios oldterm, term; + int err = 0; + const char *homedir; + char xbeerc_path[256]; + + if (parse_args(argc, argv) < 0) + exit(1); + + /* initializa libevent */ + event_init(); + + /* initialize libxbee */ + err = xbee_init(); + if (err < 0) + return -1; + + /* open xbee device */ + xbee_dev = xbee_open(xbee_devname, xbee_baud); + if (xbee_dev == NULL) + return -1; + + /* register default channel with a callback */ + if (xbee_register_channel(xbee_dev, XBEE_DEFAULT_CHANNEL, + xbee_rx, NULL) < 0) { + fprintf(stderr, "cannot register default channel\n"); + return -1; + } + + /* add read event on xbee device */ + event_set(&xbee_read_event, xbee_dev->fd, EV_READ | EV_PERSIST, + evt_xbee_input, xbee_dev); + event_add(&xbee_read_event, NULL); + + /* set terminal in raw mode */ + tcgetattr(0, &oldterm); + memcpy(&term, &oldterm, sizeof(term)); + term.c_lflag &= ~(ICANON | ECHO | ISIG); + tcsetattr(0, TCSANOW, &term); + setbuf(stdin, NULL); + + /* parse the config file */ + homedir = getenv("HOME"); + if (homedir != NULL) { + snprintf(xbeerc_path, sizeof(xbeerc_path), "%s/.xbeerc", + homedir); + if (access(xbeerc_path, R_OK) == 0) { + + xbee_cl = cmdline_file_new(&main_ctx, "xbeerc> ", + "/home/zer0/.xbeerc", 1); + if (xbee_cl != NULL) { + cmdline_interact(xbee_cl); + cmdline_free(xbee_cl); + printf("\nconfig file parsed\n"); + } + } + } + + /* create a new cmdline instance */ + xbee_cl = cmdline_stdin_new(&main_ctx, "xbee> "); + if (xbee_cl == NULL) { + err = 1; + goto fail; + } + + /* load read event on stdin */ + event_set(&stdin_read_event, 0, EV_READ | EV_PERSIST, + evt_stdin_input, xbee_cl); + event_add(&stdin_read_event, NULL); + + /* libevent main loop */ + event_dispatch(); + + tcsetattr(0, TCSANOW, &oldterm); + return 0; + + fail: + tcsetattr(0, TCSANOW, &oldterm); + return err; +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..548cc62 --- /dev/null +++ b/main.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011, 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. + */ + +enum xbee_ctx_type { + SEND_MSG, + ATCMD, + ATCMD_RMT, + DUMP_CONF, +}; + +/* used for timeouts and xbee rx callback */ +struct xbee_ctx { + enum xbee_ctx_type type; + int have_more_command; + int foreground; + int channel; + struct event timeout; + struct xbee_atcmd *atcmd_query; +}; + +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 FILE *xbee_logfile; + +#define XBEE_LOG_FILE "/home/zer0/xbee.log" +extern FILE *xbee_log_file; + +void xbeeapp_log(int always_on_stdout, const char *fmt, ...); + +void xbee_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 xbeeapp_send_msg(uint64_t addr, void *data, unsigned data_len, + int foreground); +int xbeeapp_dump_config(const char *filename); + +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); diff --git a/parse_atcmd.c b/parse_atcmd.c new file mode 100644 index 0000000..ffde312 --- /dev/null +++ b/parse_atcmd.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2011, 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 "xbee_atcmd.h" +#include "parse_atcmd.h" + +/* This file is an example of extension of libcmdline. It provides an + * example of named objects stored in a list, supporting the + * completion on objects name */ + +static int +parse_atcmd(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, + unsigned ressize) +{ + struct token_atcmd *tk2 = (struct token_atcmd *)tk; + struct token_atcmd_data *tkd = &tk2->atcmd_data; + struct xbee_atcmd *cmd; + + if (res && ressize < sizeof(struct object *)) + return -1; + + /* XXX should be related to an xbee device */ + cmd = xbee_atcmd_lookup_desc(buf); + if (cmd == NULL) + return -1; + + /* command must match flags */ + if ((cmd->flags & tkd->atcmd_mask) != tkd->atcmd_flags) + return -1; + + /* store the address of object in structure */ + if (res) + *(struct xbee_atcmd **)res = cmd; + + return 0; +} + +static int +complete_atcmd_start(cmdline_parse_token_hdr_t *tk, + __attribute__((unused)) const char *tokstr, + void **opaque) +{ + struct xbee_atcmd *cmd; + + cmd = &xbee_atcmd_list[0]; + *opaque = (void *)cmd; + return 0; +} + +static int +complete_atcmd_iterate(cmdline_parse_token_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) +{ + struct token_atcmd *tk2 = (struct token_atcmd *)tk; + struct token_atcmd_data *tkd = &tk2->atcmd_data; + struct xbee_atcmd *cmd; + int len; + + cmd = *opaque; + + while (1) { + if (cmd->name == NULL) + return -1; + + /* skip commands that don't match flags */ + if ((cmd->flags & tkd->atcmd_mask) != tkd->atcmd_flags) { + cmd++; + continue; + } + + len = snprintf(dstbuf, size, "%s", cmd->desc); + if (len < 0 || len >= size) + return -1; + + strcpy(dstbuf, cmd->desc); + break; + } + cmd++; + *opaque = cmd; + return 0; +} + +static int +cmdline_help_atcmd(cmdline_parse_token_hdr_t *tk, char *dstbuf, + unsigned int size) +{ + snprintf(dstbuf, size, "ATCMD"); + return 0; +} + +struct cmdline_token_ops token_atcmd_ops = { + .parse = parse_atcmd, + .complete_start = complete_atcmd_start, + .complete_iterate = complete_atcmd_iterate, + .complete_end = NULL, + .help = cmdline_help_atcmd, +}; diff --git a/parse_atcmd.h b/parse_atcmd.h new file mode 100644 index 0000000..657e7ec --- /dev/null +++ b/parse_atcmd.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011, 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 _PARSE_ATCMD_H_ +#define _PARSE_ATCMD_H_ + +#include + +struct token_atcmd_data { + struct xbee_dev **xbee_dev; + unsigned atcmd_flags; + unsigned atcmd_mask; +}; + +struct token_atcmd { + struct cmdline_token_hdr hdr; + struct token_atcmd_data atcmd_data; +}; +typedef struct token_atcmd parse_token_atcmd_t; + +extern struct cmdline_token_ops token_atcmd_ops; + +#define TOKEN_ATCMD_INITIALIZER(structure, field, dev, flags, mask) \ +{ \ + .hdr = { \ + .ops = &token_atcmd_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .atcmd_data = { \ + .xbee_dev = dev, \ + .atcmd_flags = flags, \ + .atcmd_mask = mask, \ + }, \ +} + +#endif /* _PARSE_ATCMD_H_ */ diff --git a/parse_monitor.c b/parse_monitor.c new file mode 100644 index 0000000..19ecd0b --- /dev/null +++ b/parse_monitor.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2009, 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 "parse_monitor.h" + +static int +parse_monitor(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, + unsigned ressize) +{ + struct token_monitor *tk2 = (struct token_monitor *)tk; + struct token_monitor_data *tkd = &tk2->monitor_data; + struct monitor_reg *m; + + if (res && ressize < sizeof(struct monitor_reg *)) + return -1; + + LIST_FOREACH(m, tkd->list, next) { + if (strcmp(buf, m->desc)) + continue; + break; + } + if (m == NULL) /* not found */ + return -1; + + /* store the address of object in structure */ + if (res) + *(struct monitor_reg **)res = m; + + return 0; +} + +static int +complete_monitor_start(cmdline_parse_token_hdr_t *tk, + __attribute__((unused)) const char *tokstr, + void **opaque) +{ + struct token_monitor *tk2 = (struct token_monitor *)tk; + struct token_monitor_data *tkd = &tk2->monitor_data; + struct monitor_reg *m; + + m = LIST_FIRST(tkd->list); + *opaque = (void *)m; + if (m == NULL) + return -1; /* no completion */ + return 0; +} + +static int +complete_monitor_iterate(cmdline_parse_token_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) +{ + struct monitor_reg *m; + int len; + + m = *opaque; + if (m == NULL) + return -1; + + len = snprintf(dstbuf, size, "%s", m->desc); + if (len < 0 || len >= size) + return -1; + + strcpy(dstbuf, m->desc); + m = LIST_NEXT(m, next); + *opaque = m; + return 0; +} + + +static int +cmdline_help_monitor(cmdline_parse_token_hdr_t *tk, char *dstbuf, + unsigned int size) +{ + snprintf(dstbuf, size, "Monitor-register"); + return 0; +} + +struct cmdline_token_ops token_monitor_ops = { + .parse = parse_monitor, + .complete_start = complete_monitor_start, + .complete_iterate = complete_monitor_iterate, + .complete_end = NULL, + .help = cmdline_help_monitor, +}; diff --git a/parse_monitor.h b/parse_monitor.h new file mode 100644 index 0000000..2f6ab62 --- /dev/null +++ b/parse_monitor.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2009, 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 _PARSE_MONITOR_H_ +#define _PARSE_MONITOR_H_ + +#include +#include + +struct monitor_reg { + LIST_ENTRY(monitor_reg) next; + const char *desc; + const char *atcmd; +}; + +LIST_HEAD(monitor_reg_list, monitor_reg); + +/* data is a pointer to a list */ +struct token_monitor_data { + struct monitor_reg_list *list; +}; + +struct token_monitor { + struct cmdline_token_hdr hdr; + struct token_monitor_data monitor_data; +}; +typedef struct token_monitor parse_token_monitor_t; + +extern struct cmdline_token_ops token_monitor_ops; + +#define TOKEN_MONITOR_INITIALIZER(structure, field, monitor_ptr) \ +{ \ + .hdr = { \ + .ops = &token_monitor_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .monitor_data = { \ + .list = monitor_ptr, \ + }, \ +} + +#endif /* _PARSE_MONITOR_H_ */ diff --git a/parse_neighbor.c b/parse_neighbor.c new file mode 100644 index 0000000..0cba0ea --- /dev/null +++ b/parse_neighbor.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2011, 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 "xbee_neighbor.h" +#include "xbee_atcmd.h" +#include "xbee_stats.h" +#include "xbee_buf.h" +#include "xbee_proto.h" +#include "xbee.h" + +#include "parse_neighbor.h" + +static int +parse_neighbor(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, + unsigned ressize) +{ + struct token_neighbor *tk2 = (struct token_neighbor *)tk; + struct token_neighbor_data *tkd = &tk2->neighbor_data; + struct xbee_dev *dev = *tkd->xbee_dev; + struct xbee_neigh *neigh; + + if (res && ressize < sizeof(struct xbee_neigh *)) + return -1; + + neigh = xbee_neigh_lookup(dev, buf); + if (neigh == NULL) /* not found */ + return -1; + + /* store the address of xbee_neigh in structure */ + if (res) + *(struct xbee_neigh **)res = neigh; + + return 0; +} + +static int +complete_neighbor_start(cmdline_parse_token_hdr_t *tk, + __attribute__((unused)) const char *tokstr, + void **opaque) +{ + struct token_neighbor *tk2 = (struct token_neighbor *)tk; + struct token_neighbor_data *tkd = &tk2->neighbor_data; + struct xbee_dev *dev = *tkd->xbee_dev; + struct xbee_neigh *neigh; + + neigh = LIST_FIRST(&dev->neigh_list); + *opaque = (void *)neigh; + if (neigh == NULL) + return -1; /* no completion */ + return 0; +} + +static int +complete_neighbor_iterate(cmdline_parse_token_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) +{ + struct xbee_neigh *neigh; + int len; + + neigh = *opaque; + if (neigh == NULL) + return -1; + + len = snprintf(dstbuf, size, "%s", neigh->name); + if (len < 0 || len >= size) + return -1; + + strcpy(dstbuf, neigh->name); + neigh = LIST_NEXT(neigh, next); + *opaque = neigh; + return 0; +} + + +static int +cmdline_help_neighbor(cmdline_parse_token_hdr_t *tk, char *dstbuf, + unsigned int size) +{ + snprintf(dstbuf, size, "Neighbor"); + return 0; +} + +struct cmdline_token_ops token_neighbor_ops = { + .parse = parse_neighbor, + .complete_start = complete_neighbor_start, + .complete_iterate = complete_neighbor_iterate, + .complete_end = NULL, + .help = cmdline_help_neighbor, +}; diff --git a/parse_neighbor.h b/parse_neighbor.h new file mode 100644 index 0000000..bdf84d9 --- /dev/null +++ b/parse_neighbor.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011, 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 _PARSE_NEIGHBOR_H_ +#define _PARSE_NEIGHBOR_H_ + +struct token_neighbor_data { + struct xbee_dev **xbee_dev; +}; + +struct token_neighbor { + struct cmdline_token_hdr hdr; + struct token_neighbor_data neighbor_data; +}; +typedef struct token_neighbor parse_token_neighbor_t; + +extern struct cmdline_token_ops token_neighbor_ops; + +#define TOKEN_NEIGHBOR_INITIALIZER(structure, field, dev) \ + { \ + .hdr = { \ + .ops = &token_neighbor_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .neighbor_data = { \ + .xbee_dev = dev, \ + }, \ +} + +#endif /* _PARSE_NEIGHBOR_H_ */ diff --git a/xbee.c b/xbee.c new file mode 100644 index 0000000..c4e8062 --- /dev/null +++ b/xbee.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2011, 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 "xbee_neighbor.h" +#include "xbee_stats.h" +#include "xbee_buf.h" +#include "xbee_proto.h" +#include "xbee.h" + +int xbee_init(void) +{ + return 0; +} + +static int xbee_settermios(struct xbee_dev *dev) +{ + struct termios term; + + memset(&term, 0, sizeof(term)); + term.c_cflag = CREAD | HUPCL; + + switch (dev->baud) { + case 50: term.c_cflag |= B50; break; + case 75: term.c_cflag |= B75; break; + case 110: term.c_cflag |= B110; break; + case 134: term.c_cflag |= B134; break; + case 150: term.c_cflag |= B150; break; + case 200: term.c_cflag |= B200; break; + case 300: term.c_cflag |= B300; break; + case 600: term.c_cflag |= B600; break; + case 1200: term.c_cflag |= B1200; break; + case 1800: term.c_cflag |= B1800; break; + case 2400: term.c_cflag |= B2400; break; + case 4800: term.c_cflag |= B4800; break; + case 9600: term.c_cflag |= B9600; break; + case 19200: term.c_cflag |= B19200; break; + case 38400: term.c_cflag |= B38400; break; + case 57600: term.c_cflag |= B57600; break; + case 115200: term.c_cflag |= B115200; break; + case 230400: term.c_cflag |= B230400; break; + default: + fprintf(stderr, "invalid baudrate\n"); + return -1; + } + + /* ignore modem control lines */ + term.c_cflag |= CLOCAL; + + /* non-canonical mode */ + term.c_lflag &= ~ICANON; + + /* minimum number of chars and minimum timeout for non-canonical read */ + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; + +#if 0 + /* XXX later */ + switch (dev->parity) { + case PARITY_ODD: term.c_cflag |= PARENB | PARODD; break; + case PARITY_EVEN: term.c_cflag |= PARENB; break; + default: break; + } + + if (twostopb) + term.c_cflag |= CSTOPB; + + if (software) + term.c_iflag |= IXON | IXOFF; + if (hardware) + term.c_cflag |= CRTSCTS; + +#endif + + if (tcsetattr(dev->fd, TCSAFLUSH, &term) < 0) { + fprintf(stderr, "ERROR: tcsetattr(TCSAFLUSH) failed: %s\n", + strerror(errno)); + return -1; + } + return(0); +} + +int xbee_register_channel(struct xbee_dev *dev, int channel, + xbee_rx_cb_t *rx_cb, void *opaque) +{ + /* user asked for any channel */ + if (channel == XBEE_CHANNEL_ANY) { + int ch; + + /* skip XBEE_DEFAULT_CHANNEL == 0 */ + for (ch = 1; ch < XBEE_MAX_CHANNEL; ch++) { + if (dev->channel[ch].registered == 0) { + channel = ch; + break; + } + } + /* no available channels */ + if (channel == XBEE_CHANNEL_ANY) + return -1; + } + /* user requested a specific channel */ + else if (channel < 0 || channel >= XBEE_MAX_CHANNEL || + dev->channel[channel].registered == 1) + return -1; /* not available */ + + dev->channel[channel].registered = 1; + dev->channel[channel].rx_cb = rx_cb; + dev->channel[channel].arg = opaque; + return channel; +} + +int xbee_unregister_channel(struct xbee_dev *dev, int channel) +{ + if (channel < 0 || channel >= XBEE_MAX_CHANNEL || + dev->channel[channel].registered == 0) + return -1; + dev->channel[channel].registered = 0; + dev->channel[channel].rx_cb = NULL; + dev->channel[channel].arg = NULL; + return 0; +} + +struct xbee_dev *xbee_open(const char *devname, unsigned baudrate) +{ + struct xbee_dev *dev; + int fd; + + /* allocate structure for xbee device */ + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + fprintf(stderr, "not enough memory\n"); + return NULL; + } + memset(dev, 0, sizeof(*dev)); + + /* open the serial port */ + fd = open(devname, O_NONBLOCK|O_RDWR); + if (fd < 0) { + fprintf(stderr, "cannot open %s: %s\n", devname, + strerror(errno)); + free(dev); + return NULL; + } + + /* set termios */ + dev->baud = baudrate; + dev->fd = fd; + if (xbee_settermios(dev) < 0) { + close(fd); + free(dev); + return NULL; + } + + dev->name = strdup(devname); + xbee_bufq_init(&dev->queue); + xbee_neigh_init(dev); + + return dev; +} + +/* read data from device fd and put it in queue */ +int xbee_read(struct xbee_dev *dev) +{ + struct xbee_buf *xbuf; + int n, tailroom, total = 0; + char *data; + + do { + /* get an xbuf to store data */ + xbuf = xbee_bufq_last(&dev->queue); + if (xbuf == NULL || xbee_buf_tailroom(xbuf) == 0) { + xbuf = xbee_buf_alloc(); + + if (xbuf == NULL) { + fprintf(stderr, "FATAL: cannot allocate buffer\n"); + return -1; + } + + /* put the xbuf in queue */ + xbee_buf_enqueue(&dev->queue, xbuf); + } + + /* read data from char device */ + tailroom = xbee_buf_tailroom(xbuf); + data = xbee_buf_tail(xbuf); + n = read(dev->fd, data, tailroom); + + /* error in read */ + if (n < 0) { + fprintf(stderr, "read() failed: %s\n", strerror(errno)); + return -1; + } + + /* update queue len and xbuf len */ + xbee_bufq_append(&dev->queue, n); + + total += n; + + } while (n == tailroom); + + return total; +} + +/* process all data in queue */ +int xbee_process_queue(struct xbee_dev *dev) +{ + char buf[XBEE_MAX_FRAME_LEN]; + int len, ret; + + while (1) { + len = xbee_proto_get_frame(dev, buf, sizeof(buf)); + if (len == 0) /* no more frame */ + break; + + if (len < 0) { + /* a frame was dropped... */ + fprintf(stderr, "xbee_proto_get_frame() failed\n"); + continue; + } + + ret = xbee_proto_parse_frame(dev, buf, len); + if (ret < 0) { + fprintf(stderr, "xbee_proto_parse_frame() failed\n"); + continue; + } + } + + return 0; +} diff --git a/xbee.h b/xbee.h new file mode 100644 index 0000000..e1cfbbd --- /dev/null +++ b/xbee.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* Callback when receiving data on a specific channel. The arguments + * of the function are the xbee device, the channel ID, the type of + * the frame (example: XBEE_TYPE_ATRESP), the pointer to the frame, + * the length of the frame, and an opaque pointer (reserved for user) */ +typedef void (xbee_rx_cb_t)(struct xbee_dev *, int, int, void *, + unsigned, void *); + +/* an xbee queue */ +struct xbee_channel { + int registered; + xbee_rx_cb_t *rx_cb; + void *arg; +}; + +#define XBEE_DEFAULT_CHANNEL 0 +#define XBEE_MAX_CHANNEL 16 +#define XBEE_CHANNEL_ANY XBEE_MAX_CHANNEL + +/* structure identifying a xbee device */ +struct xbee_dev { + const char *name; + int fd; + unsigned baud; + struct xbee_bufq queue; + struct xbee_channel channel[XBEE_MAX_CHANNEL]; + struct xbee_stats stats; + struct xbee_neigh_list neigh_list; +}; + +/* initialize xbee library */ +int xbee_init(void); + +/* open an xbee device */ +struct xbee_dev *xbee_open(const char *devname, unsigned baudrate); + +/* closes an xbee device */ +int xbee_close(struct xbee_dev *dev); + +/* Register a channel, return the ID of the channel or a negative + * value on error. The rx_cb is a pointer to a function that will be + * called by xbee_read() when a frame is received for this channel. If + * rx_cb is NULL, no callback will occur. The "channel" argument can + * be XBEE_CHANNEL_ANY to let the library choose the channel, or a + * channel number to request a specific one. */ +int xbee_register_channel(struct xbee_dev *dev, int channel, + xbee_rx_cb_t *rx_cb, void *opaque); + +/* Unregister a channel, return 0 on success */ +int xbee_unregister_channel(struct xbee_dev *dev, int channel_id); + +/* read data from device fd and put it in queue */ +int xbee_read(struct xbee_dev *dev); + +/* process all data in queue */ +int xbee_process_queue(struct xbee_dev *dev); diff --git a/xbee_atcmd.c b/xbee_atcmd.c new file mode 100644 index 0000000..91238f7 --- /dev/null +++ b/xbee_atcmd.c @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2011, 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 "xbee_atcmd.h" + +struct xbee_atcmd xbee_atcmd_list[] = { + { + "WR", + "write-param", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Write parameter values to non-volatile memory.", + }, + { + "RE", + "restore-defaults", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Restore module parameters to factory defaults.", + }, + { + "FR", + "soft-reset", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Software Reset. Responds with 'OK' then performs a " + "reset 100ms later.", + }, + { + "AC", + "apply-changes", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Apply Changes without exiting command mode.", + }, + { + "R1", + "restore-compiled", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Restore module parameters to compiled defaults.", + }, + { + "VL", + "version-long", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Shows detailed version information including" + "application build date and time.", + }, + { + "DH", + "dst-addr-high", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Upper 32 bits of the 64-bit destination address (0 " + "to 0xFFFFFFFF, default is 0x0000FFFF).", + }, + { + "DL", + "dst-addr-low", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Lower 32 bits of the 64-bit destination address (0 " + "to 0xFFFFFFFF, default is 0x0000FFFF).", + }, + { + "DD", + "device-type-id", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ, + "Device Type Identifier, it can be used to differentiate " + "multiple XBee-based products (0 to 0xFFFFFFFF, read-only, " + "default is 0x40000).", + }, + { + "SH", + "src-addr-high", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ, + "Upper 32 bits of the 64-bit source address (read-only).", + }, + { + "SL", + "src-addr-low", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ, + "Lower 32 bits of the 64-bit source address (read-only).", + }, + { + "SE", + "src-endpoint", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "The application source endpoint for all data transmissions " + "(0 to 0xFF, default is 0xE8).", + }, + { + "DE", + "dst-endpoint", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "The application destination endpoint for all data " + "transmissions (0 to 0xFF, default is 0xE8).", + }, + { + "CI", + "cluster-id", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Cluster Identifier for all data transmissions (0 to 0xFFFF, " + "default is 0x11).", + }, + { + "NP", + "max-rf-payload", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ, + "Maximum RF Payload Bytes that can be sent in a unicast " + "transmission based on the current configuration (0 to " + "0xFFFF).", + }, + { + "CE", + "coord-end-device", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Coordinator/End Device, messaging mode of the module " + "(0 - Normal, 1 - Indirect coordinator, 2 - Polling, default " + "is 0).", + }, + { + "AP", + "api-mode", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "API mode (0 - off, 1 - on, 2 - on with escape sequences).", + }, + { + "AO", + "api-output-format", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "API Output Format (0 - standard [0x90 for RX], 1 - explicit " + "addressing [0x91 for RX]).", + }, + { + "BD", + "baud-rate", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Baud rate of serial interface (0-8 select preset standard " + "rates, and 0x39 to 0x1c9c38 select baud rate).", + }, + { + "RO", + "packetization-timeout", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Packetization Timeout: the inter-character silence required " + "before packetization specified in character times (0 to 0xFF, " + "default is 3).", + }, + { + "FT", + "flow-control-thres", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Flow Control Threshhold. De-assert CTS and/or send XOFF when " + "FT bytes are in the UART receive buffer. Re-assert CTS when " + "less than FT - 16 bytes are in the UART receive buffer (0x11 " + "to 0xEE, default is 0xBE).", + }, + { + "NB", + "parity", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Parity (0 - no parity, 1 - even parity, 2 - odd parity, 3 - " + "forced high parity, 4 - forced low parity). Default is 0.", + }, + { + "D7", + "dio7", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "DIO7 Configuration (0 - unmonitored input, 1 - CTS, 3 - " + "digital input, 4 - digital output low, 5 - digital output " + "high, 6 - RS-485 low Tx, 7 - RS-485 high Tx). Default is " + "0.", + }, + { + "D6", + "dio6", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "DIO6 Configuration (0 - unmonitored input, 1 - RTS, 3 - " + "digital input, 4 - digital output low, 5 - digital output " + "high). Default is 0.", + }, + { + "P0", + "dio10-pwm0", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "DIO10/PWM0 Configuration. (0 - unmonitored input, 1 - RSSI, 2 " + "- PWM0, 3 - digital input, 4 - digital output low, 5 - " + "digital output high). Default is 1.", + }, + { + "P1", + "dio11-pwm1", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "DIO11/PWM1 Configuration. (0 - unmonitored input, 2 " + "- PWM1, 3 - digital input, 4 - digital output low, 5 - " + "digital output high). Default is 0.", + }, + { + "P2", + "dio12", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "DIO12 Configuration. (0 - unmonitored input, " + "3 - digital input, 4 - digital output low, 5 - " + "digital output high). Default is 0.", + }, + { + "RP", + "rssi-pwm", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Time RSSI signal will be output after last transmission. " + "When RP = 0xFF, output will always be on (0 - 0xFF, default " + "is 0x28 = 4 seconds).", + }, + { + "1S", + "sensor-sample", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Forces a sample to be taken on an XBee Sensor device.", + }, + { + "D0", + "dio0-ad0", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "AD0/DIO0 Configuration. (0 - unmonitored input, 1 - " + "commission button enabled, 2 - analog input, 3 - digital " + "input, 4 - digital output low, 5 - digital output high). " + "Default is 1.", + }, + { + "D1", + "dio1-ad1", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "AD1/DIO1 Configuration. (0 - unmonitored input, " + "2 - analog input, 3 - digital input, 4 - digital output " + "low, 5 - digital output high). Default is 0.", + }, + { + "D2", + "dio2-ad2", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "AD2/DIO2 Configuration. (0 - unmonitored input, " + "2 - analog input, 3 - digital input, 4 - digital output " + "low, 5 - digital output high). Default is 0.", + }, + { + "D3", + "dio3-ad3", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "AD3/DIO3 Configuration. (0 - unmonitored input, " + "2 - analog input, 3 - digital input, 4 - digital output " + "low, 5 - digital output high). Default is 0.", + }, + { + "D4", + "dio4-ad4", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "AD4/DIO4 Configuration. (0 - unmonitored input, " + "2 - analog input, 3 - digital input, 4 - digital output " + "low, 5 - digital output high). Default is 0.", + }, + { + "D5", + "dio5-ad5", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "AD4/DIO4 Configuration. (0 - unmonitored input, 1 - LED, " + "2 - analog input, 3 - digital input, 4 - digital output " + "low, 5 - digital output high). Default is 1.", + }, + { + "D8", + "dio8-sleep-rq", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "DIO8/SLEEP_RQ Configuration. (0 - unmonitored input, 1 - LED, " + "2 - analog input, 3 - digital input, 4 - digital output " + "low, 5 - digital output high). Default is 0. When used as " + "SLEEP_RQ, the D8 parameter should be configured in mode 0 " + "or 3.", + }, + { + "D9", + "dio9-on-sleep", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "DIO9/ON_SLEEP Configuration. (0 - unmonitored input, 1 - " + "ON/SLEEP, 2 - analog input, 3 - digital input, 4 - digital " + "output low, 5 - digital output high). Default is ?.", + }, + { + "PR", + "pull-up-resistor", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Pull-up Resistor. Bit field that configures the internal " + "pull-up resistors for the I/O lines (bit set = pull-up " + "enabled). Range is from 0 to 0x1FFF, default is 0x1FFF.", + }, + { + "M0", + "pwm0-out-level", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "PWM0 Output Level. The line should be configured as a PWM " + "output using the P0 command (0 to 0x3FF, default is 0).", + }, + { + "M1", + "pwm1-out-level", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "PWM1 Output Level. The line should be configured as a PWM " + "output using the P1 command (0 to 0x3FF, default is 0).", + }, + { + "LT", + "led-blink-time", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Associate LED Blink Time (should be enabled through D5 " + "command). Range is 0x14-0xFF (x10ms), default is 0.", }, + { + "IS", + "force-sample", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Forces a read of all enabled digital and " + "analog input lines.", + }, + { + "IC", + "digital-change-detect", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "I/O Digital Change Detection. If a pin is enabled as a " + "digital input/output, the IC command can be used to " + "force an immediate I/O sample transmission when the DIO " + "state changes. IC is a bitmask, range is 0 to 0xFFFF, " + "default is 0", + }, + { + "IR", + "io-sample-rate", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "IO Sample Rate for periodic sampling. If zero, periodic " + "sampling is disabled. Else the value is in milliseconds " + "(range 0 to 0xFFFF, default is 0).", + }, + { + "CB", + "comissioning-button", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Commissioning Pushbutton, simulate commissioning button " + "in software. The parameter value should be set to the number " + "of button presses to be simulated (range is 0 to 4).", + }, + { + "VR", + "firmware-version", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ, + "Firmware version of the module (read only).", + }, + { + "HV", + "hardware-version", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ, + "Hardware version of the module (read only)." + }, + { + "CK", + "config-code", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ, + "Configuration Code, that can be used as a quick " + "check to determine if a node has been configured as " + "desired (read-only, 0-0xFFFFFFFF)." + }, + { + "ER", + "rf-errors", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ, + "Number of times a packet was received which contained errors " + "of some sort. Read-only, saturate at 0xFFFF.", + }, + { + "GD", + "good-packets", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ, + "Number of good received frames. Read-only, saturate at " + "0xFFFF.", + }, + { + "RP", + "rssi-pwm-timer", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "RSSI PWM timer, the time in tenth of seconds that the RSSI " + "output indicating signal strength will remain active after " + "the last reception (1 to 0xff, default is 0x20 = 3.2 secs).", + }, + { + "TR", + "tx-errors", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ, + "Transmission Errors, the number of MAC frames that " + "exhaust MAC retries without ever receiving a MAC " + "acknowledgement message. Read-only, saturate at 0xFFFF.", + }, + { + "TP", + "temperature", + XBEE_ATCMD_F_PARAM_S16 | XBEE_ATCMD_F_READ, + "Temperature. Read module temperature in (tenths of ?) " + "Celsius. Negatives temperatures can be returned (read-only, " + "from 0xff74 [-140] to 0x0258 [600]).", + }, + { + "DB", + "rx-signal-strength", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ, + "Received Signal Strength of the last received RF data " + "packet measured in -dBm. For example if DB returns 0x60, " + "then the RSSI of the last packet received was -96dBm " + "(read-only)." + }, + { + "DC", + "duty-cycle", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ, + "Duty Cycle. Returns a current usage percentage of the " + "10% duty cycle measured over the period of 1 hour " + "(read-only, from 0 to 0x64).", + }, + { + "RC", + "rssi-for-channel", //XXX in fact it is a read with a param + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_WRITE, + "Reads the dBm level (RSSI) of the designated " + "channel.", + }, + { + "R#", + "reset-number", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ, + "Tells the reason for the last module reset (0 - Power up " + "reset, 2 - Watchdog reset, 3 - Software reset, 4 - Reset " + "line reset, 5 - Brownout reset). Read-only.", + }, + { + "TA", + "tx-ack-errors", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ, + "Transmit Acknowlegement Errors. Incremented once for " + "each failed ack retry (read-only, from 0 to 0xFFFF).", + }, + { + "%V", + "supply-voltage", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ, + "Voltage on the Vcc pin in mV (read-only, from 0 to 0xF00).", + }, + { + "CT", + "cmd-mode-timeout", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Command Mode Timeout: the period of inactivity (no valid " + "commands received) after which the RF module automatically " + "exits AT Command Mode and returns to Idle Mode (2 to 0x1770, " + "default is 0x64).", + }, + { + "CN", + "exit-cmd-mode", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Exit Command Mode.", + }, + { + "GT", + "guard-times", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Guard Times: period of silence in ms before and after the " + "Command Sequence Characters of the AT Command Mode Sequence, " + "used to prevent inadvertent entrance into AT Command Mode " + "(0 to 0xFFFF, default is 0x3E8).", + }, + { + "CC", + "command-chars", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Command Character used between guard times of the AT Command " + "Mode Sequence (0 to 0xFF, default is 0x2B).", + }, + { + "ID", + "network-id", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Network ID. Nodes must have the same network identifier " + "to communicate (0 to 0x7FFF, default is 0x7FFF).", + }, + { + "NT", + "ndisc-timeout", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Node Discover Timeout, time in tenth of secs a node will " + "spend discovering other nodes when ND or DN is issued (0 " + "to 0xFC, default is 0x82).", + }, + { + "NI", + "node-id", + XBEE_ATCMD_F_PARAM_STRING_20B | XBEE_ATCMD_F_READ | + XBEE_ATCMD_F_WRITE, + "Node Identifier in printable ASCII characters. This string is " + "returned as part of the ATND (Network Discover) command. This " + "identifier is also used with the ATDN (Destination Node) " + "command. The string contains up to 20 byte ASCII string, " + "default is a space character.", + }, + { + "DN", + "disc-node", + XBEE_ATCMD_F_PARAM_STRING_20B | XBEE_ATCMD_F_READ | + XBEE_ATCMD_F_WRITE, + /* XXX */ + "Resolves a Node Identifier string to a physical address " + "(case sensitive). 0xFFFE and the 64bits extended address are " + "returned." + }, + { + "ND", + "network-discover", + XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE, + "Network Discovery, see doc", /* XXX */ + }, + { + "NO", + "ndisc-options", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Network Discovery Options, a bitfield value that changes the " + "behavior of the ND command (bit0 - Append DD value, bit1 - " + "Local device sends ND response frame when ND is issued). " + "Default is 0." + }, + { + "EE", + "security-enable", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Enable or disable 128-bit AES encryption (0 or 1, 0 is the " + "default).", + }, + { + "KY", /* XXX */ + "security-key", + XBEE_ATCMD_F_PARAM_HEXBUF_16B | XBEE_ATCMD_F_WRITE, + "The 128bits security key (the command is write-only).", + }, + { + "MT", + "bcast-multi-xmit", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Number of additional MAC-level broadcast transmissions. All " + "broadcast packets are transmitted MT+1 times to ensure " + "it is received (0 to 0xF, default is 3).", + }, + { + "RR", + "unicast-retries", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Number of additional MAC-level packet delivery attempts for " + "unicast transactions. If RR is non-zero, packets sent from " + "the radio will request an acknowledgement, and can be resent " + "up to RR times if no acknowledgement is received. (0 to 0xF, " + "default is 10).", + }, + { + "PL", + "power-level", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Power Level of RF transmitter (0 - 1mW, 1 - 23mW, 2 - 100mW, " + "3 - 158 mW, 4 - 316 mW). Default is 4.", + }, + { + "SM", + "sleep-mode", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Sleep Mode (0 - disabled, 1 - pin sleep, 4 - async cyclic " + "sleep, 5 - async cyclic sleep with pin wakeup). Default " + "is 0.", + }, + { + "SO", + "sleep-options", + XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Sleep Options bitmask (bit8 - always wake for ST time). " + "Default is 0.", + }, + { + "ST", + "wake-time", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Wake Time: the amount of time in ms that the module will stay " + "awake after receiving RF or serial data (from 0x45 to " + "0x36EE80, default is 0x7D0 = 2 secs).", + }, + { + "SP", + "sleep-period", + XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Sleep Period: the amount of time in 10ms unit the module will " + "sleep per cycle. For a node operating as an Indirect " + "Messaging Coordinator, this command defines the amount of " + "time that it will hold an indirect message for an end device. " + "The coordinator will hold the message for (2.5 * SP). Range " + "is from 1 to 1440000, default is 200 (2 secs).", + }, + { + "SN", + "num-sleep-periods", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Number of Sleep Periods that must elapse between assertions " + "of the ON_SLEEP line during the wake time of asynchronous " + "cyclic sleep (1 to 0xFFFF, default is 1).", + }, + { + "WH", + "wake-host", + XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE, + "Wake Host time. If it is set to a non-zero value, it " + "specifies the time in ms that the device should allow after " + "waking from sleep before sending data out the UART or " + "transmitting an I/O sample. If serial characters are " + "received, the WH timer is stopped immediately. Range is " + "from 0 to 0xFFFF, default is 0.", + }, + { + NULL, + NULL, + 0, + NULL, + }, +}; + +struct xbee_atcmd *xbee_atcmd_lookup_name(const char *atcmd_str) +{ + struct xbee_atcmd *cmd; + + for (cmd = &xbee_atcmd_list[0]; cmd->name != NULL; cmd++) { + if (strcmp(atcmd_str, cmd->name)) + continue; + break; + } + + if (cmd->name == NULL) /* not found */ + return NULL; + + return cmd; +} + +struct xbee_atcmd *xbee_atcmd_lookup_desc(const char *desc) +{ + struct xbee_atcmd *cmd; + + for (cmd = &xbee_atcmd_list[0]; cmd->desc != NULL; cmd++) { + if (strcmp(desc, cmd->desc)) + continue; + break; + } + + if (cmd->name == NULL) /* not found */ + return NULL; + + return cmd; +} diff --git a/xbee_atcmd.h b/xbee_atcmd.h new file mode 100644 index 0000000..91a7e3d --- /dev/null +++ b/xbee_atcmd.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011, 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_ATCMD_H_ +#define _XBEE_ATCMD_H_ + +#define XBEE_ATCMD_F_READ 0x001 +#define XBEE_ATCMD_F_WRITE 0x002 +#define XBEE_ATCMD_F_PARAM_NONE 0x004 +#define XBEE_ATCMD_F_PARAM_U8 0x008 +#define XBEE_ATCMD_F_PARAM_U16 0x010 +#define XBEE_ATCMD_F_PARAM_S16 0x020 +#define XBEE_ATCMD_F_PARAM_U32 0x040 +#define XBEE_ATCMD_F_PARAM_STRING_20B 0x080 +#define XBEE_ATCMD_F_PARAM_HEXBUF_16B 0x100 + +/* list of xbee at commands */ +struct xbee_atcmd { + const char *name; + const char *desc; + unsigned int flags; + const char *help; +}; + +extern struct xbee_atcmd xbee_atcmd_list[]; + +struct xbee_atcmd *xbee_atcmd_lookup_name(const char *atcmd_str); +struct xbee_atcmd *xbee_atcmd_lookup_desc(const char *desc); + +#endif /* _xBEE_ATCMD_H_ */ diff --git a/xbee_buf.c b/xbee_buf.c new file mode 100644 index 0000000..b33ed0b --- /dev/null +++ b/xbee_buf.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2011, 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 "xbee_neighbor.h" +#include "xbee_stats.h" +#include "xbee_buf.h" +#include "xbee.h" + +struct xbee_buf *xbee_buf_alloc(void) +{ + struct xbee_buf *xbuf; + + xbuf = malloc(sizeof(*xbuf)); + if (xbuf == NULL) + return NULL; + memset(xbuf, 0, sizeof(*xbuf)); + xbuf->offset = 0; + xbuf->len = 0; + return xbuf; +} + +int xbee_buf_tailroom(struct xbee_buf *xbuf) +{ + return XBEE_BUF_SIZE - xbuf->len - xbuf->offset; +} + +char *xbee_buf_data(struct xbee_buf *xbuf, unsigned off) +{ + if (off >= xbuf->len) + return NULL; + return xbuf->buf + xbuf->offset + off; +} + +char *xbee_buf_head(struct xbee_buf *xbuf) +{ + return xbuf->buf + xbuf->offset; +} + +char *xbee_buf_tail(struct xbee_buf *xbuf) +{ + return xbuf->buf + xbuf->offset + xbuf->len; +} + +void xbee_buf_enqueue(struct xbee_bufq *q, struct xbee_buf *xbuf) +{ + CIRCLEQ_INSERT_TAIL(&q->xbq, xbuf, next); + q->len += xbuf->len; + q->nseg++; +} + +struct xbee_buf *xbee_bufq_last(struct xbee_bufq *q) +{ + if (CIRCLEQ_EMPTY(&q->xbq)) + return NULL; + return CIRCLEQ_LAST(&q->xbq); +} + +void xbee_bufq_init(struct xbee_bufq *q) +{ + CIRCLEQ_INIT(&q->xbq); + q->len = 0; + q->nseg = 0; +} + +void xbee_bufq_append(struct xbee_bufq *q, unsigned len) +{ + struct xbee_buf *xbuf; + + q->len += len; + xbuf = CIRCLEQ_LAST(&q->xbq); + xbuf->len += len; +} + +void xbee_bufq_flush(struct xbee_bufq *q) +{ + struct xbee_buf *xbuf; + + while (!CIRCLEQ_EMPTY(&q->xbq)) { + xbuf = CIRCLEQ_FIRST(&q->xbq); + CIRCLEQ_REMOVE(&q->xbq, xbuf, next); + q->nseg --; + q->len -= xbuf->len; + free(xbuf); + } +} + +char *xbee_bufq_data(struct xbee_bufq *q, unsigned off) +{ + struct xbee_buf *xbuf; + char *data = NULL; + + if (off >= q->len) + return NULL; + + CIRCLEQ_FOREACH(xbuf, &q->xbq, next) { + data = xbee_buf_data(xbuf, off); + if (data != NULL) + return data; + off -= xbuf->len; + } + + return data; +} + +/* drop data in front of queue */ +int xbee_bufq_drop(struct xbee_bufq *q, unsigned len) +{ + struct xbee_buf *xbuf; + + if (len > q->len) + return -1; + + while (!CIRCLEQ_EMPTY(&q->xbq)) { + xbuf = CIRCLEQ_FIRST(&q->xbq); + if (xbuf->len > len) + break; + CIRCLEQ_REMOVE(&q->xbq, xbuf, next); + len -= xbuf->len; + q->nseg --; + q->len -= xbuf->len; + free(xbuf); + xbuf = NULL; + } + + if (xbuf != NULL) { + xbuf->len -= len; + xbuf->offset += len; + q->len -= len; + } + + return 0; +} + +int xbee_bufq_copy(struct xbee_bufq *q, void *buf, unsigned len) +{ + struct xbee_buf *xbuf; + unsigned dstoff = 0, copylen; + + if (len > q->len) + return -1; + + CIRCLEQ_FOREACH(xbuf, &q->xbq, next) { + copylen = len; + if (xbuf->len < len) + copylen = xbuf->len; + memcpy(buf + dstoff, xbuf->buf + xbuf->offset, copylen); + len -= copylen; + if (len == 0) + break; + dstoff += copylen; + } + + return 0; +} + diff --git a/xbee_buf.h b/xbee_buf.h new file mode 100644 index 0000000..cecddcf --- /dev/null +++ b/xbee_buf.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011, 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. + */ + +#define XBEE_BUF_SIZE 0x200 + +/* a xbee data buffer */ +struct xbee_buf { + CIRCLEQ_ENTRY(xbee_buf) next; + unsigned offset; + unsigned len; + char buf[XBEE_BUF_SIZE]; +}; + +/* queue of xbee_buf */ +CIRCLEQ_HEAD(xbufq, xbee_buf); + +struct xbee_bufq { + struct xbufq xbq; + unsigned len; + unsigned nseg; +}; + +/* allocate a new xbee_buf */ +struct xbee_buf *xbee_buf_alloc(void); + +/* return the number of remaining bytes in xbee_buf */ +int xbee_buf_tailroom(struct xbee_buf *xbuf); + +/* return the pointer to data at offset 'off', or NULL if off > xbuf->len */ +char *xbee_buf_data(struct xbee_buf *xbuf, unsigned off); + +/* return the first data of a xbuf (also works if len is 0) */ +char *xbee_buf_head(struct xbee_buf *xbuf); + +/* return the pointer just after data of a xbuf (also works if len is 0) */ +char *xbee_buf_tail(struct xbee_buf *xbuf); + +/* enqueue a xbuf in a xbufq */ +void xbee_buf_enqueue(struct xbee_bufq *q, struct xbee_buf *xbuf); + + + +/* return the last xbuf of a queue, or NULL if the queue is empty */ +struct xbee_buf *xbee_bufq_last(struct xbee_bufq *q); + +/* initialize a xbuf queue */ +void xbee_bufq_init(struct xbee_bufq *q); + +/* flush a xbuf queue */ +void xbee_bufq_flush(struct xbee_bufq *q); + +/* append data in queue (just update lens), user should memcpy first */ +void xbee_bufq_append(struct xbee_bufq *q, unsigned len); + +/* return the pointer to data at offset 'off', or NULL if off > q->len */ +char *xbee_bufq_data(struct xbee_bufq *q, unsigned off); + +/* drop data in front of queue */ +int xbee_bufq_drop(struct xbee_bufq *q, unsigned len); + +/* copy data in front of queue in a linear buffer */ +int xbee_bufq_copy(struct xbee_bufq *q, void *buf, unsigned len); diff --git a/xbee_neighbor.c b/xbee_neighbor.c new file mode 100644 index 0000000..3495f8b --- /dev/null +++ b/xbee_neighbor.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011, 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 "xbee_neighbor.h" +#include "xbee_atcmd.h" +#include "xbee_stats.h" +#include "xbee_buf.h" +#include "xbee_proto.h" +#include "xbee.h" + +void xbee_neigh_init(struct xbee_dev *dev) +{ + LIST_INIT(&dev->neigh_list); +} + +struct xbee_neigh *xbee_neigh_lookup(struct xbee_dev *dev, const char *name) +{ + struct xbee_neigh *neigh; + + LIST_FOREACH(neigh, &dev->neigh_list, next) { + if (strcmp(name, neigh->name)) + continue; + break; + } + + return neigh; +} + +struct xbee_neigh *xbee_neigh_rlookup(struct xbee_dev *dev, uint64_t addr) +{ + struct xbee_neigh *neigh; + + LIST_FOREACH(neigh, &dev->neigh_list, next) { + if (addr != neigh->addr) + continue; + break; + } + + return neigh; +} + +struct xbee_neigh *xbee_neigh_add(struct xbee_dev *dev, const char *name, + uint64_t addr) +{ + struct xbee_neigh *neigh; + + if (xbee_neigh_rlookup(dev, addr) != NULL) + return NULL; + + if (xbee_neigh_lookup(dev, name) != NULL) + return NULL; + + neigh = malloc(sizeof(*neigh)); + if (neigh == NULL) + return NULL; + + neigh->addr = addr; + snprintf(neigh->name, sizeof(neigh->name), "%s", name); + LIST_INSERT_HEAD(&dev->neigh_list, neigh, next); + + return neigh; +} + +void xbee_neigh_del(struct xbee_dev *dev, struct xbee_neigh *neigh) +{ + dev = dev; /* silent compiler */ + LIST_REMOVE(neigh, next); + free(neigh); +} diff --git a/xbee_neighbor.h b/xbee_neighbor.h new file mode 100644 index 0000000..0939a24 --- /dev/null +++ b/xbee_neighbor.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011, 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_NEIGHBOR_H_ +#define _XBEE_NEIGHBOR_H_ + +struct xbee_neigh { + LIST_ENTRY(xbee_neigh) next; + char name[21]; + uint64_t addr; +}; + +struct xbee_dev; + +/* define struct xbee_neigh_list */ +LIST_HEAD(xbee_neigh_list, xbee_neigh); + +/* init neighbor list of an xbee device */ +void xbee_neigh_init(struct xbee_dev *dev); + +/* return a neighbor from its name */ +struct xbee_neigh *xbee_neigh_lookup(struct xbee_dev *dev, const char *name); + +/* return a neighbor from its address (in host order) */ +struct xbee_neigh *xbee_neigh_rlookup(struct xbee_dev *dev, uint64_t addr); + +/* add a neighbor */ +struct xbee_neigh *xbee_neigh_add(struct xbee_dev *dev, const char *name, + uint64_t addr); + +/* del a neighbor from list */ +void xbee_neigh_del(struct xbee_dev *dev, struct xbee_neigh *neigh); + +#endif /* _XBEE_NEIGHBOR_H_ */ diff --git a/xbee_proto.c b/xbee_proto.c new file mode 100644 index 0000000..3c694bc --- /dev/null +++ b/xbee_proto.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2011, 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 + +/* remove */ +#include +#include + +#include "xbee_neighbor.h" +#include "xbee_stats.h" +#include "xbee_buf.h" +#include "xbee_proto.h" +#include "xbee.h" + +static void xbee_proto_drop_garbage(struct xbee_dev *dev) +{ + struct xbee_bufq *q = &dev->queue; + char *data; + + /* drop all data != delimiter */ + while ((data = xbee_bufq_data(q, 0))) { + /* no more data */ + if (data == NULL) + break; + + if (*data == XBEE_DELIMITER) + break; + + dev->stats.rx_no_delim++; + xbee_bufq_drop(q, 1); + } +} + +/* return negative on error, 0 if there is not frame, or framelen */ +int xbee_proto_get_frame(struct xbee_dev *dev, void *buf, unsigned len) +{ + uint16_t framelen; + struct xbee_hdr hdr; + struct xbee_bufq *q = &dev->queue; + + xbee_proto_drop_garbage(dev); + + if (xbee_bufq_copy(q, &hdr, sizeof(hdr)) < 0) + return 0; + + framelen = ntohs(hdr.len); + framelen += 4; /* 1 for delimiter, 2 for len, 1 for cksum */ + + /* not enough data to read */ + if (q->len < framelen) + return 0; + + /* arf, provided buffer is to small */ + if (framelen > len) { + fprintf(stderr, "drop packet, buffer too small\n"); + dev->stats.rx_frame_too_large++; + xbee_bufq_drop(q, framelen); + return -1; + } + + xbee_bufq_copy(q, buf, framelen); + xbee_bufq_drop(q, framelen); + return framelen; +} + +/* return -1 if the frame is invalid */ +static int xbee_proto_parse_atresp(struct xbee_dev *dev, void *buf, + unsigned len) +{ + struct xbee_atresp_hdr *atresp_hdr; + + dev->stats.rx_atresp++; + + if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_atresp_hdr)) { + dev->stats.rx_frame_too_small++; + return -1; + } + + atresp_hdr = buf + sizeof(struct xbee_hdr); + + /* bad status, but let the frame continue */ + if (atresp_hdr->status != 0) + dev->stats.rx_atresp_error++; + + return 0; +} + +/* return -1 if the frame is invalid */ +static int xbee_proto_parse_rmt_atresp(struct xbee_dev *dev, void *buf, + unsigned len) +{ + struct xbee_rmt_atresp_hdr *rmt_atresp_hdr; + + dev->stats.rx_rmt_atresp++; + + if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_rmt_atresp_hdr)) { + dev->stats.rx_frame_too_small++; + return -1; + } + + rmt_atresp_hdr = buf + sizeof(struct xbee_hdr); + + /* bad status, but let the frame continue */ + if (rmt_atresp_hdr->status != 0) + dev->stats.rx_rmt_atresp_error++; + + return 0; +} + +/* return -1 if the frame is invalid */ +static int xbee_proto_parse_xmit_status(struct xbee_dev *dev, void *buf, + unsigned len) +{ + struct xbee_xmit_status_hdr *xmit_status_hdr; + + dev->stats.rx_xmit_status++; + + if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_xmit_status_hdr)) { + dev->stats.rx_frame_too_small++; + return -1; + } + + xmit_status_hdr = buf + sizeof(struct xbee_hdr); + dev->stats.tx_xmit_retries += xmit_status_hdr->xmit_retry_cnt; + + /* bad status, but let the frame continue */ + if (xmit_status_hdr->delivery_status != 0) + dev->stats.rx_xmit_status_error++; + + return 0; +} + +/* parse a frame: return 0 if the frame is valid, else a negative value */ +// XXX rename +int xbee_proto_parse_frame(struct xbee_dev *dev, void *buf, unsigned len) +{ + struct xbee_hdr *hdr = buf; + int i; + uint8_t cksum = 0; + int channel; + unsigned hdrlen; + + dev->stats.rx_frame++; + + /* check frame len: we must be able to read frame type */ + if (len < (offsetof(struct xbee_hdr, type) + 1)) { + dev->stats.rx_frame_too_small++; + fprintf(stderr, "Frame too small\n"); + return -1; + } + + switch (hdr->type) { + case XBEE_TYPE_MODEM_STATUS: + case XBEE_TYPE_RECV: + case XBEE_TYPE_EXPL_RECV: + hdrlen = sizeof(struct xbee_hdr) - 1; /* no frame ID */ + break; + default: + hdrlen = sizeof(struct xbee_hdr); + break; + } + + /* check frame len */ + if (len < (hdrlen + 1)) { + dev->stats.rx_frame_too_small++; + fprintf(stderr, "Frame too small\n"); + return -1; + } + + /* validate the cksum */ + for (i = 3; i < (len - 1); i++) + cksum += ((uint8_t *)buf)[i]; + cksum = 0xff - cksum; + if (cksum != ((uint8_t *)buf)[len-1]) { + fprintf(stderr, "Invalid cksum\n"); + dev->stats.rx_invalid_cksum++; + return -1; + } + + /* dispatch */ + switch (hdr->type) { + case XBEE_TYPE_MODEM_STATUS: + dev->stats.rx_modem_status++; + channel = XBEE_DEFAULT_CHANNEL; + break; + case XBEE_TYPE_ATRESP: + if (xbee_proto_parse_atresp(dev, buf, len) < 0) + return -1; + channel = hdr->id; + break; + case XBEE_TYPE_RMT_ATRESP: + if (xbee_proto_parse_rmt_atresp(dev, buf, len) < 0) + return -1; + channel = hdr->id; + break; + case XBEE_TYPE_XMIT_STATUS: + if (xbee_proto_parse_xmit_status(dev, buf, len) < 0) + return -1; + channel = hdr->id; + break; + case XBEE_TYPE_RECV: + dev->stats.rx_data++; + channel = XBEE_DEFAULT_CHANNEL; + break; + case XBEE_TYPE_EXPL_RECV: + dev->stats.rx_expl_data++; + channel = XBEE_DEFAULT_CHANNEL; + break; + case XBEE_TYPE_NODE_ID: + dev->stats.rx_node_id++; + channel = hdr->id; //XXX + break; + /* invalid commands */ + case XBEE_TYPE_ATCMD: + case XBEE_TYPE_ATCMD_Q: + case XBEE_TYPE_XMIT: + case XBEE_TYPE_EXPL_XMIT: + case XBEE_TYPE_RMT_ATCMD: + default: + dev->stats.rx_invalid_type++; + break; + } + + /* fallback to default channel if not registered */ + if (channel < 0 || channel >= XBEE_MAX_CHANNEL || + dev->channel[channel].registered == 0) + channel = XBEE_DEFAULT_CHANNEL; + + /* execute the callback if any */ + if (dev->channel[channel].rx_cb != NULL) + dev->channel[channel].rx_cb(dev, channel, hdr->type, + buf + hdrlen, len - hdrlen - 1, + dev->channel[channel].arg); + + return 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("%s at [%p], len=%d\n", title, data, len); + ofs = 0; + while (ofs < len) { + /* format 1 line in the buffer, then use printk to print them */ + out = snprintf(line, LINE_LEN, "%08X", ofs); + for (i=0; ofs+i < len && i<16; i++) + out += snprintf(line+out, LINE_LEN - out, " %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(line+out, LINE_LEN - out, "%c", c); + } + printf("%s\n", line); + } +} + +int xbee_proto_xmit(struct xbee_dev *dev, uint8_t channel_id, uint8_t type, + void *buf, unsigned len) +{ + struct xbee_hdr hdr; + struct iovec iov[3]; + int i; + uint8_t cksum = 0; + + /* there is no empty message, so return an error */ + if (len == 0) + return -1; + + /* prepare an iovec to avoid a copy: prepend a header to the + * buffer and append a checksum */ + hdr.delimiter = XBEE_DELIMITER; + hdr.len = htons(len + 2); + hdr.type = type; + hdr.id = channel_id; + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + iov[1].iov_base = buf; + iov[1].iov_len = len; + iov[2].iov_base = &cksum; + iov[2].iov_len = 1; + + if (channel_id < 0 || channel_id >= XBEE_MAX_CHANNEL || + dev->channel[channel_id].registered == 0) { + dev->stats.tx_invalid_channel ++; + return -1; + } + + /* calculate the cksum */ + cksum = hdr.type; + cksum += hdr.id; + for (i = 0; i < len; i++) + cksum += ((uint8_t *)buf)[i]; + cksum = 0xff - cksum; + dev->stats.tx_frame ++; + + /* some additional checks before sending */ + switch (hdr.type) { + + case XBEE_TYPE_ATCMD: + // XXX some checks ? + dev->stats.tx_atcmd ++; + break; + case XBEE_TYPE_ATCMD_Q: + dev->stats.tx_atcmd_q ++; + break; + case XBEE_TYPE_XMIT: + dev->stats.tx_data ++; + break; + case XBEE_TYPE_EXPL_XMIT: + dev->stats.tx_expl_data ++; + break; + case XBEE_TYPE_RMT_ATCMD: + dev->stats.tx_rmt_atcmd ++; + break; + + /* invalid commands */ + case XBEE_TYPE_XMIT_STATUS: + case XBEE_TYPE_MODEM_STATUS: + case XBEE_TYPE_ATRESP: + case XBEE_TYPE_RECV: + case XBEE_TYPE_EXPL_RECV: + case XBEE_TYPE_NODE_ID: + case XBEE_TYPE_RMT_ATRESP: + default: + dev->stats.tx_invalid_type ++; + fprintf(stderr, "unhandled xmit type=%x\n", hdr.type); + return -1; + } + + hexdump("hdr", (uint8_t *)&hdr, sizeof(hdr)); + hexdump("buf", (uint8_t *)buf, len); + hexdump("cksum", &cksum, 1); + + return writev(dev->fd, iov, 3); +} + diff --git a/xbee_proto.h b/xbee_proto.h new file mode 100644 index 0000000..49af583 --- /dev/null +++ b/xbee_proto.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* protocol headers */ + +#define XBEE_DELIMITER 0x7E +#define XBEE_MAX_FRAME_LEN 0x200 + +struct xbee_hdr { + uint8_t delimiter; + uint16_t len; + uint8_t type; + uint8_t id; /* not always present */ +} __attribute__((packed)); + +#define XBEE_TYPE_ATCMD 0x08 +struct xbee_atcmd_hdr { + uint16_t cmd; + uint8_t params[]; +} __attribute__((packed)); + +#define XBEE_TYPE_ATCMD_Q 0x09 +struct xbee_atcmd_q_hdr { + uint16_t cmd; + uint8_t params[]; +} __attribute__((packed)); + +#define XBEE_TYPE_XMIT 0x10 +struct xbee_xmit_hdr { + uint64_t dstaddr; + uint16_t reserved; + uint8_t bcast_radius; + uint8_t opts; + uint8_t data[]; +} __attribute__((packed)); + +#define XBEE_TYPE_EXPL_XMIT 0x11 +struct xbee_expl_xmit_hdr { + uint64_t dstaddr; + uint16_t reserved; + uint8_t src_endpoint; + uint8_t dst_endpoint; + uint16_t cluster_id; + uint16_t profile_id; + uint8_t bcast_radius; + uint8_t opts; + uint8_t data[]; +} __attribute__((packed)); + +#define XBEE_TYPE_RMT_ATCMD 0x17 +struct xbee_rmt_atcmd_hdr { + uint64_t dstaddr; + uint16_t reserved; + uint8_t opts; + uint16_t cmd; + uint8_t params[]; +} __attribute__((packed)); + +#define XBEE_TYPE_ATRESP 0x88 +struct xbee_atresp_hdr { + uint16_t cmd; + uint8_t status; + uint8_t data[]; +} __attribute__((packed)); + +#define XBEE_TYPE_MODEM_STATUS 0x8A +struct xbee_modem_status_hdr { + /* empty */ +} __attribute__((packed)); + +#define XBEE_TYPE_XMIT_STATUS 0x8B +struct xbee_xmit_status_hdr { + uint16_t reserved; + uint8_t xmit_retry_cnt; + uint8_t delivery_status; + uint8_t discovery_status; +} __attribute__((packed)); + +#define XBEE_TYPE_RECV 0x90 +struct xbee_recv_hdr { + uint64_t srcaddr; + uint16_t reserved; + uint8_t opts; + uint8_t data[]; +} __attribute__((packed)); + +#define XBEE_TYPE_EXPL_RECV 0x91 +struct xbee_expl_recv_hdr { + uint64_t srcaddr; + uint16_t reserved; + uint8_t src_endpoint; + uint8_t dst_endpoint; + uint16_t cluster_id; + uint16_t profile_id; + uint8_t opts; + uint8_t data[]; +} __attribute__((packed)); + +#define XBEE_TYPE_NODE_ID 0x95 +struct xbee_node_id_hdr { + uint64_t srcaddr; + uint16_t srcnetwork; + uint8_t opts; + uint16_t dstnetwork; + uint64_t dstaddr; + uint8_t ni_string[]; + /* uint16_t parentaddr; after variable field */ +} __attribute__((packed)); + +#define XBEE_TYPE_RMT_ATRESP 0x97 +struct xbee_rmt_atresp_hdr { + uint64_t srcaddr; + uint16_t reserved; + uint16_t cmd; + uint8_t status; + uint8_t data[]; +} __attribute__((packed)); + +struct xbee_dev; + +/* return negative on error, 0 if there is not frame, or framelen */ +int xbee_proto_get_frame(struct xbee_dev *dev, void *buf, unsigned len); + +/* parse a frame: return 0 if the frame is valid, else a negative value */ +int xbee_proto_parse_frame(struct xbee_dev *dev, void *buf, unsigned len); + +/* send a frame */ +int xbee_proto_xmit(struct xbee_dev *dev, uint8_t id, uint8_t type, + void *buf, unsigned len); diff --git a/xbee_stats.c b/xbee_stats.c new file mode 100644 index 0000000..424abc0 --- /dev/null +++ b/xbee_stats.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2011, 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 "xbee_neighbor.h" +#include "xbee_stats.h" +#include "xbee_proto.h" +#include "xbee_buf.h" +#include "xbee.h" + +struct xbee_stats *xbee_get_stats(struct xbee_dev *dev) +{ + return &dev->stats; +} + +void xbee_reset_stats(struct xbee_dev *dev) +{ + memset(&dev->stats, 0, sizeof(dev->stats)); +} + +void xbee_dump_stats(FILE *file, struct xbee_dev *dev) +{ + fprintf(file, "statistics on dev %s:\n", dev->name); + fprintf(file, " rx_frame: %d\n", dev->stats.rx_frame); + fprintf(file, " rx_atresp: %d\n", dev->stats.rx_atresp); + fprintf(file, " rx_atresp_error: %d\n", dev->stats.rx_atresp_error); + fprintf(file, " rx_modem_status: %d\n", dev->stats.rx_modem_status); + fprintf(file, " rx_xmit_status: %d\n", dev->stats.rx_xmit_status); + fprintf(file, " rx_xmit_status_error: %d\n", dev->stats.rx_xmit_status_error); + fprintf(file, " rx_data: %d\n", dev->stats.rx_data); + fprintf(file, " rx_expl_data: %d\n", dev->stats.rx_expl_data); + fprintf(file, " rx_node_id: %d\n", dev->stats.rx_node_id); + fprintf(file, " rx_rmt_atresp: %d\n", dev->stats.rx_rmt_atresp); + fprintf(file, " rx_rmt_atresp_error: %d\n", dev->stats.rx_rmt_atresp_error); + fprintf(file, " rx_frame_too_small: %d\n", dev->stats.rx_frame_too_small); + fprintf(file, " rx_frame_too_large: %d\n", dev->stats.rx_frame_too_large); + fprintf(file, " rx_invalid_cksum: %d\n", dev->stats.rx_invalid_cksum); + fprintf(file, " rx_invalid_type: %d\n", dev->stats.rx_invalid_type); + fprintf(file, " rx_no_delim: %d\n", dev->stats.rx_no_delim); + fprintf(file, " tx_frame: %d\n", dev->stats.tx_frame); + fprintf(file, " tx_atcmd: %d\n", dev->stats.tx_atcmd); + fprintf(file, " tx_atcmd_q: %d\n", dev->stats.tx_atcmd_q); + fprintf(file, " tx_data: %d\n", dev->stats.tx_data); + fprintf(file, " tx_expl_data: %d\n", dev->stats.tx_expl_data); + fprintf(file, " tx_xmit_retries: %d\n", dev->stats.tx_xmit_retries); + fprintf(file, " tx_rmt_atcmd: %d\n", dev->stats.tx_rmt_atcmd); + fprintf(file, " tx_invalid_type: %d\n", dev->stats.tx_invalid_type); + fprintf(file, " tx_invalid_channel: %d\n", dev->stats.tx_invalid_channel); +} diff --git a/xbee_stats.h b/xbee_stats.h new file mode 100644 index 0000000..0d3f8ec --- /dev/null +++ b/xbee_stats.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* per-device statistics */ +struct xbee_stats { + int rx_frame; + int rx_atresp; + int rx_atresp_error; + int rx_modem_status; + int rx_xmit_status; + int rx_xmit_status_error; + int rx_data; + int rx_expl_data; + int rx_node_id; + int rx_rmt_atresp; + int rx_rmt_atresp_error; + int rx_frame_too_small; + int rx_frame_too_large; + int rx_invalid_cksum; + int rx_invalid_type; + int rx_no_delim; + + int tx_frame; + int tx_atcmd; + int tx_atcmd_q; + int tx_data; + int tx_expl_data; + int tx_xmit_retries; + int tx_rmt_atcmd; + int tx_invalid_type; + int tx_invalid_channel; +}; + +struct xbee_dev; + +/* return pointer to device stats */ +struct xbee_stats *xbee_get_stats(struct xbee_dev *dev); + +/* reset statistics of device */ +void xbee_reset_stats(struct xbee_dev *dev); + +/* dump statistics on specified file */ +void xbee_dump_stats(FILE *file, struct xbee_dev *dev); -- 2.39.5