From 1bbb5063d82e9dc21677eb2a7d1d8f7ef2da3b89 Mon Sep 17 00:00:00 2001
From: Olivier Matz <zer0@droids-corp.org>
Date: Fri, 17 Feb 2012 18:21:19 +0100
Subject: [PATCH 1/1] init

---
 commands.c       | 1339 ++++++++++++++++++++++++++++++++++++++++++++++
 main.c           |  829 ++++++++++++++++++++++++++++
 main.h           |   69 +++
 parse_atcmd.c    |  130 +++++
 parse_atcmd.h    |   60 +++
 parse_monitor.c  |  117 ++++
 parse_monitor.h  |   66 +++
 parse_neighbor.c |  121 +++++
 parse_neighbor.h |   54 ++
 xbee.c           |  264 +++++++++
 xbee.h           |   82 +++
 xbee_atcmd.c     |  684 +++++++++++++++++++++++
 xbee_atcmd.h     |   54 ++
 xbee_buf.c       |  185 +++++++
 xbee_buf.h       |   86 +++
 xbee_neighbor.c  |   99 ++++
 xbee_neighbor.h  |   58 ++
 xbee_proto.c     |  378 +++++++++++++
 xbee_proto.h     |  153 ++++++
 xbee_stats.c     |   78 +++
 xbee_stats.h     |   67 +++
 21 files changed, 4973 insertions(+)
 create mode 100644 commands.c
 create mode 100644 main.c
 create mode 100644 main.h
 create mode 100644 parse_atcmd.c
 create mode 100644 parse_atcmd.h
 create mode 100644 parse_monitor.c
 create mode 100644 parse_monitor.h
 create mode 100644 parse_neighbor.c
 create mode 100644 parse_neighbor.h
 create mode 100644 xbee.c
 create mode 100644 xbee.h
 create mode 100644 xbee_atcmd.c
 create mode 100644 xbee_atcmd.h
 create mode 100644 xbee_buf.c
 create mode 100644 xbee_buf.h
 create mode 100644 xbee_neighbor.c
 create mode 100644 xbee_neighbor.h
 create mode 100644 xbee_proto.c
 create mode 100644 xbee_proto.h
 create mode 100644 xbee_stats.c
 create mode 100644 xbee_stats.h

