aboutsummaryrefslogtreecommitdiffstats
path: root/bluez/mainloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'bluez/mainloop.c')
-rw-r--r--bluez/mainloop.c405
1 files changed, 405 insertions, 0 deletions
diff --git a/bluez/mainloop.c b/bluez/mainloop.c
new file mode 100644
index 0000000..b769c8f
--- /dev/null
+++ b/bluez/mainloop.c
@@ -0,0 +1,405 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2014 Intel Corporation
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <sys/timerfd.h>
+#include <sys/epoll.h>
+
+#include "mainloop.h"
+
+#define MAX_EPOLL_EVENTS 10
+
+static int epoll_fd;
+static int epoll_terminate;
+static int exit_status;
+
+struct mainloop_data {
+ int fd;
+ uint32_t events;
+ mainloop_event_func callback;
+ mainloop_destroy_func destroy;
+ void *user_data;
+};
+
+#define MAX_MAINLOOP_ENTRIES 128
+
+static struct mainloop_data *mainloop_list[MAX_MAINLOOP_ENTRIES];
+
+struct timeout_data {
+ int fd;
+ mainloop_timeout_func callback;
+ mainloop_destroy_func destroy;
+ void *user_data;
+};
+
+struct signal_data {
+ int fd;
+ sigset_t mask;
+ mainloop_signal_func callback;
+ mainloop_destroy_func destroy;
+ void *user_data;
+};
+
+static struct signal_data *signal_data;
+
+void mainloop_init(void)
+{
+ unsigned int i;
+
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+
+ for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++)
+ mainloop_list[i] = NULL;
+
+ epoll_terminate = 0;
+}
+
+void mainloop_quit(void)
+{
+ epoll_terminate = 1;
+}
+
+void mainloop_exit_success(void)
+{
+ exit_status = EXIT_SUCCESS;
+ epoll_terminate = 1;
+}
+
+void mainloop_exit_failure(void)
+{
+ exit_status = EXIT_FAILURE;
+ epoll_terminate = 1;
+}
+
+static void signal_callback(int fd, uint32_t events, void *user_data)
+{
+ struct signal_data *data = user_data;
+ struct signalfd_siginfo si;
+ ssize_t result;
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_quit();
+ return;
+ }
+
+ result = read(fd, &si, sizeof(si));
+ if (result != sizeof(si))
+ return;
+
+ if (data->callback)
+ data->callback(si.ssi_signo, data->user_data);
+}
+
+int mainloop_run(void)
+{
+ if (signal_data) {
+ if (sigprocmask(SIG_BLOCK, &signal_data->mask, NULL) < 0)
+ return EXIT_FAILURE;
+
+ signal_data->fd = signalfd(-1, &signal_data->mask,
+ SFD_NONBLOCK | SFD_CLOEXEC);
+ if (signal_data->fd < 0)
+ return EXIT_FAILURE;
+
+ if (mainloop_add_fd(signal_data->fd, EPOLLIN,
+ signal_callback, signal_data, NULL) < 0) {
+ close(signal_data->fd);
+ return EXIT_FAILURE;
+ }
+ }
+
+ exit_status = EXIT_SUCCESS;
+
+ while (!epoll_terminate) {
+ struct epoll_event events[MAX_EPOLL_EVENTS];
+ int n, nfds;
+
+ nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
+ if (nfds < 0)
+ continue;
+
+ for (n = 0; n < nfds; n++) {
+ struct mainloop_data *data = events[n].data.ptr;
+
+ data->callback(data->fd, events[n].events,
+ data->user_data);
+ }
+ }
+
+ if (signal_data) {
+ mainloop_remove_fd(signal_data->fd);
+ close(signal_data->fd);
+
+ if (signal_data->destroy)
+ signal_data->destroy(signal_data->user_data);
+ }
+ epoll_terminate = 0;
+
+
+ return exit_status;
+}
+
+void mainloop_finish(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++) {
+ struct mainloop_data *data = mainloop_list[i];
+
+ mainloop_list[i] = NULL;
+
+ if (data) {
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+
+ free(data);
+ }
+ }
+
+ close(epoll_fd);
+ epoll_fd = 0;
+}
+
+int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
+ void *user_data, mainloop_destroy_func destroy)
+{
+ struct mainloop_data *data;
+ struct epoll_event ev;
+ int err;
+
+ if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1 || !callback)
+ return -EINVAL;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
+ memset(data, 0, sizeof(*data));
+ data->fd = fd;
+ data->events = events;
+ data->callback = callback;
+ data->destroy = destroy;
+ data->user_data = user_data;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = events;
+ ev.data.ptr = data;
+
+ err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev);
+ if (err < 0) {
+ free(data);
+ return err;
+ }
+
+ mainloop_list[fd] = data;
+
+ return 0;
+}
+
+int mainloop_modify_fd(int fd, uint32_t events)
+{
+ struct mainloop_data *data;
+ struct epoll_event ev;
+ int err;
+
+ if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
+ return -EINVAL;
+
+ data = mainloop_list[fd];
+ if (!data)
+ return -ENXIO;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = events;
+ ev.data.ptr = data;
+
+ err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, data->fd, &ev);
+ if (err < 0)
+ return err;
+
+ data->events = events;
+
+ return 0;
+}
+
+int mainloop_remove_fd(int fd)
+{
+ struct mainloop_data *data;
+ int err;
+
+ if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
+ return -EINVAL;
+
+ data = mainloop_list[fd];
+ if (!data)
+ return -ENXIO;
+
+ mainloop_list[fd] = NULL;
+
+ err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+
+ free(data);
+
+ return err;
+}
+
+static void timeout_destroy(void *user_data)
+{
+ struct timeout_data *data = user_data;
+
+ close(data->fd);
+ data->fd = -1;
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+}
+
+static void timeout_callback(int fd, uint32_t events, void *user_data)
+{
+ struct timeout_data *data = user_data;
+ uint64_t expired;
+ ssize_t result;
+
+ if (events & (EPOLLERR | EPOLLHUP))
+ return;
+
+ result = read(data->fd, &expired, sizeof(expired));
+ if (result != sizeof(expired))
+ return;
+
+ if (data->callback)
+ data->callback(data->fd, data->user_data);
+}
+
+static inline int timeout_set(int fd, unsigned int msec)
+{
+ struct itimerspec itimer;
+ unsigned int sec = msec / 1000;
+
+ memset(&itimer, 0, sizeof(itimer));
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_nsec = 0;
+ itimer.it_value.tv_sec = sec;
+ itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000;
+
+ return timerfd_settime(fd, 0, &itimer, NULL);
+}
+
+int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback,
+ void *user_data, mainloop_destroy_func destroy)
+{
+ struct timeout_data *data;
+
+ if (!callback)
+ return -EINVAL;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
+ memset(data, 0, sizeof(*data));
+ data->callback = callback;
+ data->destroy = destroy;
+ data->user_data = user_data;
+
+ data->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+ if (data->fd < 0) {
+ free(data);
+ return -EIO;
+ }
+
+ if (msec > 0) {
+ if (timeout_set(data->fd, msec) < 0) {
+ close(data->fd);
+ free(data);
+ return -EIO;
+ }
+ }
+
+ if (mainloop_add_fd(data->fd, EPOLLIN | EPOLLONESHOT,
+ timeout_callback, data, timeout_destroy) < 0) {
+ close(data->fd);
+ free(data);
+ return -EIO;
+ }
+
+ return data->fd;
+}
+
+int mainloop_modify_timeout(int id, unsigned int msec)
+{
+ if (msec > 0) {
+ if (timeout_set(id, msec) < 0)
+ return -EIO;
+ }
+
+ if (mainloop_modify_fd(id, EPOLLIN | EPOLLONESHOT) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+int mainloop_remove_timeout(int id)
+{
+ return mainloop_remove_fd(id);
+}
+
+int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
+ void *user_data, mainloop_destroy_func destroy)
+{
+ struct signal_data *data;
+
+ if (!mask || !callback)
+ return -EINVAL;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
+ memset(data, 0, sizeof(*data));
+ data->callback = callback;
+ data->destroy = destroy;
+ data->user_data = user_data;
+
+ data->fd = -1;
+ memcpy(&data->mask, mask, sizeof(sigset_t));
+
+ free(signal_data);
+ signal_data = data;
+
+ return 0;
+}