gpio: replace gdbus with direct implementation 56/260256/5
authorAdrian Szyndela <adrian.s@samsung.com>
Tue, 22 Jun 2021 10:51:37 +0000 (12:51 +0200)
committerAdrian Szyndela <adrian.s@samsung.com>
Fri, 25 Jun 2021 09:51:36 +0000 (11:51 +0200)
Move open/close code from peripheral-bus.
Add flocks() where appropriate.

Change-Id: Ib54d4b8dbf4dcd7a1300b6c48ec8207fbbce2476

CMakeLists.txt
include/gdbus/peripheral_gdbus_gpio.h [deleted file]
include/peripheral_handle.h
packaging/capi-system-peripheral-io.spec
src/gdbus/peripheral_gdbus_gpio.c [deleted file]
src/peripheral_gpio.c

index b5ad98d..742b7b4 100644 (file)
@@ -9,7 +9,7 @@ SET(fw_name "${project_prefix}-${service}-${submodule}")
 
 PROJECT(${fw_name})
 
-SET(dependents "dlog glib-2.0 gio-2.0 gio-unix-2.0 capi-base-common capi-system-info")
+SET(dependents "dlog glib-2.0 gio-2.0 gio-unix-2.0 capi-base-common capi-system-info libudev")
 SET(pc_dependents "capi-base-common")
 
 SET(CMAKE_INSTALL_PREFIX ${prefix})
@@ -62,7 +62,6 @@ SET(SOURCES src/peripheral_gpio.c
                        src/peripheral_spi.c
                        src/interface/peripheral_interface_gpio.c
                        src/interface/peripheral_interface_pwm.c
-                       src/gdbus/peripheral_gdbus_gpio.c
                        src/gdbus/peripheral_gdbus_pwm.c
                        src/gdbus/peripheral_io_gdbus.c)
 
