GPIODEV: Fix open-count race condition.
authorMichael Büsch <mb@bu3sch.de>
Wed, 19 Mar 2008 11:32:08 +0000 (11:32 +0000)
committerMichael Büsch <mb@bu3sch.de>
Wed, 19 Mar 2008 11:32:08 +0000 (11:32 +0000)
SVN-Revision: 10625

target/linux/generic-2.6/files/drivers/gpio/gpio_dev.c

index 690a3fb8c58c2b28889f45b03dd5831814e42386..48ef76f8a962ac7afef99244960a4a5b6309c5bc 100644 (file)
@@ -25,6 +25,7 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/gpio.h>
+#include <asm/atomic.h>
 #include <linux/init.h>
 #include <linux/genhd.h>
 #include <linux/device.h>
 #define DEVNAME                "gpio"
 
 static int dev_major;
-static int gpio_is_open = 0;
-unsigned int gpio_access_mask = 0;
+static unsigned int gpio_access_mask;
 static struct class *gpio_class;
 
+/* Counter is 1, if the device is not opened and zero (or less) if opened. */
+static atomic_t gpio_open_cnt = ATOMIC_INIT(1);
+
 static int
 gpio_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
 {
@@ -94,15 +97,18 @@ gpio_open(struct inode *inode, struct file *file)
                goto out;
        }
 
-       if (gpio_is_open)
-       {
+       /* FIXME: We should really allow multiple applications to open the device
+        *        at the same time, as long as the apps access different IO pins.
+        *        The generic gpio-registration functions can be used for that.
+        *        Two new IOCTLs have to be introduced for that. Need to check userspace
+        *        compatibility first. --mb */
+       if (!atomic_dec_and_test(&gpio_open_cnt)) {
+               atomic_inc(&gpio_open_cnt);
                printk(KERN_ERR DRVNAME ": Device with minor ID %d already in use\n", dev_minor);
                result = -EBUSY;
                goto out;
        }
 
-       gpio_is_open = 1;
-
 out:
        return result;
 }
@@ -110,7 +116,8 @@ out:
 static int
 gpio_close(struct inode * inode, struct file * file)
 {
-       gpio_is_open = 0;
+       smp_mb__before_atomic_inc();
+       atomic_inc(&gpio_open_cnt);
 
        return 0;
 }