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