2 * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the University of California, Berkeley nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #define _BSD_SOURCE /* for be64toh, endian.h */
42 #include <netinet/in.h>
43 #include <sys/queue.h>
44 #include <linux/joystick.h>
48 #include <cmdline_rdline.h>
49 #include <cmdline_parse.h>
50 #include <cmdline_socket.h>
55 #include "xbee_neighbor.h"
56 #include "xbee_atcmd.h"
57 #include "xbee_stats.h"
59 #include "xbee_proto.h"
65 #define TIMEOUT_US 1000000
67 /* XXX qd on deconnecte, prend 100% du cpu */
69 /* XXX neighbor discovery */
70 /* XXX dump et restauration de la config */
72 /* global xbee device */
73 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
74 struct xbee_atresp_hdr *frame,
77 struct xbee_dev *xbee_dev;
80 static struct event stdin_read_event, xbee_read_event;
81 static struct event joystick_read_event;
83 static struct cmdline *xbee_cl;
86 static char *xbee_devname = NULL;
87 static int xbee_baud = 9600;
92 static char *joystick_devname = NULL;
93 struct joystick_info joyinfo;
95 void xbeeapp_log(int always_on_stdout, const char *fmt, ...)
101 if (xbee_logfile != NULL)
102 vfprintf(xbee_logfile, fmt, ap);
103 if (always_on_stdout || xbee_debug)
109 static void hexdump(int always_on_stdout, const char *title, const void *buf,
112 unsigned int i, out, ofs;
113 const unsigned char *data = buf;
115 char line[LINE_LEN]; /* space needed 8+16*3+3+16 == 75 */
118 gettimeofday(&tv, NULL);
120 xbeeapp_log(always_on_stdout, "%"PRIi64".%.6d %s at [%p], len=%d\n",
121 (uint64_t)tv.tv_sec, (int)tv.tv_usec, title, data, len);
124 /* format 1 line in the buffer, then use printk to print them */
125 out = snprintf(line, LINE_LEN, "%08X", ofs);
126 for (i=0; ofs+i < len && i<16; i++)
127 out += snprintf(line+out, LINE_LEN - out, " %02X",
130 out += snprintf(line+out, LINE_LEN - out, " ");
131 for (i=0; ofs < len && i<16; i++, ofs++) {
132 unsigned char c = data[ofs];
133 if (!isascii(c) || !isprint(c))
135 out += snprintf(line+out, LINE_LEN - out, "%c", c);
137 xbeeapp_log(always_on_stdout, "%s\n", line);
141 static int parse_xmit_status(struct xbee_ctx *ctx,
142 struct xbee_xmit_status_hdr *frame, unsigned len)
145 xbeeapp_log(1, "no context\n");
149 /* see if it matches a xmit query */
150 if (ctx->type != SEND_MSG) {
151 xbeeapp_log(ctx->foreground, "invalid response\n");
155 /* XXX use defines for these values */
156 if (frame->delivery_status == 0x00)
157 xbeeapp_log(ctx->foreground, "Success\n");
158 else if (frame->delivery_status == 0x01)
159 xbeeapp_log(ctx->foreground, "MAC ACK Failure\n");
160 else if (frame->delivery_status == 0x15)
161 xbeeapp_log(ctx->foreground, "Invalid destination endpoint\n");
162 else if (frame->delivery_status == 0x21)
163 xbeeapp_log(ctx->foreground, "Network ACK Failure\n");
164 else if (frame->delivery_status == 0x25)
165 xbeeapp_log(ctx->foreground, "Route Not Found\n");
170 static int atcmd_frame_status(struct xbee_atresp_hdr *frame, unsigned len)
174 /* get AT command from frame */
175 memcpy(atcmd_str, &frame->cmd, 2);
178 if (frame->status == 1)
179 xbeeapp_log(1, "<%s>: Status is error\n", atcmd_str);
180 else if (frame->status == 2)
181 xbeeapp_log(1, "<%s>: Invalid command\n", atcmd_str);
182 else if (frame->status == 3)
183 xbeeapp_log(1, "<%s>: Invalid parameter\n", atcmd_str);
184 else if (frame->status != 0)
185 xbeeapp_log(1, "<%s>: Unknown status error %d\n", atcmd_str,
187 return frame->status;
190 /* assume frame->status is 0 */
191 static int atcmd_frame_to_string(struct xbee_atresp_hdr *frame, unsigned len,
192 char *dstbuf, unsigned dstlen)
199 } __attribute__((packed)) *result;
201 struct xbee_atcmd *cmd;
203 /* get AT command from frame */
204 memcpy(atcmd_str, &frame->cmd, 2);
207 /* see if it exists */
208 cmd = xbee_atcmd_lookup_name(atcmd_str);
210 xbeeapp_log(1, "unknown response\n");
215 result = (void *)frame->data;
216 len -= offsetof(struct xbee_atresp_hdr, data);
217 if (cmd->flags & XBEE_ATCMD_F_PARAM_NONE && len == 0)
218 snprintf(dstbuf, dstlen, "ok");
219 if (cmd->flags & XBEE_ATCMD_F_PARAM_U8 && len == sizeof(uint8_t))
220 snprintf(dstbuf, dstlen, "0x%x", result->u8);
221 else if (cmd->flags & XBEE_ATCMD_F_PARAM_U16 && len == sizeof(uint16_t))
222 snprintf(dstbuf, dstlen, "0x%x", ntohs(result->u16));
223 else if (cmd->flags & XBEE_ATCMD_F_PARAM_U32 && len == sizeof(uint32_t))
224 snprintf(dstbuf, dstlen, "0x%x", ntohl(result->u32));
225 else if (cmd->flags & XBEE_ATCMD_F_PARAM_S16 && len == sizeof(int16_t))
226 snprintf(dstbuf, dstlen, "%d\n", ntohs(result->s16));
233 static int dump_atcmd(struct xbee_ctx *ctx, struct xbee_atresp_hdr *frame,
238 /* see if it matches query */
239 if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
240 printf("invalid response\n");
245 if (atcmd_frame_status(frame, len) == 0) {
248 if (ctx->func != NULL)
249 ctx->func(frame, len, ctx->arg);
251 if (len == sizeof(struct xbee_atresp_hdr))
252 xbeeapp_log(ctx->foreground, "<%s>: ok\n",
253 ctx->atcmd_query->name);
254 else if (atcmd_frame_to_string(frame, len, buf,
256 xbeeapp_log(ctx->foreground, "<%s> is %s\n",
257 ctx->atcmd_query->name, buf);
259 hexdump(ctx->foreground, "atcmd answer", frame, len);
264 int xbee_recv_data(struct xbee_recv_hdr *recvframe, unsigned len)
266 int datalen = len - sizeof(*recvframe);
267 struct rc_proto_hdr *rch = (struct rc_proto_hdr *) &recvframe->data;
269 if (datalen < sizeof(struct rc_proto_hdr))
273 case RC_PROTO_TYPE_CHANNEL:
274 if (datalen != sizeof(struct rc_proto_channel))
277 case RC_PROTO_TYPE_RANGE: {
278 struct rc_proto_range *rcr =
279 (struct rc_proto_range *) recvframe->data;
281 if (datalen != sizeof(struct rc_proto_range))
284 if (rcr->power_level >= MAX_POWER_LEVEL)
287 rc_proto_rx_range(rcr->power_level);
299 void xbee_rx(struct xbee_dev *dev, int channel, int type,
300 void *frame, unsigned len, void *opaque)
302 struct xbee_ctx *ctx = opaque;
303 int do_hexdump = xbee_hexdump;
305 xbeeapp_log(0, "type=0x%x, channel=%d, ctx=%p\n", type, channel, ctx);
306 hexdump(0, "rx", frame, len);
308 /* if ctx is !NULL, it is an answer to a query */
310 /* XXX only delete timeout if answer matched */
311 xbee_unload_timeout(ctx);
312 if (xbee_debug && ctx->atcmd_query != NULL)
313 printf("Received answer to query <%s>\n",
314 ctx->atcmd_query->name);
315 xbee_unregister_channel(dev, channel);
318 /* some additional checks before sending */
320 case XBEE_TYPE_MODEM_STATUS: {
321 printf("Received Modem Status frame\n");
325 case XBEE_TYPE_RMT_ATRESP: {
327 memcpy(&addr, frame, sizeof(addr));
328 addr = be64toh(addr);
329 printf("from remote address %"PRIx64"\n", addr);
331 /* this answer contains an atcmd answer at offset 10 */
332 if (ctx != NULL && ctx->type == ATCMD_RMT) {
333 if (dump_atcmd(ctx, frame + 10, len - 10) < 0)
337 printf("invalid response\n");
342 case XBEE_TYPE_ATRESP: {
343 /* we are currently dumping config, continue */
344 if (ctx != NULL && ctx->type == DUMP_CONF) {
345 xbeeapp_dump_conf_continue(ctx, frame, len);
347 else if (ctx != NULL && ctx->type == ATCMD) {
348 if (dump_atcmd(ctx, frame, len) < 0)
352 printf("invalid response\n");
358 case XBEE_TYPE_XMIT_STATUS: {
359 if (parse_xmit_status(ctx, frame, len) < 0)
364 case XBEE_TYPE_RECV: {
365 if (xbee_recv_data(frame, len) < 0)
370 case XBEE_TYPE_ATCMD:
371 case XBEE_TYPE_ATCMD_Q:
373 case XBEE_TYPE_EXPL_XMIT:
374 case XBEE_TYPE_RMT_ATCMD:
375 case XBEE_TYPE_EXPL_RECV:
376 case XBEE_TYPE_NODE_ID:
378 xbeeapp_log(0, "Invalid frame\n");
384 xbeeapp_log(1, "rx frame type=0x%x, channel=%d, ctx=%p\n",
386 hexdump(1, "undecoded rx frame", frame, len);
389 /* restart command line if it was a blocking query */
391 if (ctx->foreground && !ctx->have_more_command) {
393 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
395 if (!ctx->have_more_command)
400 static int xbeeapp_send(struct xbee_ctx *ctx, int type, void *buf, unsigned len,
406 if (len > XBEE_MAX_FRAME_LEN) {
407 printf("frame too large\n");
411 /* register a channel */
412 channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY,
415 printf("cannot send: no free channel\n");
419 xbeeapp_log(0, "send frame ctx=%p channel=%d type=0x%x len=%d\n",
420 ctx, channel, type, len);
421 hexdump(0, "xmit frame", buf, len);
423 /* transmit the frame on this channel */
424 ret = xbee_proto_xmit(xbee_dev, channel, type, buf,
427 printf("cannot send: %s\n", strerror(errno));
428 xbee_unregister_channel(xbee_dev, channel);
432 ctx->channel = channel;
433 xbee_load_timeout(ctx); /* load a timeout event */
435 /* suspend command line until we have answer or timeout */
438 rdline_stop(&xbee_cl->rdl); /* don't display prompt when return */
439 xbee_stdin_disable(); /* unload file descriptor polling */
445 /* send an AT command with parameters filled by caller. Disable
446 * command line until we get the answer or until a timeout occurs */
447 int xbeeapp_send_atcmd(const char *atcmd_str,
448 void *param, unsigned param_len, int foreground,
449 int (*func)(void *frame, unsigned len, void *arg),
452 struct xbee_ctx *ctx;
453 struct xbee_atcmd *cmd;
455 struct xbee_atcmd_hdr atcmd;
456 char buf[XBEE_MAX_FRAME_LEN];
457 } __attribute__((packed)) frame;
459 cmd = xbee_atcmd_lookup_name(atcmd_str);
461 printf("no such at command\n");
465 /* allocate memory to store the context for async cb */
466 ctx = malloc(sizeof(*ctx));
468 printf("not enough memory\n");
472 memset(ctx, 0, sizeof(*ctx));
474 ctx->atcmd_query = cmd;
478 memcpy(&frame.atcmd.cmd, atcmd_str, 2);
479 memcpy(&frame.buf, param, param_len);
481 if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &frame,
482 sizeof(struct xbee_atcmd_hdr) +
483 param_len, foreground) < 0) {
492 static int __xbeeapp_dump_config(struct xbee_ctx *ctx, struct xbee_atcmd *cmd)
494 struct xbee_atcmd_hdr atcmd;
496 /* find the first command that is readable and writable */
497 for (; cmd->name != NULL; cmd++) {
498 if ((cmd->flags & (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE)) ==
499 (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE))
502 if (cmd->name == NULL) {
503 printf("no register to dump\n");
507 ctx->atcmd_query = cmd;
508 ctx->have_more_command = 1;
509 memcpy(&atcmd.cmd, cmd->name, 2);
511 if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &atcmd,
512 sizeof(struct xbee_atcmd_hdr), 1) < 0) {
520 int xbeeapp_dump_config(const char *filename)
522 struct xbee_ctx *ctx;
523 struct xbee_atcmd *cmd;
526 /* find the first command that is readable and writable */
527 cmd = &xbee_atcmd_list[0];
529 /* allocate memory to store the context for async cb */
530 ctx = malloc(sizeof(*ctx));
532 printf("not enough memory\n");
536 memset(ctx, 0, sizeof(*ctx));
537 ctx->type = DUMP_CONF;
538 ret = __xbeeapp_dump_config(ctx, cmd);
545 /* continue a dump conf */
546 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
547 struct xbee_atresp_hdr *frame,
550 struct xbee_atcmd *cmd;
553 /* see if it matches query */
554 if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
555 printf("invalid response\n");
556 ctx->have_more_command = 0;
560 /* dump register content in buf, skip on error */
561 if (atcmd_frame_status(frame, len) == 0 &&
562 atcmd_frame_to_string(frame, len, buf, sizeof(buf)) == 0)
563 printf("write %s %s\n", ctx->atcmd_query->desc, buf);
565 cmd = ctx->atcmd_query + 1;
566 if (__xbeeapp_dump_config(ctx, cmd) < 0) {
567 ctx->have_more_command = 0;
568 //close(xbee_config_file);
569 printf("END\n"); /* XXX */
574 /* send data message */
575 int xbeeapp_send_msg(uint64_t addr, void *data, unsigned data_len,
578 struct xbee_ctx *ctx;
580 struct xbee_xmit_hdr xmit;
581 char buf[XBEE_MAX_FRAME_LEN];
582 } __attribute__((packed)) frame;
584 /* allocate memory to store the context for async cb */
585 ctx = malloc(sizeof(*ctx));
587 printf("not enough memory\n");
591 memset(ctx, 0, sizeof(*ctx));
592 ctx->type = SEND_MSG;
593 ctx->atcmd_query = NULL;
595 frame.xmit.dstaddr = htobe64(addr);
596 frame.xmit.reserved = htons(0xFFFE);
597 frame.xmit.bcast_radius = 0;
599 memcpy(&frame.buf, data, data_len);
601 if (xbeeapp_send(ctx, XBEE_TYPE_XMIT, &frame,
602 sizeof(struct xbee_xmit_hdr) +
603 data_len, foreground) < 0) {
611 void xbee_stdin_enable(void)
613 event_add(&stdin_read_event, NULL);
616 void xbee_stdin_disable(void)
618 event_del(&stdin_read_event);
621 static void evt_timeout(int s, short event, void *arg)
623 struct xbee_ctx *ctx = arg;
625 xbeeapp_log(0, "Timeout\n");
627 /* restart command line */
628 if (ctx->foreground) {
632 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
635 xbee_unregister_channel(xbee_dev, ctx->channel);
639 void xbee_load_timeout(struct xbee_ctx *ctx)
644 tv.tv_usec = TIMEOUT_US;
645 evtimer_set(&ctx->timeout, evt_timeout, ctx);
646 evtimer_add(&ctx->timeout, &tv);
649 void xbee_unload_timeout(struct xbee_ctx *ctx)
651 evtimer_del(&ctx->timeout);
655 evt_stdin_input(int s, short event, void *arg)
660 n = read(s, buf, sizeof(buf));
668 for (i = 0; i < n; i++) {
669 /* ctrl-d is pressed, back in cmdline mode XXX */
673 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
679 write(xbee_dev->fd, "\r\n", 2);
681 write(xbee_dev->fd, buf, n);
686 if (cmdline_in(xbee_cl, buf, n) < 0) {
694 evt_xbee_input(int s, short event, void *arg)
700 n = read(s, buf, sizeof(buf));
708 xbee_process_queue(xbee_dev);
713 usage(const char *prgname)
715 printf("%s [-s BAUD] -d DEVNAME\n"
716 " -s BAUD: set baud rate of xbee device\n"
717 " -d DEVNAME: xbee char device\n"
718 " -r: start in raw mode\n"
723 /* Parse the argument given in the command line of the application */
725 parse_args(int argc, char **argv)
730 char *prgname = argv[0];
731 static struct option lgopts[] = {
737 while ((opt = getopt_long(argc, argvopt, "hd:s:j:",
738 lgopts, &option_index)) != EOF) {
748 xbee_devname = strdup(optarg);
752 xbee_baud = atoi(optarg);
760 joystick_devname = optarg;
765 /* if (!strcmp(lgopts[option_index].name, "option")) */
774 if (xbee_devname == NULL) {
775 fprintf(stderr, "xbee device argument missing\n");
780 if (argc != optind) {
781 printf("Invalid argument\n");
791 int main(int argc, char **argv)
793 struct termios oldterm, term;
796 char xbeerc_path[256];
798 if (parse_args(argc, argv) < 0)
801 /* initializa libevent */
804 /* initialize libxbee */
813 if (joystick_devname != NULL) {
814 if (joystick_init(joystick_devname, &joyinfo) < 0) {
815 fprintf(stderr, "error in joystick init\n");
818 event_set(&joystick_read_event, joyinfo.fd, EV_READ | EV_PERSIST,
819 joystick_input, &joyinfo);
820 event_add(&joystick_read_event, NULL);
823 /* open xbee device */
824 xbee_dev = xbee_open(xbee_devname, xbee_baud);
825 if (xbee_dev == NULL)
828 /* register default channel with a callback */
829 if (xbee_register_channel(xbee_dev, XBEE_DEFAULT_CHANNEL,
830 xbee_rx, NULL) < 0) {
831 fprintf(stderr, "cannot register default channel\n");
835 /* add read event on xbee device */
836 event_set(&xbee_read_event, xbee_dev->fd, EV_READ | EV_PERSIST,
837 evt_xbee_input, xbee_dev);
838 event_add(&xbee_read_event, NULL);
840 /* set terminal in raw mode */
841 tcgetattr(0, &oldterm);
842 memcpy(&term, &oldterm, sizeof(term));
843 term.c_lflag &= ~(ICANON | ECHO | ISIG);
844 tcsetattr(0, TCSANOW, &term);
847 /* parse the config file */
848 homedir = getenv("HOME");
849 if (homedir != NULL) {
850 snprintf(xbeerc_path, sizeof(xbeerc_path), "%s/.xbeerc",
852 if (access(xbeerc_path, R_OK) == 0) {
854 xbee_cl = cmdline_file_new(&main_ctx, "xbeerc> ",
856 if (xbee_cl != NULL) {
857 cmdline_interact(xbee_cl);
858 cmdline_free(xbee_cl);
859 printf("\nconfig file parsed\n");
864 /* create a new cmdline instance */
865 xbee_cl = cmdline_stdin_new(&main_ctx, "xbee> ");
866 if (xbee_cl == NULL) {
871 /* load read event on stdin */
872 event_set(&stdin_read_event, 0, EV_READ | EV_PERSIST,
873 evt_stdin_input, xbee_cl);
874 event_add(&stdin_read_event, NULL);
876 /* libevent main loop */
879 tcsetattr(0, TCSANOW, &oldterm);
883 tcsetattr(0, TCSANOW, &oldterm);