ucmb: Fix the lowlevel protocol
authormb <mb@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Wed, 18 Feb 2009 11:54:09 +0000 (11:54 +0000)
committermb <mb@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Wed, 18 Feb 2009 11:54:09 +0000 (11:54 +0000)
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@14549 3c298f89-4303-0410-b956-a3cf2f4a3e73

package/ucmb/Makefile
package/ucmb/driver/Makefile [moved from package/ucmb/src/Makefile with 100% similarity]
package/ucmb/driver/ucmb.c [moved from package/ucmb/src/ucmb.c with 84% similarity]
package/ucmb/driver/ucmb.h [new file with mode: 0644]
package/ucmb/src/ucmb.h [deleted file]

index b0b569ebc8bc2b0f6271d06641ad44b3048cd173..793fb9ee2e8b6b39eeaa5b8bb36b831b6010e2f7 100644 (file)
@@ -12,9 +12,10 @@ PKG_RELEASE:=1
 
 include $(INCLUDE_DIR)/package.mk
 
+
 define KernelPackage/ucmb
   SUBMENU:=Other modules
-  DEPENDS:=@LINUX_2_6 +kmod-spi-gpio
+  DEPENDS:=@LINUX_2_6 +kmod-spi-gpio +kmod-crc16
   KCONFIG:=CONFIG_SPI=y \
           CONFIG_SPI_MASTER=y
   TITLE:=Microcontroller Message Bus (EXPERIMENTAL)
@@ -24,7 +25,7 @@ endef
 
 define Build/Prepare
        mkdir -p $(PKG_BUILD_DIR)
-       $(CP) ./src/* $(PKG_BUILD_DIR)/
+       $(CP) ./driver/* $(PKG_BUILD_DIR)/
 endef
 
 define Build/Compile
similarity index 84%
rename from package/ucmb/src/ucmb.c
rename to package/ucmb/driver/ucmb.c
index c2cb1a9c6c9bf8a56265d27b5194251ada31cacc..276553ada52e4e52d2a77802e78e07fef4c7c531 100644 (file)
 #include <linux/spi/spi_bitbang.h>
 #include <linux/gfp.h>
 #include <linux/delay.h>
+#include <linux/crc16.h>
 
 
 #define PFX    "ucmb: "
 
+#define DEBUG
+
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Microcontroller Message Bus");
@@ -28,6 +31,8 @@ MODULE_AUTHOR("Michael Buesch");
 
 
 struct ucmb {
+       unsigned int msg_delay_ms;
+
        /* Misc character device driver */
        struct miscdevice mdev;
        struct file_operations mdev_fops;
@@ -45,6 +50,10 @@ struct ucmb_message_hdr {
        __le16 len;             /* Payload length (excluding header) */
 } __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 */
@@ -58,6 +67,7 @@ enum ucmb_status_code {
        UCMB_STAT_EPROTO,       /* Protocol format error */
        UCMB_STAT_ENOMEM,       /* Out of memory */
        UCMB_STAT_E2BIG,        /* Message too big */
+       UCMB_STAT_ECRC,         /* CRC error */
 };
 
 
@@ -71,6 +81,9 @@ static struct ucmb_platform_data ucmb_list[] = {
                .gpio_sck       = 0,
                .gpio_miso      = 1,
                .gpio_mosi      = 2,
+               .mode           = SPI_MODE_0,
+               .max_speed_hz   = 128000, /* Hz */
+               .msg_delay_ms   = 1, /* mS */
        },
 };
 
@@ -106,6 +119,8 @@ static int ucmb_status_code_to_errno(enum ucmb_status_code code)
                return -ENOMEM;
        case UCMB_STAT_E2BIG:
                return -E2BIG;
+       case UCMB_STAT_ECRC:
+               return -EBADMSG;
        }
        return -EBUSY;
 }
@@ -115,7 +130,6 @@ static inline struct ucmb * filp_to_ucmb(struct file *filp)
        return container_of(filp->f_op, struct ucmb, mdev_fops);
 }
 
