diff options
Diffstat (limited to 'bluez/io-mainloop.c')
-rw-r--r-- | bluez/io-mainloop.c | 325 |
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; +} |