/* $Id: mcl_lib.cpp,v 1.10 2003/10/31 14:58:44 chneuman Exp $ */ /* * Copyright (c) 1999-2003 INRIA - Universite Paris 6 - All rights reserved * (main author: Vincent Roca - vincent.roca@inrialpes.fr) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ /* * This file contains the entry functions of the MCL library. */ #include "mcl_includes.h" /****** external functions of the library *************************************/ /* * mcl_open * * params : "r", "w", "rw", or "wr" * * return : returns to appli a unique identifier */ int mcl_open (const char* mode) { mclcb_t *mclcb; mclcb = mcl_alloc_mclcb(); /* to do immediately before anything else */ TRACELVL(5, (mcl_stdout, "-> mcl_open: \"%s\"\n", mode))/*after alloc!*/ /* * trick to get a unique identifier in the file descriptor space */ if ((mclcb->id = dup(0)) < 0) { PRINT_ERR((mcl_stderr, "mcl_open: ERROR cannot create a new session identifier\n")) goto bad; } if (mclcb->id >= MCLCB_MAX_ID) { PRINT_ERR((mcl_stderr, "mcl_open: ERROR too many opens, mcl file descriptor space full\n")) goto bad; } mclcbs[mclcb->id] = mclcb; if (mcl_fsm_opened(mclcb, SENDER) || mcl_fsm_opened(mclcb, RECEIVER)) { PRINT_ERR((mcl_stderr, "mcl_open: ERROR already opened\n")) goto bad2; } if (!strcmp("w", mode)) { mclcb->sender = 1; mclcb->receiver = 0; mcl_fsm_update_state(mclcb, SENDER, TEVENT_OPEN_CALLED, REVENT_NIL); /* can't call InitSender() here because of param parsing */ } else if (!strcmp("r", mode)) { mclcb->sender = 0; mclcb->receiver = 1; mcl_fsm_update_state(mclcb, RECEIVER, TEVENT_NIL, REVENT_OPEN_CALLED); /* can't call InitReceiver() here because of param parsing */ } else if (!strcmp("wr", mode)) { /* this is above all a sender => init as sender first */ mclcb->sender = 1; mclcb->receiver = 2; mcl_fsm_update_state(mclcb, SENDER, TEVENT_OPEN_CALLED, REVENT_NIL); mcl_fsm_update_state(mclcb, RECEIVER, TEVENT_NIL, REVENT_OPEN_CALLED); } else if (!strcmp("rw", mode)) { /* this is above all a receiver => init as receiver first */ mclcb->receiver = 1; mclcb->sender = 2; mcl_fsm_update_state(mclcb, SENDER, TEVENT_OPEN_CALLED, REVENT_NIL); mcl_fsm_update_state(mclcb, RECEIVER, TEVENT_NIL, REVENT_OPEN_CALLED); } else { PRINT_ERR((mcl_stderr, "mcl_open: ERROR unknown mode %s\n", mode)) goto bad2; } /* * we can now init the congestion control protocol... * NB: moved from mcl_end_init_mclcb */ #ifdef RLC rlc_init_session(mclcb); #endif /* RLC */ #ifdef FLIDS FLIDs_InitSession(mclcb); #endif /* FLIDS */ TRACELVL(5, (mcl_stdout, "<- mcl_open: return %d\n", mclcb->id)) return mclcb->id; bad2: mclcbs[mclcb->id] = NULL; mcl_unlock(&mcl_mutex_lock); bad: PRINT_ERR((mcl_stderr, "mcl_open: ERROR\n")) free(mclcb); return -1; } /* * mcl_close * * params : * * return : */ int mcl_close (int id) { int max_adu; /* highest ADU seq number */ mclcb_t *mclcb; if (id >= MCLCB_MAX_ID || (mclcb = mclcbs[id]) == NULL) { TRACE((mcl_stdout, "<- mcl_close: not an MCL id, switch to standard close\n")) return (close(id)); } mcl_lock(&mcl_mutex_lock); TRACELVL(5, (mcl_stdout, "-> mcl_close: id=%d\n", id)) if (mclcb->sender && mcl_fsm_can_send_data(mclcb)) { /* data has already been submitted => send NO_NEW_ADU first */ mcl_fsm_update_state(mclcb, SENDER, TEVENT_CLOSE_CALLED, REVENT_NIL); max_adu = mclcb->lastADUseq; if(!mclcb->no_nonewadu) SendNONEWADU(mclcb, max_adu); /* wait till everything has been sent... */ while (!mcl_fsm_close_can_return(mclcb, SENDER)) { TRACELVL(5, (mcl_stdout, " mcl_close: (sender) wait a bit...\n")) mcl_unlock(&mcl_mutex_lock); mcl_usleep(DFLT_POLLING_PERIOD); mcl_lock(&mcl_mutex_lock); }; mcl_fsm_update_state(mclcb, SENDER, TEVENT_CLOSE_RETURN, REVENT_NIL); } else if (mclcb->sender) { /* data has never been submitted => send a CLOSE and return */ if (!mclcb->initialized) #ifdef STREAM mcl_end_init_mclcb(mclcb,1); /* yes, we really need it here!!! */ #else mcl_end_init_mclcb(mclcb); /* yes, we really need it here!!! */ #endif mcl_fsm_update_state(mclcb, SENDER, TEVENT_CLOSE_CALLED, REVENT_NIL); SendCLOSE(mclcb); mcl_fsm_update_state(mclcb, SENDER, TEVENT_CLOSE_SENT, REVENT_NIL); mcl_fsm_update_state(mclcb, SENDER, TEVENT_CLOSE_RETURN, REVENT_NIL); } else { mcl_fsm_update_state(mclcb, RECEIVER, TEVENT_NIL, REVENT_CLOSE_CALLED); while (!mcl_fsm_close_can_return(mclcb, RECEIVER)) { TRACELVL(5, (mcl_stdout, " mcl_close: (receiver) wait a bit...\n")) mcl_unlock(&mcl_mutex_lock); mcl_usleep(DFLT_POLLING_PERIOD); mcl_lock(&mcl_mutex_lock); }; mcl_fsm_update_state(mclcb, RECEIVER, TEVENT_NIL, REVENT_CLOSE_RETURN); } /* * destroy the rx thread now... */ #ifdef WIN32 if (mclcb->rx_thread) mclcb->test_cancel = TRUE; mcl_unlock(&mcl_mutex_lock); /* tx and rx threads might be blocked */ if (mclcb->rx_thread) { DWORD res = WaitForSingleObject((HANDLE)mclcb->rx_thread, 5000); if( res == WAIT_FAILED ) { if( GetLastError()==ERROR_INVALID_HANDLE ) { Sleep(100); } else { PRINT_ERR((mcl_stderr, "mcl_close: ERROR, WaitForSingleObject() failed (%d)\n", GetLastError())) mcl_exit(-1); } } } #else if (mclcb->rx_thread) pthread_cancel(mclcb->rx_thread); mcl_unlock(&mcl_mutex_lock); /* tx and rx threads might be blocked */ if (mclcb->rx_thread) pthread_join(mclcb->rx_thread,NULL); #endif mclcb->rx_thread = 0; #ifdef VIRTUAL_TX_MEM mcl_vtm_close (mclcb); #endif #ifdef VIRTUAL_RX_MEM mcl_vrm_close (mclcb); #endif TRACELVL(5, (mcl_stdout, "<- mcl_close:\n")) mcl_free_mclcb(mclcb); /* free everything */ return (close(id)); } /* * mcl_abort * close the session immediately and send CLOSE messages if we * are a source. * * params : * * return : */ int mcl_abort (int id) { mclcb_t *mclcb; int err; /* return code from close */ int trylock_err; /* return code from trylock: 0 if ok, or EBUSY*/ static int in_abort = 0;/* return if == 1 as someone else is in abort */ if (in_abort) { TRACE((mcl_stdout, "<- mcl_abort: someone else in abort\n")) return 0; /* don't try to call close in abort mode */ } in_abort = 1; /* take "lock" */ if (id >= MCLCB_MAX_ID || (mclcb = mclcbs[id]) == NULL) { TRACE((mcl_stdout, "<- mcl_abort: not an MCL id\n")) in_abort = 0; /* release "lock" */ return 0; /* don't try to call close in abort mode */ } /* * do not call lock in an async signal environment... * this is a problem as mcl_abort is usually called after a CTRL-C * so, use trylock instead and take lock only if available. */ trylock_err = mcl_trylock(&mcl_mutex_lock); TRACELVL(5, (mcl_stdout, "-> mcl_abort: id=%d\n", id)) if (mclcb->sender) { /* send a CLOSE and return, no matter what is current state */ if (!mclcb->initialized) #ifdef STREAM mcl_end_init_mclcb(mclcb,1); /* yes, we really need it! */ #else mcl_end_init_mclcb(mclcb); /* yes, we really need it! */ #endif mcl_fsm_update_state(mclcb, SENDER, TEVENT_ABORT, REVENT_NIL); SendCLOSE(mclcb); mcl_fsm_update_state(mclcb, SENDER, TEVENT_CLOSE_SENT, REVENT_NIL); mcl_fsm_update_state(mclcb, SENDER, TEVENT_CLOSE_RETURN, REVENT_NIL); } else { if (mcl_fsm_opened(mclcb, RECEIVER)) { mcl_fsm_update_state(mclcb, RECEIVER, TEVENT_NIL, REVENT_CLOSE_CALLED); mcl_fsm_update_state(mclcb, RECEIVER, TEVENT_NIL, REVENT_CLOSE_RETURN); } /* else already in RSTATE_NIL, do nothing */ } /* * destroy the rx thread now... */ #ifdef WIN32 if (mclcb->rx_thread) mclcb->test_cancel = TRUE; if (trylock_err == 0) mcl_unlock(&mcl_mutex_lock); if (mclcb->rx_thread) { DWORD res = WaitForSingleObject((HANDLE)mclcb->rx_thread, 5000); if (res == WAIT_FAILED) { if (GetLastError()==ERROR_INVALID_HANDLE) { Sleep(100); } else { PRINT_ERR((mcl_stderr, "mcl_abort ERROR: WaitForSingleObject() failed (%d)\n", GetLastError())) mcl_exit(-1); } } } #else if (mclcb->rx_thread) pthread_cancel(mclcb->rx_thread); if (trylock_err == 0) mcl_unlock(&mcl_mutex_lock); /* tx and rx threads might be blocked */ if (mclcb->rx_thread) pthread_join(mclcb->rx_thread,NULL); #endif mclcb->rx_thread = 0; #ifdef VIRTUAL_TX_MEM mcl_vtm_close (mclcb); #endif #ifdef VIRTUAL_RX_MEM mcl_vrm_close (mclcb); #endif TRACELVL(5, (mcl_stdout, "<- mcl_abort:\n")) mcl_free_mclcb(mclcb); /* free everything */ err = close(id); in_abort = 0; /* release "lock" */ return err; } /* * mcl_ctl * * params : * * return : 0 if ok, < 0 in case of error * * Warning: usually the tx and rx contexts are still not created! * This is only done during the first call to mcl_send or mcl_recv! */ int mcl_ctl (int id, int optname, void *optvalue, int optlen) { mclcb_t *mclcb; int err; if (id >= MCLCB_MAX_ID || (mclcb = mclcbs[id]) == NULL) { PRINT_ERR((mcl_stderr, "mcl_ctl: ERROR, bad id %d\n", id)) return -1; } mcl_lock(&mcl_mutex_lock); if (!mcl_fsm_opened(mclcb, SENDER) && !mcl_fsm_opened(mclcb, RECEIVER)) { /* not yet opened */ PRINT_ERR((mcl_stderr, "mcl_ctl: ERROR not opened\n")) mcl_unlock(&mcl_mutex_lock); return -1; } TRACELVL(5, (mcl_stdout, "-> mcl_ctl: id=%d\n", id)) err = mcl_ctl2(mclcb, optname, optvalue, optlen); TRACELVL(5, (mcl_stdout, "<- mcl_ctl: return %d\n", err)) mcl_unlock(&mcl_mutex_lock); return err; } /* * private version of the MCL control function */ int mcl_ctl2 (mclcb_t *mclcb, int optname, void *optvalue, int optlen) { char opt_found; int i; TRACELVL(5, (mcl_stdout, "-> mcl_ctl2: optname=%d, optvalue=x%x, optlen=%d\n", optname, (int)optvalue, optlen)) /* * called first (before mcl_open) to set the mcl options * or call the internal application */ switch (optname) { case MCL_OPT_LAYER_NB: /* set the number of layers */ if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_LAYER_NB ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->max_level = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_LAYER_NB (%d)\n", mclcb->max_level)) if (mclcb->max_level < 1 || mclcb->max_level > MAX_TX_LEVEL) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_LAYER_NB ERROR: layer %d out of range ([1; %d])\n", mclcb->max_level, MAX_TX_LEVEL)) mclcb->max_level = 1; goto error; } break; case MCL_OPT_SINGLE_LAYER: /* set/unset the single layer mode */ if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SINGLE_LAYER ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->single_layer_mode = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SINGLE_LAYER (%d)\n", mclcb->max_level)) /* * perform some sanity checks... */ if (mclcb->single_layer_mode != 0 && mclcb->single_layer_mode != 1) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SINGLE_LAYER ERROR: value %d invalid (0 or 1)\n", mclcb->single_layer_mode)) mclcb->single_layer_mode = 0; goto error; } /* * set the various parameters appropriately... */ if (mclcb->single_layer_mode) { mclcb->max_level = 1; /* 1 layer only! */ mclcb->scheduler = MCL_SCHED_LCT1; /* mix everything */ /* * Required for FLUTE interoperability tests, but a * limitation is that no loss statistics are possible */ mclcb->no_congestion_control = 1; /* no CC header */ } else { mclcb->no_congestion_control = 0; } break; case MCL_OPT_TX_PROFILE: /* select a pre-defined tx profile */ if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_TX_PROFILE ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_TX_PROFILE (%d)\n", *(int*)optvalue)) if (mcl_set_tx_profile(mclcb, *(int*)optvalue) < 0) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_TX_PROFILE: mcl_set_tx_profile() failed\n")) goto error; } break; case MCL_OPT_DATAGRAM_SIZE: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_DATAGRAM_SIZE ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->max_datagram_size = *(int*)optvalue; mclcb->payload_size = mclcb->max_datagram_size - MAX_ALC_HEADER_SIZE; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_DATAGRAM_SIZE (%d)\n", mclcb->max_datagram_size)) if (mclcb->max_datagram_size <= 0 || mclcb->max_datagram_size > MAX_DATAGRAM_SIZE) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_DATAGRAM_SIZE ERROR: size %d out of range ([1; %d])\n", mclcb->max_datagram_size, MAX_DATAGRAM_SIZE)) mclcb->payload_size = MAX_PAYLOAD_SIZE; goto error; } break; case MCL_OPT_TX_RATE: { int rate; if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_TX_RATE ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } rate = *(int*)optvalue; /* in packets/s on base layer */ TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_TX_RATE (%d)\n", rate)) #define MCL_MAX_TX_RATE 4000 if (rate <= 0 || rate > MCL_MAX_TX_RATE) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_TX_RATE ERROR: rate %d out of range ([1; %d] pkts/s)\n", rate, MCL_MAX_TX_RATE)) goto error; } mclcb->rate_l0 = rate; break; } case MCL_OPT_NETIF: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_NETIF ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->mcast_if.set_addr(*(int*)optvalue); TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_NETIF (%s)\n", mclcb->mcast_if.get_addr_string())) break; case MCL_OPT_PORT: { int port; if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_PORT ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } /* port in host byte order */ //mclcb->myport = *(int*)optvalue; port = *(int*)optvalue; mclcb->addr.set_port((u_short)port); TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_PORT (%d)\n", port)) break; } case MCL_OPT_ADDR: if (!optvalue || optlen != sizeof(u_int32_t)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_ADDR ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(u_int32_t))) goto error; } /* addr in host byte order */ #if 0 mclcb->addr = *(u_int32_t*)optvalue; /* IN_MULTICAST requires host order too */ if (IN_MULTICAST(mclcb->addr)) mclcb->is_multicast = 1; else mclcb->is_multicast = 0; #endif mclcb->addr.set_addr(*(u_int32_t*)optvalue); TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_ADDR (%s addr %s)\n", (mclcb->addr.is_multicast_addr() ? "multicast" : "unicast"), mclcb->addr.get_addr_string())) break; case MCL_OPT_SRC_ADDR: if (!optvalue || optlen != sizeof(u_int32_t)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SRC_ADDR ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(u_int32_t))) goto error; } /* addr in host byte order */ mclcb->src_addr.set_addr(*(u_int32_t*)optvalue); mclcb->check_src_addr = true; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SRC_ADDR\n")) break; case MCL_OPT_BIND: { struct sockaddr_in sin; /* specify sockaddr to use like a bind syscall */ if (!optvalue || optlen != sizeof(struct sockaddr_in)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_BIND ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(struct sockaddr_in))) goto error; } sin = *(struct sockaddr_in*)optvalue; /* assumes addr/port in network order */ mclcb->addr.set_addr_struct(&sin); #if 0 /* addr/port in network order => change to host order */ mclcb->addr = ntohl(sin.sin_addr.s_addr); mclcb->myport = ntohs(sin.sin_port); /* IN_MULTICAST requires host order too */ if (IN_MULTICAST(mclcb->addr)) mclcb->is_multicast = 1; else mclcb->is_multicast = 0; #endif TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_BIND (%s addr %s)\n", (mclcb->addr.is_multicast_addr() ? "multicast" : "unicast"), mclcb->addr.get_addr_string())) break; } case MCL_OPT_TTL: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_TTL ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->ttl = (ushort)(*(int*)optvalue); TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_TTL (%d)\n", mclcb->ttl)) if (mclcb->ttl > 127) { /* ttl 0 means do not leave host which is valid */ PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_TTL ERROR: TTL %d out of range ([0; %d])\nNB: TTL == 0 means do not leave host\n", mclcb->ttl, 127)) mclcb->ttl = 1; goto error; } TRACELVL(5, (mcl_stdout, " mcl_ctl: ttl=%d\n", mclcb->ttl)) break; case MCL_OPT_DEMUX_LABEL: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_DEMUX_LABEL ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->demux_label = (*(int*)optvalue); TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_DEMUX_LABEL (%d)\n", mclcb->demux_label)) break; case MCL_OPT_NO_NONEWADU: if (optvalue || optlen != 0) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_NO_NONEWADU ERROR: no argument needed)\n")) goto error; } mclcb->no_nonewadu = 1; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_NO_NONEWADU\n")) break; case MCL_OPT_STATS: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_STATS ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->statistics = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_STATS (%d)\n", mclcb->statistics)) if (mclcb->statistics < 0 || mclcb->statistics > 2) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_STATS ERROR: statistics arg %d out of range ([0; %d])\n", mclcb->statistics, 2)) mclcb->statistics = 0; goto error; } break; case MCL_OPT_VERBOSITY: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_VERBOSITY ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->verbose = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_VERBOSITY (%d)\n", mclcb->verbose)) if (mclcb->verbose < 0 || mclcb->verbose > 6) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_VERBOSITY ERROR: verbose arg %d out of range ([0; %d])\n", mclcb->verbose, 5)) mclcb->verbose = 0; goto error; } break; case MCL_OPT_DEBUG: /* debug mode : no tx or rx thread */ TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_DEBUG\n")) mclcb->debug = 1; break; case MCL_OPT_MOREABOUT: TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_MOREABOUT\n")) mcl_moreabout(); break; case MCL_OPT_TMP_DIR: /* name of directory to use for temp files */ if (!optvalue || optlen <= 0 || optlen >= MAX_FILE_NAME_LEN) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_TMP_DIR ERROR: null optvalue or bad optlen (got %d, expected in ]0; %d[)\n", optlen, MAX_FILE_NAME_LEN)) goto error; } strncpy(mcl_tmp_dir_name, (char*)optvalue, optlen); mcl_tmp_dir_name[MAX_FILE_NAME_LEN-1] = '\0'; /* security */ TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_TMP_DIR %s\n", mcl_tmp_dir_name)) break; case MCL_OPT_SET_NEXT_TOI: /* set the Transport Object Id (AKA ADU id)*/ /* to use for the next object submitted */ if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SET_NEXT_TOI ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } if ((*(int*)optvalue) < 0) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SET_NEXT_TOI ERROR: bad value (got %d, expected > 0)\n", (*(int*)optvalue))) goto error; } mclcb->next_adu_seq = (*(int*)optvalue); TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SET_NEXT_TOI (%d)\n", mclcb->next_adu_seq)) break; case MCL_OPT_KEEP_DATA: /* step1: wait before doing ADU scheduling */ TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_KEEP_DATA\n")) if (mclcb->keep_data != 0) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_TMP_DIR ERROR: KEEP_DATA mode already set\n")) goto error; } ASSERT(mclcb->adu_start == NULL && mclcb->adu_end == NULL); mclcb->keep_data = 1; break; case MCL_OPT_PUSH_DATA: /* step2: now perform ADU scheduling and send */ TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_PUSH_DATA\n")) if (mclcb->keep_data != 1) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_PUSH_DATA ERROR: KEEP_DATA mode not set\n")) goto error; } if (mclcb->adu_start == NULL || mclcb->adu_end == NULL) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_PUSH_DATA ERROR: no data submitted\n")) goto error; } /* and now do the tx plannification... */ UpdateTxPlanning(mclcb, mclcb->adu_start, mclcb->adu_end); mclcb->keep_data = 0; mclcb->adu_start = mclcb->adu_end = NULL; mclcb->lastADUseq = LastADUSeq(mclcb, mclcb->txlvl_tab[0].adu_head); for(i=0;itxlvl_tab[i].adu_head=NULL; } break; case MCL_OPT_RESET_TRANSMISSIONS: /* Delete and free all transmission tables and their ADUs */ TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_RESET_TRANSMISSIONS\n")) if (!mclcb->sender) PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_RESET_TRANSMISSIONS : Reset tx only possible in SENDER mode\n")) //mcl_free_all_txtab(mclcb); mcl_free_all_adu_from_txtab (mclcb); mcl_fsm_update_state(mclcb, SENDER, TEVENT_RESET, REVENT_NIL); break; #ifdef STREAM case MCL_OPT_GET_RX_LEVEL: TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_GET_RX_LEVEL\n")) if (mclcb->sender) PRINT_ERR((mcl_stderr,"mcl_ctl: MCL_OPT_GET_RX_LEVEL : Only possible in RECEIVER mode\n")) TRACELVL(5, (mcl_stdout, "<- mcl_ctl2: ok\n")) return (mclcb->nb_level); case MCL_BASE_VIDEO_LAYER: TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_BASE_VIDEO_LAYER\n")) #ifdef ANTICIPATED_TX_FOR_PUSH mclcb->anticipated_tx_for_push = 1; #endif break; #endif case MCL_OPT_REUSE_APPLI_TX_BUFFER: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_REUSE_APPLI_TX_BUFFER ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->reuse_appli_tx_buffer = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_REUSE_APPLI_TX_BUFFER (%d)\n", mclcb->reuse_appli_tx_buffer)) if (mclcb->reuse_appli_tx_buffer < 0 || mclcb->reuse_appli_tx_buffer > 1) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_REUSE_APPLI_TX_BUFFER ERROR: arg %d out of range (0/1)\n", mclcb->reuse_appli_tx_buffer)) mclcb->reuse_appli_tx_buffer = 0; goto error; } break; #ifdef VIRTUAL_TX_MEM case MCL_OPT_VIRTUAL_TX_MEMORY: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_VIRTUAL_TX_MEMORY ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->vtm_used = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_VIRTUAL_TX_MEMORY (%d)\n", mclcb->vtm_used)) if (mclcb->vtm_used != 0 && mclcb->vtm_used != 1) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_VIRTUAL_TX_MEMORY ERROR: %d invalid (expected 0 or 1)\n", mclcb->vtm_used)) mclcb->vtm_used = 0; goto error; } break; #endif /* VIRTUAL_TX_MEM */ #ifdef VIRTUAL_RX_MEM case MCL_OPT_VIRTUAL_RX_MEMORY: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_VIRTUAL_RX_MEMORY ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->vrm_used = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_VIRTUAL_RX_MEMORY (%d)\n", mclcb->vrm_used)) if (mclcb->vrm_used != 0 && mclcb->vrm_used != 1) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_VIRTUAL_RX_MEMORY ERROR: %d invalid (expected 0 or 1)\n", mclcb->vrm_used)) mclcb->vrm_used = 0; goto error; } break; #endif /* VIRTUAL_RX_MEM */ case MCL_OPT_DELIVERY_MODE: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_DELIVERY_MODE ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->delivery_mode = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_DELIVERY_MODE (%d)\n", mclcb->delivery_mode)) if (mclcb->delivery_mode < 0 || mclcb->delivery_mode > 2) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_DELIVERY_MODE ERROR: delivery_mode arg %d out of range ([0; %d])\n", mclcb->delivery_mode, 2)) mclcb->delivery_mode = 0; goto error; } #ifdef ANTICIPATED_TX_FOR_PUSH if (mclcb->delivery_mode == DEL_MODE_PUSH) { mclcb->anticipated_tx_for_push = 1; /* usefull here */ } else { mclcb->anticipated_tx_for_push = 0; /* useless otherw */ } #endif /* ANTICIPATED_TX_FOR_PUSH */ break; case MCL_OPT_IMMEDIATE_DELIVERY: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_IMMEDIATE_DELIVERY ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->immediate_delivery = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_IMMEDIATE_DELIVERY (%d)\n", mclcb->immediate_delivery)) if (mclcb->immediate_delivery < 0 || mclcb->immediate_delivery > 1) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_IMMEDIATE_DELIVERY ERROR: arg %d out of range (0/1)\n", mclcb->immediate_delivery)) mclcb->immediate_delivery = 0; goto error; } break; case MCL_OPT_FLUTE_DELIVERY: if (optvalue || optlen != 0) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_FLUTE_DELIVERY ERROR: no argument needed)\n")) goto error; } mclcb->immediate_delivery = (char) 3; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_FLUTE_DELIVERY set\n")) break; case MCL_OPT_FLUTE_DELIVER_ALL_ADU: if (optvalue || optlen != 0) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_DELIVER_ALL_ADU ERROR: no argument needed)\n")) goto error; } mclcb->deliverallADU = (char) 1; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_DELIVER_ALL_ADU\n")) break; case MCL_OPT_FLUTE_DELIVER_THIS_ADU: if (!optvalue || optlen != sizeof(u_int32_t)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_DELIVER_THIS_ADU ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(u_int32_t))) goto error; } addrequestedtoi_flute(mclcb,*(u_int32_t*)optvalue); TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_DELIVER_THIS_ADU (%d)\n",*(u_int32_t*)optvalue)) break; case MCL_OPT_POSTPONE_FEC_DECODING: #ifdef RSE_FEC if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_POSTPONE_FEC_DECODING ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->postpone_fec_decoding = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_POSTPONE_FEC_DECODING (%d)\n", mclcb->immediate_delivery)) if (mclcb->postpone_fec_decoding < 0 || mclcb->postpone_fec_decoding > 1) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_POSTPONE_FEC_DECODING ERROR: arg %d out of range (0/1)\n", mclcb->postpone_fec_decoding)) mclcb->postpone_fec_decoding = 0; goto error; } #else /* LDPC FEC */ /* not critical, avoid a warning if DEBUG not set */ TRACELVL(1, (mcl_stderr, "mcl_ctl: MCL_OPT_POSTPONE_FEC_DECODING ERROR: only valid with RSE, doesn't apply to LDPC\n")) goto error; #endif /* FEC flavor */ break; case MCL_OPT_NEVER_LEAVE_BASE_LAYER: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_NEVER_LEAVE_BASE_LAYER ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->never_leave_base_layer = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_NEVER_LEAVE_BASE_LAYER (%d)\n", mclcb->never_leave_base_layer)) if (mclcb->never_leave_base_layer < 0 || mclcb->never_leave_base_layer > 1) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_NEVER_LEAVE_BASE_LAYER ERROR: arg %d out of range (0/1)\n", mclcb->never_leave_base_layer)) mclcb->never_leave_base_layer = 0; goto error; } break; case MCL_OPT_FEC_RATIO: if (!optvalue || optlen != sizeof(float)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_FEC_RATIO ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(float))) goto error; } #if 0 mclcb->max_fec_ratio = *(float*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_FEC_RATIO (%f)\n", mclcb->max_fec_ratio)) if (mclcb->max_fec_ratio < 1.0) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_FEC_RATIO ERROR: %f invalid (expected >= 1.0)\n", mclcb->max_fec_ratio)) mclcb->max_fec_ratio = 2.0; goto error; } #endif if (mclcb->fec.set_max_fec_ratio(mclcb, *(float*)optvalue) == MCL_ERROR) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_FEC_RATIO failed for fec_ratio %f\n", *(float*)optvalue)) goto error; } TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_FEC_RATIO (%f)\n", mclcb->fec.get_max_fec_ratio())) break; case MCL_OPT_NB_OF_TX: /* send DUs that number of times on each layer */ if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_NB_OF_TX ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->nb_tx = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_NB_OF_TX (%d)\n", mclcb->nb_tx)) if (mclcb->nb_tx <= 0) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_NB_OF_TX ERROR: %d invalid (expected > 0)\n", mclcb->nb_tx)) mclcb->nb_tx = 1; goto error; } break; case MCL_OPT_SCHED: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SCHED ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } #ifdef LCT_SCHED1 if (*(int*)optvalue == MCL_SCHED_LCT1) { mclcb->scheduler = MCL_SCHED_LCT1; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SCHED (sched1)\n")) break; } #endif #ifdef LCT_SCHED2 if (*(int*)optvalue == MCL_SCHED_LCT2) { mclcb->scheduler = MCL_SCHED_LCT2; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SCHED (sched2)\n")) break; } #endif #ifdef LCT_SCHED3 if (*(int*)optvalue == MCL_SCHED_LCT3) { mclcb->scheduler = MCL_SCHED_LCT3; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SCHED (sched3, or SDA)\n")) break; } #endif #ifdef LCT_SCHEDSTREAM if (*(int*)optvalue == MCL_SCHED_LCTSTREAM) { mclcb->scheduler = MCL_SCHED_LCTSTREAM; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SCHED (sched_stream)\n")) break; } #endif PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SCHED ERROR: arg %d unsupported\n", *(int*)optvalue)) goto error; case MCL_OPT_OBJ_SCHED: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_OBJ_SCHED ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } mclcb->adu_scheduling = *(int*)optvalue; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_OBJ_SCHED: %d\n", mclcb->adu_scheduling)) if (mclcb->adu_scheduling < 0 || mclcb->adu_scheduling > MCL_SCHED_MIXED_ORDER) { TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SCHED ERROR: bad value, must be in range {0..%d}\n", MCL_SCHED_MIXED_ORDER)) mclcb->adu_scheduling = 0; goto error; } break; case MCL_OPT_SET_FEC_CODE: /* set the FEC codec to be used */ if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SET_FEC_CODE ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(int))) goto error; } if ((*(int*)optvalue) < MCL_FEC_CODE_NULL || (*(int*)optvalue) > MCL_FEC_CODE_LDPC) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SET_FEC_CODE ERROR: value out or range (got %d, expected [%d; %d])\n", (*(int*)optvalue), MCL_FEC_CODE_NULL, MCL_FEC_CODE_LDPC)) goto error; } if (mclcb->fec.set_fec_code(mclcb, (*(int*)optvalue)) == MCL_ERROR) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_SET_FEC_CODE WARNING: fec.set_fec_code(%d) failed\n", (*(int*)optvalue))) goto error; } TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_SET_FEC_CODE (%d)\n", (*(int*)optvalue))) break; case MCL_OPT_GET_MAX_BLOCK_SIZE_FOR_CURRENT_FEC: if (!optvalue || optlen != sizeof(int)) { PRINT_ERR((mcl_stderr, "mcl_ctl: MCL_OPT_GET_MAX_BLOCK_SIZE_FOR_CURRENT_FEC ERROR: null optvalue or bad optlen (got %d, expected %d)\n", optlen, sizeof(UINT32))) goto error; } *(int*)optvalue = mclcb->fec.get_k() * mclcb->payload_size; TRACELVL(5, (mcl_stdout, " mcl_ctl: MCL_OPT_GET_MAX_BLOCK_SIZE_FOR_CURRENT_FEC (%d)\n", (*(int*)optvalue))) break; default: opt_found = 0; #ifdef RLC if (rlc_ctl( mclcb, optname, optvalue, optlen) == MCL_OK) { opt_found = 1; } #endif /* RLC */ #ifdef FLIDS if (FLIDs_ctl( mclcb, optname, optvalue, optlen) == MCL_OK) { opt_found = 1; } #endif /* FLIDS */ if (!opt_found) { PRINT_ERR((mcl_stderr, "mcl_ctl: ERROR: unknown optname %d\n", optname)) PRINT_ERR((mcl_stderr, "Check that the MCL lib has been compiled with the appropriate flags...\n")) goto error; } break; } TRACELVL(5, (mcl_stdout, "<- mcl_ctl2: ok\n")) return 0; error: TRACELVL(5, (mcl_stdout, "<- mcl_ctl2: error\n")) return -1; } /* * mcl_send * * params : in MCL_OPT_REUSE_APPLU_TX_BUFFER mode, the data pointer must be * the result of a standard malloc/calloc/realloc function call. * * return : number of bytes sent or -1 if error */ int mcl_send (int id, const void *data, int len) { if (id >= MCLCB_MAX_ID || mclcbs[id] == NULL) { TRACE((mcl_stdout, " mcl_send: not an MCL id, switch to standard send\n")) return (send(id,(const char*)data,len,0)); } return mcl_sendto(id, data, len, NULL, 0); } /* * mcl_sendto * * params : destination address * in MCL_OPT_REUSE_APPLU_TX_BUFFER mode, the data pointer must be * the result of a standard malloc/calloc/realloc function call. * * return : number of bytes sent or -1 if error */ int mcl_sendto (int id, const void *data, int len, const struct sockaddr *saddr, int saddr_len) { adu_t *adu; /* DU descriptor of the data buffer */ #ifdef FEC block_t *blk; int i; #endif /* FEC */ int padded_len; /* size with padding to 0 for FEC */ mclcb_t *mclcb; int tot_fec_nb = 0; /* fec DUs created by mcl_fec_encode */ #ifdef ANTICIPATED_TX_FOR_PUSH int try_2_send_count = 4; /* period between 2 attempts */ #endif if (id >= MCLCB_MAX_ID || (mclcb = mclcbs[id]) == NULL) { TRACE((mcl_stdout, " mcl_sendto: not an MCL id, switch to standard sendto\n")) return (sendto(id, (const char*)data, len, 0, saddr, saddr_len)); } mcl_lock(&mcl_mutex_lock); TRACELVL(5, (mcl_stdout, "-> mcl_sendto: len=%d, saddr_len=%d\n", len, saddr_len)) if (!mclcb->initialized) #ifdef STREAM mcl_end_init_mclcb(mclcb,1); #else mcl_end_init_mclcb(mclcb); #endif if (data == NULL && len == 0) { /* * the appli issued this call just to finish the init... * nothing else to do, return. */ TRACELVL(5, (mcl_stdout, "<- mcl_sendto: no data, return\n")) goto end; } mcl_fsm_update_state(mclcb, SENDER, TEVENT_NEW_ADU, REVENT_NIL); if (!mcl_fsm_can_send_data(mclcb)) { TRACELVL(5, (mcl_stdout, "<- mcl_sendto: ERROR, cannot send\n")) goto error; } if (!data || len <= 0) { PRINT_ERR((mcl_stderr, "mcl_sendto: ERROR: bad arg data|len\n")) goto error; } if (saddr && saddr_len != sizeof(struct sockaddr_in)) { PRINT_ERR((mcl_stderr, "mcl_sendto: ERROR: bad addr/addr_len arg\n")) goto error; } /* * Create the ADU descriptor... */ adu = CreateADU(mclcb); switch (mclcb->fec.get_fec_code()) { // current FEC codec case MCL_FEC_CODE_NULL: adu->fec_encoding_id = FEC_ENCODING_ID_NO_FEC; adu->fec_instance_id = FEC_INSTANCE_ID_NULL; break; case MCL_FEC_CODE_RSE: adu->fec_encoding_id = FEC_ENCODING_ID_SMALL_LARGE_EXP_FEC; adu->fec_instance_id = FEC_INSTANCE_ID_RSE; break; case MCL_FEC_CODE_LDGM: adu->fec_encoding_id = FEC_ENCODING_ID_LDPC_FEC; adu->fec_instance_id = FEC_INSTANCE_ID_LDGM; break; case MCL_FEC_CODE_LDPC: adu->fec_encoding_id = FEC_ENCODING_ID_LDPC_FEC; adu->fec_instance_id = FEC_INSTANCE_ID_LDPC; break; default: exit(-1); } adu->len = len; if (saddr_len > 0) { /* remember the dest addr to use (rather than default dest) */ #if 0 memcpy(&(adu->saddr), saddr, saddr_len); adu->saddr_len = saddr_len; #endif adu->addr.set_addr_struct((struct sockaddr_in*)saddr); adu->addr_valid = true; } adu->seq = mclcb->next_adu_seq; /* all DUs for this ADU have this size */ adu->symbol_len = mclcb->payload_size; mclcb->next_adu_seq++; if (adu->seq == 0) { /* FDT */ adu->FDTinstanceID = mclcb->nextFDTinstanceID; mclcb->nextFDTinstanceID = (mclcb->nextFDTinstanceID+1) % (2^24 - 1); } else { adu->FDTinstanceID = 0; } /* * insert it in the tl_0 ADU list... */ InsertADU(mclcb, adu, &(mclcb->txlvl_tab[0].adu_head)); //update lastADUseq mclcb->lastADUseq = LastADUSeq(mclcb, mclcb->txlvl_tab[0].adu_head); /* * store the application data... */ padded_len = (int)(ceil((double)len / (double)adu->symbol_len) * (double)adu->symbol_len); adu->padded_len = padded_len; #ifdef VIRTUAL_TX_MEM if (mcl_vtm_can_store_in_vtm(mclcb, padded_len)) { /* * use the VTMEM service to register data */ if (mcl_vtm_store_data(mclcb, adu, NULL, (char*)data, len, padded_len) ) { PRINT_ERR((mcl_stderr, "mcl_sendto: ERROR: Virtual Tx Memory service failed\n")) goto error; } if (mclcb->reuse_appli_tx_buffer) { /* * free the data buffer. * In fact using reuse_appli is here useless! */ free((void*)data); PRINT_ERR((mcl_stderr, "mcl_sendto: WARNING: using reuse_appli_tx_buffer is useless in Virtual Tx Memory mode\n")) } } else { /* * store in physical tx memory (ptm) */ #endif /* VIRTUAL_TX_MEM */ if (mclcb->reuse_appli_tx_buffer) { /* take control of buf and avoid an extra data copy */ if (padded_len != len) { /* add a null padding to end of block */ if (!(adu->data = (char*)realloc((void*)data, padded_len))) { PRINT_ERR((mcl_stderr, "mcl_sendto: ERROR: out of memory\n")) goto error; } memset(adu->data + len, 0, padded_len - len); } } else { if (!(adu->data = (char*)malloc(padded_len))) { PRINT_ERR((mcl_stderr, "mcl_sendto: ERROR: out of memory\n")) goto error; } memcpy(adu->data, data, len); memset(adu->data + len, 0, padded_len - len);/*padding*/ /*PRINT_OUT((mcl_stdout, "copied data to x%x, len=%d, padded_len=%d\n", (int)adu->data, len, padded_len))*/ } #ifdef VIRTUAL_TX_MEM if (mclcb->vtm_used) { /* remember it kept in physical memory */ mcl_vtm_register_in_ptm(mclcb, adu, NULL, padded_len); } } #endif /* VIRTUAL_TX_MEM */ /* * segment it... */ mcl_tx_segment_adu(mclcb, adu); #ifdef FEC /* * create additional FEC DUs for each block... */ for (i = adu->block_nb, blk = adu->block_head; i > 0; i--, blk++) { tot_fec_nb += mclcb->fec.encode(mclcb, blk); /*PRINT_OUT((mcl_stdout, "tot_fec_nb=%d, blk->k=%d, blk->fec_nb=%d \n", tot_fec_nb, blk->k, blk->fec_du_nb_in_list))*/ #ifdef ANTICIPATED_TX_FOR_PUSH if (mclcb->anticipated_tx_for_push) { if (--try_2_send_count <= 0) { try_2_send_count = 4; /* now try to send */ mcl_do_periodic_proc(mclcb); } } #endif } #endif /* FEC */ /* * planify transmissions... */ if (mclcb->keep_data) { /* * It we are in KEEP_DATA mode (in that case scheduling * will be done later when the application issues a PUSH), * then remember it for a future PUSH... */ if (mclcb->adu_start == NULL) mclcb->adu_start = adu; if (mclcb->adu_end==NULL || adu->seq>mclcb->adu_end->seq) mclcb->adu_end = adu; if (adu->seqadu_start->seq) mclcb->adu_start = adu; #ifdef ANTICIPATED_TX_FOR_PUSH if (mclcb->anticipated_tx_for_push) { /* update tx planning for anticipated tx in push mode */ AnticipTx_UpdateTxPlanning(mclcb, adu); /* and now try to send */ mcl_do_periodic_proc(mclcb); } #endif /* ANTICIPATED_TX_FOR_PUSH */ } else { UpdateTxPlanning(mclcb, adu, adu); } mclcb->more_to_tx = 1; /* * update stats */ mclcb->stats.adus_announced++; mclcb->stats.buf_space += padded_len + tot_fec_nb * adu->symbol_len; if (mclcb->stats.buf_space > mclcb->stats.max_buf_space) mclcb->stats.max_buf_space = mclcb->stats.buf_space; if (mclcb->statistics) mcl_print_tx_stats(mclcb); TRACELVL(5, (mcl_stdout, "<- mcl_sendto: return %d\n", len)) end: mcl_unlock(&mcl_mutex_lock); return len; error: mcl_unlock(&mcl_mutex_lock); return -1; } #ifndef STREAM //flute will never be used with stream /* * mcl_recv_flute * * params : * * return : number of bytes read or -1 if error */ int mcl_recv_flute (int id, void *buf, int len, unsigned int *toi) { int saddr_len = 0; if (id >= MCLCB_MAX_ID || mclcbs[id] == NULL) { TRACE((mcl_stdout, " mcl_recv: not an MCL id, switch to standard recv\n")) return (recv(id, (char*)buf, len, 0)); } return mcl_recvfrom_flute(id, buf, len, NULL, &saddr_len,toi); } /* * mcl_recvfrom_flute * * params : * * return : number of bytes read or -1 if error */ int mcl_recvfrom_flute (int id, void *buf, int len, struct sockaddr *saddr, int *saddr_len, unsigned int *toi) { int rlen; struct sockaddr tmp_saddr; int tmp_saddr_len = sizeof(tmp_saddr); mclcb_t *mclcb; if (id >= MCLCB_MAX_ID || (mclcb = mclcbs[id]) == NULL) { TRACE((mcl_stdout, " mcl_recvfrom: not an MCL id, switch to standard recvfrom\n")) return (recvfrom(id, (char*)buf, len, 0, saddr, #if defined(LINUX) (size_t*) /* linux => uint, solaris => int */ #elif defined(FREEBSD) (socklen_t*) #endif saddr_len)); } mcl_lock(&mcl_mutex_lock); TRACELVL(5, (mcl_stdout, "-> mcl_recvfrom: len=%d, saddr_len ptr=x%x\n", len, (int)saddr_len)) if (!mclcb->initialized) mcl_end_init_mclcb(mclcb); if (buf == NULL || len == 0) { /* * the appli issued this call just to finish the init... * nothing else to do, return. */ TRACELVL(5, (mcl_stdout, "<- mcl_recvfrom: NULL reception buffer, return\n")) rlen = 0; goto end; } #ifdef NEVERDEF /* can't check because the tx may be finished and the appli */ /* only retrieves data available... */ if (!mcl_fsm_can_recv_data(mclcb)) { PRINT_ERR((mcl_stderr, "mcl_recvfrom: ERROR: cannot recv\n")) goto error; } #endif /* * check if an adu is available and copy it to * the application buffer * XXX : use polling for the present... change it! */ while ((rlen = mcl_return_adu_to_appli_flute(mclcb, (char*)buf, (u_int)len, &tmp_saddr, &tmp_saddr_len, toi)) < 0) { if (mcl_fsm_no_new_undup_du(mclcb)) { TRACELVL(5, (mcl_stdout, "<- mcl_recvfrom: closed, return -1\n")) goto error; } /* * nothing received yet so wait a little bit (polling). * the waiting time is a compromize... what should we use? */ mcl_unlock(&mcl_mutex_lock); mcl_usleep(DFLT_POLLING_PERIOD); mcl_lock(&mcl_mutex_lock); } /* * copy back remote addr if appli is interested */ if (saddr) { if (saddr_len == NULL) { PRINT_ERR((mcl_stderr, "mcl_recvfrom: ERROR: bad saddr_len arg (NULL pointer)\n")) goto error; } *saddr = tmp_saddr; *saddr_len = tmp_saddr_len; } TRACELVL(5, (mcl_stdout, "<- mcl_recvfrom: return %d from %s\n", rlen, inet_ntoa(((struct sockaddr_in*)&tmp_saddr)->sin_addr))) end: mcl_unlock(&mcl_mutex_lock); return rlen; error: mcl_unlock(&mcl_mutex_lock); return -1; } #endif /* * mcl_recv * * params : * * return : number of bytes read or -1 if error */ #ifdef STREAM int mcl_recv (int id, void *buf, int len, int nb_level) #else int mcl_recv (int id, void *buf, int len) #endif { int saddr_len = 0; if (id >= MCLCB_MAX_ID || mclcbs[id] == NULL) { TRACE((mcl_stdout, " mcl_recv: not an MCL id, switch to standard recv\n")) return (recv(id, (char*)buf, len, 0)); } #ifdef STREAM return mcl_recvfrom(id, buf, len, NULL, &saddr_len, nb_level); #else return mcl_recvfrom(id, buf, len, NULL, &saddr_len); #endif } /* * mcl_recvfrom * * params : * * return : number of bytes read or -1 if error */ #ifdef STREAM int mcl_recvfrom (int id, void *buf, int len, struct sockaddr *saddr, int *saddr_len, int nb_level) #else int mcl_recvfrom (int id, void *buf, int len, struct sockaddr *saddr, int *saddr_len) #endif { int rlen; struct sockaddr tmp_saddr; int tmp_saddr_len = sizeof(tmp_saddr); mclcb_t *mclcb; if (id >= MCLCB_MAX_ID || (mclcb = mclcbs[id]) == NULL) { TRACE((mcl_stdout, " mcl_recvfrom: not an MCL id, switch to standard recvfrom\n")) return (recvfrom(id, (char*)buf, len, 0, saddr, #if defined(LINUX) (size_t*) /* linux => uint, solaris => int */ #elif defined(FREEBSD) (socklen_t*) #endif saddr_len)); } mcl_lock(&mcl_mutex_lock); TRACELVL(5, (mcl_stdout, "-> mcl_recvfrom: len=%d, saddr_len ptr=x%x\n", len, (int)saddr_len)) if (!mclcb->initialized) #ifdef STREAM mcl_end_init_mclcb(mclcb, nb_level); #else mcl_end_init_mclcb(mclcb); #endif if (buf == NULL || len == 0) { /* * the appli issued this call just to finish the init... * nothing else to do, return. */ TRACELVL(5, (mcl_stdout, "<- mcl_recvfrom: NULL reception buffer, return\n")) rlen = 0; goto end; } #ifdef NEVERDEF /* can't check because the tx may be finished and the appli */ /* only retrieves data available... */ if (!mcl_fsm_can_recv_data(mclcb)) { PRINT_ERR((mcl_stderr, "mcl_recvfrom: ERROR: cannot recv\n")) goto error; } #endif /* * check if an adu is available and copy it to * the application buffer * XXX : use polling for the present... change it! */ while ((rlen = mcl_return_adu_to_appli(mclcb, (char*)buf, (u_int)len, &tmp_saddr, &tmp_saddr_len)) < 0) { if (mcl_fsm_no_new_undup_du(mclcb)) { TRACELVL(5, (mcl_stdout, "<- mcl_recvfrom: closed, return -1\n")) goto error; } /* * nothing received yet so wait a little bit (polling). * the waiting time is a compromize... what should we use? */ mcl_unlock(&mcl_mutex_lock); mcl_usleep(DFLT_POLLING_PERIOD); mcl_lock(&mcl_mutex_lock); } /* * copy back remote addr if appli is interested */ if (saddr) { if (saddr_len == NULL) { PRINT_ERR((mcl_stderr, "mcl_recvfrom: ERROR: bad saddr_len arg (NULL pointer)\n")) goto error; } *saddr = tmp_saddr; *saddr_len = tmp_saddr_len; } TRACELVL(5, (mcl_stdout, "<- mcl_recvfrom: return %d from %s\n", rlen, inet_ntoa(((struct sockaddr_in*)&tmp_saddr)->sin_addr))) end: mcl_unlock(&mcl_mutex_lock); return rlen; error: mcl_unlock(&mcl_mutex_lock); return -1; } /* * mcl_wait_event * * Wait (i.e. block) that a given event takes place and return * * Returns 0 if ok or -1 if error */ int mcl_wait_event (int id, int event) { mclcb_t *mclcb; if (id >= MCLCB_MAX_ID || (mclcb=mclcbs[id]) == NULL) { PRINT_ERR((mcl_stderr, "mcl_wait_event: ERROR, not an MCL id %d\n", id)) return -1; } mcl_lock(&mcl_mutex_lock); switch(event) { case MCL_WAIT_EVENT_END_TX: /* * wait untill all DUs have been sent */ if (!mclcb->sender) { PRINT_ERR((mcl_stderr, "mcl_wait_event: MCL_WAIT_EVENT_END_TX, ERROR, id %d isn't sender\n", id)) goto error; } while (mclcb->more_to_tx) { TRACELVL(5, (mcl_stdout, " mcl_wait_event: MCL_WAIT_EVENT_END_TX, wait a bit...\n")) mcl_unlock(&mcl_mutex_lock); mcl_usleep(DFLT_POLLING_PERIOD); mcl_lock(&mcl_mutex_lock); } break; case MCL_WAIT_EVENT_END_RX: /* * wait untill a sufficient number of DUs have been received * to complete all ADUs */ if (!mclcb->receiver) { PRINT_ERR((mcl_stderr, "mcl_wait_event: MCL_WAIT_EVENT_END_RX, ERROR, id %d isn't receiver\n", id)) goto error; } while (!mcl_fsm_no_new_undup_du(mclcb)) { TRACELVL(5, (mcl_stdout, " mcl_wait_event: MCL_WAIT_EVENT_END_RX, wait a bit...\n")) mcl_unlock(&mcl_mutex_lock); mcl_usleep(DFLT_POLLING_PERIOD); mcl_lock(&mcl_mutex_lock); } break; case MCL_WAIT_EVENT_CLOSED: /* * wait untill we receive a CLOSE message from source, * indicating that all DUs have been sent... */ if (!mclcb->receiver) { PRINT_ERR((mcl_stderr, "mcl_wait_event: MCL_WAIT_EVENT_CLOSED, ERROR, id %d isn't receiver\n", id)) goto error; } /* * NB: it requires that the receiver continues to receive * DUs, even after the completion of all the ADUs, so don't * leave layer 0 otherwise the CLOSE messages may be lost... */ if (!mclcb->never_leave_base_layer) { PRINT_ERR((mcl_stderr, "mcl_wait_event: MCL_WAIT_EVENT_CLOSED, ERROR, use NEVER_LEAVE_BASE_LAYER option first\n")) goto error; } while (!mcl_fsm_close_already_rx(mclcb)) { TRACELVL(5, (mcl_stdout, " mcl_wait_event: MCL_WAIT_EVENT_CLOSED, wait a bit...\n")) mcl_unlock(&mcl_mutex_lock); mcl_usleep(DFLT_POLLING_PERIOD); mcl_lock(&mcl_mutex_lock); } break; default: PRINT_ERR((mcl_stderr, "mcl_wait_event: ERROR, unknown event %d\n", event)) goto error; } mcl_unlock(&mcl_mutex_lock); return 0; error: mcl_unlock(&mcl_mutex_lock); return -1; } /* * mcl_select * * WARNING: this is a limited version of mcl_select that only accepts * a single MCL fd of read type, with a timeout. * All uses with several readfds, a non null writefds or exceptfds * will fail. * * params : same at the Socket select() syscall * * return : same at the Socket select() syscall * WARNING: the timeout parameter is undefined after calling * mcl_select, do not assume linux' select(2) like behaviour * concerning timeout... */ int mcl_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { #ifdef MCL_SELECT mclcb_t *mclcb; int id; /* readfd of interest (there should be only 1)*/ int rlen; struct timeval begin; /* time starts here... */ struct timeval end; /* ...and stops here */ struct timeval cur; /* current time to compare with end */ if (writefds != NULL || exceptfds != NULL) { PRINT_ERR((mcl_stderr, "mcl_select: ERROR, non NULL writefds or exceptfds not supported\n")) return -1; } if (readfds == NULL) { PRINT_ERR((mcl_stderr, "mcl_select: ERROR, NULL readfs not supported\n")) return -1; } if (timeout) { /* sanity checks */ if (timeout->tv_sec < 0 || timeout->tv_usec < 0 || timeout->tv_usec >= 1000000) { PRINT_ERR((mcl_stderr, "mcl_select: ERROR, invalid timeout value\n")) return -1; } /* calculate timeout date */ mcl_gettimeofday(&begin); end.tv_usec = (begin.tv_usec + timeout->tv_usec) % 1000000; end.tv_sec = begin.tv_sec + timeout->tv_sec + (begin.tv_usec + timeout-> tv_usec) / 1000000; } #ifdef DEBUG /* check there is a single fd set in readfds */ #endif id = n - 1; if (id >= MCLCB_MAX_ID || (mclcb=mclcbs[id]) == NULL) { PRINT_ERR((mcl_stderr, "mcl_select: ERROR, not an MCL id %d\n", id)) return -1; } mcl_lock(&mcl_mutex_lock); /* * since the problem is to wait at most timeout until a new ADU * is available on this session, we can reuse the mcl_recvfrom * code, with the only exception that we don't read data from * the incoming list, just check if there is some available. */ TRACELVL(5, (mcl_stdout, "-> mcl_select: watch mclcb=x%x\n", (int)mclcb)) if (!mclcb->initialized) #ifdef STREAM mcl_end_init_mclcb(mclcb,1); #else mcl_end_init_mclcb(mclcb); #endif /* * check if an adu is available without removing it from MCL's * incoming list (use NULL buffer/ 0 len for that). * XXX : use polling for the present... change it! */ while ((rlen = mcl_return_adu_to_appli(mclcb, (char*)NULL, (u_int)0, NULL, NULL)) < 0) { if (mcl_fsm_no_new_undup_du(mclcb)) { TRACELVL(5, (mcl_stdout, "<- mcl_recvfrom: closed, return -1\n")) return 0; } /* * check if we can afford to wait some more... */ if (timeout) { mcl_gettimeofday(&cur); if (cur.tv_sec > end.tv_sec || (cur.tv_sec == end.tv_sec && cur.tv_usec > end.tv_usec)) { TRACELVL(5, (mcl_stdout, "<- mcl_select: timeout\n")) mcl_unlock(&mcl_mutex_lock); return 0; } } else { TRACELVL(5, (mcl_stdout, "<- mcl_select: nothing available\n")) mcl_unlock(&mcl_mutex_lock); return 0; } /* * nothing received yet so wait a little bit (polling). * the waiting time is a compromize... what should we use? */ mcl_unlock(&mcl_mutex_lock); mcl_usleep(DFLT_POLLING_PERIOD); mcl_lock(&mcl_mutex_lock); } ASSERT(rlen == 0); TRACELVL(5, (mcl_stdout, "<- mcl_select: new ADU available\n")) mcl_unlock(&mcl_mutex_lock); return 1; #else /* MCL_SELECT */ PRINT_ERR((mcl_stderr, "mcl_select: ERROR, not compiled with mcl_select support\n" " (enable MCL_SELECT in mcl_profile.h)\n")) return -1; #endif /* MCL_SELECT */ } #if 0 /* bad old version { */ /* * mcl_select * * Some of the fds may be a MCL descriptor, so scan everything and * for each MCL fd replace the single fd with the range of fds we use. * Then, before returning, come back to the initial fd set. * * params : same at the Socket select() syscall * * return : same at the Socket select() syscall * WARNING: the timeout parameter is undefined after calling * mcl_select, do not assume linux' select(2) like behaviour * concerning timeout... */ int mcl_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { #ifdef MCL_SELECT /*********************************************************************** * FIXME : not fullproof, especially when descriptors are open'ed * or close'd by another thread during mcl_select (if they're * in the descriptor sets) **********************************************************************/ int id; fd_set our_rfds; fd_set our_wfds; fd_set zero_rfds; /* zero'ed read fds if arg is NULL */ fd_set zero_wfds; /* zero'ed write fds if arg is NULL */ fd_set zero_efds; /* zero'ed exceptfds */ /* except has no sense with mcl descr, ignored */ fd_set nonmcl_rfds; fd_set nonmcl_wfds; fd_set nonmcl_efds; int our_n = 0; int ret; int rfound = 0; int wfound = 0; int fdr, fdw, fde; int i; mclcb_t *mclcb; struct timeval begin; struct timeval end; struct timeval last; int last_pass = 0; #define MAX_TIMEOUT_PER_LOOP 2 /* in seconds */ ASSERT(!timeout || (timeout->tv_sec >= 0 && timeout->tv_usec >= 0 && timeout->tv_usec < 1000000)); /* * point to local fds variable if argument is NULL */ if (readfds == NULL) { readfds = &zero_rfds; FD_ZERO(readfds); } if (writefds == NULL) { writefds = &zero_wfds; FD_ZERO(writefds); } /* * except has no sense on mcl descritors - ignore them */ exceptfds = &zero_efds; FD_ZERO(exceptfds); /* * compute the deadline of the mcl_select function */ if (timeout) { begin = mcl_get_tvtime(); end.tv_usec = (begin.tv_usec + timeout->tv_usec) % 1000000; end.tv_sec = begin.tv_sec + timeout->tv_sec + (begin.tv_usec + timeout-> tv_usec) / 1000000; } /* * step 1 : clear all internal mcl fds from fdsets (and public mcl fds * from exceptfds) * this shouldn't be necessary if user calls mcl_select properly * however. */ for (id = 0; id < MCLCB_MAX_ID; id++) if ((mclcb = mclcbs[id]) != NULL && mclcb->initialized) { /* * except has no sense on mcl descritors - ignore them */ /*if (exceptfds != NULL && FD_ISSET(id, exceptfds))*/ /* FD_CLR((unsigned int)id, exceptfds); */ /* * remove mcl internal descriptors from fd sets * they shouldn't be in user's fd sets anyway * just to prevent some rogue behaviour from the user */ if (FD_ISSET(id, &mclcb->rxlvl.fds)) { if (FD_ISSET(id, readfds)) FD_CLR((unsigned int)id, readfds); if (FD_ISSET(id, writefds)) FD_CLR((unsigned int)id, writefds); if (FD_ISSET(id, exceptfds)) FD_CLR((unsigned int)id, exceptfds); } for (i = 0; i < MAX_TX_LEVEL; i++) { if (id == (int)mclcb->txlvl_tab[i].mcgroup->ses_sock || id == (int)mclcb->txlvl_tab[i].mcgroup->priv_sock) { /* TODO WIN32 compliant? */ if (FD_ISSET(id, readfds)) FD_CLR((unsigned int)id, readfds); if (FD_ISSET(id, writefds)) FD_CLR((unsigned int)id, writefds); if (FD_ISSET(id, exceptfds)) FD_CLR((unsigned int)id, exceptfds); } } } for (;;) { /* * step 2 : check for ready ADUs on specified mcl public * descriptors */ our_n = n; FD_ZERO(&our_rfds); FD_ZERO(&our_wfds); FD_ZERO(&nonmcl_rfds); FD_ZERO(&nonmcl_wfds); FD_ZERO(&nonmcl_efds); for (id = 0; id < n; id++) { fdr = FD_ISSET(id, readfds); fdw = FD_ISSET(id, writefds); fde = FD_ISSET(id, exceptfds); if (fdr || fdw || fde) { /* * this descritor is one or more of the fd sets */ if (id < MCLCB_MAX_ID && (mclcb = mclcbs[id]) != NULL) { /* * this is a public mcl descriptor * which has to be initialized in * order to receive anything */ if (!mclcb->initialized) #ifdef STREAM mcl_end_init_mclcb(mclcb,1); #else mcl_end_init_mclcb(mclcb); #endif mcl_lock(&mcl_mutex_lock); /* * if id is in read fd set, do we have * ready data in session #id ? */ if (fdr && mclcb->ready_data > 0) { if (!rfound++) { /* * found something on * mcl fds * we won't select(2) * on mcl fds... * clear them */ FD_ZERO(&our_rfds); FD_ZERO(&our_wfds); } FD_SET((unsigned int)id, &our_rfds); } /* * if id is in write fd set, can we * write in this session? */ if (fdw && mcl_fsm_can_send_data(mclcb)) { if (!wfound++) { /* * found something on * mcl fds * we won't select(2) * on mcl fds... * clear them */ FD_ZERO(&our_rfds); FD_ZERO(&our_wfds); } FD_SET((unsigned int)id, &our_wfds); } /* * still nothing ready - prepare * select(2) call */ if (!(rfound || wfound)) { if (readfds && fdr) our_n = max(our_n, mcl_add_in_our_fds (id, &our_rfds)); if (writefds && fdw) our_n = max(our_n, mcl_add_in_our_fds (id, &our_wfds)); } mcl_unlock(&mcl_mutex_lock); } else { /* * this is a non-mcl descriptor - * prepare select(2) call */ if (fdr) FD_SET((unsigned int)id, &nonmcl_rfds); if (fdw) FD_SET((unsigned int)id, &nonmcl_wfds); if (fde) FD_SET((unsigned int)id, &nonmcl_efds); } } } /* * step 3 : if there are ready ADUs, or sessions ready to send * data then do a non-blocking select on non-mcl fds */ if (rfound || wfound) { last.tv_sec = 0; /* timeout = {0, 0} */ last.tv_usec = 0; if ((ret = select(n, &nonmcl_rfds, &nonmcl_wfds, &nonmcl_efds, &last)) == -1) return ret; /* * clear user's fd sets */ FD_ZERO(readfds); FD_ZERO(writefds); FD_ZERO(exceptfds); /* * and set the appropriate fds in them */ for (id = 0; id < n; id++) { if (FD_ISSET(id, &our_rfds)) FD_SET((unsigned int)id, readfds); if (FD_ISSET(id, &our_wfds)) FD_SET((unsigned int)id, writefds); if (FD_ISSET(id, &nonmcl_rfds)) FD_SET((unsigned int)id, readfds); if (FD_ISSET(id, &nonmcl_wfds)) FD_SET((unsigned int)id, writefds); if (FD_ISSET(id, &nonmcl_efds)) FD_SET((unsigned int)id, exceptfds); } return (rfound + wfound + ret); } /* * step 4 : else do a blocking select on both non-mcl fds and * mcl internal fds */ else { /* * merge fd sets : non-mcl + mcl internals */ for (id = 0; id < our_n; id++) { if (FD_ISSET(id, &our_rfds)) FD_SET((unsigned int)id, &nonmcl_rfds); if (FD_ISSET(id, &our_wfds)) FD_SET((unsigned int)id, &nonmcl_wfds); } if (timeout) { /* * we won't block indefinitely... * compute the time we have left */ last = mcl_get_tvtime(); if (timercmp(&end, &last, <)) { /* * finished - * do one more non-blocking select(2) * and return */ last.tv_sec = 0; last.tv_usec = 0; last_pass = 1; } else { /* * compute max duration of next * select(2) */ last.tv_sec = end.tv_sec - last.tv_sec; last.tv_usec = end.tv_usec - last.tv_usec; if (last.tv_usec < 0) { last.tv_usec += 1000000; last.tv_sec--; } if (last.tv_sec >= MAX_TIMEOUT_PER_LOOP) { /* * max duration can't exceed * MAX_TIMEOUT_PER_LOOP * because we have to do some * polling on mcl descriptors */ last.tv_sec = MAX_TIMEOUT_PER_LOOP; last.tv_usec = 0; } } } else { last.tv_sec = MAX_TIMEOUT_PER_LOOP; last.tv_usec = 0; } if ((ret = select(our_n, &nonmcl_rfds, &nonmcl_wfds, &nonmcl_efds, &last)) == -1) return ret; if (last_pass) { /* * user timeout exceeded - return */ for (id = 0; id < our_n; id++) { /* * keep in user's fd sets only what's * also in temporary fd sets */ if (!(FD_ISSET(id, &nonmcl_rfds) && FD_ISSET(id, readfds))) FD_CLR((unsigned int)id, readfds); if (!(FD_ISSET(id, &nonmcl_wfds) && FD_ISSET(id, writefds))) FD_CLR((unsigned int)id, writefds); if (!(FD_ISSET(id, &nonmcl_efds) && FD_ISSET(id, exceptfds))) FD_CLR((unsigned int)id, exceptfds); } return (ret); } /* * we don't return right now : next step 3 will return * if new ADUs have been completed * FIXME : we should wait a little so that the * incoming packets are treated by the lib; * else mcl_select return may be delayed untill next * loop (MAX_TIMEOUT_PER_LOOP * seconds later at most) */ } } #else /* MCL_SELECT */ PRINT_ERR((mcl_stderr, "mcl_select: ERROR, not compiled with mcl_select support\n" " (enable MCL_SELECT in mcl_profile.h)\n")) return -1; #endif /* MCL_SELECT */ } #endif /* } 0 */