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"
64 #define TIMEOUT_US 1000000
66 /* XXX qd on deconnecte, prend 100% du cpu */
68 /* XXX neighbor discovery */
69 /* XXX dump et restauration de la config */
71 /* global xbee device */
72 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
73 struct xbee_atresp_hdr *frame,
76 struct xbee_dev *xbee_dev;
79 static struct event stdin_read_event, xbee_read_event;
80 static struct event joystick_read_event;
82 static struct cmdline *xbee_cl;
85 static char *xbee_devname = NULL;
86 static int xbee_baud = 9600;
91 static char *joystick_devname = NULL;
92 struct joystick_info joyinfo;
94 void xbeeapp_log(int always_on_stdout, const char *fmt, ...)
100 if (xbee_logfile != NULL)
101 vfprintf(xbee_logfile, fmt, ap);
102 if (always_on_stdout || xbee_debug)
108 static void hexdump(int always_on_stdout, const char *title, const void *buf,
111 unsigned int i, out, ofs;
112 const unsigned char *data = buf;
114 char line[LINE_LEN]; /* space needed 8+16*3+3+16 == 75 */
117 gettimeofday(&tv, NULL);
119 xbeeapp_log(always_on_stdout, "%"PRIi64".%.6d %s at [%p], len=%d\n",
120 (uint64_t)tv.tv_sec, (int)tv.tv_usec, title, data, len);
123 /* format 1 line in the buffer, then use printk to print them */
124 out = snprintf(line, LINE_LEN, "%08X", ofs);
125 for (i=0; ofs+i < len && i<16; i++)
126 out += snprintf(line+out, LINE_LEN - out, " %02X",
129 out += snprintf(line+out, LINE_LEN - out, " ");
130 for (i=0; ofs < len && i<16; i++, ofs++) {
131 unsigned char c = data[ofs];
132 if (!isascii(c) || !isprint(c))
134 out += snprintf(line+out, LINE_LEN - out, "%c", c);
136 xbeeapp_log(always_on_stdout, "%s\n", line);
140 static int parse_xmit_status(struct xbee_ctx *ctx,
141 struct xbee_xmit_status_hdr *frame, unsigned len)
144 xbeeapp_log(1, "no context\n");
148 /* see if it matches a xmit query */
149 if (ctx->type != SEND_MSG) {
150 xbeeapp_log(ctx->foreground, "invalid response\n");
154 /* XXX use defines for these values */
155 if (frame->delivery_status == 0x00)
156 xbeeapp_log(ctx->foreground, "Success\n");
157 else if (frame->delivery_status == 0x01)
158 xbeeapp_log(ctx->foreground, "MAC ACK Failure\n");
159 else if (frame->delivery_status == 0x15)
160 xbeeapp_log(ctx->foreground, "Invalid destination endpoint\n");
161 else if (frame->delivery_status == 0x21)
162 xbeeapp_log(ctx->foreground, "Network ACK Failure\n");
163 else if (frame->delivery_status == 0x25)
164 xbeeapp_log(ctx->foreground, "Route Not Found\n");
169 static int atcmd_frame_status(struct xbee_atresp_hdr *frame, unsigned len)
173 /* get AT command from frame */
174 memcpy(atcmd_str, &frame->cmd, 2);
177 if (frame->status == 1)
178 xbeeapp_log(1, "<%s>: Status is error\n", atcmd_str);
179 else if (frame->status == 2)
180 xbeeapp_log(1, "<%s>: Invalid command\n", atcmd_str);
181 else if (frame->status == 3)
182 xbeeapp_log(1, "<%s>: Invalid parameter\n", atcmd_str);
183 else if (frame->status != 0)
184 xbeeapp_log(1, "<%s>: Unknown status error %d\n", atcmd_str,
186 return frame->status;
189 /* assume frame->status is 0 */
190 static int atcmd_frame_to_string(struct xbee_atresp_hdr *frame, unsigned len,
191 char *dstbuf, unsigned dstlen)
198 } __attribute__((packed)) *result;
200 struct xbee_atcmd *cmd;
202 /* get AT command from frame */
203 memcpy(atcmd_str, &frame->cmd, 2);
206 /* see if it exists */
207 cmd = xbee_atcmd_lookup_name(atcmd_str);
209 xbeeapp_log(1, "unknown response\n");
214 result = (void *)frame->data;
215 len -= offsetof(struct xbee_atresp_hdr, data);
216 if (cmd->flags & XBEE_ATCMD_F_PARAM_NONE && len == 0)
217 snprintf(dstbuf, dstlen, "ok");
218 if (cmd->flags & XBEE_ATCMD_F_PARAM_U8 && len == sizeof(uint8_t))
219 snprintf(dstbuf, dstlen, "0x%x", result->u8);
220 else if (cmd->flags & XBEE_ATCMD_F_PARAM_U16 && len == sizeof(uint16_t))
221 snprintf(dstbuf, dstlen, "0x%x", ntohs(result->u16));
222 else if (cmd->flags & XBEE_ATCMD_F_PARAM_U32 && len == sizeof(uint32_t))
223 snprintf(dstbuf, dstlen, "0x%x", ntohl(result->u32));
224 else if (cmd->flags & XBEE_ATCMD_F_PARAM_S16 && len == sizeof(int16_t))
225 snprintf(dstbuf, dstlen, "%d\n", ntohs(result->s16));
232 static int dump_atcmd(struct xbee_ctx *ctx, struct xbee_atresp_hdr *frame,
237 /* see if it matches query */
238 if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
239 printf("invalid response\n");
244 if (atcmd_frame_status(frame, len) == 0) {
247 if (ctx->func != NULL)
248 ctx->func(frame, len, ctx->arg);
250 if (len == sizeof(struct xbee_atresp_hdr))
251 xbeeapp_log(ctx->foreground, "<%s>: ok\n",
252 ctx->atcmd_query->name);
253 else if (atcmd_frame_to_string(frame, len, buf,
255 xbeeapp_log(ctx->foreground, "<%s> is %s\n",
256 ctx->atcmd_query->name, buf);
258 hexdump(ctx->foreground, "atcmd answer", frame, len);
263 void xbee_rx(struct xbee_dev *dev, int channel, int type,
264 void *frame, unsigned len, void *opaque)
266 struct xbee_ctx *ctx = opaque;
267 int do_hexdump = xbee_hexdump;
269 xbeeapp_log(0, "type=0x%x, channel=%d, ctx=%p\n", type, channel, ctx);
270 hexdump(0, "rx", frame, len);
272 /* if ctx is !NULL, it is an answer to a query */
274 /* XXX only delete timeout if answer matched */
275 xbee_unload_timeout(ctx);
276 if (xbee_debug && ctx->atcmd_query != NULL)
277 printf("Received answer to query <%s>\n",
278 ctx->atcmd_query->name);
279 xbee_unregister_channel(dev, channel);
282 /* some additional checks before sending */
284 case XBEE_TYPE_MODEM_STATUS: {
285 printf("Received Modem Status frame\n");
289 case XBEE_TYPE_RMT_ATRESP: {
291 memcpy(&addr, frame, sizeof(addr));
292 addr = be64toh(addr);
293 printf("from remote address %"PRIx64"\n", addr);
295 /* this answer contains an atcmd answer at offset 10 */
296 if (ctx != NULL && ctx->type == ATCMD_RMT) {
297 if (dump_atcmd(ctx, frame + 10, len - 10) < 0)
301 printf("invalid response\n");
306 case XBEE_TYPE_ATRESP: {
307 /* we are currently dumping config, continue */
308 if (ctx != NULL && ctx->type == DUMP_CONF) {
309 xbeeapp_dump_conf_continue(ctx, frame, len);
311 else if (ctx != NULL && ctx->type == ATCMD) {
312 if (dump_atcmd(ctx, frame, len) < 0)
316 printf("invalid response\n");
322 case XBEE_TYPE_XMIT_STATUS: {
323 if (parse_xmit_status(ctx, frame, len) < 0)
328 case XBEE_TYPE_RECV: {
329 struct xbee_recv_hdr *recvframe = frame;
330 int recvlen = len - sizeof(*recvframe);
333 /* if we receive a range-test frame, ask for RSSI now */
334 if (recvlen >= strlen("range") &&
335 !strncmp((char *)recvframe->data,
336 "range", strlen("range"))) {
337 xbeeapp_send_atcmd("DB", NULL, 0, 0, NULL, NULL);
340 hexdump(on_stdout, "rx data", recvframe->data,
345 case XBEE_TYPE_ATCMD:
346 case XBEE_TYPE_ATCMD_Q:
348 case XBEE_TYPE_EXPL_XMIT:
349 case XBEE_TYPE_RMT_ATCMD:
350 case XBEE_TYPE_EXPL_RECV:
351 case XBEE_TYPE_NODE_ID:
353 xbeeapp_log(0, "Invalid frame\n");
359 xbeeapp_log(1, "rx frame type=0x%x, channel=%d, ctx=%p\n",
361 hexdump(1, "undecoded rx frame", frame, len);
364 /* restart command line if it was a blocking query */
366 if (ctx->foreground && !ctx->have_more_command) {
368 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
370 if (!ctx->have_more_command)
375 static int xbeeapp_send(struct xbee_ctx *ctx, int type, void *buf, unsigned len,
381 if (len > XBEE_MAX_FRAME_LEN) {
382 printf("frame too large\n");
386 /* register a channel */
387 channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY,
390 printf("cannot send: no free channel\n");
394 xbeeapp_log(0, "send frame ctx=%p channel=%d type=0x%x len=%d\n",
395 ctx, channel, type, len);
396 hexdump(0, "xmit frame", buf, len);
398 /* transmit the frame on this channel */
399 ret = xbee_proto_xmit(xbee_dev, channel, type, buf,
402 printf("cannot send: %s\n", strerror(errno));
403 xbee_unregister_channel(xbee_dev, channel);
407 ctx->channel = channel;
408 xbee_load_timeout(ctx); /* load a timeout event */
410 /* suspend command line until we have answer or timeout */
413 rdline_stop(&xbee_cl->rdl); /* don't display prompt when return */
414 xbee_stdin_disable(); /* unload file descriptor polling */
420 /* send an AT command with parameters filled by caller. Disable
421 * command line until we get the answer or until a timeout occurs */
422 int xbeeapp_send_atcmd(const char *atcmd_str,
423 void *param, unsigned param_len, int foreground,
424 int (*func)(void *frame, unsigned len, void *arg),
427 struct xbee_ctx *ctx;
428 struct xbee_atcmd *cmd;
430 struct xbee_atcmd_hdr atcmd;
431 char buf[XBEE_MAX_FRAME_LEN];
432 } __attribute__((packed)) frame;
434 cmd = xbee_atcmd_lookup_name(atcmd_str);
436 printf("no such at command\n");
440 /* allocate memory to store the context for async cb */
441 ctx = malloc(sizeof(*ctx));
443 printf("not enough memory\n");
447 memset(ctx, 0, sizeof(*ctx));
449 ctx->atcmd_query = cmd;
453 memcpy(&frame.atcmd.cmd, atcmd_str, 2);
454 memcpy(&frame.buf, param, param_len);
456 if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &frame,
457 sizeof(struct xbee_atcmd_hdr) +
458 param_len, foreground) < 0) {
467 static int __xbeeapp_dump_config(struct xbee_ctx *ctx, struct xbee_atcmd *cmd)
469 struct xbee_atcmd_hdr atcmd;
471 /* find the first command that is readable and writable */
472 for (; cmd->name != NULL; cmd++) {
473 if ((cmd->flags & (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE)) ==
474 (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE))
477 if (cmd->name == NULL) {
478 printf("no register to dump\n");
482 ctx->atcmd_query = cmd;
483 ctx->have_more_command = 1;
484 memcpy(&atcmd.cmd, cmd->name, 2);
486 if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &atcmd,
487 sizeof(struct xbee_atcmd_hdr), 1) < 0) {
495 int xbeeapp_dump_config(const char *filename)
497 struct xbee_ctx *ctx;
498 struct xbee_atcmd *cmd;
501 /* find the first command that is readable and writable */
502 cmd = &xbee_atcmd_list[0];
504 /* allocate memory to store the context for async cb */
505 ctx = malloc(sizeof(*ctx));
507 printf("not enough memory\n");
511 memset(ctx, 0, sizeof(*ctx));
512 ctx->type = DUMP_CONF;
513 ret = __xbeeapp_dump_config(ctx, cmd);
520 /* continue a dump conf */
521 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
522 struct xbee_atresp_hdr *frame,
525 struct xbee_atcmd *cmd;
528 /* see if it matches query */
529 if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
530 printf("invalid response\n");
531 ctx->have_more_command = 0;
535 /* dump register content in buf, skip on error */
536 if (atcmd_frame_status(frame, len) == 0 &&
537 atcmd_frame_to_string(frame, len, buf, sizeof(buf)) == 0)
538 printf("write %s %s\n", ctx->atcmd_query->desc, buf);
540 cmd = ctx->atcmd_query + 1;
541 if (__xbeeapp_dump_config(ctx, cmd) < 0) {
542 ctx->have_more_command = 0;
543 //close(xbee_config_file);
544 printf("END\n"); /* XXX */
549 /* send data message */
550 int xbeeapp_send_msg(uint64_t addr, void *data, unsigned data_len,
553 struct xbee_ctx *ctx;
555 struct xbee_xmit_hdr xmit;
556 char buf[XBEE_MAX_FRAME_LEN];
557 } __attribute__((packed)) frame;
559 /* allocate memory to store the context for async cb */
560 ctx = malloc(sizeof(*ctx));
562 printf("not enough memory\n");
566 memset(ctx, 0, sizeof(*ctx));
567 ctx->type = SEND_MSG;
568 ctx->atcmd_query = NULL;
570 frame.xmit.dstaddr = htobe64(addr);
571 frame.xmit.reserved = htons(0xFFFE);
572 frame.xmit.bcast_radius = 0;
574 memcpy(&frame.buf, data, data_len);
576 if (xbeeapp_send(ctx, XBEE_TYPE_XMIT, &frame,
577 sizeof(struct xbee_xmit_hdr) +
578 data_len, foreground) < 0) {
586 void xbee_stdin_enable(void)
588 event_add(&stdin_read_event, NULL);
591 void xbee_stdin_disable(void)
593 event_del(&stdin_read_event);
596 static void evt_timeout(int s, short event, void *arg)
598 struct xbee_ctx *ctx = arg;
600 xbeeapp_log(0, "Timeout\n");
602 /* restart command line */
603 if (ctx->foreground) {
607 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
610 xbee_unregister_channel(xbee_dev, ctx->channel);
614 void xbee_load_timeout(struct xbee_ctx *ctx)
619 tv.tv_usec = TIMEOUT_US;
620 evtimer_set(&ctx->timeout, evt_timeout, ctx);
621 evtimer_add(&ctx->timeout, &tv);
624 void xbee_unload_timeout(struct xbee_ctx *ctx)
626 evtimer_del(&ctx->timeout);
630 evt_stdin_input(int s, short event, void *arg)
635 n = read(s, buf, sizeof(buf));
643 for (i = 0; i < n; i++) {
644 /* ctrl-d is pressed, back in cmdline mode XXX */
648 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
654 write(xbee_dev->fd, "\r\n", 2);
656 write(xbee_dev->fd, buf, n);
661 if (cmdline_in(xbee_cl, buf, n) < 0) {
669 evt_xbee_input(int s, short event, void *arg)
675 n = read(s, buf, sizeof(buf));
683 xbee_process_queue(xbee_dev);
688 usage(const char *prgname)
690 printf("%s [-s BAUD] -d DEVNAME\n"
691 " -s BAUD: set baud rate of xbee device\n"
692 " -d DEVNAME: xbee char device\n"
693 " -r: start in raw mode\n"
698 /* Parse the argument given in the command line of the application */
700 parse_args(int argc, char **argv)
705 char *prgname = argv[0];
706 static struct option lgopts[] = {
712 while ((opt = getopt_long(argc, argvopt, "hd:s:j:",
713 lgopts, &option_index)) != EOF) {
723 xbee_devname = strdup(optarg);
727 xbee_baud = atoi(optarg);
735 joystick_devname = optarg;
740 /* if (!strcmp(lgopts[option_index].name, "option")) */
749 if (xbee_devname == NULL) {
750 fprintf(stderr, "xbee device argument missing\n");
755 if (argc != optind) {
756 printf("Invalid argument\n");
766 int main(int argc, char **argv)
768 struct termios oldterm, term;
771 char xbeerc_path[256];
773 if (parse_args(argc, argv) < 0)
776 /* initializa libevent */
779 /* initialize libxbee */
785 if (joystick_devname != NULL) {
786 if (joystick_init(joystick_devname, &joyinfo) < 0) {
787 fprintf(stderr, "error in joystick init\n");
790 event_set(&joystick_read_event, joyinfo.fd, EV_READ | EV_PERSIST,
791 joystick_input, &joyinfo);
792 event_add(&joystick_read_event, NULL);
795 /* open xbee device */
796 xbee_dev = xbee_open(xbee_devname, xbee_baud);
797 if (xbee_dev == NULL)
800 /* register default channel with a callback */
801 if (xbee_register_channel(xbee_dev, XBEE_DEFAULT_CHANNEL,
802 xbee_rx, NULL) < 0) {
803 fprintf(stderr, "cannot register default channel\n");
807 /* add read event on xbee device */
808 event_set(&xbee_read_event, xbee_dev->fd, EV_READ | EV_PERSIST,
809 evt_xbee_input, xbee_dev);
810 event_add(&xbee_read_event, NULL);
812 /* set terminal in raw mode */
813 tcgetattr(0, &oldterm);
814 memcpy(&term, &oldterm, sizeof(term));
815 term.c_lflag &= ~(ICANON | ECHO | ISIG);
816 tcsetattr(0, TCSANOW, &term);
819 /* parse the config file */
820 homedir = getenv("HOME");
821 if (homedir != NULL) {
822 snprintf(xbeerc_path, sizeof(xbeerc_path), "%s/.xbeerc",
824 if (access(xbeerc_path, R_OK) == 0) {
826 xbee_cl = cmdline_file_new(&main_ctx, "xbeerc> ",
827 "/home/zer0/.xbeerc", 1);
828 if (xbee_cl != NULL) {
829 cmdline_interact(xbee_cl);
830 cmdline_free(xbee_cl);
831 printf("\nconfig file parsed\n");
836 /* create a new cmdline instance */
837 xbee_cl = cmdline_stdin_new(&main_ctx, "xbee> ");
838 if (xbee_cl == NULL) {
843 /* load read event on stdin */
844 event_set(&stdin_read_event, 0, EV_READ | EV_PERSIST,
845 evt_stdin_input, xbee_cl);
846 event_add(&stdin_read_event, NULL);
848 /* libevent main loop */
851 tcsetattr(0, TCSANOW, &oldterm);
855 tcsetattr(0, TCSANOW, &oldterm);