4 * Copyright 2007 Develer S.r.l. (http://www.develer.com/)
7 * \version $Id: pocketbus.c 20131 2007-12-13 17:39:55Z batt $
9 * \author Francesco Sacchi <batt@develer.com>
11 * \brief pocketBus protocol implementation.
13 * pocketBus protocol is a simple strictly master-slave protocol, usable
14 * in embedded systems.
15 * pocketBus frame is as follows:
17 * +----------------------------------------+
18 * | STX | VER | ADDR | PAYLOAD | CKS | ETX |
19 * +----------------------------------------+
21 * + 1B + 1B + 2B + N Byte + 2B + 1B +
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
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.
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
44 * In the ADDR field is always specified the slave address.
45 * In the case of master trasmitting, ADDR contains the slave destination
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.
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).
58 #include "pocketbus.h"
60 #include <cfg/macros.h>
61 #include <cfg/debug.h>
63 #include <kern/kfile.h>
65 #include <mware/byteorder.h>
70 * Send a character over pocketBus channel stream, handling escape mode.
72 void pocketbus_putchar(struct PocketBusCtx *ctx, uint8_t c)
75 rotating_update1(c, &ctx->out_cks);
77 /* Escape characters with special meaning */
78 if (c == POCKETBUS_ESC || c == POCKETBUS_STX || c == POCKETBUS_ETX)
79 kfile_putc(POCKETBUS_ESC, ctx->fd);
81 kfile_putc(c, ctx->fd);
85 * Send pocketBus packet header.
87 void pocketbus_begin(struct PocketBusCtx *ctx, pocketbus_addr_t addr)
91 hdr.ver = POCKETBUS_VER;
92 hdr.addr = cpu_to_be16(addr);
93 rotating_init(&ctx->out_cks);
96 kfile_putc(POCKETBUS_STX, ctx->fd);
99 pocketbus_write(ctx, &hdr, sizeof(hdr));
103 * Send buffer \a _data over bus, handling escape.
105 void pocketbus_write(struct PocketBusCtx *ctx, const void *_data, size_t len)
107 const uint8_t *data = (const uint8_t *)_data;
110 pocketbus_putchar(ctx, *data++);
114 * Send pocketBus packet tail.
116 void pocketbus_end(struct PocketBusCtx *ctx)
119 rotating_t cks = cpu_to_be16(ctx->out_cks);
120 pocketbus_write(ctx, &cks, sizeof(cks));
123 kfile_putc(POCKETBUS_ETX, ctx->fd);
127 * Send buffer of \a data to address \a addr with a pocketBus packet over channel stream.
129 void pocketbus_send(struct PocketBusCtx *ctx, pocketbus_addr_t addr, const void *data, size_t len)
131 pocketbus_begin(ctx, addr);
134 pocketbus_write(ctx, data, len);
141 * Try to read a packet from the pocketBus.
142 * \return true if a packet is received, false otherwise.
144 bool pocketbus_recv(struct PocketBusCtx *ctx, struct PocketMsg *msg)
148 /* Process incoming characters until buffer is not empty */
149 while ((c = kfile_getc(ctx->fd)) != EOF)
151 /* Look for STX char */
152 if (c == POCKETBUS_STX && !ctx->escape)
154 /* When an STX is found, inconditionally start a new packet */
156 kprintf("pocketBus double sync!\n");
160 rotating_init(&ctx->in_cks);
166 /* Handle escape mode */
167 if (c == POCKETBUS_ESC && !ctx->escape)
173 /* Handle message end */
174 if (c == POCKETBUS_ETX && !ctx->escape)
178 /* Check minimum size */
179 if (ctx->len < sizeof(PocketBusHdr) + sizeof(rotating_t))
181 kprintf("pocketBus short pkt!\n");
185 /* Remove checksum bytes from packet len */
186 ctx->len -= sizeof(rotating_t);
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)));
193 if (recv_cks == ctx->in_cks)
195 PocketBusHdr *hdr = (PocketBusHdr *)(ctx->buf);
196 /* Check packet version */
197 if (hdr->ver == POCKETBUS_VER)
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);
208 kprintf("pocketBus version mismatch, here[%d], there[%d]\n", POCKETBUS_VER, hdr->ver);
214 kprintf("pocketBus cks error, here[%04X], there[%04X]\n", ctx->in_cks, recv_cks);
222 /* Check buffer overflow: simply ignore
223 received data and go to unsynced state. */
224 if (ctx->len >= CONFIG_POCKETBUS_BUFLEN)
226 kprintf("pocketBus buffer overflow\n");
231 /* Put received data in the buffer */
232 ctx->buf[ctx->len] = c;
238 * Check stream status.
240 if (kfile_error(ctx->fd))
242 TRACEMSG("fd status[%04X]", kfile_error(ctx->fd));
243 kfile_clearerr(ctx->fd);
251 * Initialize pocketBus protocol handler.
253 void pocketbus_init(struct PocketBusCtx *ctx, struct KFile *fd)
258 memset(ctx, 0, sizeof(*ctx));