Reformat. Add header.
[bertos.git] / bertos / net / pocketbus.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2007 Develer S.r.l. (http://www.develer.com/)
30  * -->
31  *
32  * \brief pocketBus protocol implementation.
33  *
34  * pocketBus protocol is a simple strictly master-slave protocol, usable
35  * in embedded systems.
36  * pocketBus frame is as follows:
37  * <pre>
38  * +----------------------------------------+
39  * | STX | VER | ADDR | PAYLOAD | CKS | ETX |
40  * +----------------------------------------+
41  * |     |     |      |         |     |     |
42  * + 1B  + 1B  +  2B  + N Byte  + 2B  + 1B  +
43  * </pre>
44  *
45  * - STX, 1 byte (0x02), packet start
46  * - VER, 1 byte, packet version
47  * - ADDR, 2 byte, slave address
48  * - PAYLOAD, N byte, data field
49  * - CKS, 2 byte, checksum
50  * - ETX, 1 byte, (0x03) packet end
51  *
52  * Protocol parsing start on STX reception. When the receiving routine
53  * finds an STX char, it starts to read characters from the bus
54  * until an ETX is received. Once a packet is received,
55  * the parser checks packet correctness and checksum. If all is OK
56  * the payload is returned.
57  *
58  * STX (0x02), ETX(0x03) and ESC(0x1B) are special characters and cannot be
59  * transmitted inside payload without escaping them.
60  * To escape a character you must precede it by the ESC char.
61  * E.G. STX -> ESC + STX
62  *      ETX -> ESC + ETX
63  *      ESC -> ESC + ESC
64  *
65  * In the ADDR field is always specified the slave address.
66  * In the case of master trasmitting, ADDR contains the slave destination
67  * address.
68  * In case of slave replying, ADDR contains the slave address itself.
69  * Thus, the master device does not have an address. Packet must be routed to
70  * master by hardware bus design.
71  *
72  * The checksum algorithm used is rotating hash algortihm, quite simple but more
73  * reliable than simple checksum.
74  * The checksum in computed on all fields excluding STX, ETX and CHK fields itself.
75  * Checksum is computed on the packet *before* escaping.
76  * Escape sequence counts for 1 character only (the escaped one).
77  */
78
79 #include "pocketbus.h"
80
81 #include <cfg/macros.h>
82 #include <cfg/debug.h>
83
84 #include <kern/kfile.h>
85
86 #include <mware/byteorder.h>
87
88 #include <string.h>
89
90 /**
91  * Send a character over pocketBus channel stream, handling escape mode.
92  */
93 void pocketbus_putchar(struct PocketBusCtx *ctx, uint8_t c)
94 {
95         /* Update checksum */
96         rotating_update1(c, &ctx->out_cks);
97
98         /* Escape characters with special meaning */
99         if (c == POCKETBUS_ESC || c == POCKETBUS_STX || c == POCKETBUS_ETX)
100                 kfile_putc(POCKETBUS_ESC, ctx->fd);
101         
102         kfile_putc(c, ctx->fd);
103 }
104
105 /**
106  * Send pocketBus packet header.
107  */
108 void pocketbus_begin(struct PocketBusCtx *ctx, pocketbus_addr_t addr)
109 {
110         PocketBusHdr hdr;
111
112         hdr.ver = POCKETBUS_VER;
113         hdr.addr = cpu_to_be16(addr);
114         rotating_init(&ctx->out_cks);
115
116         /* Send STX */
117         kfile_putc(POCKETBUS_STX, ctx->fd);
118         
119         /* Send header */
120         pocketbus_write(ctx, &hdr, sizeof(hdr));
121 }
122
123 /**
124  * Send buffer \a _data over bus, handling escape.
125  */
126 void pocketbus_write(struct PocketBusCtx *ctx, const void *_data, size_t len)
127 {
128         const uint8_t *data = (const uint8_t *)_data;
129
130         while (len--)
131                 pocketbus_putchar(ctx, *data++);
132 }
133
134 /**
135  * Send pocketBus packet tail.
136  */
137 void pocketbus_end(struct PocketBusCtx *ctx)
138 {
139         /* Send checksum */
140         rotating_t cks = cpu_to_be16(ctx->out_cks);
141         pocketbus_write(ctx, &cks, sizeof(cks));
142
143         /* Send ETX */
144         kfile_putc(POCKETBUS_ETX, ctx->fd);
145 }
146
147 /**
148  * Send buffer of \a data to address \a addr with a pocketBus packet over channel stream.
149  */
150 void pocketbus_send(struct PocketBusCtx *ctx, pocketbus_addr_t addr, const void *data, size_t len)
151 {
152         pocketbus_begin(ctx, addr);
153
154         /* Send data */
155         pocketbus_write(ctx, data, len);
156
157         pocketbus_end(ctx);
158 }
159
160
161 /**
162  * Try to read a packet from the pocketBus.
163  * \return true if a packet is received, false otherwise.
164  */
165 bool pocketbus_recv(struct PocketBusCtx *ctx, struct PocketMsg *msg)
166 {
167         int c;
168
169         /* Process incoming characters until buffer is not empty */
170         while ((c = kfile_getc(ctx->fd)) != EOF)
171         {
172                 /* Look for STX char */
173                 if (c == POCKETBUS_STX && !ctx->escape)
174                 {
175                         /* When an STX is found, inconditionally start a new packet */
176                         if (ctx->sync)
177                                 kprintf("pocketBus double sync!\n");
178
179                         ctx->sync = true;
180                         ctx->len = 0;
181                         rotating_init(&ctx->in_cks);
182                         continue;
183                 }
184
185                 if (ctx->sync)
186                 {
187                         /* Handle escape mode */
188                         if (c == POCKETBUS_ESC && !ctx->escape)
189                         {
190                                 ctx->escape = true;
191                                 continue;
192                         }
193
194                         /* Handle message end */
195                         if (c == POCKETBUS_ETX && !ctx->escape)
196                         {
197                                 ctx->sync = false;
198
199                                 /* Check minimum size */
200                                 if (ctx->len < sizeof(PocketBusHdr) + sizeof(rotating_t))
201                                 {
202                                         kprintf("pocketBus short pkt!\n");
203                                         continue;
204                                 }
205
206                                 /* Remove checksum bytes from packet len */
207                                 ctx->len -= sizeof(rotating_t);
208
209                                 /* Compute checksum */
210                                 rotating_update(ctx->buf, ctx->len, &ctx->in_cks);
211                                 rotating_t recv_cks = be16_to_cpu(*((rotating_t *)(ctx->buf + ctx->len)));
212
213                                 /* Checksum check */
214                                 if (recv_cks == ctx->in_cks)
215                                 {
216                                         PocketBusHdr *hdr = (PocketBusHdr *)(ctx->buf);
217                                         /* Check packet version */
218                                         if (hdr->ver == POCKETBUS_VER)
219                                         {
220                                                 /* Packet received, set msg fields */
221                                                 msg->payload = ctx->buf + sizeof(PocketBusHdr);
222                                                 msg->addr = be16_to_cpu(hdr->addr);
223                                                 msg->len = ctx->len - sizeof(PocketBusHdr);
224                                                 msg->ctx = ctx;
225                                                 return true;
226                                         }
227                                         else
228                                         {
229                                                 kprintf("pocketBus version mismatch, here[%d], there[%d]\n", POCKETBUS_VER, hdr->ver);
230                                                 continue;
231                                         }
232                                 }
233                                 else
234                                 {
235                                         kprintf("pocketBus cks error, here[%04X], there[%04X]\n", ctx->in_cks, recv_cks);
236                                         continue;
237                                 }
238
239                         }
240
241                         ctx->escape = false;
242
243                         /* Check buffer overflow: simply ignore
244                            received data and go to unsynced state. */
245                         if (ctx->len >= CONFIG_POCKETBUS_BUFLEN)
246                         {
247                                 kprintf("pocketBus buffer overflow\n");
248                                 ctx->sync = false;
249                                 continue;
250                         }
251
252                         /* Put received data in the buffer */
253                         ctx->buf[ctx->len] = c;
254                         ctx->len++;
255                 }
256         }
257
258         /*
259          * Check stream status.
260          */
261         if (kfile_error(ctx->fd))
262         {
263                 TRACEMSG("fd status[%04X]", kfile_error(ctx->fd));
264                 kfile_clearerr(ctx->fd);
265         }
266
267         return false;
268 }
269
270
271 /**
272  * Initialize pocketBus protocol handler.
273  */
274 void pocketbus_init(struct PocketBusCtx *ctx, struct KFile *fd)
275 {
276         ASSERT(ctx);
277         ASSERT(fd);
278
279         memset(ctx, 0, sizeof(*ctx));
280         ctx->fd = fd;
281 }