freebsd-dev/sys/netatm/uni/sscop_timer.c
Poul-Henning Kamp 1820df7a2d Add new files for HARP3
Host ATM Research Platform (HARP), Network Computing Services, Inc.
This software was developed with the support of the Defense Advanced
Research Projects Agency (DARPA).
1998-09-15 08:23:17 +00:00

577 lines
10 KiB
C

/*
*
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*
* @(#) $Id: sscop_timer.c,v 1.6 1998/04/07 23:21:48 mks Exp $
*
*/
/*
* ATM Forum UNI Support
* ---------------------
*
* SSCOP - Timer processing
*
*/
#ifndef lint
static char *RCSid = "@(#) $Id: sscop_timer.c,v 1.6 1998/04/07 23:21:48 mks Exp $";
#endif
#include <netatm/kern_include.h>
#include <netatm/uni/uni.h>
#include <netatm/uni/sscop.h>
#include <netatm/uni/sscop_misc.h>
#include <netatm/uni/sscop_pdu.h>
#include <netatm/uni/sscop_var.h>
/*
* Local functions
*/
static void sscop_poll_expire __P((struct sscop *));
static void sscop_noresponse_expire __P((struct sscop *));
static void sscop_cc_expire __P((struct sscop *));
static void sscop_idle_expire __P((struct sscop *));
/*
* Local variables
*/
static void (*sscop_expired[SSCOP_T_NUM]) __P((struct sscop *)) = {
sscop_poll_expire,
sscop_noresponse_expire,
sscop_cc_expire,
sscop_idle_expire
};
/*
* Process an SSCOP timer tick
*
* This function is called SSCOP_HZ times a second in order to update
* all of the sscop connection timers. The sscop expiration function
* will be called to process all timer expirations.
*
* Called at splnet.
*
* Arguments:
* tip pointer to sscop timer control block
*
* Returns:
* none
*
*/
void
sscop_timeout(tip)
struct atm_time *tip;
{
struct sscop *sop, **sprev;
int i;
/*
* Schedule next timeout
*/
atm_timeout(&sscop_timer, ATM_HZ/SSCOP_HZ, sscop_timeout);
/*
* Run through all connections, updating each active timer.
* If an expired timer is found, notify that entry.
*/
sprev = &sscop_head;
while (sop = *sprev) {
/*
* Check out each timer
*/
for (i =0; i < SSCOP_T_NUM; i++) {
/*
* Decrement timer if it's active
*/
if (sop->so_timer[i] && (--sop->so_timer[i] == 0)) {
#ifdef DIAGNOSTIC
{
static char *tn[] = {
"POLL",
"NORESPONSE",
"CC",
"IDLE"
};
ATM_DEBUG3("sscop_timer: %s expired, sop=0x%x, state=%d\n",
tn[i], (int)sop, sop->so_state);
}
#endif
/*
* Expired timer - process it
*/
(*sscop_expired[i])(sop);
/*
* Make sure connection still exists
*/
if (*sprev != sop)
break;
}
}
/*
* Update previous pointer if current control
* block wasn't deleted
*/
if (*sprev == sop)
sprev = &sop->so_next;
}
}
/*
* Process an SSCOP Timer_POLL expiration
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* none
*
*/
static void
sscop_poll_expire(sop)
struct sscop *sop;
{
/*
* Validate current state
*/
if ((sop->so_state != SOS_READY) &&
((sop->so_state != SOS_INRESYN) ||
(sop->so_vers != SSCOP_VERS_QSAAL))) {
log(LOG_ERR, "sscop: invalid %s state: sop=0x%x, state=%d\n",
"Timer_POLL", (int)sop, sop->so_state);
return;
}
/*
* Send next poll along its way
*/
SEQ_INCR(sop->so_pollsend, 1);
(void) sscop_send_poll(sop);
/*
* Reset data counter for this poll cycle
*/
sop->so_polldata = 0;
/*
* Reset polling timer
*/
sscop_set_poll(sop);
return;
}
/*
* Process an SSCOP Timer_IDLE expiration
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* none
*
*/
static void
sscop_idle_expire(sop)
struct sscop *sop;
{
/*
* Timer_IDLE only valid in READY state
*/
if (sop->so_state != SOS_READY) {
log(LOG_ERR, "sscop: invalid %s state: sop=0x%x, state=%d\n",
"Timer_IDLE", (int)sop, sop->so_state);
return;
}
/*
* Send next poll along its way
*/
SEQ_INCR(sop->so_pollsend, 1);
(void) sscop_send_poll(sop);
/*
* Reset data counter for this poll cycle
*/
sop->so_polldata = 0;
/*
* Start NO-RESPONSE timer
*/
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
/*
* Reset polling timer
*/
sscop_set_poll(sop);
return;
}
/*
* Process an SSCOP Timer_NORESPONSE expiration
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* none
*
*/
static void
sscop_noresponse_expire(sop)
struct sscop *sop;
{
int err;
/*
* Validate current state
*/
if ((sop->so_state != SOS_READY) &&
((sop->so_state != SOS_INRESYN) ||
(sop->so_vers != SSCOP_VERS_QSAAL))) {
log(LOG_ERR, "sscop: invalid %s state: sop=0x%x, state=%d\n",
"Timer_NORESPONSE", (int)sop, sop->so_state);
return;
}
/*
* Peer seems to be dead, so terminate session
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper,
sop->so_toku, sop->so_connvc,
SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
if (err) {
/*
* Error, force retry
*/
sop->so_timer[SSCOP_T_NORESP] = 1;
return;
}
/*
* Stop data transfer timers
*/
sop->so_timer[SSCOP_T_POLL] = 0;
sop->so_timer[SSCOP_T_IDLE] = 0;
sop->so_flags &= ~SOF_KEEPALIVE;
/*
* Notify peer of termination
*/
(void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP);
/*
* Report peer's failure
*/
sscop_maa_error(sop, 'P');
if (sop->so_vers == SSCOP_VERS_QSAAL)
/*
* Clear connection data
*/
qsaal1_clear_connection(sop);
else
/*
* Clear out appropriate queues
*/
q2110_prep_retrieve(sop);
/*
* Return to IDLE state
*/
sop->so_state = SOS_IDLE;
return;
}
/*
* Process an SSCOP Timer_CC expiration
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* none
*
*/
static void
sscop_cc_expire(sop)
struct sscop *sop;
{
int err;
/*
* Process timeout based on protocol state
*/
switch (sop->so_state) {
case SOS_OUTCONN:
/*
* No response to our BGN yet
*/
if (sop->so_connctl < sop->so_parm.sp_maxcc) {
/*
* Send another BGN PDU
*/
sop->so_connctl++;
(void) sscop_send_bgn(sop, SSCOP_SOURCE_USER);
/*
* Restart retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc;
} else {
/*
* Retransmit limit exceeded, terminate session
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper,
sop->so_toku, sop->so_connvc,
SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
if (err) {
/*
* Error, force retry
*/
sop->so_timer[SSCOP_T_CC] = 1;
break;
}
/*
* Notify peer of termination
*/
(void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP);
/*
* Report establishment failure
*/
sscop_maa_error(sop, 'O');
/*
* Clear reestablishment flag
*/
sop->so_flags &= ~SOF_REESTAB;
/*
* Return to IDLE state
*/
sop->so_state = SOS_IDLE;
}
break;
case SOS_OUTDISC:
/*
* No response to our END yet
*/
if (sop->so_connctl < sop->so_parm.sp_maxcc) {
/*
* Send another END PDU
*/
sop->so_connctl++;
(void) sscop_send_end(sop, SSCOP_SOURCE_LAST);
/*
* Restart retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc;
} else {
/*
* Retransmit limit exceeded, force session termination
*/
STACK_CALL(SSCOP_RELEASE_CNF, sop->so_upper,
sop->so_toku, sop->so_connvc, 0, 0, err);
if (err) {
/*
* Error, force retry
*/
sop->so_timer[SSCOP_T_CC] = 1;
break;
}
/*
* Report establishment failure
*/
sscop_maa_error(sop, 'O');
/*
* Return to IDLE state
*/
sop->so_state = SOS_IDLE;
}
break;
case SOS_OUTRESYN:
rexmitrs:
/*
* No response to our RS yet
*/
if (sop->so_connctl < sop->so_parm.sp_maxcc) {
/*
* Send another RS PDU
*/
sop->so_connctl++;
(void) sscop_send_rs(sop);
/*
* Restart retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc;
} else {
/*
* Retransmit limit exceeded, terminate session
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper,
sop->so_toku, sop->so_connvc,
SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
if (err) {
/*
* Error, force retry
*/
sop->so_timer[SSCOP_T_CC] = 1;
break;
}
/*
* Notify peer of termination
*/
(void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP);
/*
* Report establishment failure
*/
sscop_maa_error(sop, 'O');
if (sop->so_vers == SSCOP_VERS_QSAAL)
/*
* Clear connection data
*/
qsaal1_clear_connection(sop);
/*
* Return to IDLE state
*/
sop->so_state = SOS_IDLE;
}
break;
case SOS_CONRESYN: /* Q.SAAL1 */
#if (SOS_OUTRECOV != SOS_CONRESYN)
case SOS_OUTRECOV: /* Q.2110 */
#endif
if (sop->so_vers == SSCOP_VERS_QSAAL) {
/*
* Handle timeout for SOS_CONRESYN
*/
goto rexmitrs;
}
/*
* Handle timeout for SOS_OUTRECOV
*/
/*
* No response to our ER yet
*/
if (sop->so_connctl < sop->so_parm.sp_maxcc) {
/*
* Send another ER PDU
*/
sop->so_connctl++;
(void) sscop_send_er(sop);
/*
* Restart retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc;
} else {
/*
* Retransmit limit exceeded, terminate session
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper,
sop->so_toku, sop->so_connvc,
SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
if (err) {
/*
* Error, force retry
*/
sop->so_timer[SSCOP_T_CC] = 1;
break;
}
/*
* Notify peer of termination
*/
(void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP);
/*
* Report establishment failure
*/
sscop_maa_error(sop, 'O');
/*
* Clear receiver buffer
*/
sscop_rcvr_drain(sop);
/*
* Return to IDLE state
*/
sop->so_state = SOS_IDLE;
}
break;
default:
log(LOG_ERR, "sscop: invalid %s state: sop=0x%x, state=%d\n",
"Timer_CC", (int)sop, sop->so_state);
}
}