diff --git a/include/gdbus/peripheral_gdbus_gpio.h b/include/gdbus/peripheral_gdbus_gpio.h
deleted file mode 100644 (file)
index 7ccc4a3..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __PERIPHERAL_GDBUS_GPIO_H__
-#define __PERIPHERAL_GDBUS_GPIO_H__
-
-#include "peripheral_gdbus_common.h"
-
-int peripheral_gdbus_gpio_open(peripheral_gpio_h gpio, int pin);
-int peripheral_gdbus_gpio_close(peripheral_gpio_h gpio);
-
-#endif /* __PERIPHERAL_GDBUS_GPIO_H__ */
index b87dc49..e7fb06f 100644 (file)
@@ -42,7 +42,7 @@ struct _peripheral_gpio_s {
        // otherwise update csapi accordingly
        int vermagic;
        interrupted_cb_info_s cb_info;
-       uint handle;
+       int pin;
        int fd_direction;
        int fd_edge;
        int fd_value;
index 705b2cb..be714b3 100644 (file)
@@ -13,6 +13,7 @@ BuildRequires:  pkgconfig(gio-2.0)
 BuildRequires:  pkgconfig(dlog)
 BuildRequires:  pkgconfig(capi-base-common)
 BuildRequires:  pkgconfig(capi-system-info)
+BuildRequires:  pkgconfig(libudev)
 %if 0%{?gcov:1}
 BuildRequires:  lcov
 BuildRequires:  zip
@@ -72,4 +73,4 @@ MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
 %manifest %{name}.manifest
 %defattr(-,root,root,-)
 %{_bindir}/peripheral-io-test
-%license LICENSE.APLv2
\ No newline at end of file
+%license LICENSE.APLv2
diff --git a/src/gdbus/peripheral_gdbus_gpio.c b/src/gdbus/peripheral_gdbus_gpio.c
deleted file mode 100644 (file)
index e38b9b2..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "peripheral_gdbus_gpio.h"
-
-#define GPIO_FD_INDEX_DIRECTION 0
-#define GPIO_FD_INDEX_EDGE      1
-#define GPIO_FD_INDEX_VALUE     2
-
-static PeripheralIoGdbusGpio *gpio_proxy = NULL;
-
-static int __gpio_proxy_init(void)
-{
-       GError *error = NULL;
-
-       if (gpio_proxy != NULL) {
-               _E("Gpio proxy is already created");
-               g_object_ref(gpio_proxy);
-               return PERIPHERAL_ERROR_NONE;
-       }
-
-       gpio_proxy = peripheral_io_gdbus_gpio_proxy_new_for_bus_sync(
-               G_BUS_TYPE_SYSTEM,
-               G_DBUS_PROXY_FLAGS_NONE,
-               PERIPHERAL_GDBUS_NAME,
-               PERIPHERAL_GDBUS_GPIO_PATH,
-               NULL,
-               &error);
-
-       if (gpio_proxy == NULL) {
-               if (error) {
-                       _E("Failed to create gpio proxy : %s", error->message);
-                       g_error_free(error);
-               }
-               return PERIPHERAL_ERROR_IO_ERROR;
-       }
-
-       return PERIPHERAL_ERROR_NONE;
-}
-
-static int __gpio_proxy_deinit(void)
-{
-       RETVM_IF(gpio_proxy == NULL, PERIPHERAL_ERROR_IO_ERROR, "Gpio proxy is NULL");
-
-       g_object_unref(gpio_proxy);
-       if (!G_IS_OBJECT(gpio_proxy))
-               gpio_proxy = NULL;
-
-       return PERIPHERAL_ERROR_NONE;
-}
-
-int peripheral_gdbus_gpio_open(peripheral_gpio_h gpio, int pin)
-{
-       int ret;
-       GError *error = NULL;
-       GUnixFDList *fd_list = NULL;
-
-       ret = __gpio_proxy_init();
-       if (ret != PERIPHERAL_ERROR_NONE)
-               return ret;
-
-       if (peripheral_io_gdbus_gpio_call_open_sync(
-                       gpio_proxy,
-                       pin,
-                       NULL,
-                       &gpio->handle,
-                       &ret,
-                       &fd_list,
-                       NULL,
-                       &error) == FALSE) {
-               _E("Failed to request daemon to gpio open : %s", error->message);
-               g_error_free(error);
-               return PERIPHERAL_ERROR_IO_ERROR;
-       }
-
-       // TODO : If ret is not PERIPHERAL_ERROR_NONE, fd list it NULL from daemon.
-       if (ret != PERIPHERAL_ERROR_NONE)
-               return ret;
-
-       gpio->fd_direction = g_unix_fd_list_get(fd_list, GPIO_FD_INDEX_DIRECTION, &error);
-       if (gpio->fd_direction < 0) {
-               _E("Failed to get fd for gpio direction : %s", error->message);
-               g_error_free(error);
-               ret = PERIPHERAL_ERROR_IO_ERROR;
-       }
-
-       gpio->fd_edge = g_unix_fd_list_get(fd_list, GPIO_FD_INDEX_EDGE, &error);
-       if (gpio->fd_edge < 0) {
-               _E("Failed to get fd for gpio edge : %s", error->message);
-               g_error_free(error);
-               ret = PERIPHERAL_ERROR_IO_ERROR;
-       }
-
-       gpio->fd_value = g_unix_fd_list_get(fd_list, GPIO_FD_INDEX_VALUE, &error);
-       if (gpio->fd_value < 0) {
-               _E("Failed to get fd for gpio value : %s", error->message);
-               g_error_free(error);
-               ret = PERIPHERAL_ERROR_IO_ERROR;
-       }
-
-       g_object_unref(fd_list);
-
-       return ret;
-}
-
-int peripheral_gdbus_gpio_close(peripheral_gpio_h gpio)
-{
-       RETVM_IF(gpio_proxy == NULL, PERIPHERAL_ERROR_IO_ERROR, "Gpio proxy is NULL");
-
-       int ret;
-       GError *error = NULL;
-
-       if (peripheral_io_gdbus_gpio_call_close_sync(
-                       gpio_proxy,
-                       gpio->handle,
-                       &ret,
-                       NULL,
-                       &error) == FALSE) {
-               _E("Failed to request daemon to gpio close : %s", error->message);
-               g_error_free(error);
-               return PERIPHERAL_ERROR_IO_ERROR;
-       }
-
-       __gpio_proxy_deinit();
-
-       return ret;
-}
\ No newline at end of file
index c75f911..fe6fc9d 100644 (file)
  * limitations under the License.
  */
 
+#include <libudev.h>
+#include <poll.h>
 #include <stdlib.h>
+#include <sys/file.h>
 #include <system_info.h>
 
 #include "peripheral_io.h"
 #include "peripheral_handle.h"
-#include "peripheral_gdbus_gpio.h"
 #include "peripheral_interface_gpio.h"
 #include "peripheral_log.h"
 
@@ -47,52 +49,295 @@ static bool __is_feature_supported(void)
        return (gpio_feature == GPIO_FEATURE_TRUE ? true : false);
 }
 
