From: Adrian Szyndela Date: Tue, 22 Jun 2021 10:51:37 +0000 (+0200) Subject: gpio: replace gdbus with direct implementation X-Git-Tag: submit/tizen/20210629.011533~12 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=97d7a29f15c0291ba9a19601d6b6897e8f10535a;p=platform%2Fcore%2Fapi%2Fperipheral-io.git gpio: replace gdbus with direct implementation Move open/close code from peripheral-bus. Add flocks() where appropriate. Change-Id: Ib54d4b8dbf4dcd7a1300b6c48ec8207fbbce2476 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index b5ad98d..742b7b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index 7ccc4a3..0000000 --- a/include/gdbus/peripheral_gdbus_gpio.h +++ /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__ */ diff --git a/include/peripheral_handle.h b/include/peripheral_handle.h index b87dc49..e7fb06f 100644 --- a/include/peripheral_handle.h +++ b/include/peripheral_handle.h @@ -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; diff --git a/packaging/capi-system-peripheral-io.spec b/packaging/capi-system-peripheral-io.spec index 705b2cb..be714b3 100644 --- a/packaging/capi-system-peripheral-io.spec +++ b/packaging/capi-system-peripheral-io.spec @@ -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 index e38b9b2..0000000 --- a/src/gdbus/peripheral_gdbus_gpio.c +++ /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 diff --git a/src/peripheral_gpio.c b/src/peripheral_gpio.c index c75f911..fe6fc9d 100644 --- a/src/peripheral_gpio.c +++ b/src/peripheral_gpio.c @@ -14,12 +14,14 @@ * limitations under the License. */ +#include +#include #include +#include #include #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; } /**