/*
* Copyright(c) 2009 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Maintained at www.Open-FCoE.org
*/
#include "fcoemon_utils.h"
#include "net_types.h"
#include "fc_types.h"
#define NFC_NFDS 64
/*
* Static module state.
*/
static struct sa_sel_state {
fd_set ts_rx_fds;
fd_set ts_tx_fds;
fd_set ts_ex_fds;
int ts_max_fd;
int ts_sig;
struct sa_sel_fd {
void (*ts_rx_handler)(void *);
void (*ts_tx_handler)(void *);
void (*ts_ex_handler)(void *);
void *ts_handler_arg;
} ts_fd[NFC_NFDS];
void (*ts_callback)(void);
} sa_sel_state;
/**
* sa_select_loop() - listens to registered descriptors
*
* Return value:
* -1 on failure or interrupted signal number
*/
int sa_select_loop(void)
{
struct sa_sel_state *ss = &sa_sel_state;
struct sa_sel_fd *fp;
fd_set rx_fds;
fd_set tx_fds;
fd_set ex_fds;
struct timeval tval;
struct timeval *tvp;
int rv, i;
ss->ts_sig = 0;
while (ss->ts_sig == 0) {
sa_timer_check(&tval);
if (ss->ts_sig)
break;
if (tval.tv_sec == 0 && tval.tv_usec == 0)
tvp = NULL;
else
tvp = &tval;
rx_fds = ss->ts_rx_fds;
tx_fds = ss->ts_tx_fds;
ex_fds = ss->ts_ex_fds;
rv = select(ss->ts_max_fd + 1, &rx_fds, &tx_fds, &ex_fds, tvp);
if (rv == -1) {
if (errno == EINTR)
continue;
return rv;
}
fp = ss->ts_fd;
for (i = 0; rv > 0 && i <= sa_sel_state.ts_max_fd; i++, fp++) {
if (FD_ISSET(i, &rx_fds)) {
if (fp->ts_rx_handler != NULL)
(*fp->ts_rx_handler)
(fp->ts_handler_arg);
else {
ASSERT(!FD_ISSET(i, &ss->ts_rx_fds));
}
--rv;
}
if (FD_ISSET(i, &tx_fds)) {
if (fp->ts_tx_handler != NULL)
(*fp->ts_tx_handler)
(fp->ts_handler_arg);
else {
ASSERT(!FD_ISSET(i, &ss->ts_tx_fds));
}
--rv;
}
if (FD_ISSET(i, &ex_fds)) {
if (fp->ts_ex_handler != NULL)
(*fp->ts_ex_handler)
(fp->ts_handler_arg);
else {
ASSERT(!FD_ISSET(i, &ss->ts_ex_fds));
}
--rv;
}
}
if (ss->ts_callback != NULL)
(*ss->ts_callback)();
}
return ss->ts_sig;
}
void
sa_select_add_fd(int fd,
void (*rx_handler)(void *),
void (*tx_handler)(void *),
void (*ex_handler)(void *),
void *arg)
{
struct sa_sel_state *ss = &sa_sel_state;
struct sa_sel_fd *fp;
ASSERT_NOTIMPL(fd < NFC_NFDS);
ASSERT(rx_handler != NULL || tx_handler != NULL || ex_handler != NULL);
if (ss->ts_max_fd < fd)
ss->ts_max_fd = fd;
fp = &ss->ts_fd[fd];
fp->ts_handler_arg = arg;
if (rx_handler != NULL) {
fp->ts_rx_handler = rx_handler;
FD_SET(fd, &ss->ts_rx_fds);
}
if (tx_handler != NULL) {
fp->ts_tx_handler = tx_handler;
FD_SET(fd, &ss->ts_tx_fds);
}
if (ex_handler != NULL) {
fp->ts_ex_handler = ex_handler;
FD_SET(fd, &ss->ts_ex_fds);
}
}
void
sa_select_set_rx(int fd, void (*handler)(void *))
{
struct sa_sel_state *ss = &sa_sel_state;
ASSERT(fd <= ss->ts_max_fd);
ss->ts_fd[fd].ts_rx_handler = handler;
if (handler != NULL)
FD_SET(fd, &ss->ts_rx_fds);
else
FD_CLR(fd, &ss->ts_rx_fds);
}
void
sa_select_set_tx(int fd, void (*handler)(void *))
{
struct sa_sel_state *ss = &sa_sel_state;
ASSERT(fd <= ss->ts_max_fd);
ss->ts_fd[fd].ts_tx_handler = handler;
if (handler != NULL)
FD_SET(fd, &ss->ts_tx_fds);
else
FD_CLR(fd, &ss->ts_tx_fds);
}
void
sa_select_set_ex(int fd, void (*handler)(void *))
{
struct sa_sel_state *ss = &sa_sel_state;
ASSERT(fd <= ss->ts_max_fd);
ss->ts_fd[fd].ts_ex_handler = handler;
if (handler != NULL)
FD_SET(fd, &ss->ts_ex_fds);
else
FD_CLR(fd, &ss->ts_ex_fds);
}
void
sa_select_rem_fd(int fd)
{
struct sa_sel_state *ss = &sa_sel_state;
struct sa_sel_fd *fp;
ASSERT_NOTIMPL(fd < NFC_NFDS);
FD_CLR(fd, &ss->ts_rx_fds);
FD_CLR(fd, &ss->ts_tx_fds);
FD_CLR(fd, &ss->ts_ex_fds);
fp = &ss->ts_fd[fd];
fp->ts_rx_handler = NULL;
fp->ts_tx_handler = NULL;
fp->ts_ex_handler = NULL;
fp->ts_handler_arg = NULL;
}
/*
* Set callback for every time through the select loop.
*/
void
sa_select_set_callback(void (*cb)(void))
{
sa_sel_state.ts_callback = cb;
}
/*
* Cause select loop to exit.
* This is invoked from a handler which wants the select loop to return
* after the handler is finished. For example, during receipt of a network
* packet, the program may decide to clean up and exit, but in order to do
* this cleanly, all lower-level protocol handlers should return first.
*/
void
sa_select_exit(int sig)
{
sa_sel_state.ts_sig = sig;
}