-/* FIXME offp */
 static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
                         size_t size, loff_t *offp)
 {
@@ -123,13 +137,11 @@ static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
        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;
 
-       err = -E2BIG;
-       if (size > PAGE_SIZE)
-               goto out;
-       if (size > UCMB_MAX_MSG_LEN)
-               goto out;
+       size = min_t(size_t, size, PAGE_SIZE);
 
        err = -ENOMEM;
        buf = (char *)__get_free_page(GFP_KERNEL);
@@ -139,6 +151,10 @@ static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
        err = spi_read(ucmb->sdev, (u8 *)&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;
@@ -147,17 +163,31 @@ static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
                goto out_free;
        size = le16_to_cpu(hdr.len);
        err = spi_read(ucmb->sdev, buf, size);
+       if (err)
+               goto out_free;
+       err = spi_read(ucmb->sdev, (u8 *)&footer, sizeof(footer));
        if (err)
                goto out_free;
 
-       err = -EFAULT;
-       if (copy_to_user(user_buf, buf, size))
+       crc = crc16(crc, &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:
-       status.code = err ? UCMB_STAT_ENOMEM : UCMB_STAT_OK;
        res = spi_write(ucmb->sdev, (u8 *)&status, sizeof(status));
        if (res && !err)
                err = res;
@@ -167,7 +197,6 @@ out:
        return err ? err : size;
 }
 
-/* FIXME offp */
 static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
                          size_t size, loff_t *offp)
 {
@@ -175,8 +204,10 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
        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;
        struct spi_transfer spi_hdr_xfer;
+       struct spi_transfer spi_footer_xfer;
        struct spi_transfer spi_data_xfer;
        struct spi_message spi_msg;
 
@@ -192,6 +223,10 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
                goto out_free;
        hdr.len = cpu_to_le16(size);
 
+       footer.crc = crc16(footer.crc, &hdr, sizeof(hdr));
+       footer.crc = crc16(footer.crc, buf, size);
+       footer.crc ^= 0xFFFF;
+
        spi_message_init(&spi_msg);
 
        memset(&spi_hdr_xfer, 0, sizeof(spi_hdr_xfer));
@@ -204,18 +239,28 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
        spi_data_xfer.len = size;
        spi_message_add_tail(&spi_data_xfer, &spi_msg);
 
+       memset(&spi_footer_xfer, 0, sizeof(spi_footer_xfer));
+       spi_footer_xfer.tx_buf = &footer;
+       spi_footer_xfer.len = sizeof(footer);
+       spi_message_add_tail(&spi_footer_xfer, &spi_msg);
+
        /* Send the message, including header. */
        err = spi_sync(ucmb->sdev, &spi_msg);
        if (err)
                goto out_free;
 
        /* The microcontroller deserves some time to process the message. */
-       msleep(1);
+       if (ucmb->msg_delay_ms)
+               msleep(ucmb->msg_delay_ms);
 
        /* Get the status code. */
        err = spi_read(ucmb->sdev, (u8 *)&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;
@@ -244,6 +289,7 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
        ucmb = kzalloc(sizeof(struct ucmb), GFP_KERNEL);
        if (!ucmb)
                return -ENOMEM;
+       ucmb->msg_delay_ms = pdata->msg_delay_ms;
 
        /* Create the SPI GPIO bus master. */
 
@@ -280,9 +326,9 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
                printk(KERN_ERR PFX "Failed to allocate SPI device\n");
                goto err_unreg_spi_gpio_pdev;
        }
-       ucmb->sdev->max_speed_hz = 500000;
+       ucmb->sdev->max_speed_hz = pdata->max_speed_hz;
        ucmb->sdev->chip_select = 0;
-       ucmb->sdev->mode = SPI_MODE_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;
diff --git a/package/ucmb/driver/ucmb.h b/package/ucmb/driver/ucmb.h
new file mode 100644 (file)
index 0000000..94fdc64
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef LINUX_UCMB_H_
+#define LINUX_UCMB_H_
+
+#include <linux/types.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.
+ * @gpio_sck:          The clock GPIO pin.
+ * @gpio_miso:         The master-in slave-out GPIO pin.
+ * @gpio_mosi:         The master-out slave-in GPIO pin.
+ *
+ * @mode:              The SPI bus mode. SPI_MODE_*
+ * @max_speed_hz:      The bus speed, in Hz. If zero the speed is not limited.
+ * @msg_delay_ms:      The message delay time, in milliseconds.
+ *                     This is the time the microcontroller takes to process
+ *                     one message.
+ */
+struct ucmb_platform_data {
+       const char *name;
+
+       unsigned long gpio_cs;
+       unsigned int gpio_sck;
+       unsigned int gpio_miso;
+       unsigned int gpio_mosi;
+
+       u8 mode;
+       u32 max_speed_hz;
+       unsigned int msg_delay_ms;
+
+       struct platform_device *pdev; /* internal */
+};
+
+#endif /* LINUX_UCMB_H_ */
diff --git a/package/ucmb/src/ucmb.h b/package/ucmb/src/ucmb.h
deleted file mode 100644 (file)
index cce4e74..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef LINUX_UCMB_H_
-#define LINUX_UCMB_H_
-
-struct ucmb_platform_data {
-       const char *name;
-       unsigned long gpio_cs;
-       unsigned int gpio_sck;
-       unsigned int gpio_miso;
-       unsigned int gpio_mosi;
-
-       struct platform_device *pdev; /* internal */
-};
-
-#endif /* LINUX_UCMB_H_ */