From 7f835873de98db76814d5ac733dfad67b89f61f6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michael=20B=C3=BCsch?= Date: Thu, 19 Feb 2009 12:03:32 +0000 Subject: [PATCH] ucmb: Add more documentation and example code for AVR8 SVN-Revision: 14564 --- utils/ucmb/Makefile | 13 ++ utils/ucmb/driver/ucmb.c | 4 +- .../atmel_avr8/ucmb.c | 213 ++++++++++++++++++ .../atmel_avr8/ucmb.h | 24 ++ 4 files changed, 251 insertions(+), 3 deletions(-) create mode 100644 utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.c create mode 100644 utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.h diff --git a/utils/ucmb/Makefile b/utils/ucmb/Makefile index 793fb9ee2e..8bce64afd4 100644 --- a/utils/ucmb/Makefile +++ b/utils/ucmb/Makefile @@ -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)/ diff --git a/utils/ucmb/driver/ucmb.c b/utils/ucmb/driver/ucmb.c index 276553ada5..2b7232c40d 100644 --- a/utils/ucmb/driver/ucmb.c +++ b/utils/ucmb/driver/ucmb.c @@ -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 index 0000000000..142424c4a6 --- /dev/null +++ b/utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.c @@ -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 + * + * 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 +#include +#include +#include + + +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 index 0000000000..9ecb92f435 --- /dev/null +++ b/utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.h @@ -0,0 +1,24 @@ +#ifndef UCMB_AVR8_H_ +#define UCMB_AVR8_H_ + +#include + + +/* 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_ */ -- 2.30.2