+static int __gpio_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, "gpio", 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 __gpio_wait_for_udev(struct udev_monitor *monitor, int pin)
+{
+       struct udev_device *dev;
+       struct pollfd pfd;
+#define GPIO_BASE "gpio"
+       char gpio_name[sizeof(GPIO_BASE "1234567890")];
+
+       pfd.fd = udev_monitor_get_fd(monitor);
+       pfd.events = POLLIN;
+
+       snprintf(gpio_name, sizeof gpio_name, "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");
+                       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);
+                               udev_device_unref(dev);
+                               return 0;
+                       }
+                       udev_device_unref(dev);
+               }
+       }
+       _E("Time out");
+
+       return 0;
+}
+
+static int peripheral_gpio_export(int pin)
+{
+       RETVM_IF(pin < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid gpio pin");
+
+       int ret;
+       int fd = -1;
+       int length;
+       char buf[sizeof "1234567890"] = {0, }; /* space for pin %d */
+       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 | O_CLOEXEC);
+       if (fd < 0) {
+               _E("open() failed: %m");
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
+       }
+
+       length = snprintf(buf, sizeof buf, "%d", pin);
+       ret = write(fd, buf, length);
+       if (ret != length) {
+               /* If write() fails with EBUSY, then it means
+                  this pin has been already exported.
+                  That's fine for us.
+                */
+               if (errno != EBUSY) {
+                       _E("write() failed: %m");
+                       ret = PERIPHERAL_ERROR_IO_ERROR;
+                       goto out;
+               }
+       }
+
+       ret = close(fd);
+       if (ret != 0) {
+               _E("close() failed: %m");
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
+       } else {
+               fd = -1;
+       }
+
+       ret = __gpio_wait_for_udev(monitor, pin);
+       if (ret < 0) {
+               _E("device nodes are not writable");
+               ret = PERIPHERAL_ERROR_IO_ERROR;
+               goto out;
+       }
+
+out:
+       if (fd != -1)
+               close(fd);
+
+       udev_monitor_unref(monitor);
+       udev_unref(udev);
+
+       return ret;
+}
+
+static inline void close_fd(int fd) {
+       if (fd != -1)
+               close(fd);
+}
+
+static int peripheral_gpio_lock_export(void)
+{
+       int fd = open("/sys/class/gpio/export", O_WRONLY | O_CLOEXEC);
+       if (fd < 0)
+               return -1;
+
+       if (flock(fd, LOCK_EX)) {
+               close(fd);
+               fd = -1;
+       }
+
+       return fd;
+}
+
+static int peripheral_gpio_unlock_export(int lock)
+{
+       close_fd(lock);
+       return -1;
+}
+
+static void peripheral_gpio_unlock_exportp(int *lock)
+{
+       peripheral_gpio_unlock_export(*lock);
+}
+
+static inline int cleanup_handle(peripheral_gpio_h handle) {
+       if (handle == NULL)
+               return PERIPHERAL_ERROR_NONE;
+
+       __attribute__ ((cleanup(peripheral_gpio_unlock_exportp))) int lock = peripheral_gpio_lock_export();
+       int pin = handle->pin;
+
+       close_fd(handle->fd_edge);
+       close_fd(handle->fd_value);
+       close_fd(handle->fd_direction); /* this also closes pin flock() */
+       free(handle);
+
+       if (pin < 0)
+               return PERIPHERAL_ERROR_NONE;
+
+       char buf[sizeof "1234567890"] = {0, };  /* space for %d */
+       int fd = open("/sys/class/gpio/unexport", O_WRONLY | O_CLOEXEC);
+       if (fd < 0) {
+               _E("gpio: unexport pin %d: open() failed: %m", pin);
+               return PERIPHERAL_ERROR_IO_ERROR;
+       }
+
+       int length = snprintf(buf, sizeof(buf), "%d", pin);
+       int ret = write(fd, buf, length);
+
+       close(fd);
+
+       if (ret != length) {
+               _E("gpio: unexport pin %d: write() failed: %m", pin);
+               return PERIPHERAL_ERROR_IO_ERROR;
+       }
+
+       return PERIPHERAL_ERROR_NONE;
+}
+
+static inline void cleanup_handlep(peripheral_gpio_h *handle) {
+       cleanup_handle(*handle);
+}
+
 /**
  * @brief Initializes(export) gpio pin and creates gpio handle.
  */
 int peripheral_gpio_open(int gpio_pin, peripheral_gpio_h *gpio)
 {
        int ret = PERIPHERAL_ERROR_NONE;
-       peripheral_gpio_h handle;
 
        RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "GPIO feature is not supported");
        RETVM_IF(gpio == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid gpio handle");
        RETVM_IF(gpio_pin < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid gpio pin number");
 
+       __attribute__ ((cleanup(cleanup_handlep))) peripheral_gpio_h handle = NULL;
+       __attribute__ ((cleanup(peripheral_gpio_unlock_exportp))) int lock = -1;
+
        /* Initialize */
        handle = (peripheral_gpio_h)calloc(1, sizeof(struct _peripheral_gpio_s));
        if (handle == NULL) {
                _E("Failed to allocate peripheral_gpio_h");
                return PERIPHERAL_ERROR_OUT_OF_MEMORY;
        }
+
        // set gpio_s_vermagic and callback vermagic so csapi can verify structure
        handle->vermagic = GPIO_STRUCTURE_VERMAGIC;
        handle->cb_info.vermagic = GPIO_CALLBACK_STRUCTURE_VERMAGIC;
-
-       ret = peripheral_gdbus_gpio_open(handle, gpio_pin);
-       if (ret != PERIPHERAL_ERROR_NONE) {
-               _E("Failed to open the gpio pin, ret : %d", ret);
-               free(handle);
-               handle = NULL;
-               return ret;
+       handle->fd_direction = -1;
+       handle->fd_edge = -1;
+       handle->fd_value = -1;
+       handle->pin = -1;
+
+       lock = peripheral_gpio_lock_export();
+       RETVM_IF(lock == -1, PERIPHERAL_ERROR_IO_ERROR, "Failed to lock 'export'");
+
+       ret = peripheral_gpio_export(gpio_pin);
+       RETVM_IF(ret != 0, PERIPHERAL_ERROR_IO_ERROR, "Failed to export the gpio pin, ret : %d", ret);
+
+       handle->pin = gpio_pin;
+
+#define DEV_PATH(pin) ("/sys/class/gpio/gpio" pin "/direction")
+       /* space for /sys/class/gpio/gpio%d/direction,
+          which is larger than /sys/class/gpio/gpio%d/edge and /sys/class/gpio/gpio%d/value
+        */
+       char path[sizeof(DEV_PATH("1234567890"))] = {0, };
+       snprintf(path, sizeof path, "/sys/class/gpio/gpio%d/direction", handle->pin);
+       handle->fd_direction = open(path, O_RDWR | O_CLOEXEC);
+       CHECK_ERROR(handle->fd_direction < 0);
+
+       if (flock(handle->fd_direction, LOCK_EX | LOCK_NB)) {
+               if (errno == EWOULDBLOCK) {
+                       _E("gpio: pin %d is not available", gpio_pin);
+                       return PERIPHERAL_ERROR_RESOURCE_BUSY;
+               } else {
+                       _E("gpio: pin %d flock() error: %d", gpio_pin, errno);
+                       return PERIPHERAL_ERROR_IO_ERROR;
+               }
        }
 
+       snprintf(path, sizeof path, "/sys/class/gpio/gpio%d/edge", handle->pin);
+       handle->fd_edge = open(path, O_RDWR | O_CLOEXEC);
+       CHECK_ERROR(handle->fd_edge < 0);
+
+       snprintf(path, sizeof path, "/sys/class/gpio/gpio%d/value", handle->pin);
+       handle->fd_value = open(path, O_RDWR | O_CLOEXEC);
+       CHECK_ERROR(handle->fd_value < 0);
+
+       lock = peripheral_gpio_unlock_export(lock);
+
        ret = peripheral_interface_gpio_set_initial_direction_into_handle(handle);
        if (ret != PERIPHERAL_ERROR_NONE) {
                _E("Failed to peripheral_interface_gpio_set_initial_direction_into_handle()");
-               peripheral_gpio_close(handle);
                return ret;
        }
 
        ret = peripheral_interface_gpio_set_initial_edge_into_handle(handle);
        if (ret != PERIPHERAL_ERROR_NONE) {
                _E("Failed to peripheral_interface_gpio_set_initial_edge_into_handle()");
-               peripheral_gpio_close(handle);
                return ret;
        }
 
        handle->cb_info.thread = NULL;
        *gpio = handle;
+       handle = NULL;
 
        return PERIPHERAL_ERROR_NONE;
 }
@@ -103,22 +348,14 @@ int peripheral_gpio_open(int gpio_pin, peripheral_gpio_h *gpio)
  */
 int peripheral_gpio_close(peripheral_gpio_h gpio)
 {
-       int ret = PERIPHERAL_ERROR_NONE;
-
        RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "GPIO feature is not supported");
        RETVM_IF(gpio == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "gpio handle is NULL");
 
-       /* call gpio_close */
-       ret = peripheral_gdbus_gpio_close(gpio);
-       if (ret != PERIPHERAL_ERROR_NONE)
-               _E("Failed to close the gpio pin, ret : %d", ret);
-
-       peripheral_interface_gpio_close(gpio);
+       peripheral_interface_gpio_unset_interrupted_cb(gpio);
 
-       free(gpio);
-       gpio = NULL;
+       cleanup_handlep(&gpio);
 
-       return ret;
+       return PERIPHERAL_ERROR_NONE;
 }
 
 /**