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