vt100: include pgmspace.h as we use PROGMEM macro
[aversive.git] / modules / devices / radio / xbee / xbee_rxtx.c
1 /*
2  * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <aversive.h>
29 #include <aversive/pgmspace.h>
30 #include <aversive/queue.h>
31 #include <aversive/endian.h>
32
33 #include <uart.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <string.h>
39 #include <ctype.h>
40
41 #include "xbee_neighbor.h"
42 #include "xbee_stats.h"
43 #include "xbee_rxtx.h"
44 #include "xbee.h"
45
46 /* Return -1 if the frame is invalid. The arguments buf and len correspond to
47  * the whole xbee frame, from delimiter to cksum.  */
48 static int xbee_parse_atresp(struct xbee_dev *dev, void *buf, unsigned len)
49 {
50         struct xbee_atresp_hdr *atresp_hdr;
51
52         dev->stats.rx_atresp++;
53
54         if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_atresp_hdr)) {
55                 dev->stats.rx_frame_too_small++;
56                 return -1;
57         }
58
59         atresp_hdr = buf + sizeof(struct xbee_hdr);
60
61         /* bad status, but let the frame continue */
62         if (atresp_hdr->status != 0)
63                 dev->stats.rx_atresp_error++;
64
65         return 0;
66 }
67
68 /* Return -1 if the frame is invalid. The arguments buf and len correspond to
69  * the whole xbee frame, from delimiter to cksum.  */
70 static int xbee_parse_rmt_atresp(struct xbee_dev *dev, void *buf, unsigned len)
71 {
72         struct xbee_rmt_atresp_hdr *rmt_atresp_hdr;
73
74         dev->stats.rx_rmt_atresp++;
75
76         if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_rmt_atresp_hdr)) {
77                 dev->stats.rx_frame_too_small++;
78                 return -1;
79         }
80
81         rmt_atresp_hdr = buf + sizeof(struct xbee_hdr);
82
83         /* bad status, but let the frame continue */
84         if (rmt_atresp_hdr->status != 0)
85                 dev->stats.rx_rmt_atresp_error++;
86
87         return 0;
88 }
89
90 /* Parse the reception of a "xmit status message". The arguments buf and len
91  * correspond to the whole xbee frame, from delimiter to cksum. Return -1 if the
92  * frame is invalid */
93 static int xbee_parse_xmit_status(struct xbee_dev *dev, void *buf, unsigned len)
94 {
95         struct xbee_xmit_status_hdr *xmit_status_hdr;
96
97         dev->stats.rx_xmit_status++;
98
99         if (len < sizeof(struct xbee_hdr) + sizeof(struct xbee_xmit_status_hdr)) {
100                 dev->stats.rx_frame_too_small++;
101                 return -1;
102         }
103
104         xmit_status_hdr = buf + sizeof(struct xbee_hdr);
105         dev->stats.tx_xmit_retries += xmit_status_hdr->xmit_retry_cnt;
106
107         /* bad status, but let the frame continue */
108         if (xmit_status_hdr->delivery_status != 0)
109                 dev->stats.rx_xmit_status_error++;
110
111         return 0;
112 }
113
114 /* parse the frame stored in the xbee_dev structure: return 0 if the frame is
115  * valid, else a negative value */
116 static void xbee_parse_frame(struct xbee_dev *dev)
117 {
118         void *buf = dev->frame;
119         uint8_t len = dev->frame_len;
120         uint8_t hdrlen;
121         struct xbee_hdr *hdr = buf;
122         int i;
123         uint8_t cksum = 0;
124         int channel = XBEE_DEFAULT_CHANNEL;
125
126         dev->stats.rx_frame++;
127
128         switch (hdr->type) {
129                 case XBEE_TYPE_MODEM_STATUS:
130                 case XBEE_TYPE_RECV:
131                 case XBEE_TYPE_EXPL_RECV:
132                         hdrlen = sizeof(struct xbee_hdr) - 1; /* no frame ID */
133                         break;
134                 default:
135                         hdrlen = sizeof(struct xbee_hdr);
136                         break;
137         }
138
139         /* check frame len */
140         if (len < (hdrlen + 1)) {
141                 dev->stats.rx_frame_too_small++;
142                 fprintf_P(stderr, PSTR("Frame too small\r\n"));
143                 return;
144         }
145
146         /* validate the cksum */
147         for (i = 3; i < (len - 1); i++)
148                 cksum += ((uint8_t *)buf)[i];
149         cksum = 0xff - cksum;
150         if (cksum != ((uint8_t *)buf)[len-1]) {
151                 fprintf_P(stderr, PSTR("Invalid cksum\r\n"));
152                 dev->stats.rx_invalid_cksum++;
153                 return;
154         }
155
156         /* dispatch */
157         switch (hdr->type) {
158                 case XBEE_TYPE_MODEM_STATUS:
159                         dev->stats.rx_modem_status++;
160                         channel = XBEE_DEFAULT_CHANNEL;
161                         break;
162                 case XBEE_TYPE_ATRESP:
163                         if (xbee_parse_atresp(dev, buf, len) < 0)
164                                 return;
165                         channel = hdr->id;
166                         break;
167                 case XBEE_TYPE_RMT_ATRESP:
168                         if (xbee_parse_rmt_atresp(dev, buf, len) < 0)
169                                 return;
170                         channel = hdr->id;
171                         break;
172                 case XBEE_TYPE_XMIT_STATUS:
173                         if (xbee_parse_xmit_status(dev, buf, len) < 0)
174                                 return;
175                         channel = hdr->id;
176                         break;
177                 case XBEE_TYPE_RECV:
178                         dev->stats.rx_data++;
179                         channel = XBEE_DEFAULT_CHANNEL;
180                         break;
181                 case XBEE_TYPE_EXPL_RECV:
182                         dev->stats.rx_expl_data++;
183                         channel = XBEE_DEFAULT_CHANNEL;
184                         break;
185                 case XBEE_TYPE_NODE_ID:
186                         dev->stats.rx_node_id++;
187                         channel = hdr->id; //XXX
188                         break;
189                         /* invalid commands */
190                 case XBEE_TYPE_ATCMD:
191                 case XBEE_TYPE_ATCMD_Q:
192                 case XBEE_TYPE_XMIT:
193                 case XBEE_TYPE_EXPL_XMIT:
194                 case XBEE_TYPE_RMT_ATCMD:
195                 default:
196                         dev->stats.rx_invalid_type++;
197                         break;
198         }
199
200         /* fallback to default channel if not registered */
201         if (channel < 0 || channel >= XBEE_MAX_CHANNEL ||
202             dev->channel[channel].registered == 0)
203                 channel = XBEE_DEFAULT_CHANNEL;
204
205         /* execute the callback if any */
206         if (dev->channel[channel].rx_cb == NULL)
207                 return;
208         if (dev->channel[channel].rx_cb(dev, channel, hdr->type,
209                                         buf + hdrlen,
210                                         len - hdrlen - 1,
211                                         dev->channel[channel].arg) < 0)
212                 dev->stats.rx_usr_error++;
213 }
214
215 int xbee_tx_iovec(struct xbee_dev *dev, uint8_t channel_id, uint8_t type,
216         const struct xbee_msg *msg)
217 {
218         struct xbee_hdr hdr;
219         unsigned i, j;
220         uint8_t cksum = 0;
221         unsigned len = 0;
222
223         for (i = 0; i < msg->iovlen; i++)
224                 len += msg->iov[i].len;
225
226         /* prepare an iovec to avoid a copy: prepend a header to the
227          * buffer and append a checksum */
228         hdr.delimiter = XBEE_DELIMITER;
229         hdr.len = htons(len + 2);
230         hdr.type = type;
231         hdr.id = channel_id;
232
233         if (channel_id >= XBEE_MAX_CHANNEL ||
234             dev->channel[channel_id].registered == 0) {
235                 dev->stats.tx_invalid_channel ++;
236                 return -1;
237         }
238
239         /* calculate the cksum */
240         cksum = hdr.type;
241         cksum += hdr.id;
242         for (i = 0; i < msg->iovlen; i++) {
243                 for (j = 0; j < msg->iov[i].len; j++)
244                         cksum += ((uint8_t *)msg->iov[i].buf)[j];
245         }
246         cksum = 0xff - cksum;
247         dev->stats.tx_frame ++;
248
249         /* some additional checks before sending */
250         switch (hdr.type) {
251
252                 case XBEE_TYPE_ATCMD:
253                         // XXX some checks ?
254                         dev->stats.tx_atcmd ++;
255                         break;
256                 case XBEE_TYPE_ATCMD_Q:
257                         dev->stats.tx_atcmd_q ++;
258                         break;
259                 case XBEE_TYPE_XMIT:
260                         dev->stats.tx_data ++;
261                         break;
262                 case XBEE_TYPE_EXPL_XMIT:
263                         dev->stats.tx_expl_data ++;
264                         break;
265                 case XBEE_TYPE_RMT_ATCMD:
266                         dev->stats.tx_rmt_atcmd ++;
267                         break;
268
269                 /* invalid commands */
270                 case XBEE_TYPE_XMIT_STATUS:
271                 case XBEE_TYPE_MODEM_STATUS:
272                 case XBEE_TYPE_ATRESP:
273                 case XBEE_TYPE_RECV:
274                 case XBEE_TYPE_EXPL_RECV:
275                 case XBEE_TYPE_NODE_ID:
276                 case XBEE_TYPE_RMT_ATRESP:
277                 default:
278                         dev->stats.tx_invalid_type ++;
279                         fprintf_P(stderr, PSTR("unhandled xmit type=%x\r\n"),
280                                   hdr.type);
281                         return -1;
282         }
283
284         /* send the frame on the wire */
285         fwrite(&hdr, 1, sizeof(hdr), dev->file);
286         for (i = 0; i < msg->iovlen; i++)
287                 fwrite(msg->iov[i].buf, 1, msg->iov[i].len, dev->file);
288         fwrite(&cksum, 1, 1, dev->file);
289
290         return 0;
291 }
292
293 int xbee_tx(struct xbee_dev *dev, uint8_t channel_id, uint8_t type,
294         char *buf, unsigned len)
295 {
296         struct xbee_msg msg;
297         msg.iovlen = 1;
298         msg.iov[0].buf = buf;
299         msg.iov[0].len = len;
300         return xbee_tx_iovec(dev, channel_id, type, &msg);
301 }
302
303 void xbee_rx(struct xbee_dev *dev)
304 {
305         uint8_t framelen;
306         struct xbee_hdr *hdr = (struct xbee_hdr *)dev->frame;
307         int c;
308
309         while (1) {
310
311                 /* read from UART */
312                 c = fgetc(dev->file);
313                 if (c == EOF)
314                         break;
315
316                 /* frame too long XXX stats */
317                 if (dev->frame_len >= XBEE_MAX_FRAME_LEN) {
318                         dev->frame_len = 0;
319                         continue;
320                 }
321
322                 if (dev->frame_len == 0 && c != XBEE_DELIMITER)
323                         continue;
324
325                 dev->frame[dev->frame_len++] = c;
326
327                 /* not enough data to read len */
328                 if (dev->frame_len < sizeof(*hdr))
329                         continue;
330
331                 framelen = ntohs(hdr->len);
332                 framelen += 4; /* 1 for delimiter, 2 for len, 1 for cksum */
333
334                 /* frame too long XXX stats */
335                 if (framelen >= XBEE_MAX_FRAME_LEN) {
336                         dev->frame_len = 0;
337                         dev->stats.rx_frame++;
338                         dev->stats.rx_frame_too_large++;
339                         continue;
340                 }
341
342                 /* not enough data */
343                 if (dev->frame_len < framelen)
344                         continue;
345
346                 xbee_parse_frame(dev);
347                 dev->frame_len = 0;
348         }
349 }