ucmb: Add more documentation and example code for AVR8

git-svn-id: svn://svn.openwrt.org/openwrt/packages@14564 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
mb 2009-02-19 12:03:32 +00:00
parent 183a54ea57
commit 0fceb7fffc
4 changed files with 251 additions and 3 deletions

View 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)/

View 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;

View File

@ -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 */
}

View File

@ -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_ */