+++ /dev/null
-/*
- * 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;
-}
* 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_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"
#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)
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) {
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)
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)
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)
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)
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;
}