From 6fc097f074b9f0339f8e3230b034ec16f1492aa1 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Wed, 23 Jun 2021 16:28:02 +0200 Subject: [PATCH] pwm: replace gdbus with direct implementation Move open/close code from peripheral-bus. Add flocks() where appropriate. Change-Id: If2554f2d30389836eb39794dffaf9e34df31176a --- CMakeLists.txt | 1 - include/gdbus/peripheral_gdbus_pwm.h | 25 --- include/peripheral_handle.h | 3 +- src/gdbus/peripheral_gdbus_pwm.c | 149 --------------- src/peripheral_pwm.c | 340 ++++++++++++++++++++++++++++++++--- 5 files changed, 321 insertions(+), 197 deletions(-) delete mode 100644 include/gdbus/peripheral_gdbus_pwm.h delete mode 100644 src/gdbus/peripheral_gdbus_pwm.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fbd65b5..34b4bab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,6 @@ SET(SOURCES src/peripheral_gpio.c src/peripheral_uart.c src/peripheral_spi.c src/interface/peripheral_interface_pwm.c - src/gdbus/peripheral_gdbus_pwm.c src/gdbus/peripheral_io_gdbus.c) ADD_LIBRARY(${fw_name} SHARED ${SOURCES}) diff --git a/include/gdbus/peripheral_gdbus_pwm.h b/include/gdbus/peripheral_gdbus_pwm.h deleted file mode 100644 index 70a4b02..0000000 --- a/include/gdbus/peripheral_gdbus_pwm.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_PWM_H__ -#define __PERIPHERAL_GDBUS_PWM_H__ - -#include "peripheral_gdbus_common.h" - -int peripheral_gdbus_pwm_open(peripheral_pwm_h pwm, int chip, int pin); -int peripheral_gdbus_pwm_close(peripheral_pwm_h pwm); - -#endif /* __PERIPHERAL_GDBUS_PWM_H__ */ diff --git a/include/peripheral_handle.h b/include/peripheral_handle.h index e7fb06f..4585021 100644 --- a/include/peripheral_handle.h +++ b/include/peripheral_handle.h @@ -61,7 +61,8 @@ struct _peripheral_i2c_s { * @brief Internal struct for pwm context */ struct _peripheral_pwm_s { - uint handle; + int chip; + int pin; int fd_period; int fd_duty_cycle; int fd_polarity; diff --git a/src/gdbus/peripheral_gdbus_pwm.c b/src/gdbus/peripheral_gdbus_pwm.c deleted file mode 100644 index 74fae2c..0000000 --- a/src/gdbus/peripheral_gdbus_pwm.c +++ /dev/null @@ -1,149 +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_pwm.h" - -#define PWM_FD_INDEX_PERIOD 0 -#define PWM_FD_INDEX_DUTY_CYCLE 1 -#define PWM_FD_INDEX_POLARITY 2 -#define PWM_FD_INDEX_ENABLE 3 - -static PeripheralIoGdbusPwm *pwm_proxy = NULL; - -static int __pwm_proxy_init(void) -{ - GError *error = NULL; - - if (pwm_proxy != NULL) { - _E("Pwm proxy is already created"); - g_object_ref(pwm_proxy); - return PERIPHERAL_ERROR_NONE; - } - - pwm_proxy = peripheral_io_gdbus_pwm_proxy_new_for_bus_sync( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - PERIPHERAL_GDBUS_NAME, - PERIPHERAL_GDBUS_PWM_PATH, - NULL, - &error); - - if (pwm_proxy == NULL) { - if (error) { - _E("Failed to create pwm proxy : %s", error->message); - g_error_free(error); - } - return PERIPHERAL_ERROR_IO_ERROR; - } - - return PERIPHERAL_ERROR_NONE; -} - -static int __pwm_proxy_deinit(void) -{ - RETVM_IF(pwm_proxy == NULL, PERIPHERAL_ERROR_IO_ERROR, "Pwm proxy is NULL"); - - g_object_unref(pwm_proxy); - if (!G_IS_OBJECT(pwm_proxy)) - pwm_proxy = NULL; - - return PERIPHERAL_ERROR_NONE; -} - -int peripheral_gdbus_pwm_open(peripheral_pwm_h pwm, int chip, int pin) -{ - int ret; - GError *error = NULL; - GUnixFDList *fd_list = NULL; - - ret = __pwm_proxy_init(); - if (ret != PERIPHERAL_ERROR_NONE) - return ret; - - if (peripheral_io_gdbus_pwm_call_open_sync( - pwm_proxy, - chip, - pin, - NULL, - &pwm->handle, - &ret, - &fd_list, - NULL, - &error) == FALSE) { - _E("Failed to request daemon to pwm 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; - - pwm->fd_period = g_unix_fd_list_get(fd_list, PWM_FD_INDEX_PERIOD, &error); - if (pwm->fd_period < 0) { - _E("Failed to get fd for pwm period : %s", error->message); - g_error_free(error); - ret = PERIPHERAL_ERROR_IO_ERROR; - } - - pwm->fd_duty_cycle = g_unix_fd_list_get(fd_list, PWM_FD_INDEX_DUTY_CYCLE, &error); - if (pwm->fd_duty_cycle < 0) { - _E("Failed to get fd for pwm duty cycle : %s", error->message); - g_error_free(error); - ret = PERIPHERAL_ERROR_IO_ERROR; - } - - pwm->fd_polarity = g_unix_fd_list_get(fd_list, PWM_FD_INDEX_POLARITY, &error); - if (pwm->fd_polarity < 0) { - _E("Failed to get fd for pwm polarity : %s", error->message); - g_error_free(error); - ret = PERIPHERAL_ERROR_IO_ERROR; - } - - pwm->fd_enable = g_unix_fd_list_get(fd_list, PWM_FD_INDEX_ENABLE, &error); - if (pwm->fd_enable < 0) { - _E("Failed to get fd for pwm enable : %s", error->message); - g_error_free(error); - ret = PERIPHERAL_ERROR_IO_ERROR; - } - - g_object_unref(fd_list); - - return ret; -} - -int peripheral_gdbus_pwm_close(peripheral_pwm_h pwm) -{ - RETVM_IF(pwm_proxy == NULL, PERIPHERAL_ERROR_IO_ERROR, "Pwm proxy is NULL"); - - int ret; - GError *error = NULL; - - if (peripheral_io_gdbus_pwm_call_close_sync( - pwm_proxy, - pwm->handle, - &ret, - NULL, - &error) == FALSE) { - _E("Failed to request daemon to gpio pwm : %s", error->message); - g_error_free(error); - return PERIPHERAL_ERROR_IO_ERROR; - } - - __pwm_proxy_deinit(); - - return ret; -} diff --git a/src/peripheral_pwm.c b/src/peripheral_pwm.c index d6a306a..51b447c 100644 --- a/src/peripheral_pwm.c +++ b/src/peripheral_pwm.c @@ -14,13 +14,14 @@ * limitations under the License. */ +#include +#include #include +#include #include #include "peripheral_io.h" -#include "peripheral_handle.h" -#include "peripheral_gdbus_pwm.h" -#include "peripheral_interface_pwm.h" +#include "peripheral_interface_common.h" #include "peripheral_log.h" #define PERIPHERAL_IO_PWM_FEATURE "http://tizen.org/feature/peripheral_io.pwm" @@ -29,6 +30,8 @@ #define PWM_FEATURE_FALSE 0 #define PWM_FEATURE_TRUE 1 +#define PWM_BUF_MAX 16 + static int pwm_feature = PWM_FEATURE_UNKNOWN; static bool __is_feature_supported(void) @@ -46,16 +49,248 @@ static bool __is_feature_supported(void) return (pwm_feature == PWM_FEATURE_TRUE ? true : false); } +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; +#define PWMCHIP_BASE "pwmchip" + char pwmchip_name[sizeof(PWMCHIP_BASE "1234567890")]; /* space for pwmchip%d */ +#define PWM_BASE "pwm" + char pwm_name[sizeof(PWM_BASE "1234567890")]; /* space for pwm%d */ + + pfd.fd = udev_monitor_get_fd(monitor); + pfd.events = POLLIN; + + snprintf(pwmchip_name, sizeof pwmchip_name, PWMCHIP_BASE "%d", chip); + snprintf(pwm_name, sizeof pwm_name, PWM_BASE "%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) { + const char *prop = udev_device_get_property_value(dev, "EXPORT"); + if (prop && strcmp(prop, 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; +} + +static int peripheral_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 = -1; + int length; +#define EXPORT_PATH(chip) ("/sys/class/pwm/pwmchip" chip "/export") + char path[sizeof EXPORT_PATH("1234567890")] = {0, }; /* space for /sys/class/pwm/pwmchip%d/export */ + char buf[sizeof "1234567890"] = {0, }; /* space for pin %d */ + 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, sizeof path, EXPORT_PATH("%d"), chip); + fd = open(path, 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) { + _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 = __pwm_wait_for_udev(monitor, chip, 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_pwm_lock_export(int chip) +{ + char path[sizeof EXPORT_PATH("1234567890")] = {0, }; /* space for /sys/class/pwm/pwmchip%d/export */ + snprintf(path, sizeof path, EXPORT_PATH("%d"), chip); + + int fd = open(path, O_WRONLY | O_CLOEXEC); + if (fd < 0) + return -1; + + if (flock(fd, LOCK_EX)) { + close(fd); + fd = -1; + } + + return fd; +} + +static int peripheral_pwm_unlock_export(int lock) +{ + close_fd(lock); + return -1; +} + +static void peripheral_pwm_unlock_exportp(int *lock) +{ + peripheral_pwm_unlock_export(*lock); +} + +static inline int cleanup_handle(peripheral_pwm_h handle) +{ + if (handle == NULL) + return PERIPHERAL_ERROR_NONE; + + int pin = handle->pin; + int chip = handle->chip; + __attribute__ ((cleanup(peripheral_pwm_unlock_exportp))) int lock = peripheral_pwm_lock_export(chip); + + close_fd(handle->fd_duty_cycle); + close_fd(handle->fd_polarity); + close_fd(handle->fd_enable); + close_fd(handle->fd_period); /* this releases chip+pin flock() */ + free(handle); + + if (pin <0) + return PERIPHERAL_ERROR_NONE; + + char buf[sizeof "1234567890"] = {0, }; /* space for pin %d */ +#define UNEXPORT_PATH(chip) ("/sys/class/pwm/pwmchip" chip "/unexport") + char path[sizeof UNEXPORT_PATH("1234567890")] = {0, }; /* space for /sys/class/pwm/pwmchip%d/unexport */ + snprintf(path, sizeof path, UNEXPORT_PATH("%d"), chip); + + int fd = open(path, O_WRONLY | O_CLOEXEC); + if (fd < 0) { + _E("pwm: 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("pwm: unexport pin %d: write() failed: %m", pin); + return PERIPHERAL_ERROR_IO_ERROR; + } + + return PERIPHERAL_ERROR_NONE; +} + +static inline void cleanup_handlep(peripheral_pwm_h *handle) +{ + cleanup_handle(*handle); +} int peripheral_pwm_open(int chip, int pin, peripheral_pwm_h *pwm) { - peripheral_pwm_h handle; int ret = PERIPHERAL_ERROR_NONE; RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported"); RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid pwm handle"); RETVM_IF(chip < 0 || pin < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + __attribute__ ((cleanup(peripheral_pwm_unlock_exportp))) int lock = -1; + __attribute__ ((cleanup(cleanup_handlep))) peripheral_pwm_h handle = NULL; +#define PWM_PATH_BASE(chip, pin, file) ("/sys/class/pwm/pwmchip" chip "/pwm" pin "/" file) + /* space for /sys/class/pwm/pwmchip%d/pwm%d/duty_cycle + which is longer than: + /sys/class/pwm/pwmchip%d/pwm%d/period + /sys/class/pwm/pwmchip%d/pwm%d/polarity + /sys/class/pwm/pwmchip%d/pwm%d/enable + */ + char path[sizeof (PWM_PATH_BASE("1234567890", "1234567890", "duty_cycle"))] = {0, }; + /* Initialize */ handle = (peripheral_pwm_h)calloc(1, sizeof(struct _peripheral_pwm_s)); if (handle == NULL) { @@ -63,35 +298,66 @@ int peripheral_pwm_open(int chip, int pin, peripheral_pwm_h *pwm) return PERIPHERAL_ERROR_OUT_OF_MEMORY; } - ret = peripheral_gdbus_pwm_open(handle, chip, pin); + handle->fd_period = -1; + handle->fd_duty_cycle = -1; + handle->fd_polarity = -1; + handle->fd_enable = -1; + handle->chip = -1; + handle->pin = -1; + + lock = peripheral_pwm_lock_export(chip); + RETVM_IF(lock == -1, PERIPHERAL_ERROR_IO_ERROR, "Failed to lock 'export'"); + + ret = peripheral_pwm_export(chip, pin); if (ret != PERIPHERAL_ERROR_NONE) { _E("Failed to open PWM chip : %d, pin : %d", chip, pin); free(handle); handle = NULL; } + handle->chip = chip; + handle->pin = pin; + + snprintf(path, sizeof path, PWM_PATH_BASE("%d", "%d", "period"), chip, pin); + handle->fd_period = open(path, O_RDWR | O_CLOEXEC); + CHECK_ERROR(handle->fd_period < 0); + + if (flock(handle->fd_period, LOCK_EX | LOCK_NB)) { + if (errno == EWOULDBLOCK) { + _E("pwm: chip %d, pin %d is not available", chip, pin); + return PERIPHERAL_ERROR_RESOURCE_BUSY; + } else { + _E("pwm: chip %d, pin %d flock() error: %m", chip, pin); + return PERIPHERAL_ERROR_IO_ERROR; + } + } + + snprintf(path, sizeof path, PWM_PATH_BASE("%d", "%d", "duty_cycle"), chip, pin); + handle->fd_duty_cycle = open(path, O_RDWR | O_CLOEXEC); + CHECK_ERROR(handle->fd_duty_cycle < 0); + + snprintf(path, sizeof path, PWM_PATH_BASE("%d", "%d", "polarity"), chip, pin); + handle->fd_polarity = open(path, O_RDWR | O_CLOEXEC); + CHECK_ERROR(handle->fd_polarity < 0); + + snprintf(path, sizeof path, PWM_PATH_BASE("%d", "%d", "enable"), chip, pin); + handle->fd_enable = open(path, O_RDWR | O_CLOEXEC); + CHECK_ERROR(handle->fd_enable < 0); + + lock = peripheral_pwm_unlock_export(lock); + *pwm = handle; + handle = NULL; return ret; } int peripheral_pwm_close(peripheral_pwm_h pwm) { - int ret = PERIPHERAL_ERROR_NONE; - RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported"); RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL"); - ret = peripheral_gdbus_pwm_close(pwm); - if (ret != PERIPHERAL_ERROR_NONE) - _E("Failed to close PWM chip, continuing anyway, ret : %d", ret); - - peripheral_interface_pwm_close(pwm); - - free(pwm); - pwm = NULL; - - return ret; + return cleanup_handle(pwm); } int peripheral_pwm_set_period(peripheral_pwm_h pwm, uint32_t period_ns) @@ -99,7 +365,15 @@ int peripheral_pwm_set_period(peripheral_pwm_h pwm, uint32_t period_ns) RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported"); RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL"); - return peripheral_interface_pwm_set_period(pwm, period_ns); + int ret; + int length; + char pwm_buf[PWM_BUF_MAX] = {0}; + + length = snprintf(pwm_buf, sizeof(pwm_buf), "%d", period_ns); + ret = write(pwm->fd_period, pwm_buf, length); + CHECK_ERROR(ret != length); + + return PERIPHERAL_ERROR_NONE; } int peripheral_pwm_set_duty_cycle(peripheral_pwm_h pwm, uint32_t duty_cycle_ns) @@ -107,7 +381,15 @@ int peripheral_pwm_set_duty_cycle(peripheral_pwm_h pwm, uint32_t duty_cycle_ns) RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported"); RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL"); - return peripheral_interface_pwm_set_duty_cycle(pwm, duty_cycle_ns); + int ret; + int length; + char pwm_buf[PWM_BUF_MAX] = {0}; + + length = snprintf(pwm_buf, sizeof(pwm_buf), "%d", duty_cycle_ns); + ret = write(pwm->fd_duty_cycle, pwm_buf, length); + CHECK_ERROR(ret != length); + + return PERIPHERAL_ERROR_NONE; } int peripheral_pwm_set_polarity(peripheral_pwm_h pwm, peripheral_pwm_polarity_e polarity) @@ -116,7 +398,15 @@ int peripheral_pwm_set_polarity(peripheral_pwm_h pwm, peripheral_pwm_polarity_e RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL"); RETVM_IF((polarity < PERIPHERAL_PWM_POLARITY_ACTIVE_HIGH) || (polarity > PERIPHERAL_PWM_POLARITY_ACTIVE_LOW), PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid polarity parameter"); - return peripheral_interface_pwm_set_polarity(pwm, polarity); + static predefined_type_s types[2] = { + {"normal", 6}, + {"inversed", 8} + }; + + int ret = write(pwm->fd_polarity, types[polarity].type, types[polarity].len); + CHECK_ERROR(ret != types[polarity].len); + + return PERIPHERAL_ERROR_NONE; } int peripheral_pwm_set_enabled(peripheral_pwm_h pwm, bool enable) @@ -124,5 +414,13 @@ int peripheral_pwm_set_enabled(peripheral_pwm_h pwm, bool enable) RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported"); RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL"); - return peripheral_interface_pwm_set_enable(pwm, enable); + static predefined_type_s types[2] = { + {"0", 1}, + {"1", 1} + }; + + int ret = write(pwm->fd_enable, types[enable].type, types[enable].len); + CHECK_ERROR(ret != types[enable].len); + + return PERIPHERAL_ERROR_NONE; } -- 2.7.4