diff options
Diffstat (limited to 'sys/netatm/uni/q2110_sigcpcs.c')
-rw-r--r-- | sys/netatm/uni/q2110_sigcpcs.c | 1760 |
1 files changed, 1760 insertions, 0 deletions
diff --git a/sys/netatm/uni/q2110_sigcpcs.c b/sys/netatm/uni/q2110_sigcpcs.c new file mode 100644 index 000000000000..0fa5555a7ce9 --- /dev/null +++ b/sys/netatm/uni/q2110_sigcpcs.c @@ -0,0 +1,1760 @@ +/* + * + * =================================== + * 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; +} + |