gpio: Setup udev monitor before exporting pin 93/238093/1
authorKonrad Kuchciak <k.kuchciak@samsung.com>
Wed, 8 Jul 2020 09:23:55 +0000 (11:23 +0200)
committerKonrad Kuchciak <k.kuchciak@samsung.com>
Wed, 8 Jul 2020 09:24:19 +0000 (11:24 +0200)
This will get rid of race condition that may lead to
missing udev event after exporting gpio pin.
Udev event monitor is now created before export.

Change-Id: Ifbad613252243c24cb03ed1765d3d947d244f56a

src/interface/peripheral_interface_gpio.c

index 0a916aadfddde8f12710b7caab2e31dad79c22be..850711da3b9edb82cc1463bc060a5f6625c55546 100644 (file)
 #include "peripheral_interface_gpio.h"
 #include "peripheral_interface_common.h"
 
-static int __gpio_wait_for_udev(int pin)
+static int __gpio_create_udev_monitor(struct udev **udev, struct udev_monitor **monitor)
 {
-       struct udev *udev;
-       struct udev_monitor *monitor;
-       struct udev_device *dev;
-       struct pollfd pfd;
-       char gpio_name[MAX_BUF_LEN];
+       struct udev *_udev = NULL;
+       struct udev_monitor *_monitor = NULL;
        int ret = -EIO;
 
-       udev = udev_new();
-       if (!udev) {
+       _udev = udev_new();
+       if (!_udev) {
                _E("Cannot create udev");
-               return ret;
+               goto error;
        }
 
-       monitor = udev_monitor_new_from_netlink(udev, "udev");
-       if (!monitor) {
+       _monitor = udev_monitor_new_from_netlink(_udev, "udev");
+       if (!_monitor) {
                _E("Cannot create udev monitor");
-               udev_unref(udev);
-               return ret;
+               goto error;
        }
 
-       ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "gpio", NULL);
+       ret = udev_monitor_filter_add_match_subsystem_devtype(_monitor, "gpio", NULL);
        if (ret < 0) {
                _E("Failed to add monitor filter");
-               goto out;
+               goto error;
        }
 
-       ret = udev_monitor_enable_receiving(monitor);
+       ret = udev_monitor_enable_receiving(_monitor);
        if (ret < 0) {
                _E("Failed to enable udev receiving");
-               goto out;
+               goto error;
        }
 
+       *udev = _udev;
+       *monitor = _monitor;
+
+       return 0;
+
+error:
+       udev_monitor_unref(_monitor);
+       udev_unref(_udev);
+       return ret;
+}
+
+static int __gpio_wait_for_udev(struct udev_monitor *monitor, int pin)
+{
+       struct udev_device *dev;
+       struct pollfd pfd;
+       char gpio_name[MAX_BUF_LEN];
+
        pfd.fd = udev_monitor_get_fd(monitor);
        pfd.events = POLLIN;
 
        snprintf(gpio_name, MAX_BUF_LEN, "gpio%d", pin);
 
        for (int cnt = 0; cnt < 10; cnt++) {
+               _D("poll iteration");
                if (poll(&pfd, 1, 100) < 0) {
                        _E("Failed to watch udev monitor");
-                       goto out;
+                       return -EIO;
                }
 
                dev = udev_monitor_receive_device(monitor);
                if (dev) {
                        if (strcmp(udev_device_get_sysname(dev), gpio_name) == 0) {
                                _D("udev for %s is initialized", gpio_name);
-                               ret = 0;
-                               goto out;
+                               udev_device_unref(dev);
+                               return 0;
                        }
+                       udev_device_unref(dev);
                }
        }
        _E("Time out");
 
-out:
-       udev_monitor_unref(monitor);
-       udev_unref(udev);
-
-       return ret;
+       return 0;
 }
 
 int peripheral_interface_gpio_export(int pin)
@@ -88,27 +99,55 @@ int peripheral_interface_gpio_export(int pin)
        RETVM_IF(pin < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid gpio pin");
 
        int ret;
-       int fd;
+       int fd = -1;
        int length;
        char buf[MAX_BUF_LEN] = {0, };
+       struct udev *udev = NULL;
+       struct udev_monitor *monitor = NULL;
+
+       ret = __gpio_create_udev_monitor(&udev, &monitor);
+       if (ret != 0) {
+               _E("Failed to create udev monitor");
+               goto out;
+       };
 
        fd = open("/sys/class/gpio/export", O_WRONLY);
-       IF_ERROR_RETURN(fd < 0);
+       if (fd < 0) {
+               _E("open() failed: %m");
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
+       }
 
        length = snprintf(buf, MAX_BUF_LEN, "%d", pin);
        ret = write(fd, buf, length);
-       IF_ERROR_RETURN(ret != length, close(fd));
+       if (ret != length) {
+               _E("write() failed: %m");
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
+       }
 
        ret = close(fd);
-       IF_ERROR_RETURN(ret != 0);
+       if (ret != 0) {
+               _E("close() failed: %m");
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
+       }
 
-       ret = __gpio_wait_for_udev(pin);
+       ret = __gpio_wait_for_udev(monitor, pin);
        if (ret < 0) {
                _E("device nodes are not writable");
-               return PERIPHERAL_ERROR_IO_ERROR;
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
        }
 
-       return PERIPHERAL_ERROR_NONE;
+out:
+       if (fd != -1)
+               close(fd);
+
+       udev_monitor_unref(monitor);
+       udev_unref(udev);
+
+       return ret;
 }
 
 int peripheral_interface_gpio_unexport(int pin)