freebsd-dev/sys/netatm/uni/q2110_sigcpcs.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

1761 lines
34 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: q2110_sigcpcs.c,v 1.7 1998/08/26 23:29:18 mks Exp $
*
*/
/*
* ATM Forum UNI Support
* ---------------------
*
* ITU-T Q.2110 - Process CPCS-signals (SSCOP PDUs)
*
*/
#ifndef lint
static char *RCSid = "@(#) $Id: q2110_sigcpcs.c,v 1.7 1998/08/26 23:29:18 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_bgn_outconn __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_bgn_inconn __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_bgn_ready __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_bgrej_outrecov __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_end_outrecov __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_end_ready __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_endak_outrecov __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_rs_outresyn __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_rs_inresyn __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_rs_outrecov __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_rs_ready __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_er_error __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_er_idle __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_er_outrecov __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_er_recovrsp __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_er_inrecov __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_er_ready __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_erak_error __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_erak_idle __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_erak_outrecov __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_sd_ready __P((struct sscop *, KBuffer *, caddr_t));
static void sscop_poll_ready __P((struct sscop *, KBuffer *, caddr_t));
/*
* PDU type state lookup tables
*/
/* BGN PDU */
static void (*sscop_bgn_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_bgn_idle, /* SOS_IDLE */
sscop_bgn_outconn, /* SOS_OUTCONN */
sscop_bgn_inconn, /* SOS_INCONN */
sscop_bgn_outdisc, /* SOS_OUTDISC */
sscop_bgn_outresyn, /* SOS_OUTRESYN */
sscop_bgn_inresyn, /* SOS_INRESYN */
sscop_bgn_inresyn, /* SOS_OUTRECOV */
sscop_bgn_inresyn, /* SOS_RECOVRSP */
sscop_bgn_inresyn, /* SOS_INRECOV */
sscop_bgn_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* BGAK PDU */
static void (*sscop_bgak_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_bgak_idle, /* SOS_IDLE */
sscop_bgak_outconn, /* SOS_OUTCONN */
sscop_bgak_error, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_noop, /* SOS_OUTRESYN */
sscop_bgak_error, /* SOS_INRESYN */
sscop_bgak_error, /* SOS_OUTRECOV */
sscop_bgak_error, /* SOS_RECOVRSP */
sscop_bgak_error, /* SOS_INRECOV */
sscop_noop, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* BGREJ PDU */
static void (*sscop_bgrej_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_bgrej_error, /* SOS_IDLE */
sscop_bgrej_outconn, /* SOS_OUTCONN */
sscop_bgrej_inconn, /* SOS_INCONN */
sscop_endak_outdisc, /* SOS_OUTDISC */
sscop_bgrej_outresyn, /* SOS_OUTRESYN */
sscop_bgrej_inconn, /* SOS_INRESYN */
sscop_bgrej_outrecov, /* SOS_OUTRECOV */
sscop_bgrej_inconn, /* SOS_RECOVRSP */
sscop_bgrej_inconn, /* SOS_INRECOV */
sscop_bgrej_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* END PDU */
static void (*sscop_end_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_end_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_end_inconn, /* SOS_INCONN */
sscop_end_outdisc, /* SOS_OUTDISC */
sscop_end_inconn, /* SOS_OUTRESYN */
sscop_end_inconn, /* SOS_INRESYN */
sscop_end_outrecov, /* SOS_OUTRECOV */
sscop_end_inconn, /* SOS_RECOVRSP */
sscop_end_inconn, /* SOS_INRECOV */
sscop_end_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* ENDAK PDU */
static void (*sscop_endak_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_noop, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_endak_inconn, /* SOS_INCONN */
sscop_endak_outdisc, /* SOS_OUTDISC */
sscop_endak_inconn, /* SOS_OUTRESYN */
sscop_endak_inconn, /* SOS_INRESYN */
sscop_endak_outrecov, /* SOS_OUTRECOV */
sscop_endak_inconn, /* SOS_RECOVRSP */
sscop_endak_inconn, /* SOS_INRECOV */
sscop_endak_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* RS PDU */
static void (*sscop_rs_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_rs_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_rs_error, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_rs_outresyn, /* SOS_OUTRESYN */
sscop_rs_inresyn, /* SOS_INRESYN */
sscop_rs_outrecov, /* SOS_OUTRECOV */
sscop_rs_outrecov, /* SOS_RECOVRSP */
sscop_rs_outrecov, /* SOS_INRECOV */
sscop_rs_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* RSAK PDU */
static void (*sscop_rsak_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_rsak_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_rsak_error, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_rsak_outresyn, /* SOS_OUTRESYN */
sscop_rsak_error, /* SOS_INRESYN */
sscop_rsak_error, /* SOS_OUTRECOV */
sscop_rsak_error, /* SOS_RECOVRSP */
sscop_rsak_error, /* SOS_INRECOV */
sscop_noop, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* ER PDU */
static void (*sscop_er_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_er_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_er_error, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_noop, /* SOS_OUTRESYN */
sscop_er_error, /* SOS_INRESYN */
sscop_er_outrecov, /* SOS_OUTRECOV */
sscop_er_recovrsp, /* SOS_RECOVRSP */
sscop_er_inrecov, /* SOS_INRECOV */
sscop_er_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* ERAK PDU */
static void (*sscop_erak_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_erak_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_erak_error, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_noop, /* SOS_OUTRESYN */
sscop_erak_error, /* SOS_INRESYN */
sscop_erak_outrecov, /* SOS_OUTRECOV */
sscop_noop, /* SOS_RECOVRSP */
sscop_erak_error, /* SOS_INRECOV */
sscop_noop, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* SD PDU */
static void (*sscop_sd_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_sd_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_sd_inconn, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_noop, /* SOS_OUTRESYN */
sscop_sd_inconn, /* SOS_INRESYN */
sscop_noop, /* SOS_OUTRECOV */
sscop_noop, /* SOS_RECOVRSP */
sscop_sd_inconn, /* SOS_INRECOV */
sscop_sd_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* POLL PDU */
static void (*sscop_poll_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_poll_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_poll_inconn, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_noop, /* SOS_OUTRESYN */
sscop_poll_inconn, /* SOS_INRESYN */
sscop_noop, /* SOS_OUTRECOV */
sscop_noop, /* SOS_RECOVRSP */
sscop_poll_inconn, /* SOS_INRECOV */
sscop_poll_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* STAT PDU */
static void (*sscop_stat_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_stat_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_stat_inconn, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_noop, /* SOS_OUTRESYN */
sscop_stat_inconn, /* SOS_INRESYN */
sscop_noop, /* SOS_OUTRECOV */
sscop_stat_inconn, /* SOS_RECOVRSP */
sscop_stat_inconn, /* SOS_INRECOV */
sscop_stat_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* USTAT PDU */
static void (*sscop_ustat_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_ustat_idle, /* SOS_IDLE */
sscop_noop, /* SOS_OUTCONN */
sscop_ustat_inconn, /* SOS_INCONN */
sscop_noop, /* SOS_OUTDISC */
sscop_noop, /* SOS_OUTRESYN */
sscop_ustat_inconn, /* SOS_INRESYN */
sscop_noop, /* SOS_OUTRECOV */
sscop_ustat_inconn, /* SOS_RECOVRSP */
sscop_ustat_inconn, /* SOS_INRECOV */
sscop_ustat_ready, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* UD PDU */
static void (*sscop_ud_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_ud_all, /* SOS_IDLE */
sscop_ud_all, /* SOS_OUTCONN */
sscop_ud_all, /* SOS_INCONN */
sscop_ud_all, /* SOS_OUTDISC */
sscop_ud_all, /* SOS_OUTRESYN */
sscop_ud_all, /* SOS_INRESYN */
sscop_ud_all, /* SOS_OUTRECOV */
sscop_ud_all, /* SOS_RECOVRSP */
sscop_ud_all, /* SOS_INRECOV */
sscop_ud_all, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/* MD PDU */
static void (*sscop_md_tab[SOS_NUMSTATES])
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL, /* SOS_INST */
sscop_md_all, /* SOS_IDLE */
sscop_md_all, /* SOS_OUTCONN */
sscop_md_all, /* SOS_INCONN */
sscop_md_all, /* SOS_OUTDISC */
sscop_md_all, /* SOS_OUTRESYN */
sscop_md_all, /* SOS_INRESYN */
sscop_md_all, /* SOS_OUTRECOV */
sscop_md_all, /* SOS_RECOVRSP */
sscop_md_all, /* SOS_INRECOV */
sscop_md_all, /* SOS_READY */
sscop_noop /* SOS_TERM */
};
/*
* PDU type lookup table
*/
void (*(*sscop_q2110_pdutab[]))
__P((struct sscop *, KBuffer *, caddr_t)) = {
NULL,
sscop_bgn_tab,
sscop_bgak_tab,
sscop_end_tab,
sscop_endak_tab,
sscop_rs_tab,
sscop_rsak_tab,
sscop_bgrej_tab,
sscop_sd_tab,
sscop_er_tab,
sscop_poll_tab,
sscop_stat_tab,
sscop_ustat_tab,
sscop_ud_tab,
sscop_md_tab,
sscop_erak_tab
};
/*
* BGN PDU / SOS_OUTCONN Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_bgn_outconn(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct bgn_pdu *bp = (struct bgn_pdu *)trlr;
int err;
/*
* If retransmitted BGN, ignore it
*/
if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
KB_FREEALL(m);
return;
}
/*
* Stop retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = 0;
/*
* Initialize state variables
*/
SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));
SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin);
q2110_init_state(sop);
/*
* Return an ACK to peer
*/
(void) sscop_send_bgak(sop);
/*
* Notify user of connection establishment
*/
STACK_CALL(SSCOP_ESTABLISH_CNF, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)m, 0, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Start data transfer timers
*/
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
/*
* OK, we're ready for data
*/
sop->so_state = SOS_READY;
/*
* See if transmit queues need servicing
*/
if (sop->so_flags & SOF_XMITSRVC)
sscop_service_xmit(sop);
return;
}
/*
* BGN PDU / SOS_INCONN Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_bgn_inconn(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct bgn_pdu *bp = (struct bgn_pdu *)trlr;
int err;
/*
* If retransmitted BGN, ignore it
*/
if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
KB_FREEALL(m);
return;
}
/*
* Initialize transmit window
*/
SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));
/*
* First, tell user current connection has been released
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Now, tell user of new connection establishment
*/
STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)m, 0, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
return;
}
/*
* BGN PDU / SOS_READY Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_bgn_ready(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct bgn_pdu *bp = (struct bgn_pdu *)trlr;
int err;
/*
* If retransmitted BGN, just ACK it again
*/
if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
KB_FREEALL(m);
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
(void) sscop_send_bgak(sop);
return;
}
/*
* Stop data transfer timers
*/
sop->so_timer[SSCOP_T_POLL] = 0;
sop->so_timer[SSCOP_T_NORESP] = 0;
sop->so_timer[SSCOP_T_IDLE] = 0;
sop->so_flags &= ~SOF_KEEPALIVE;
/*
* Initialize transmit window
*/
SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));
/*
* Clear out appropriate queues
*/
q2110_prep_retrieve(sop);
/*
* Tell user current connection has been released
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Tell user of incoming connection
*/
STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)m, 0, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Wait for user's response
*/
sop->so_state = SOS_INCONN;
return;
}
/*
* BGREJ PDU / SOS_OUTRECOV Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_bgrej_outrecov(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
int err;
/*
* Stop retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = 0;
/*
* Report protocol error
*/
sscop_bgrej_error(sop, m, trlr);
/*
* Notify user of connection failure
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
if (err) {
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Clear receiver buffer
*/
sscop_rcvr_drain(sop);
/*
* Back to idle state
*/
sop->so_state = SOS_IDLE;
return;
}
/*
* END PDU / SOS_OUTRECOV Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_end_outrecov(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct end_pdu *ep = (struct end_pdu *)trlr;
int err, source;
/*
* Stop retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = 0;
/*
* Acknowledge END
*/
(void) sscop_send_endak(sop);
/*
* Get Source value
*/
if (ep->end_type & PT_SOURCE_SSCOP)
source = SSCOP_SOURCE_SSCOP;
else
source = SSCOP_SOURCE_USER;
/*
* Notify user of connection termination
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)m, source, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Clear receiver buffer
*/
sscop_rcvr_drain(sop);
/*
* Back to idle state
*/
sop->so_state = SOS_IDLE;
return;
}
/*
* END PDU / SOS_READY Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_end_ready(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct end_pdu *ep = (struct end_pdu *)trlr;
int err, source;
/*
* Stop data transfer timers
*/
sop->so_timer[SSCOP_T_POLL] = 0;
sop->so_timer[SSCOP_T_NORESP] = 0;
sop->so_timer[SSCOP_T_IDLE] = 0;
sop->so_flags &= ~SOF_KEEPALIVE;
/*
* Acknowledge END
*/
(void) sscop_send_endak(sop);
/*
* Get Source value
*/
if (ep->end_type & PT_SOURCE_SSCOP)
source = SSCOP_SOURCE_SSCOP;
else
source = SSCOP_SOURCE_USER;
/*
* Notify user of connection termination
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)m, source, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Clear out appropriate queues
*/
q2110_prep_retrieve(sop);
/*
* Back to idle state
*/
sop->so_state = SOS_IDLE;
return;
}
/*
* ENDAK PDU / SOS_OUTRECOV Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_endak_outrecov(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
int err;
/*
* Stop retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = 0;
/*
* Report protocol error
*/
sscop_endak_error(sop, m, trlr);
/*
* Notify user of connection failure
*/
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
if (err) {
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Clear receiver buffer
*/
sscop_rcvr_drain(sop);
/*
* Back to idle state
*/
sop->so_state = SOS_IDLE;
return;
}
/*
* RS PDU / SOS_OUTRESYN Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_rs_outresyn(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct rs_pdu *rp = (struct rs_pdu *)trlr;
int err;
/*
* If retransmitted RS, ignore it
*/
if (sscop_is_rexmit(sop, rp->rs_nsq)) {
KB_FREEALL(m);
return;
}
/*
* Stop retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = 0;
/*
* Initialize state variables
*/
SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr));
SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin);
q2110_init_state(sop);
/*
* Free PDU buffers
*/
KB_FREEALL(m);
/*
* Return an ACK to peer
*/
(void) sscop_send_rsak(sop);
/*
* Notify user of connection resynchronization
*/
STACK_CALL(SSCOP_RESYNC_CNF, sop->so_upper, sop->so_toku,
sop->so_connvc, 0, 0, err);
if (err) {
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Start data transfer timers
*/
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
/*
* OK, we're ready for data
*/
sop->so_state = SOS_READY;
/*
* See if transmit queues need servicing
*/
if (sop->so_flags & SOF_XMITSRVC)
sscop_service_xmit(sop);
return;
}
/*
* RS PDU / SOS_INRESYN Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_rs_inresyn(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct rs_pdu *rp = (struct rs_pdu *)trlr;
/*
* If retransmitted RS, ignore it
*/
if (sscop_is_rexmit(sop, rp->rs_nsq)) {
KB_FREEALL(m);
return;
}
/*
* Report error condition
*/
sscop_rs_error(sop, m, trlr);
return;
}
/*
* RS PDU / SOS_OUTRECOV Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_rs_outrecov(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct rs_pdu *rp = (struct rs_pdu *)trlr;
int err;
/*
* If retransmitted RS, report an error
*/
if (sscop_is_rexmit(sop, rp->rs_nsq)) {
sscop_rs_error(sop, m, trlr);
return;
}
/*
* Stop retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = 0;
/*
* Initialize transmit window
*/
SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr));
/*
* Notify user of connection resynchronization
*/
STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)m, 0, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Clear receiver buffer
*/
sscop_rcvr_drain(sop);
/*
* Wait for user response
*/
sop->so_state = SOS_INRESYN;
return;
}
/*
* RS PDU / SOS_READY Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_rs_ready(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct rs_pdu *rp = (struct rs_pdu *)trlr;
int err;
/*
* If retransmitted RS, just ACK it
*/
if (sscop_is_rexmit(sop, rp->rs_nsq)) {
KB_FREEALL(m);
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
sscop_send_rsak(sop);
return;
}
/*
* Stop data transfer timers
*/
sop->so_timer[SSCOP_T_POLL] = 0;
sop->so_timer[SSCOP_T_NORESP] = 0;
sop->so_timer[SSCOP_T_IDLE] = 0;
sop->so_flags &= ~SOF_KEEPALIVE;
/*
* Initialize transmit window
*/
SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr));
/*
* Notify user of connection resynchronization
*/
STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)m, 0, err);
if (err) {
KB_FREEALL(m);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Clear out appropriate queues
*/
q2110_prep_retrieve(sop);
/*
* Wait for user response
*/
sop->so_state = SOS_INRESYN;
return;
}
/*
* ER PDU / Protocol Error
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_er_error(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
/*
* Record error condition
*/
sscop_maa_error(sop, 'L');
KB_FREEALL(m);
return;
}
/*
* ER PDU / SOS_IDLE Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_er_idle(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
/*
* Record error condition
*/
sscop_er_error(sop, m, trlr);
/*
* Return an END to peer
*/
(void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP);
return;
}
/*
* ER PDU / SOS_OUTRECOV Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_er_outrecov(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct er_pdu *ep = (struct er_pdu *)trlr;
int err;
/*
* If retransmitted ER, report an error
*/
if (sscop_is_rexmit(sop, ep->er_nsq)) {
sscop_er_error(sop, m, trlr);
return;
}
/*
* Stop retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = 0;
/*
* Initialize transmit window
*/
SEQ_SET(sop->so_sendmax, ntohl(ep->er_nmr));
/*
* Initialize receiver window
*/
SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin);
/*
* Free PDU buffers
*/
KB_FREEALL(m);
/*
* Acknowledge ER
*/
(void) sscop_send_erak(sop);
/*
* Deliver any outstanding data to user
*/
q2110_deliver_data(sop);
/*
* Notify user of connection recovery
*/
STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, 0, 0, err);
if (err) {
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Wait for user response
*/
sop->so_state = SOS_RECOVRSP;
return;
}
/*
* ER PDU / SOS_RECOVRSP Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_er_recovrsp(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct er_pdu *ep = (struct er_pdu *)trlr;
/*
* If retransmitted ER, just ACK it
*/
if (sscop_is_rexmit(sop, ep->er_nsq)) {
KB_FREEALL(m);
(void) sscop_send_erak(sop);
return;
}
/*
* Report error condition
*/
sscop_er_error(sop, m, trlr);
return;
}
/*
* ER PDU / SOS_INRECOV Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_er_inrecov(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct er_pdu *ep = (struct er_pdu *)trlr;
/*
* If retransmitted ER, just ignore it
*/
if (sscop_is_rexmit(sop, ep->er_nsq)) {
KB_FREEALL(m);
return;
}
/*
* Report error condition
*/
sscop_er_error(sop, m, trlr);
return;
}
/*
* ER PDU / SOS_READY Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_er_ready(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct er_pdu *ep = (struct er_pdu *)trlr;
int err;
/*
* If retransmitted ER, just ACK it
*/
if (sscop_is_rexmit(sop, ep->er_nsq)) {
KB_FREEALL(m);
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
sscop_send_erak(sop);
return;
}
/*
* Stop data transfer timers
*/
sop->so_timer[SSCOP_T_POLL] = 0;
sop->so_timer[SSCOP_T_NORESP] = 0;
sop->so_timer[SSCOP_T_IDLE] = 0;
sop->so_flags &= ~SOF_KEEPALIVE;
/*
* Initialize transmit window
*/
SEQ_SET(sop->so_sendmax, ntohl(ep->er_nmr));
/*
* Free PDU buffers
*/
KB_FREEALL(m);
/*
* Clear out appropriate queues
*/
q2110_prep_recovery(sop);
/*
* Deliver any outstanding data to user
*/
q2110_deliver_data(sop);
/*
* Notify user of connection recovery
*/
STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, 0, 0, err);
if (err) {
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Wait for user response
*/
sop->so_state = SOS_INRECOV;
return;
}
/*
* ERAK PDU / Protocol Error
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_erak_error(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
/*
* Record error condition
*/
sscop_maa_error(sop, 'M');
KB_FREEALL(m);
return;
}
/*
* ERAK PDU / SOS_IDLE Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_erak_idle(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
/*
* Record error condition
*/
sscop_erak_error(sop, m, trlr);
/*
* Return an END to peer
*/
(void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP);
return;
}
/*
* ERAK PDU / SOS_OUTRECOV Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_erak_outrecov(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct erak_pdu *ep = (struct erak_pdu *)trlr;
int err;
/*
* Stop retransmit timer
*/
sop->so_timer[SSCOP_T_CC] = 0;
/*
* Initialize transmit window
*/
SEQ_SET(sop->so_sendmax, ntohl(ep->erak_nmr));
/*
* Free PDU buffers
*/
KB_FREEALL(m);
/*
* Deliver any outstanding data to user
*/
q2110_deliver_data(sop);
/*
* Notify user of connection recovery
*/
STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, 0, 0, err);
if (err) {
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Wait for user response
*/
sop->so_state = SOS_RECOVRSP;
return;
}
/*
* SD PDU / SOS_READY Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_sd_ready(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct sd_pdu *sp = (struct sd_pdu *)trlr;
struct pdu_hdr *php;
KBuffer *n;
sscop_seq ns;
int err, space;
/*
* Get PDU sequence number
*/
SEQ_SET(ns, ntohl(sp->sd_ns));
/*
* Ensure that the sequence number fits within the window
*/
if (SEQ_GEQ(ns, sop->so_rcvmax, sop->so_rcvnext)) {
/*
* It doesn't, drop received data
*/
KB_FREEALL(m);
/*
* If next highest PDU hasn't reached window end yet,
* then send a USTAT to inform transmitter of this gap
*/
if (SEQ_LT(sop->so_rcvhigh, sop->so_rcvmax, sop->so_rcvnext)) {
(void) sscop_send_ustat(sop, sop->so_rcvmax);
sop->so_rcvhigh = sop->so_rcvmax;
}
return;
}
/*
* If this is the next in-sequence PDU, hand it to user
*/
if (ns == sop->so_rcvnext) {
STACK_CALL(SSCOP_DATA_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)m, ns, err);
if (err) {
KB_FREEALL(m);
return;
}
/*
* Bump next expected sequence number
*/
SEQ_INCR(sop->so_rcvnext, 1);
/*
* Slide receive window down
*/
SEQ_INCR(sop->so_rcvmax, 1);
/*
* Is this the highest sequence PDU we've received??
*/
if (ns == sop->so_rcvhigh) {
/*
* Yes, bump the limit and exit
*/
sop->so_rcvhigh = sop->so_rcvnext;
return;
}
/*
* This is a retransmitted PDU, so see if we have
* more in-sequence PDUs already queued up
*/
while ((php = sop->so_recv_hd) &&
(php->ph_ns == sop->so_rcvnext)) {
/*
* Yup we do, so remove next PDU from queue and
* pass it up to the user as well
*/
sop->so_recv_hd = php->ph_recv_lk;
if (sop->so_recv_hd == NULL)
sop->so_recv_tl = NULL;
STACK_CALL(SSCOP_DATA_IND, sop->so_upper, sop->so_toku,
sop->so_connvc, (int)php->ph_buf, php->ph_ns,
err);
if (err) {
/*
* Should never happen, but...
*/
KB_FREEALL(php->ph_buf);
sscop_abort(sop, "stack memory\n");
return;
}
/*
* Bump next expected sequence number
*/
SEQ_INCR(sop->so_rcvnext, 1);
/*
* Slide receive window down
*/
SEQ_INCR(sop->so_rcvmax, 1);
}
/*
* Finished with data delivery...
*/
return;
}
/*
* We're gonna have to queue this PDU, so find space
* for the PDU header
*/
KB_HEADROOM(m, space);
/*
* If there's not enough room in the received buffer,
* allocate & link a new buffer for the header
*/
if (space < sizeof(struct pdu_hdr)) {
KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER);
if (n == NULL) {
KB_FREEALL(m);
return;
}
KB_HEADSET(n, sizeof(struct pdu_hdr));
KB_LEN(n) = 0;
KB_LINKHEAD(n, m);
m = n;
}
/*
* Build PDU header
*
* We can at least assume/require that the start of
* the user data is aligned. Also note that we don't
* include this header in the buffer len/offset fields.
*/
KB_DATASTART(m, php, struct pdu_hdr *);
php--;
php->ph_ns = ns;
php->ph_buf = m;
/*
* Insert PDU into the receive queue
*/
if (sscop_recv_insert(sop, php)) {
/*
* Oops, a duplicate sequence number PDU is already on
* the queue, somethings wrong here.
*/
sscop_maa_error(sop, 'Q');
/*
* Free buffers
*/
KB_FREEALL(m);
/*
* Go into recovery mode
*/
q2110_error_recovery(sop);
return;
}
/*
* Are we at the high-water mark??
*/
if (ns == sop->so_rcvhigh) {
/*
* Yes, just bump the mark
*/
SEQ_INCR(sop->so_rcvhigh, 1);
return;
}
/*
* Are we beyond the high-water mark??
*/
if (SEQ_GT(ns, sop->so_rcvhigh, sop->so_rcvnext)) {
/*
* Yes, then there's a missing PDU, so inform the transmitter
*/
(void) sscop_send_ustat(sop, ns);
/*
* Update high-water mark
*/
sop->so_rcvhigh = SEQ_ADD(ns, 1);
}
return;
}
/*
* POLL PDU / SOS_READY Processor
*
* Arguments:
* sop pointer to sscop connection block
* m pointer to PDU buffer (without trailer)
* trlr pointer to PDU trailer
*
* Returns:
* none
*
*/
static void
sscop_poll_ready(sop, m, trlr)
struct sscop *sop;
KBuffer *m;
caddr_t trlr;
{
struct poll_pdu *pp = (struct poll_pdu *)trlr;
sscop_seq nps;
NTOHL(pp->poll_ns);
/*
* If the poll sequence number is less than highest number
* we've already seen, something's wrong
*/
if (SEQ_LT(pp->poll_ns, sop->so_rcvhigh, sop->so_rcvnext)) {
/*
* Record error condition
*/
sscop_maa_error(sop, 'Q');
/*
* Free buffers
*/
KB_FREEALL(m);
/*
* Go into recovery mode
*/
q2110_error_recovery(sop);
return;
}
/*
* Set a new "next highest" sequence number expected
*/
if (SEQ_LT(pp->poll_ns, sop->so_rcvmax, sop->so_rcvnext))
SEQ_SET(sop->so_rcvhigh, pp->poll_ns);
else
sop->so_rcvhigh = sop->so_rcvmax;
/*
* Return a STAT PDU to peer
*/
SEQ_SET(nps, ntohl(pp->poll_nps));
KB_FREEALL(m);
(void) sscop_send_stat(sop, nps);
return;
}