joystick: axis & button are 16 bit
[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                 if (len == sizeof(struct xbee_atresp_hdr))
247                         xbeeapp_log(ctx->foreground, "<%s>: ok\n",
248                                     ctx->atcmd_query->name);
249                 else if (atcmd_frame_to_string(frame, len, buf,
250                                               sizeof(buf)) == 0)
251                         xbeeapp_log(ctx->foreground, "<%s> is %s\n",
252                                     ctx->atcmd_query->name, buf);
253                 else
254                         hexdump(ctx->foreground, "atcmd answer", frame, len);
255         }
256         return 0;
257 }
258
259 void xbee_rx(struct xbee_dev *dev, int channel, int type,
260              void *frame, unsigned len, void *opaque)
261 {
262         struct xbee_ctx *ctx = opaque;
263         int do_hexdump = xbee_hexdump;
264
265         xbeeapp_log(0, "type=0x%x, channel=%d, ctx=%p\n", type, channel, ctx);
266         hexdump(0, "rx", frame, len);
267
268         /* if ctx is !NULL, it is an answer to a query */
269         if (ctx != NULL) {
270                 /* XXX only delete timeout if answer matched */
271                 xbee_unload_timeout(ctx);
272                 if (xbee_debug && ctx->atcmd_query != NULL)
273                         printf("Received answer to query <%s>\n",
274                                ctx->atcmd_query->name);
275                 xbee_unregister_channel(dev, channel);
276         }
277
278         /* some additional checks before sending */
279         switch (type) {
280                 case XBEE_TYPE_MODEM_STATUS: {
281                         printf("Received Modem Status frame\n");
282                         break;
283                 }
284
285                 case XBEE_TYPE_RMT_ATRESP: {
286                         uint64_t addr;
287                         memcpy(&addr, frame, sizeof(addr));
288                         addr = be64toh(addr);
289                         printf("from remote address %"PRIx64"\n", addr);
290
291                         /* this answer contains an atcmd answer at offset 10 */
292                         if (ctx != NULL && ctx->type == ATCMD_RMT) {
293                                 if (dump_atcmd(ctx, frame + 10, len - 10) < 0)
294                                         do_hexdump = 1;
295                         }
296                         else {
297                                 printf("invalid response\n");
298                                 do_hexdump = 1;
299                         }
300                         break;
301                 }
302                 case XBEE_TYPE_ATRESP: {
303                         /* we are currently dumping config, continue */
304                         if (ctx != NULL && ctx->type == DUMP_CONF) {
305                                 xbeeapp_dump_conf_continue(ctx, frame, len);
306                         }
307                         else if (ctx != NULL && ctx->type == ATCMD) {
308                                 if (dump_atcmd(ctx, frame, len) < 0)
309                                         do_hexdump = 1;
310                         }
311                         else {
312                                 printf("invalid response\n");
313                                 do_hexdump = 1;
314                         }
315                         break;
316                 }
317
318                 case XBEE_TYPE_XMIT_STATUS: {
319                         if (parse_xmit_status(ctx, frame, len) < 0)
320                                 do_hexdump = 1;
321                         break;
322                 }
323
324                 case XBEE_TYPE_RECV: {
325                         struct xbee_recv_hdr *recvframe = frame;
326                         int recvlen = len - sizeof(*recvframe);
327                         int on_stdout = 1;
328
329                         /* if we receive a range-test frame, ask for RSSI now */
330                         if (recvlen >= strlen("range") &&
331                             !strncmp((char *)recvframe->data,
332                                      "range", strlen("range"))) {
333                                 xbeeapp_send_atcmd("DB", NULL, 0, 0);
334                                 on_stdout = 0;
335                         }
336                         hexdump(on_stdout, "rx data", recvframe->data,
337                                 recvlen);
338                         break;
339                 }
340
341                 case XBEE_TYPE_ATCMD:
342                 case XBEE_TYPE_ATCMD_Q:
343                 case XBEE_TYPE_XMIT:
344                 case XBEE_TYPE_EXPL_XMIT:
345                 case XBEE_TYPE_RMT_ATCMD:
346                 case XBEE_TYPE_EXPL_RECV:
347                 case XBEE_TYPE_NODE_ID:
348                 default:
349                         xbeeapp_log(0, "Invalid frame\n");
350                         do_hexdump = 1;
351                         break;
352         }
353
354         if (do_hexdump) {
355                 xbeeapp_log(1, "rx frame type=0x%x, channel=%d, ctx=%p\n",
356                             type, channel, ctx);
357                 hexdump(1, "undecoded rx frame", frame, len);
358         }
359
360         /* restart command line if it was a blocking query */
361         if (ctx != NULL) {
362                 if (ctx->foreground && !ctx->have_more_command) {
363                         xbee_stdin_enable();
364                         rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
365                 }
366                 if (!ctx->have_more_command)
367                         free(ctx);
368         }
369 }
370
371 static int xbeeapp_send(struct xbee_ctx *ctx, int type, void *buf, unsigned len,
372                         int foreground)
373 {
374         int ret;
375         int channel;
376
377         if (len > XBEE_MAX_FRAME_LEN) {
378                 printf("frame too large\n");
379                 return -1;
380         }
381
382         /* register a channel */
383         channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY,
384                                         xbee_rx, ctx);
385         if (channel < 0) {
386                 printf("cannot send: no free channel\n");
387                 return -1;
388         }
389
390         xbeeapp_log(0, "send frame ctx=%p channel=%d type=0x%x len=%d\n",
391                ctx, channel, type, len);
392         hexdump(0, "xmit frame", buf, len);
393
394         /* transmit the frame on this channel */
395         ret = xbee_proto_xmit(xbee_dev, channel, type, buf,
396                               len);
397         if (ret < 0) {
398                 printf("cannot send: %s\n", strerror(errno));
399                 xbee_unregister_channel(xbee_dev, channel);
400                 return -1;
401         }
402
403         ctx->channel = channel;
404         xbee_load_timeout(ctx);   /* load a timeout event */
405
406         /* suspend command line until we have answer or timeout */
407         if (foreground) {
408                 ctx->foreground = 1;
409                 rdline_stop(&xbee_cl->rdl); /* don't display prompt when return */
410                 xbee_stdin_disable();  /* unload file descriptor polling */
411         }
412
413         return 0;
414 }
415
416 /* send an AT command with parameters filled by caller. Disable
417  * command line until we get the answer or until a timeout occurs */
418 int xbeeapp_send_atcmd(const char *atcmd_str, void *param, unsigned param_len,
419                        int foreground)
420 {
421         struct xbee_ctx *ctx;
422         struct xbee_atcmd *cmd;
423         struct {
424                 struct xbee_atcmd_hdr atcmd;
425                 char buf[XBEE_MAX_FRAME_LEN];
426         } __attribute__((packed)) frame;
427
428         cmd = xbee_atcmd_lookup_name(atcmd_str);
429         if (cmd == NULL) {
430                 printf("no such at command\n");
431                 return -1;
432         }
433
434         /* allocate memory to store the context for async cb */
435         ctx = malloc(sizeof(*ctx));
436         if (ctx == NULL) {
437                 printf("not enough memory\n");
438                 return -1;
439         }
440
441         memset(ctx, 0, sizeof(*ctx));
442         ctx->type = ATCMD;
443         ctx->atcmd_query = cmd;
444
445         memcpy(&frame.atcmd.cmd, atcmd_str, 2);
446         memcpy(&frame.buf, param, param_len);
447
448         if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &frame,
449                          sizeof(struct xbee_atcmd_hdr) +
450                          param_len, foreground) < 0) {
451                 free(ctx);
452                 return -1;
453         }
454
455         return 0;
456 }
457
458 /* dump config */
459 static int __xbeeapp_dump_config(struct xbee_ctx *ctx, struct xbee_atcmd *cmd)
460 {
461         struct xbee_atcmd_hdr atcmd;
462
463         /* find the first command that is readable and writable */
464         for (; cmd->name != NULL; cmd++) {
465                 if ((cmd->flags & (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE)) ==
466                     (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE))
467                         break;
468         }
469         if (cmd->name == NULL) {
470                 printf("no register to dump\n");
471                 return -1;
472         }
473
474         ctx->atcmd_query = cmd;
475         ctx->have_more_command = 1;
476         memcpy(&atcmd.cmd, cmd->name, 2);
477
478         if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &atcmd,
479                          sizeof(struct xbee_atcmd_hdr), 1) < 0) {
480                 return -1;
481         }
482
483         return 0;
484 }
485
486 /* dump config */
487 int xbeeapp_dump_config(const char *filename)
488 {
489         struct xbee_ctx *ctx;
490         struct xbee_atcmd *cmd;
491         int ret;
492
493         /* find the first command that is readable and writable */
494         cmd = &xbee_atcmd_list[0];
495
496         /* allocate memory to store the context for async cb */
497         ctx = malloc(sizeof(*ctx));
498         if (ctx == NULL) {
499                 printf("not enough memory\n");
500                 return -1;
501         }
502
503         memset(ctx, 0, sizeof(*ctx));
504         ctx->type = DUMP_CONF;
505         ret = __xbeeapp_dump_config(ctx, cmd);
506
507         if (ret < 0)
508                 free(ctx);
509         return ret;
510 }
511
512 /* continue a dump conf */
513 static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
514                                       struct xbee_atresp_hdr *frame,
515                                       unsigned len)
516 {
517         struct xbee_atcmd *cmd;
518         char buf[32];
519
520         /* see if it matches query */
521         if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
522                 printf("invalid response\n");
523                 ctx->have_more_command = 0;
524                 return -1;
525         }
526
527         /* dump register content in buf, skip on error */
528         if (atcmd_frame_status(frame, len) == 0 &&
529             atcmd_frame_to_string(frame, len, buf, sizeof(buf)) == 0)
530                 printf("write %s %s\n", ctx->atcmd_query->desc, buf);
531
532         cmd = ctx->atcmd_query + 1;
533         if (__xbeeapp_dump_config(ctx, cmd) < 0) {
534                 ctx->have_more_command = 0;
535                 //close(xbee_config_file);
536                 printf("END\n"); /* XXX */
537         }
538         return 0;
539 }
540
541 /* send data message */
542 int xbeeapp_send_msg(uint64_t addr, void *data, unsigned data_len,
543                      int foreground)
544 {
545         struct xbee_ctx *ctx;
546         struct {
547                 struct xbee_xmit_hdr xmit;
548                 char buf[XBEE_MAX_FRAME_LEN];
549         } __attribute__((packed)) frame;
550
551         /* allocate memory to store the context for async cb */
552         ctx = malloc(sizeof(*ctx));
553         if (ctx == NULL) {
554                 printf("not enough memory\n");
555                 return -1;
556         }
557
558         memset(ctx, 0, sizeof(*ctx));
559         ctx->type = SEND_MSG;
560         ctx->atcmd_query = NULL;
561
562         frame.xmit.dstaddr = htobe64(addr);
563         frame.xmit.reserved = htons(0xFFFE);
564         frame.xmit.bcast_radius = 0;
565         frame.xmit.opts = 0;
566         memcpy(&frame.buf, data, data_len);
567
568         if (xbeeapp_send(ctx, XBEE_TYPE_XMIT, &frame,
569                          sizeof(struct xbee_xmit_hdr) +
570                          data_len, foreground) < 0) {
571                 free(ctx);
572                 return -1;
573         }
574
575         return 0;
576 }
577
578 void xbee_stdin_enable(void)
579 {
580         event_add(&stdin_read_event, NULL);
581 }
582
583 void xbee_stdin_disable(void)
584 {
585         event_del(&stdin_read_event);
586 }
587
588 static void evt_timeout(int s, short event, void *arg)
589 {
590         struct xbee_ctx *ctx = arg;
591
592         xbeeapp_log(0, "Timeout\n");
593
594         /* restart command line */
595         if (ctx->foreground) {
596                 if (xbee_debug == 0)
597                         printf("Timeout\n");
598                 xbee_stdin_enable();
599                 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
600         }
601         /* free event */
602         xbee_unregister_channel(xbee_dev, ctx->channel);
603         free(ctx);
604 }
605
606 void xbee_load_timeout(struct xbee_ctx *ctx)
607 {
608         struct timeval tv;
609
610         tv.tv_sec = 0;
611         tv.tv_usec = TIMEOUT_US;
612         evtimer_set(&ctx->timeout, evt_timeout, ctx);
613         evtimer_add(&ctx->timeout, &tv);
614 }
615
616 void xbee_unload_timeout(struct xbee_ctx *ctx)
617 {
618         evtimer_del(&ctx->timeout);
619 }
620
621 static void
622 evt_stdin_input(int s, short event, void *arg)
623 {
624         char buf[2048];
625         int n;
626
627         n = read(s, buf, sizeof(buf));
628         if (n < 0) {
629                 event_loopbreak();
630                 return;
631         }
632
633         if (xbee_raw) {
634                 int i;
635                 for (i = 0; i < n; i++) {
636                         /* ctrl-d is pressed, back in cmdline mode XXX */
637                         if (buf[i] == 4) {
638                                 xbee_raw = 0;
639                                 n = i;
640                                 rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
641                                 break;
642                         }
643                 }
644                 /* XXX bad hack */
645                 if (buf[0] == '\n')
646                         write(xbee_dev->fd, "\r\n", 2);
647                 else
648                         write(xbee_dev->fd, buf, n);
649                 write(s, buf, n);
650
651         }
652         else {
653                 if (cmdline_in(xbee_cl, buf, n) < 0) {
654                         event_loopbreak();
655                         return;
656                 }
657         }
658 }
659
660 static void
661 evt_xbee_input(int s, short event, void *arg)
662 {
663         if (xbee_raw) {
664                 char buf[2048];
665                 int n;
666
667                 n = read(s, buf, sizeof(buf));
668                 if (n < 0)
669                         return;
670
671                 write(1, buf, n);
672         }
673         else {
674                 xbee_read(xbee_dev);
675                 xbee_process_queue(xbee_dev);
676         }
677 }
678
679 static void
680 usage(const char *prgname)
681 {
682         printf("%s [-s BAUD] -d DEVNAME\n"
683                "  -s BAUD:      set baud rate of xbee device\n"
684                "  -d DEVNAME:   xbee char device\n"
685                "  -r:           start in raw mode\n"
686                "\n",
687                prgname);
688 }
689
690 /* Parse the argument given in the command line of the application */
691 static int
692 parse_args(int argc, char **argv)
693 {
694         int opt, ret;
695         char **argvopt;
696         int option_index;
697         char *prgname = argv[0];
698         static struct option lgopts[] = {
699                 {0, 0, 0, 0}
700         };
701
702         argvopt = argv;
703
704         while ((opt = getopt_long(argc, argvopt, "hd:s:j:",
705                                   lgopts, &option_index)) != EOF) {
706
707                 switch (opt) {
708
709                 case 'h':
710                         usage(prgname);
711                         exit(0);
712                         break;
713
714                 case 'd':
715                         xbee_devname = strdup(optarg);
716                         break;
717
718                 case 's':
719                         xbee_baud = atoi(optarg);
720                         break;
721
722                 case 'r':
723                         xbee_raw = 1;
724                         break;
725
726                 case 'j':
727                         joystick_devname = optarg;
728                         break;
729
730                 /* long options */
731                 case 0:
732                         /* if (!strcmp(lgopts[option_index].name, "option")) */
733                         break;
734
735                 default:
736                         usage(prgname);
737                         return -1;
738                 }
739         }
740
741         if (xbee_devname == NULL) {
742                 fprintf(stderr, "xbee device argument missing\n");
743                 usage(prgname);
744                 return -1;
745         }
746
747         if (argc != optind) {
748                 printf("Invalid argument\n");
749                 usage(prgname);
750                 return -1;
751         }
752
753         ret = optind-1;
754
755         return ret;
756 }
757
758 int main(int argc, char **argv)
759 {
760         struct termios oldterm, term;
761         int err = 0;
762         const char *homedir;
763         char xbeerc_path[256];
764
765         if (parse_args(argc, argv) < 0)
766                 exit(1);
767
768         /* initializa libevent */
769         event_init();
770
771         /* initialize libxbee */
772         err = xbee_init();
773         if (err < 0)
774                 return -1;
775
776         /* init joystick */
777         if (joystick_devname != NULL) {
778                 if (joystick_init(joystick_devname, &joyinfo) < 0) {
779                         fprintf(stderr, "error in joystick init\n");
780                         return -1;
781                 }
782                 event_set(&joystick_read_event, joyinfo.fd, EV_READ | EV_PERSIST,
783                           joystick_input, &joyinfo);
784                 event_add(&joystick_read_event, NULL);
785         }
786
787         /* open xbee device */
788         xbee_dev = xbee_open(xbee_devname, xbee_baud);
789         if (xbee_dev == NULL)
790                 return -1;
791
792         /* register default channel with a callback */
793         if (xbee_register_channel(xbee_dev, XBEE_DEFAULT_CHANNEL,
794                                   xbee_rx, NULL) < 0) {
795                 fprintf(stderr, "cannot register default channel\n");
796                 return -1;
797         }
798
799         /* add read event on xbee device */
800         event_set(&xbee_read_event, xbee_dev->fd, EV_READ | EV_PERSIST,
801                   evt_xbee_input, xbee_dev);
802         event_add(&xbee_read_event, NULL);
803
804         /* set terminal in raw mode */
805         tcgetattr(0, &oldterm);
806         memcpy(&term, &oldterm, sizeof(term));
807         term.c_lflag &= ~(ICANON | ECHO | ISIG);
808         tcsetattr(0, TCSANOW, &term);
809         setbuf(stdin, NULL);
810
811         /* parse the config file */
812         homedir = getenv("HOME");
813         if (homedir != NULL) {
814                 snprintf(xbeerc_path, sizeof(xbeerc_path), "%s/.xbeerc",
815                          homedir);
816                 if (access(xbeerc_path, R_OK) == 0) {
817
818                         xbee_cl = cmdline_file_new(&main_ctx, "xbeerc> ",
819                                               "/home/zer0/.xbeerc", 1);
820                         if (xbee_cl != NULL) {
821                                 cmdline_interact(xbee_cl);
822                                 cmdline_free(xbee_cl);
823                                 printf("\nconfig file parsed\n");
824                         }
825                 }
826         }
827
828         /* create a new cmdline instance */
829         xbee_cl = cmdline_stdin_new(&main_ctx, "xbee> ");
830         if (xbee_cl == NULL) {
831                 err = 1;
832                 goto fail;
833         }
834
835         /* load read event on stdin */
836         event_set(&stdin_read_event, 0, EV_READ | EV_PERSIST,
837                   evt_stdin_input, xbee_cl);
838         event_add(&stdin_read_event, NULL);
839
840         /* libevent main loop */
841         event_dispatch();
842
843         tcsetattr(0, TCSANOW, &oldterm);
844         return 0;
845
846  fail:
847         tcsetattr(0, TCSANOW, &oldterm);
848         return err;
849 }