diff --git a/commands.c b/commands.c
new file mode 100644
index 0000000..a12ed42
--- /dev/null
+++ b/commands.c
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/queue.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+
+#include <event.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_file.h>
+#include <cmdline.h>
+
+#include "xbee_neighbor.h"
+#include "xbee_atcmd.h"
+#include "xbee_stats.h"
+#include "xbee_buf.h"
+#include "xbee_proto.h"
+#include "xbee.h"
+#include "parse_atcmd.h"
+#include "parse_neighbor.h"
+#include "parse_monitor.h"
+#include "main.h"
+
+static struct monitor_reg_list monitor_list = LIST_HEAD_INITIALIZER(x/*XXX*/);
+static int monitor_period_ms = 1000;
+static int monitor_running = 0;
+static int monitor_count = 0;
+static struct event monitor_event;
+struct monitor_reg *monitor_current;
+
+static int range_period_ms = 1000;
+static int range_powermask = 0x1F;
+static uint8_t range_power = 0;
+static int range_running = 0;
+static uint64_t range_dstaddr = 0xFFFF; /* broadcast by default */
+static struct event range_event;
+static int range_count = 100;
+static int range_cur_count = 0;
+
+static const char *xbee_logfilename = "/tmp/xbee.log";
+
+static void monitor_cb(int s, short event, void *arg)
+{
+	struct timeval tv;
+	struct cmdline *cl = arg;
+
+	if (monitor_current == NULL)
+		monitor_current = LIST_FIRST(&monitor_list);
+
+	xbeeapp_send_atcmd(monitor_current->atcmd, NULL, 0, 0);
+	monitor_current = LIST_NEXT(monitor_current, next);
+
+	evtimer_set(&monitor_event, monitor_cb, cl);
+	tv.tv_sec = 0;
+	tv.tv_usec = (1000 * monitor_period_ms) / monitor_count;
+	evtimer_add(&monitor_event, &tv);
+}
+
+static void range_cb(int s, short event, void *arg)
+{
+	struct timeval tv;
+	struct cmdline *cl = arg;
+	char buf[16];
+	uint8_t i, mask;
+
+	range_cur_count--;
+
+	/* get new xmit power */
+	for (i = 1; i <= 8; i++) {
+		mask = 1 << ((range_power + i) & 0x7);
+		if (mask & range_powermask)
+			break;
+	}
+	range_power = ((range_power + i) & 0x7);
+
+	xbeeapp_send_atcmd("PL", &range_power, sizeof(range_power), 0);
+	snprintf(buf, sizeof(buf), "range%d", range_power);
+	xbeeapp_send_msg(range_dstaddr, buf, strlen(buf), 0);
+
+	if (range_cur_count == 0) {
+		range_running = 0;
+		return;
+	}
+
+	evtimer_set(&range_event, range_cb, cl);
+	tv.tv_sec = 0;
+	tv.tv_usec = 1000 * range_period_ms;
+	evtimer_add(&range_event, &tv);
+}
+
+/* ************* */
+
+/* this structure is filled when cmd_stats is parsed successfully */
+struct cmd_stats_result {
+	cmdline_fixed_string_t stats;
+	cmdline_fixed_string_t action;
+};
+
+/* function called when cmd_stats is parsed successfully */
+static void cmd_stats_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+	struct cmd_stats_result *res = parsed_result;
+
+	if (!strcmp(res->action, "show")) {
+		xbee_dump_stats(stdout, xbee_dev);
+		if (xbee_logfile != NULL)
+			xbee_dump_stats(xbee_logfile, xbee_dev);
+	}
+	else if (!strcmp(res->action, "reset"))
+		xbee_reset_stats(xbee_dev);
+}
+
+cmdline_parse_token_string_t cmd_stats_stats =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_result, stats, "stats");
+cmdline_parse_token_string_t cmd_stats_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_result, action, "show#reset");
+
+cmdline_parse_inst_t cmd_stats = {
+	.f = cmd_stats_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Send a stats to the xbee device",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_stats_stats,
+		(void *)&cmd_stats_action,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_monitor is parsed successfully */
+struct cmd_monitor_result {
+	cmdline_fixed_string_t monitor;
+	cmdline_fixed_string_t action;
+};
+
+/* function called when cmd_monitor is parsed successfully */
+static void cmd_monitor_parsed(void *parsed_result, struct cmdline *cl,
+			       void *data)
+{
+	struct cmd_monitor_result *res = parsed_result;
+	struct monitor_reg *m;
+
+	if (!strcmp(res->action, "show")) {
+		printf("monitor period is %d ms, %d regs in list\n",
+		       monitor_period_ms, monitor_count);
+		LIST_FOREACH(m, &monitor_list, next)
+			printf(" %s\n", m->desc);
+	}
+	else if (!strcmp(res->action, "start")) {
+		struct timeval tv;
+		if (monitor_running) {
+			printf("already running\n");
+			return;
+		}
+		if (monitor_count == 0) {
+			printf("no regs to be monitored\n");
+			return;
+		}
+		evtimer_set(&monitor_event, monitor_cb, cl);
+		tv.tv_sec = 0;
+		tv.tv_usec = 0;
+		evtimer_add(&monitor_event, &tv);
+		monitor_running = 1;
+		monitor_current = LIST_FIRST(&monitor_list);
+	}
+	else if (!strcmp(res->action, "end")) {
+		if (monitor_running == 0) {
+			printf("not running\n");
+			return;
+		}
+		monitor_running = 0;
+		evtimer_del(&monitor_event);
+	}
+}
+
+cmdline_parse_token_string_t cmd_monitor_monitor =
+	TOKEN_STRING_INITIALIZER(struct cmd_monitor_result, monitor, "monitor");
+cmdline_parse_token_string_t cmd_monitor_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_monitor_result, action,
+				 "show#start#end");
+
+cmdline_parse_inst_t cmd_monitor = {
+	.f = cmd_monitor_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "start/stop/show current monitoring",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_monitor_monitor,
+		(void *)&cmd_monitor_action,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_monitor_add is parsed successfully */
+struct cmd_monitor_add_result {
+	cmdline_fixed_string_t monitor;
+	cmdline_fixed_string_t action;
+	struct xbee_atcmd *cmd;
+};
+
+/* function called when cmd_monitor_add is parsed successfully */
+static void cmd_monitor_add_parsed(void *parsed_result, struct cmdline *cl,
+				   void *data)
+{
+	struct cmd_monitor_add_result *res = parsed_result;
+	struct monitor_reg *m;
+
+	LIST_FOREACH(m, &monitor_list, next) {
+		if (!strcmp(m->desc, res->cmd->desc))
+			break;
+	}
+
+	if (m != NULL) {
+		printf("already exist\n");
+		return;
+	}
+
+	m = malloc(sizeof(*m));
+	if (m == NULL) {
+		printf("no mem\n");
+		return;
+	}
+
+	m->desc = res->cmd->desc;
+	m->atcmd = res->cmd->name;
+	LIST_INSERT_HEAD(&monitor_list, m, next);
+	monitor_count ++;
+}
+
+cmdline_parse_token_string_t cmd_monitor_add_monitor_add =
+	TOKEN_STRING_INITIALIZER(struct cmd_monitor_add_result, monitor,
+				 "monitor");
+cmdline_parse_token_string_t cmd_monitor_add_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_monitor_add_result, action,
+				 "add");
+parse_token_atcmd_t cmd_monitor_add_atcmd =
+	TOKEN_ATCMD_INITIALIZER(struct cmd_monitor_add_result, cmd, &xbee_dev,
+				XBEE_ATCMD_F_READ, XBEE_ATCMD_F_READ);
+
+
+cmdline_parse_inst_t cmd_monitor_add = {
+	.f = cmd_monitor_add_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "add a register in monitor list",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_monitor_add_monitor_add,
+		(void *)&cmd_monitor_add_action,
+		(void *)&cmd_monitor_add_atcmd,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_monitor_period is parsed successfully */
+struct cmd_monitor_period_result {
+	cmdline_fixed_string_t monitor;
+	cmdline_fixed_string_t action;
+	uint32_t period;
+};
+
+/* function called when cmd_monitor_period is parsed successfully */
+static void cmd_monitor_period_parsed(void *parsed_result, struct cmdline *cl,
+				   void *data)
+{
+	struct cmd_monitor_period_result *res = parsed_result;
+
+	if (res->period < 100) {
+		printf("error, minimum period is 100 ms\n");
+		return;
+	}
+
+	monitor_period_ms = res->period;
+}
+
+cmdline_parse_token_string_t cmd_monitor_period_monitor_period =
+	TOKEN_STRING_INITIALIZER(struct cmd_monitor_period_result, monitor,
+				 "monitor");
+cmdline_parse_token_string_t cmd_monitor_period_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_monitor_period_result, action,
+				 "period");
+cmdline_parse_token_num_t cmd_monitor_period_period =
+	TOKEN_NUM_INITIALIZER(struct cmd_monitor_period_result, period, UINT32);
+
+
+cmdline_parse_inst_t cmd_monitor_period = {
+	.f = cmd_monitor_period_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "set register monitoring period",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_monitor_period_monitor_period,
+		(void *)&cmd_monitor_period_action,
+		(void *)&cmd_monitor_period_period,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_monitor_del is parsed successfully */
+struct cmd_monitor_del_result {
+	cmdline_fixed_string_t monitor;
+	cmdline_fixed_string_t action;
+	struct monitor_reg *m;
+};
+
+/* function called when cmd_monitor_del is parsed successfully */
+static void cmd_monitor_del_parsed(void *parsed_result, struct cmdline *cl,
+				   void *data)
+{
+	struct cmd_monitor_del_result *res = parsed_result;
+
+	monitor_current = LIST_NEXT(res->m, next);
+	LIST_REMOVE(res->m, next);
+	free(res->m);
+	monitor_count --;
+	if (monitor_count == 0) {
+		printf("Disable monitoring, no more event\n");
+		evtimer_del(&monitor_event);
+		monitor_running = 0;
+		return;
+	}
+}
+
+cmdline_parse_token_string_t cmd_monitor_del_monitor_del =
+	TOKEN_STRING_INITIALIZER(struct cmd_monitor_del_result, monitor,
+				 "monitor");
+cmdline_parse_token_string_t cmd_monitor_del_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_monitor_del_result, action,
+				 "del");
+parse_token_monitor_t cmd_monitor_del_atcmd =
+	TOKEN_MONITOR_INITIALIZER(struct cmd_monitor_del_result, m,
+				  &monitor_list);
+
+
+cmdline_parse_inst_t cmd_monitor_del = {
+	.f = cmd_monitor_del_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "del a register in monitor list",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_monitor_del_monitor_del,
+		(void *)&cmd_monitor_del_action,
+		(void *)&cmd_monitor_del_atcmd,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_range is parsed successfully */
+struct cmd_range_result {
+	cmdline_fixed_string_t range;
+	cmdline_fixed_string_t action;
+};
+
+/* function called when cmd_range is parsed successfully */
+static void cmd_range_parsed(void *parsed_result, struct cmdline *cl,
+			       void *data)
+{
+	struct cmd_range_result *res = parsed_result;
+
+	if (!strcmp(res->action, "show")) {
+		printf("range infos:\n");
+		printf("  range period %d\n", range_period_ms);
+		printf("  range count %d\n", range_count);
+		printf("  range powermask 0x%x\n", range_powermask);
+		printf("  range dstaddr %"PRIx64"\n", range_dstaddr);
+		if (range_running)
+			printf("  range test is running\n");
+		else
+			printf("  range test is not running\n");
+	}
+	else if (!strcmp(res->action, "start")) {
+		struct timeval tv;
+		if (range_running) {
+			printf("already running\n");
+			return;
+		}
+		range_cur_count = range_count;
+		evtimer_set(&range_event, range_cb, cl);
+		tv.tv_sec = 0;
+		tv.tv_usec = 0;
+		evtimer_add(&range_event, &tv);
+		range_running = 1;
+	}
+	else if (!strcmp(res->action, "end")) {
+		if (range_running == 0) {
+			printf("not running\n");
+			return;
+		}
+		range_running = 0;
+		evtimer_del(&range_event);
+	}
+}
+
+cmdline_parse_token_string_t cmd_range_range =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_result, range, "range");
+cmdline_parse_token_string_t cmd_range_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_result, action,
+				 "show#start#end");
+
+cmdline_parse_inst_t cmd_range = {
+	.f = cmd_range_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "start/stop/show current rangeing",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_range_range,
+		(void *)&cmd_range_action,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_range_period is parsed successfully */
+struct cmd_range_period_result {
+	cmdline_fixed_string_t range;
+	cmdline_fixed_string_t action;
+	uint32_t period;
+};
+
+/* function called when cmd_range_period is parsed successfully */
+static void cmd_range_period_parsed(void *parsed_result, struct cmdline *cl,
+				   void *data)
+{
+	struct cmd_range_period_result *res = parsed_result;
+
+	if (res->period < 10) {
+		printf("error, minimum period is 10 ms\n");
+		return;
+	}
+
+	range_period_ms = res->period;
+}
+
+cmdline_parse_token_string_t cmd_range_period_range_period =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_period_result, range,
+				 "range");
+cmdline_parse_token_string_t cmd_range_period_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_period_result, action,
+				 "period");
+cmdline_parse_token_num_t cmd_range_period_period =
+	TOKEN_NUM_INITIALIZER(struct cmd_range_period_result, period, UINT32);
+
+
+cmdline_parse_inst_t cmd_range_period = {
+	.f = cmd_range_period_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "set range test period",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_range_period_range_period,
+		(void *)&cmd_range_period_action,
+		(void *)&cmd_range_period_period,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_range_count is parsed successfully */
+struct cmd_range_count_result {
+	cmdline_fixed_string_t range;
+	cmdline_fixed_string_t action;
+	uint32_t count;
+};
+
+/* function called when cmd_range_count is parsed successfully */
+static void cmd_range_count_parsed(void *parsed_result, struct cmdline *cl,
+				   void *data)
+{
+	struct cmd_range_count_result *res = parsed_result;
+	range_count = res->count;
+}
+
+cmdline_parse_token_string_t cmd_range_count_range_count =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_count_result, range,
+				 "range");
+cmdline_parse_token_string_t cmd_range_count_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_count_result, action,
+				 "count");
+cmdline_parse_token_num_t cmd_range_count_count =
+	TOKEN_NUM_INITIALIZER(struct cmd_range_count_result, count, UINT32);
+
+
+cmdline_parse_inst_t cmd_range_count = {
+	.f = cmd_range_count_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "set range test count",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_range_count_range_count,
+		(void *)&cmd_range_count_action,
+		(void *)&cmd_range_count_count,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_range_powermask is parsed successfully */
+struct cmd_range_powermask_result {
+	cmdline_fixed_string_t range;
+	cmdline_fixed_string_t action;
+	uint8_t powermask;
+};
+
+/* function called when cmd_range_powermask is parsed successfully */
+static void cmd_range_powermask_parsed(void *parsed_result, struct cmdline *cl,
+				   void *data)
+{
+	struct cmd_range_powermask_result *res = parsed_result;
+	range_powermask = res->powermask;
+}
+
+cmdline_parse_token_string_t cmd_range_powermask_range_powermask =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_powermask_result, range,
+				 "range");
+cmdline_parse_token_string_t cmd_range_powermask_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_powermask_result, action,
+				 "powermask");
+cmdline_parse_token_num_t cmd_range_powermask_powermask =
+	TOKEN_NUM_INITIALIZER(struct cmd_range_powermask_result, powermask,
+			      UINT8);
+
+
+cmdline_parse_inst_t cmd_range_powermask = {
+	.f = cmd_range_powermask_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "set range test powermask",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_range_powermask_range_powermask,
+		(void *)&cmd_range_powermask_action,
+		(void *)&cmd_range_powermask_powermask,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_range_dstaddr is parsed successfully */
+struct cmd_range_dstaddr_result {
+	cmdline_fixed_string_t range;
+	cmdline_fixed_string_t action;
+	uint64_t dstaddr;
+};
+
+/* function called when cmd_range_dstaddr is parsed successfully */
+static void cmd_range_dstaddr_parsed(void *parsed_result, struct cmdline *cl,
+				     void *data)
+{
+	struct cmd_range_dstaddr_result *res = parsed_result;
+
+	range_dstaddr = res->dstaddr;
+}
+
+cmdline_parse_token_string_t cmd_range_dstaddr_range_dstaddr =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_dstaddr_result, range,
+				 "range");
+cmdline_parse_token_string_t cmd_range_dstaddr_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_range_dstaddr_result, action,
+				 "dstaddr");
+cmdline_parse_token_num_t cmd_range_dstaddr_dstaddr =
+	TOKEN_NUM_INITIALIZER(struct cmd_range_dstaddr_result, dstaddr, UINT64);
+
+
+cmdline_parse_inst_t cmd_range_dstaddr = {
+	.f = cmd_range_dstaddr_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "set register rangeing dstaddr",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_range_dstaddr_range_dstaddr,
+		(void *)&cmd_range_dstaddr_action,
+		(void *)&cmd_range_dstaddr_dstaddr,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_ping is parsed successfully */
+struct cmd_ping_result {
+	cmdline_fixed_string_t ping;
+};
+
+/* function called when cmd_ping is parsed successfully */
+static void cmd_ping_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+	xbeeapp_send_atcmd("VL", NULL, 0, 1);
+}
+
+cmdline_parse_token_string_t cmd_ping_ping =
+	TOKEN_STRING_INITIALIZER(struct cmd_ping_result, ping, "ping");
+
+cmdline_parse_inst_t cmd_ping = {
+	.f = cmd_ping_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Send a ping to the xbee device",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_ping_ping,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_raw is parsed successfully */
+struct cmd_raw_result {
+	cmdline_fixed_string_t raw;
+};
+
+/* function called when cmd_raw is parsed successfully */
+static void cmd_raw_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+	printf("switched to raw mode, CTRL-D to exit\n");
+	rdline_stop(&cl->rdl); /* don't display prompt when return */
+	xbee_raw = 1;
+}
+
+cmdline_parse_token_string_t cmd_raw_raw =
+	TOKEN_STRING_INITIALIZER(struct cmd_raw_result, raw, "raw");
+
+cmdline_parse_inst_t cmd_raw = {
+	.f = cmd_raw_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Switch to raw mode",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_raw_raw,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_dump is parsed successfully */
+struct cmd_dump_result {
+	cmdline_fixed_string_t dump;
+	cmdline_fixed_string_t onoff;
+};
+
+/* function called when cmd_dump is parsed successfully */
+static void cmd_dump_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+	struct cmd_dump_result *res = parsed_result;
+	if (!strcmp(res->onoff, "on"))
+		xbee_hexdump = 1;
+	else
+		xbee_hexdump = 0;
+}
+
+cmdline_parse_token_string_t cmd_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_dump_result, dump, "dump");
+
+cmdline_parse_token_string_t cmd_dump_onoff =
+	TOKEN_STRING_INITIALIZER(struct cmd_dump_result, onoff, "on#off");
+
+cmdline_parse_inst_t cmd_dump = {
+	.f = cmd_dump_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "enable/disable hexdump of received packets",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_dump_dump,
+		(void *)&cmd_dump_onoff,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_debug is parsed successfully */
+struct cmd_debug_result {
+	cmdline_fixed_string_t debug;
+	cmdline_fixed_string_t onoff;
+};
+
+/* function called when cmd_debug is parsed successfully */
+static void cmd_debug_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+	struct cmd_debug_result *res = parsed_result;
+	if (!strcmp(res->onoff, "on"))
+		xbee_debug = 1;
+	else
+		xbee_debug = 0;
+}
+
+cmdline_parse_token_string_t cmd_debug_debug =
+	TOKEN_STRING_INITIALIZER(struct cmd_debug_result, debug, "debug");
+
+cmdline_parse_token_string_t cmd_debug_onoff =
+	TOKEN_STRING_INITIALIZER(struct cmd_debug_result, onoff, "on#off");
+
+cmdline_parse_inst_t cmd_debug = {
+	.f = cmd_debug_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "enable/disable additionnal debug",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_debug_debug,
+		(void *)&cmd_debug_onoff,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_help is parsed successfully */
+struct cmd_help_result {
+	cmdline_fixed_string_t help;
+	struct xbee_atcmd *cmd;
+};
+
+/* function called when cmd_help is parsed successfully */
+static void cmd_help_parsed(void *parsed_result, struct cmdline *cl,
+			    void *data)
+{
+	struct cmd_help_result *res = parsed_result;
+	int type;
+
+	type = (res->cmd->flags & (XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE));
+	switch (type) {
+		case XBEE_ATCMD_F_READ:
+			printf("Read-only\n");
+			break;
+		case XBEE_ATCMD_F_WRITE:
+			printf("Write-only\n");
+			break;
+		default:
+			printf("Read-write\n");
+			break;
+	}
+	if (res->cmd->flags & XBEE_ATCMD_F_PARAM_NONE)
+		printf("No argument\n");
+	else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U8)
+		printf("Register is unsigned 8 bits\n");
+	else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U16)
+		printf("Register is unsigned 16 bits\n");
+	else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U32)
+		printf("Register is unsigned 32 bits\n");
+	else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_S16)
+		printf("Register is signed 16 bits\n");
+	else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_STRING_20B)
+		printf("Register is a 20 bytes string\n");
+	else
+		printf("Unknown argument\n");
+
+	printf("%s\n", res->cmd->help);
+}
+
+cmdline_parse_token_string_t cmd_help_help =
+	TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help");
+
+parse_token_atcmd_t cmd_help_atcmd =
+	TOKEN_ATCMD_INITIALIZER(struct cmd_help_result, cmd, &xbee_dev,
+				0, 0);
+
+cmdline_parse_inst_t cmd_help = {
+	.f = cmd_help_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Help a register using an AT command",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_help_help,
+		(void *)&cmd_help_atcmd,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_read is parsed successfully */
+struct cmd_read_result {
+	cmdline_fixed_string_t read;
+	struct xbee_atcmd *cmd;
+};
+
+/* function called when cmd_read is parsed successfully */
+static void cmd_read_parsed(void *parsed_result, struct cmdline *cl,
+			    void *data)
+{
+	struct cmd_read_result *res = parsed_result;
+	xbeeapp_send_atcmd(res->cmd->name, NULL, 0, 1);
+}
+
+cmdline_parse_token_string_t cmd_read_read =
+	TOKEN_STRING_INITIALIZER(struct cmd_read_result, read, "read");
+
+parse_token_atcmd_t cmd_read_atcmd =
+	TOKEN_ATCMD_INITIALIZER(struct cmd_read_result, cmd, &xbee_dev,
+				XBEE_ATCMD_F_READ, XBEE_ATCMD_F_READ);
+
+cmdline_parse_inst_t cmd_read = {
+	.f = cmd_read_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Read a register using an AT command",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_read_read,
+		(void *)&cmd_read_atcmd,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_write is parsed successfully */
+struct cmd_write_result {
+	cmdline_fixed_string_t write;
+	struct xbee_atcmd *cmd;
+	union {
+		uint8_t u8;
+		uint16_t u16;
+		uint32_t u32;
+	};
+};
+
+/* function called when cmd_write is parsed successfully */
+static void cmd_write_parsed(void *parsed_result, struct cmdline *cl,
+			     void *data)
+{
+	struct cmd_write_result *res = parsed_result;
+	int len;
+	void *param;
+
+	if (res->cmd->flags & XBEE_ATCMD_F_PARAM_NONE) {
+		len = 0;
+		param = NULL;
+	}
+	else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U8) {
+		len = sizeof(res->u8);
+		param = &res->u8;
+	}
+	else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U16) {
+		len = sizeof(res->u16);
+		res->u16 = htons(res->u16);
+		param = &res->u16;
+	}
+	else if (res->cmd->flags & XBEE_ATCMD_F_PARAM_U32) {
+		len = sizeof(res->u32);
+		res->u32 = htonl(res->u32);
+		param = &res->u32;
+	}
+	else {
+		printf("Unknown argument type\n");
+		return;
+	}
+	xbeeapp_send_atcmd(res->cmd->name, param, len, 1);
+}
+
+cmdline_parse_token_string_t cmd_write_write =
+	TOKEN_STRING_INITIALIZER(struct cmd_write_result, write,
+				 "write");
+
+parse_token_atcmd_t cmd_write_none_atcmd =
+	TOKEN_ATCMD_INITIALIZER(struct cmd_write_result, cmd,
+				&xbee_dev,
+				XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_NONE,
+				XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_NONE);
+
+cmdline_parse_inst_t cmd_write_none = {
+	.f = cmd_write_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Send an AT command (no argument)",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_write_write,
+		(void *)&cmd_write_none_atcmd,
+		NULL,
+	},
+};
+
+parse_token_atcmd_t cmd_write_u8_atcmd =
+	TOKEN_ATCMD_INITIALIZER(struct cmd_write_result, cmd,
+				&xbee_dev,
+				XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U8,
+				XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U8);
+
+cmdline_parse_token_num_t cmd_write_u8_u8 =
+	TOKEN_NUM_INITIALIZER(struct cmd_write_result, u8, UINT8);
+
+cmdline_parse_inst_t cmd_write_u8 = {
+	.f = cmd_write_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Write a 8 bits register using an AT command",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_write_write,
+		(void *)&cmd_write_u8_atcmd,
+		(void *)&cmd_write_u8_u8,
+		NULL,
+	},
+};
+
+parse_token_atcmd_t cmd_write_u16_atcmd =
+	TOKEN_ATCMD_INITIALIZER(struct cmd_write_result, cmd,
+				&xbee_dev,
+				XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U16,
+				XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U16);
+
+cmdline_parse_token_num_t cmd_write_u16_u16 =
+	TOKEN_NUM_INITIALIZER(struct cmd_write_result, u16, UINT16);
+
+cmdline_parse_inst_t cmd_write_u16 = {
+	.f = cmd_write_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Write a 16 bits register using an AT command",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_write_write,
+		(void *)&cmd_write_u16_atcmd,
+		(void *)&cmd_write_u16_u16,
+		NULL,
+	},
+};
+
+parse_token_atcmd_t cmd_write_u32_atcmd =
+	TOKEN_ATCMD_INITIALIZER(struct cmd_write_result, cmd,
+				&xbee_dev,
+				XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U32,
+				XBEE_ATCMD_F_WRITE | XBEE_ATCMD_F_PARAM_U32);
+
+cmdline_parse_token_num_t cmd_write_u32_u32 =
+	TOKEN_NUM_INITIALIZER(struct cmd_write_result, u32, UINT32);
+
+cmdline_parse_inst_t cmd_write_u32 = {
+	.f = cmd_write_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Write a 32 bits register using an AT command",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_write_write,
+		(void *)&cmd_write_u32_atcmd,
+		(void *)&cmd_write_u32_u32,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_sendmsg is parsed successfully */
+struct cmd_sendmsg_result {
+	cmdline_fixed_string_t sendmsg;
+	uint64_t addr;
+	cmdline_fixed_string_t data;
+};
+
+/* function called when cmd_sendmsg is parsed successfully */
+static void cmd_sendmsg_parsed(void *parsed_result, struct cmdline *cl,
+			    void *data)
+{
+	struct cmd_sendmsg_result *res = parsed_result;
+	xbeeapp_send_msg(res->addr, res->data, strlen(res->data), 1);
+}
+
+cmdline_parse_token_string_t cmd_sendmsg_sendmsg =
+	TOKEN_STRING_INITIALIZER(struct cmd_sendmsg_result, sendmsg, "sendmsg");
+
+cmdline_parse_token_num_t cmd_sendmsg_addr =
+	TOKEN_NUM_INITIALIZER(struct cmd_sendmsg_result, addr, UINT64);
+
+cmdline_parse_token_string_t cmd_sendmsg_data =
+	TOKEN_STRING_INITIALIZER(struct cmd_sendmsg_result, data, NULL);
+
+cmdline_parse_inst_t cmd_sendmsg = {
+	.f = cmd_sendmsg_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Send data to a node using its address",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_sendmsg_sendmsg,
+		(void *)&cmd_sendmsg_addr,
+		(void *)&cmd_sendmsg_data,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_sendmsg_name is parsed successfully */
+struct cmd_sendmsg_name_result {
+	cmdline_fixed_string_t sendmsg_name;
+	struct xbee_neigh *neigh;
+	cmdline_fixed_string_t data;
+};
+
+/* function called when cmd_sendmsg_name is parsed successfully */
+static void cmd_sendmsg_name_parsed(void *parsed_result, struct cmdline *cl,
+			    void *data)
+{
+	struct cmd_sendmsg_name_result *res = parsed_result;
+	xbeeapp_send_msg(res->neigh->addr, res->data, strlen(res->data), 1);
+}
+
+cmdline_parse_token_string_t cmd_sendmsg_name_sendmsg_name =
+	TOKEN_STRING_INITIALIZER(struct cmd_sendmsg_name_result, sendmsg_name,
+				 "sendmsg");
+
+parse_token_neighbor_t cmd_sendmsg_name_neigh =
+	TOKEN_NEIGHBOR_INITIALIZER(struct cmd_sendmsg_name_result, neigh,
+				   &xbee_dev);
+
+cmdline_parse_token_string_t cmd_sendmsg_name_data =
+	TOKEN_STRING_INITIALIZER(struct cmd_sendmsg_name_result, data, NULL);
+
+cmdline_parse_inst_t cmd_sendmsg_name = {
+	.f = cmd_sendmsg_name_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "Send data to a node using its name",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_sendmsg_name_sendmsg_name,
+		(void *)&cmd_sendmsg_name_neigh,
+		(void *)&cmd_sendmsg_name_data,
+		NULL,
+	},
+};
+
+/* ************* */
+
+struct cmd_neigh_del_result {
+	cmdline_fixed_string_t cmd;
+	cmdline_fixed_string_t action;
+	struct xbee_neigh *neigh;
+};
+
+static void cmd_neigh_del_parsed(void *parsed_result,
+				struct cmdline *cl,
+				void *data)
+{
+	struct cmd_neigh_del_result *res = parsed_result;
+	xbee_neigh_del(xbee_dev, res->neigh);
+}
+
+cmdline_parse_token_string_t cmd_neigh_del_cmd =
+	TOKEN_STRING_INITIALIZER(struct cmd_neigh_del_result, cmd, "neigh");
+cmdline_parse_token_string_t cmd_neigh_del_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_neigh_del_result, action, "del");
+parse_token_neighbor_t cmd_neigh_del_neigh =
+	TOKEN_NEIGHBOR_INITIALIZER(struct cmd_neigh_del_result, neigh,
+				   &xbee_dev);
+
+cmdline_parse_inst_t cmd_neigh_del = {
+	.f = cmd_neigh_del_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "delete a neighbor",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_neigh_del_cmd,
+		(void *)&cmd_neigh_del_action,
+		(void *)&cmd_neigh_del_neigh,
+		NULL,
+	},
+};
+
+/* ************* */
+
+struct cmd_neigh_add_result {
+	cmdline_fixed_string_t cmd;
+	cmdline_fixed_string_t action;
+	cmdline_fixed_string_t name;
+	uint64_t addr;
+};
+
+static void cmd_neigh_add_parsed(void *parsed_result,
+				struct cmdline *cl,
+				void *data)
+{
+	struct cmd_neigh_add_result *res = parsed_result;
+	if (xbee_neigh_add(xbee_dev, res->name, res->addr) == NULL)
+		printf("name or addr already exist\n");
+}
+
+cmdline_parse_token_string_t cmd_neigh_add_cmd =
+	TOKEN_STRING_INITIALIZER(struct cmd_neigh_add_result, cmd, "neigh");
+cmdline_parse_token_string_t cmd_neigh_add_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_neigh_add_result, action, "add");
+cmdline_parse_token_string_t cmd_neigh_add_name =
+	TOKEN_STRING_INITIALIZER(struct cmd_neigh_add_result, name, NULL);
+cmdline_parse_token_num_t cmd_neigh_add_addr =
+	TOKEN_NUM_INITIALIZER(struct cmd_neigh_add_result, addr, UINT64);
+
+cmdline_parse_inst_t cmd_neigh_add = {
+	.f = cmd_neigh_add_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "add a neighbor",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_neigh_add_cmd,
+		(void *)&cmd_neigh_add_action,
+		(void *)&cmd_neigh_add_name,
+		(void *)&cmd_neigh_add_addr,
+		NULL,
+	},
+};
+
+/* ************* */
+
+struct cmd_neigh_list_result {
+	cmdline_fixed_string_t cmd;
+	cmdline_fixed_string_t action;
+};
+
+static void cmd_neigh_list_parsed(void *parsed_result,
+				struct cmdline *cl,
+				void *data)
+{
+	struct xbee_neigh *neigh;
+
+	LIST_FOREACH(neigh, &xbee_dev->neigh_list, next) {
+		printf(" %s: 0x%"PRIx64"\n", neigh->name, neigh->addr);
+	}
+}
+
+cmdline_parse_token_string_t cmd_neigh_list_cmd =
+	TOKEN_STRING_INITIALIZER(struct cmd_neigh_list_result, cmd, "neigh");
+cmdline_parse_token_string_t cmd_neigh_list_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_neigh_list_result, action, "list");
+
+cmdline_parse_inst_t cmd_neigh_list = {
+	.f = cmd_neigh_list_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "list all known neighbors",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_neigh_list_cmd,
+		(void *)&cmd_neigh_list_action,
+		NULL,
+	},
+};
+
+/*******************/
+
+struct cmd_logfile_result {
+	cmdline_fixed_string_t logfile;
+	cmdline_filename_t file;
+};
+
+static void cmd_logfile_parsed(void *parsed_result,
+			       struct cmdline *cl,
+			       void *data)
+{
+	if (xbee_logfile != NULL)
+		fclose(xbee_logfile);
+	xbee_logfile = fopen(xbee_logfilename, "a");
+	if (xbee_logfile == NULL)
+		printf("cannot open file: %s\n", strerror(errno));
+	fprintf(xbee_logfile, "-------------------start\n");
+	printf("enabling log\n");
+}
+
+cmdline_parse_token_string_t cmd_logfile_logfile =
+	TOKEN_STRING_INITIALIZER(struct cmd_logfile_result, logfile, "logfile");
+
+cmdline_parse_token_file_t cmd_logfile_file =
+	TOKEN_FILE_INITIALIZER(struct cmd_logfile_result, file,
+			       PARSE_FILE_F_CREATE);
+
+cmdline_parse_inst_t cmd_logfile = {
+	.f = cmd_logfile_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "<logfile FILE> set log file",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_logfile_logfile,
+		(void *)&cmd_logfile_file,
+		NULL,
+	},
+};
+
+/* ************* */
+
+/* this structure is filled when cmd_log is parsed successfully */
+struct cmd_log_result {
+	cmdline_fixed_string_t log;
+	cmdline_fixed_string_t onoff;
+};
+
+/* function called when cmd_log is parsed successfully */
+static void cmd_log_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+	struct cmd_log_result *res = parsed_result;
+	if (!strcmp(res->onoff, "on") && xbee_logfile == NULL) {
+		xbee_logfile = fopen(xbee_logfilename, "a");
+		if (xbee_logfile == NULL)
+			printf("cannot open file: %s\n", strerror(errno));
+		fprintf(xbee_logfile, "-------------------start\n");
+	}
+	else if (!strcmp(res->onoff, "off") && xbee_logfile != NULL) {
+		fclose(xbee_logfile);
+		xbee_logfile = NULL;
+	}
+}
+
+cmdline_parse_token_string_t cmd_log_log =
+	TOKEN_STRING_INITIALIZER(struct cmd_log_result, log, "log");
+
+cmdline_parse_token_string_t cmd_log_onoff =
+	TOKEN_STRING_INITIALIZER(struct cmd_log_result, onoff, "on#off");
+
+cmdline_parse_inst_t cmd_log = {
+	.f = cmd_log_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "enable/disable hexlog of received packets",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_log_log,
+		(void *)&cmd_log_onoff,
+		NULL,
+	},
+};
+
+
+/*******************/
+
+struct cmd_saveconfig_result {
+	cmdline_fixed_string_t saveconfig;
+	cmdline_filename_t file;
+};
+
+static void cmd_saveconfig_parsed(void *parsed_result,
+			       struct cmdline *cl,
+			       void *data)
+{
+	struct cmd_saveconfig_result *res = parsed_result;
+
+	if (xbeeapp_dump_config(res->file) < 0)
+		printf("cannot save config\n");
+}
+
+cmdline_parse_token_string_t cmd_saveconfig_saveconfig =
+	TOKEN_STRING_INITIALIZER(struct cmd_saveconfig_result, saveconfig,
+				 "saveconfig");
+
+cmdline_parse_token_file_t cmd_saveconfig_file =
+	TOKEN_FILE_INITIALIZER(struct cmd_saveconfig_result, file,
+			       PARSE_FILE_F_CREATE);
+
+cmdline_parse_inst_t cmd_saveconfig = {
+	.f = cmd_saveconfig_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "<saveconfig FILE> set log file",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_saveconfig_saveconfig,
+		(void *)&cmd_saveconfig_file,
+		NULL,
+	},
+};
+
+/*******************/
+
+struct cmd_loadconfig_result {
+	cmdline_fixed_string_t loadconfig;
+	cmdline_filename_t file;
+};
+
+static void cmd_loadconfig_parsed(void *parsed_result,
+			       struct cmdline *cl,
+			       void *data)
+{
+}
+
+cmdline_parse_token_string_t cmd_loadconfig_loadconfig =
+	TOKEN_STRING_INITIALIZER(struct cmd_loadconfig_result, loadconfig,
+				 "loadconfig");
+
+cmdline_parse_token_file_t cmd_loadconfig_file =
+	TOKEN_FILE_INITIALIZER(struct cmd_loadconfig_result, file,
+			       PARSE_FILE_F_CREATE);
+
+cmdline_parse_inst_t cmd_loadconfig = {
+	.f = cmd_loadconfig_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "<loadconfig FILE> set log file",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_loadconfig_loadconfig,
+		(void *)&cmd_loadconfig_file,
+		NULL,
+	},
+};
+
+/**********************************************************/
+/**********************************************************/
+/****** CONTEXT (list of instruction) */
+
+/* in progmem */
+cmdline_parse_ctx_t main_ctx = {
+	.name = "main",
+	.insts = {
+		&cmd_stats,
+		&cmd_monitor,
+		&cmd_monitor_period,
+		&cmd_monitor_add,
+		&cmd_monitor_del,
+		&cmd_range,
+		&cmd_range_period,
+		&cmd_range_count,
+		&cmd_range_powermask,
+		&cmd_range_dstaddr,
+		&cmd_ping,
+		&cmd_raw,
+		&cmd_dump,
+		&cmd_debug,
+		&cmd_help,
+		&cmd_read,
+		&cmd_write_none,
+		&cmd_write_u8,
+		&cmd_write_u16,
+		&cmd_write_u32,
+		&cmd_sendmsg,
+		&cmd_sendmsg_name,
+		&cmd_neigh_del,
+		&cmd_neigh_add,
+		&cmd_neigh_list,
+		&cmd_logfile,
+		&cmd_log,
+		&cmd_saveconfig,
+		&cmd_loadconfig,
+		NULL,
+	},
+};
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..52c0d80
--- /dev/null
+++ b/main.c
@@ -0,0 +1,829 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _BSD_SOURCE /* for be64toh, endian.h */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <termios.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <endian.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <sys/queue.h>
+
+#include <getopt.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include <event.h>
+
+#include "xbee_neighbor.h"
+#include "xbee_atcmd.h"
+#include "xbee_stats.h"
+#include "xbee_buf.h"
+#include "xbee_proto.h"
+#include "xbee.h"
+#include "main.h"
+
+#define TIMEOUT_US 1000000
+
+/* XXX qd on deconnecte, prend 100% du cpu */
+/* XXX rmt at cmd */
+/* XXX neighbor discovery */
+/* XXX dump et restauration de la config */
+
+/* global xbee device */
+static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
+				      struct xbee_atresp_hdr *frame,
+				      unsigned len);
+
+struct xbee_dev *xbee_dev;
+
+/* events */
+static struct event stdin_read_event, xbee_read_event;
+
+static struct cmdline *xbee_cl;
+
+/* parameters */
+static char *xbee_devname = NULL;
+static int xbee_baud = 9600;
+int xbee_raw = 0;
+int xbee_hexdump = 0;
+int xbee_debug = 0;
+FILE *xbee_logfile;
+
+void xbeeapp_log(int always_on_stdout, const char *fmt, ...)
+{
+	va_list ap, ap2;
+
+	va_start(ap, fmt);
+	va_copy(ap2, ap);
+	if (xbee_logfile != NULL)
+		vfprintf(xbee_logfile, fmt, ap);
+	if (always_on_stdout || xbee_debug)
+		vprintf(fmt, ap2);
+	va_end(ap2);
+	va_end(ap);
+}
+
+static void hexdump(int always_on_stdout, const char *title, const void *buf,
+		    unsigned int len)
+{
+	unsigned int i, out, ofs;
+	const unsigned char *data = buf;
+#define LINE_LEN 80
+	char line[LINE_LEN];	/* space needed 8+16*3+3+16 == 75 */
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);
+
+	xbeeapp_log(always_on_stdout, "%"PRIi64".%.6d %s at [%p], len=%d\n",
+		    (uint64_t)tv.tv_sec, (int)tv.tv_usec, title, data, len);
+	ofs = 0;
+	while (ofs < len) {
+		/* format 1 line in the buffer, then use printk to print them */
+		out = snprintf(line, LINE_LEN, "%08X", ofs);
+		for (i=0; ofs+i < len && i<16; i++)
+			out += snprintf(line+out, LINE_LEN - out, " %02X",
+					data[ofs+i]&0xff);
+		for (;i<=16;i++)
+			out += snprintf(line+out, LINE_LEN - out, "   ");
+		for (i=0; ofs < len && i<16; i++, ofs++) {
+			unsigned char c = data[ofs];
+			if (!isascii(c) || !isprint(c))
+				c = '.';
+			out += snprintf(line+out, LINE_LEN - out, "%c", c);
+		}
+		xbeeapp_log(always_on_stdout, "%s\n", line);
+	}
+}
+
+static int parse_xmit_status(struct xbee_ctx *ctx,
+			     struct xbee_xmit_status_hdr *frame, unsigned len)
+{
+	if (ctx == NULL) {
+		xbeeapp_log(1, "no context\n");
+		return -1;
+	}
+
+	/* see if it matches a xmit query */
+	if (ctx->type != SEND_MSG) {
+		xbeeapp_log(ctx->foreground, "invalid response\n");
+		return -1;
+	}
+
+	/* XXX use defines for these values */
+	if (frame->delivery_status == 0x00)
+		xbeeapp_log(ctx->foreground, "Success\n");
+	else if (frame->delivery_status == 0x01)
+		xbeeapp_log(ctx->foreground, "MAC ACK Failure\n");
+	else if (frame->delivery_status == 0x15)
+		xbeeapp_log(ctx->foreground, "Invalid destination endpoint\n");
+	else if (frame->delivery_status == 0x21)
+		xbeeapp_log(ctx->foreground, "Network ACK Failure\n");
+	else if (frame->delivery_status == 0x25)
+		xbeeapp_log(ctx->foreground, "Route Not Found\n");
+
+	return 0;
+}
+
+static int atcmd_frame_status(struct xbee_atresp_hdr *frame, unsigned len)
+{
+	char atcmd_str[3];
+
+	/* get AT command from frame */
+	memcpy(atcmd_str, &frame->cmd, 2);
+	atcmd_str[2] = '\0';
+
+	if (frame->status == 1)
+		xbeeapp_log(1, "<%s>: Status is error\n", atcmd_str);
+	else if (frame->status == 2)
+		xbeeapp_log(1, "<%s>: Invalid command\n", atcmd_str);
+	else if (frame->status == 3)
+		xbeeapp_log(1, "<%s>: Invalid parameter\n", atcmd_str);
+	else if (frame->status != 0)
+		xbeeapp_log(1, "<%s>: Unknown status error %d\n", atcmd_str,
+		       frame->status);
+	return frame->status;
+}
+
+/* assume frame->status is 0 */
+static int atcmd_frame_to_string(struct xbee_atresp_hdr *frame, unsigned len,
+				 char *dstbuf, unsigned dstlen)
+{
+	union {
+		uint8_t u8;
+		uint16_t u16;
+		uint32_t u32;
+		int16_t s16;
+	} __attribute__((packed)) *result;
+	char atcmd_str[3];
+	struct xbee_atcmd *cmd;
+
+	/* get AT command from frame */
+	memcpy(atcmd_str, &frame->cmd, 2);
+	atcmd_str[2] = '\0';
+
+	/* see if it exists */
+	cmd = xbee_atcmd_lookup_name(atcmd_str);
+	if (cmd == NULL) {
+		xbeeapp_log(1, "unknown response\n");
+		return -1;
+	}
+
+	/* dump frame */
+	result = (void *)frame->data;
+	len -= offsetof(struct xbee_atresp_hdr, data);
+	if (cmd->flags & XBEE_ATCMD_F_PARAM_NONE && len == 0)
+		snprintf(dstbuf, dstlen, "ok");
+	if (cmd->flags & XBEE_ATCMD_F_PARAM_U8 && len == sizeof(uint8_t))
+		snprintf(dstbuf, dstlen, "0x%x", result->u8);
+	else if (cmd->flags & XBEE_ATCMD_F_PARAM_U16 && len == sizeof(uint16_t))
+		snprintf(dstbuf, dstlen, "0x%x", ntohs(result->u16));
+	else if (cmd->flags & XBEE_ATCMD_F_PARAM_U32 && len == sizeof(uint32_t))
+		snprintf(dstbuf, dstlen, "0x%x", ntohl(result->u32));
+	else if (cmd->flags & XBEE_ATCMD_F_PARAM_S16 && len == sizeof(int16_t))
+		snprintf(dstbuf, dstlen, "%d\n", ntohs(result->s16));
+	else
+		return -1;
+
+	return 0;
+}
+
+static int dump_atcmd(struct xbee_ctx *ctx, struct xbee_atresp_hdr *frame,
+		      unsigned len)
+{
+	char buf[32];
+
+	/* see if it matches query */
+	if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
+		printf("invalid response\n");
+		return -1;
+	}
+
+	/* dump frame */
+	if (atcmd_frame_status(frame, len) == 0) {
+
+		if (len == sizeof(struct xbee_atresp_hdr))
+			xbeeapp_log(ctx->foreground, "<%s>: ok\n",
+				    ctx->atcmd_query->name);
+		else if (atcmd_frame_to_string(frame, len, buf,
+					      sizeof(buf)) == 0)
+			xbeeapp_log(ctx->foreground, "<%s> is %s\n",
+				    ctx->atcmd_query->name, buf);
+		else
+			hexdump(ctx->foreground, "atcmd answer", frame, len);
+	}
+	return 0;
+}
+
+void xbee_rx(struct xbee_dev *dev, int channel, int type,
+	     void *frame, unsigned len, void *opaque)
+{
+	struct xbee_ctx *ctx = opaque;
+	int do_hexdump = xbee_hexdump;
+
+	xbeeapp_log(0, "type=0x%x, channel=%d, ctx=%p\n", type, channel, ctx);
+	hexdump(0, "rx", frame, len);
+
+	/* if ctx is !NULL, it is an answer to a query */
+	if (ctx != NULL) {
+		/* XXX only delete timeout if answer matched */
+		xbee_unload_timeout(ctx);
+		if (xbee_debug && ctx->atcmd_query != NULL)
+			printf("Received answer to query <%s>\n",
+			       ctx->atcmd_query->name);
+		xbee_unregister_channel(dev, channel);
+	}
+
+	/* some additional checks before sending */
+	switch (type) {
+		case XBEE_TYPE_MODEM_STATUS: {
+			printf("Received Modem Status frame\n");
+			break;
+		}
+
+		case XBEE_TYPE_RMT_ATRESP: {
+			uint64_t addr;
+			memcpy(&addr, frame, sizeof(addr));
+			addr = be64toh(addr);
+			printf("from remote address %"PRIx64"\n", addr);
+
+			/* this answer contains an atcmd answer at offset 10 */
+			if (ctx != NULL && ctx->type == ATCMD_RMT) {
+				if (dump_atcmd(ctx, frame + 10, len - 10) < 0)
+					do_hexdump = 1;
+			}
+			else {
+				printf("invalid response\n");
+				do_hexdump = 1;
+			}
+			break;
+		}
+		case XBEE_TYPE_ATRESP: {
+			/* we are currently dumping config, continue */
+			if (ctx != NULL && ctx->type == DUMP_CONF) {
+				xbeeapp_dump_conf_continue(ctx, frame, len);
+			}
+			else if (ctx != NULL && ctx->type == ATCMD) {
+				if (dump_atcmd(ctx, frame, len) < 0)
+					do_hexdump = 1;
+			}
+			else {
+				printf("invalid response\n");
+				do_hexdump = 1;
+			}
+			break;
+		}
+
+		case XBEE_TYPE_XMIT_STATUS: {
+			if (parse_xmit_status(ctx, frame, len) < 0)
+				do_hexdump = 1;
+			break;
+		}
+
+		case XBEE_TYPE_RECV: {
+			struct xbee_recv_hdr *recvframe = frame;
+			int recvlen = len - sizeof(*recvframe);
+			int on_stdout = 1;
+
+			/* if we receive a range-test frame, ask for RSSI now */
+			if (recvlen >= strlen("range") &&
+			    !strncmp((char *)recvframe->data,
+				     "range", strlen("range"))) {
+				xbeeapp_send_atcmd("DB", NULL, 0, 0);
+				on_stdout = 0;
+			}
+			hexdump(on_stdout, "rx data", recvframe->data,
+				recvlen);
+			break;
+		}
+
+		case XBEE_TYPE_ATCMD:
+		case XBEE_TYPE_ATCMD_Q:
+		case XBEE_TYPE_XMIT:
+		case XBEE_TYPE_EXPL_XMIT:
+		case XBEE_TYPE_RMT_ATCMD:
+		case XBEE_TYPE_EXPL_RECV:
+		case XBEE_TYPE_NODE_ID:
+		default:
+			xbeeapp_log(0, "Invalid frame\n");
+			do_hexdump = 1;
+			break;
+	}
+
+	if (do_hexdump) {
+		xbeeapp_log(1, "rx frame type=0x%x, channel=%d, ctx=%p\n",
+			    type, channel, ctx);
+		hexdump(1, "undecoded rx frame", frame, len);
+	}
+
+	/* restart command line if it was a blocking query */
+	if (ctx != NULL) {
+		if (ctx->foreground && !ctx->have_more_command) {
+			xbee_stdin_enable();
+			rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
+		}
+		if (!ctx->have_more_command)
+			free(ctx);
+	}
+}
+
+static int xbeeapp_send(struct xbee_ctx *ctx, int type, void *buf, unsigned len,
+			int foreground)
+{
+	int ret;
+	int channel;
+
+	if (len > XBEE_MAX_FRAME_LEN) {
+		printf("frame too large\n");
+		return -1;
+	}
+
+	/* register a channel */
+	channel = xbee_register_channel(xbee_dev, XBEE_CHANNEL_ANY,
+					xbee_rx, ctx);
+	if (channel < 0) {
+		printf("cannot send: no free channel\n");
+		return -1;
+	}
+
+	xbeeapp_log(0, "send frame ctx=%p channel=%d type=0x%x len=%d\n",
+	       ctx, channel, type, len);
+	hexdump(0, "xmit frame", buf, len);
+
+	/* transmit the frame on this channel */
+	ret = xbee_proto_xmit(xbee_dev, channel, type, buf,
+			      len);
+	if (ret < 0) {
+		printf("cannot send: %s\n", strerror(errno));
+		xbee_unregister_channel(xbee_dev, channel);
+		return -1;
+	}
+
+	ctx->channel = channel;
+	xbee_load_timeout(ctx);   /* load a timeout event */
+
+	/* suspend command line until we have answer or timeout */
+	if (foreground) {
+		ctx->foreground = 1;
+		rdline_stop(&xbee_cl->rdl); /* don't display prompt when return */
+		xbee_stdin_disable();  /* unload file descriptor polling */
+	}
+
+	return 0;
+}
+
+/* send an AT command with parameters filled by caller. Disable
+ * command line until we get the answer or until a timeout occurs */
+int xbeeapp_send_atcmd(const char *atcmd_str, void *param, unsigned param_len,
+		       int foreground)
+{
+	struct xbee_ctx *ctx;
+	struct xbee_atcmd *cmd;
+	struct {
+		struct xbee_atcmd_hdr atcmd;
+		char buf[XBEE_MAX_FRAME_LEN];
+	} __attribute__((packed)) frame;
+
+	cmd = xbee_atcmd_lookup_name(atcmd_str);
+	if (cmd == NULL) {
+		printf("no such at command\n");
+		return -1;
+	}
+
+	/* allocate memory to store the context for async cb */
+	ctx = malloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		printf("not enough memory\n");
+		return -1;
+	}
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->type = ATCMD;
+	ctx->atcmd_query = cmd;
+
+	memcpy(&frame.atcmd.cmd, atcmd_str, 2);
+	memcpy(&frame.buf, param, param_len);
+
+	if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &frame,
+			 sizeof(struct xbee_atcmd_hdr) +
+			 param_len, foreground) < 0) {
+		free(ctx);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* dump config */
+static int __xbeeapp_dump_config(struct xbee_ctx *ctx, struct xbee_atcmd *cmd)
+{
+	struct xbee_atcmd_hdr atcmd;
+
+	/* find the first command that is readable and writable */
+	for (; cmd->name != NULL; cmd++) {
+		if ((cmd->flags & (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE)) ==
+		    (XBEE_ATCMD_F_READ|XBEE_ATCMD_F_WRITE))
+			break;
+	}
+	if (cmd->name == NULL) {
+		printf("no register to dump\n");
+		return -1;
+	}
+
+	ctx->atcmd_query = cmd;
+	ctx->have_more_command = 1;
+	memcpy(&atcmd.cmd, cmd->name, 2);
+
+	if (xbeeapp_send(ctx, XBEE_TYPE_ATCMD, &atcmd,
+			 sizeof(struct xbee_atcmd_hdr), 1) < 0) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/* dump config */
+int xbeeapp_dump_config(const char *filename)
+{
+	struct xbee_ctx *ctx;
+	struct xbee_atcmd *cmd;
+	int ret;
+
+	/* find the first command that is readable and writable */
+	cmd = &xbee_atcmd_list[0];
+
+	/* allocate memory to store the context for async cb */
+	ctx = malloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		printf("not enough memory\n");
+		return -1;
+	}
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->type = DUMP_CONF;
+	ret = __xbeeapp_dump_config(ctx, cmd);
+
+	if (ret < 0)
+		free(ctx);
+	return ret;
+}
+
+/* continue a dump conf */
+static int xbeeapp_dump_conf_continue(struct xbee_ctx *ctx,
+				      struct xbee_atresp_hdr *frame,
+				      unsigned len)
+{
+	struct xbee_atcmd *cmd;
+	char buf[32];
+
+	/* see if it matches query */
+	if (memcmp(&frame->cmd, ctx->atcmd_query->name, 2)) {
+		printf("invalid response\n");
+		ctx->have_more_command = 0;
+		return -1;
+	}
+
+	/* dump register content in buf, skip on error */
+	if (atcmd_frame_status(frame, len) == 0 &&
+	    atcmd_frame_to_string(frame, len, buf, sizeof(buf)) == 0)
+		printf("write %s %s\n", ctx->atcmd_query->desc, buf);
+
+	cmd = ctx->atcmd_query + 1;
+	if (__xbeeapp_dump_config(ctx, cmd) < 0) {
+		ctx->have_more_command = 0;
+		//close(xbee_config_file);
+		printf("END\n"); /* XXX */
+	}
+	return 0;
+}
+
+/* send data message */
+int xbeeapp_send_msg(uint64_t addr, void *data, unsigned data_len,
+		     int foreground)
+{
+	struct xbee_ctx *ctx;
+	struct {
+		struct xbee_xmit_hdr xmit;
+		char buf[XBEE_MAX_FRAME_LEN];
+	} __attribute__((packed)) frame;
+
+	/* allocate memory to store the context for async cb */
+	ctx = malloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		printf("not enough memory\n");
+		return -1;
+	}
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->type = SEND_MSG;
+	ctx->atcmd_query = NULL;
+
+	frame.xmit.dstaddr = htobe64(addr);
+	frame.xmit.reserved = htons(0xFFFE);
+	frame.xmit.bcast_radius = 0;
+	frame.xmit.opts = 0;
+	memcpy(&frame.buf, data, data_len);
+
+	if (xbeeapp_send(ctx, XBEE_TYPE_XMIT, &frame,
+			 sizeof(struct xbee_xmit_hdr) +
+			 data_len, foreground) < 0) {
+		free(ctx);
+		return -1;
+	}
+
+	return 0;
+}
+
+void xbee_stdin_enable(void)
+{
+	event_add(&stdin_read_event, NULL);
+}
+
+void xbee_stdin_disable(void)
+{
+	event_del(&stdin_read_event);
+}
+
+static void evt_timeout(int s, short event, void *arg)
+{
+	struct xbee_ctx *ctx = arg;
+
+	xbeeapp_log(0, "Timeout\n");
+
+	/* restart command line */
+	if (ctx->foreground) {
+		if (xbee_debug == 0)
+			printf("Timeout\n");
+		xbee_stdin_enable();
+		rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
+	}
+	/* free event */
+	xbee_unregister_channel(xbee_dev, ctx->channel);
+	free(ctx);
+}
+
+void xbee_load_timeout(struct xbee_ctx *ctx)
+{
+	struct timeval tv;
+
+	tv.tv_sec = 0;
+	tv.tv_usec = TIMEOUT_US;
+	evtimer_set(&ctx->timeout, evt_timeout, ctx);
+	evtimer_add(&ctx->timeout, &tv);
+}
+
+void xbee_unload_timeout(struct xbee_ctx *ctx)
+{
+	evtimer_del(&ctx->timeout);
+}
+
+static void
+evt_stdin_input(int s, short event, void *arg)
+{
+	char buf[2048];
+	int n;
+
+	n = read(s, buf, sizeof(buf));
+	if (n < 0) {
+		event_loopbreak();
+		return;
+	}
+
+	if (xbee_raw) {
+		int i;
+		for (i = 0; i < n; i++) {
+			/* ctrl-d is pressed, back in cmdline mode XXX */
+			if (buf[i] == 4) {
+				xbee_raw = 0;
+				n = i;
+				rdline_newline(&xbee_cl->rdl, xbee_cl->prompt);
+				break;
+			}
+		}
+		/* XXX bad hack */
+		if (buf[0] == '\n')
+			write(xbee_dev->fd, "\r\n", 2);
+		else
+			write(xbee_dev->fd, buf, n);
+		write(s, buf, n);
+
+	}
+	else {
+		if (cmdline_in(xbee_cl, buf, n) < 0) {
+			event_loopbreak();
+			return;
+		}
+	}
+}
+
+static void
+evt_xbee_input(int s, short event, void *arg)
+{
+	if (xbee_raw) {
+		char buf[2048];
+		int n;
+
+		n = read(s, buf, sizeof(buf));
+		if (n < 0)
+			return;
+
+		write(1, buf, n);
+	}
+	else {
+		xbee_read(xbee_dev);
+		xbee_process_queue(xbee_dev);
+	}
+}
+
+static void
+usage(const char *prgname)
+{
+	printf("%s [-s BAUD] -d DEVNAME\n"
+	       "  -s BAUD:      set baud rate of xbee device\n"
+	       "  -d DEVNAME:   xbee char device\n"
+	       "  -r:           start in raw mode\n"
+	       "\n",
+	       prgname);
+}
+
+/* Parse the argument given in the command line of the application */
+static int
+parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = {
+		{0, 0, 0, 0}
+	};
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "hd:s:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+
+		case 'h':
+			usage(prgname);
+			exit(0);
+			break;
+
+		case 'd':
+			xbee_devname = strdup(optarg);
+			break;
+
+		case 's':
+			xbee_baud = atoi(optarg);
+			break;
+
+		case 'r':
+			xbee_raw = 1;
+			break;
+
+		/* long options */
+		case 0:
+			/* if (!strcmp(lgopts[option_index].name, "option")) */
+			break;
+
+		default:
+			usage(prgname);
+			return -1;
+		}
+	}
+
+	if (xbee_devname == NULL) {
+		fprintf(stderr, "xbee device argument missing\n");
+		usage(prgname);
+		return -1;
+	}
+
+	if (argc != optind) {
+		printf("Invalid argument\n");
+		usage(prgname);
+		return -1;
+	}
+
+	ret = optind-1;
+
+	return ret;
+}
+
+int main(int argc, char **argv)
+{
+	struct termios oldterm, term;
+	int err = 0;
+	const char *homedir;
+	char xbeerc_path[256];
+
+	if (parse_args(argc, argv) < 0)
+		exit(1);
+
+	/* initializa libevent */
+	event_init();
+
+	/* initialize libxbee */
+	err = xbee_init();
+	if (err < 0)
+		return -1;
+
+	/* open xbee device */
+	xbee_dev = xbee_open(xbee_devname, xbee_baud);
+	if (xbee_dev == NULL)
+		return -1;
+
+	/* register default channel with a callback */
+	if (xbee_register_channel(xbee_dev, XBEE_DEFAULT_CHANNEL,
+				  xbee_rx, NULL) < 0) {
+		fprintf(stderr, "cannot register default channel\n");
+		return -1;
+	}
+
+	/* add read event on xbee device */
+	event_set(&xbee_read_event, xbee_dev->fd, EV_READ | EV_PERSIST,
+		  evt_xbee_input, xbee_dev);
+	event_add(&xbee_read_event, NULL);
+
+	/* set terminal in raw mode */
+	tcgetattr(0, &oldterm);
+	memcpy(&term, &oldterm, sizeof(term));
+	term.c_lflag &= ~(ICANON | ECHO | ISIG);
+	tcsetattr(0, TCSANOW, &term);
+	setbuf(stdin, NULL);
+
+	/* parse the config file */
+	homedir = getenv("HOME");
+	if (homedir != NULL) {
+		snprintf(xbeerc_path, sizeof(xbeerc_path), "%s/.xbeerc",
+			 homedir);
+		if (access(xbeerc_path, R_OK) == 0) {
+
+			xbee_cl = cmdline_file_new(&main_ctx, "xbeerc> ",
+					      "/home/zer0/.xbeerc", 1);
+			if (xbee_cl != NULL) {
+				cmdline_interact(xbee_cl);
+				cmdline_free(xbee_cl);
+				printf("\nconfig file parsed\n");
+			}
+		}
+	}
+
+	/* create a new cmdline instance */
+	xbee_cl = cmdline_stdin_new(&main_ctx, "xbee> ");
+	if (xbee_cl == NULL) {
+		err = 1;
+		goto fail;
+	}
+
+	/* load read event on stdin */
+	event_set(&stdin_read_event, 0, EV_READ | EV_PERSIST,
+		  evt_stdin_input, xbee_cl);
+	event_add(&stdin_read_event, NULL);
+
+	/* libevent main loop */
+	event_dispatch();
+
+	tcsetattr(0, TCSANOW, &oldterm);
+	return 0;
+
+ fail:
+	tcsetattr(0, TCSANOW, &oldterm);
+	return err;
+}
diff --git a/main.h b/main.h
new file mode 100644
index 0000000..548cc62
--- /dev/null
+++ b/main.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+enum xbee_ctx_type {
+	SEND_MSG,
+	ATCMD,
+	ATCMD_RMT,
+	DUMP_CONF,
+};
+
+/* used for timeouts and xbee rx callback */
+struct xbee_ctx {
+	enum xbee_ctx_type type;
+	int have_more_command;
+	int foreground;
+	int channel;
+	struct event timeout;
+	struct xbee_atcmd *atcmd_query;
+};
+
+extern cmdline_parse_ctx_t main_ctx;
+extern struct xbee_dev *xbee_dev;
+extern int xbee_raw;
+extern int xbee_hexdump;
+extern int xbee_debug;
+extern FILE *xbee_logfile;
+
+#define XBEE_LOG_FILE "/home/zer0/xbee.log"
+extern FILE *xbee_log_file;
+
+void xbeeapp_log(int always_on_stdout, const char *fmt, ...);
+
+void xbee_rx(struct xbee_dev *dev, int channel, int type,
+	     void *frame, unsigned len, void *opaque);
+int xbeeapp_send_atcmd(const char *atcmd_str, void *param, unsigned param_len,
+		       int foreground);
+int xbeeapp_send_msg(uint64_t addr, void *data, unsigned data_len,
+		     int foreground);
+int xbeeapp_dump_config(const char *filename);
+
+void xbee_stdin_enable(void);
+void xbee_stdin_disable(void);
+
+void xbee_load_timeout(struct xbee_ctx *ctx);
+void xbee_unload_timeout(struct xbee_ctx *ctx);
diff --git a/parse_atcmd.c b/parse_atcmd.c
new file mode 100644
index 0000000..ffde312
--- /dev/null
+++ b/parse_atcmd.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse.h>
+
+#include "xbee_atcmd.h"
+#include "parse_atcmd.h"
+
+/* This file is an example of extension of libcmdline. It provides an
+ * example of named objects stored in a list, supporting the
+ * completion on objects name */
+
+static int
+parse_atcmd(cmdline_parse_token_hdr_t *tk, const char *buf, void *res,
+	    unsigned ressize)
+{
+	struct token_atcmd *tk2 = (struct token_atcmd *)tk;
+	struct token_atcmd_data *tkd = &tk2->atcmd_data;
+	struct xbee_atcmd *cmd;
+
+	if (res && ressize < sizeof(struct object *))
+		return -1;
+
+	/* XXX should be related to an xbee device */
+	cmd = xbee_atcmd_lookup_desc(buf);
+	if (cmd == NULL)
+		return -1;
+
+	/* command must match flags */
+	if ((cmd->flags & tkd->atcmd_mask) != tkd->atcmd_flags)
+		return -1;
+
+	/* store the address of object in structure */
+	if (res)
+		*(struct xbee_atcmd **)res = cmd;
+
+	return 0;
+}
+
+static int
+complete_atcmd_start(cmdline_parse_token_hdr_t *tk,
+		     __attribute__((unused)) const char *tokstr,
+		     void **opaque)
+{
+	struct xbee_atcmd *cmd;
+
+	cmd = &xbee_atcmd_list[0];
+	*opaque = (void *)cmd;
+	return 0;
+}
+
+static int
+complete_atcmd_iterate(cmdline_parse_token_hdr_t *tk, void **opaque,
+		       char *dstbuf, unsigned int size)
+{
+	struct token_atcmd *tk2 = (struct token_atcmd *)tk;
+	struct token_atcmd_data *tkd = &tk2->atcmd_data;
+	struct xbee_atcmd *cmd;
+	int len;
+
+	cmd = *opaque;
+
+	while (1) {
+		if (cmd->name == NULL)
+			return -1;
+
+		/* skip commands that don't match flags */
+		if ((cmd->flags & tkd->atcmd_mask) != tkd->atcmd_flags) {
+			cmd++;
+			continue;
+		}
+
+		len = snprintf(dstbuf, size, "%s", cmd->desc);
+		if (len < 0 || len >= size)
+			return -1;
+
+		strcpy(dstbuf, cmd->desc);
+		break;
+	}
+	cmd++;
+	*opaque = cmd;
+	return 0;
+}
+
+static int
+cmdline_help_atcmd(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+		   unsigned int size)
+{
+	snprintf(dstbuf, size, "ATCMD");
+	return 0;
+}
+
+struct cmdline_token_ops token_atcmd_ops = {
+	.parse = parse_atcmd,
+	.complete_start = complete_atcmd_start,
+	.complete_iterate = complete_atcmd_iterate,
+	.complete_end = NULL,
+	.help = cmdline_help_atcmd,
+};
diff --git a/parse_atcmd.h b/parse_atcmd.h
new file mode 100644
index 0000000..657e7ec
--- /dev/null
+++ b/parse_atcmd.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PARSE_ATCMD_H_
+#define _PARSE_ATCMD_H_
+
+#include <cmdline_parse.h>
+
+struct token_atcmd_data {
+	struct xbee_dev **xbee_dev;
+	unsigned atcmd_flags;
+	unsigned atcmd_mask;
+};
+
+struct token_atcmd {
+	struct cmdline_token_hdr hdr;
+	struct token_atcmd_data atcmd_data;
+};
+typedef struct token_atcmd parse_token_atcmd_t;
+
+extern struct cmdline_token_ops token_atcmd_ops;
+
+#define TOKEN_ATCMD_INITIALIZER(structure, field, dev, flags, mask)	\
+{									\
+	.hdr = {							\
+		.ops = &token_atcmd_ops,				\
+		.offset = offsetof(structure, field),			\
+	},								\
+	.atcmd_data = {							\
+		.xbee_dev = dev,					\
+		.atcmd_flags = flags,					\
+		.atcmd_mask = mask,					\
+	},								\
+}
+
+#endif /* _PARSE_ATCMD_H_ */
diff --git a/parse_monitor.c b/parse_monitor.c
new file mode 100644
index 0000000..19ecd0b
--- /dev/null
+++ b/parse_monitor.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse.h>
+
+#include "parse_monitor.h"
+
+static int
+parse_monitor(cmdline_parse_token_hdr_t *tk, const char *buf, void *res,
+	       unsigned ressize)
+{
+	struct token_monitor *tk2 = (struct token_monitor *)tk;
+	struct token_monitor_data *tkd = &tk2->monitor_data;
+	struct monitor_reg *m;
+
+	if (res && ressize < sizeof(struct monitor_reg *))
+		return -1;
+
+	LIST_FOREACH(m, tkd->list, next) {
+		if (strcmp(buf, m->desc))
+			continue;
+		break;
+	}
+	if (m == NULL) /* not found */
+		return -1;
+
+	/* store the address of object in structure */
+	if (res)
+		*(struct monitor_reg **)res = m;
+
+	return 0;
+}
+
+static int
+complete_monitor_start(cmdline_parse_token_hdr_t *tk,
+			__attribute__((unused)) const char *tokstr,
+			void **opaque)
+{
+	struct token_monitor *tk2 = (struct token_monitor *)tk;
+	struct token_monitor_data *tkd = &tk2->monitor_data;
+	struct monitor_reg *m;
+
+	m = LIST_FIRST(tkd->list);
+	*opaque = (void *)m;
+	if (m == NULL)
+		return -1; /* no completion */
+	return 0;
+}
+
+static int
+complete_monitor_iterate(cmdline_parse_token_hdr_t *tk, void **opaque,
+			  char *dstbuf, unsigned int size)
+{
+	struct monitor_reg *m;
+	int len;
+
+	m = *opaque;
+	if (m == NULL)
+		return -1;
+
+	len = snprintf(dstbuf, size, "%s", m->desc);
+	if (len < 0 || len >= size)
+		return -1;
+
+	strcpy(dstbuf, m->desc);
+	m = LIST_NEXT(m, next);
+	*opaque = m;
+	return 0;
+}
+
+
+static int
+cmdline_help_monitor(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+		      unsigned int size)
+{
+	snprintf(dstbuf, size, "Monitor-register");
+	return 0;
+}
+
+struct cmdline_token_ops token_monitor_ops = {
+	.parse = parse_monitor,
+	.complete_start = complete_monitor_start,
+	.complete_iterate = complete_monitor_iterate,
+	.complete_end = NULL,
+	.help = cmdline_help_monitor,
+};
diff --git a/parse_monitor.h b/parse_monitor.h
new file mode 100644
index 0000000..2f6ab62
--- /dev/null
+++ b/parse_monitor.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PARSE_MONITOR_H_
+#define _PARSE_MONITOR_H_
+
+#include <sys/queue.h>
+#include <cmdline_parse.h>
+
+struct monitor_reg {
+	LIST_ENTRY(monitor_reg) next;
+	const char *desc;
+	const char *atcmd;
+};
+
+LIST_HEAD(monitor_reg_list, monitor_reg);
+
+/* data is a pointer to a list */
+struct token_monitor_data {
+	struct monitor_reg_list *list;
+};
+
+struct token_monitor {
+	struct cmdline_token_hdr hdr;
+	struct token_monitor_data monitor_data;
+};
+typedef struct token_monitor parse_token_monitor_t;
+
+extern struct cmdline_token_ops token_monitor_ops;
+
+#define TOKEN_MONITOR_INITIALIZER(structure, field, monitor_ptr)  \
+{								    \
+	.hdr = {						    \
+		.ops = &token_monitor_ops,			    \
+		.offset = offsetof(structure, field),		    \
+	},							    \
+		.monitor_data = {				    \
+		.list = monitor_ptr,				    \
+	},							    \
+}
+
+#endif /* _PARSE_MONITOR_H_ */
diff --git a/parse_neighbor.c b/parse_neighbor.c
new file mode 100644
index 0000000..0cba0ea
--- /dev/null
+++ b/parse_neighbor.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <cmdline_parse.h>
+
+#include "xbee_neighbor.h"
+#include "xbee_atcmd.h"
+#include "xbee_stats.h"
+#include "xbee_buf.h"
+#include "xbee_proto.h"
+#include "xbee.h"
+
+#include "parse_neighbor.h"
+
+static int
+parse_neighbor(cmdline_parse_token_hdr_t *tk, const char *buf, void *res,
+	       unsigned ressize)
+{
+	struct token_neighbor *tk2 = (struct token_neighbor *)tk;
+	struct token_neighbor_data *tkd = &tk2->neighbor_data;
+	struct xbee_dev *dev = *tkd->xbee_dev;
+	struct xbee_neigh *neigh;
+
+	if (res && ressize < sizeof(struct xbee_neigh *))
+		return -1;
+
+	neigh = xbee_neigh_lookup(dev, buf);
+	if (neigh == NULL) /* not found */
+		return -1;
+
+	/* store the address of xbee_neigh in structure */
+	if (res)
+		*(struct xbee_neigh **)res = neigh;
+
+	return 0;
+}
+
+static int
+complete_neighbor_start(cmdline_parse_token_hdr_t *tk,
+			__attribute__((unused)) const char *tokstr,
+			void **opaque)
+{
+	struct token_neighbor *tk2 = (struct token_neighbor *)tk;
+	struct token_neighbor_data *tkd = &tk2->neighbor_data;
+	struct xbee_dev *dev = *tkd->xbee_dev;
+	struct xbee_neigh *neigh;
+
+	neigh = LIST_FIRST(&dev->neigh_list);
+	*opaque = (void *)neigh;
+	if (neigh == NULL)
+		return -1; /* no completion */
+	return 0;
+}
+
+static int
+complete_neighbor_iterate(cmdline_parse_token_hdr_t *tk, void **opaque,
+			  char *dstbuf, unsigned int size)
+{
+	struct xbee_neigh *neigh;
+	int len;
+
+	neigh = *opaque;
+	if (neigh == NULL)
+		return -1;
+
+	len = snprintf(dstbuf, size, "%s", neigh->name);
+	if (len < 0 || len >= size)
+		return -1;
+
+	strcpy(dstbuf, neigh->name);
+	neigh = LIST_NEXT(neigh, next);
+	*opaque = neigh;
+	return 0;
+}
+
+
+static int
+cmdline_help_neighbor(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+		      unsigned int size)
+{
+	snprintf(dstbuf, size, "Neighbor");
+	return 0;
+}
+
+struct cmdline_token_ops token_neighbor_ops = {
+	.parse = parse_neighbor,
+	.complete_start = complete_neighbor_start,
+	.complete_iterate = complete_neighbor_iterate,
+	.complete_end = NULL,
+	.help = cmdline_help_neighbor,
+};
diff --git a/parse_neighbor.h b/parse_neighbor.h
new file mode 100644
index 0000000..bdf84d9
--- /dev/null
+++ b/parse_neighbor.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PARSE_NEIGHBOR_H_
+#define _PARSE_NEIGHBOR_H_
+
+struct token_neighbor_data {
+	struct xbee_dev **xbee_dev;
+};
+
+struct token_neighbor {
+	struct cmdline_token_hdr hdr;
+	struct token_neighbor_data neighbor_data;
+};
+typedef struct token_neighbor parse_token_neighbor_t;
+
+extern struct cmdline_token_ops token_neighbor_ops;
+
+#define TOKEN_NEIGHBOR_INITIALIZER(structure, field, dev)		\
+	{								\
+	.hdr = {							\
+		.ops = &token_neighbor_ops,				\
+		.offset = offsetof(structure, field),			\
+	},								\
+	.neighbor_data = {						\
+		.xbee_dev = dev,					\
+	},								\
+}
+
+#endif /* _PARSE_NEIGHBOR_H_ */
diff --git a/xbee.c b/xbee.c
new file mode 100644
index 0000000..c4e8062
--- /dev/null
+++ b/xbee.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include "xbee_neighbor.h"
+#include "xbee_stats.h"
+#include "xbee_buf.h"
+#include "xbee_proto.h"
+#include "xbee.h"
+
+int xbee_init(void)
+{
+	return 0;
+}
+
+static int xbee_settermios(struct xbee_dev *dev)
+{
+	struct termios term;
+
+	memset(&term, 0, sizeof(term));
+	term.c_cflag = CREAD | HUPCL;
+
+	switch (dev->baud) {
+		case 50: term.c_cflag |= B50; break;
+		case 75: term.c_cflag |= B75; break;
+		case 110: term.c_cflag |= B110; break;
+		case 134: term.c_cflag |= B134; break;
+		case 150: term.c_cflag |= B150; break;
+		case 200: term.c_cflag |= B200; break;
+		case 300: term.c_cflag |= B300; break;
+		case 600: term.c_cflag |= B600; break;
+		case 1200: term.c_cflag |= B1200; break;
+		case 1800: term.c_cflag |= B1800; break;
+		case 2400: term.c_cflag |= B2400; break;
+		case 4800: term.c_cflag |= B4800; break;
+		case 9600: term.c_cflag |= B9600; break;
+		case 19200: term.c_cflag |= B19200; break;
+		case 38400: term.c_cflag |= B38400; break;
+		case 57600: term.c_cflag |= B57600; break;
+		case 115200: term.c_cflag |= B115200; break;
+		case 230400: term.c_cflag |= B230400; break;
+		default:
+			fprintf(stderr, "invalid baudrate\n");
+			return -1;
+	}
+
+	/* ignore modem control lines */
+	term.c_cflag |= CLOCAL;
+
+	/* non-canonical mode */
+	term.c_lflag &= ~ICANON;
+
+	/* minimum number of chars and minimum timeout for non-canonical read */
+	term.c_cc[VMIN] = 1;
+	term.c_cc[VTIME] = 0;
+
+#if 0
+	/* XXX later */
+	switch (dev->parity) {
+		case PARITY_ODD: term.c_cflag |= PARENB | PARODD; break;
+		case PARITY_EVEN: term.c_cflag |= PARENB; break;
+		default:		break;
+	}
+
+	if (twostopb)
+		term.c_cflag |= CSTOPB;
+
+	if (software)
+		term.c_iflag |= IXON | IXOFF;
+	if (hardware)
+		term.c_cflag |= CRTSCTS;
+
+#endif
+
+	if (tcsetattr(dev->fd, TCSAFLUSH, &term) < 0) {
+		fprintf(stderr, "ERROR: tcsetattr(TCSAFLUSH) failed: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	return(0);
+}
+
+int xbee_register_channel(struct xbee_dev *dev, int channel,
+			  xbee_rx_cb_t *rx_cb, void *opaque)
+{
+	/* user asked for any channel */
+	if (channel == XBEE_CHANNEL_ANY) {
+		int ch;
+
+		/* skip XBEE_DEFAULT_CHANNEL == 0 */
+		for (ch = 1; ch < XBEE_MAX_CHANNEL; ch++) {
+			if (dev->channel[ch].registered == 0) {
+				channel = ch;
+				break;
+			}
+		}
+		/* no available channels */
+		if (channel == XBEE_CHANNEL_ANY)
+			return -1;
+	}
+	/* user requested a specific channel */
+	else if (channel < 0 || channel >= XBEE_MAX_CHANNEL ||
+		 dev->channel[channel].registered == 1)
+		return -1; /* not available */
+
+	dev->channel[channel].registered = 1;
+	dev->channel[channel].rx_cb = rx_cb;
+	dev->channel[channel].arg = opaque;
+	return channel;
+}
+
+int xbee_unregister_channel(struct xbee_dev *dev, int channel)
+{
+	if (channel < 0 || channel >= XBEE_MAX_CHANNEL ||
+	    dev->channel[channel].registered == 0)
+		return -1;
+	dev->channel[channel].registered = 0;
+	dev->channel[channel].rx_cb = NULL;
+	dev->channel[channel].arg = NULL;
+	return 0;
+}
+
+struct xbee_dev *xbee_open(const char *devname, unsigned baudrate)
+{
+	struct xbee_dev *dev;
+	int fd;
+
+	/* allocate structure for xbee device */
+	dev = malloc(sizeof(*dev));
+	if (dev == NULL) {
+		fprintf(stderr, "not enough memory\n");
+		return NULL;
+	}
+	memset(dev, 0, sizeof(*dev));
+
+	/* open the serial port */
+	fd = open(devname, O_NONBLOCK|O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "cannot open %s: %s\n", devname,
+			strerror(errno));
+		free(dev);
+		return NULL;
+	}
+
+	/* set termios */
+	dev->baud = baudrate;
+	dev->fd = fd;
+	if (xbee_settermios(dev) < 0) {
+		close(fd);
+		free(dev);
+		return NULL;
+	}
+
+	dev->name = strdup(devname);
+	xbee_bufq_init(&dev->queue);
+	xbee_neigh_init(dev);
+
+	return dev;
+}
+
+/* read data from device fd and put it in queue */
+int xbee_read(struct xbee_dev *dev)
+{
+	struct xbee_buf *xbuf;
+	int n, tailroom, total = 0;
+	char *data;
+
+	do {
+		/* get an xbuf to store data */
+		xbuf = xbee_bufq_last(&dev->queue);
+		if (xbuf == NULL || xbee_buf_tailroom(xbuf) == 0) {
+			xbuf = xbee_buf_alloc();
+
+			if (xbuf == NULL) {
+				fprintf(stderr, "FATAL: cannot allocate buffer\n");
+				return -1;
+			}
+
+			/* put the xbuf in queue */
+			xbee_buf_enqueue(&dev->queue, xbuf);
+		}
+
+		/* read data from char device */
+		tailroom = xbee_buf_tailroom(xbuf);
+		data = xbee_buf_tail(xbuf);
+		n = read(dev->fd, data, tailroom);
+
+		/* error in read */
+		if (n < 0) {
+			fprintf(stderr, "read() failed: %s\n", strerror(errno));
+			return -1;
+		}
+
+		/* update queue len and xbuf len */
+		xbee_bufq_append(&dev->queue, n);
+
+		total += n;
+
+	} while (n == tailroom);
+
+	return total;
+}
+
+/* process all data in queue */
+int xbee_process_queue(struct xbee_dev *dev)
+{
+	char buf[XBEE_MAX_FRAME_LEN];
+	int len, ret;
+
+	while (1) {
+		len = xbee_proto_get_frame(dev, buf, sizeof(buf));
+		if (len == 0) /* no more frame */
+			break;
+
+		if (len < 0) {
+			/* a frame was dropped... */
+			fprintf(stderr, "xbee_proto_get_frame() failed\n");
+			continue;
+		}
+
+		ret = xbee_proto_parse_frame(dev, buf, len);
+		if (ret < 0) {
+			fprintf(stderr, "xbee_proto_parse_frame() failed\n");
+			continue;
+		}
+	}
+
+	return 0;
+}
diff --git a/xbee.h b/xbee.h
new file mode 100644
index 0000000..e1cfbbd
--- /dev/null
+++ b/xbee.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Callback when receiving data on a specific channel. The arguments
+ * of the function are the xbee device, the channel ID, the type of
+ * the frame (example: XBEE_TYPE_ATRESP), the pointer to the frame,
+ * the length of the frame, and an opaque pointer (reserved for user) */
+typedef void (xbee_rx_cb_t)(struct xbee_dev *, int, int, void *,
+			    unsigned, void *);
+
+/* an xbee queue */
+struct xbee_channel {
+	int registered;
+	xbee_rx_cb_t *rx_cb;
+	void *arg;
+};
+
+#define XBEE_DEFAULT_CHANNEL 0
+#define XBEE_MAX_CHANNEL 16
+#define XBEE_CHANNEL_ANY XBEE_MAX_CHANNEL
+
+/* structure identifying a xbee device */
+struct xbee_dev {
+	const char *name;
+	int fd;
+	unsigned baud;
+	struct xbee_bufq queue;
+	struct xbee_channel channel[XBEE_MAX_CHANNEL];
+	struct xbee_stats stats;
+	struct xbee_neigh_list neigh_list;
+};
+
+/* initialize xbee library */
+int xbee_init(void);
+
+/* open an xbee device */
+struct xbee_dev *xbee_open(const char *devname, unsigned baudrate);
+
+/* closes an xbee device */
+int xbee_close(struct xbee_dev *dev);
+
+/* Register a channel, return the ID of the channel or a negative
+ * value on error. The rx_cb is a pointer to a function that will be
+ * called by xbee_read() when a frame is received for this channel. If
+ * rx_cb is NULL, no callback will occur. The "channel" argument can
+ * be XBEE_CHANNEL_ANY to let the library choose the channel, or a
+ * channel number to request a specific one. */
+int xbee_register_channel(struct xbee_dev *dev, int channel,
+			  xbee_rx_cb_t *rx_cb, void *opaque);
+
+/* Unregister a channel, return 0 on success */
+int xbee_unregister_channel(struct xbee_dev *dev, int channel_id);
+
+/* read data from device fd and put it in queue */
+int xbee_read(struct xbee_dev *dev);
+
+/* process all data in queue */
+int xbee_process_queue(struct xbee_dev *dev);
diff --git a/xbee_atcmd.c b/xbee_atcmd.c
new file mode 100644
index 0000000..91238f7
--- /dev/null
+++ b/xbee_atcmd.c
@@ -0,0 +1,684 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "xbee_atcmd.h"
+
+struct xbee_atcmd xbee_atcmd_list[] = {
+	{
+		"WR",
+		"write-param",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Write parameter values to non-volatile memory.",
+	},
+	{
+		"RE",
+		"restore-defaults",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Restore module parameters to factory defaults.",
+	},
+	{
+		"FR",
+		"soft-reset",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Software Reset. Responds with 'OK' then performs a "
+		"reset 100ms later.",
+	},
+	{
+		"AC",
+		"apply-changes",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Apply Changes without exiting command mode.",
+	},
+	{
+		"R1",
+		"restore-compiled",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Restore module parameters to compiled defaults.",
+	},
+	{
+		"VL",
+		"version-long",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Shows detailed version information including"
+		"application build date and time.",
+	},
+	{
+		"DH",
+		"dst-addr-high",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Upper 32 bits of the 64-bit destination address (0 "
+		"to 0xFFFFFFFF, default is 0x0000FFFF).",
+	},
+	{
+		"DL",
+		"dst-addr-low",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Lower 32 bits of the 64-bit destination address (0 "
+		"to 0xFFFFFFFF, default is 0x0000FFFF).",
+	},
+	{
+		"DD",
+		"device-type-id",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ,
+		"Device Type Identifier, it can be used to differentiate "
+		"multiple XBee-based products (0 to 0xFFFFFFFF, read-only, "
+		"default is 0x40000).",
+	},
+	{
+		"SH",
+		"src-addr-high",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ,
+		"Upper 32 bits of the 64-bit source address (read-only).",
+	},
+	{
+		"SL",
+		"src-addr-low",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ,
+		"Lower 32 bits of the 64-bit source address (read-only).",
+	},
+	{
+		"SE",
+		"src-endpoint",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"The application source endpoint for all data transmissions "
+		"(0 to 0xFF, default is 0xE8).",
+	},
+	{
+		"DE",
+		"dst-endpoint",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"The application destination endpoint for all data "
+		"transmissions (0 to 0xFF, default is 0xE8).",
+	},
+	{
+		"CI",
+		"cluster-id",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Cluster Identifier for all data transmissions (0 to 0xFFFF, "
+		"default is 0x11).",
+	},
+	{
+		"NP",
+		"max-rf-payload",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ,
+		"Maximum RF Payload Bytes that can be sent in a unicast "
+		"transmission based on the current configuration (0 to "
+		"0xFFFF).",
+	},
+	{
+		"CE",
+		"coord-end-device",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Coordinator/End Device, messaging mode of the module "
+		"(0 - Normal, 1 - Indirect coordinator, 2 - Polling, default "
+		"is 0).",
+	},
+	{
+		"AP",
+		"api-mode",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"API mode (0 - off, 1 - on, 2 - on with escape sequences).",
+	},
+	{
+		"AO",
+		"api-output-format",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"API Output Format (0 - standard [0x90 for RX], 1 - explicit "
+		"addressing [0x91 for RX]).",
+	},
+	{
+		"BD",
+		"baud-rate",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Baud rate of serial interface (0-8 select preset standard "
+		"rates, and 0x39 to 0x1c9c38 select baud rate).",
+	},
+	{
+		"RO",
+		"packetization-timeout",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Packetization Timeout: the inter-character silence required "
+		"before packetization specified in character times (0 to 0xFF, "
+		"default is 3).",
+	},
+	{
+		"FT",
+		"flow-control-thres",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Flow Control Threshhold. De-assert CTS and/or send XOFF when "
+		"FT bytes are in the UART receive buffer. Re-assert CTS when "
+		"less than FT - 16 bytes are in the UART receive buffer (0x11 "
+		"to 0xEE, default is 0xBE).",
+	},
+	{
+		"NB",
+		"parity",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Parity (0 - no parity, 1 - even parity, 2 - odd parity, 3 - "
+		"forced high parity, 4 - forced low parity). Default is 0.",
+	},
+	{
+		"D7",
+		"dio7",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"DIO7 Configuration (0 - unmonitored input, 1 - CTS, 3 - "
+		"digital input, 4 - digital output low, 5 - digital output "
+		"high, 6 - RS-485 low Tx, 7 -  RS-485 high Tx). Default is "
+		"0.",
+	},
+	{
+		"D6",
+		"dio6",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"DIO6 Configuration (0 - unmonitored input, 1 - RTS, 3 - "
+		"digital input, 4 - digital output low, 5 - digital output "
+		"high). Default is 0.",
+	},
+	{
+		"P0",
+		"dio10-pwm0",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"DIO10/PWM0 Configuration. (0 - unmonitored input, 1 - RSSI, 2 "
+		"- PWM0, 3 - digital input, 4 - digital output low, 5 - "
+		"digital output high). Default is 1.",
+	},
+	{
+		"P1",
+		"dio11-pwm1",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"DIO11/PWM1 Configuration. (0 - unmonitored input, 2 "
+		"- PWM1, 3 - digital input, 4 - digital output low, 5 - "
+		"digital output high). Default is 0.",
+	},
+	{
+		"P2",
+		"dio12",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"DIO12 Configuration. (0 - unmonitored input, "
+		"3 - digital input, 4 - digital output low, 5 - "
+		"digital output high). Default is 0.",
+	},
+	{
+		"RP",
+		"rssi-pwm",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Time RSSI signal will be output after last transmission. "
+		"When RP = 0xFF, output will always be on (0 - 0xFF, default "
+		"is 0x28 = 4 seconds).",
+	},
+	{
+		"1S",
+		"sensor-sample",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Forces a sample to be taken on an XBee Sensor device.",
+	},
+	{
+		"D0",
+		"dio0-ad0",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"AD0/DIO0 Configuration. (0 - unmonitored input, 1 - "
+		"commission button enabled, 2 - analog input, 3 - digital "
+		"input, 4 - digital output low, 5 - digital output high). "
+		"Default is 1.",
+	},
+	{
+		"D1",
+		"dio1-ad1",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"AD1/DIO1 Configuration. (0 - unmonitored input, "
+		"2 - analog input, 3 - digital input, 4 - digital output "
+		"low, 5 - digital output high). Default is 0.",
+	},
+	{
+		"D2",
+		"dio2-ad2",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"AD2/DIO2 Configuration. (0 - unmonitored input, "
+		"2 - analog input, 3 - digital input, 4 - digital output "
+		"low, 5 - digital output high). Default is 0.",
+	},
+	{
+		"D3",
+		"dio3-ad3",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"AD3/DIO3 Configuration. (0 - unmonitored input, "
+		"2 - analog input, 3 - digital input, 4 - digital output "
+		"low, 5 - digital output high). Default is 0.",
+	},
+	{
+		"D4",
+		"dio4-ad4",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"AD4/DIO4 Configuration. (0 - unmonitored input, "
+		"2 - analog input, 3 - digital input, 4 - digital output "
+		"low, 5 - digital output high). Default is 0.",
+	},
+	{
+		"D5",
+		"dio5-ad5",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"AD4/DIO4 Configuration. (0 - unmonitored input, 1 - LED, "
+		"2 - analog input, 3 - digital input, 4 - digital output "
+		"low, 5 - digital output high). Default is 1.",
+	},
+	{
+		"D8",
+		"dio8-sleep-rq",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"DIO8/SLEEP_RQ Configuration. (0 - unmonitored input, 1 - LED, "
+		"2 - analog input, 3 - digital input, 4 - digital output "
+		"low, 5 - digital output high). Default is 0. When used as "
+		"SLEEP_RQ, the D8 parameter should be configured in mode 0 "
+		"or 3.",
+	},
+	{
+		"D9",
+		"dio9-on-sleep",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"DIO9/ON_SLEEP Configuration. (0 - unmonitored input, 1 - "
+		"ON/SLEEP, 2 - analog input, 3 - digital input, 4 - digital "
+		"output low, 5 - digital output high). Default is ?.",
+	},
+	{
+		"PR",
+		"pull-up-resistor",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Pull-up Resistor. Bit field that configures the internal "
+		"pull-up resistors for the I/O lines (bit set = pull-up "
+		"enabled). Range is from 0 to 0x1FFF, default is 0x1FFF.",
+	},
+	{
+		"M0",
+		"pwm0-out-level",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"PWM0 Output Level. The line should be configured as a PWM "
+		"output using the P0 command (0 to 0x3FF, default is 0).",
+	},
+	{
+		"M1",
+		"pwm1-out-level",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"PWM1 Output Level. The line should be configured as a PWM "
+		"output using the P1 command (0 to 0x3FF, default is 0).",
+	},
+	{
+		"LT",
+		"led-blink-time",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Associate LED Blink Time (should be enabled through D5 "
+		"command). Range is 0x14-0xFF (x10ms), default is 0.",	},
+	{
+		"IS",
+		"force-sample",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Forces a read of all enabled digital and "
+		"analog input lines.",
+	},
+	{
+		"IC",
+		"digital-change-detect",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"I/O Digital Change Detection. If a pin is enabled as a "
+		"digital input/output, the IC command can be used to "
+		"force an immediate I/O sample transmission when the DIO "
+		"state changes. IC is a bitmask, range is 0 to 0xFFFF, "
+		"default is 0",
+	},
+	{
+		"IR",
+		"io-sample-rate",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"IO Sample Rate for periodic sampling. If zero, periodic "
+		"sampling is disabled. Else the value is in milliseconds "
+		"(range 0 to 0xFFFF, default is 0).",
+	},
+	{
+		"CB",
+		"comissioning-button",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Commissioning Pushbutton, simulate commissioning button "
+		"in software. The parameter value should be set to the number "
+		"of button presses to be simulated (range is 0 to 4).",
+	},
+	{
+		"VR",
+		"firmware-version",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ,
+		"Firmware version of the module (read only).",
+	},
+	{
+		"HV",
+		"hardware-version",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ,
+		"Hardware version of the module (read only)."
+	},
+	{
+		"CK",
+		"config-code",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ,
+		"Configuration Code, that can be used as a quick "
+		"check to determine if a node has been configured as "
+		"desired (read-only, 0-0xFFFFFFFF)."
+	},
+	{
+		"ER",
+		"rf-errors",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ,
+		"Number of times a packet was received which contained errors "
+		"of some sort. Read-only, saturate at 0xFFFF.",
+	},
+	{
+		"GD",
+		"good-packets",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ,
+		"Number of good received frames. Read-only, saturate at "
+		"0xFFFF.",
+	},
+	{
+		"RP",
+		"rssi-pwm-timer",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"RSSI PWM timer, the time in tenth of seconds that the RSSI "
+		"output indicating signal strength will remain active after "
+		"the last reception (1 to 0xff, default is 0x20 = 3.2 secs).",
+	},
+	{
+		"TR",
+		"tx-errors",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ,
+		"Transmission Errors, the number of MAC frames that "
+		"exhaust MAC retries without ever receiving a MAC "
+		"acknowledgement message. Read-only, saturate at 0xFFFF.",
+	},
+	{
+		"TP",
+		"temperature",
+		XBEE_ATCMD_F_PARAM_S16 | XBEE_ATCMD_F_READ,
+		"Temperature. Read module temperature in (tenths of ?) "
+		"Celsius. Negatives temperatures can be returned (read-only, "
+		"from 0xff74 [-140] to 0x0258 [600]).",
+	},
+	{
+		"DB",
+		"rx-signal-strength",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ,
+		"Received Signal Strength of the last received RF data "
+		"packet measured in -dBm. For example if DB returns 0x60, "
+		"then the RSSI of the last packet received was -96dBm "
+		"(read-only)."
+	},
+	{
+		"DC",
+		"duty-cycle",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ,
+		"Duty Cycle. Returns a current usage percentage of the "
+		"10% duty cycle measured over the period of 1 hour "
+		"(read-only, from 0 to 0x64).",
+	},
+	{
+		"RC",
+		"rssi-for-channel", //XXX in fact it is a read with a param
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_WRITE,
+		"Reads the dBm level (RSSI) of the designated "
+		"channel.",
+	},
+	{
+		"R#",
+		"reset-number",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ,
+		"Tells the reason for the last module reset (0 - Power up "
+		"reset, 2 - Watchdog reset, 3 - Software reset, 4 - Reset "
+		"line reset, 5 - Brownout reset). Read-only.",
+	},
+	{
+		"TA",
+		"tx-ack-errors",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ,
+		"Transmit Acknowlegement Errors. Incremented once for "
+		"each failed ack retry (read-only, from 0 to 0xFFFF).",
+	},
+	{
+		"%V",
+		"supply-voltage",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ,
+		"Voltage on the Vcc pin in mV (read-only, from 0 to 0xF00).",
+	},
+	{
+		"CT",
+		"cmd-mode-timeout",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Command Mode Timeout: the period of inactivity (no valid "
+		"commands received) after which the RF module automatically "
+		"exits AT Command Mode and returns to Idle Mode (2 to 0x1770, "
+		"default is 0x64).",
+	},
+	{
+		"CN",
+		"exit-cmd-mode",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Exit Command Mode.",
+	},
+	{
+		"GT",
+		"guard-times",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Guard Times: period of silence in ms before and after the "
+		"Command Sequence Characters of the AT Command Mode Sequence, "
+		"used to prevent inadvertent entrance into AT Command Mode "
+		"(0 to 0xFFFF, default is 0x3E8).",
+	},
+	{
+		"CC",
+		"command-chars",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Command Character used between guard times of the AT Command "
+		"Mode Sequence (0 to 0xFF, default is 0x2B).",
+	},
+	{
+		"ID",
+		"network-id",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Network ID. Nodes must have the same network identifier "
+		"to communicate (0 to 0x7FFF, default is 0x7FFF).",
+	},
+	{
+		"NT",
+		"ndisc-timeout",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Node Discover Timeout, time in tenth of secs a node will "
+		"spend discovering other nodes when ND or DN is issued (0 "
+		"to 0xFC, default is 0x82).",
+	},
+	{
+		"NI",
+		"node-id",
+		XBEE_ATCMD_F_PARAM_STRING_20B | XBEE_ATCMD_F_READ |
+		XBEE_ATCMD_F_WRITE,
+		"Node Identifier in printable ASCII characters. This string is "
+		"returned as part of the ATND (Network Discover) command. This "
+		"identifier is also used with the ATDN (Destination Node) "
+		"command. The string contains up to 20 byte ASCII string, "
+		"default is a space character.",
+	},
+	{
+		"DN",
+		"disc-node",
+		XBEE_ATCMD_F_PARAM_STRING_20B | XBEE_ATCMD_F_READ |
+		XBEE_ATCMD_F_WRITE,
+		/* XXX */
+		"Resolves a Node Identifier string to a physical address "
+		"(case sensitive). 0xFFFE and the 64bits extended address are "
+		"returned."
+	},
+	{
+		"ND",
+		"network-discover",
+		XBEE_ATCMD_F_PARAM_NONE | XBEE_ATCMD_F_WRITE,
+		"Network Discovery, see doc", /* XXX */
+	},
+	{
+		"NO",
+		"ndisc-options",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Network Discovery Options, a bitfield value that changes the "
+		"behavior of the ND command (bit0 - Append DD value, bit1 - "
+		"Local device sends ND response frame when ND is issued). "
+		"Default is 0."
+	},
+	{
+		"EE",
+		"security-enable",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Enable or disable 128-bit AES encryption (0 or 1, 0 is the "
+		"default).",
+	},
+	{
+		"KY", /* XXX */
+		"security-key",
+		XBEE_ATCMD_F_PARAM_HEXBUF_16B | XBEE_ATCMD_F_WRITE,
+		"The 128bits security key (the command is write-only).",
+	},
+	{
+		"MT",
+		"bcast-multi-xmit",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Number of additional MAC-level broadcast transmissions. All "
+		"broadcast packets are transmitted MT+1 times to ensure "
+		"it is received (0 to 0xF, default is 3).",
+	},
+	{
+		"RR",
+		"unicast-retries",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Number of additional MAC-level packet delivery attempts for "
+		"unicast transactions. If RR is non-zero, packets sent from "
+		"the radio will request an acknowledgement, and can be resent "
+		"up to RR times if no acknowledgement is received. (0 to 0xF, "
+		"default is 10).",
+	},
+	{
+		"PL",
+		"power-level",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Power Level of RF transmitter (0 - 1mW, 1 - 23mW, 2 - 100mW, "
+		"3 - 158 mW, 4 - 316 mW). Default is 4.",
+	},
+	{
+		"SM",
+		"sleep-mode",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Sleep Mode (0 - disabled, 1 - pin sleep, 4 - async cyclic "
+		"sleep, 5 - async cyclic sleep with pin wakeup). Default "
+		"is 0.",
+	},
+	{
+		"SO",
+		"sleep-options",
+		XBEE_ATCMD_F_PARAM_U8 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Sleep Options bitmask (bit8 - always wake for ST time). "
+		"Default is 0.",
+	},
+	{
+		"ST",
+		"wake-time",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Wake Time: the amount of time in ms that the module will stay "
+		"awake after receiving RF or serial data (from 0x45 to "
+		"0x36EE80, default is 0x7D0 = 2 secs).",
+	},
+	{
+		"SP",
+		"sleep-period",
+		XBEE_ATCMD_F_PARAM_U32 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Sleep Period: the amount of time in 10ms unit the module will "
+		"sleep per cycle.  For a node operating as an Indirect "
+		"Messaging Coordinator, this command defines the amount of "
+		"time that it will hold an indirect message for an end device. "
+		"The coordinator will hold the message for (2.5 * SP). Range "
+		"is from 1 to 1440000, default is 200 (2 secs).",
+	},
+	{
+		"SN",
+		"num-sleep-periods",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Number of Sleep Periods that must elapse between assertions "
+		"of the ON_SLEEP line during the wake time of asynchronous "
+		"cyclic sleep (1 to 0xFFFF, default is 1).",
+	},
+	{
+		"WH",
+		"wake-host",
+		XBEE_ATCMD_F_PARAM_U16 | XBEE_ATCMD_F_READ | XBEE_ATCMD_F_WRITE,
+		"Wake Host time. If it is set to a non-zero value, it "
+		"specifies the time in ms that the device should allow after "
+		"waking from sleep before sending data out the UART or "
+		"transmitting an I/O sample. If serial characters are "
+		"received, the WH timer is stopped immediately. Range is "
+		"from 0 to 0xFFFF, default is 0.",
+	},
+	{
+		NULL,
+		NULL,
+		0,
+		NULL,
+	},
+};
+
+struct xbee_atcmd *xbee_atcmd_lookup_name(const char *atcmd_str)
+{
+	struct xbee_atcmd *cmd;
+
+	for (cmd = &xbee_atcmd_list[0]; cmd->name != NULL; cmd++) {
+		if (strcmp(atcmd_str, cmd->name))
+			continue;
+		break;
+	}
+
+	if (cmd->name == NULL) /* not found */
+		return NULL;
+
+	return cmd;
+}
+
+struct xbee_atcmd *xbee_atcmd_lookup_desc(const char *desc)
+{
+	struct xbee_atcmd *cmd;
+
+	for (cmd = &xbee_atcmd_list[0]; cmd->desc != NULL; cmd++) {
+		if (strcmp(desc, cmd->desc))
+			continue;
+		break;
+	}
+
+	if (cmd->name == NULL) /* not found */
+		return NULL;
+
+	return cmd;
+}
diff --git a/xbee_atcmd.h b/xbee_atcmd.h
new file mode 100644
index 0000000..91a7e3d
--- /dev/null
+++ b/xbee_atcmd.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XBEE_ATCMD_H_
+#define _XBEE_ATCMD_H_
+
+#define XBEE_ATCMD_F_READ              0x001
+#define XBEE_ATCMD_F_WRITE             0x002
+#define XBEE_ATCMD_F_PARAM_NONE        0x004
+#define XBEE_ATCMD_F_PARAM_U8          0x008
+#define XBEE_ATCMD_F_PARAM_U16         0x010
+#define XBEE_ATCMD_F_PARAM_S16         0x020
+#define XBEE_ATCMD_F_PARAM_U32         0x040
+#define XBEE_ATCMD_F_PARAM_STRING_20B  0x080
+#define XBEE_ATCMD_F_PARAM_HEXBUF_16B  0x100
+
+/* list of xbee at commands */
+struct xbee_atcmd {
+	const char *name;
+	const char *desc;
+	unsigned int flags;
+	const char *help;
+};
+
+extern struct xbee_atcmd xbee_atcmd_list[];
+
+struct xbee_atcmd *xbee_atcmd_lookup_name(const char *atcmd_str);
+struct xbee_atcmd *xbee_atcmd_lookup_desc(const char *desc);
+
+#endif /* _xBEE_ATCMD_H_ */
diff --git a/xbee_buf.c b/xbee_buf.c
new file mode 100644
index 0000000..b33ed0b
--- /dev/null
+++ b/xbee_buf.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/queue.h>
+
+#include "xbee_neighbor.h"
+#include "xbee_stats.h"
+#include "xbee_buf.h"
+#include "xbee.h"
+
+struct xbee_buf *xbee_buf_alloc(void)
+{
+	struct xbee_buf *xbuf;
+
+	xbuf = malloc(sizeof(*xbuf));
+	if (xbuf == NULL)
+		return NULL;
+	memset(xbuf, 0, sizeof(*xbuf));
+	xbuf->offset = 0;
+	xbuf->len = 0;
+	return xbuf;
+}
+
+int xbee_buf_tailroom(struct xbee_buf *xbuf)
+{
+	return XBEE_BUF_SIZE - xbuf->len - xbuf->offset;
+}
+
+char *xbee_buf_data(struct xbee_buf *xbuf, unsigned off)
+{
+	if (off >= xbuf->len)
+		return NULL;
+	return xbuf->buf + xbuf->offset + off;
+}
+
+char *xbee_buf_head(struct xbee_buf *xbuf)
+{
+	return xbuf->buf + xbuf->offset;
+}
+
+char *xbee_buf_tail(struct xbee_buf *xbuf)
+{
+	return xbuf->buf + xbuf->offset + xbuf->len;
+}
+
+void xbee_buf_enqueue(struct xbee_bufq *q, struct xbee_buf *xbuf)
+{
+	CIRCLEQ_INSERT_TAIL(&q->xbq, xbuf, next);
+	q->len += xbuf->len;
+	q->nseg++;
+}
+
+struct xbee_buf *xbee_bufq_last(struct xbee_bufq *q)
+{
+	if (CIRCLEQ_EMPTY(&q->xbq))
+		return NULL;
+	return CIRCLEQ_LAST(&q->xbq);
+}
+
+void xbee_bufq_init(struct xbee_bufq *q)
+{
+	CIRCLEQ_INIT(&q->xbq);
+	q->len = 0;
+	q->nseg = 0;
+}
+
+void xbee_bufq_append(struct xbee_bufq *q, unsigned len)
+{
+	struct xbee_buf *xbuf;
+
+	q->len += len;
+	xbuf = CIRCLEQ_LAST(&q->xbq);
+	xbuf->len += len;
+}
+
+void xbee_bufq_flush(struct xbee_bufq *q)
+{
+	struct xbee_buf *xbuf;
+
+	while (!CIRCLEQ_EMPTY(&q->xbq)) {
+		xbuf = CIRCLEQ_FIRST(&q->xbq);
+		CIRCLEQ_REMOVE(&q->xbq, xbuf, next);
+		q->nseg --;
+		q->len -= xbuf->len;
+		free(xbuf);
+	}
+}
+
+char *xbee_bufq_data(struct xbee_bufq *q, unsigned off)
+{
+	struct xbee_buf *xbuf;
+	char *data = NULL;
+
+	if (off >= q->len)
+		return NULL;
+
+	CIRCLEQ_FOREACH(xbuf, &q->xbq, next) {
+		data = xbee_buf_data(xbuf, off);
+		if (data != NULL)
+			return data;
+		off -= xbuf->len;
+	}
+
+	return data;
+}
+
+/* drop data in front of queue */
+int xbee_bufq_drop(struct xbee_bufq *q, unsigned len)
+{
+	struct xbee_buf *xbuf;
+
+	if (len > q->len)
+		return -1;
+
+	while (!CIRCLEQ_EMPTY(&q->xbq)) {
+		xbuf = CIRCLEQ_FIRST(&q->xbq);
+		if (xbuf->len > len)
+			break;
+		CIRCLEQ_REMOVE(&q->xbq, xbuf, next);
+		len -= xbuf->len;
+		q->nseg --;
+		q->len -= xbuf->len;
+		free(xbuf);
+		xbuf = NULL;
+	}
+
+	if (xbuf != NULL) {
+		xbuf->len -= len;
+		xbuf->offset += len;
+		q->len -= len;
+	}
+
+	return 0;
+}
+
+int xbee_bufq_copy(struct xbee_bufq *q, void *buf, unsigned len)
+{
+	struct xbee_buf *xbuf;
+	unsigned dstoff = 0, copylen;
+
+	if (len > q->len)
+		return -1;
+
+	CIRCLEQ_FOREACH(xbuf, &q->xbq, next) {
+		copylen = len;
+		if (xbuf->len < len)
+			copylen = xbuf->len;
+		memcpy(buf + dstoff, xbuf->buf + xbuf->offset, copylen);
+		len -= copylen;
+		if (len == 0)
+			break;
+		dstoff += copylen;
+	}
+
+	return 0;
+}
+
diff --git a/xbee_buf.h b/xbee_buf.h
new file mode 100644
index 0000000..cecddcf
--- /dev/null
+++ b/xbee_buf.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define XBEE_BUF_SIZE 0x200
+
+/* a xbee data buffer */
+struct xbee_buf {
+	CIRCLEQ_ENTRY(xbee_buf) next;
+	unsigned offset;
+	unsigned len;
+	char buf[XBEE_BUF_SIZE];
+};
+
+/* queue of xbee_buf */
+CIRCLEQ_HEAD(xbufq, xbee_buf);
+
+struct xbee_bufq {
+	struct xbufq xbq;
+	unsigned len;
+	unsigned nseg;
+};
+
+/* allocate a new xbee_buf */
+struct xbee_buf *xbee_buf_alloc(void);
+
+/* return the number of remaining bytes in xbee_buf */
+int xbee_buf_tailroom(struct xbee_buf *xbuf);
+
+/* return the pointer to data at offset 'off', or NULL if off > xbuf->len */
+char *xbee_buf_data(struct xbee_buf *xbuf, unsigned off);
+
+/* return the first data of a xbuf (also works if len is 0) */
+char *xbee_buf_head(struct xbee_buf *xbuf);
+
+/* return the pointer just after data of a xbuf (also works if len is 0) */
+char *xbee_buf_tail(struct xbee_buf *xbuf);
+
+/* enqueue a xbuf in a xbufq */
+void xbee_buf_enqueue(struct xbee_bufq *q, struct xbee_buf *xbuf);
+
+
+
+/* return the last xbuf of a queue, or NULL if the queue is empty */
+struct xbee_buf *xbee_bufq_last(struct xbee_bufq *q);
+
+/* initialize a xbuf queue */
+void xbee_bufq_init(struct xbee_bufq *q);
+
+/* flush a xbuf queue */
+void xbee_bufq_flush(struct xbee_bufq *q);
+
+/* append data in queue (just update lens), user should memcpy first */
+void xbee_bufq_append(struct xbee_bufq *q, unsigned len);
+
+/* return the pointer to data at offset 'off', or NULL if off > q->len */
+char *xbee_bufq_data(struct xbee_bufq *q, unsigned off);
+
+/* drop data in front of queue */
+int xbee_bufq_drop(struct xbee_bufq *q, unsigned len);
+
+/* copy data in front of queue in a linear buffer */
+int xbee_bufq_copy(struct xbee_bufq *q, void *buf, unsigned len);
diff --git a/xbee_neighbor.c b/xbee_neighbor.c
new file mode 100644
index 0000000..3495f8b
--- /dev/null
+++ b/xbee_neighbor.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/queue.h>
+
+#include "xbee_neighbor.h"
+#include "xbee_atcmd.h"
+#include "xbee_stats.h"
+#include "xbee_buf.h"
+#include "xbee_proto.h"
+#include "xbee.h"
+
+void xbee_neigh_init(struct xbee_dev *dev)
+{
+	LIST_INIT(&dev->neigh_list);
+}
+
+struct xbee_neigh *xbee_neigh_lookup(struct xbee_dev *dev, const char *name)
+{
+	struct xbee_neigh *neigh;
+
+	LIST_FOREACH(neigh, &dev->neigh_list, next) {
+		if (strcmp(name, neigh->name))
+			continue;
+		break;
+	}
+
+	return neigh;
+}
+
+struct xbee_neigh *xbee_neigh_rlookup(struct xbee_dev *dev, uint64_t addr)
+{
+	struct xbee_neigh *neigh;
+
+	LIST_FOREACH(neigh, &dev->neigh_list, next) {
+		if (addr != neigh->addr)
+			continue;
+		break;
+	}
+
+	return neigh;
+}
+
+struct xbee_neigh *xbee_neigh_add(struct xbee_dev *dev, const char *name,
+				  uint64_t addr)
+{
+	struct xbee_neigh *neigh;
+
+	if (xbee_neigh_rlookup(dev, addr) != NULL)
+		return NULL;
+
+	if (xbee_neigh_lookup(dev, name) != NULL)
+		return NULL;
+
+	neigh = malloc(sizeof(*neigh));
+	if (neigh == NULL)
+		return NULL;
+
+	neigh->addr = addr;
+	snprintf(neigh->name, sizeof(neigh->name), "%s", name);
+	LIST_INSERT_HEAD(&dev->neigh_list, neigh, next);
+
+	return neigh;
+}
+
+void xbee_neigh_del(struct xbee_dev *dev, struct xbee_neigh *neigh)
+{
+	dev = dev; /* silent compiler */
+	LIST_REMOVE(neigh, next);
+	free(neigh);
+}
diff --git a/xbee_neighbor.h b/xbee_neighbor.h
new file mode 100644
index 0000000..0939a24
--- /dev/null
+++ b/xbee_neighbor.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XBEE_NEIGHBOR_H_
+#define _XBEE_NEIGHBOR_H_
+
+struct xbee_neigh {
+	LIST_ENTRY(xbee_neigh) next;
+	char name[21];
+	uint64_t addr;
+};
+
+struct xbee_dev;
+
+/* define struct xbee_neigh_list */
+LIST_HEAD(xbee_neigh_list, xbee_neigh);
+
+/* init neighbor list of an xbee device */
+void xbee_neigh_init(struct xbee_dev *dev);
+
+/* return a neighbor from its name */
+struct xbee_neigh *xbee_neigh_lookup(struct xbee_dev *dev, const char *name);
+
+/* return a neighbor from its address (in host order) */
+struct xbee_neigh *xbee_neigh_rlookup(struct xbee_dev *dev, uint64_t addr);
+
+/* add a neighbor */
+struct xbee_neigh *xbee_neigh_add(struct xbee_dev *dev, const char *name,
+				  uint64_t addr);
+
+/* del a neighbor from list */
+void xbee_neigh_del(struct xbee_dev *dev, struct xbee_neigh *neigh);
+
+#endif /* _XBEE_NEIGHBOR_H_ */
diff --git a/xbee_proto.c b/xbee_proto.c
new file mode 100644
index 0000000..3c694bc
--- /dev/null
+++ b/xbee_proto.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/queue.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+
+/* remove */
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "xbee_neighbor.h"
+#include "xbee_stats.h"
+#include "xbee_buf.h"
+#include "xbee_proto.h"
+#include "xbee.h"
+
+static void xbee_proto_drop_garbage(struct xbee_dev *dev)
+{
+	struct xbee_bufq *q = &dev->queue;
+	char *data;
+
+	/* drop all data != delimiter */
+	while ((data = xbee_bufq_data(q, 0))) {
+		/* no more data */
+		if (data == NULL)
+			break;
+
+		if (*data == XBEE_DELIMITER)
+			break;
+
+		dev->stats.rx_no_delim++;
+		xbee_bufq_drop(q, 1);
+	}
+}
+
+/* return negative on error, 0 if there is not frame, or framelen */
+int xbee_proto_get_frame(struct xbee_dev *dev, void *buf, unsigned len)
+{
+	uint16_t framelen;
+	struct xbee_hdr hdr;
+	struct xbee_bufq *q = &dev->queue;
+
+	xbee_proto_drop_garbage(dev);
+
+	if (xbee_bufq_copy(q, &hdr, sizeof(hdr)) < 0)
+		return 0;
+
+	framelen = ntohs(hdr.len);
+	framelen += 4; /* 1 for delimiter, 2 for len, 1 for cksum */
+
+	/* not enough data to read */
+	if (q->len < framelen)
+		return 0;
+
+	/* arf, provided buffer is to small */
+	if (framelen > len) {
+		fprintf(stderr, "drop packet, buffer too small\n");
+		dev->stats.rx_frame_too_large++;
+		xbee_bufq_drop(q, framelen);
+		return -1;
+	}
+
+	xbee_bufq_copy(q, buf, framelen);
+	xbee_bufq_drop(q, framelen);
+	return framelen;
+}
+
+/* return -1 if the frame is invalid */
+static int xbee_proto_parse_atresp(struct xbee_dev *dev, void *buf,
+				   unsigned len)
+{
+	struct xbee_atresp_hdr *atresp_hdr;
+
+	dev->stats.rx_atresp++;
+
+	if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_atresp_hdr)) {
+		dev->stats.rx_frame_too_small++;
+		return -1;
+	}
+
+	atresp_hdr = buf + sizeof(struct xbee_hdr);
+
+	/* bad status, but let the frame continue */
+	if (atresp_hdr->status != 0)
+		dev->stats.rx_atresp_error++;
+
+	return 0;
+}
+
+/* return -1 if the frame is invalid */
+static int xbee_proto_parse_rmt_atresp(struct xbee_dev *dev, void *buf,
+				   unsigned len)
+{
+	struct xbee_rmt_atresp_hdr *rmt_atresp_hdr;
+
+	dev->stats.rx_rmt_atresp++;
+
+	if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_rmt_atresp_hdr)) {
+		dev->stats.rx_frame_too_small++;
+		return -1;
+	}
+
+	rmt_atresp_hdr = buf + sizeof(struct xbee_hdr);
+
+	/* bad status, but let the frame continue */
+	if (rmt_atresp_hdr->status != 0)
+		dev->stats.rx_rmt_atresp_error++;
+
+	return 0;
+}
+
+/* return -1 if the frame is invalid */
+static int xbee_proto_parse_xmit_status(struct xbee_dev *dev, void *buf,
+					unsigned len)
+{
+	struct xbee_xmit_status_hdr *xmit_status_hdr;
+
+	dev->stats.rx_xmit_status++;
+
+	if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_xmit_status_hdr)) {
+		dev->stats.rx_frame_too_small++;
+		return -1;
+	}
+
+	xmit_status_hdr = buf + sizeof(struct xbee_hdr);
+	dev->stats.tx_xmit_retries += xmit_status_hdr->xmit_retry_cnt;
+
+	/* bad status, but let the frame continue */
+	if (xmit_status_hdr->delivery_status != 0)
+		dev->stats.rx_xmit_status_error++;
+
+	return 0;
+}
+
+/* parse a frame: return 0 if the frame is valid, else a negative value */
+// XXX rename
+int xbee_proto_parse_frame(struct xbee_dev *dev, void *buf, unsigned len)
+{
+	struct xbee_hdr *hdr = buf;
+	int i;
+	uint8_t cksum = 0;
+	int channel;
+	unsigned hdrlen;
+
+	dev->stats.rx_frame++;
+
+	/* check frame len: we must be able to read frame type */
+	if (len < (offsetof(struct xbee_hdr, type) + 1)) {
+		dev->stats.rx_frame_too_small++;
+		fprintf(stderr, "Frame too small\n");
+		return -1;
+	}
+
+	switch (hdr->type) {
+		case XBEE_TYPE_MODEM_STATUS:
+		case XBEE_TYPE_RECV:
+		case XBEE_TYPE_EXPL_RECV:
+			hdrlen = sizeof(struct xbee_hdr) - 1; /* no frame ID */
+			break;
+		default:
+			hdrlen = sizeof(struct xbee_hdr);
+			break;
+	}
+
+	/* check frame len */
+	if (len < (hdrlen + 1)) {
+		dev->stats.rx_frame_too_small++;
+		fprintf(stderr, "Frame too small\n");
+		return -1;
+	}
+
+	/* validate the cksum */
+	for (i = 3; i < (len - 1); i++)
+		cksum += ((uint8_t *)buf)[i];
+	cksum = 0xff - cksum;
+	if (cksum != ((uint8_t *)buf)[len-1]) {
+		fprintf(stderr, "Invalid cksum\n");
+		dev->stats.rx_invalid_cksum++;
+		return -1;
+	}
+
+	/* dispatch */
+	switch (hdr->type) {
+		case XBEE_TYPE_MODEM_STATUS:
+			dev->stats.rx_modem_status++;
+			channel = XBEE_DEFAULT_CHANNEL;
+			break;
+		case XBEE_TYPE_ATRESP:
+			if (xbee_proto_parse_atresp(dev, buf, len) < 0)
+				return -1;
+			channel = hdr->id;
+			break;
+		case XBEE_TYPE_RMT_ATRESP:
+			if (xbee_proto_parse_rmt_atresp(dev, buf, len) < 0)
+				return -1;
+			channel = hdr->id;
+			break;
+		case XBEE_TYPE_XMIT_STATUS:
+			if (xbee_proto_parse_xmit_status(dev, buf, len) < 0)
+				return -1;
+			channel = hdr->id;
+			break;
+		case XBEE_TYPE_RECV:
+			dev->stats.rx_data++;
+			channel = XBEE_DEFAULT_CHANNEL;
+			break;
+		case XBEE_TYPE_EXPL_RECV:
+			dev->stats.rx_expl_data++;
+			channel = XBEE_DEFAULT_CHANNEL;
+			break;
+		case XBEE_TYPE_NODE_ID:
+			dev->stats.rx_node_id++;
+			channel = hdr->id; //XXX
+			break;
+			/* invalid commands */
+		case XBEE_TYPE_ATCMD:
+		case XBEE_TYPE_ATCMD_Q:
+		case XBEE_TYPE_XMIT:
+		case XBEE_TYPE_EXPL_XMIT:
+		case XBEE_TYPE_RMT_ATCMD:
+		default:
+			dev->stats.rx_invalid_type++;
+			break;
+	}
+
+	/* fallback to default channel if not registered */
+	if (channel < 0 || channel >= XBEE_MAX_CHANNEL ||
+	    dev->channel[channel].registered == 0)
+		channel = XBEE_DEFAULT_CHANNEL;
+
+	/* execute the callback if any */
+	if (dev->channel[channel].rx_cb != NULL)
+		dev->channel[channel].rx_cb(dev, channel, hdr->type,
+					    buf + hdrlen, len - hdrlen - 1,
+					    dev->channel[channel].arg);
+
+	return 0;
+}
+
+static void hexdump(const char *title, const void *buf, unsigned int len)
+{
+	unsigned int i, out, ofs;
+	const unsigned char *data = buf;
+#define LINE_LEN 80
+	char line[LINE_LEN];	/* space needed 8+16*3+3+16 == 75 */
+
+	printf("%s at [%p], len=%d\n", title, data, len);
+	ofs = 0;
+	while (ofs < len) {
+		/* format 1 line in the buffer, then use printk to print them */
+		out = snprintf(line, LINE_LEN, "%08X", ofs);
+		for (i=0; ofs+i < len && i<16; i++)
+			out += snprintf(line+out, LINE_LEN - out, " %02X",
+					data[ofs+i]&0xff);
+		for (;i<=16;i++)
+			out += snprintf(line+out, LINE_LEN - out, "   ");
+		for (i=0; ofs < len && i<16; i++, ofs++) {
+			unsigned char c = data[ofs];
+			if (!isascii(c) || !isprint(c))
+				c = '.';
+			out += snprintf(line+out, LINE_LEN - out, "%c", c);
+		}
+		printf("%s\n", line);
+	}
+}
+
+int xbee_proto_xmit(struct xbee_dev *dev, uint8_t channel_id, uint8_t type,
+		    void *buf, unsigned len)
+{
+	struct xbee_hdr hdr;
+	struct iovec iov[3];
+	int i;
+	uint8_t cksum = 0;
+
+	/* there is no empty message, so return an error */
+	if (len == 0)
+		return -1;
+
+	/* prepare an iovec to avoid a copy: prepend a header to the
+	 * buffer and append a checksum */
+	hdr.delimiter = XBEE_DELIMITER;
+	hdr.len = htons(len + 2);
+	hdr.type = type;
+	hdr.id = channel_id;
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = sizeof(hdr);
+	iov[1].iov_base = buf;
+	iov[1].iov_len = len;
+	iov[2].iov_base = &cksum;
+	iov[2].iov_len = 1;
+
+	if (channel_id < 0 || channel_id >= XBEE_MAX_CHANNEL ||
+	    dev->channel[channel_id].registered == 0) {
+		dev->stats.tx_invalid_channel ++;
+		return -1;
+	}
+
+	/* calculate the cksum */
+	cksum = hdr.type;
+	cksum += hdr.id;
+	for (i = 0; i < len; i++)
+		cksum += ((uint8_t *)buf)[i];
+	cksum = 0xff - cksum;
+	dev->stats.tx_frame ++;
+
+	/* some additional checks before sending */
+	switch (hdr.type) {
+
+		case XBEE_TYPE_ATCMD:
+			// XXX some checks ?
+			dev->stats.tx_atcmd ++;
+			break;
+		case XBEE_TYPE_ATCMD_Q:
+			dev->stats.tx_atcmd_q ++;
+			break;
+		case XBEE_TYPE_XMIT:
+			dev->stats.tx_data ++;
+			break;
+		case XBEE_TYPE_EXPL_XMIT:
+			dev->stats.tx_expl_data ++;
+			break;
+		case XBEE_TYPE_RMT_ATCMD:
+			dev->stats.tx_rmt_atcmd ++;
+			break;
+
+		/* invalid commands */
+		case XBEE_TYPE_XMIT_STATUS:
+		case XBEE_TYPE_MODEM_STATUS:
+		case XBEE_TYPE_ATRESP:
+		case XBEE_TYPE_RECV:
+		case XBEE_TYPE_EXPL_RECV:
+		case XBEE_TYPE_NODE_ID:
+		case XBEE_TYPE_RMT_ATRESP:
+		default:
+			dev->stats.tx_invalid_type ++;
+			fprintf(stderr, "unhandled xmit type=%x\n", hdr.type);
+			return -1;
+	}
+
+	hexdump("hdr", (uint8_t *)&hdr, sizeof(hdr));
+	hexdump("buf", (uint8_t *)buf, len);
+	hexdump("cksum", &cksum, 1);
+
+	return writev(dev->fd, iov, 3);
+}
+
diff --git a/xbee_proto.h b/xbee_proto.h
new file mode 100644
index 0000000..49af583
--- /dev/null
+++ b/xbee_proto.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* protocol headers */
+
+#define XBEE_DELIMITER 0x7E
+#define XBEE_MAX_FRAME_LEN 0x200
+
+struct xbee_hdr {
+	uint8_t delimiter;
+	uint16_t len;
+	uint8_t type;
+	uint8_t id; /* not always present */
+} __attribute__((packed));
+
+#define XBEE_TYPE_ATCMD 0x08
+struct xbee_atcmd_hdr {
+	uint16_t cmd;
+	uint8_t params[];
+} __attribute__((packed));
+
+#define XBEE_TYPE_ATCMD_Q 0x09
+struct xbee_atcmd_q_hdr {
+	uint16_t cmd;
+	uint8_t params[];
+} __attribute__((packed));
+
+#define XBEE_TYPE_XMIT 0x10
+struct xbee_xmit_hdr {
+	uint64_t dstaddr;
+	uint16_t reserved;
+	uint8_t bcast_radius;
+	uint8_t opts;
+	uint8_t data[];
+} __attribute__((packed));
+
+#define XBEE_TYPE_EXPL_XMIT 0x11
+struct xbee_expl_xmit_hdr {
+	uint64_t dstaddr;
+	uint16_t reserved;
+	uint8_t src_endpoint;
+	uint8_t dst_endpoint;
+	uint16_t cluster_id;
+	uint16_t profile_id;
+	uint8_t bcast_radius;
+	uint8_t opts;
+	uint8_t data[];
+} __attribute__((packed));
+
+#define XBEE_TYPE_RMT_ATCMD 0x17
+struct xbee_rmt_atcmd_hdr {
+	uint64_t dstaddr;
+	uint16_t reserved;
+	uint8_t opts;
+	uint16_t cmd;
+	uint8_t params[];
+} __attribute__((packed));
+
+#define XBEE_TYPE_ATRESP 0x88
+struct xbee_atresp_hdr {
+	uint16_t cmd;
+	uint8_t status;
+	uint8_t data[];
+} __attribute__((packed));
+
+#define XBEE_TYPE_MODEM_STATUS 0x8A
+struct xbee_modem_status_hdr {
+	/* empty */
+} __attribute__((packed));
+
+#define XBEE_TYPE_XMIT_STATUS 0x8B
+struct xbee_xmit_status_hdr {
+	uint16_t reserved;
+	uint8_t xmit_retry_cnt;
+	uint8_t delivery_status;
+	uint8_t discovery_status;
+} __attribute__((packed));
+
+#define XBEE_TYPE_RECV 0x90
+struct xbee_recv_hdr {
+	uint64_t srcaddr;
+	uint16_t reserved;
+	uint8_t opts;
+	uint8_t data[];
+} __attribute__((packed));
+
+#define XBEE_TYPE_EXPL_RECV 0x91
+struct xbee_expl_recv_hdr {
+	uint64_t srcaddr;
+	uint16_t reserved;
+	uint8_t src_endpoint;
+	uint8_t dst_endpoint;
+	uint16_t cluster_id;
+	uint16_t profile_id;
+	uint8_t opts;
+	uint8_t data[];
+} __attribute__((packed));
+
+#define XBEE_TYPE_NODE_ID 0x95
+struct xbee_node_id_hdr {
+	uint64_t srcaddr;
+	uint16_t srcnetwork;
+	uint8_t opts;
+	uint16_t dstnetwork;
+	uint64_t dstaddr;
+	uint8_t ni_string[];
+	/* uint16_t parentaddr; after variable field */
+} __attribute__((packed));
+
+#define XBEE_TYPE_RMT_ATRESP 0x97
+struct xbee_rmt_atresp_hdr {
+	uint64_t srcaddr;
+	uint16_t reserved;
+	uint16_t cmd;
+	uint8_t status;
+	uint8_t data[];
+} __attribute__((packed));
+
+struct xbee_dev;
+
+/* return negative on error, 0 if there is not frame, or framelen */
+int xbee_proto_get_frame(struct xbee_dev *dev, void *buf, unsigned len);
+
+/* parse a frame: return 0 if the frame is valid, else a negative value */
+int xbee_proto_parse_frame(struct xbee_dev *dev, void *buf, unsigned len);
+
+/* send a frame */
+int xbee_proto_xmit(struct xbee_dev *dev, uint8_t id, uint8_t type,
+		    void *buf, unsigned len);
diff --git a/xbee_stats.c b/xbee_stats.c
new file mode 100644
index 0000000..424abc0
--- /dev/null
+++ b/xbee_stats.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/queue.h>
+
+#include "xbee_neighbor.h"
+#include "xbee_stats.h"
+#include "xbee_proto.h"
+#include "xbee_buf.h"
+#include "xbee.h"
+
+struct xbee_stats *xbee_get_stats(struct xbee_dev *dev)
+{
+	return &dev->stats;
+}
+
+void xbee_reset_stats(struct xbee_dev *dev)
+{
+	memset(&dev->stats, 0, sizeof(dev->stats));
+}
+
+void xbee_dump_stats(FILE *file, struct xbee_dev *dev)
+{
+	fprintf(file, "statistics on dev %s:\n", dev->name);
+	fprintf(file, " rx_frame: %d\n", dev->stats.rx_frame);
+	fprintf(file, " rx_atresp: %d\n", dev->stats.rx_atresp);
+	fprintf(file, " rx_atresp_error: %d\n", dev->stats.rx_atresp_error);
+	fprintf(file, " rx_modem_status: %d\n", dev->stats.rx_modem_status);
+	fprintf(file, " rx_xmit_status: %d\n", dev->stats.rx_xmit_status);
+	fprintf(file, " rx_xmit_status_error: %d\n", dev->stats.rx_xmit_status_error);
+	fprintf(file, " rx_data: %d\n", dev->stats.rx_data);
+	fprintf(file, " rx_expl_data: %d\n", dev->stats.rx_expl_data);
+	fprintf(file, " rx_node_id: %d\n", dev->stats.rx_node_id);
+	fprintf(file, " rx_rmt_atresp: %d\n", dev->stats.rx_rmt_atresp);
+	fprintf(file, " rx_rmt_atresp_error: %d\n", dev->stats.rx_rmt_atresp_error);
+	fprintf(file, " rx_frame_too_small: %d\n", dev->stats.rx_frame_too_small);
+	fprintf(file, " rx_frame_too_large: %d\n", dev->stats.rx_frame_too_large);
+	fprintf(file, " rx_invalid_cksum: %d\n", dev->stats.rx_invalid_cksum);
+	fprintf(file, " rx_invalid_type: %d\n", dev->stats.rx_invalid_type);
+	fprintf(file, " rx_no_delim: %d\n", dev->stats.rx_no_delim);
+	fprintf(file, " tx_frame: %d\n", dev->stats.tx_frame);
+	fprintf(file, " tx_atcmd: %d\n", dev->stats.tx_atcmd);
+	fprintf(file, " tx_atcmd_q: %d\n", dev->stats.tx_atcmd_q);
+	fprintf(file, " tx_data: %d\n", dev->stats.tx_data);
+	fprintf(file, " tx_expl_data: %d\n", dev->stats.tx_expl_data);
+	fprintf(file, " tx_xmit_retries: %d\n", dev->stats.tx_xmit_retries);
+	fprintf(file, " tx_rmt_atcmd: %d\n", dev->stats.tx_rmt_atcmd);
+	fprintf(file, " tx_invalid_type: %d\n", dev->stats.tx_invalid_type);
+	fprintf(file, " tx_invalid_channel: %d\n", dev->stats.tx_invalid_channel);
+}
diff --git a/xbee_stats.h b/xbee_stats.h
new file mode 100644
index 0000000..0d3f8ec
--- /dev/null
+++ b/xbee_stats.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* per-device statistics */
+struct xbee_stats {
+	int rx_frame;
+	int rx_atresp;
+	int rx_atresp_error;
+	int rx_modem_status;
+	int rx_xmit_status;
+	int rx_xmit_status_error;
+	int rx_data;
+	int rx_expl_data;
+	int rx_node_id;
+	int rx_rmt_atresp;
+	int rx_rmt_atresp_error;
+	int rx_frame_too_small;
+	int rx_frame_too_large;
+	int rx_invalid_cksum;
+	int rx_invalid_type;
+	int rx_no_delim;
+
+	int tx_frame;
+	int tx_atcmd;
+	int tx_atcmd_q;
+	int tx_data;
+	int tx_expl_data;
+	int tx_xmit_retries;
+	int tx_rmt_atcmd;
+	int tx_invalid_type;
+	int tx_invalid_channel;
+};
+
+struct xbee_dev;
+
+/* return pointer to device stats */
+struct xbee_stats *xbee_get_stats(struct xbee_dev *dev);
+
+/* reset statistics of device */
+void xbee_reset_stats(struct xbee_dev *dev);
+
+/* dump statistics on specified file */
+void xbee_dump_stats(FILE *file, struct xbee_dev *dev);
-- 
2.39.5