blob: 23127931c90a50f614ca021b3ac59de6c08bbc52 [file] [log] [blame]
/*
*
* Embedded Linux library
*
* Copyright (C) 2011-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
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <unistd.h>
#include <sys/epoll.h>
#include "util.h"
#include "io.h"
#include "private.h"
/**
* SECTION:io
* @short_description: IO support
*
* IO support
*/
/**
* l_io:
*
* Opague object representing the IO.
*/
struct l_io {
int fd;
uint32_t events;
bool close_on_destroy;
l_io_read_cb_t read_handler;
l_io_destroy_cb_t read_destroy;
void *read_data;
l_io_write_cb_t write_handler;
l_io_destroy_cb_t write_destroy;
void *write_data;
l_io_disconnect_cb_t disconnect_handler;
l_io_destroy_cb_t disconnect_destroy;
void *disconnect_data;
l_io_debug_cb_t debug_handler;
l_io_destroy_cb_t debug_destroy;
void *debug_data;
};
static void io_cleanup(void *user_data)
{
struct l_io *io = user_data;
l_util_debug(io->debug_handler, io->debug_data, "cleanup <%p>", io);
if (io->write_destroy)
io->write_destroy(io->write_data);
if (io->read_destroy)
io->read_destroy(io->read_data);
if (io->disconnect_handler) {
io->disconnect_handler(io, io->disconnect_data);
io->disconnect_handler = NULL;
}
if (io->disconnect_destroy)
io->disconnect_destroy(io->disconnect_data);
if (io->debug_destroy)
io->debug_destroy(io->debug_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 l_io *io = user_data;
if (unlikely(events & (EPOLLERR | EPOLLHUP))) {
if (io->disconnect_handler) {
l_util_debug(io->debug_handler, io->debug_data,
"disconnect event <%p>", io);
io->disconnect_handler(io, io->disconnect_data);
io->disconnect_handler = NULL;
}
io->read_handler = NULL;
io->write_handler = NULL;
watch_remove(io->fd);
io->fd = -1;
return;
}
if ((events & EPOLLIN) && io->read_handler) {
l_util_debug(io->debug_handler, io->debug_data,
"read event <%p>", io);
if (!io->read_handler(io, io->read_data)) {
if (io->read_destroy)
io->read_destroy(io->read_data);
io->read_handler = NULL;
io->read_destroy = NULL;
io->read_data = NULL;
io->events &= ~EPOLLIN;
watch_modify(io->fd, io->events, false);
}
}
if ((events & EPOLLOUT) && io->write_handler) {
l_util_debug(io->debug_handler, io->debug_data,
"write event <%p>", io);
if (!io->write_handler(io, io->write_data)) {
if (io->write_destroy)
io->write_destroy(io->write_data);
io->write_handler = NULL;
io->write_destroy = NULL;
io->write_data = NULL;
io->events &= ~EPOLLOUT;
watch_modify(io->fd, io->events, false);
}
}
}
/**
* l_io_new:
* @fd: file descriptor
*
* Create new IO handling for a given file descriptor.
*
* Returns: a newly allocated #l_io object
**/
LIB_EXPORT struct l_io *l_io_new(int fd)
{
struct l_io *io;
int err;
if (unlikely(fd < 0))
return NULL;
io = l_new(struct l_io, 1);
io->fd = fd;
io->events = 0;
io->close_on_destroy = false;
err = watch_add(io->fd, io->events, io_callback, io, io_cleanup);
if (err) {
l_free(io);
return NULL;
}
return io;
}
/**
* l_io_destroy:
* @io: IO object
*
* Free IO object and close file descriptor (if enabled).
**/
LIB_EXPORT void l_io_destroy(struct l_io *io)
{
if (unlikely(!io))
return;
io->read_handler = NULL;
io->write_handler = NULL;
watch_remove(io->fd);
l_free(io);
}
/**
* l_io_get_fd:
* @io: IO object
*
* Returns: file descriptor associated with @io
**/
LIB_EXPORT int l_io_get_fd(struct l_io *io)
{
if (unlikely(!io))
return -1;
return io->fd;
}
/**
* l_io_set_close_on_destroy:
* @io: IO object
* @do_close: setting for destroy handling
*
* Set the automatic closing of the file descriptor when destroying @io.
*
* Returns: #true on success and #false on failure
**/
LIB_EXPORT bool l_io_set_close_on_destroy(struct l_io *io, bool do_close)
{
if (unlikely(!io))
return false;
io->close_on_destroy = do_close;
return true;
}
/**
* l_io_set_read_handler:
* @io: IO object
* @callback: read handler callback function
* @user_data: user data provided to read handler callback function
* @destroy: destroy function for user data
*
* Set read function.
*
* Returns: #true on success and #false on failure
**/
LIB_EXPORT bool l_io_set_read_handler(struct l_io *io, l_io_read_cb_t callback,
void *user_data, l_io_destroy_cb_t destroy)
{
uint32_t events;
int err;
if (unlikely(!io || io->fd < 0))
return false;
l_util_debug(io->debug_handler, io->debug_data,
"set read handler <%p>", io);
if (io->read_destroy)
io->read_destroy(io->read_data);
if (callback)
events = io->events | EPOLLIN;
else
events = io->events & ~EPOLLIN;
io->read_handler = callback;
io->read_destroy = destroy;
io->read_data = user_data;
if (events == io->events)
return true;
err = watch_modify(io->fd, events, false);
if (err)
return false;
io->events = events;
return true;
}
/**
* l_io_set_write_handler:
* @io: IO object
* @callback: write handler callback function
* @user_data: user data provided to write handler callback function
* @destroy: destroy function for user data
*
* Set write function.
*
* Returns: #true on success and #false on failure
**/
LIB_EXPORT bool l_io_set_write_handler(struct l_io *io, l_io_write_cb_t callback,
void *user_data, l_io_destroy_cb_t destroy)
{
uint32_t events;
int err;
if (unlikely(!io || io->fd < 0))
return false;
l_util_debug(io->debug_handler, io->debug_data,
"set write handler <%p>", io);
if (io->write_handler == callback && io->write_destroy == destroy &&
io->write_data == user_data)
return true;
if (io->write_destroy)
io->write_destroy(io->write_data);
if (callback)
events = io->events | EPOLLOUT;
else
events = io->events & ~EPOLLOUT;
io->write_handler = callback;
io->write_destroy = destroy;
io->write_data = user_data;
if (events == io->events)
return true;
err = watch_modify(io->fd, events, false);
if (err)
return false;
io->events = events;
return true;
}
/**
* l_io_set_disconnect_handler:
* @io: IO object
* @callback: disconnect handler callback function
* @user_data: user data provided to disconnect handler callback function
* @destroy: destroy function for user data
*
* Set disconnect function.
*
* Returns: #true on success and #false on failure
**/
LIB_EXPORT bool l_io_set_disconnect_handler(struct l_io *io,
l_io_disconnect_cb_t callback,
void *user_data, l_io_destroy_cb_t destroy)
{
if (unlikely(!io || io->fd < 0))
return false;
l_util_debug(io->debug_handler, io->debug_data,
"set disconnect handler <%p>", io);
if (io->disconnect_destroy)
io->disconnect_destroy(io->disconnect_data);
io->disconnect_handler = callback;
io->disconnect_destroy = destroy;
io->disconnect_data = user_data;
return true;
}
/**
* l_io_set_debug:
* @io: IO object
* @callback: debug callback function
* @user_data: user data provided to debug callback function
* @destroy: destroy function for user data
*
* Set debug function.
*
* Returns: #true on success and #false on failure
**/
LIB_EXPORT bool l_io_set_debug(struct l_io *io, l_io_debug_cb_t callback,
void *user_data, l_io_destroy_cb_t destroy)
{
if (unlikely(!io))
return false;
if (io->debug_destroy)
io->debug_destroy(io->debug_data);
io->debug_handler = callback;
io->debug_destroy = destroy;
io->debug_data = user_data;
return true;
}