Remove 'ucmb' packages

git-svn-id: svn://svn.openwrt.org/openwrt/packages@28606 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
mb 2011-10-26 14:57:17 +00:00
parent 963bb3df2e
commit 041ceda9e3
11 changed files with 0 additions and 1800 deletions

View File

@ -1,41 +0,0 @@
#
# Copyright (C) 2009 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
include $(TOPDIR)/rules.mk
PKG_NAME:=ucmb-tools
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/ucmb-tools
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Microcontroller Message Bus tools
DEPENDS:=+kmod-ucmb
MAINTAINER:=Michael Buesch <mb@bu3sch.de>
endef
define Package/ucmb-tools/description
Debugging tool for UCMB
endef
define Build/Prepare
$(CP) ./tools/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
$(TARGET_CONFIGURE_OPTS) CFLAGS="$(TARGET_CFLAGS) -I$(LINUX_DIR)/include"
endef
define Package/ucmb-tools/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ucmb $(1)/usr/bin/
endef
$(eval $(call BuildPackage,ucmb-tools))

View File

@ -1,17 +0,0 @@
INSTDIR = $(prefix)/usr/bin
all: ucmb
ucmb: ucmb.o
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
$(STRIP) $@
%.o: %.c
$(CC) -c $(CFLAGS) $^ -o $@
install: ucmb
$(INSTALL) -d $(INSTDIR)
$(INSTALL) -m 0755 -o 0 -g 0 ucmb $(INSTDIR)
clean:
rm -f ucmb *.o

View File

