/* * * 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 #include #include #include #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; }