ucmb: Add msg-delay ioctls; Move device registration out of the driver; Add open...
authorMichael Büsch <mb@bu3sch.de>
Sat, 7 Mar 2009 11:58:10 +0000 (11:58 +0000)
committerMichael Büsch <mb@bu3sch.de>
Sat, 7 Mar 2009 11:58:10 +0000 (11:58 +0000)
SVN-Revision: 14769

utils/ucmb-tools/tools/ucmb.c
utils/ucmb/driver/ucmb.c
utils/ucmb/driver/ucmb.h

index f432b0ec42da925f4f6a52d7640efed8f45ed8e2..20cd1bfe818f92c2295d84074c0935b6c39bfde8 100644 (file)
@@ -29,6 +29,8 @@
 
 #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)
index 890a2abbd517889ace5548353b22908a5d4b6c93..9b89408059f632d718a89eb1c3790bdccb42d1a5 100644 (file)
@@ -43,7 +43,9 @@ MODULE_AUTHOR("Michael Buesch");
 struct ucmb {
        struct mutex mutex;
 
-       unsigned int msg_delay_ms;
+       bool is_open;
+
+       unsigned int msg_delay_usec;
        unsigned int gpio_reset;
        bool reset_activelow;
 
@@ -59,6 +61,9 @@ struct ucmb {
        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) */
@@ -85,22 +90,7 @@ enum ucmb_status_code {
 
 
 static int ucmb_spi_busnum_count = 1337;
-
-
-static struct ucmb_platform_data ucmb_list[] = {
-       { //FIXME don't define it here.
-               .name                   = "ucmb",
-               .gpio_cs                = SPI_GPIO_NO_CHIPSELECT,
-               .gpio_sck               = 0,
-               .gpio_miso              = 1,
-               .gpio_mosi              = 2,
-               .gpio_reset             = 3,
-               .reset_activelow        = 0,
-               .mode                   = SPI_MODE_0,
-               .max_speed_hz           = 128000, /* Hz */
-               .msg_delay_ms           = 1, /* mS */
-       },
-};
+static int ucmb_pdev_id_count;
 
 
 static int __devinit ucmb_spi_probe(struct spi_device *sdev)
@@ -138,7 +128,7 @@ static int ucmb_reset_microcontroller(struct ucmb *ucmb)
        ucmb_toggle_reset_line(ucmb, 1);
        msleep(50);
        ucmb_toggle_reset_line(ucmb, 0);
-       msleep(10);
+       msleep(50);
 
        return 0;
 }
@@ -165,6 +155,38 @@ 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;
+}
+
 static int ucmb_ioctl(struct inode *inode, struct file *filp,
                      unsigned int cmd, unsigned long arg)
 {
@@ -176,6 +198,26 @@ static int ucmb_ioctl(struct inode *inode, struct file *filp,
        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;
        }
@@ -308,9 +350,16 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
        if (err)
                goto out_free;
 
-       /* The microcontroller deserves some time to process the message. */
-       if (ucmb->msg_delay_ms)
-               msleep(ucmb->msg_delay_ms);
+       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, 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 = spi_read(ucmb->sdev, (u8 *)&status, sizeof(status));
@@ -351,7 +400,6 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
        if (!ucmb)
                return -ENOMEM;
        mutex_init(&ucmb->mutex);
-       ucmb->msg_delay_ms = pdata->msg_delay_ms;
        ucmb->gpio_reset = pdata->gpio_reset;
        ucmb->reset_activelow = pdata->reset_activelow;
 
@@ -426,6 +474,8 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
        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;
        ucmb->mdev_fops.ioctl = ucmb_ioctl;
@@ -490,67 +540,69 @@ static struct platform_driver ucmb_driver = {
        .remove         = __devexit_p(ucmb_remove),
 };
 
-static int ucmb_modinit(void)
+int ucmb_device_register(struct ucmb_platform_data *pdata)
 {
-       struct ucmb_platform_data *pdata;
        struct platform_device *pdev;
-       int err, i;
-
-       printk(KERN_INFO "Microcontroller message bus driver\n");
+       int err;
 
-       err = platform_driver_register(&ucmb_driver);
+       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 register platform driver\n");
+               printk(KERN_ERR PFX "Failed to add platform data.\n");
+               platform_device_put(pdev);
                return err;
        }
-       err = spi_register_driver(&ucmb_spi_driver);
+       err = platform_device_add(pdev);
        if (err) {
-               printk(KERN_ERR PFX "Failed to register SPI driver\n");
-               platform_driver_unregister(&ucmb_driver);
+               printk(KERN_ERR PFX "Failed to register platform device.\n");
+               platform_device_put(pdev);
                return err;
        }
+       pdata->pdev = pdev;
 
-       for (i = 0; i < ARRAY_SIZE(ucmb_list); i++) {
-               pdata = &ucmb_list[i];
+       return 0;
+}
+EXPORT_SYMBOL(ucmb_device_register);
 
-               pdev = platform_device_alloc("ucmb", i);
-               if (!pdev) {
-                       printk(KERN_ERR PFX "Failed to allocate platform device.\n");
-                       break;
-               }
-               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);
-                       break;
-               }
-               err = platform_device_add(pdev);
-               if (err) {
-                       printk(KERN_ERR PFX "Failed to register platform device.\n");
-                       platform_device_put(pdev);
-                       break;
-               }
-               pdata->pdev = pdev;
+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;
 }
-module_init(ucmb_modinit);
+subsys_initcall(ucmb_modinit);
 
 static void ucmb_modexit(void)
 {
-       struct ucmb_platform_data *pdata;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(ucmb_list); i++) {
-               pdata = &ucmb_list[i];
-
-               if (pdata->pdev) {
-                       platform_device_unregister(pdata->pdev);
-                       platform_device_put(pdata->pdev);
-               }
-       }
-       spi_unregister_driver(&ucmb_spi_driver);
        platform_driver_unregister(&ucmb_driver);
+       spi_unregister_driver(&ucmb_spi_driver);
 }
 module_exit(ucmb_modexit);
index 8455dd00257b168ab5ba78f760063264a667e7e4..d64f2e4f66f65103628c134f21ad830a14fb9e4a 100644 (file)
 
 /** 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
  *
@@ -33,9 +41,6 @@
  *
  * @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;
@@ -50,13 +55,15 @@ struct ucmb_platform_data {
 
        u8 mode;
        u32 max_speed_hz;
-       unsigned int msg_delay_ms;
 
        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_ */