+++ /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_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
* 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"
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;
}
*/
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;
}
/**