Use udev rules to make PWM nodes writable 18/237918/2
authorKonrad Kuchciak <k.kuchciak@samsung.com>
Mon, 6 Jul 2020 11:40:19 +0000 (13:40 +0200)
committerKonrad Kuchciak <k.kuchciak@samsung.com>
Tue, 7 Jul 2020 10:16:27 +0000 (12:16 +0200)
Change-Id: Iad2bc5d9c43cb6b849f6308232b8ee2e995112ff

packaging/90-peripheral-io.rules
src/interface/peripheral_interface_pwm.c

index ca910bd83f44304855a53d8399b7394e5425a2aa..d294e604222e76345b35309e668e943b0d6a3d8f 100644 (file)
@@ -11,6 +11,23 @@ SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add",\
  RUN+="/bin/chmod 0660 /sys%p/edge",\
  RUN+="/bin/chsmack -a * /sys%p/edge"
 
+SUBSYSTEM=="pwm", KERNEL=="pwm*", ACTION=="change",\
+ RUN+="/bin/chown root:root /sys%p/%E{EXPORT}/period",\
+ RUN+="/bin/chmod 0660 /sys%p/%E{EXPORT}/period",\
+ RUN+="/bin/chsmack -a * /sys%p/%E{EXPORT}/period"
+SUBSYSTEM=="pwm", KERNEL=="pwm*", ACTION=="change",\
+ RUN+="/bin/chown root:root /sys%p/%E{EXPORT}/duty_cycle",\
+ RUN+="/bin/chmod 0660 /sys%p/%E{EXPORT}/duty_cycle",\
+ RUN+="/bin/chsmack -a * /sys%p/%E{EXPORT}/duty_cycle"
+SUBSYSTEM=="pwm", KERNEL=="pwm*", ACTION=="change",\
+ RUN+="/bin/chown root:root /sys%p/%E{EXPORT}/polarity",\
+ RUN+="/bin/chmod 0660 /sys%p/%E{EXPORT}/polarity",\
+ RUN+="/bin/chsmack -a * /sys%p/%E{EXPORT}/polarity"
+SUBSYSTEM=="pwm", KERNEL=="pwm*", ACTION=="change",\
+ RUN+="/bin/chown root:root /sys%p/%E{EXPORT}/enable",\
+ RUN+="/bin/chmod 0660 /sys%p/%E{EXPORT}/enable",\
+ RUN+="/bin/chsmack -a * /sys%p/%E{EXPORT}/enable"
+
 SUBSYSTEM=="i2c-dev", KERNEL=="i2c-*", ACTION=="add", \
  MODE="0660", OWNER="root", GROUP="root", SECLABEL{smack}="*"
 
index a7e62c4bde10385fc1b72e325239604265aca5a7..9c1497577209d03e0b6d8e2313e6aaa7b535480a 100644 (file)
  */
 
 #include <stdlib.h>
+#include <libudev.h>
+#include <poll.h>
+
 #include "peripheral_interface_pwm.h"
 #include "peripheral_interface_common.h"
 