@ -1,120 +0,0 @@
/*
* Microcontroller Message Bus
* Userspace commandline utility
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#define UCMB_DEV "/dev/ucmb"
#define __UCMB_IOCTL ('U'|'C'|'M'|'B')
#define UCMB_IOCTL_RESETUC _IO(__UCMB_IOCTL, 0)
#define UCMB_IOCTL_GMSGDELAY _IOR(__UCMB_IOCTL, 1, unsigned int)
#define UCMB_IOCTL_SMSGDELAY _IOW(__UCMB_IOCTL, 2, unsigned int)
static void usage(int argc, char **argv)
{
fprintf(stderr, "Usage: %s read|write|reset [" UCMB_DEV "]\n", argv[0]);
}
int main(int argc, char **argv)
{
const char *command, *devpath = UCMB_DEV;
int res, errcode = 0;
int ucmb_fd;
char *buf;
size_t count, buflen;
ssize_t nrbytes;
if (argc != 2 && argc != 3) {
usage(argc, argv);
return 1;
}
if (argc == 3)
devpath = argv[2];
command = argv[1];
ucmb_fd = open(devpath, O_RDWR);
if (ucmb_fd == -1) {
fprintf(stderr, "Failed to open %s\n", UCMB_DEV);
errcode = 1;
goto out;
}
buflen = 4096;
buf = malloc(buflen);
if (!buf) {
fprintf(stderr, "Out of memory\n");
errcode = 1;
goto out_close;
}
if (strcasecmp(command, "read") == 0) {
nrbytes = read(ucmb_fd, buf, buflen);
if (nrbytes < 0) {
fprintf(stderr, "Failed to read UCMB: %s (%d)\n",
strerror(errno), errno);
errcode = 1;
goto out_free;
}
if (fwrite(buf, nrbytes, 1, stdout) != 1) {
fprintf(stderr, "Failed to write stdout\n");
errcode = 1;
goto out_free;
}
} else if (strcasecmp(command, "write") == 0) {
count = fread(buf, 1, buflen, stdin);
if (!count) {
fprintf(stderr, "Failed to read stdin\n");
errcode = 1;
goto out_free;
}
nrbytes = write(ucmb_fd, buf, count);
if (nrbytes != count) {
fprintf(stderr, "Failed to write UCMB: %s (%d)\n",
strerror(errno), errno);
errcode = 1;
goto out_free;
}
} else if (strcasecmp(command, "reset") == 0) {
res = ioctl(ucmb_fd, UCMB_IOCTL_RESETUC);
if (res) {
fprintf(stderr, "RESET ioctl failed: %s (%d)\n",
strerror(res < 0 ? -res : res), res);
errcode = 1;
goto out_free;
}
} else {
usage(argc, argv);
errcode = 1;
goto out_free;
}
out_free:
free(buf);
out_close:
close(ucmb_fd);
out:
return errcode;
}

View File

@ -1,65 +0,0 @@
#
# Copyright (C) 2010 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=ucmb
PKG_RELEASE:=2
include $(INCLUDE_DIR)/package.mk
define KernelPackage/ucmb
SUBMENU:=Other modules
DEPENDS:=@!LINUX_2_4 \
@!LINUX_2_6_21 @!LINUX_2_6_22 @!LINUX_2_6_23 @!LINUX_2_6_24 \
@!LINUX_2_6_25 @!LINUX_2_6_26 @!LINUX_2_6_27 \
+kmod-spi-gpio +kmod-crc16
KCONFIG:=CONFIG_SPI=y \
CONFIG_SPI_MASTER=y
TITLE:=Microcontroller Message Bus
FILES:=$(PKG_BUILD_DIR)/ucmb.$(LINUX_KMOD_SUFFIX)
AUTOLOAD:=$(call AutoLoad,93,ucmb)
MAINTAINER:=Michael Buesch <mb@bu3sch.de>
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.
To register an ucmb device, simply create a struct ucmb_platform_data
and register it via ucmb_device_register() from another kernel module.
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
$(CP) ./driver/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
ARCH="$(LINUX_KARCH)" \
SUBDIRS="$(PKG_BUILD_DIR)" \
EXTRA_CFLAGS="$(BUILDFLAGS)" \
modules
endef
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include
$(CP) \
$(PKG_BUILD_DIR)/ucmb.h \
$(1)/usr/include
endef
$(eval $(call KernelPackage,ucmb))

View File

@ -1 +0,0 @@
obj-m := ucmb.o

View File

@ -1,656 +0,0 @@
/*
* Microcontroller Message Bus
* Linux kernel driver
*
* Copyright (c) 2009-2010 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 <linux/version.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_gpio.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/gpio.h>
#include <linux/gfp.h>
#include <linux/delay.h>
#include <linux/crc16.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#define PFX "ucmb: "
#undef DEBUG
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Microcontroller Message Bus");
MODULE_AUTHOR("Michael Buesch");
struct ucmb {
struct mutex mutex;
bool is_open;
unsigned int chunk_size;
unsigned int msg_delay_usec;
unsigned int gpio_reset;
bool reset_activelow;
/* Misc character device driver */
struct miscdevice mdev;
struct file_operations mdev_fops;
/* SPI driver */
struct spi_device *sdev;
/* SPI-GPIO driver */
struct spi_gpio_platform_data spi_gpio_pdata;
struct platform_device spi_gpio_pdev;
};
#define UCMB_MAX_MSG_DELAY (10 * 1000 * 1000) /* 10 seconds */
struct ucmb_message_hdr {
__le16 magic; /* UCMB_MAGIC */
__le16 len; /* Payload length (excluding header and footer) */
} __attribute__((packed));
struct ucmb_message_footer {
__le16 crc; /* CRC of the header + payload. */
} __attribute__((packed));
struct ucmb_status {
__le16 magic; /* UCMB_MAGIC */
__le16 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 int ucmb_spi_busnum_count = 1337;
static int ucmb_pdev_id_count;
static int __devinit ucmb_spi_probe(struct spi_device *sdev)
{
return 0;
}
static int __devexit ucmb_spi_remove(struct spi_device *sdev)
{
return 0;
}
static struct spi_driver ucmb_spi_driver = {
.driver = {
.name = "ucmb",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = ucmb_spi_probe,
.remove = __devexit_p(ucmb_spi_remove),
};
static void ucmb_toggle_reset_line(struct ucmb *ucmb, bool active)
{
if (ucmb->reset_activelow)
active = !active;
gpio_set_value(ucmb->gpio_reset, active);
}
static int ucmb_reset_microcontroller(struct ucmb *ucmb)
{
if (ucmb->gpio_reset == UCMB_NO_RESET)
return -ENODEV;
ucmb_toggle_reset_line(ucmb, 1);
msleep(50);
ucmb_toggle_reset_line(ucmb, 0);
msleep(50);
return 0;
}
static int ucmb_status_code_to_errno(enum ucmb_status_code code)
{
switch (code) {
case UCMB_STAT_OK:
return 0;
case UCMB_STAT_EPROTO:
return -EPROTO;
case UCMB_STAT_ENOMEM:
return -ENOMEM;
case UCMB_STAT_E2BIG:
return -E2BIG;
case UCMB_STAT_ECRC:
return -EBADMSG;
}
return -EBUSY;
}
static inline struct ucmb * filp_to_ucmb(struct file *filp)
{
return container_of(filp->f_op, struct ucmb, mdev_fops);
}
static int ucmb_open(struct inode *inode, struct file *filp)
{
struct ucmb *ucmb = filp_to_ucmb(filp);
int err = 0;
mutex_lock(&ucmb->mutex);
if (ucmb->is_open) {
err = -EBUSY;
goto out_unlock;
}
ucmb->is_open = 1;
ucmb->msg_delay_usec = 0;
out_unlock:
mutex_unlock(&ucmb->mutex);
return err;
}
static int ucmb_release(struct inode *inode, struct file *filp)
{
struct ucmb *ucmb = filp_to_ucmb(filp);
mutex_lock(&ucmb->mutex);
WARN_ON(!ucmb->is_open);
ucmb->is_open = 0;
mutex_unlock(&ucmb->mutex);
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
static long ucmb_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
#else
static int ucmb_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
#endif
{
struct ucmb *ucmb = filp_to_ucmb(filp);
int ret = 0;
mutex_lock(&ucmb->mutex);
switch (cmd) {
case UCMB_IOCTL_RESETUC:
ret = ucmb_reset_microcontroller(ucmb);
break;
case UCMB_IOCTL_GMSGDELAY:
if (put_user(ucmb->msg_delay_usec, (unsigned int __user *)arg)) {
ret = -EFAULT;
break;
}
break;
case UCMB_IOCTL_SMSGDELAY: {
unsigned int msg_delay_usec;
if (get_user(msg_delay_usec, (unsigned int __user *)arg)) {
ret = -EFAULT;
break;
}
if (msg_delay_usec > UCMB_MAX_MSG_DELAY) {
ret = -E2BIG;
break;
}
ucmb->msg_delay_usec = msg_delay_usec;
break;
}
default:
ret = -EINVAL;
}
mutex_unlock(&ucmb->mutex);
return ret;
}
static int ucmb_spi_write(struct ucmb *ucmb, const void *_buf, size_t size)
{
const u8 *buf = _buf;
size_t i, chunk_size, current_size;
int err = 0;
chunk_size = ucmb->chunk_size ? : size;
for (i = 0; i < size; i += chunk_size) {
current_size = chunk_size;
if (i + current_size > size)
current_size = size - i;
err = spi_write(ucmb->sdev, buf + i, current_size);
if (err)
goto out;
if (ucmb->chunk_size && need_resched())
msleep(1);
}
out:
return err;
}
static int ucmb_spi_read(struct ucmb *ucmb, void *_buf, size_t size)
{
u8 *buf = _buf;
size_t i, chunk_size, current_size;
int err = 0;
chunk_size = ucmb->chunk_size ? : size;
for (i = 0; i < size; i += chunk_size) {
current_size = chunk_size;
if (i + current_size > size)
current_size = size - i;
err = spi_read(ucmb->sdev, buf + i, current_size);
if (err)
goto out;
if (ucmb->chunk_size && need_resched())
msleep(1);
}
out:
return err;
}
static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
size_t size, loff_t *offp)
{
struct ucmb *ucmb = filp_to_ucmb(filp);
u8 *buf;
int res, err;
struct ucmb_message_hdr hdr;
struct ucmb_message_footer footer;
struct ucmb_status status = { .magic = cpu_to_le16(UCMB_MAGIC), };
u16 crc = 0xFFFF;
mutex_lock(&ucmb->mutex);
size = min_t(size_t, size, PAGE_SIZE);
err = -ENOMEM;
buf = (char *)__get_free_page(GFP_KERNEL);
if (!buf)
goto out;
err = ucmb_spi_read(ucmb, &hdr, sizeof(hdr));
if (err)
goto out_free;
#ifdef DEBUG
printk(KERN_DEBUG PFX "Received message header 0x%04X 0x%04X\n",
le16_to_cpu(hdr.magic), le16_to_cpu(hdr.len));
#endif
err = -EPROTO;
if (hdr.magic != cpu_to_le16(UCMB_MAGIC))
goto out_free;
err = -ENOBUFS;
if (size < le16_to_cpu(hdr.len))
goto out_free;
size = le16_to_cpu(hdr.len);
err = ucmb_spi_read(ucmb, buf, size);
if (err)
goto out_free;
err = ucmb_spi_read(ucmb, &footer, sizeof(footer));
if (err)
goto out_free;
crc = crc16(crc, (u8 *)&hdr, sizeof(hdr));
crc = crc16(crc, buf, size);
crc ^= 0xFFFF;
if (crc != le16_to_cpu(footer.crc)) {
err = -EPROTO;
status.code = UCMB_STAT_ECRC;
goto out_send_status;
}
if (copy_to_user(user_buf, buf, size)) {
err = -EFAULT;
status.code = UCMB_STAT_ENOMEM;
goto out_send_status;
}
status.code = UCMB_STAT_OK;
err = 0;
out_send_status:
res = ucmb_spi_write(ucmb, &status, sizeof(status));
if (res && !err)
err = res;
out_free:
free_page((unsigned long)buf);
out:
mutex_unlock(&ucmb->mutex);
return err ? err : size;
}
static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
size_t size, loff_t *offp)
{
struct ucmb *ucmb = filp_to_ucmb(filp);
u8 *buf;
int err;
struct ucmb_message_hdr hdr = { .magic = cpu_to_le16(UCMB_MAGIC), };
struct ucmb_message_footer footer = { .crc = 0xFFFF, };
struct ucmb_status status;
mutex_lock(&ucmb->mutex);
err = -ENOMEM;
buf = (char *)__get_free_page(GFP_KERNEL);
if (!buf)
goto out;
size = min_t(size_t, PAGE_SIZE, size);
err = -EFAULT;
if (copy_from_user(buf, user_buf, size))
goto out_free;
hdr.len = cpu_to_le16(size);
footer.crc = crc16(footer.crc, (u8 *)&hdr, sizeof(hdr));
footer.crc = crc16(footer.crc, buf, size);
footer.crc ^= 0xFFFF;
err = ucmb_spi_write(ucmb, &hdr, sizeof(hdr));
if (err)
goto out_free;
err = ucmb_spi_write(ucmb, buf, size);
if (err)
goto out_free;
err = ucmb_spi_write(ucmb, &footer, sizeof(footer));
if (err)
goto out_free;
if (ucmb->msg_delay_usec) {
/* The microcontroller deserves some time to process the message. */
if (ucmb->msg_delay_usec >= 1000000) {
ssleep(ucmb->msg_delay_usec / 1000000);
msleep(DIV_ROUND_UP(ucmb->msg_delay_usec % 1000000, 1000));
} else if (ucmb->msg_delay_usec >= 1000) {
msleep(DIV_ROUND_UP(ucmb->msg_delay_usec, 1000));
} else
udelay(ucmb->msg_delay_usec);
}
/* Get the status code. */
err = ucmb_spi_read(ucmb, &status, sizeof(status));
if (err)
goto out_free;
#ifdef DEBUG
printk(KERN_DEBUG PFX "Sent message. Status report: 0x%04X 0x%04X\n",
le16_to_cpu(status.magic), le16_to_cpu(status.code));
#endif
err = -EPROTO;
if (status.magic != cpu_to_le16(UCMB_MAGIC))
goto out_free;
err = ucmb_status_code_to_errno(le16_to_cpu(status.code));
if (err)
goto out_free;
out_free:
free_page((unsigned long)buf);
out:
mutex_unlock(&ucmb->mutex);
return err ? err : size;
}
static int __devinit ucmb_probe(struct platform_device *pdev)
{
struct ucmb_platform_data *pdata;
struct ucmb *ucmb;
int err;
const int bus_num = ucmb_spi_busnum_count++;
struct spi_bitbang *bb;
pdata = pdev->dev.platform_data;
if (!pdata)
return -ENXIO;
ucmb = kzalloc(sizeof(struct ucmb), GFP_KERNEL);
if (!ucmb)
return -ENOMEM;
mutex_init(&ucmb->mutex);
ucmb->gpio_reset = pdata->gpio_reset;
ucmb->reset_activelow = pdata->reset_activelow;
ucmb->chunk_size = pdata->chunk_size;
#ifdef CONFIG_PREEMPT
/* A preemptible kernel does not need to sleep between
* chunks, because it can sleep at desire (if IRQs are enabled).
* So transmit/receive it all in one go. */
ucmb->chunk_size = 0;
#endif
/* Create the SPI GPIO bus master. */
#ifdef CONFIG_SPI_GPIO_MODULE
err = request_module("spi_gpio");
if (err)
printk(KERN_WARNING PFX "Failed to request spi_gpio module\n");
#endif /* CONFIG_SPI_GPIO_MODULE */
ucmb->spi_gpio_pdata.sck = pdata->gpio_sck;
ucmb->spi_gpio_pdata.mosi = pdata->gpio_mosi;
ucmb->spi_gpio_pdata.miso = pdata->gpio_miso;
ucmb->spi_gpio_pdata.num_chipselect = 1;
ucmb->spi_gpio_pdev.name = "spi_gpio";
ucmb->spi_gpio_pdev.id = bus_num;
ucmb->spi_gpio_pdev.dev.platform_data = &ucmb->spi_gpio_pdata;
err = platform_device_register(&ucmb->spi_gpio_pdev);
if (err) {
printk(KERN_ERR PFX "Failed to register SPI-GPIO platform device\n");
goto err_free_ucmb;
}
bb = platform_get_drvdata(&ucmb->spi_gpio_pdev);
if (!bb || !bb->master) {
printk(KERN_ERR PFX "No bitbanged master device found.\n");
goto err_unreg_spi_gpio_pdev;
}
/* Create the SPI device. */
ucmb->sdev = spi_alloc_device(bb->master);
if (!ucmb->sdev) {
printk(KERN_ERR PFX "Failed to allocate SPI device\n");
goto err_unreg_spi_gpio_pdev;
}
ucmb->sdev->max_speed_hz = pdata->max_speed_hz;
ucmb->sdev->chip_select = 0;
ucmb->sdev->mode = pdata->mode;
strlcpy(ucmb->sdev->modalias, "ucmb", /* We are the SPI driver. */
sizeof(ucmb->sdev->modalias));
ucmb->sdev->controller_data = (void *)pdata->gpio_cs;
err = spi_add_device(ucmb->sdev);
if (err) {
printk(KERN_ERR PFX "Failed to add SPI device\n");
goto err_free_spi_device;
}
/* Initialize the RESET line. */
if (pdata->gpio_reset != UCMB_NO_RESET) {
err = gpio_request(pdata->gpio_reset, pdata->name);
if (err) {
printk(KERN_ERR PFX
"Failed to request RESET GPIO line\n");
goto err_unreg_spi_device;
}
err = gpio_direction_output(pdata->gpio_reset,
pdata->reset_activelow);
if (err) {
printk(KERN_ERR PFX
"Failed to set RESET GPIO direction\n");
goto err_free_reset_gpio;
}
ucmb_reset_microcontroller(ucmb);
}
/* Create the Misc char device. */
ucmb->mdev.minor = MISC_DYNAMIC_MINOR;
ucmb->mdev.name = pdata->name;
ucmb->mdev.parent = &pdev->dev;
ucmb->mdev_fops.open = ucmb_open;
ucmb->mdev_fops.release = ucmb_release;
ucmb->mdev_fops.read = ucmb_read;
ucmb->mdev_fops.write = ucmb_write;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
ucmb->mdev_fops.unlocked_ioctl = ucmb_ioctl;
#else
ucmb->mdev_fops.ioctl = ucmb_ioctl;
#endif
ucmb->mdev.fops = &ucmb->mdev_fops;
err = misc_register(&ucmb->mdev);
if (err) {
printk(KERN_ERR PFX "Failed to register miscdev %s\n",
ucmb->mdev.name);
goto err_free_reset_gpio;
}
platform_set_drvdata(pdev, ucmb);
printk(KERN_INFO PFX "Registered message bus \"%s\"\n", pdata->name);
return 0;
err_free_reset_gpio:
if (pdata->gpio_reset != UCMB_NO_RESET)
gpio_free(pdata->gpio_reset);
err_unreg_spi_device:
spi_unregister_device(ucmb->sdev);
err_free_spi_device:
spi_dev_put(ucmb->sdev);
err_unreg_spi_gpio_pdev:
platform_device_unregister(&ucmb->spi_gpio_pdev);
err_free_ucmb:
kfree(ucmb);
return err;
}
static int __devexit ucmb_remove(struct platform_device *pdev)
{
struct ucmb *ucmb = platform_get_drvdata(pdev);
int err;
err = misc_deregister(&ucmb->mdev);
if (err) {
printk(KERN_ERR PFX "Failed to unregister miscdev %s\n",
ucmb->mdev.name);
}
if (ucmb->gpio_reset != UCMB_NO_RESET)
gpio_free(ucmb->gpio_reset);
spi_unregister_device(ucmb->sdev);
spi_dev_put(ucmb->sdev);
platform_device_unregister(&ucmb->spi_gpio_pdev);
kfree(ucmb);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver ucmb_driver = {
.driver = {
.name = "ucmb",
.owner = THIS_MODULE,
},
.probe = ucmb_probe,
.remove = __devexit_p(ucmb_remove),
};
int ucmb_device_register(struct ucmb_platform_data *pdata)
{
struct platform_device *pdev;
int err;
pdev = platform_device_alloc("ucmb", ucmb_pdev_id_count++);
if (!pdev) {
printk(KERN_ERR PFX "Failed to allocate platform device.\n");
return -ENOMEM;
}
err = platform_device_add_data(pdev, pdata, sizeof(*pdata));
if (err) {
printk(KERN_ERR PFX "Failed to add platform data.\n");
platform_device_put(pdev);
return err;
}
err = platform_device_add(pdev);
if (err) {
printk(KERN_ERR PFX "Failed to register platform device.\n");
platform_device_put(pdev);
return err;
}
pdata->pdev = pdev;
return 0;
}
EXPORT_SYMBOL(ucmb_device_register);
void ucmb_device_unregister(struct ucmb_platform_data *pdata)
{
if (!pdata->pdev)
return;
platform_device_unregister(pdata->pdev);
platform_device_put(pdata->pdev);
pdata->pdev = NULL;
}
EXPORT_SYMBOL(ucmb_device_unregister);
static int ucmb_modinit(void)
{
int err;
printk(KERN_INFO "Microcontroller message bus driver\n");
err = spi_register_driver(&ucmb_spi_driver);
if (err) {
printk(KERN_ERR PFX "Failed to register SPI driver\n");
return err;
}
err = platform_driver_register(&ucmb_driver);
if (err) {
printk(KERN_ERR PFX "Failed to register platform driver\n");
spi_unregister_driver(&ucmb_spi_driver);
return err;
}
return 0;
}
subsys_initcall(ucmb_modinit);
static void ucmb_modexit(void)
{
platform_driver_unregister(&ucmb_driver);
spi_unregister_driver(&ucmb_spi_driver);
}
module_exit(ucmb_modexit);

View File

@ -1,77 +0,0 @@
#ifndef LINUX_UCMB_H_
#define LINUX_UCMB_H_
#include <linux/types.h>
#include <linux/ioctl.h>
/* IOCTLs */
#define __UCMB_IOCTL ('U'|'C'|'M'|'B')
/** UCMB_IOCTL_RESETUC - Reset the microcontroller. */
#define UCMB_IOCTL_RESETUC _IO(__UCMB_IOCTL, 0)
/** UCMB_IOCTL_GMSGDELAY - Get the delay to wait before fetching the status. */
#define UCMB_IOCTL_GMSGDELAY _IOR(__UCMB_IOCTL, 1, unsigned int)
/** UCMB_IOCTL_SMSGDELAY - Set the delay to wait before fetching the status. */
#define UCMB_IOCTL_SMSGDELAY _IOW(__UCMB_IOCTL, 2, unsigned int)
#ifdef __KERNEL__
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_gpio.h>
/**
* struct ucmb_platform_data - UCMB device descriptor
*
* @name: The name of the device. This will also be the name of
* the misc char device.
*
* @gpio_cs: The chipselect GPIO pin. Can be SPI_GPIO_NO_CHIPSELECT,
* if chipselect is not used.
* @gpio_sck: The clock GPIO pin.
* @gpio_miso: The master-in slave-out GPIO pin.
* @gpio_mosi: The master-out slave-in GPIO pin.
*
* @gpio_reset: The GPIO pin to the microcontroller reset line.
* Can be UCMB_NO_RESET, if reset GPIO is not used.
* @reset_activelow: If true, @gpio_reset is considered to be active
* on logical 0 (inverted).
*
* @mode: The SPI bus mode. SPI_MODE_*
* @max_speed_hz: The bus speed, in Hz. If zero the speed is not limited.
* @chunk_size: The maximum chunk size to transmit/receive in one go
* without sleeping. The kernel will be allowed to sleep
* after each chunk.
* If set to 0, the whole data will be transmitted/received
* in one big rush without sleeping. Note that this might hurt
* system responsiveness, if the kernel is not preemptible.
* If CONFIG_PREEMPT is enabled, chunk_size will be forced to 0.
*/
struct ucmb_platform_data {
const char *name;
unsigned long gpio_cs;
unsigned int gpio_sck;
unsigned int gpio_miso;
unsigned int gpio_mosi;
unsigned int gpio_reset;
bool reset_activelow;
u8 mode;
u32 max_speed_hz;
unsigned int chunk_size;
struct platform_device *pdev; /* internal */
};
#define UCMB_NO_RESET ((unsigned int)-1)
int ucmb_device_register(struct ucmb_platform_data *pdata);
void ucmb_device_unregister(struct ucmb_platform_data *pdata);
#endif /* __KERNEL__ */
#endif /* LINUX_UCMB_H_ */

View File

@ -1,233 +0,0 @@
/*
* 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 and footer) */
} __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, /* Listen for incoming messages. */
UCMB_ST_SENDSTATUS, /* Send the status report. */
UCMB_ST_SENDMESSAGE, /* Send the message. */
UCMB_ST_RETRSTATUS, /* Retrieve the status report. */
};
#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_RETRSTATUS;
}
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;
SPDR = 0;
switch (ucmb_state) {
case UCMB_ST_LISTEN: {
struct ucmb_message_hdr *hdr;
struct ucmb_message_footer *footer;
if (ucmb_buf_ptr < sizeof(ucmb_buf))
ucmb_buf[ucmb_buf_ptr] = data;
ucmb_buf_ptr++;
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 > 0x8000) {
/* Completely bogus length! Reset. */
ucmb_buf_ptr = 0;
return;
}
return;
}
if (ucmb_buf_ptr == sizeof(struct ucmb_message_hdr) +
sizeof(struct ucmb_message_footer) +
hdr->len) {
status_buf.magic = UCMB_MAGIC;
status_buf.code = UCMB_STAT_OK;
if (ucmb_buf_ptr > sizeof(ucmb_buf)) {
/* Message is way too big and was truncated. */
status_buf.code = UCMB_STAT_E2BIG;
} else {
footer = (struct ucmb_message_footer *)(
ucmb_buf + sizeof(struct ucmb_message_hdr) +
hdr->len);
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();
if (status_buf.code != UCMB_STAT_OK)
return; /* Corrupt message. Don't pass it to user code. */
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;
case UCMB_ST_RETRSTATUS: {
uint8_t *st = (uint8_t *)&status_buf;
st[ucmb_buf_ptr++] = data;
if (ucmb_buf_ptr == sizeof(struct ucmb_status)) {
/* We could possibly handle the status report here... */
ucmb_buf_ptr = 0;
ucmb_state = UCMB_ST_LISTEN;
}
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

@ -1,24 +0,0 @@
#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_ */

View File

@ -1,530 +0,0 @@
/*
* 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.
*
* This hotpath-assembly implementation is about twice as fast
* as the C implementation.
*
* 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 <avr/pgmspace.h>
#include <avr/wdt.h>
#ifndef __GNUC__
# error "Need the GNU C compiler"
#endif
#undef __naked
#define __naked __attribute__((__naked__))
#undef __used
#define __used __attribute__((__used__))
#undef __noret
#define __noret __attribute__((__noreturn__))
#undef offsetof
#define offsetof(type, member) ((size_t)&((type *)0)->member)
#undef unlikely
#define unlikely(x) __builtin_expect(!!(x), 0)
#undef mb
#define mb() __asm__ __volatile__("" : : : "memory") /* memory barrier */
#ifndef ucmb_errorlog
# define ucmb_errorlog(message) do { /* nothing */ } while (0)
#endif
struct ucmb_message_hdr {
uint16_t magic; /* UCMB_MAGIC */
uint16_t len; /* Payload length (excluding header and footer) */
} __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)] __used;
static uint16_t ucmb_buf_ptr __used;
static struct ucmb_status status_buf __used;
static uint16_t ucmb_send_message_len __used;
static uint8_t ucmb_received_message;
/* The current IRQ handler */
static void (*ucmb_interrupt_handler)(void) __used;
/* Polynomial: x^16 + x^15 + x^2 + 1 */
static const prog_uint16_t crc16_table[256] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
static inline uint16_t crc16_block_update(uint16_t crc, const void *_data, uint16_t size)
{
const uint8_t *data = _data;
uint8_t offset;
while (size--) {
wdt_reset();
offset = crc ^ *data;
crc = (crc >> 8) ^ pgm_read_word(&crc16_table[offset]);
data++;
}
return crc;
}
static inline 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;
}
/* The generic interrupt handler.
* This just branches to the state specific handler.
*/
ISR(SPI_STC_vect) __naked;
ISR(SPI_STC_vect)
{
__asm__ __volatile__(
" ; UCMB generic interrupt handler \n"
" push __tmp_reg__ \n"
" in __tmp_reg__, __SREG__ \n"
" push r30 \n"
" push r31 \n"
" lds r30, ucmb_interrupt_handler + 0 \n"
" lds r31, ucmb_interrupt_handler + 1 \n"
" ijmp ; Jump to the real handler \n"
);
}
#define UCMB_IRQ_EPILOGUE \
" ; UCMB interrupt epilogue (start) \n"\
" pop r31 \n"\
" pop r30 \n"\
" out __SREG__, __tmp_reg__ \n"\
" pop __tmp_reg__ \n"\
" reti \n"\
" ; UCMB interrupt epilogue (end) \n"
static void __naked __used ucmb_handler_LISTEN(void)
{
__asm__ __volatile__(
" push r16 \n"
" push r17 \n"
" push r18 \n"
" lds r16, ucmb_buf_ptr + 0 \n"
" lds r17, ucmb_buf_ptr + 1 \n"
" ldi r18, hi8(%[sizeof_buf]) \n"
" cpi r16, lo8(%[sizeof_buf]) \n"
" cpc r17, r18 \n"
" in r18, %[_SPDR] \n"
" brsh 1f ; overflow \n"
" ; Store SPDR in the buffer \n"
" movw r30, r16 \n"
" subi r30, lo8(-(ucmb_buf)) \n"
" sbci r31, hi8(-(ucmb_buf)) \n"
" st Z, r18 \n"
"1: \n"
" ; Increment the buffer pointer \n"
" subi r16, lo8(-1) \n"
" sbci r17, hi8(-1) \n"
" sts ucmb_buf_ptr + 0, r16 \n"
" sts ucmb_buf_ptr + 1, r17 \n"
" cpi r17, 0 \n"
" brne 1f \n"
" cpi r16, %[sizeof_msg_hdr] \n"
" breq hdr_sanity_check ; buf_ptr == hdrlen \n"
" brlo st_listen_out ; buf_ptr < hdrlen \n"
"1: \n"
" ; Get payload length from header \n"
" lds r30, (ucmb_buf + %[offsetof_hdr_len] + 0) \n"
" lds r31, (ucmb_buf + %[offsetof_hdr_len] + 1) \n"
" ; Add header and footer length to get full length \n"
" subi r30, lo8(-(%[sizeof_msg_hdr] + %[sizeof_msg_footer])) \n"
" sbci r31, hi8(-(%[sizeof_msg_hdr] + %[sizeof_msg_footer])) \n"
" ; Check if we have the full packet \n"
" cp r30, r16 \n"
" cpc r31, r17 \n"
" breq st_listen_have_full_packet \n"
"st_listen_out: \n"
" pop r18 \n"
" pop r17 \n"
" pop r16 \n"
UCMB_IRQ_EPILOGUE /* reti */
" \n"
"hdr_sanity_check: \n"
" lds r30, (ucmb_buf + %[offsetof_hdr_magic] + 0) \n"
" lds r31, (ucmb_buf + %[offsetof_hdr_magic] + 1) \n"
" ldi r18, hi8(%[_UCMB_MAGIC]) \n"
" cpi r30, lo8(%[_UCMB_MAGIC]) \n"
" cpc r31, r18 \n"
" brne invalid_hdr_magic \n"
" lds r30, (ucmb_buf + %[offsetof_hdr_len] + 0) \n"
" lds r31, (ucmb_buf + %[offsetof_hdr_len] + 1) \n"
" ldi r18, hi8(0x8001) \n"
" cpi r30, lo8(0x8001) \n"
" cpc r31, r18 \n"
" brsh bogus_payload_len \n"
" rjmp st_listen_out \n"
" \n"
"invalid_hdr_magic: \n"
" ; Invalid magic number in the packet header. Reset. \n"
"bogus_payload_len: \n"
" ; Bogus payload length in packet header. Reset. \n"
" clr r18 \n"
" sts ucmb_buf_ptr + 0, r18 \n"
" sts ucmb_buf_ptr + 1, r18 \n"
" rjmp st_listen_out \n"
" \n"
"st_listen_have_full_packet: \n"
" ; We have the full packet. Any SPI transfer is stopped \n"
" ; while we are processing the packet, so this \n"
" ; is a slowpath. \n"
" ; Disable SPI and pass control to ucmb_work to \n"
" ; handle the message. \n"
" cbi %[_SPCR], %[_SPIE] \n"
" ldi r18, 1 \n"
" sts ucmb_received_message, r18 \n"
" rjmp st_listen_out \n"
: /* none */
: [sizeof_buf] "i" (sizeof(ucmb_buf))
, [sizeof_msg_hdr] "M" (sizeof(struct ucmb_message_hdr))
, [sizeof_msg_footer] "M" (sizeof(struct ucmb_message_footer))
, [offsetof_hdr_magic] "M" (offsetof(struct ucmb_message_hdr, magic))
, [offsetof_hdr_len] "M" (offsetof(struct ucmb_message_hdr, len))
, [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
, [_SPCR] "M" (_SFR_IO_ADDR(SPCR))
, [_SPIE] "i" (SPIE)
, [_UCMB_MAGIC] "i" (UCMB_MAGIC)
: "memory"
);
}
static void __naked __used ucmb_handler_SENDSTATUS(void)
{
__asm__ __volatile__(
" push r16 \n"
/*" push r17 \n" */
" push r18 \n"
" lds r16, ucmb_buf_ptr + 0 \n"
" cpi r16, %[sizeof_ucmb_status] \n"
" brsh 1f ; This is the trailing byte \n"
" ; Write the next byte from status_buf to SPDR \n"
" mov r30, r16 \n"
" clr r31 \n"
" subi r30, lo8(-(status_buf)) \n"
" sbci r31, hi8(-(status_buf)) \n"
" ld r18, Z \n"
" out %[_SPDR], r18 \n"
"1: \n"
" subi r16, lo8(-1) \n"
" sts ucmb_buf_ptr + 0, r16 \n"
" cpi r16, (%[sizeof_ucmb_status] + 1) \n"
" brne st_sendstatus_out \n"
" ; Finished. Sent all status_buf bytes + trailing byte. \n"
" clr r18 \n"
" sts ucmb_buf_ptr + 0, r18 \n"
" ; Switch back to listening state... \n"
" ldi r18, lo8(gs(ucmb_handler_LISTEN)) \n"
" sts ucmb_interrupt_handler + 0, r18 \n"
" ldi r18, hi8(gs(ucmb_handler_LISTEN)) \n"
" sts ucmb_interrupt_handler + 1, r18 \n"
" ; ...if we have no pending transmission \n"
" lds r30, ucmb_send_message_len + 0 \n"
" lds r31, ucmb_send_message_len + 1 \n"
" clr r18 \n"
" cpi r30, 0 \n"
" cpc r31, r18 \n"
" breq st_sendstatus_out \n"
" ; Switch status to SENDMESSAGE and send the first byte. \n"
" ldi r18, lo8(gs(ucmb_handler_SENDMESSAGE)) \n"
" sts ucmb_interrupt_handler + 0, r18 \n"
" ldi r18, hi8(gs(ucmb_handler_SENDMESSAGE)) \n"
" sts ucmb_interrupt_handler + 1, r18 \n"
" ; Send the first byte \n"
" lds r18, ucmb_buf + 0 \n"
" out %[_SPDR], r18 \n"
" ldi r18, 1 \n"
" sts ucmb_buf_ptr + 0, r18 \n"
"st_sendstatus_out: \n"
" pop r18 \n"
/*" pop r17 \n"*/
" pop r16 \n"
UCMB_IRQ_EPILOGUE /* reti */
: /* none */
: [sizeof_ucmb_status] "M" (sizeof(struct ucmb_status))
, [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
: "memory"
);
}
static void __naked __used ucmb_handler_SENDMESSAGE(void)
{
__asm__ __volatile__(
" push r16 \n"
" push r17 \n"
" push r18 \n"
" lds r16, ucmb_buf_ptr + 0 \n"
" lds r17, ucmb_buf_ptr + 1 \n"
" lds r30, ucmb_send_message_len + 0 \n"
" lds r31, ucmb_send_message_len + 1 \n"
" cp r16, r30 \n"
" cpc r17, r31 \n"
" brsh 1f ; This is the trailing byte \n"
" movw r30, r16 \n"
" subi r30, lo8(-(ucmb_buf)) \n"
" sbci r31, hi8(-(ucmb_buf)) \n"
" ld r18, Z \n"
" out %[_SPDR], r18 \n"
"1: \n"
" subi r16, lo8(-1) \n"
" sbci r17, hi8(-1) \n"
" sts ucmb_buf_ptr + 0, r16 \n"
" sts ucmb_buf_ptr + 1, r17 \n"
" lds r30, ucmb_send_message_len + 0 \n"
" lds r31, ucmb_send_message_len + 1 \n"
" subi r30, lo8(-1) \n"
" sbci r31, hi8(-1) \n"
" cp r16, r30 \n"
" cpc r17, r31 \n"
" brne st_sendmessage_out \n"
" ; Message + trailing byte processed. Retrieve status. \n"
" clr r18 \n"
" sts ucmb_buf_ptr + 0, r18 \n"
" sts ucmb_buf_ptr + 1, r18 \n"
" ldi r18, lo8(gs(ucmb_handler_RETRSTATUS)) \n"
" sts ucmb_interrupt_handler + 0, r18 \n"
" ldi r18, hi8(gs(ucmb_handler_RETRSTATUS)) \n"
" sts ucmb_interrupt_handler + 1, r18 \n"
"st_sendmessage_out: \n"
" pop r18 \n"
" pop r17 \n"
" pop r16 \n"
UCMB_IRQ_EPILOGUE /* reti */
: /* none */
: [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
: "memory"
);
}
static void __naked __used ucmb_handler_RETRSTATUS(void)
{
__asm__ __volatile__(
" push r16 \n"
/*" push r17 \n"*/
" push r18 \n"
" in r18, %[_SPDR] \n"
" lds r16, ucmb_buf_ptr + 0 \n"
" mov r30, r16 \n"
" clr r31 \n"
" subi r30, lo8(-(status_buf)) \n"
" sbci r31, hi8(-(status_buf)) \n"
" st Z, r18 \n"
" subi r16, -1 \n"
" sts ucmb_buf_ptr + 0, r16 \n"
" cpi r16, %[sizeof_ucmb_status] \n"
" brne st_retrstatus_out \n"
" ; Completely received the status \n"
" clr r16 \n"
" sts ucmb_buf_ptr + 0, r16 \n"
" ; Switch back to listening state... \n"
" ldi r18, lo8(gs(ucmb_handler_LISTEN)) \n"
" sts ucmb_interrupt_handler + 0, r18 \n"
" ldi r18, hi8(gs(ucmb_handler_LISTEN)) \n"
" sts ucmb_interrupt_handler + 1, r18 \n"
" ; Check status-report magic value \n"
" lds r30, (status_buf + %[offsetof_status_magic] + 0) \n"
" lds r31, (status_buf + %[offsetof_status_magic] + 1) \n"
" ldi r18, hi8(%[_UCMB_MAGIC]) \n"
" cpi r30, lo8(%[_UCMB_MAGIC]) \n"
" cpc r31, r18 \n"
" brne invalid_status_magic \n"
" ; Check status-report error code \n"
" lds r30, (status_buf + %[offsetof_status_code] + 0) \n"
" lds r31, (status_buf + %[offsetof_status_code] + 1) \n"
" ldi r18, hi8(%[_UCMB_STAT_OK]) \n"
" cpi r30, lo8(%[_UCMB_STAT_OK]) \n"
" cpc r31, r18 \n"
" brne faulty_status_code \n"
"st_retrstatus_out: \n"
" pop r18 \n"
/*" pop r17 \n"*/
" pop r16 \n"
UCMB_IRQ_EPILOGUE /* reti */
" \n"
"invalid_status_magic: \n"
"faulty_status_code: \n"
" ; Branch to the C error handler \n"
" ; The handler does not return, so we don't need to \n"
" ; push/pop the registers. \n"
" clr __zero_reg__ \n"
" rjmp ucmb_received_faulty_status \n"
: /* none */
: [sizeof_ucmb_status] "M" (sizeof(struct ucmb_status))
, [offsetof_status_magic] "M" (offsetof(struct ucmb_status, magic))
, [offsetof_status_code] "M" (offsetof(struct ucmb_status, code))
, [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
, [_UCMB_MAGIC] "i" (UCMB_MAGIC)
, [_UCMB_STAT_OK] "i" (UCMB_STAT_OK)
: "memory"
);
}
/* We received a status report with an error condition.
* This is called from assembly code. The function does not return. */
static void __used __noret ucmb_received_faulty_status(void)
{
/* The master sent us a status report with an error code.
* Something's wrong with us. Print a status message and
* get caught by the watchdog, yummy.
*/
cli();
wdt_disable();
wdt_enable(WDTO_15MS);
ucmb_errorlog("UCMB: Received faulty status report. Triggering reset.");
while (1) {
/* "It's Coming Right For Us!" */
}
}
void ucmb_work(void)
{
struct ucmb_message_hdr *hdr;
struct ucmb_message_footer *footer;
uint16_t payload_len;
if (!ucmb_received_message)
return;
hdr = (struct ucmb_message_hdr *)ucmb_buf;
payload_len = hdr->len;
status_buf.magic = UCMB_MAGIC;
status_buf.code = UCMB_STAT_OK;
if (unlikely(ucmb_buf_ptr > sizeof(ucmb_buf))) {
/* Message is way too big and was truncated. */
status_buf.code = UCMB_STAT_E2BIG;
} else {
footer = (struct ucmb_message_footer *)(
ucmb_buf + sizeof(struct ucmb_message_hdr) +
payload_len);
if (ucmb_calc_msg_buffer_crc() != footer->crc)
status_buf.code = UCMB_STAT_ECRC;
}
ucmb_interrupt_handler = ucmb_handler_SENDSTATUS;
ucmb_buf_ptr = 0;
/* Send the first byte */
SPDR = ((uint8_t *)&status_buf)[ucmb_buf_ptr];
ucmb_buf_ptr++;
if (unlikely(status_buf.code != UCMB_STAT_OK))
goto out; /* Corrupt message. Don't pass it to user code. */
ucmb_send_message_len = ucmb_rx_message(
ucmb_buf + sizeof(struct ucmb_message_hdr),
payload_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();
ucmb_send_message_len += sizeof(struct ucmb_message_hdr) +
sizeof(struct ucmb_message_footer);
}
out:
ucmb_received_message = 0;
mb();
/* Re-enable SPI */
SPCR |= (1 << SPIE);
}
void ucmb_init(void)
{
ucmb_interrupt_handler = ucmb_handler_LISTEN;
/* SPI slave mode 0 with IRQ enabled. */
DDRB |= (1 << 6/*MISO*/);
DDRB &= ~((1 << 7/*SCK*/) | (1 << 5/*MOSI*/) | (1 << 4/*SS*/));
SPCR = (1 << SPE) | (1 << SPIE) /*| (1 << CPOL) | (1 << CPHA)*/;
(void)SPSR; /* clear state */
(void)SPDR; /* clear state */
}

View File

@ -1,36 +0,0 @@
#ifndef UCMB_AVR8_H_
#define UCMB_AVR8_H_
#include <stdint.h>
/* Max length of the message payload. */
#define UCMB_MAX_MSG_LEN (128 * 64 / 8 + 16)
/* Error logs: If you want UCMB error log messages, define
* ucmb_errorlog(message_string_literal)
* somewhere. If you don't define it, UCMB will be compiled
* without error messages. */
/* 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.
* This function is called with interrupts enabled.
*/
extern uint16_t ucmb_rx_message(uint8_t *payload,
uint16_t payload_length);
/* ucmb_work - Frequently call this from the mainloop.
* Must not be called from interrupt context.
* Must not be called with interrupts disabled.
*/
void ucmb_work(void);
/* Initialize the UCMB subsystem. */
void ucmb_init(void);
#endif /* UCMB_AVR8_H_ */