aboutsummaryrefslogtreecommitdiffstats
path: root/bluez/io-mainloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'bluez/io-mainloop.c')
-rw-r--r--bluez/io-mainloop.c325
1 files changed, 325 insertions, 0 deletions
diff --git a/bluez/io-mainloop.c b/bluez/io-mainloop.c
new file mode 100644
index 0000000..752b05d
--- /dev/null
+++ b/bluez/io-mainloop.c
@@ -0,0 +1,325 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * 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 <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "mainloop.h"
+#include "util.h"
+#include "io.h"
+
+struct io {
+ int ref_count;
+ int fd;
+ uint32_t events;
+ bool close_on_destroy;
+ io_callback_func_t read_callback;
+ io_destroy_func_t read_destroy;
+ void *read_data;
+ io_callback_func_t write_callback;
+ io_destroy_func_t write_destroy;
+ void *write_data;
+ io_callback_func_t disconnect_callback;
+ io_destroy_func_t disconnect_destroy;
+ void *disconnect_data;
+};
+
+static struct io *io_ref(struct io *io)
+{
+ if (!io)
+ return NULL;
+
+ __sync_fetch_and_add(&io->ref_count, 1);
+
+ return io;
+}
+
+static void io_unref(struct io *io)
+{
+ if (!io)
+ return;
+
+ if (__sync_sub_and_fetch(&io->ref_count, 1))
+ return;
+
+ free(io);
+}
+
+static void io_cleanup(void *user_data)
+{
+ struct io *io = user_data;
+
+ if (io->write_destroy)
+ io->write_destroy(io->write_data);
+
+ if (io->read_destroy)
+ io->read_destroy(io->read_data);
+
+ if (io->disconnect_destroy)
+ io->disconnect_destroy(io->disconnect_data);
+
+ if (io->close_on_destroy)
+ close(io->fd);
+
+ io->fd = -1;
+}
+
+static void io_callback(int fd, uint32_t events, void *user_data)
+{
+ struct io *io = user_data;
+
+ io_ref(io);
+
+ if ((events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))) {
+ io->read_callback = NULL;
+ io->write_callback = NULL;
+
+ if (!io->disconnect_callback) {
+ mainloop_remove_fd(io->fd);
+ io_unref(io);
+ return;
+ }
+
+ if (!io->disconnect_callback(io, io->disconnect_data)) {
+ if (io->disconnect_destroy)
+ io->disconnect_destroy(io->disconnect_data);
+
+ io->disconnect_callback = NULL;
+ io->disconnect_destroy = NULL;
+ io->disconnect_data = NULL;
+
+ io->events &= ~EPOLLRDHUP;
+
+ mainloop_modify_fd(io->fd, io->events);
+ }
+ }
+
+ if ((events & EPOLLIN) && io->read_callback) {
+ if (!io->read_callback(io, io->read_data)) {
+ if (io->read_destroy)
+ io->read_destroy(io->read_data);
+
+ io->read_callback = NULL;
+ io->read_destroy = NULL;
+ io->read_data = NULL;
+
+ io->events &= ~EPOLLIN;
+
+ mainloop_modify_fd(io->fd, io->events);
+ }
+ }
+
+ if ((events & EPOLLOUT) && io->write_callback) {
+ if (!io->write_callback(io, io->write_data)) {
+ if (io->write_destroy)
+ io->write_destroy(io->write_data);
+
+ io->write_callback = NULL;
+ io->write_destroy = NULL;
+ io->write_data = NULL;
+
+ io->events &= ~EPOLLOUT;
+
+ mainloop_modify_fd(io->fd, io->events);
+ }
+ }
+
+ io_unref(io);
+}
+
+struct io *io_new(int fd)
+{
+ struct io *io;
+
+ if (fd < 0)
+ return NULL;
+
+ io = new0(struct io, 1);
+ if (!io)
+ return NULL;
+
+ io->fd = fd;
+ io->events = 0;
+ io->close_on_destroy = false;
+
+ if (mainloop_add_fd(io->fd, io->events, io_callback,
+ io, io_cleanup) < 0) {
+ free(io);
+ return NULL;
+ }
+
+ return io_ref(io);
+}
+
+void io_destroy(struct io *io)
+{
+ if (!io)
+ return;
+
+ io->read_callback = NULL;
+ io->write_callback = NULL;
+ io->disconnect_callback = NULL;
+
+ mainloop_remove_fd(io->fd);
+
+ io_unref(io);
+}
+
+int io_get_fd(struct io *io)
+{
+ if (!io)
+ return -ENOTCONN;
+
+ return io->fd;
+}
+
+bool io_set_close_on_destroy(struct io *io, bool do_close)
+{
+ if (!io)
+ return false;
+
+ io->close_on_destroy = do_close;
+
+ return true;
+}
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+ void *user_data, io_destroy_func_t destroy)
+{
+ uint32_t events;
+
+ if (!io || io->fd < 0)
+ return false;
+
+ if (io->read_destroy)
+ io->read_destroy(io->read_data);
+
+ if (callback)
+ events = io->events | EPOLLIN;
+ else
+ events = io->events & ~EPOLLIN;
+
+ io->read_callback = callback;
+ io->read_destroy = destroy;
+ io->read_data = user_data;
+
+ if (events == io->events)
+ return true;
+
+ if (mainloop_modify_fd(io->fd, events) < 0)
+ return false;
+
+ io->events = events;
+
+ return true;
+}
+
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+ void *user_data, io_destroy_func_t destroy)
+{
+ uint32_t events;
+
+ if (!io || io->fd < 0)
+ return false;
+
+ if (io->write_destroy)
+ io->write_destroy(io->write_data);
+
+ if (callback)
+ events = io->events | EPOLLOUT;
+ else
+ events = io->events & ~EPOLLOUT;
+
+ io->write_callback = callback;
+ io->write_destroy = destroy;
+ io->write_data = user_data;
+
+ if (events == io->events)
+ return true;
+
+ if (mainloop_modify_fd(io->fd, events) < 0)
+ return false;
+
+ io->events = events;
+
+ return true;
+}
+
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+ void *user_data, io_destroy_func_t destroy)
+{
+ uint32_t events;
+
+ if (!io || io->fd < 0)
+ return false;
+
+ if (io->disconnect_destroy)
+ io->disconnect_destroy(io->disconnect_data);
+
+ if (callback)
+ events = io->events | EPOLLRDHUP;
+ else
+ events = io->events & ~EPOLLRDHUP;
+
+ io->disconnect_callback = callback;
+ io->disconnect_destroy = destroy;
+ io->disconnect_data = user_data;
+
+ if (events == io->events)
+ return true;
+
+ if (mainloop_modify_fd(io->fd, events) < 0)
+ return false;
+
+ io->events = events;
+
+ return true;
+}
+
+ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
+{
+ ssize_t ret;
+
+ if (!io || io->fd < 0)
+ return -ENOTCONN;
+
+ do {
+ ret = writev(io->fd, iov, iovcnt);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+
+bool io_shutdown(struct io *io)
+{
+ if (!io || io->fd < 0)
+ return false;
+
+ return shutdown(io->fd, SHUT_RDWR) == 0;
+}