+static int __pwm_create_udev_monitor(struct udev **udev, struct udev_monitor **monitor)
+{
+       struct udev *_udev = NULL;
+       struct udev_monitor *_monitor = NULL;
+       int ret = -EIO;
+
+       _udev = udev_new();
+       if (!_udev) {
+               _E("Cannot create udev");
+               goto error;
+       }
+
+       _monitor = udev_monitor_new_from_netlink(_udev, "udev");
+       if (!_monitor) {
+               _E("Cannot create udev monitor");
+               goto error;
+       }
+
+       ret = udev_monitor_filter_add_match_subsystem_devtype(_monitor, "pwm", NULL);
+       if (ret < 0) {
+               _E("Failed to add monitor filter");
+               goto error;
+       }
+
+       ret = udev_monitor_enable_receiving(_monitor);
+       if (ret < 0) {
+               _E("Failed to enable udev receiving");
+               goto error;
+       }
+
+       *udev = _udev;
+       *monitor = _monitor;
+
+       return 0;
+
+error:
+       udev_monitor_unref(_monitor);
+       udev_unref(_udev);
+       return ret;
+}
+
+static int __pwm_wait_for_udev(struct udev_monitor *monitor, int chip, int pin)
+{
+       struct udev_device *dev = NULL;
+       struct pollfd pfd;
+       char pwmchip_name[MAX_BUF_LEN];
+       char pwm_name[MAX_BUF_LEN];
+
+       pfd.fd = udev_monitor_get_fd(monitor);
+       pfd.events = POLLIN;
+
+       snprintf(pwmchip_name, MAX_BUF_LEN, "pwmchip%d", chip);
+       snprintf(pwm_name, MAX_BUF_LEN, "pwm%d", pin);
+
+       for (int cnt = 0; cnt < 10; cnt++) {
+               _D("poll iteration");
+               if (poll(&pfd, 1, 100) < 0) {
+                       _E("Failed to watch udev monitor");
+                       return -EIO;
+               }
+
+               dev = udev_monitor_receive_device(monitor);
+               if (dev) {
+                       if (strcmp(udev_device_get_sysname(dev), pwmchip_name) == 0 &&
+                               strcmp(udev_device_get_property_value(dev, "EXPORT"), pwm_name) == 0) {
+                               _D("udev for %s is initialized", pwm_name);
+                               udev_device_unref(dev);
+                               return 0;
+                       }
+                       udev_device_unref(dev);
+               }
+       }
+       _E("Time out");
+
+       return 0;
+}
+
 int peripheral_interface_pwm_export(int chip, int pin)
 {
        RETVM_IF(chip < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid pwm chip");
        RETVM_IF(pin < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid pwm pin");
 
        int ret;
-       int fd;
+       int fd = -1;
        int length;
        char path[MAX_BUF_LEN] = {0, };
        char buf[MAX_BUF_LEN] = {0, };
+       struct udev *udev = NULL;
+       struct udev_monitor *monitor = NULL;
+
+       ret = __pwm_create_udev_monitor(&udev, &monitor);
+       if (ret != 0) {
+               _E("Failed to create udev monitor");
+               goto out;
+       };
 
        snprintf(path, MAX_BUF_LEN, "/sys/class/pwm/pwmchip%d/export", chip);
        fd = open(path, 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);
-
-       snprintf(buf, MAX_BUF_LEN, "chsmack -a \"*\" /sys/class/pwm/pwmchip%d/pwm%d/period", chip, pin);
-       ret = system(buf);
        if (ret != 0) {
-               _E("Failed to change period security label to read/write.");
-               return PERIPHERAL_ERROR_IO_ERROR;
+               _E("close() failed: %m");
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
        }
 
-       snprintf(buf, MAX_BUF_LEN, "chsmack -a \"*\" /sys/class/pwm/pwmchip%d/pwm%d/duty_cycle", chip, pin);
-       ret = system(buf);
-       if (ret != 0) {
-               _E("Failed to change duty_cycle security label to read/write.");
-               return PERIPHERAL_ERROR_IO_ERROR;
+       ret = __pwm_wait_for_udev(monitor, chip, pin);
+       if (ret < 0) {
+               _E("device nodes are not writable");
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
        }
 
-       snprintf(buf, MAX_BUF_LEN, "chsmack -a \"*\" /sys/class/pwm/pwmchip%d/pwm%d/polarity", chip, pin);
-       ret = system(buf);
-       if (ret != 0) {
-               _E("Failed to change polarity security label to read/write.");
-               return PERIPHERAL_ERROR_IO_ERROR;
-       }
+out:
+       if (fd != -1)
+               close(fd);
 
-       snprintf(buf, MAX_BUF_LEN, "chsmack -a \"*\" /sys/class/pwm/pwmchip%d/pwm%d/enable", chip, pin);
-       ret = system(buf);
-       if (ret != 0) {
-               _E("Failed to change enable security label to read/write.");
-               return PERIPHERAL_ERROR_IO_ERROR;
-       }
+       udev_monitor_unref(monitor);
+       udev_unref(udev);
 
-       return PERIPHERAL_ERROR_NONE;
+       return ret;
 }
 
 int peripheral_interface_pwm_unexport(int chip, int pin)