52c0d808535e4a3f98af75a5a1b3f0b59a5df43a
[protos/xbee.git] / main.c
1 /*
2  * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
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.
15  *
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.
26  */
27
28 #define _BSD_SOURCE /* for be64toh, endian.h */
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdint.h>
33 #include <inttypes.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <errno.h>
37 #include <termios.h>
38 #include <ctype.h>
39 #include <unistd.h>
40 #include <endian.h>
41 #include <time.h>
42 #include <netinet/in.h>
43 #include <sys/queue.h>
44
45 #include <getopt.h>
46
47 #include <cmdline_rdline.h>
48 #include <cmdline_parse.h>
49 #include <cmdline_socket.h>
50 #include <cmdline.h>
51
52 #include <event.h>
53
54 #include "xbee_neighbor.h"
55 #include "xbee_atcmd.h"
56 #include "xbee_stats.h"
57 #include "xbee_buf.h"
58 #include "xbee_proto.h"
59 #include "xbee.h"
60 #include "main.h"
61
62 #define TIMEOUT_US 1000000
63
64 /* XXX qd on deconnecte, prend 100% du cpu */
65 /* XXX rmt at cmd */
66 /* XXX neighbor discovery */
67 /* XXX dump et restauration de la config */
68
69 /* global xbee device */
70 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
71                                       struct xbee_atresp_hdr *frame,
72                                       unsigned len);
73
74 struct xbee_dev *xbee_dev;
75
76 /* events */
77 static struct event stdin_read_event, xbee_read_event;
78
79 static struct cmdline *xbee_cl;
80
81 /* parameters */
82 static char *xbee_devname = NULL;
83 static int xbee_baud = 9600;
84 int xbee_raw = 0;
85 int xbee_hexdump = 0;
86 int xbee_debug = 0;
87 FILE *xbee_logfile;
88
89 void xbeeapp_log(int always_on_stdout, const char *fmt, ...)
90 {
91         va_list ap, ap2;
92
93         va_start(ap, fmt);
94         va_copy(ap2, ap);
95         if (xbee_logfile != NULL)
96                 vfprintf(xbee_logfile, fmt, ap);
97         if (always_on_stdout || xbee_debug)
98                 vprintf(fmt, ap2);
99         va_end(ap2);
100         va_end(ap);
101 }
102
103 static void hexdump(int always_on_stdout, const char *title, const void *buf,
104                     unsigned int len)
105 {
106         unsigned int i, out, ofs;
107         const unsigned char *data = buf;
108 #define LINE_LEN 80
109         char line[LINE_LEN];    /* space needed 8+16*3+3+16 == 75 */
110         struct timeval tv;
111
112         gettimeofday(&tv, NULL);
113
114         xbeeapp_log(always_on_stdout, "%"PRIi64".%.6d %s at [%p], len=%d\n",
115                     (uint64_t)tv.tv_sec, (int)tv.tv_usec, title, data, len);
116         ofs = 0;
117         while (ofs < len) {
118                 /* format 1 line in the buffer, then use printk to print them */
119                 out = snprintf(line, LINE_LEN, "%08X", ofs);
120                 for (i=0; ofs+i < len && i<16; i++)
121                         out += snprintf(line+out, LINE_LEN - out, " %02X",
122                                         data[ofs+i]&0xff);
123                 for (;i<=16;i++)
124                         out += snprintf(line+out, LINE_LEN - out, "   ");
125                 for (i=0; ofs < len && i<16; i++, ofs++) {
126                         unsigned char c = data[ofs];
127                         if (!isascii(c) || !isprint(c))
128                                 c = '.';
129                         out += snprintf(line+out, LINE_LEN - out, "%c", c);
130                 }
131                 xbeeapp_log(always_on_stdout, "%s\n", line);
132         }
133 }
134
135 static int parse_xmit_status(struct xbee_ctx *ctx,
136                              struct xbee_xmit_status_hdr *frame, unsigned len)
137 {
138         if (ctx == NULL) {
139                 xbeeapp_log(1, "no context\n");
140                 return -1;
141         }
142
143         /* see if it matches a xmit query */
144         if (ctx->type != SEND_MSG) {
145                 xbeeapp_log(ctx->foreground, "invalid response\n");
146                 return -1;
147         }
148
149         /* XXX use defines for these values */
150         if (frame->delivery_status == 0x00)
151                 xbeeapp_log(ctx->foreground, "Success\n");
152         else if (frame->delivery_status == 0x01)
153                 xbeeapp_log(ctx->foreground, "MAC ACK Failure\n");
154         else if (frame->delivery_status == 0x15)
155                 xbeeapp_log(ctx->foreground, "Invalid destination endpoint\n");
156         else if (frame->delivery_status == 0x21)
157                 xbeeapp_log(ctx->foreground, "Network ACK Failure\n");
158         else if (frame->delivery_status == 0x25)
159                 xbeeapp_log(ctx->foreground, "Route Not Found\n");
160
161         return 0;
162 }
163
164 static int atcmd_frame_status(struct xbee_atresp_hdr *frame, unsigned len)
165 {
166         char atcmd_str[3];
167
168         /* get AT command from frame */
169         memcpy(atcmd_str, &frame->cmd, 2);
170         atcmd_str[2] = '\0';
171
172         if (frame->status == 1)
173                 xbeeapp_log(1, "<%s>: Status is error\n", atcmd_str);
174         else if (frame->status == 2)
175                 xbeeapp_log(1, "<%s>: Invalid command\n", atcmd_str);
176         else if (frame->status == 3)
177                 xbeeapp_log(1, "<%s>: Invalid parameter\n", atcmd_str);
178         else if (frame->status != 0)
179                 xbeeapp_log(1, "<%s>: Unknown status error %d\n", atcmd_str,
180                        frame->status);
181         return frame->status;
182 }
183
184 /* assume frame->status is 0 */
185 static int atcmd_frame_to_string(struct xbee_atresp_hdr *frame, unsigned len,
186                                  char *dstbuf, unsigned dstlen)
187 {
188         union {
189                 uint8_t u8;
190                 uint16_t u16;
191                 uint32_t u32;
192                 int16_t s16;
193         } __attribute__((packed)) *result;
194         char atcmd_str[3];
195         struct xbee_atcmd *cmd;
196
197         /* get AT command from frame */
198         memcpy(atcmd_str, &frame->cmd, 2);
199         atcmd_str[2] = '\0';
200
201         /* see if it exists */
202         cmd = xbee_atcmd_lookup_name(atcmd_str);
203         if (cmd == NULL) {
204                 xbeeapp_log(1, "unknown response\n");
205                 return -1;
206         }
207
208         /* dump frame */
209         result = (void *)frame->data;
210         len -= offsetof(struct xbee_atresp_hdr, data);
211         if (cmd->flags & XBEE_ATCMD_F_PARAM_NONE && len == 0)
212                 snprintf(dstbuf, dstlen, "ok");
213         if (cmd->flags & XBEE_ATCMD_F_PARAM_U8 && len == sizeof(uint8_t))
214                 snprintf(dstbuf, dstlen, "0x%x", result->u8);
215         else if (cmd->flags & XBEE_ATCMD_F_PARAM_U16 && len == sizeof(uint16_t))
216                 snprintf(dstbuf, dstlen, "0x%x", ntohs(result->u16));
217         else if (cmd->flags & XBEE_ATCMD_F_PARAM_U32 && len == sizeof(uint32_t))
218                 snprintf(dstbuf, dstlen, "0x%x", ntohl(result->u32));
219         else if (cmd->flags & XBEE_ATCMD_F_PARAM_S16 && len == sizeof(int16_t))
220                 snprintf(dstbuf, dstlen, "%d\n", ntohs(result->s16));
221         else
222                 return -1;
223
224         return 0;
225 }
226
227 static int dump_atcmd(struct xbee_ctx *ctx, struct xbee_atresp_hdr *frame,
228                       unsigned len)
229 {
230         char buf[32];
231
232         /* see if it matches query */
233         if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
234                 printf("invalid response\n");
235                 return -1;
236         }
237
238         /* dump frame */
239         if (atcmd_frame_status(frame, len) == 0) {
240
241                 if (len == sizeof(struct xbee_atresp_hdr))
242                         xbeeapp_log(ctx->foreground, "<%s>: ok\n",
243                                     ctx->atcmd_query->name);
244                 else if (atcmd_frame_to_string(frame, len, buf,
245                                               sizeof(buf)) == 0)
246                         xbeeapp_log(ctx->foreground, "<%s> is %s\n",
247                                     ctx->atcmd_query->name, buf);
248                 else
249                         hexdump(ctx->foreground, "atcmd answer", frame, len);
250         }
251         return 0;
252 }
253
254 void xbee_rx(struct xbee_dev *dev, int channel, int type,
255              void *frame, unsigned len, void *opaque)
256 {
257         struct xbee_ctx *ctx = opaque;
258         int do_hexdump = xbee_hexdump;
259
260         xbeeapp_log(0, "type=0x%x, channel=%d, ctx=%p\n", type, channel, ctx);
261         hexdump(0, "rx", frame, len);
262
263         /* if ctx is !NULL, it is an answer to a query */
264         if (ctx != NULL) {
265                 /* XXX only delete timeout if answer matched */
266                 xbee_unload_timeout(ctx);
267                 if (xbee_debug && ctx->atcmd_query != NULL)
268                         printf("Received answer to query <%s>\n",
269                                ctx->atcmd_query->name);
270                 xbee_unregister_channel(dev, channel);
271         }
272
273         /* some additional checks before sending */
274         switch (type) {
275                 case XBEE_TYPE_MODEM_STATUS: {
276                         printf("Received Modem Status frame\n");
277                         break;
278                 }
279
280                 case XBEE_TYPE_RMT_ATRESP: {
281                         uint64_t addr;
282                         memcpy(&addr, frame, sizeof(addr));
283                         addr = be64toh(addr);
284                         printf("from remote address %"PRIx64"\n", addr);
285
286                         /* this answer contains an atcmd answer at offset 10 */
287                         if (ctx != NULL && ctx->type == ATCMD_RMT) {
288                                 if (dump_atcmd(ctx, frame + 10, len - 10) < 0)
289                                         do_hexdump = 1;
290                         }
291                         else {
292                                 printf("invalid response\n");
293                                 do_hexdump = 1;
294                         }
295                         break;
296                 }
297                 case XBEE_TYPE_ATRESP: {
298                         /* we are currently dumping config, continue */
299                         if (ctx != NULL && ctx->type == DUMP_CONF) {
300                                 xbeeapp_dump_conf_continue(ctx, frame, len);
301                         }
302                         else if (ctx != NULL && ctx->type == ATCMD) {
303                                 if (dump_atcmd(ctx, frame, len) < 0)
304                                         do_hexdump = 1;
305                         }
306                         else {
307                                 printf("invalid response\n");
308                                 do_hexdump = 1;
309                         }
310                         break;
311                 }
312
313                 case XBEE_TYPE_XMIT_STATUS: {
314                         if (parse_xmit_status(ctx, frame, len) < 0)
315                                 do_hexdump = 1;
316                         break;
317                 }
318
319                 case XBEE_TYPE_RECV: {
320                         struct xbee_recv_hdr *recvframe = frame;
321                         int recvlen = len - sizeof(*recvframe);
322                         int on_stdout = 1;
323
324                         /* if we receive a range-test frame, ask for RSSI now */
325                         if (recvlen >= strlen("range") &&
326                             !strncmp((char *)recvframe->data,
327                                      "range", strlen("range"))) {
328                                 xbeeapp_send_atcmd("DB", NULL, 0, 0);
329                                 on_stdout = 0;
330                         }
331                         hexdump(on_stdout, "rx data", recvframe->data,
332                                 recvlen);
333                         break;
334                 }
335
336                 case XBEE_TYPE_ATCMD:
337                 case XBEE_TYPE_ATCMD_Q:
338                 case XBEE_TYPE_XMIT:
339                 case XBEE_TYPE_EXPL_XMIT:
340                 case XBEE_TYPE_RMT_ATCMD:
341                 case XBEE_TYPE_EXPL_RECV:
342                 case XBEE_TYPE_NODE_ID:
343                 default:
344                         xbeeapp_log(0, "Invalid frame\n");
345                         do_hexdump = 1;
346                         break;
347         }
348
349         if (do_hexdump) {
350                 xbeeapp_log(1, "rx frame type=0x%x, channel=%d, ctx=%p\n",
351                             type, channel, ctx);
352                 hexdump(1, "undecoded rx frame", frame, len);
353         }
354
355         /* restart command line if it was a blocking query */
356         if (ctx != NULL) {
357                 if (ctx->foreground && !ctx->have_more_command) {
358                         xbee_stdin_enable();
359                         rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
360                 }
361                 if (!ctx->have_more_command)
362                         free(ctx);
363         }
364 }
365
366 static int xbeeapp_send(struct xbee_ctx *ctx, int type, void *buf, unsigned len,
367                         int foreground)
368 {
369         int ret;
370         int channel;
371
372         if (len > XBEE_MAX_FRAME_LEN) {
373                 printf("frame too large\n");
374                 return -1;
375         }
376
377         /* register a channel */
378         channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY,
379                                         xbee_rx, ctx);
380         if (channel < 0) {
381                 printf("cannot send: no free channel\n");
382                 return -1;
383         }
384
385         xbeeapp_log(0, "send frame ctx=%p channel=%d type=0x%x len=%d\n",
386                ctx, channel, type, len);
387         hexdump(0, "xmit frame", buf, len);
388
389         /* transmit the frame on this channel */
390         ret = xbee_proto_xmit(xbee_dev, channel, type, buf,
391                               len);
392         if (ret < 0) {
393                 printf("cannot send: %s\n", strerror(errno));
394                 xbee_unregister_channel(xbee_dev, channel);
395                 return -1;
396         }
397
398         ctx->channel = channel;
399         xbee_load_timeout(ctx);   /* load a timeout event */
400
401         /* suspend command line until we have answer or timeout */
402         if (foreground) {
403                 ctx->foreground = 1;
404                 rdline_stop(&xbee_cl->rdl); /* don't display prompt when return */
405                 xbee_stdin_disable();  /* unload file descriptor polling */
406         }
407
408         return 0;
409 }
410
411 /* send an AT command with parameters filled by caller. Disable
412  * command line until we get the answer or until a timeout occurs */
413 int xbeeapp_send_atcmd(const char *atcmd_str, void *param, unsigned param_len,
414                        int foreground)
415 {
416         struct xbee_ctx *ctx;
417         struct xbee_atcmd *cmd;
418         struct {
419                 struct xbee_atcmd_hdr atcmd;
420                 char buf[XBEE_MAX_FRAME_LEN];
421         } __attribute__((packed)) frame;
422
423         cmd = xbee_atcmd_lookup_name(atcmd_str);
424         if (cmd == NULL) {
425                 printf("no such at command\n");
426                 return -1;
427         }
428
429         /* allocate memory to store the context for async cb */
430         ctx = malloc(sizeof(*ctx));
431         if (ctx == NULL) {
432                 printf("not enough memory\n");
433                 return -1;
434         }
435
436         memset(ctx, 0, sizeof(*ctx));
437         ctx->type = ATCMD;
438         ctx->atcmd_query = cmd;
439
440         memcpy(&frame.atcmd.cmd, atcmd_str, 2);
441         memcpy(&frame.buf, param, param_len);
442
443         if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &frame,
444                          sizeof(struct xbee_atcmd_hdr) +
445                          param_len, foreground) < 0) {
446                 free(ctx);
447                 return -1;
448         }
449
450         return 0;
451 }
452
453 /* dump config */
454 static int __xbeeapp_dump_config(struct xbee_ctx *ctx, struct xbee_atcmd *cmd)
455 {
456         struct xbee_atcmd_hdr atcmd;
457
458         /* find the first command that is readable and writable */
459         for (; cmd->name != NULL; cmd++) {
460                 if ((cmd->flags & (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE)) ==
461                     (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE))
462                         break;
463         }
464         if (cmd->name == NULL) {
465                 printf("no register to dump\n");
466                 return -1;
467         }
468
469         ctx->atcmd_query = cmd;
470         ctx->have_more_command = 1;
471         memcpy(&atcmd.cmd, cmd->name, 2);
472
473         if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &atcmd,
474                          sizeof(struct xbee_atcmd_hdr), 1) < 0) {
475                 return -1;
476         }
477
478         return 0;
479 }
480
481 /* dump config */
482 int xbeeapp_dump_config(const char *filename)
483 {
484         struct xbee_ctx *ctx;
485         struct xbee_atcmd *cmd;
486         int ret;
487
488         /* find the first command that is readable and writable */
489         cmd = &xbee_atcmd_list[0];
490
491         /* allocate memory to store the context for async cb */
492         ctx = malloc(sizeof(*ctx));
493         if (ctx == NULL) {
494                 printf("not enough memory\n");
495                 return -1;
496         }
497
498         memset(ctx, 0, sizeof(*ctx));
499         ctx->type = DUMP_CONF;
500         ret = __xbeeapp_dump_config(ctx, cmd);
501
502         if (ret < 0)
503                 free(ctx);
504         return ret;
505 }
506
507 /* continue a dump conf */
508 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
509                                       struct xbee_atresp_hdr *frame,
510                                       unsigned len)
511 {
512         struct xbee_atcmd *cmd;
513         char buf[32];
514
515         /* see if it matches query */
516         if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
517                 printf("invalid response\n");
518                 ctx->have_more_command = 0;
519                 return -1;
520         }
521
522         /* dump register content in buf, skip on error */
523         if (atcmd_frame_status(frame, len) == 0 &&
524             atcmd_frame_to_string(frame, len, buf, sizeof(buf)) == 0)
525                 printf("write %s %s\n", ctx->atcmd_query->desc, buf);
526
527         cmd = ctx->atcmd_query + 1;
528         if (__xbeeapp_dump_config(ctx, cmd) < 0) {
529                 ctx->have_more_command = 0;
530                 //close(xbee_config_file);
531                 printf("END\n"); /* XXX */
532         }
533         return 0;
534 }
535
536 /* send data message */
537 int xbeeapp_send_msg(uint64_t addr, void *data, unsigned data_len,
538                      int foreground)
539 {
540         struct xbee_ctx *ctx;
541         struct {
542                 struct xbee_xmit_hdr xmit;
543                 char buf[XBEE_MAX_FRAME_LEN];
544         } __attribute__((packed)) frame;
545
546         /* allocate memory to store the context for async cb */
547         ctx = malloc(sizeof(*ctx));
548         if (ctx == NULL) {
549                 printf("not enough memory\n");
550                 return -1;
551         }
552
553         memset(ctx, 0, sizeof(*ctx));
554         ctx->type = SEND_MSG;
555         ctx->atcmd_query = NULL;
556
557         frame.xmit.dstaddr = htobe64(addr);
558         frame.xmit.reserved = htons(0xFFFE);
559         frame.xmit.bcast_radius = 0;
560         frame.xmit.opts = 0;
561         memcpy(&frame.buf, data, data_len);
562
563         if (xbeeapp_send(ctx, XBEE_TYPE_XMIT, &frame,
564                          sizeof(struct xbee_xmit_hdr) +
565                          data_len, foreground) < 0) {
566                 free(ctx);
567                 return -1;
568         }
569
570         return 0;
571 }
572
573 void xbee_stdin_enable(void)
574 {
575         event_add(&stdin_read_event, NULL);
576 }
577
578 void xbee_stdin_disable(void)
579 {
580         event_del(&stdin_read_event);
581 }
582
583 static void evt_timeout(int s, short event, void *arg)
584 {
585         struct xbee_ctx *ctx = arg;
586
587         xbeeapp_log(0, "Timeout\n");
588
589         /* restart command line */
590         if (ctx->foreground) {
591                 if (xbee_debug == 0)
592                         printf("Timeout\n");
593                 xbee_stdin_enable();
594                 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
595         }
596         /* free event */
597         xbee_unregister_channel(xbee_dev, ctx->channel);
598         free(ctx);
599 }
600
601 void xbee_load_timeout(struct xbee_ctx *ctx)
602 {
603         struct timeval tv;
604
605         tv.tv_sec = 0;
606         tv.tv_usec = TIMEOUT_US;
607         evtimer_set(&ctx->timeout, evt_timeout, ctx);
608         evtimer_add(&ctx->timeout, &tv);
609 }
610
611 void xbee_unload_timeout(struct xbee_ctx *ctx)
612 {
613         evtimer_del(&ctx->timeout);
614 }
615
616 static void
617 evt_stdin_input(int s, short event, void *arg)
618 {
619         char buf[2048];
620         int n;
621
622         n = read(s, buf, sizeof(buf));
623         if (n < 0) {
624                 event_loopbreak();
625                 return;
626         }
627
628         if (xbee_raw) {
629                 int i;
630                 for (i = 0; i < n; i++) {
631                         /* ctrl-d is pressed, back in cmdline mode XXX */
632                         if (buf[i] == 4) {
633                                 xbee_raw = 0;
634                                 n = i;
635                                 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
636                                 break;
637                         }
638                 }
639                 /* XXX bad hack */
640                 if (buf[0] == '\n')
641                         write(xbee_dev->fd, "\r\n", 2);
642                 else
643                         write(xbee_dev->fd, buf, n);
644                 write(s, buf, n);
645
646         }
647         else {
648                 if (cmdline_in(xbee_cl, buf, n) < 0) {
649                         event_loopbreak();
650                         return;
651                 }
652         }
653 }
654
655 static void
656 evt_xbee_input(int s, short event, void *arg)
657 {
658         if (xbee_raw) {
659                 char buf[2048];
660                 int n;
661
662                 n = read(s, buf, sizeof(buf));
663                 if (n < 0)
664                         return;
665
666                 write(1, buf, n);
667         }
668         else {
669                 xbee_read(xbee_dev);
670                 xbee_process_queue(xbee_dev);
671         }
672 }
673
674 static void
675 usage(const char *prgname)
676 {
677         printf("%s [-s BAUD] -d DEVNAME\n"
678                "  -s BAUD:      set baud rate of xbee device\n"
679                "  -d DEVNAME:   xbee char device\n"
680                "  -r:           start in raw mode\n"
681                "\n",
682                prgname);
683 }
684
685 /* Parse the argument given in the command line of the application */
686 static int
687 parse_args(int argc, char **argv)
688 {
689         int opt, ret;
690         char **argvopt;
691         int option_index;
692         char *prgname = argv[0];
693         static struct option lgopts[] = {
694                 {0, 0, 0, 0}
695         };
696
697         argvopt = argv;
698
699         while ((opt = getopt_long(argc, argvopt, "hd:s:",
700                                   lgopts, &option_index)) != EOF) {
701
702                 switch (opt) {
703
704                 case 'h':
705                         usage(prgname);
706                         exit(0);
707                         break;
708
709                 case 'd':
710                         xbee_devname = strdup(optarg);
711                         break;
712
713                 case 's':
714                         xbee_baud = atoi(optarg);
715                         break;
716
717                 case 'r':
718                         xbee_raw = 1;
719                         break;
720
721                 /* long options */
722                 case 0:
723                         /* if (!strcmp(lgopts[option_index].name, "option")) */
724                         break;
725
726                 default:
727                         usage(prgname);
728                         return -1;
729                 }
730         }
731
732         if (xbee_devname == NULL) {
733                 fprintf(stderr, "xbee device argument missing\n");
734                 usage(prgname);
735                 return -1;
736         }
737
738         if (argc != optind) {
739                 printf("Invalid argument\n");
740                 usage(prgname);
741                 return -1;
742         }
743
744         ret = optind-1;
745
746         return ret;
747 }
748
749 int main(int argc, char **argv)
750 {
751         struct termios oldterm, term;
752         int err = 0;
753         const char *homedir;
754         char xbeerc_path[256];
755
756         if (parse_args(argc, argv) < 0)
757                 exit(1);
758
759         /* initializa libevent */
760         event_init();
761
762         /* initialize libxbee */
763         err = xbee_init();
764         if (err < 0)
765                 return -1;
766
767         /* open xbee device */
768         xbee_dev = xbee_open(xbee_devname, xbee_baud);
769         if (xbee_dev == NULL)
770                 return -1;
771
772         /* register default channel with a callback */
773         if (xbee_register_channel(xbee_dev, XBEE_DEFAULT_CHANNEL,
774                                   xbee_rx, NULL) < 0) {
775                 fprintf(stderr, "cannot register default channel\n");
776                 return -1;
777         }
778
779         /* add read event on xbee device */
780         event_set(&xbee_read_event, xbee_dev->fd, EV_READ | EV_PERSIST,
781                   evt_xbee_input, xbee_dev);
782         event_add(&xbee_read_event, NULL);
783
784         /* set terminal in raw mode */
785         tcgetattr(0, &oldterm);
786         memcpy(&term, &oldterm, sizeof(term));
787         term.c_lflag &= ~(ICANON | ECHO | ISIG);
788         tcsetattr(0, TCSANOW, &term);
789         setbuf(stdin, NULL);
790
791         /* parse the config file */
792         homedir = getenv("HOME");
793         if (homedir != NULL) {
794                 snprintf(xbeerc_path, sizeof(xbeerc_path), "%s/.xbeerc",
795                          homedir);
796                 if (access(xbeerc_path, R_OK) == 0) {
797
798                         xbee_cl = cmdline_file_new(&main_ctx, "xbeerc> ",
799                                               "/home/zer0/.xbeerc", 1);
800                         if (xbee_cl != NULL) {
801                                 cmdline_interact(xbee_cl);
802                                 cmdline_free(xbee_cl);
803                                 printf("\nconfig file parsed\n");
804                         }
805                 }
806         }
807
808         /* create a new cmdline instance */
809         xbee_cl = cmdline_stdin_new(&main_ctx, "xbee> ");
810         if (xbee_cl == NULL) {
811                 err = 1;
812                 goto fail;
813         }
814
815         /* load read event on stdin */
816         event_set(&stdin_read_event, 0, EV_READ | EV_PERSIST,
817                   evt_stdin_input, xbee_cl);
818         event_add(&stdin_read_event, NULL);
819
820         /* libevent main loop */
821         event_dispatch();
822
823         tcsetattr(0, TCSANOW, &oldterm);
824         return 0;
825
826  fail:
827         tcsetattr(0, TCSANOW, &oldterm);
828         return err;
829 }