/** * \file * * * \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 "cfg/cfg_pocketbus.h" // Define logging setting (for cfg/log.h module). #define LOG_LEVEL POCKETBUS_LOG_LEVEL #define LOG_VERBOSITY POCKETBUS_LOG_FORMAT #include #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); uint8_t cks_h = *(ctx->buf + ctx->len); uint8_t cks_l = *(ctx->buf + ctx->len + 1); rotating_t recv_cks = (cks_h << 8) | cks_l; /* Checksum check */ if (recv_cks == ctx->in_cks) { PocketBusHdr *hdr = (PocketBusHdr *)ctx; /* 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)) { LOG_ERR("fd status[%04X]\n", 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; }