From 07382ad480794063a2d5be63547eb288034d9832 Mon Sep 17 00:00:00 2001 From: batt Date: Wed, 5 Mar 2008 08:07:47 +0000 Subject: [PATCH] Add pocketbus protocol. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@1162 38d2e660-2303-0410-9eaa-f027e97ec537 --- net/pocketbus.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++ net/pocketbus.h | 99 ++++++++++++++++++ net/pocketcmd.c | 167 +++++++++++++++++++++++++++++++ net/pocketcmd.h | 112 +++++++++++++++++++++ 4 files changed, 638 insertions(+) create mode 100644 net/pocketbus.c create mode 100644 net/pocketbus.h create mode 100644 net/pocketcmd.c create mode 100644 net/pocketcmd.h diff --git a/net/pocketbus.c b/net/pocketbus.c new file mode 100644 index 00000000..e793f95d --- /dev/null +++ b/net/pocketbus.c @@ -0,0 +1,260 @@ +/** + * \file + * + * + * \version $Id: pocketbus.c 20131 2007-12-13 17:39:55Z batt $ + * + * \author Francesco Sacchi + * + * \brief pocketBus protocol implementation. + * + * pocketBus protocol is a simple strictly master-slave protocol, usable + * in embedded systems. + * pocketBus frame is as follows: + *
+ * +----------------------------------------+
+ * | STX | VER | ADDR | PAYLOAD | CKS | ETX |
+ * +----------------------------------------+
+ * |     |     |      |         |     |     |
+ * + 1B  + 1B  +  2B  + N Byte  + 2B  + 1B  +
+ * 
+ * + * - STX, 1 byte (0x02), packet start + * - VER, 1 byte, packet version + * - ADDR, 2 byte, slave address + * - PAYLOAD, N byte, data field + * - CKS, 2 byte, checksum + * - ETX, 1 byte, (0x03) packet end + * + * Protocol parsing start on STX reception. When the receiving routine + * finds an STX char, it starts to read characters from the bus + * until an ETX is received. Once a packet is received, + * the parser checks packet correctness and checksum. If all is OK + * the payload is returned. + * + * STX (0x02), ETX(0x03) and ESC(0x1B) are special characters and cannot be + * transmitted inside payload without escaping them. + * To escape a character you must precede it by the ESC char. + * E.G. STX -> ESC + STX + * ETX -> ESC + ETX + * ESC -> ESC + ESC + * + * In the ADDR field is always specified the slave address. + * In the case of master trasmitting, ADDR contains the slave destination + * address. + * In case of slave replying, ADDR contains the slave address itself. + * Thus, the master device does not have an address. Packet must be routed to + * master by hardware bus design. + * + * The checksum algorithm used is rotating hash algortihm, quite simple but more + * reliable than simple checksum. + * The checksum in computed on all fields excluding STX, ETX and CHK fields itself. + * Checksum is computed on the packet *before* escaping. + * Escape sequence counts for 1 character only (the escaped one). + */ + +#include "pocketbus.h" + +#include +#include + +#include + +#include + +#include + +/** + * Send a character over pocketBus channel stream, handling escape mode. + */ +void pocketbus_putchar(struct PocketBusCtx *ctx, uint8_t c) +{ + /* Update checksum */ + rotating_update1(c, &ctx->out_cks); + + /* Escape characters with special meaning */ + if (c == POCKETBUS_ESC || c == POCKETBUS_STX || c == POCKETBUS_ETX) + kfile_putc(POCKETBUS_ESC, ctx->fd); + + kfile_putc(c, ctx->fd); +} + +/** + * Send pocketBus packet header. + */ +void pocketbus_begin(struct PocketBusCtx *ctx, pocketbus_addr_t addr) +{ + PocketBusHdr hdr; + + hdr.ver = POCKETBUS_VER; + hdr.addr = cpu_to_be16(addr); + rotating_init(&ctx->out_cks); + + /* Send STX */ + kfile_putc(POCKETBUS_STX, ctx->fd); + + /* Send header */ + pocketbus_write(ctx, &hdr, sizeof(hdr)); +} + +/** + * Send buffer \a _data over bus, handling escape. + */ +void pocketbus_write(struct PocketBusCtx *ctx, const void *_data, size_t len) +{ + const uint8_t *data = (const uint8_t *)_data; + + while (len--) + pocketbus_putchar(ctx, *data++); +} + +/** + * Send pocketBus packet tail. + */ +void pocketbus_end(struct PocketBusCtx *ctx) +{ + /* Send checksum */ + rotating_t cks = cpu_to_be16(ctx->out_cks); + pocketbus_write(ctx, &cks, sizeof(cks)); + + /* Send ETX */ + kfile_putc(POCKETBUS_ETX, ctx->fd); +} + +/** + * Send buffer of \a data to address \a addr with a pocketBus packet over channel stream. + */ +void pocketbus_send(struct PocketBusCtx *ctx, pocketbus_addr_t addr, const void *data, size_t len) +{ + pocketbus_begin(ctx, addr); + + /* Send data */ + pocketbus_write(ctx, data, len); + + pocketbus_end(ctx); +} + + +/** + * Try to read a packet from the pocketBus. + * \return true if a packet is received, false otherwise. + */ +bool pocketbus_recv(struct PocketBusCtx *ctx, struct PocketMsg *msg) +{ + int c; + + /* Process incoming characters until buffer is not empty */ + while ((c = kfile_getc(ctx->fd)) != EOF) + { + /* Look for STX char */ + if (c == POCKETBUS_STX && !ctx->escape) + { + /* When an STX is found, inconditionally start a new packet */ + if (ctx->sync) + kprintf("pocketBus double sync!\n"); + + ctx->sync = true; + ctx->len = 0; + rotating_init(&ctx->in_cks); + continue; + } + + if (ctx->sync) + { + /* Handle escape mode */ + if (c == POCKETBUS_ESC && !ctx->escape) + { + ctx->escape = true; + continue; + } + + /* Handle message end */ + if (c == POCKETBUS_ETX && !ctx->escape) + { + ctx->sync = false; + + /* Check minimum size */ + if (ctx->len < sizeof(PocketBusHdr) + sizeof(rotating_t)) + { + kprintf("pocketBus short pkt!\n"); + continue; + } + + /* Remove checksum bytes from packet len */ + ctx->len -= sizeof(rotating_t); + + /* Compute checksum */ + rotating_update(ctx->buf, ctx->len, &ctx->in_cks); + rotating_t recv_cks = be16_to_cpu(*((rotating_t *)(ctx->buf + ctx->len))); + + /* Checksum check */ + if (recv_cks == ctx->in_cks) + { + PocketBusHdr *hdr = (PocketBusHdr *)(ctx->buf); + /* Check packet version */ + if (hdr->ver == POCKETBUS_VER) + { + /* Packet received, set msg fields */ + msg->payload = ctx->buf + sizeof(PocketBusHdr); + msg->addr = be16_to_cpu(hdr->addr); + msg->len = ctx->len - sizeof(PocketBusHdr); + msg->ctx = ctx; + return true; + } + else + { + kprintf("pocketBus version mismatch, here[%d], there[%d]\n", POCKETBUS_VER, hdr->ver); + continue; + } + } + else + { + kprintf("pocketBus cks error, here[%04X], there[%04X]\n", ctx->in_cks, recv_cks); + continue; + } + + } + + ctx->escape = false; + + /* Check buffer overflow: simply ignore + received data and go to unsynced state. */ + if (ctx->len >= CONFIG_POCKETBUS_BUFLEN) + { + kprintf("pocketBus buffer overflow\n"); + ctx->sync = false; + continue; + } + + /* Put received data in the buffer */ + ctx->buf[ctx->len] = c; + ctx->len++; + } + } + + /* + * Check stream status. + */ + if (kfile_error(ctx->fd)) + { + TRACEMSG("fd status[%04X]", kfile_error(ctx->fd)); + kfile_clearerr(ctx->fd); + } + + return false; +} + + +/** + * Initialize pocketBus protocol handler. + */ +void pocketbus_init(struct PocketBusCtx *ctx, struct KFile *fd) +{ + ASSERT(ctx); + ASSERT(fd); + + memset(ctx, 0, sizeof(*ctx)); + ctx->fd = fd; +} diff --git a/net/pocketbus.h b/net/pocketbus.h new file mode 100644 index 00000000..397cd6eb --- /dev/null +++ b/net/pocketbus.h @@ -0,0 +1,99 @@ +/** + * \file + * + * + * \version $Id: pocketbus.h 20131 2007-12-13 17:39:55Z batt $ + * + * \author Francesco Sacchi + * + * \brief pocketBus protocol interface. + */ + +#ifndef NET_POCKETBUS_H +#define NET_POCKETBUS_H + +#include +#include +#include +#include +#include "appconfig.h" //for CONFIG_POCKETBUS_BUFLEN + +/** + * pocketBus special characters definitions. + * \{ + */ +#define POCKETBUS_STX 0x02 //ASCII STX +#define POCKETBUS_ETX 0x03 //ASCII ETX +#define POCKETBUS_ESC 0x1B //ASCII ESC +#define POCKETBUS_ACK 0x06 //ASCII ACK +#define POCKETBUS_NAK 0x15 //ASCII NAK +/*\}*/ + +#define POCKETBUS_BROADCAST_ADDR 0xFFFF ///< pocketBus broadcast address + +/** + * Type for pocketBus length. + */ +typedef uint16_t pocketbus_len_t; + +/** + * Type for pocketBus addresses. + */ +typedef uint16_t pocketbus_addr_t; + +/** + * Header of pocketBus messages. + */ +typedef struct PocketBusHdr +{ + #define POCKETBUS_VER 1 + uint8_t ver; ///< packet version + pocketbus_addr_t addr; ///< slave address +} PocketBusHdr; + +/** + * pocketBus context structure. + */ +typedef struct PocketBusCtx +{ + struct KFile *fd; ///< File descriptor + bool sync; ///< Status flag: true if we have received an STX, false otherwise + bool escape; ///< Status flag: true if we are in escape mode, false otherwise + rotating_t in_cks; ///< Checksum computation for received data. + rotating_t out_cks; ///< Checksum computation for transmitted data. + pocketbus_len_t len; ///< Received length + uint8_t buf[CONFIG_POCKETBUS_BUFLEN]; ///< receiving Buffer +} PocketBusCtx; + +/** + * Structure holding pocketBus message parameters. + */ +typedef struct PocketMsg +{ + struct PocketBusCtx *ctx; ///< pocketBus message context + pocketbus_addr_t addr; ///< address for received packet + pocketbus_len_t len; ///< payload length + const uint8_t *payload; ///< payload data +} PocketMsg; + +/** + * This ensure that endianess convertion functions work on + * the right data size. + * \{ + */ +STATIC_ASSERT(sizeof(pocketbus_addr_t) == sizeof(uint16_t)); +STATIC_ASSERT(sizeof(rotating_t) == sizeof(uint16_t)); +/*\}*/ + +void pocketbus_putchar(struct PocketBusCtx *ctx, uint8_t c); +void pocketbus_begin(struct PocketBusCtx *ctx, pocketbus_addr_t addr); +void pocketbus_write(struct PocketBusCtx *ctx, const void *_data, size_t len); +void pocketbus_end(struct PocketBusCtx *ctx); + +void pocketbus_send(struct PocketBusCtx *ctx, pocketbus_addr_t addr, const void *data, size_t len); +bool pocketbus_recv(struct PocketBusCtx *ctx, struct PocketMsg *msg); +void pocketbus_init(struct PocketBusCtx *ctx, struct KFile *fd); + +#endif /* NET_POCKETBUS_H */ diff --git a/net/pocketcmd.c b/net/pocketcmd.c new file mode 100644 index 00000000..d59cbf41 --- /dev/null +++ b/net/pocketcmd.c @@ -0,0 +1,167 @@ +/** + * \file + * + * + * \version $Id: pocketcmd.c 16587 2007-10-02 14:31:02Z batt $ + * + * \author Francesco Sacchi + * + * \brief pocketBus protocol Command layer implementation. + * + * This module implements command layer over pocketBus + * protocol. + * Payload packets received by pocketBus are first checked for + * address matching. + * If a packet is addressed to us we look for a suitable + * callback function to call. + * + * The received payload format is as follows: + *
+ * +----------------------------------------+
+ * |  CMD |            DATA                 |
+ * +----------------------------------------+
+ * |      |                                 |
+ * +  2B  +           0..N Byte             +
+ * 
+ * + * The CMD ID used is the same supplied by the master when + * the command was sent. + */ + +#include "pocketcmd.h" +#include "pocketbus.h" + +#include +#include +#include + +#include + +#include + +#include + +/** + * pocketBus Command poll function. + * Call it to read and process pocketBus commands. + */ +void pocketcmd_poll(struct PocketCmdCtx *ctx) +{ + PocketMsg msg; + + /* Try to read a packet from pocketBus */ + while (pocketbus_recv(ctx->bus_ctx, &msg)) + { + /* Check address */ + if (msg.addr == ctx->addr || + msg.addr == POCKETBUS_BROADCAST_ADDR) + { + const PocketCmdHdr *hdr = (const PocketCmdHdr *)msg.payload; + pocketcmd_t cmd = be16_to_cpu(hdr->cmd); + + /* We're no longer waiting for a reply (in case we were) */ + if (cmd == ctx->waiting) + ctx->waiting = PKTCMD_NULL; + + /* Check for command callback */ + pocketcmd_hook_t callback = ctx->search(cmd); + + /* Call it if exists */ + if (callback) + { + PocketCmdMsg cmd_msg; + + cmd_msg.cmd_ctx = ctx; + cmd_msg.cmd = cmd; + cmd_msg.len = msg.len - sizeof(PocketCmdHdr); + cmd_msg.buf = msg.payload + sizeof(PocketCmdHdr); + + callback(&cmd_msg); + } + } + } +} + +/** + * Send command \a cmd to/from slave adding \a len arguments in \a buf. + * Address used is contained in \a ctx->addr . + * If we are master and the message has a reply, you must set \a wait_reply to true. + * \return true if all is ok, false if we are already waiting a replay from another slave. + */ +bool pocketcmd_send(struct PocketCmdCtx *ctx, pocketcmd_t cmd, const void *buf, size_t len, bool wait_reply) +{ + /* Check if we are waiting a reply from someone */ + if (ctx->waiting != PKTCMD_NULL) + { + /* Check is reply timeout is elapsed */ + if (timer_clock() - ctx->reply_timer < ms_to_ticks(PKTCMD_REPLY_TIMEOUT)) + { + TRACEMSG("Pkt discard! waiting cmd[%04X]\n", ctx->waiting); + return false; + } + else + { + TRACEMSG("Timeout waiting cmd[%04X]\n", ctx->waiting); + ctx->waiting = PKTCMD_NULL; + } + } + + /* Endianess! */ + cmd = cpu_to_be16(cmd); + + /* Send packet */ + pocketbus_begin(ctx->bus_ctx, ctx->addr); + pocketbus_write(ctx->bus_ctx, &cmd, sizeof(cmd)); + pocketbus_write(ctx->bus_ctx, buf, len); + pocketbus_end(ctx->bus_ctx); + + if (wait_reply) + { + ctx->waiting = cmd; + ctx->reply_timer = timer_clock(); + } + return true; +} + +/** + * Init pocketBus command layer. + * \a ctx is pocketBus command layer context. + * \a bus_ctx is pocketBus context. + * \a addr is slave address (see pocketcmd_setAddr for details.) + * \a search is the lookup function used to search command ID callbacks. + */ +void pocketcmd_init(struct PocketCmdCtx *ctx, struct PocketBusCtx *bus_ctx, pocketbus_addr_t addr, pocketcmd_lookup_t search) +{ + ASSERT(ctx); + ASSERT(bus_ctx); + ASSERT(search); + MOD_CHECK(timer); + + memset(ctx, 0, sizeof(*ctx)); + ctx->bus_ctx = bus_ctx; + ctx->search = search; + pocketcmd_setAddr(ctx, addr); +} + +/** + * Helper function used to reply to master with an ACK. + */ +void pocketcmd_replyAck(struct PocketCmdMsg *msg) +{ + uint8_t ack[] = { POCKETBUS_ACK }; + + pocketcmd_slaveReply(msg->cmd_ctx, msg->cmd, ack, sizeof(ack)); +} + +/** + * Helper function used to reply to master with a NAK. + */ +void pocketcmd_replyNak(struct PocketCmdMsg *msg) +{ + uint8_t nak[] = { POCKETBUS_NAK }; + + pocketcmd_slaveReply(msg->cmd_ctx, msg->cmd, nak, sizeof(nak)); +} + diff --git a/net/pocketcmd.h b/net/pocketcmd.h new file mode 100644 index 00000000..7045f3fe --- /dev/null +++ b/net/pocketcmd.h @@ -0,0 +1,112 @@ +/** + * \file + * + * + * \version $Id: pocketcmd.h 20030 2007-12-04 16:16:09Z batt $ + * + * \author Francesco Sacchi + * + * \brief pocketBus protocol command layer interface. + */ + +#ifndef NET_POCKETCMD_H +#define NET_POCKETCMD_H + +#include "pocketbus.h" +#include + +#define PKTCMD_NULL 0 ///< pocketBus Null command + +#define PKTCMD_REPLY_TIMEOUT 50 ///< Command replay timeout in milliseconds + +typedef uint16_t pocketcmd_t; ///< Type for Command IDs + +/** + * Header for transmitted pocketBus Commands. + */ +typedef struct PocketCmdHdr +{ + pocketcmd_t cmd; ///< command ID +} PocketCmdHdr; + +/** + * This ensure that endianess convertion functions work on + * the right data size. + * \{ + */ +STATIC_ASSERT(sizeof(pocketcmd_t) == sizeof(uint16_t)); +/*\}*/ + +/* fwd declaration */ +struct PocketCmdCtx; + +/** + * pocketBus command message structure. + */ +typedef struct PocketCmdMsg +{ + struct PocketCmdCtx *cmd_ctx; ///< command context + pocketcmd_t cmd; ///< command id + pocketbus_len_t len; ///< optional arg length + const uint8_t *buf; ///< optional arguments +} PocketCmdMsg; + +/** + * Type for command hooks. + */ +typedef void (*pocketcmd_hook_t)(struct PocketCmdMsg *cmd_msg); + +/** + * Type for lookup function hooks. + */ +typedef pocketcmd_hook_t (*pocketcmd_lookup_t)(pocketcmd_t cmd); + +/** + * pocketBus context for command layer communications. + */ +typedef struct PocketCmdCtx +{ + struct PocketBusCtx *bus_ctx; ///< pocketBus context + pocketbus_addr_t addr; ///< Our address + pocketcmd_lookup_t search; ///< Lookup function used to search for command callbacks + pocketcmd_t waiting; ///< The command ID we are waiting for or PKTCMD_NULL. + ticks_t reply_timer; ///< For waiting_reply +} PocketCmdCtx; + +/** + * Set slave address \a addr for pocketBus command layer. + * If we are a slave this is *our* address. + * If we are the master this is the slave address to send messages to. + */ +INLINE void pocketcmd_setAddr(struct PocketCmdCtx *ctx, pocketbus_addr_t addr) +{ + ctx->addr = addr; +} + +void pocketcmd_init(struct PocketCmdCtx *ctx, struct PocketBusCtx *bus_ctx, pocketbus_addr_t addr, pocketcmd_lookup_t search); +void pocketcmd_poll(struct PocketCmdCtx *ctx); +bool pocketcmd_send(struct PocketCmdCtx *ctx, pocketcmd_t cmd, const void *buf, size_t len, bool has_replay); +void pocketcmd_replyNak(struct PocketCmdMsg *msg); +void pocketcmd_replyAck(struct PocketCmdMsg *msg); + +/** + * Helper function used by master to send a command to slave \a addr. + */ +INLINE bool pocketcmd_masterSend(struct PocketCmdCtx *ctx, pocketbus_addr_t addr, pocketcmd_t cmd, const void *buf, size_t len) +{ + pocketcmd_setAddr(ctx, addr); + return pocketcmd_send(ctx, cmd, buf, len, true); +} + +/** + * Helper function used by slave to reply to a master command. + */ +INLINE bool pocketcmd_slaveReply(struct PocketCmdCtx *ctx, pocketcmd_t cmd, const void *buf, size_t len) +{ + return pocketcmd_send(ctx, cmd, buf, len, false); +} + + +#endif /* NET_POCKETCMD_H */ -- 2.25.1