ucmb: Add more documentation and example code for AVR8
authorMichael Büsch <mb@bu3sch.de>
Thu, 19 Feb 2009 12:03:32 +0000 (12:03 +0000)
committerMichael Büsch <mb@bu3sch.de>
Thu, 19 Feb 2009 12:03:32 +0000 (12:03 +0000)
SVN-Revision: 14564

utils/ucmb/Makefile
utils/ucmb/driver/ucmb.c
utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.c [new file with mode: 0644]
utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.h [new file with mode: 0644]

index 793fb9e..8bce64a 100644 (file)
@@ -23,6 +23,19 @@ define KernelPackage/ucmb
   AUTOLOAD:=$(call AutoLoad,93,ucmb)
 endef
 
+define KernelPackage/ucmb/description
+       The Microcontroller Message Bus is a tiny SPI-GPIO based communication
+       channel used to talk to microcontrollers over GPIO pins.
+       The lowlevel protocol is CRC16 protected, so one can be pretty sure
+       that the data transmitted and received through the /dev/ucmb node is not corrupted.
+       So no further checks should be needed at upper protocol layers.
+       The device node considers every read/write to be one packet. The maximum packet
+       size is either PAGE_SIZE (at least 4kb) or the microcontroller specific packet size
+       limit, which is likely to be a lot smaller than PAGE_SIZE.
+       Example implementations for the microcontroller-side code can be found in
+       the utils/ucmb/microcontroller_examples subdirectory of the OpenWRT packages feed.
+endef
+
 define Build/Prepare
        mkdir -p $(PKG_BUILD_DIR)
        $(CP) ./driver/* $(PKG_BUILD_DIR)/
index 276553a..2b7232c 100644 (file)
@@ -22,7 +22,7 @@
 
 #define PFX    "ucmb: "
 
-#define DEBUG
+#undef DEBUG
 
 
 MODULE_LICENSE("GPL");
@@ -60,7 +60,6 @@ struct ucmb_status {
 } __attribute__((packed));
 
 #define UCMB_MAGIC             0x1337
-#define UCMB_MAX_MSG_LEN       0x200
 
 enum ucmb_status_code {
        UCMB_STAT_OK = 0,
@@ -217,7 +216,6 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
                goto out;
 
        size = min_t(size_t, PAGE_SIZE, size);
-       size = min_t(size_t, UCMB_MAX_MSG_LEN, size);
        err = -EFAULT;
        if (copy_from_user(buf, user_buf, size))
                goto out_free;
diff --git a/utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.c b/utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.c
new file mode 100644 (file)
index 0000000..142424c
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ *   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 */
+}
diff --git a/utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.h b/utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.h
new file mode 100644 (file)
index 0000000..9ecb92f
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef UCMB_AVR8_H_
+#define UCMB_AVR8_H_
+
+#include <stdint.h>
+
+
+/* Max length of the message payload. */
+#define UCMB_MAX_MSG_LEN       0x200
+
+/* ucmb_rx_message - Message receive callback.
+ * Define this elsewhere. It's called on successful retrieval
+ * of a new message.
+ * If a reply message has to be transferred after this one, put the
+ * message payload into the "payload" buffer and return the number
+ * of bytes to transmit. If no reply message is needed, return 0.
+ * Note that the "payload" buffer always has a size of UCMB_MAX_MSG_LEN.
+ */
+extern uint16_t ucmb_rx_message(uint8_t *payload,
+                               uint16_t payload_length);
+
+/* Initialize the UCMB subsystem. */
+void ucmb_init(void);
+
+#endif /* UCMB_AVR8_H_ */