xbee_atcmd: add atcmd callback on response
[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 #include <linux/joystick.h>
45
46 #include <getopt.h>
47
48 #include <cmdline_rdline.h>
49 #include <cmdline_parse.h>
50 #include <cmdline_socket.h>
51 #include <cmdline.h>
52
53 #include <event.h>
54
55 #include "xbee_neighbor.h"
56 #include "xbee_atcmd.h"
57 #include "xbee_stats.h"
58 #include "xbee_buf.h"
59 #include "xbee_proto.h"
60 #include "xbee.h"
61 #include "joystick.h"
62 #include "main.h"
63
64 #define TIMEOUT_US 1000000
65
66 /* XXX qd on deconnecte, prend 100% du cpu */
67 /* XXX rmt at cmd */
68 /* XXX neighbor discovery */
69 /* XXX dump et restauration de la config */
70
71 /* global xbee device */
72 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
73                                       struct xbee_atresp_hdr *frame,
74                                       unsigned len);
75
76 struct xbee_dev *xbee_dev;
77
78 /* events */
79 static struct event stdin_read_event, xbee_read_event;
80 static struct event joystick_read_event;
81
82 static struct cmdline *xbee_cl;
83
84 /* parameters */
85 static char *xbee_devname = NULL;
86 static int xbee_baud = 9600;
87 int xbee_raw = 0;
88 int xbee_hexdump = 0;
89 int xbee_debug = 0;
90 FILE *xbee_logfile;
91 static char *joystick_devname = NULL;
92 struct joystick_info joyinfo;
93
94 void xbeeapp_log(int always_on_stdout, const char *fmt, ...)
95 {
96         va_list ap, ap2;
97
98         va_start(ap, fmt);
99         va_copy(ap2, ap);
100         if (xbee_logfile != NULL)
101                 vfprintf(xbee_logfile, fmt, ap);
102         if (always_on_stdout || xbee_debug)
103                 vprintf(fmt, ap2);
104         va_end(ap2);
105         va_end(ap);
106 }
107
108 static void hexdump(int always_on_stdout, const char *title, const void *buf,
109                     unsigned int len)
110 {
111         unsigned int i, out, ofs;
112         const unsigned char *data = buf;
113 #define LINE_LEN 80
114         char line[LINE_LEN];    /* space needed 8+16*3+3+16 == 75 */
115         struct timeval tv;
116
117         gettimeofday(&tv, NULL);
118
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);
121         ofs = 0;
122         while (ofs < 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",
127                                         data[ofs+i]&0xff);
128                 for (;i<=16;i++)
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))
133                                 c = '.';
134                         out += snprintf(line+out, LINE_LEN - out, "%c", c);
135                 }
136                 xbeeapp_log(always_on_stdout, "%s\n", line);
137         }
138 }
139
140 static int parse_xmit_status(struct xbee_ctx *ctx,
141                              struct xbee_xmit_status_hdr *frame, unsigned len)
142 {
143         if (ctx == NULL) {
144                 xbeeapp_log(1, "no context\n");
145                 return -1;
146         }
147
148         /* see if it matches a xmit query */
149         if (ctx->type != SEND_MSG) {
150                 xbeeapp_log(ctx->foreground, "invalid response\n");
151                 return -1;
152         }
153
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");
165
166         return 0;
167 }
168
169 static int atcmd_frame_status(struct xbee_atresp_hdr *frame, unsigned len)
170 {
171         char atcmd_str[3];
172
173         /* get AT command from frame */
174         memcpy(atcmd_str, &frame->cmd, 2);
175         atcmd_str[2] = '\0';
176
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,
185                        frame->status);
186         return frame->status;
187 }
188
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)
192 {
193         union {
194                 uint8_t u8;
195                 uint16_t u16;
196                 uint32_t u32;
197                 int16_t s16;
198         } __attribute__((packed)) *result;
199         char atcmd_str[3];
200         struct xbee_atcmd *cmd;
201
202         /* get AT command from frame */
203         memcpy(atcmd_str, &frame->cmd, 2);
204         atcmd_str[2] = '\0';
205
206         /* see if it exists */
207         cmd = xbee_atcmd_lookup_name(atcmd_str);
208         if (cmd == NULL) {
209                 xbeeapp_log(1, "unknown response\n");
210                 return -1;
211         }
212
213         /* dump frame */
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));
226         else
227                 return -1;
228
229         return 0;
230 }
231
232 static int dump_atcmd(struct xbee_ctx *ctx, struct xbee_atresp_hdr *frame,
233                       unsigned len)
234 {
235         char buf[32];
236
237         /* see if it matches query */
238         if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
239                 printf("invalid response\n");
240                 return -1;
241         }
242
243         /* dump frame */
244         if (atcmd_frame_status(frame, len) == 0) {
245
246                 /* callback */
247                 if (ctx->func != NULL)
248                         ctx->func(frame, len, ctx->arg);
249
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,
254                                               sizeof(buf)) == 0)
255                         xbeeapp_log(ctx->foreground, "<%s> is %s\n",
256                                     ctx->atcmd_query->name, buf);
257                 else
258                         hexdump(ctx->foreground, "atcmd answer", frame, len);
259         }
260         return 0;
261 }
262
263 void xbee_rx(struct xbee_dev *dev, int channel, int type,
264              void *frame, unsigned len, void *opaque)
265 {
266         struct xbee_ctx *ctx = opaque;
267         int do_hexdump = xbee_hexdump;
268
269         xbeeapp_log(0, "type=0x%x, channel=%d, ctx=%p\n", type, channel, ctx);
270         hexdump(0, "rx", frame, len);
271
272         /* if ctx is !NULL, it is an answer to a query */
273         if (ctx != NULL) {
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);
280         }
281
282         /* some additional checks before sending */
283         switch (type) {
284                 case XBEE_TYPE_MODEM_STATUS: {
285                         printf("Received Modem Status frame\n");
286                         break;
287                 }
288
289                 case XBEE_TYPE_RMT_ATRESP: {
290                         uint64_t addr;
291                         memcpy(&addr, frame, sizeof(addr));
292                         addr = be64toh(addr);
293                         printf("from remote address %"PRIx64"\n", addr);
294
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)
298                                         do_hexdump = 1;
299                         }
300                         else {
301                                 printf("invalid response\n");
302                                 do_hexdump = 1;
303                         }
304                         break;
305                 }
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);
310                         }
311                         else if (ctx != NULL && ctx->type == ATCMD) {
312                                 if (dump_atcmd(ctx, frame, len) < 0)
313                                         do_hexdump = 1;
314                         }
315                         else {
316                                 printf("invalid response\n");
317                                 do_hexdump = 1;
318                         }
319                         break;
320                 }
321
322                 case XBEE_TYPE_XMIT_STATUS: {
323                         if (parse_xmit_status(ctx, frame, len) < 0)
324                                 do_hexdump = 1;
325                         break;
326                 }
327
328                 case XBEE_TYPE_RECV: {
329                         struct xbee_recv_hdr *recvframe = frame;
330                         int recvlen = len - sizeof(*recvframe);
331                         int on_stdout = 1;
332
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);
338                                 on_stdout = 0;
339                         }
340                         hexdump(on_stdout, "rx data", recvframe->data,
341                                 recvlen);
342                         break;
343                 }
344
345                 case XBEE_TYPE_ATCMD:
346                 case XBEE_TYPE_ATCMD_Q:
347                 case XBEE_TYPE_XMIT:
348                 case XBEE_TYPE_EXPL_XMIT:
349                 case XBEE_TYPE_RMT_ATCMD:
350                 case XBEE_TYPE_EXPL_RECV:
351                 case XBEE_TYPE_NODE_ID:
352                 default:
353                         xbeeapp_log(0, "Invalid frame\n");
354                         do_hexdump = 1;
355                         break;
356         }
357
358         if (do_hexdump) {
359                 xbeeapp_log(1, "rx frame type=0x%x, channel=%d, ctx=%p\n",
360                             type, channel, ctx);
361                 hexdump(1, "undecoded rx frame", frame, len);
362         }
363
364         /* restart command line if it was a blocking query */
365         if (ctx != NULL) {
366                 if (ctx->foreground && !ctx->have_more_command) {
367                         xbee_stdin_enable();
368                         rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
369                 }
370                 if (!ctx->have_more_command)
371                         free(ctx);
372         }
373 }
374
375 static int xbeeapp_send(struct xbee_ctx *ctx, int type, void *buf, unsigned len,
376                         int foreground)
377 {
378         int ret;
379         int channel;
380
381         if (len > XBEE_MAX_FRAME_LEN) {
382                 printf("frame too large\n");
383                 return -1;
384         }
385
386         /* register a channel */
387         channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY,
388                                         xbee_rx, ctx);
389         if (channel < 0) {
390                 printf("cannot send: no free channel\n");
391                 return -1;
392         }
393
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);
397
398         /* transmit the frame on this channel */
399         ret = xbee_proto_xmit(xbee_dev, channel, type, buf,
400                               len);
401         if (ret < 0) {
402                 printf("cannot send: %s\n", strerror(errno));
403                 xbee_unregister_channel(xbee_dev, channel);
404                 return -1;
405         }
406
407         ctx->channel = channel;
408         xbee_load_timeout(ctx);   /* load a timeout event */
409
410         /* suspend command line until we have answer or timeout */
411         if (foreground) {
412                 ctx->foreground = 1;
413                 rdline_stop(&xbee_cl->rdl); /* don't display prompt when return */
414                 xbee_stdin_disable();  /* unload file descriptor polling */
415         }
416
417         return 0;
418 }
419
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),
425                        void *arg)
426 {
427         struct xbee_ctx *ctx;
428         struct xbee_atcmd *cmd;
429         struct {
430                 struct xbee_atcmd_hdr atcmd;
431                 char buf[XBEE_MAX_FRAME_LEN];
432         } __attribute__((packed)) frame;
433
434         cmd = xbee_atcmd_lookup_name(atcmd_str);
435         if (cmd == NULL) {
436                 printf("no such at command\n");
437                 return -1;
438         }
439
440         /* allocate memory to store the context for async cb */
441         ctx = malloc(sizeof(*ctx));
442         if (ctx == NULL) {
443                 printf("not enough memory\n");
444                 return -1;
445         }
446
447         memset(ctx, 0, sizeof(*ctx));
448         ctx->type = ATCMD;
449         ctx->atcmd_query = cmd;
450         ctx->func = func;
451         ctx->arg = arg;
452
453         memcpy(&frame.atcmd.cmd, atcmd_str, 2);
454         memcpy(&frame.buf, param, param_len);
455
456         if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &frame,
457                          sizeof(struct xbee_atcmd_hdr) +
458                          param_len, foreground) < 0) {
459                 free(ctx);
460                 return -1;
461         }
462
463         return 0;
464 }
465
466 /* dump config */
467 static int __xbeeapp_dump_config(struct xbee_ctx *ctx, struct xbee_atcmd *cmd)
468 {
469         struct xbee_atcmd_hdr atcmd;
470
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))
475                         break;
476         }
477         if (cmd->name == NULL) {
478                 printf("no register to dump\n");
479                 return -1;
480         }
481
482         ctx->atcmd_query = cmd;
483         ctx->have_more_command = 1;
484         memcpy(&atcmd.cmd, cmd->name, 2);
485
486         if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &atcmd,
487                          sizeof(struct xbee_atcmd_hdr), 1) < 0) {
488                 return -1;
489         }
490
491         return 0;
492 }
493
494 /* dump config */
495 int xbeeapp_dump_config(const char *filename)
496 {
497         struct xbee_ctx *ctx;
498         struct xbee_atcmd *cmd;
499         int ret;
500
501         /* find the first command that is readable and writable */
502         cmd = &xbee_atcmd_list[0];
503
504         /* allocate memory to store the context for async cb */
505         ctx = malloc(sizeof(*ctx));
506         if (ctx == NULL) {
507                 printf("not enough memory\n");
508                 return -1;
509         }
510
511         memset(ctx, 0, sizeof(*ctx));
512         ctx->type = DUMP_CONF;
513         ret = __xbeeapp_dump_config(ctx, cmd);
514
515         if (ret < 0)
516                 free(ctx);
517         return ret;
518 }
519
520 /* continue a dump conf */
521 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
522                                       struct xbee_atresp_hdr *frame,
523                                       unsigned len)
524 {
525         struct xbee_atcmd *cmd;
526         char buf[32];
527
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;
532                 return -1;
533         }
534
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);
539
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 */
545         }
546         return 0;
547 }
548
549 /* send data message */
550 int xbeeapp_send_msg(uint64_t addr, void *data, unsigned data_len,
551                      int foreground)
552 {
553         struct xbee_ctx *ctx;
554         struct {
555                 struct xbee_xmit_hdr xmit;
556                 char buf[XBEE_MAX_FRAME_LEN];
557         } __attribute__((packed)) frame;
558
559         /* allocate memory to store the context for async cb */
560         ctx = malloc(sizeof(*ctx));
561         if (ctx == NULL) {
562                 printf("not enough memory\n");
563                 return -1;
564         }
565
566         memset(ctx, 0, sizeof(*ctx));
567         ctx->type = SEND_MSG;
568         ctx->atcmd_query = NULL;
569
570         frame.xmit.dstaddr = htobe64(addr);
571         frame.xmit.reserved = htons(0xFFFE);
572         frame.xmit.bcast_radius = 0;
573         frame.xmit.opts = 0;
574         memcpy(&frame.buf, data, data_len);
575
576         if (xbeeapp_send(ctx, XBEE_TYPE_XMIT, &frame,
577                          sizeof(struct xbee_xmit_hdr) +
578                          data_len, foreground) < 0) {
579                 free(ctx);
580                 return -1;
581         }
582
583         return 0;
584 }
585
586 void xbee_stdin_enable(void)
587 {
588         event_add(&stdin_read_event, NULL);
589 }
590
591 void xbee_stdin_disable(void)
592 {
593         event_del(&stdin_read_event);
594 }
595
596 static void evt_timeout(int s, short event, void *arg)
597 {
598         struct xbee_ctx *ctx = arg;
599
600         xbeeapp_log(0, "Timeout\n");
601
602         /* restart command line */
603         if (ctx->foreground) {
604                 if (xbee_debug == 0)
605                         printf("Timeout\n");
606                 xbee_stdin_enable();
607                 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
608         }
609         /* free event */
610         xbee_unregister_channel(xbee_dev, ctx->channel);
611         free(ctx);
612 }
613
614 void xbee_load_timeout(struct xbee_ctx *ctx)
615 {
616         struct timeval tv;
617
618         tv.tv_sec = 0;
619         tv.tv_usec = TIMEOUT_US;
620         evtimer_set(&ctx->timeout, evt_timeout, ctx);
621         evtimer_add(&ctx->timeout, &tv);
622 }
623
624 void xbee_unload_timeout(struct xbee_ctx *ctx)
625 {
626         evtimer_del(&ctx->timeout);
627 }
628
629 static void
630 evt_stdin_input(int s, short event, void *arg)
631 {
632         char buf[2048];
633         int n;
634
635         n = read(s, buf, sizeof(buf));
636         if (n < 0) {
637                 event_loopbreak();
638                 return;
639         }
640
641         if (xbee_raw) {
642                 int i;
643                 for (i = 0; i < n; i++) {
644                         /* ctrl-d is pressed, back in cmdline mode XXX */
645                         if (buf[i] == 4) {
646                                 xbee_raw = 0;
647                                 n = i;
648                                 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
649                                 break;
650                         }
651                 }
652                 /* XXX bad hack */
653                 if (buf[0] == '\n')
654                         write(xbee_dev->fd, "\r\n", 2);
655                 else
656                         write(xbee_dev->fd, buf, n);
657                 write(s, buf, n);
658
659         }
660         else {
661                 if (cmdline_in(xbee_cl, buf, n) < 0) {
662                         event_loopbreak();
663                         return;
664                 }
665         }
666 }
667
668 static void
669 evt_xbee_input(int s, short event, void *arg)
670 {
671         if (xbee_raw) {
672                 char buf[2048];
673                 int n;
674
675                 n = read(s, buf, sizeof(buf));
676                 if (n < 0)
677                         return;
678
679                 write(1, buf, n);
680         }
681         else {
682                 xbee_read(xbee_dev);
683                 xbee_process_queue(xbee_dev);
684         }
685 }
686
687 static void
688 usage(const char *prgname)
689 {
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"
694                "\n",
695                prgname);
696 }
697
698 /* Parse the argument given in the command line of the application */
699 static int
700 parse_args(int argc, char **argv)
701 {
702         int opt, ret;
703         char **argvopt;
704         int option_index;
705         char *prgname = argv[0];
706         static struct option lgopts[] = {
707                 {0, 0, 0, 0}
708         };
709
710         argvopt = argv;
711
712         while ((opt = getopt_long(argc, argvopt, "hd:s:j:",
713                                   lgopts, &option_index)) != EOF) {
714
715                 switch (opt) {
716
717                 case 'h':
718                         usage(prgname);
719                         exit(0);
720                         break;
721
722                 case 'd':
723                         xbee_devname = strdup(optarg);
724                         break;
725
726                 case 's':
727                         xbee_baud = atoi(optarg);
728                         break;
729
730                 case 'r':
731                         xbee_raw = 1;
732                         break;
733
734                 case 'j':
735                         joystick_devname = optarg;
736                         break;
737
738                 /* long options */
739                 case 0:
740                         /* if (!strcmp(lgopts[option_index].name, "option")) */
741                         break;
742
743                 default:
744                         usage(prgname);
745                         return -1;
746                 }
747         }
748
749         if (xbee_devname == NULL) {
750                 fprintf(stderr, "xbee device argument missing\n");
751                 usage(prgname);
752                 return -1;
753         }
754
755         if (argc != optind) {
756                 printf("Invalid argument\n");
757                 usage(prgname);
758                 return -1;
759         }
760
761         ret = optind-1;
762
763         return ret;
764 }
765
766 int main(int argc, char **argv)
767 {
768         struct termios oldterm, term;
769         int err = 0;
770         const char *homedir;
771         char xbeerc_path[256];
772
773         if (parse_args(argc, argv) < 0)
774                 exit(1);
775
776         /* initializa libevent */
777         event_init();
778
779         /* initialize libxbee */
780         err = xbee_init();
781         if (err < 0)
782                 return -1;
783
784         /* init joystick */
785         if (joystick_devname != NULL) {
786                 if (joystick_init(joystick_devname, &joyinfo) < 0) {
787                         fprintf(stderr, "error in joystick init\n");
788                         return -1;
789                 }
790                 event_set(&joystick_read_event, joyinfo.fd, EV_READ | EV_PERSIST,
791                           joystick_input, &joyinfo);
792                 event_add(&joystick_read_event, NULL);
793         }
794
795         /* open xbee device */
796         xbee_dev = xbee_open(xbee_devname, xbee_baud);
797         if (xbee_dev == NULL)
798                 return -1;
799
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");
804                 return -1;
805         }
806
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);
811
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);
817         setbuf(stdin, NULL);
818
819         /* parse the config file */
820         homedir = getenv("HOME");
821         if (homedir != NULL) {
822                 snprintf(xbeerc_path, sizeof(xbeerc_path), "%s/.xbeerc",
823                          homedir);
824                 if (access(xbeerc_path, R_OK) == 0) {
825
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");
832                         }
833                 }
834         }
835
836         /* create a new cmdline instance */
837         xbee_cl = cmdline_stdin_new(&main_ctx, "xbee> ");
838         if (xbee_cl == NULL) {
839                 err = 1;
840                 goto fail;
841         }
842
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);
847
848         /* libevent main loop */
849         event_dispatch();
850
851         tcsetattr(0, TCSANOW, &oldterm);
852         return 0;
853
854  fail:
855         tcsetattr(0, TCSANOW, &oldterm);
856         return err;
857 }