/* * * =================================== * 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. * * @(#) $FreeBSD: src/sys/netatm/uni/qsaal1_sigcpcs.c,v 1.4 2000/01/17 20:49:49 mks Exp $ * @(#) $DragonFly: src/sys/netproto/atm/uni/qsaal1_sigcpcs.c,v 1.4 2003/08/07 21:54:34 dillon Exp $ */ /* * ATM Forum UNI Support * --------------------- * * ITU-T Q.SAAL1 - Process CPCS-signals (SSCOP PDUs) * */ #include #include "sscop.h" #include "sscop_misc.h" #include "sscop_pdu.h" #include "sscop_var.h" /* * Local functions */ static void sscop_bgn_outconn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_end_outresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_end_conresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_end_ready __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_endak_outresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_rs_outresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_rs_ready __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_rsak_conresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_sd_inresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_sd_conresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_sd_process __P((struct sscop *, KBuffer *, caddr_t, int)); static void sscop_sd_ready __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_sdp_ready __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_poll_inresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_poll_conresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_poll_ready __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_stat_conresyn __P((struct sscop *, KBuffer *, caddr_t)); static void sscop_ustat_conresyn __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_noop, /* SOS_INCONN */ sscop_bgn_outdisc, /* SOS_OUTDISC */ sscop_bgn_outresyn, /* SOS_OUTRESYN */ sscop_bgn_inresyn, /* SOS_INRESYN */ sscop_bgn_outresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ sscop_bgn_inresyn, /* 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_bgak_error, /* SOS_OUTRESYN */ sscop_bgak_error, /* SOS_INRESYN */ sscop_bgak_error, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_noop, /* SOS_OUTDISC */ sscop_bgrej_outresyn, /* SOS_OUTRESYN */ sscop_bgrej_ready, /* SOS_INRESYN */ sscop_bgrej_outresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_outresyn, /* SOS_OUTRESYN */ sscop_end_ready, /* SOS_INRESYN */ sscop_end_conresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_outresyn, /* SOS_OUTRESYN */ sscop_endak_ready, /* SOS_INRESYN */ sscop_endak_outresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_noop, /* SOS_INRESYN */ sscop_noop, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_conresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ sscop_rsak_error, /* 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_sd_ready, /* SOS_OUTRESYN */ sscop_sd_inresyn, /* SOS_INRESYN */ sscop_sd_conresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ sscop_sd_ready, /* SOS_READY */ sscop_noop /* SOS_TERM */ }; /* SDP PDU */ static void (*sscop_sdp_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_sdp_ready, /* SOS_OUTRESYN */ sscop_sd_inresyn, /* SOS_INRESYN */ sscop_sd_conresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ sscop_sdp_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_poll_ready, /* SOS_OUTRESYN */ sscop_poll_inresyn, /* SOS_INRESYN */ sscop_poll_conresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_ready, /* SOS_INRESYN */ sscop_stat_conresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_ready, /* SOS_INRESYN */ sscop_ustat_conresyn, /* SOS_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ 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_CONRESYN */ NULL, /* invalid */ NULL, /* invalid */ sscop_md_all, /* SOS_READY */ sscop_noop /* SOS_TERM */ }; /* * PDU type lookup table */ void (*(*sscop_qsaal_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_sdp_tab, sscop_poll_tab, sscop_stat_tab, sscop_ustat_tab, sscop_ud_tab, sscop_md_tab, NULL }; /* * 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; /* * Initialize transmit window */ SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); /* * Notify user of connection establishment */ if (sop->so_flags & SOF_REESTAB) { KB_FREEALL(m); STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); if (err) { sscop_abort(sop, "sscop_bgn_outconn: stack memory\n"); return; } sop->so_flags &= ~SOF_REESTAB; } else { 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, "sscop_bgn_outconn: stack memory\n"); return; } } /* * Return an ACK to peer */ (void) sscop_send_bgak(sop); /* * Stop retransmit timer */ sop->so_timer[SSCOP_T_CC] = 0; /* * Reset receiver variables */ qsaal1_reset_rcvr(sop); /* * Start polling timer */ sscop_set_poll(sop); /* * Start lost poll/stat timer */ 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; } /* * END 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_end_outresyn(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, "sscop_end_outresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(sop); /* * Back to idle state */ sop->so_state = SOS_IDLE; return; } /* * END PDU / SOS_CONRESYN 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_conresyn(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { int err; /* * Stop retransmit timer */ sop->so_timer[SSCOP_T_CC] = 0; /* * Free up buffers */ KB_FREEALL(m); /* * Acknowledge END */ (void) sscop_send_endak(sop); /* * Notify user of connection termination */ 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, "sscop_end_conresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(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 poll timer */ sop->so_timer[SSCOP_T_POLL] = 0; sop->so_flags &= ~SOF_KEEPALIVE; /* * Stop lost poll/stat timer */ sop->so_timer[SSCOP_T_NORESP] = 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, "sscop_end_ready: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(sop); /* * Back to idle state */ sop->so_state = SOS_IDLE; return; } /* * ENDAK 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_endak_outresyn(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, "sscop_endak_outresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(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; { int err; /* * Notify user of 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, "sscop_rs_outresyn: stack memory\n"); return; } /* * Reset receiver state variables */ qsaal1_reset_rcvr(sop); /* * Wait for both peer and user responses */ sop->so_state = SOS_CONRESYN; 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; { int err; /* * Notify user of 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, "sscop_rs_ready: stack memory\n"); return; } /* * Reset receiver state variables */ qsaal1_reset_rcvr(sop); /* * Wait for user response */ sop->so_state = SOS_INRESYN; return; } /* * RSAK PDU / SOS_CONRESYN 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_rsak_conresyn(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { int err; /* * Stop retransmit timer */ sop->so_timer[SSCOP_T_CC] = 0; /* * Free buffers */ KB_FREEALL(m); /* * Notify user of resynchronization completion */ STACK_CALL(SSCOP_RESYNC_CNF, sop->so_upper, sop->so_toku, sop->so_connvc, 0, 0, err); if (err) { sscop_abort(sop, "sscop_rsak_conresyn: stack memory\n"); return; } /* * Start the polling timer */ sscop_set_poll(sop); /* * Start lost poll/stat timer */ sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; /* * Continue waiting for user response */ sop->so_state = SOS_INRESYN; /* * See if transmit queues need servicing */ if (sop->so_flags & SOF_XMITSRVC) sscop_service_xmit(sop); return; } /* * SD 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_sd_inresyn(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { int err; /* * Stop poll timer */ sop->so_timer[SSCOP_T_POLL] = 0; sop->so_flags &= ~SOF_KEEPALIVE; /* * Stop lost poll/stat timer */ sop->so_timer[SSCOP_T_NORESP] = 0; /* * Record error condition */ sscop_sd_error(sop, m, trlr); /* * Return an END to peer */ (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); /* * 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, "sscop_sd_inresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(sop); /* * Go back to idle state */ sop->so_state = SOS_IDLE; return; } /* * SD PDU / SOS_CONRESYN 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_conresyn(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { int err; /* * Stop retransmit timer */ sop->so_timer[SSCOP_T_CC] = 0; /* * Record error condition */ sscop_sd_error(sop, m, trlr); /* * Return an END to peer */ (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); /* * 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, "sscop_sd_conresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(sop); /* * Go back to idle state */ sop->so_state = SOS_IDLE; return; } /* * SD/SDP PDU Common Processor * * Arguments: * sop pointer to sscop connection block * m pointer to PDU user data buffer chain * trlr pointer to PDU trailer * type PDU type (SD or SDP) * * Returns: * none * */ static void sscop_sd_process(sop, m, trlr, type) struct sscop *sop; KBuffer *m; caddr_t trlr; int type; { struct sd_pdu *sp; struct sdp_pdu *spp; struct poll_pdu poll; struct pdu_hdr *php; KBuffer *n; sscop_seq ns, nps; int err, space; /* * Get PDU sequence number(s) */ if (type == PT_SD) { sp = (struct sd_pdu *)trlr; SEQ_SET(ns, ntohl(sp->sd_ns)); SEQ_SET(nps, 0); } else { spp = (struct sdp_pdu *)trlr; SEQ_SET(ns, ntohl(spp->sdp_ns)); SEQ_SET(nps, ntohl(spp->sdp_nps)); } /* * Ensure that the sequence number fits within the window */ if (SEQ_GEQ(ns, sop->so_rcvmax, sop->so_rcvnext)) { KB_FREEALL(m); 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; if (type == PT_SDP) goto dopoll; 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, "sscop_sd_process: 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...see if we need to poll */ if (type == PT_SDP) goto dopoll; 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); /* * Reestablish a new connection */ qsaal1_reestablish(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); if (type == PT_SDP) goto dopoll; 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 */ if (type == PT_SD) (void) sscop_send_ustat(sop, ns); /* * Update high-water mark */ sop->so_rcvhigh = SEQ_ADD(ns, 1); } if (type == PT_SD) return; dopoll: /* * Do the "poll" part of an SDP PDU */ poll.poll_nps = htonl(nps); poll.poll_ns = htonl((PT_POLL << PT_TYPE_SHIFT) | ns); sscop_poll_ready(sop, NULL, (caddr_t)&poll); 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; { /* * Just call common SD/SDP processor */ sscop_sd_process(sop, m, trlr, PT_SD); return; } /* * SDP 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_sdp_ready(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { /* * Just call common SD/SDP processor */ sscop_sd_process(sop, m, trlr, PT_SDP); return; } /* * POLL 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_poll_inresyn(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { int err; /* * Stop poll timer */ sop->so_timer[SSCOP_T_POLL] = 0; sop->so_flags &= ~SOF_KEEPALIVE; /* * Stop lost poll/stat timer */ sop->so_timer[SSCOP_T_NORESP] = 0; /* * Report protocol error */ sscop_poll_error(sop, m, trlr); /* * Return an END to peer */ (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); /* * 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, "sscop_poll_inresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(sop); /* * Back to idle state */ sop->so_state = SOS_IDLE; return; } /* * POLL PDU / SOS_CONRESYN 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_conresyn(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { int err; /* * Stop retransmit timer */ sop->so_timer[SSCOP_T_CC] = 0; /* * Record error condition */ sscop_poll_error(sop, m, trlr); /* * Return an END to peer */ (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); /* * 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, "sscop_poll_conresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(sop); /* * Go back to idle state */ sop->so_state = SOS_IDLE; 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 - so attempt to * reestablish a new connection. */ 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); /* * Reestablish a new connection */ qsaal1_reestablish(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; } /* * STAT PDU / SOS_CONRESYN 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_stat_conresyn(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { int err; /* * Stop retransmit timer */ sop->so_timer[SSCOP_T_CC] = 0; /* * Record error condition */ sscop_stat_error(sop, m, trlr); /* * Return an END to peer */ (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); /* * 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, "sscop_stat_conresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(sop); /* * Go back to idle state */ sop->so_state = SOS_IDLE; return; } /* * USTAT PDU / SOS_CONRESYN 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_ustat_conresyn(sop, m, trlr) struct sscop *sop; KBuffer *m; caddr_t trlr; { int err; /* * Stop retransmit timer */ sop->so_timer[SSCOP_T_CC] = 0; /* * Record error condition */ sscop_ustat_error(sop, m, trlr); /* * Return an END to peer */ (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); /* * 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, "sscop_ustat_conresyn: stack memory\n"); return; } /* * Clear connection data */ qsaal1_clear_connection(sop); /* * Go back to idle state */ sop->so_state = SOS_IDLE; return; }