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:
parent
183a54ea57
commit
0fceb7fffc
@ -23,6 +23,19 @@ define KernelPackage/ucmb
|
|||||||
AUTOLOAD:=$(call AutoLoad,93,ucmb)
|
AUTOLOAD:=$(call AutoLoad,93,ucmb)
|
||||||
endef
|
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
|
define Build/Prepare
|
||||||
mkdir -p $(PKG_BUILD_DIR)
|
mkdir -p $(PKG_BUILD_DIR)
|
||||||
$(CP) ./driver/* $(PKG_BUILD_DIR)/
|
$(CP) ./driver/* $(PKG_BUILD_DIR)/
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
#define PFX "ucmb: "
|
#define PFX "ucmb: "
|
||||||
|
|
||||||
#define DEBUG
|
#undef DEBUG
|
||||||
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
@ -60,7 +60,6 @@ struct ucmb_status {
|
|||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
#define UCMB_MAGIC 0x1337
|
#define UCMB_MAGIC 0x1337
|
||||||
#define UCMB_MAX_MSG_LEN 0x200
|
|
||||||
|
|
||||||
enum ucmb_status_code {
|
enum ucmb_status_code {
|
||||||
UCMB_STAT_OK = 0,
|
UCMB_STAT_OK = 0,
|
||||||
@ -217,7 +216,6 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
size = min_t(size_t, PAGE_SIZE, size);
|
size = min_t(size_t, PAGE_SIZE, size);
|
||||||
size = min_t(size_t, UCMB_MAX_MSG_LEN, size);
|
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
if (copy_from_user(buf, user_buf, size))
|
if (copy_from_user(buf, user_buf, size))
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
213
utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.c
Normal file
213
utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.c
Normal 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 */
|
||||||
|
}
|
24
utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.h
Normal file
24
utils/ucmb/microcontroller_examples/atmel_avr8/ucmb.h
Normal 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_ */
|
Loading…
x
Reference in New Issue
Block a user