--- /dev/null
+/*
+ * Microcontroller message bus
+ * uc-side slave implementation for Atmel AVR8
+ *
+ * The gcc compiler always treats multi-byte variables as litte-endian.
+ * So no explicit endianness conversions are done on the message header,
+ * footer and status data structures.
+ *
+ * Copyright (C) 2009 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ucmb.h"
+
+#include <stdint.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/crc16.h>
+
+
+struct ucmb_message_hdr {
+ uint16_t magic; /* UCMB_MAGIC */
+ uint16_t len; /* Payload length (excluding header) */
+} __attribute__((packed));
+
+struct ucmb_message_footer {
+ uint16_t crc; /* CRC of the header + payload. */
+} __attribute__((packed));
+
+struct ucmb_status {
+ uint16_t magic; /* UCMB_MAGIC */
+ uint16_t code; /* enum ucmb_status_code */
+} __attribute__((packed));
+
+#define UCMB_MAGIC 0x1337
+
+enum ucmb_status_code {
+ UCMB_STAT_OK = 0,
+ UCMB_STAT_EPROTO, /* Protocol format error */
+ UCMB_STAT_ENOMEM, /* Out of memory */
+ UCMB_STAT_E2BIG, /* Message too big */
+ UCMB_STAT_ECRC, /* CRC error */
+};
+
+
+static uint8_t ucmb_buf[sizeof(struct ucmb_message_hdr) +
+ UCMB_MAX_MSG_LEN +
+ sizeof(struct ucmb_message_footer)];
+static uint16_t ucmb_buf_ptr;
+static struct ucmb_status status_buf;
+static uint16_t ucmb_send_message_len;
+
+/* Statemachine */
+static uint8_t ucmb_state;
+enum {
+ UCMB_ST_LISTEN,
+ UCMB_ST_SENDSTATUS,
+ UCMB_ST_SENDMESSAGE,
+};
+
+#define TRAILING 1
+
+
+static void ucmb_send_next_byte(void)
+{
+ switch (ucmb_state) {
+ case UCMB_ST_SENDSTATUS: {
+ const uint8_t *st = (const uint8_t *)&status_buf;
+
+ if (ucmb_buf_ptr < sizeof(struct ucmb_status))
+ SPDR = st[ucmb_buf_ptr];
+ ucmb_buf_ptr++;
+ if (ucmb_buf_ptr == sizeof(struct ucmb_status) + TRAILING) {
+ ucmb_buf_ptr = 0;
+ if (ucmb_send_message_len) {
+ ucmb_state = UCMB_ST_SENDMESSAGE;
+ goto st_sendmessage;
+ } else
+ ucmb_state = UCMB_ST_LISTEN;
+ }
+ break;
+ }
+ case UCMB_ST_SENDMESSAGE: {
+ st_sendmessage:;
+ uint16_t full_length = sizeof(struct ucmb_message_hdr) +
+ ucmb_send_message_len +
+ sizeof(struct ucmb_message_footer);
+ if (ucmb_buf_ptr < full_length)
+ SPDR = ucmb_buf[ucmb_buf_ptr];
+ ucmb_buf_ptr++;
+ if (ucmb_buf_ptr == full_length + TRAILING) {
+ ucmb_send_message_len = 0;
+ ucmb_buf_ptr = 0;
+ ucmb_state = UCMB_ST_LISTEN;//FIXME retr status
+ }
+ break;
+ } }
+}
+
+static uint16_t crc16_block_update(uint16_t crc, const void *_data, uint16_t size)
+{
+ const uint8_t *data = _data;
+
+ while (size) {
+ crc = _crc16_update(crc, *data);
+ data++;
+ size--;
+ }
+
+ return crc;
+}
+
+static uint16_t ucmb_calc_msg_buffer_crc(void)
+{
+ const struct ucmb_message_hdr *hdr;
+ uint16_t crc = 0xFFFF;
+
+ hdr = (const struct ucmb_message_hdr *)ucmb_buf;
+ crc = crc16_block_update(crc, ucmb_buf,
+ sizeof(struct ucmb_message_hdr) + hdr->len);
+ crc ^= 0xFFFF;
+
+ return crc;
+}
+
+/* SPI data transfer interrupt. */
+ISR(SPI_STC_vect)
+{
+ uint8_t data;
+
+ data = SPDR;
+
+ switch (ucmb_state) {
+ case UCMB_ST_LISTEN: {
+ struct ucmb_message_hdr *hdr;
+ struct ucmb_message_footer *footer;
+
+ ucmb_buf[ucmb_buf_ptr++] = data;
+ if (ucmb_buf_ptr < sizeof(struct ucmb_message_hdr))
+ return; /* Header RX not complete. */
+ hdr = (struct ucmb_message_hdr *)ucmb_buf;
+ if (ucmb_buf_ptr == sizeof(struct ucmb_message_hdr)) {
+ if (hdr->magic != UCMB_MAGIC) {
+ /* Invalid magic! Reset. */
+ ucmb_buf_ptr = 0;
+ return;
+ }
+ if (hdr->len > UCMB_MAX_MSG_LEN) {
+ /* Invalid length. */
+ //FIXME don't interrupt, but poll len bytes and
+ // send an immediate failure report
+ ucmb_buf_ptr = 0;
+ return;
+ }
+ return;
+ }
+
+ if (ucmb_buf_ptr == sizeof(struct ucmb_message_hdr) +
+ sizeof(struct ucmb_message_footer) +
+ hdr->len) {
+ footer = (struct ucmb_message_footer *)(
+ ucmb_buf + sizeof(struct ucmb_message_hdr) +
+ hdr->len);
+ status_buf.magic = UCMB_MAGIC;
+ status_buf.code = UCMB_STAT_OK;
+ if (ucmb_calc_msg_buffer_crc() != footer->crc)
+ status_buf.code = UCMB_STAT_ECRC;
+ ucmb_state = UCMB_ST_SENDSTATUS;
+ ucmb_buf_ptr = 0;
+ ucmb_send_next_byte();
+
+ ucmb_send_message_len = ucmb_rx_message(
+ ucmb_buf + sizeof(struct ucmb_message_hdr),
+ hdr->len);
+ if (ucmb_send_message_len) {
+ footer = (struct ucmb_message_footer *)(
+ ucmb_buf + sizeof(struct ucmb_message_hdr) +
+ ucmb_send_message_len);
+
+ hdr->magic = UCMB_MAGIC;
+ hdr->len = ucmb_send_message_len;
+ footer->crc = ucmb_calc_msg_buffer_crc();
+ }
+ }
+ break;
+ }
+ case UCMB_ST_SENDSTATUS:
+ case UCMB_ST_SENDMESSAGE:
+ ucmb_send_next_byte();
+ break;
+ }
+}
+
+void ucmb_init(void)
+{
+ ucmb_state = UCMB_ST_LISTEN;
+
+ /* SPI slave mode 0 with IRQ enabled. */
+ DDRB |= (1 << 4/*MISO*/);
+ DDRB &= ~((1 << 5/*SCK*/) | (1 << 3/*MOSI*/) | (1 << 2/*SS*/));
+ SPCR = (1 << SPE) | (1 << SPIE) /*| (1 << CPOL) | (1 << CPHA)*/;
+ (void)SPSR; /* clear state */
+ (void)SPDR; /* clear state */
+}