diff options
Diffstat (limited to 'sys/contrib/ngatm/netnatm/saal/saal_sscop.c')
-rw-r--r-- | sys/contrib/ngatm/netnatm/saal/saal_sscop.c | 4947 |
1 files changed, 4947 insertions, 0 deletions
diff --git a/sys/contrib/ngatm/netnatm/saal/saal_sscop.c b/sys/contrib/ngatm/netnatm/saal/saal_sscop.c new file mode 100644 index 0000000..75ce17d --- /dev/null +++ b/sys/contrib/ngatm/netnatm/saal/saal_sscop.c @@ -0,0 +1,4947 @@ +/* + * Copyright (c) 1996-2003 + * Fraunhofer Institute for Open Communication Systems (FhG Fokus). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Hartmut Brandt <harti@freebsd.org> + * + * $Begemot: libunimsg/netnatm/saal/saal_sscop.c,v 1.11 2004/07/08 08:22:13 brandt Exp $ + * + * Core SSCOP code (ITU-T Q.2110) + */ + +#include <netnatm/saal/sscop.h> +#include <netnatm/saal/sscoppriv.h> + +#ifndef FAILURE +#define FAILURE(S) +#endif + +#define MKSTR(S) #S + +static const char *const sscop_sigs[] = { + MKSTR(SSCOP_ESTABLISH_request), + MKSTR(SSCOP_ESTABLISH_indication), + MKSTR(SSCOP_ESTABLISH_response), + MKSTR(SSCOP_ESTABLISH_confirm), + MKSTR(SSCOP_RELEASE_request), + MKSTR(SSCOP_RELEASE_indication), + MKSTR(SSCOP_RELEASE_confirm), + MKSTR(SSCOP_DATA_request), + MKSTR(SSCOP_DATA_indication), + MKSTR(SSCOP_UDATA_request), + MKSTR(SSCOP_UDATA_indication), + MKSTR(SSCOP_RECOVER_indication), + MKSTR(SSCOP_RECOVER_response), + MKSTR(SSCOP_RESYNC_request), + MKSTR(SSCOP_RESYNC_indication), + MKSTR(SSCOP_RESYNC_response), + MKSTR(SSCOP_RESYNC_confirm), + MKSTR(SSCOP_RETRIEVE_request), + MKSTR(SSCOP_RETRIEVE_indication), + MKSTR(SSCOP_RETRIEVE_COMPL_indication), +}; + +static const char *const sscop_msigs[] = { + MKSTR(SSCOP_MDATA_request), + MKSTR(SSCOP_MDATA_indication), + MKSTR(SSCOP_MERROR_indication), +}; + +static const char *const states[] = { + MKSTR(SSCOP_IDLE), + MKSTR(SSCOP_OUT_PEND), + MKSTR(SSCOP_IN_PEND), + MKSTR(SSCOP_OUT_DIS_PEND), + MKSTR(SSCOP_OUT_RESYNC_PEND), + MKSTR(SSCOP_IN_RESYNC_PEND), + MKSTR(SSCOP_OUT_REC_PEND), + MKSTR(SSCOP_REC_PEND), + MKSTR(SSCOP_IN_REC_PEND), + MKSTR(SSCOP_READY), +}; + +#ifdef SSCOP_DEBUG +static const char *const events[] = { + MKSTR(SIG_BGN), + MKSTR(SIG_BGAK), + MKSTR(SIG_END), + MKSTR(SIG_ENDAK), + MKSTR(SIG_RS), + MKSTR(SIG_RSAK), + MKSTR(SIG_BGREJ), + MKSTR(SIG_SD), + MKSTR(SIG_ER), + MKSTR(SIG_POLL), + MKSTR(SIG_STAT), + MKSTR(SIG_USTAT), + MKSTR(SIG_UD), + MKSTR(SIG_MD), + MKSTR(SIG_ERAK), + + MKSTR(SIG_T_CC), + MKSTR(SIG_T_POLL), + MKSTR(SIG_T_KA), + MKSTR(SIG_T_NR), + MKSTR(SIG_T_IDLE), + + MKSTR(SIG_PDU_Q), + MKSTR(SIG_USER_DATA), + MKSTR(SIG_ESTAB_REQ), + MKSTR(SIG_ESTAB_RESP), + MKSTR(SIG_RELEASE_REQ), + MKSTR(SIG_RECOVER), + MKSTR(SIG_SYNC_REQ), + MKSTR(SIG_SYNC_RESP), + MKSTR(SIG_UDATA), + MKSTR(SIG_MDATA), + MKSTR(SIG_UPDU_Q), + MKSTR(SIG_MPDU_Q), + MKSTR(SIG_RETRIEVE), +}; + +static const char *const pdus[] = { + "illegale PDU type 0", /* no PDU type 0 */ + MKSTR(PDU_BGN), + MKSTR(PDU_BGAK), + MKSTR(PDU_END), + MKSTR(PDU_ENDAK), + MKSTR(PDU_RS), + MKSTR(PDU_RSAK), + MKSTR(PDU_BGREJ), + MKSTR(PDU_SD), + MKSTR(PDU_ER), + MKSTR(PDU_POLL), + MKSTR(PDU_STAT), + MKSTR(PDU_USTAT), + MKSTR(PDU_UD), + MKSTR(PDU_MD), + MKSTR(PDU_ERAK), +}; +#endif + +MEMINIT(); + +static void sscop_signal(struct sscop *, u_int, struct sscop_msg *); +static void sscop_save_signal(struct sscop *, u_int, struct sscop_msg *); +static void handle_sigs(struct sscop *); +static void sscop_set_state(struct sscop *, u_int); + +/************************************************************/ + + +/************************************************************/ +/* + * Queue macros + */ +#define SSCOP_MSG_FREE(MSG) \ + do { \ + if(MSG) { \ + MBUF_FREE((MSG)->m); \ + MSG_FREE((MSG)); \ + } \ + } while(0) + + +#define QFIND(Q,RN) \ + ({ \ + struct sscop_msg *_msg = NULL, *_m; \ + MSGQ_FOREACH(_m, (Q)) { \ + if(_m->seqno == (RN)) { \ + _msg = _m; \ + break; \ + } \ + } \ + _msg; \ + }) + +#define QINSERT(Q,M) \ + do { \ + struct sscop_msg *_msg = NULL, *_m; \ + MSGQ_FOREACH(_m, (Q)) { \ + if (_m->seqno > (M)->seqno) { \ + _msg = _m; \ + break; \ + } \ + } \ + if (_msg != NULL) \ + MSGQ_INSERT_BEFORE(_msg, (M)); \ + else \ + MSGQ_APPEND((Q), (M)); \ + } while (0) + + +/* + * Send an error indication to the management plane. + */ +#define MAAL_ERROR(S,E,C) \ + do { \ + VERBOSE(S, SSCOP_DBG_USIG, ((S), (S)->aarg, \ + "MAA-Signal %s in state %s", \ + sscop_msigs[SSCOP_MERROR_indication], states[(S)->state])); \ + (S)->funcs->send_manage((S), (S)->aarg, \ + SSCOP_MERROR_indication, NULL, (E), (C)); \ + } while(0) + +#define MAAL_DATA(S,M) \ + do { \ + VERBOSE(S, SSCOP_DBG_USIG, ((S), (S)->aarg, \ + "MAA-Signal %s in state %s", \ + sscop_msigs[SSCOP_MDATA_indication], states[(S)->state])); \ + (S)->funcs->send_manage((S), (S)->aarg, \ + SSCOP_MDATA_indication, (M), 0, 0); \ + } while(0) + +#define AAL_DATA(S,D,M,N) \ + do { \ + VERBOSE(S, SSCOP_DBG_USIG, ((S), (S)->aarg, \ + "AA-Signal %s in state %s", \ + sscop_sigs[D], states[(S)->state])); \ + (S)->funcs->send_upper((S), (S)->aarg, (D), (M), (N)); \ + } while(0) + +#define AAL_SIG(S,D) \ + do { \ + VERBOSE(S, SSCOP_DBG_USIG, ((S), (S)->aarg, \ + "AA-Signal %s in state %s", \ + sscop_sigs[D], states[(S)->state])); \ + (S)->funcs->send_upper((S), (S)->aarg, (D), NULL, 0); \ + } while(0) + +#ifdef SSCOP_DEBUG +#define AAL_SEND(S,M) do { \ + if (ISVERBOSE(S, SSCOP_DBG_PDU)) \ + sscop_dump_pdu(S, "tx", (M)); \ + (S)->funcs->send_lower((S), (S)->aarg, (M)); \ + } while(0) +#else +#define AAL_SEND(S,M) (S)->funcs->send_lower((S), (S)->aarg, (M)) +#endif + + +/* + * Free a save user-to-user data buffer and set the pointer to zero + * to signal, that it is free. + */ +#define FREE_UU(F) \ + do { \ + if(sscop->F) { \ + MBUF_FREE(sscop->F); \ + sscop->F = NULL; \ + } \ + } while(0) + +#define SET_UU(F,U) \ + do { \ + FREE_UU(F); \ + sscop->F = U->m; \ + U->m = NULL; \ + SSCOP_MSG_FREE(U); \ + } while(0) + +#define AAL_UU_SIGNAL(S, SIG, M, PL, SN) \ + do { \ + if(MBUF_LEN((M)->m) > 0) { \ + MBUF_UNPAD((M)->m,(PL)); \ + AAL_DATA((S), (SIG), (M)->m, (SN)); \ + (M)->m = NULL; \ + } else { \ + AAL_DATA((S), (SIG), NULL, (SN)); \ + } \ + SSCOP_MSG_FREE((M)); \ + } while(0) + + + +TIMER_FUNC(cc, CC) +TIMER_FUNC(nr, NR) +TIMER_FUNC(ka, KA) +TIMER_FUNC(poll, POLL) +TIMER_FUNC(idle, IDLE) + +/************************************************************/ +/* + * INSTANCE AND TYPE HANDLING. + */ +#ifdef SSCOP_DEBUG +static void +sscop_dump_pdu(struct sscop *sscop, const char *dir, + const struct SSCOP_MBUF_T *m) +{ + u_int32_t v1, v2, v3, v4; + u_int size = MBUF_LEN(m); + u_int n, i; + + if (size < 8) + return; + + v1 = MBUF_TRAIL32(m, -1); + v2 = MBUF_TRAIL32(m, -2); + + switch ((v1 >> 24) & 0xf) { + + case 0: + return; + + case PDU_BGN: + sscop->funcs->verbose(sscop, sscop->aarg, + "%s BGN n(mr)=%u n(sq)=%u pl=%u", + dir, v1 & 0xffffff, v2 & 0xff, (v1 >> 30) & 0x3); + return; + + case PDU_BGAK: + sscop->funcs->verbose(sscop, sscop->aarg, + "%s BGAK n(mr)=%u pl=%u", + dir, v1 & 0xffffff, (v1 >> 30) & 0x3); + return; + + case PDU_END: + sscop->funcs->verbose(sscop, sscop->aarg, + "%s END r=%u s=%u pl=%u", + dir, (v1 >> 29) & 1, (v1 >> 28) & 1, (v1 >> 30) & 0x3); + return; + + case PDU_ENDAK: + sscop->funcs->verbose(sscop, sscop->aarg, "%s ENDAK", dir); + return; + + case PDU_RS: + sscop->funcs->verbose(sscop, sscop->aarg, + "%s RS n(mr)=%u n(sq)=%u pl=%u", + dir, v1 & 0xffffff, v2 & 0xff, (v1 >> 30) & 0x3); + return; + + case PDU_RSAK: + sscop->funcs->verbose(sscop, sscop->aarg, "%s RSAK n(mr)=%u", + dir, v1 & 0xffffff); + return; + + case PDU_BGREJ: + sscop->funcs->verbose(sscop, sscop->aarg, "%s BGREJ pl=%u", + dir, (v1 >> 30) & 0x3); + return; + + case PDU_SD: + sscop->funcs->verbose(sscop, sscop->aarg, "%s SD n(s)=%u pl=%u", + dir, v1 & 0xffffff, (v1 >> 30) & 0x3); + return; + + case PDU_ER: + sscop->funcs->verbose(sscop, sscop->aarg, "%s ER n(mr)=%u n(sq)=%u", + dir, v1 & 0xffffff, v2 & 0xff); + return; + + case PDU_POLL: + sscop->funcs->verbose(sscop, sscop->aarg, "%s POLL n(s)=%u n(ps)=%u", + dir, v1 & 0xffffff, v2 & 0xffffff); + return; + + case PDU_STAT: + if (size < 12) + return; + v3 = MBUF_TRAIL32(m, -3); + sscop->funcs->verbose(sscop, sscop->aarg, + "%s STAT n(r)=%u n(mr)=%u n(ps)=%u", + dir, v1 & 0xffffff, v2 & 0xffffff, v3 & 0xffffff); + n = (size - 12) / 4; + for (i = 0; i < (size - 12) / 4; i++, n--) { + v4 = MBUF_TRAIL32(m, -4 - (int)i); + sscop->funcs->verbose(sscop, sscop->aarg, + " LE(%u)=%u", n, v4 & 0xffffff); + } + return; + + case PDU_USTAT: + if (size < 16) + return; + sscop->funcs->verbose(sscop, sscop->aarg, + "%s STAT n(r)=%u n(mr)=%u LE1=%u LE2=%u", + dir, v1 & 0xffffff, v2 & 0xffffff, + MBUF_TRAIL32(m, -4) & 0xffffff, + MBUF_TRAIL32(m, -3) & 0xffffff); + return; + + case PDU_UD: + sscop->funcs->verbose(sscop, sscop->aarg, + "%s UD pl=%u", dir, (v1 >> 30) & 0x3); + return; + + case PDU_MD: + sscop->funcs->verbose(sscop, sscop->aarg, + "%s MD pl=%u", dir, (v1 >> 30) & 0x3); + return; + + case PDU_ERAK: + sscop->funcs->verbose(sscop, sscop->aarg, + "%s ERAK n(mr)=%u", dir, v1 & 0xffffff); + return; + } +} +#endif + + +/* + * Initialize state of variables + */ +static void +sscop_init(struct sscop *sscop) +{ + sscop->state = SSCOP_IDLE; + + sscop->vt_sq = 0; + sscop->vr_sq = 0; + sscop->clear_buffers = 1; + + sscop->ll_busy = 0; + + sscop->rxq = 0; +} + +static void +sscop_clear(struct sscop *sscop) +{ + TIMER_STOP(sscop, cc); + TIMER_STOP(sscop, ka); + TIMER_STOP(sscop, nr); + TIMER_STOP(sscop, idle); + TIMER_STOP(sscop, poll); + + FREE_UU(uu_bgn); + FREE_UU(uu_bgak); + FREE_UU(uu_bgrej); + FREE_UU(uu_end); + FREE_UU(uu_rs); + + MSGQ_CLEAR(&sscop->xq); + MSGQ_CLEAR(&sscop->uxq); + MSGQ_CLEAR(&sscop->mxq); + MSGQ_CLEAR(&sscop->xbuf); + MSGQ_CLEAR(&sscop->rbuf); + + SIGQ_CLEAR(&sscop->sigs); + SIGQ_CLEAR(&sscop->saved_sigs); +} + + +/* + * Allocate instance memory, initialize the state of all variables. + */ +struct sscop * +sscop_create(void *a, const struct sscop_funcs *funcs) +{ + struct sscop *sscop; + + MEMZALLOC(sscop, struct sscop *, sizeof(struct sscop)); + if (sscop == NULL) + return (NULL); + + if (a == NULL) + sscop->aarg = sscop; + else + sscop->aarg = a; + sscop->funcs = funcs; + + sscop->maxk = MAXK; + sscop->maxj = MAXJ; + sscop->maxcc = MAXCC; + sscop->maxpd = MAXPD; + sscop->maxstat = MAXSTAT; + sscop->timercc = TIMERCC; + sscop->timerka = TIMERKA; + sscop->timernr = TIMERNR; + sscop->timerpoll = TIMERPOLL; + sscop->timeridle = TIMERIDLE; + sscop->robustness = 0; + sscop->poll_after_rex = 0; + sscop->mr = MAXMR; + + TIMER_INIT(sscop, cc); + TIMER_INIT(sscop, nr); + TIMER_INIT(sscop, ka); + TIMER_INIT(sscop, poll); + TIMER_INIT(sscop, idle); + + MSGQ_INIT(&sscop->xq); + MSGQ_INIT(&sscop->uxq); + MSGQ_INIT(&sscop->mxq); + MSGQ_INIT(&sscop->rbuf); + MSGQ_INIT(&sscop->xbuf); + + SIGQ_INIT(&sscop->sigs); + SIGQ_INIT(&sscop->saved_sigs); + + sscop_init(sscop); + + return (sscop); +} + +/* + * Free all resources in a sscop instance + */ +void +sscop_destroy(struct sscop *sscop) +{ + sscop_reset(sscop); + + MEMFREE(sscop); +} + +/* + * Reset the SSCOP instance. + */ +void +sscop_reset(struct sscop *sscop) +{ + sscop_clear(sscop); + sscop_init(sscop); +} + +void +sscop_getparam(const struct sscop *sscop, struct sscop_param *p) +{ + p->timer_cc = sscop->timercc; + p->timer_poll = sscop->timerpoll; + p->timer_keep_alive = sscop->timerka; + p->timer_no_response = sscop->timernr; + p->timer_idle = sscop->timeridle; + p->maxk = sscop->maxk; + p->maxj = sscop->maxj; + p->maxcc = sscop->maxcc; + p->maxpd = sscop->maxpd; + p->maxstat = sscop->maxstat; + p->mr = sscop->mr; + p->flags = 0; + if(sscop->robustness) + p->flags |= SSCOP_ROBUST; + if(sscop->poll_after_rex) + p->flags |= SSCOP_POLLREX; +} + +int +sscop_setparam(struct sscop *sscop, struct sscop_param *p, u_int *pmask) +{ + u_int mask = *pmask; + + /* can change only in idle state */ + if (sscop->state != SSCOP_IDLE) + return (EISCONN); + + *pmask = 0; + + /* + * first check all parameters + */ + if ((mask & SSCOP_SET_TCC) && p->timer_cc == 0) + *pmask |= SSCOP_SET_TCC; + if ((mask & SSCOP_SET_TPOLL) && p->timer_poll == 0) + *pmask |= SSCOP_SET_TPOLL; + if ((mask & SSCOP_SET_TKA) && p->timer_keep_alive == 0) + *pmask |= SSCOP_SET_TKA; + if ((mask & SSCOP_SET_TNR) && p->timer_no_response == 0) + *pmask |= SSCOP_SET_TNR; + if ((mask & SSCOP_SET_TIDLE) && p->timer_idle == 0) + *pmask |= SSCOP_SET_TIDLE; + if ((mask & SSCOP_SET_MAXK) && p->maxk > MAXMAXK) + *pmask |= SSCOP_SET_MAXK; + if ((mask & SSCOP_SET_MAXJ) && p->maxj > MAXMAXJ) + *pmask |= SSCOP_SET_MAXJ; + if ((mask & SSCOP_SET_MAXCC) && p->maxcc > 255) + *pmask |= SSCOP_SET_MAXCC; + if ((mask & SSCOP_SET_MAXPD) && p->maxpd >= (1 << 24)) + *pmask |= SSCOP_SET_MAXPD; + if ((mask & SSCOP_SET_MAXSTAT) && + ((p->maxstat & 1) == 0 || p->maxstat == 1 || p->maxstat == 2 || + p->maxstat * 4 > MAXMAXK - 8)) + *pmask |= SSCOP_SET_MAXSTAT; + if ((mask & SSCOP_SET_MR) && p->mr >= (1 << 24) - 1) + *pmask |= SSCOP_SET_MR; + + if (*pmask) + return (EINVAL); + + + /* + * now set it + */ + if (mask & SSCOP_SET_TCC) + sscop->timercc = p->timer_cc; + + if (mask & SSCOP_SET_TPOLL) + sscop->timerpoll = p->timer_poll; + + if (mask & SSCOP_SET_TKA) + sscop->timerka = p->timer_keep_alive; + + if (mask & SSCOP_SET_TNR) + sscop->timernr = p->timer_no_response; + + if (mask & SSCOP_SET_TIDLE) + sscop->timeridle = p->timer_idle; + + if (mask & SSCOP_SET_MAXK) + sscop->maxk = p->maxk; + if (mask & SSCOP_SET_MAXJ) + sscop->maxj = p->maxj; + + if (mask & SSCOP_SET_MAXCC) + sscop->maxcc = p->maxcc; + if (mask & SSCOP_SET_MAXPD) + sscop->maxpd = p->maxpd; + if (mask & SSCOP_SET_MAXSTAT) + sscop->maxstat = p->maxstat; + + if (mask & SSCOP_SET_MR) + sscop->mr = p->mr; + + if (mask & SSCOP_SET_ROBUST) + sscop->robustness = ((p->flags & SSCOP_ROBUST) != 0); + + if (mask & SSCOP_SET_POLLREX) + sscop->poll_after_rex = ((p->flags & SSCOP_POLLREX) != 0); + + return (0); +} + +enum sscop_state +sscop_getstate(const struct sscop *sscop) +{ + return (sscop->state); +} + + +/************************************************************/ +/* + * EXTERNAL INPUT SIGNAL MAPPING + */ + +/* + * Map AA signal to SSCOP internal signal + */ +int +sscop_aasig(struct sscop *sscop, enum sscop_aasig sig, + struct SSCOP_MBUF_T *m, u_int arg) +{ + struct sscop_msg *msg; + + if (sig >= sizeof(sscop_sigs)/sizeof(sscop_sigs[0])) { + VERBOSE(sscop, SSCOP_DBG_INSIG, (sscop, sscop->aarg, + "AA-Signal %u - bad signal", sig)); + MBUF_FREE(m); + return (EINVAL); + } + VERBOSE(sscop, SSCOP_DBG_INSIG, (sscop, sscop->aarg, + "AA-Signal %s in state %s with%s message", + sscop_sigs[sig], states[sscop->state], m ? "" : "out")); + + MSG_ALLOC(msg); + if (msg == NULL) { + FAILURE("sscop: cannot allocate aasig"); + MBUF_FREE(m); + return (ENOMEM); + } + + switch(sig) { + + case SSCOP_ESTABLISH_request: + msg->m = m; + msg->rexmit = arg; + sscop_signal(sscop, SIG_ESTAB_REQ, msg); + break; + + case SSCOP_ESTABLISH_response: + msg->m = m; + msg->rexmit = arg; + sscop_signal(sscop, SIG_ESTAB_RESP, msg); + break; + + case SSCOP_RELEASE_request: + msg->m = m; + sscop_signal(sscop, SIG_RELEASE_REQ, msg); + break; + + case SSCOP_DATA_request: + msg->m = m; + sscop_signal(sscop, SIG_USER_DATA, msg); + break; + + case SSCOP_UDATA_request: + msg->m = m; + sscop_signal(sscop, SIG_UDATA, msg); + break; + + case SSCOP_RECOVER_response: + MBUF_FREE(m); + MSG_FREE(msg); + sscop_signal(sscop, SIG_RECOVER, NULL); + break; + + case SSCOP_RESYNC_request: + msg->m = m; + sscop_signal(sscop, SIG_SYNC_REQ, msg); + break; + + case SSCOP_RESYNC_response: + MBUF_FREE(m); + MSG_FREE(msg); + sscop_signal(sscop, SIG_SYNC_RESP, NULL); + break; + + case SSCOP_RETRIEVE_request: + MBUF_FREE(m); + msg->rexmit = arg; + sscop_signal(sscop, SIG_RETRIEVE, msg); + break; + + case SSCOP_ESTABLISH_indication: + case SSCOP_ESTABLISH_confirm: + case SSCOP_RELEASE_indication: + case SSCOP_RELEASE_confirm: + case SSCOP_DATA_indication: + case SSCOP_UDATA_indication: + case SSCOP_RECOVER_indication: + case SSCOP_RESYNC_indication: + case SSCOP_RESYNC_confirm: + case SSCOP_RETRIEVE_indication: + case SSCOP_RETRIEVE_COMPL_indication: + MBUF_FREE(m); + MSG_FREE(msg); + return EINVAL; + } + + return 0; +} + +/* + * Signal from layer management. + */ +int +sscop_maasig(struct sscop *sscop, enum sscop_maasig sig, struct SSCOP_MBUF_T *m) +{ + struct sscop_msg *msg; + + if (sig >= sizeof(sscop_msigs)/sizeof(sscop_msigs[0])) { + VERBOSE(sscop, SSCOP_DBG_INSIG, (sscop, sscop->aarg, + "MAA-Signal %u - bad signal", sig)); + MBUF_FREE(m); + return (EINVAL); + } + VERBOSE(sscop, SSCOP_DBG_INSIG, (sscop, sscop->aarg, + "MAA-Signal %s in state %s with%s message", + sscop_msigs[sig], states[sscop->state], m ? "" : "out")); + + MSG_ALLOC(msg); + if (msg == NULL) { + FAILURE("sscop: cannot allocate maasig"); + MBUF_FREE(m); + return (ENOMEM); + } + + switch (sig) { + + case SSCOP_MDATA_request: + msg->m = m; + sscop_signal(sscop, SIG_MDATA, msg); + break; + + case SSCOP_MDATA_indication: + case SSCOP_MERROR_indication: + MBUF_FREE(m); + MSG_FREE(msg); + return (EINVAL); + } + return (0); +} + +/* + * Map PDU to SSCOP signal. + */ +void +sscop_input(struct sscop *sscop, struct SSCOP_MBUF_T *m) +{ + struct sscop_msg *msg; + union pdu pdu; + u_int size; + + MSG_ALLOC(msg); + if(msg == NULL) { + FAILURE("sscop: cannot allocate in pdu msg"); + MBUF_FREE(m); + return; + } + + msg->m = m; + msg->rexmit = 0; + + size = MBUF_LEN(m); + + if(size % 4 != 0 || size < 4) + goto err; + + pdu.sscop_null = MBUF_TRAIL32(m, -1); + + VERBOSE(sscop, SSCOP_DBG_PDU, (sscop, sscop->aarg, + "got %s, size=%u", pdus[pdu.sscop_type], size)); + +#ifdef SSCOP_DEBUG +#define ENSURE(C,F) if(!(C)) { VERBOSE(sscop, SSCOP_DBG_PDU, F); goto err; } +#else +#define ENSURE(C,F) if(!(C)) goto err +#endif + +#ifdef SSCOP_DEBUG + if (ISVERBOSE(sscop, SSCOP_DBG_PDU)) + sscop_dump_pdu(sscop, "rx", m); +#endif + + switch(pdu.sscop_type) { + + default: + ENSURE(0, (sscop, sscop->aarg, + "Bad PDU type %u", pdu.sscop_type)); + break; + + case PDU_BGN: + ENSURE(size >= 8U, (sscop, sscop->aarg, + "PDU_BGN size=%u", size)); + ENSURE(size >= 8U + pdu.sscop_pl, (sscop, sscop->aarg, + "PDU_BGN size=%u pl=%u", size, pdu.sscop_pl)); + ENSURE(size <= 8U + sscop->maxj, (sscop, sscop->aarg, + "PDU_BGN size=%u", size)); + sscop_signal(sscop, SIG_BGN, msg); + break; + + case PDU_BGAK: + ENSURE(size >= 8U, (sscop, sscop->aarg, + "PDU_BGAK size=%u", size)); + ENSURE(size >= 8U + pdu.sscop_pl, (sscop, sscop->aarg, + "PDU_BGAK size=%u pl=%u", size, pdu.sscop_pl)); + ENSURE(size <= 8U + sscop->maxj, (sscop, sscop->aarg, + "PDU_BGAK size=%u", size)); + sscop_signal(sscop, SIG_BGAK, msg); + break; + + case PDU_END: + ENSURE(size >= 8U, (sscop, sscop->aarg, + "PDU_END size=%u", size)); + ENSURE(size >= 8U + pdu.sscop_pl, (sscop, sscop->aarg, + "PDU_END size=%u pl=%u", size, pdu.sscop_pl)); + ENSURE(size <= 8U + sscop->maxj, (sscop, sscop->aarg, + "PDU_END size=%u", size)); + sscop_signal(sscop, SIG_END, msg); + break; + + case PDU_ENDAK: + ENSURE(size == 8U, (sscop, sscop->aarg, + "PDU_ENDAK size=%u", size)); + sscop_signal(sscop, SIG_ENDAK, msg); + break; + + case PDU_BGREJ: + ENSURE(size >= 8U, (sscop, sscop->aarg, + "PDU_BGREJ size=%u", size)); + ENSURE(size >= 8U + pdu.sscop_pl, (sscop, sscop->aarg, + "PDU_BGREJ size=%u pl=%u", size, pdu.sscop_pl)); + ENSURE(size <= 8U + sscop->maxj, (sscop, sscop->aarg, + "PDU_BGREJ size=%u", size)); + sscop_signal(sscop, SIG_BGREJ, msg); + break; + + case PDU_SD: + ENSURE(size >= 4U + pdu.sscop_pl, (sscop, sscop->aarg, + "PDU_SD size=%u pl=%u", size, pdu.sscop_pl)); + ENSURE(size <= 4U + sscop->maxk, (sscop, sscop->aarg, + "PDU_SD size=%u", size)); + sscop_signal(sscop, SIG_SD, msg); + break; + + case PDU_UD: + ENSURE(size >= 4U + pdu.sscop_pl, (sscop, sscop->aarg, + "PDU_UD size=%u pl=%u", size, pdu.sscop_pl)); + ENSURE(size <= 4U + sscop->maxk, (sscop, sscop->aarg, + "PDU_UD size=%u", size)); + sscop_signal(sscop, SIG_UD, msg); + break; + + case PDU_MD: + ENSURE(size >= 4U + pdu.sscop_pl, (sscop, sscop->aarg, + "PDU_MD size=%u pl=%u", size, pdu.sscop_pl)); + ENSURE(size <= 4U + sscop->maxk, (sscop, sscop->aarg, + "PDU_MD size=%u", size)); + sscop_signal(sscop, SIG_MD, msg); + break; + + case PDU_POLL: + ENSURE(size == 8U, (sscop, sscop->aarg, + "PDU_POLL size=%u", size)); + sscop_signal(sscop, SIG_POLL, msg); + break; + + case PDU_STAT: + ENSURE(size >= 12U, (sscop, sscop->aarg, + "PDU_STAT size=%u", size)); + ENSURE(size <= 12U + 4 * sscop->maxstat, (sscop, sscop->aarg, + "PDU_STAT size=%u", size)); + sscop_signal(sscop, SIG_STAT, msg); + break; + + case PDU_RS: + ENSURE(size >= 8U, (sscop, sscop->aarg, + "PDU_RS size=%u", size)); + ENSURE(size >= 8U + pdu.sscop_pl, (sscop, sscop->aarg, + "PDU_RS size=%u pl=%u", size, pdu.sscop_pl)); + ENSURE(size <= 8U + sscop->maxj, (sscop, sscop->aarg, + "PDU_RS size=%u", size)); + sscop_signal(sscop, SIG_RS, msg); + break; + + case PDU_RSAK: + ENSURE(size == 8U, (sscop, sscop->aarg, + "PDU_RSAK size=%u", size)); + sscop_signal(sscop, SIG_RSAK, msg); + break; + + case PDU_ER: + ENSURE(size == 8U, (sscop, sscop->aarg, + "PDU_ER size=%u", size)); + sscop_signal(sscop, SIG_ER, msg); + break; + + case PDU_ERAK: + ENSURE(size == 8U, (sscop, sscop->aarg, + "PDU_ERAK size=%u", size)); + sscop_signal(sscop, SIG_ERAK, msg); + break; + + case PDU_USTAT: + ENSURE(size == 16U, (sscop, sscop->aarg, + "PDU_ERAK size=%u", size)); + sscop_signal(sscop, SIG_USTAT, msg); + break; + } +#undef ENSURE + return; + + err: + MAAL_ERROR(sscop, 'U', 0); + SSCOP_MSG_FREE(msg); +} + +/************************************************************/ +/* + * UTILITIES + */ + +/* + * Move the receiver window by N packets + */ +u_int +sscop_window(struct sscop *sscop, u_int n) +{ + sscop->vr_mr += n; + return (SEQNO_DIFF(sscop->vr_mr, sscop->vr_r)); +} + +/* + * Lower layer busy handling + */ +u_int +sscop_setbusy(struct sscop *sscop, int busy) +{ + u_int old = sscop->ll_busy; + + if (busy > 0) + sscop->ll_busy = 1; + else if (busy == 0) { + sscop->ll_busy = 0; + if(old) + handle_sigs(sscop); + } + + return (old); +} + +const char * +sscop_signame(enum sscop_aasig sig) +{ + static char str[40]; + + if (sig >= sizeof(sscop_sigs)/sizeof(sscop_sigs[0])) { + sprintf(str, "BAD SSCOP_AASIG %u", sig); + return (str); + } else { + return (sscop_sigs[sig]); + } +} + +const char * +sscop_msigname(enum sscop_maasig sig) +{ + static char str[40]; + + if (sig >= sizeof(sscop_msigs)/sizeof(sscop_msigs[0])) { + sprintf(str, "BAD SSCOP_MAASIG %u", sig); + return (str); + } else { + return (sscop_msigs[sig]); + } +} + +const char * +sscop_statename(enum sscop_state s) +{ + static char str[40]; + + if (s >= sizeof(states)/sizeof(states[0])) { + sprintf(str, "BAD SSCOP_STATE %u", s); + return (str); + } else { + return (states[s]); + } +} + + +/************************************************************/ +/* + * MACROS + */ + +/* + * p 75: release buffers + */ +static void +m_release_buffers(struct sscop *sscop) +{ + MSGQ_CLEAR(&sscop->xq); + MSGQ_CLEAR(&sscop->xbuf); + sscop->rxq = 0; + MSGQ_CLEAR(&sscop->rbuf); +} + +/* + * P 75: Prepare retrival + */ +static void +m_prepare_retrieval(struct sscop *sscop) +{ + struct sscop_msg *msg; + + if (sscop->clear_buffers) { + MSGQ_CLEAR(&sscop->xq); + MSGQ_CLEAR(&sscop->xbuf); + } + MSGQ_FOREACH(msg, &sscop->xbuf) + msg->rexmit = 0; + sscop->rxq = 0; + + MSGQ_CLEAR(&sscop->rbuf); +} + +/* + * P 75: Prepare retrival + */ +static void +m_prepare_recovery(struct sscop *sscop) +{ + struct sscop_msg *msg; + + if(sscop->clear_buffers) { + MSGQ_CLEAR(&sscop->xq); + MSGQ_CLEAR(&sscop->xbuf); + } + MSGQ_FOREACH(msg, &sscop->xbuf) + msg->rexmit = 0; + sscop->rxq = 0; +} + + +/* + * P 75: Clear transmitter + */ +static void +m_clear_transmitter(struct sscop *sscop) +{ + if(!sscop->clear_buffers) { + MSGQ_CLEAR(&sscop->xq); + MSGQ_CLEAR(&sscop->xbuf); + } +} + + +/* + * p 75: Deliver data + * Freeing the message is the responibility of the handler function. + */ +static void +m_deliver_data(struct sscop *sscop) +{ + struct sscop_msg *msg; + u_int sn; + + if ((msg = MSGQ_GET(&sscop->rbuf)) == NULL) + return; + + if (sscop->clear_buffers) { + MSGQ_CLEAR(&sscop->rbuf); + return; + } + + sn = msg->seqno + 1; + AAL_DATA(sscop, SSCOP_DATA_indication, msg->m, msg->seqno); + MSG_FREE(msg); + + while ((msg = MSGQ_GET(&sscop->rbuf)) != NULL) { + ASSERT(msg->seqno == sn); + if (++sn == SSCOP_MAXSEQNO) + sn = 0; + AAL_DATA(sscop, SSCOP_DATA_indication, msg->m, msg->seqno); + MSG_FREE(msg); + } +} + +/* + * P 75: Initialize state variables + */ +static void +m_initialize_state(struct sscop *sscop) +{ + sscop->vt_s = 0; + sscop->vt_ps = 0; + sscop->vt_a = 0; + + sscop->vt_pa = 1; + sscop->vt_pd = 0; + sscop->credit = 1; + + sscop->vr_r = 0; + sscop->vr_h = 0; +} + +/* + * p 76: Data retrieval + */ +static void +m_data_retrieval(struct sscop *sscop, u_int rn) +{ + struct sscop_msg *s; + + if (rn != SSCOP_RETRIEVE_UNKNOWN) { + if(rn >= SSCOP_RETRIEVE_TOTAL) + rn = sscop->vt_a; + else + rn++; + while(rn >= sscop->vt_a && rn < sscop->vt_s) { + if(rn == SSCOP_MAXSEQNO) rn = 0; + if((s = QFIND(&sscop->xbuf, rn)) != NULL) { + MSGQ_REMOVE(&sscop->xbuf, s); + AAL_DATA(sscop, SSCOP_RETRIEVE_indication, + s->m, 0); + MSG_FREE(s); + } + rn++; + } + } + + while((s = MSGQ_GET(&sscop->xq)) != NULL) { + AAL_DATA(sscop, SSCOP_RETRIEVE_indication, s->m, 0); + MSG_FREE(s); + } + AAL_SIG(sscop, SSCOP_RETRIEVE_COMPL_indication); +} + +/* + * P 76: Detect retransmission. PDU type must already be stripped. + */ +static int +m_detect_retransmission(struct sscop *sscop, struct sscop_msg *msg) +{ + union bgn bgn; + + bgn.sscop_null = MBUF_TRAIL32(msg->m, -1); + + if (sscop->vr_sq == bgn.sscop_bgns) + return (1); + + sscop->vr_sq = bgn.sscop_bgns; + return (0); +} + +/* + * P 76: Set POLL timer + */ +static void +m_set_poll_timer(struct sscop *sscop) +{ + if(MSGQ_EMPTY(&sscop->xq) && sscop->vt_s == sscop->vt_a) + TIMER_RESTART(sscop, ka); + else + TIMER_RESTART(sscop, poll); +} + +/* + * P 77: Reset data transfer timers + */ +static void +m_reset_data_xfer_timers(struct sscop *sscop) +{ + TIMER_STOP(sscop, ka); + TIMER_STOP(sscop, nr); + TIMER_STOP(sscop, idle); + TIMER_STOP(sscop, poll); +} + +/* + * P 77: Set data transfer timers + */ +static void +m_set_data_xfer_timers(struct sscop *sscop) +{ + TIMER_RESTART(sscop, poll); + TIMER_RESTART(sscop, nr); +} + +/* + * P 77: Initialize VR(MR) + */ +static void +m_initialize_mr(struct sscop *sscop) +{ + sscop->vr_mr = sscop->mr; +} + +/************************************************************/ +/* + * CONDITIONS + */ +static int +c_ready_pduq(struct sscop *sscop) +{ + if (!sscop->ll_busy && + (sscop->rxq != 0 || + sscop->vt_s < sscop->vt_ms || + TIMER_ISACT(sscop, idle))) + return (1); + return (0); +} + +/************************************************************/ +/* + * SEND PDUS + */ + +/* + * Send BG PDU. + */ +static void +send_bgn(struct sscop *sscop, struct SSCOP_MBUF_T *uu) +{ + union pdu pdu; + union bgn bgn; + struct SSCOP_MBUF_T *m; + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_BGN; + pdu.sscop_ns = sscop->vr_mr; + + bgn.sscop_null = 0; + bgn.sscop_bgns = sscop->vt_sq; + + if(uu) { + if ((m = MBUF_DUP(uu)) == NULL) { + FAILURE("sscop: cannot allocate BGN"); + return; + } + pdu.sscop_pl += MBUF_PAD4(m); + } else { + if ((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate BGN"); + return; + } + } + + MBUF_APPEND32(m, bgn.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send BGREJ PDU. + */ +static void +send_bgrej(struct sscop *sscop, struct SSCOP_MBUF_T *uu) +{ + union pdu pdu; + union bgn bgn; + struct SSCOP_MBUF_T *m; + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_BGREJ; + bgn.sscop_null = 0; + + if(uu) { + if((m = MBUF_DUP(uu)) == NULL) { + FAILURE("sscop: cannot allocate BGREJ"); + return; + } + pdu.sscop_pl += MBUF_PAD4(m); + } else { + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate BGREJ"); + return; + } + } + + MBUF_APPEND32(m, bgn.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send BGAK PDU. + */ +static void +send_bgak(struct sscop *sscop, struct SSCOP_MBUF_T *uu) +{ + union pdu pdu; + union bgn bgn; + struct SSCOP_MBUF_T *m; + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_BGAK; + pdu.sscop_ns = sscop->vr_mr; + bgn.sscop_null = 0; + + if(uu) { + if((m = MBUF_DUP(uu)) == NULL) { + FAILURE("sscop: cannot allocate BGAK"); + return; + } + pdu.sscop_pl += MBUF_PAD4(m); + } else { + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate BGAK"); + return; + } + } + + MBUF_APPEND32(m, bgn.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send SD PDU. The function makes a duplicate of the message. + */ +static void +send_sd(struct sscop *sscop, struct SSCOP_MBUF_T *m, u_int seqno) +{ + union pdu pdu; + + if((m = MBUF_DUP(m)) == NULL) { + FAILURE("sscop: cannot allocate SD"); + return; + } + + pdu.sscop_null = 0; + pdu.sscop_pl = 0; + pdu.sscop_type = PDU_SD; + pdu.sscop_ns = seqno; + + pdu.sscop_pl += MBUF_PAD4(m); + + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send a UD PDU. The caller must free the sscop msg part. + */ +static void +send_ud(struct sscop *sscop, struct SSCOP_MBUF_T *m) +{ + union pdu pdu; + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_UD; + + pdu.sscop_pl += MBUF_PAD4(m); + + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send a MD PDU. The caller must free the sscop msg part. + */ +static void +send_md(struct sscop *sscop, struct SSCOP_MBUF_T *m) +{ + union pdu pdu; + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_MD; + + pdu.sscop_pl += MBUF_PAD4(m); + + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send END PDU. + */ +static void +send_end(struct sscop *sscop, int src, struct SSCOP_MBUF_T *uu) +{ + union pdu pdu; + struct SSCOP_MBUF_T *m; + + sscop->last_end_src = src; + + pdu.sscop_null = 0; + pdu.sscop_s = src; + pdu.sscop_type = PDU_END; + + if(uu) { + if((m = MBUF_DUP(uu)) == NULL) { + FAILURE("sscop: cannot allocate END"); + return; + } + pdu.sscop_pl += MBUF_PAD4(m); + } else { + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate END"); + return; + } + } + + MBUF_APPEND32(m, 0); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send USTAT PDU. List must be terminated by -1. + */ +static void +send_ustat(struct sscop *sscop, ...) +{ + va_list ap; + int f; + u_int n; + union pdu pdu; + union seqno seqno; + struct SSCOP_MBUF_T *m; + + va_start(ap, sscop); + n = 0; + while((f = va_arg(ap, int)) >= 0) + n++; + va_end(ap); + + if((m = MBUF_ALLOC(n * 4 + 8)) == NULL) { + FAILURE("sscop: cannot allocate USTAT"); + return; + } + + va_start(ap, sscop); + while((f = va_arg(ap, int)) >= 0) { + seqno.sscop_null = 0; + seqno.sscop_n = f; + MBUF_APPEND32(m, seqno.sscop_null); + } + va_end(ap); + + seqno.sscop_null = 0; + seqno.sscop_n = sscop->vr_mr; + MBUF_APPEND32(m, seqno.sscop_null); + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_USTAT; + pdu.sscop_ns = sscop->vr_r; + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send ER PDU. + */ +static void +send_er(struct sscop *sscop) +{ + union pdu pdu; + union bgn bgn; + struct SSCOP_MBUF_T *m; + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_ER; + pdu.sscop_ns = sscop->vr_mr; + + bgn.sscop_null = 0; + bgn.sscop_bgns = sscop->vt_sq; + + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate ER"); + return; + } + MBUF_APPEND32(m, bgn.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send POLL PDU. + */ +static void +send_poll(struct sscop *sscop) +{ + union pdu pdu; + union seqno seqno; + struct SSCOP_MBUF_T *m; + + seqno.sscop_null = 0; + seqno.sscop_n = sscop->vt_ps; + + pdu.sscop_null = 0; + pdu.sscop_ns = sscop->vt_s; + pdu.sscop_type = PDU_POLL; + + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate POLL"); + return; + } + MBUF_APPEND32(m, seqno.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send STAT PDU. List is already in buffer. + */ +static void +send_stat(struct sscop *sscop, u_int nps, struct SSCOP_MBUF_T *m) +{ + union pdu pdu; + union seqno seqno; + + seqno.sscop_null = 0; + seqno.sscop_n = nps; + MBUF_APPEND32(m, seqno.sscop_null); + + seqno.sscop_null = 0; + seqno.sscop_n = sscop->vr_mr; + MBUF_APPEND32(m, seqno.sscop_null); + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_STAT; + pdu.sscop_ns = sscop->vr_r; + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send ENDAK PDU. + */ +static void +send_endak(struct sscop *sscop) +{ + union pdu pdu; + union seqno seqno; + struct SSCOP_MBUF_T *m; + + seqno.sscop_null = 0; + pdu.sscop_null = 0; + pdu.sscop_type = PDU_ENDAK; + + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate ENDAK"); + return; + } + MBUF_APPEND32(m, seqno.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send ERAK PDU. + */ +static void +send_erak(struct sscop *sscop) +{ + union pdu pdu; + union seqno seqno; + struct SSCOP_MBUF_T *m; + + seqno.sscop_null = 0; + pdu.sscop_null = 0; + pdu.sscop_type = PDU_ERAK; + pdu.sscop_ns = sscop->vr_mr; + + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate ERAK"); + return; + } + MBUF_APPEND32(m, seqno.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send RS PDU + */ +static void +send_rs(struct sscop *sscop, int resend, struct SSCOP_MBUF_T *uu) +{ + union pdu pdu; + union bgn bgn; + struct SSCOP_MBUF_T *m; + + pdu.sscop_null = 0; + pdu.sscop_type = PDU_RS; + pdu.sscop_ns = resend ? sscop->rs_mr : sscop->vr_mr; + + bgn.sscop_null = 0; + bgn.sscop_bgns = resend ? sscop->rs_sq : sscop->vt_sq; + + sscop->rs_mr = pdu.sscop_ns; + sscop->rs_sq = bgn.sscop_bgns; + + if(uu) { + if((m = MBUF_DUP(uu)) == NULL) { + FAILURE("sscop: cannot allocate RS"); + return; + } + pdu.sscop_pl += MBUF_PAD4(m); + } else { + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate RS"); + return; + } + } + + MBUF_APPEND32(m, bgn.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/* + * Send RSAK pdu + */ +static void +send_rsak(struct sscop *sscop) +{ + union pdu pdu; + union seqno seqno; + struct SSCOP_MBUF_T *m; + + seqno.sscop_null = 0; + pdu.sscop_null = 0; + pdu.sscop_type = PDU_RSAK; + pdu.sscop_ns = sscop->vr_mr; + + if((m = MBUF_ALLOC(8)) == NULL) { + FAILURE("sscop: cannot allocate RSAK"); + return; + } + + MBUF_APPEND32(m, seqno.sscop_null); + MBUF_APPEND32(m, pdu.sscop_null); + + AAL_SEND(sscop, m); +} + +/************************************************************/ +/* + * P 31; IDLE && AA-ESTABLISH-request + * arg is UU data (opt). + */ +static void +sscop_idle_establish_req(struct sscop *sscop, struct sscop_msg *uu) +{ + u_int br = uu->rexmit; + + SET_UU(uu_bgn, uu); + + m_clear_transmitter(sscop); + + sscop->clear_buffers = br; + + sscop->vt_cc = 1; + sscop->vt_sq++; + + m_initialize_mr(sscop); + + send_bgn(sscop, sscop->uu_bgn); + + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_PEND); +} + +/* + * P 31: IDLE && BGN PDU + * arg is the received PDU (freed). + */ +static void +sscop_idle_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + union bgn bgn; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(sscop->robustness) { + bgn.sscop_null = MBUF_STRIP32(msg->m); + sscop->vr_sq = bgn.sscop_bgns; + } else { + if(m_detect_retransmission(sscop, msg)) { + send_bgrej(sscop, sscop->uu_bgrej); + SSCOP_MSG_FREE(msg); + return; + } + (void)MBUF_STRIP32(msg->m); + } + + sscop->vt_ms = pdu.sscop_ns; + sscop_set_state(sscop, SSCOP_IN_PEND); + + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, msg, pdu.sscop_pl, 0); +} + +/* + * p 31: IDLE && ENDAK PDU + * p 34: OUT_PEND && ENDAK PDU + * p 34: OUT_PEND && SD PDU + * p 34: OUT_PEND && ERAK PDU + * p 34: OUT_PEND && END PDU + * p 34: OUT_PEND && STAT PDU + * p 34: OUT_PEND && USTAT PDU + * p 34: OUT_PEND && POLL PDU + * p 36: OUT_PEND && RS PDU + * p 36: OUT_PEND && RSAK PDU + * p 40: OUTGOING_DISCONNECT_PENDING && SD PDU + * p 40: OUTGOING_DISCONNECT_PENDING && BGAK PDU + * p 40: OUTGOING_DISCONNECT_PENDING && POLL PDU + * p 40: OUTGOING_DISCONNECT_PENDING && STAT PDU + * p 40: OUTGOING_DISCONNECT_PENDING && USTAT PDU + * p 41: OUTGOING_DISCONNECT_PENDING && ERAK PDU + * p 42: OUTGOING_DISCONNECT_PENDING && ER PDU + * p 42: OUTGOING_DISCONNECT_PENDING && RS PDU + * p 42: OUTGOING_DISCONNECT_PENDING && RSAK PDU + * p 43: OUTGOING_RESYNC && ER PDU + * p 43: OUTGOING_RESYNC && POLL PDU + * p 44: OUTGOING_RESYNC && STAT PDU + * p 44: OUTGOING_RESYNC && USTAT PDU + * p 45: OUTGOING_RESYNC && BGAK PDU + * p 45: OUTGOING_RESYNC && SD PDU + * p 45: OUTGOING_RESYNC && ERAK PDU + * P 60: READY && BGAK PDU + * P 60: READY && ERAK PDU + * arg is pdu (freed). + */ +static void +sscop_ignore_pdu(struct sscop *sscop __unused, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); +} + +/* + * p 31: IDLE && END PDU + * arg is pdu (freed). + */ +static void +sscop_idle_end(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + send_endak(sscop); +} + +/* + * p 31: IDLE && ER PDU + * arg is pdu (freed). + */ +static void +sscop_idle_er(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'L', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 31: IDLE && BGREJ PDU + * arg is pdu (freed). + */ +static void +sscop_idle_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'D', 0); + FREE_UU(uu_end); +} + +/* + * p 32: IDLE && POLL PDU + * arg is pdu (freed). + */ +static void +sscop_idle_poll(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'G', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 32: IDLE && SD PDU + * arg is pdu (freed). + */ +static void +sscop_idle_sd(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'A', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 32: IDLE && BGAK PDU + * arg is pdu (freed). + */ +static void +sscop_idle_bgak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'C', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 32: IDLE && ERAK PDU + * arg is pdu (freed). + */ +static void +sscop_idle_erak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'M', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 32: IDLE && STAT PDU + * arg is pdu (freed). + */ +static void +sscop_idle_stat(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'H', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 32: IDLE && USTAT PDU + * arg is pdu (freed). + */ +static void +sscop_idle_ustat(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'I', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 33: IDLE & RS PDU + * arg is pdu (freed). + */ +static void +sscop_idle_rs(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'J', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 33: IDLE & RSAK PDU + * arg is pdu (freed). + */ +static void +sscop_idle_rsak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'K', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); +} + +/* + * p 33: IDLE && PDU_Q + * p XX: OUTPEND && PDU_Q + * p 39: IN_PEND && PDU_Q + * p 45: OUT_RESYNC_PEND && PDU_Q + * p 48: IN_RESYNC_PEND && PDU_Q + * no arg + */ +static void +sscop_flush_pduq(struct sscop *sscop __unused, struct sscop_msg *unused __unused) +{ +#if 0 + MSGQ_CLEAR(&sscop->xq); +#endif +} + +/* + * p 34: OUT_PEND && BGAK PDU + * arg is pdu (freed). + */ +static void +sscop_outpend_bgak(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_confirm, msg, pdu.sscop_pl, 0); + + m_initialize_state(sscop); + m_set_data_xfer_timers(sscop); + + sscop_set_state(sscop, SSCOP_READY); +} + +/* + * P 34: OUT_PEND && BGREJ PDU + */ +static void +sscop_outpend_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_indication, msg, pdu.sscop_pl, 0); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * P 35: OUT_PEND && TIMER_CC expiry + * no arg + */ +static void +sscop_outpend_tcc(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + if(sscop->vt_cc >= sscop->maxcc) { + MAAL_ERROR(sscop, 'O', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + + sscop_set_state(sscop, SSCOP_IDLE); + } else { + sscop->vt_cc++; + send_bgn(sscop, sscop->uu_bgn); + TIMER_RESTART(sscop, cc); + } +} + +/* + * P 35: OUT_PEND && RELEASE_REQ + * arg is UU + */ +static void +sscop_outpend_release_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_end, uu); + + TIMER_STOP(sscop, cc); + sscop->vt_cc = 1; + send_end(sscop, 0, sscop->uu_end); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_DIS_PEND); +} + +/* + * P 36: OUT_PEND && BGN PDU + * arg is the received PDU (freed). + */ +static void +sscop_outpend_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + SSCOP_MSG_FREE(msg); + return; + } + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + + sscop->vt_ms = pdu.sscop_ns; + + m_initialize_mr(sscop); + + send_bgak(sscop, sscop->uu_bgak); + + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_confirm, msg, pdu.sscop_pl, 0); + + m_initialize_state(sscop); + + m_set_data_xfer_timers(sscop); + + sscop_set_state(sscop, SSCOP_READY); +} + +/* + * p 37: IN_PEND && AA-ESTABLISH.response + * arg is UU + */ +static void +sscop_inpend_establish_resp(struct sscop *sscop, struct sscop_msg *uu) +{ + u_int br = uu->rexmit; + + SET_UU(uu_bgak, uu); + + m_clear_transmitter(sscop); + sscop->clear_buffers = br; + m_initialize_mr(sscop); + send_bgak(sscop, sscop->uu_bgak); + m_initialize_state(sscop); + m_set_data_xfer_timers(sscop); + + sscop_set_state(sscop, SSCOP_READY); +} + +/* + * p 37: IN_PEND && AA-RELEASE.request + * arg is uu. + */ +static void +sscop_inpend_release_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_bgrej, uu); + + send_bgrej(sscop, sscop->uu_bgrej); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 37: IN_PEND && BGN PDU + * arg is pdu. (freed) + */ +static void +sscop_inpend_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + SSCOP_MSG_FREE(msg); + return; + } + (void)MBUF_STRIP32(msg->m); + + sscop->vt_ms = pdu.sscop_ns; + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 0); + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, msg, pdu.sscop_pl, 0); +} + +/* + * p 37: IN_PEND && ER PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_er(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'L', 0); + SSCOP_MSG_FREE(msg); +} + +/* + * p 37: IN_PEND && ENDAK PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_endak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'F', 0); + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + + sscop_set_state(sscop, SSCOP_IDLE); + + SSCOP_MSG_FREE(msg); +} + +/* + * p 38: IN_PEND && BGAK PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_bgak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'C', 0); + + SSCOP_MSG_FREE(msg); +} + +/* + * p 38: IN_PEND && BGREJ PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'D', 0); + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + + SSCOP_MSG_FREE(msg); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 38: IN_PEND && SD PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_sd(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'A', 0); + + SSCOP_MSG_FREE(msg); + + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 38: IN_PEND && USTAT PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_ustat(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'I', 0); + + SSCOP_MSG_FREE(msg); + + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 38: IN_PEND && STAT PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_stat(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'H', 0); + + SSCOP_MSG_FREE(msg); + + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 38: IN_PEND && POLL PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_poll(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'G', 0); + + SSCOP_MSG_FREE(msg); + + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 39: IN_PEND && ERAK PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_erak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'M', 0); +} + +/* + * p 39: IN_PEND & RS PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_rs(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'J', 0); +} + +/* + * p 39: IN_PEND & RSAK PDU + * arg is pdu (freed). + */ +static void +sscop_inpend_rsak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'K', 0); +} + +/* + * p 39: IN_PEND && END PDU + * arg is pdu (freed). + * no uui + */ +static void +sscop_inpend_end(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + send_endak(sscop); + + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_indication, + msg, pdu.sscop_pl, (u_int)pdu.sscop_s); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 40: OUT_DIS_PEND && SSCOP_ESTABLISH_request + * no arg. + * no uui. + */ +static void +sscop_outdis_establish_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_bgn, uu); + + TIMER_STOP(sscop, cc); + m_clear_transmitter(sscop); + sscop->clear_buffers = 1; + sscop->vt_cc = 1; + sscop->vt_sq++; + m_initialize_mr(sscop); + send_bgn(sscop, sscop->uu_bgn); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_PEND); +} + +/* + * p 41: OUT_DIS_PEND && END PDU + * arg is pdu (freed). + */ +static void +sscop_outdis_end(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + send_endak(sscop); + + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_confirm, msg, pdu.sscop_pl, 0); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 41: OUT_DIS_PEND && ENDAK PDU + * p 41: OUT_DIS_PEND && BGREJ PDU + * arg is pdu (freed) + */ +static void +sscop_outdis_endak(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_confirm, msg, pdu.sscop_pl, 0); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 41: OUT_DIS_PEND && TIMER CC expiry + * no arg + */ +static void +sscop_outdis_cc(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + if(sscop->vt_cc >= sscop->maxcc) { + MAAL_ERROR(sscop, 'O', 0); + AAL_SIG(sscop, SSCOP_RELEASE_confirm); + sscop_set_state(sscop, SSCOP_IDLE); + } else { + sscop->vt_cc++; + send_end(sscop, sscop->last_end_src, sscop->uu_end); + TIMER_RESTART(sscop, cc); + } +} + +/* + * p 42: OUT_DIS_PEND && BGN PDU + * arg is pdu (freed). + */ +static void +sscop_outdis_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + FREE_UU(uu_bgak); + send_bgak(sscop, NULL); + send_end(sscop, sscop->last_end_src, sscop->uu_end); + SSCOP_MSG_FREE(msg); + + } else { + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + AAL_SIG(sscop, SSCOP_RELEASE_confirm); + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, + msg, pdu.sscop_pl, 0); + sscop_set_state(sscop, SSCOP_IN_PEND); + } +} + +/* + * p 43: OUT_RESYNC_PEND && BGN PDU + * arg is pdu (freed). + */ +static void +sscop_outsync_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + send_bgak(sscop, sscop->uu_bgak); + send_rs(sscop, 1, sscop->uu_rs); + SSCOP_MSG_FREE(msg); + } else { + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 0); + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, + msg, pdu.sscop_pl, 0); + sscop_set_state(sscop, SSCOP_IN_PEND); + } +} + +/* + * p 43: OUT_RESYNC_PEND && ENDAK PDU + * arg is pdu (freed). + */ +static void +sscop_outsync_endak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + TIMER_STOP(sscop, cc); + MAAL_ERROR(sscop, 'F', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 43: OUT_RESYNC_PEND && BGREJ PDU + * arg is pdu (freed). + */ +static void +sscop_outsync_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + TIMER_STOP(sscop, cc); + MAAL_ERROR(sscop, 'D', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 43: OUT_RESYNC_PEND && END PDU + * arg is pdu (freed). + * no UU-data + */ +static void +sscop_outsync_end(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + send_endak(sscop); + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_indication, msg, pdu.sscop_pl, + (u_int)pdu.sscop_s); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 44: OUT_RESYNC && TIMER CC expiry + */ +static void +sscop_outsync_cc(struct sscop *sscop, struct sscop_msg *msg __unused) +{ + if(sscop->vt_cc == sscop->maxcc) { + MAAL_ERROR(sscop, 'O', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); + } else { + sscop->vt_cc++; + send_rs(sscop, 1, sscop->uu_rs); + TIMER_RESTART(sscop, cc); + } +} + +/* + * p 44: OUT_RESYNC && AA-RELEASE.request + * arg is UU + */ +static void +sscop_outsync_release_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_end, uu); + + TIMER_STOP(sscop, cc); + sscop->vt_cc = 1; + send_end(sscop, 0, sscop->uu_end); + TIMER_RESTART(sscop, cc); + sscop_set_state(sscop, SSCOP_OUT_DIS_PEND); +} + +/* + * p 45: OUT_RESYNC && RS PDU + * arg is pdu (freed). + */ +static void +sscop_outsync_rs(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + SSCOP_MSG_FREE(msg); + return; + } + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + m_initialize_mr(sscop); + send_rsak(sscop); + AAL_UU_SIGNAL(sscop, SSCOP_RESYNC_confirm, msg, pdu.sscop_pl, 0); + m_initialize_state(sscop); + m_set_data_xfer_timers(sscop); + sscop_set_state(sscop, SSCOP_READY); +} + +/* + * p 45: OUT_RESYNC && RSAK PDU + * arg is pdu (freed). + */ +static void +sscop_outsync_rsak(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + SSCOP_MSG_FREE(msg); + + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + AAL_SIG(sscop, SSCOP_RESYNC_confirm); + m_initialize_state(sscop); + m_set_data_xfer_timers(sscop); + sscop_set_state(sscop, SSCOP_READY); +} + +/* + * p 46: IN_RESYNC_PEND && AA-RESYNC.response + */ +static void +sscop_insync_sync_resp(struct sscop *sscop, struct sscop_msg *noarg __unused) +{ + m_initialize_mr(sscop); + send_rsak(sscop); + m_clear_transmitter(sscop); + m_initialize_state(sscop); + m_set_data_xfer_timers(sscop); + sscop_set_state(sscop, SSCOP_READY); +} + +/* + * p 46: IN_RESYNC_PEND && AA-RELEASE.request + * arg is uu + */ +static void +sscop_insync_release_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_end, uu); + + sscop->vt_cc = 1; + send_end(sscop, 0, sscop->uu_end); + TIMER_RESTART(sscop, cc); + sscop_set_state(sscop, SSCOP_OUT_DIS_PEND); +} + +/* + * p 46: IN_RESYNC_PEND && ENDAK PDU + * arg is pdu (freed). + */ +static void +sscop_insync_endak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'F', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 46: IN_RESYNC_PEND && BGREJ PDU + * arg is pdu (freed). + */ +static void +sscop_insync_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'D', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 46: IN_RESYNC_PEND && END PDU + * arg is pdu (freed). + */ +static void +sscop_insync_end(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + send_endak(sscop); + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_indication, + msg, pdu.sscop_pl, (u_int)pdu.sscop_s); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 47: IN_RESYNC_PEND && ER PDU + * arg is pdu (freed). + */ +static void +sscop_insync_er(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'L', 0); +} + +/* + * p 47: IN_RESYNC_PEND && BGN PDU + * arg is pdu (freed). + */ +static void +sscop_insync_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + MAAL_ERROR(sscop, 'B', 0); + SSCOP_MSG_FREE(msg); + return; + } + (void)MBUF_STRIP32(msg->m); + + sscop->vt_ms = pdu.sscop_ns; + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 0); + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, msg, pdu.sscop_pl, 0); + + sscop_set_state(sscop, SSCOP_IN_PEND); +} + +/* + * p 47: IN_RESYNC_PEND && SD PDU + * arg is pdu (freed). + */ +static void +sscop_insync_sd(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'A', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 47: IN_RESYNC_PEND && POLL PDU + * arg is pdu (freed). + */ +static void +sscop_insync_poll(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'G', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 47: IN_RESYNC_PEND && STAT PDU + * arg is pdu (freed). + */ +static void +sscop_insync_stat(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'H', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 47: IN_RESYNC_PEND && USTAT PDU + * arg is pdu (freed). + */ +static void +sscop_insync_ustat(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'I', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 48: IN_RESYNC_PEND && BGAK PDU + * arg is pdu (freed). + */ +static void +sscop_insync_bgak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'C', 0); + SSCOP_MSG_FREE(msg); +} + +/* + * p 48: IN_RESYNC_PEND && ERAK PDU + * arg is pdu (freed). + */ +static void +sscop_insync_erak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'M', 0); + SSCOP_MSG_FREE(msg); +} + +/* + * p 48: IN_RESYNC_PEND && RS PDU + * arg is pdu (freed). + */ +static void +sscop_insync_rs(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + SSCOP_MSG_FREE(msg); + return; + } + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'J', 0); +} + +/* + * p 48: IN_RESYNC_PEND && RSAK PDU + * arg is pdu (freed). + */ +static void +sscop_insync_rsak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'K', 0); + SSCOP_MSG_FREE(msg); +} + + +/* + * p 49: OUT_REC_PEND && AA-DATA.request + * arg is message (queued). + */ +static void +sscop_outrec_userdata(struct sscop *sscop, struct sscop_msg *msg) +{ + if(!sscop->clear_buffers) { + MSGQ_APPEND(&sscop->xq, msg); + sscop_signal(sscop, SIG_PDU_Q, msg); + } else { + SSCOP_MSG_FREE(msg); + } +} + +/* + * p 49: OUT_REC_PEND && BGAK PDU + * arg is pdu (freed) + */ +static void +sscop_outrec_bgak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'C', 0); + + SSCOP_MSG_FREE(msg); +} + +/* + * p 49: OUT_REC_PEND && ERAK PDU + * arg is pdu (freed) + */ +static void +sscop_outrec_erak(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + m_deliver_data(sscop); + + AAL_SIG(sscop, SSCOP_RECOVER_indication); + + sscop_set_state(sscop, SSCOP_REC_PEND); + + SSCOP_MSG_FREE(msg); +} + +/* + * p 49: OUT_REC_PEND && END PDU + * arg is pdu (freed) + */ +static void +sscop_outrec_end(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + send_endak(sscop); + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_indication, + msg, pdu.sscop_pl, (u_int)pdu.sscop_s); + + MSGQ_CLEAR(&sscop->rbuf); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 49: OUT_REC_PEND && ENDAK PDU + * arg is pdu (freed) + */ +static void +sscop_outrec_endak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'F', 0); + TIMER_STOP(sscop, cc); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + MSGQ_CLEAR(&sscop->rbuf); + + sscop_set_state(sscop, SSCOP_IDLE); + + SSCOP_MSG_FREE(msg); +} + +/* + * p 49: OUT_REC_PEND && BGREJ PDU + * arg is pdu (freed) + */ +static void +sscop_outrec_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'D', 0); + TIMER_STOP(sscop, cc); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + MSGQ_CLEAR(&sscop->rbuf); + + sscop_set_state(sscop, SSCOP_IDLE); + + SSCOP_MSG_FREE(msg); +} + +/* + * p 50: OUT_REC_PEND && TIMER CC expiry + * no arg. + */ +static void +sscop_outrec_cc(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + if(sscop->vt_cc >= sscop->maxcc) { + MAAL_ERROR(sscop, 'O', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + MSGQ_CLEAR(&sscop->rbuf); + sscop_set_state(sscop, SSCOP_IDLE); + } else { + sscop->vt_cc++; + send_er(sscop); + TIMER_RESTART(sscop, cc); + } +} + +/* + * p 50: OUT_REC_PEND && SSCOP_RELEASE_request + * arg is UU + */ +static void +sscop_outrec_release_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_end, uu); + + TIMER_STOP(sscop, cc); + sscop->vt_cc = 1; + send_end(sscop, 0, sscop->uu_end); + MSGQ_CLEAR(&sscop->rbuf); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_DIS_PEND); +} + +/* + * p 51: OUT_REC_PEND && AA-RESYNC.request + * arg is uu + */ +static void +sscop_outrec_sync_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_rs, uu); + + TIMER_STOP(sscop, cc); + sscop->vt_cc = 1; + sscop->vt_sq++; + m_initialize_mr(sscop); + send_rs(sscop, 0, sscop->uu_rs); + m_clear_transmitter(sscop); + MSGQ_CLEAR(&sscop->rbuf); + TIMER_RESTART(sscop, cc); +} + +/* + * p 51: OUT_REC_PEND && BGN PDU + * arg is pdu (freed). + * no uui + */ +static void +sscop_outrec_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + MAAL_ERROR(sscop, 'B', 0); + SSCOP_MSG_FREE(msg); + } else { + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 0); + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, + msg, pdu.sscop_pl, 0); + MSGQ_CLEAR(&sscop->rbuf); + + sscop_set_state(sscop, SSCOP_IN_PEND); + } +} + +/* + * p 51: OUT_REC_PEND && ER PDU + * arg is pdu (freed). + */ +static void +sscop_outrec_er(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + MAAL_ERROR(sscop, 'L', 0); + } else { + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + m_initialize_mr(sscop); + send_erak(sscop); + m_deliver_data(sscop); + + AAL_SIG(sscop, SSCOP_RECOVER_indication); + + sscop_set_state(sscop, SSCOP_REC_PEND); + } + + SSCOP_MSG_FREE(msg); +} + +/* + * p 52: OUT_REC_PEND && SD PDU queued + * no arg. + */ +static void +sscop_outrec_pduq(struct sscop *sscop, struct sscop_msg *msg) +{ + sscop_save_signal(sscop, SIG_PDU_Q, msg); +} + +/* + * p 52: OUT_REC_PEND && RSAK PDU + * arg is pdu (freed). + */ +static void +sscop_outrec_rsak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'K', 0); +} + +/* + * p 52: OUT_REC_PEND && RS PDU + * arg is pdu (freed). + */ +static void +sscop_outrec_rs(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'J', 0); + return; + } + (void)MBUF_STRIP32(msg->m); + + TIMER_STOP(sscop, cc); + sscop->vt_ms = pdu.sscop_ns; + AAL_UU_SIGNAL(sscop, SSCOP_RESYNC_indication, msg, pdu.sscop_pl, 0); + MSGQ_CLEAR(&sscop->rbuf); + sscop_set_state(sscop, SSCOP_IN_RESYNC_PEND); +} + +/* + * p 53: REC_PEND && BGAK PDU + * arg is pdu (freed) + */ +static void +sscop_rec_bgak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'C', 0); + + SSCOP_MSG_FREE(msg); +} + +/* + * p 53: REC_PEND && END PDU + * arg is pdu (freed) + * no uui + */ +static void +sscop_rec_end(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + send_endak(sscop); + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_indication, + msg, pdu.sscop_pl, (u_int)pdu.sscop_s); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 53: REC_PEND && ENDAK PDU + * arg is pdu (freed) + */ +static void +sscop_rec_endak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'F', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); + SSCOP_MSG_FREE(msg); +} + +/* + * p 53: REC_PEND && BGREJ PDU + * arg is pdu (freed) + */ +static void +sscop_rec_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'D', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); + SSCOP_MSG_FREE(msg); +} + +/* + * p 54: REC_PEND && RELEASE + * arg is UU + */ +static void +sscop_rec_release_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_end, uu); + + sscop->vt_cc = 1; + send_end(sscop, 0, sscop->uu_end); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_DIS_PEND); +} + +/* + * p 54: REC_PEND && RSAK PDU + * arg is pdu (freed). + */ +static void +sscop_rec_rsak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'K', 0); + SSCOP_MSG_FREE(msg); +} + + +/* + * p 54: REC_PEND && RS PDU + * arg is pdu (freed). + */ +static void +sscop_rec_rs(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'J', 0); + return; + } + (void)MBUF_STRIP32(msg->m); + + sscop->vt_ms = pdu.sscop_ns; + AAL_UU_SIGNAL(sscop, SSCOP_RESYNC_indication, msg, pdu.sscop_pl, 0); + + sscop_set_state(sscop, SSCOP_IN_RESYNC_PEND); +} + +/* + * p 54: REC_PEND && RECOVER response + * no arg + */ +static void +sscop_rec_recover(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + if(!sscop->clear_buffers) { + MSGQ_CLEAR(&sscop->xbuf); + } + m_initialize_state(sscop); + m_set_data_xfer_timers(sscop); + + sscop_set_state(sscop, SSCOP_READY); +} + +/* + * p 54: REC_PEND && RESYNC request + * arg is uu + */ +static void +sscop_rec_sync_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_rs, uu); + + m_clear_transmitter(sscop); + sscop->vt_cc = 1; + sscop->vt_sq++; + m_initialize_mr(sscop); + send_rs(sscop, 0, sscop->uu_rs); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_RESYNC_PEND); +} + +/* + * p 55: REC_PEND && SD PDU queued + * no arg + */ +static void +sscop_rec_pduq(struct sscop *sscop, struct sscop_msg *msg) +{ + sscop_save_signal(sscop, SIG_PDU_Q, msg); +} + +/* + * p 55: REC_PEND && ER PDU + * arg is pdu (freed). + */ +static void +sscop_rec_er(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + send_erak(sscop); + } else { + MAAL_ERROR(sscop, 'L', 0); + } + SSCOP_MSG_FREE(msg); +} + +/* + * p 55: REC_PEND && BGN PDU + * arg is pdu (freed) + * no uui + */ +static void +sscop_rec_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + MAAL_ERROR(sscop, 'B', 0); + SSCOP_MSG_FREE(msg); + return; + } + (void)MBUF_STRIP32(msg->m); + + sscop->vt_ms = pdu.sscop_ns; + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 0); + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, msg, pdu.sscop_pl, 0); + + sscop_set_state(sscop, SSCOP_IN_PEND); +} + +/* + * p 55: REC_PEND && STAT PDU + * arg is pdu (freed) + */ +static void +sscop_rec_stat(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'H', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); + SSCOP_MSG_FREE(msg); +} + +/* + * p 55: REC_PEND && USTAT PDU + * arg is pdu (freed) + */ +static void +sscop_rec_ustat(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'I', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + sscop_set_state(sscop, SSCOP_IDLE); + SSCOP_MSG_FREE(msg); +} + +/* + * p 56: IN_REC_PEND && AA-RECOVER.response + * no arg + */ +static void +sscop_inrec_recover(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + if(!sscop->clear_buffers) { + MSGQ_CLEAR(&sscop->xbuf); + } + m_initialize_mr(sscop); + send_erak(sscop); + m_initialize_state(sscop); + m_set_data_xfer_timers(sscop); + + sscop_set_state(sscop, SSCOP_READY); +} + +/* + * p 56: IN_REC_PEND && SD PDU queued + * no arg + */ +static void +sscop_inrec_pduq(struct sscop *sscop, struct sscop_msg *msg) +{ + sscop_save_signal(sscop, SIG_PDU_Q, msg); +} + +/* + * p 56: IN_REC_PEND && AA-RELEASE.request + * arg is UU + */ +static void +sscop_inrec_release_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_end, uu); + + sscop->vt_cc = 1; + send_end(sscop, 0, sscop->uu_end); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_DIS_PEND); +} + +/* + * p 56: IN_REC_PEND && END PDU + * arg is pdu (freed). + * no uui + */ +static void +sscop_inrec_end(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + send_endak(sscop); + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_indication, + msg, pdu.sscop_pl, (u_int)pdu.sscop_s); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 56: IN_REC_PEND && RESYNC_REQ + */ +static void +sscop_inrec_sync_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_rs, uu); + + m_clear_transmitter(sscop); + sscop->vt_cc = 1; + sscop->vt_sq++; + m_initialize_mr(sscop); + send_rs(sscop, 0, sscop->uu_rs); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_RESYNC_PEND); +} + + +/* + * p 57: IN_REC_PEND && ENDAK PDU + * arg is pdu (freed) + */ +static void +sscop_inrec_endak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'F', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + SSCOP_MSG_FREE(msg); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 57: IN_REC_PEND && BGREJ PDU + * arg is pdu (freed) + */ +static void +sscop_inrec_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'D', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + SSCOP_MSG_FREE(msg); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 57: IN_REC_PEND && USTAT PDU + * arg is pdu (freed) + */ +static void +sscop_inrec_ustat(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'I', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + SSCOP_MSG_FREE(msg); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 57: IN_REC_PEND && STAT PDU + * arg is pdu (freed) + */ +static void +sscop_inrec_stat(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'H', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + SSCOP_MSG_FREE(msg); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 57: IN_REC_PEND && POLL PDU + * arg is pdu (freed) + */ +static void +sscop_inrec_poll(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'G', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + SSCOP_MSG_FREE(msg); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 57: IN_REC_PEND && SD PDU + * arg is pdu (freed) + */ +static void +sscop_inrec_sd(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'A', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + SSCOP_MSG_FREE(msg); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 58: IN_REC_PEND && RSAK PDU + * arg is pdu (freed). + */ +static void +sscop_inrec_rsak(struct sscop *sscop, struct sscop_msg *msg) +{ + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'K', 0); +} + +/* + * p 58: IN_REC_PEND && RS PDU + * arg is pdu (freed). + */ +static void +sscop_inrec_rs(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + SSCOP_MSG_FREE(msg); + MAAL_ERROR(sscop, 'J', 0); + return; + } + (void)MBUF_STRIP32(msg->m); + + sscop->vt_ms = pdu.sscop_ns; + AAL_UU_SIGNAL(sscop, SSCOP_RESYNC_indication, msg, pdu.sscop_pl, 0); + + sscop_set_state(sscop, SSCOP_IN_RESYNC_PEND); +} + +/* + * p 59: IN_REC_PEND && ER PDU + * arg is pdu (freed) + */ +static void +sscop_inrec_er(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(!m_detect_retransmission(sscop, msg)) { + MAAL_ERROR(sscop, 'L', 0); + } + + SSCOP_MSG_FREE(msg); +} + +/* + * p 59: IN_REC_PEND && BGN PDU + * arg is pdu (freed). + * no uui + */ +static void +sscop_inrec_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + MAAL_ERROR(sscop, 'B', 0); + SSCOP_MSG_FREE(msg); + return; + } + (void)MBUF_STRIP32(msg->m); + + sscop->vt_ms = pdu.sscop_ns; + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 0); + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, msg, pdu.sscop_pl, 0); + + sscop_set_state(sscop, SSCOP_IN_PEND); +} + +/* + * p 59: IN_REC_PEND && BGAK PDU + * arg is pdu (freed) + * no uui + */ +static void +sscop_inrec_bgak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'C', 0); + SSCOP_MSG_FREE(msg); +} + +/* + * p 59: IN_REC_PEND && ERAK PDU + * arg is pdu (freed) + * no uui + */ +static void +sscop_inrec_erak(struct sscop *sscop, struct sscop_msg *msg) +{ + MAAL_ERROR(sscop, 'M', 0); + SSCOP_MSG_FREE(msg); +} + +/* + * p 60: READY && RESYNC request + * arg is UU + */ +static void +sscop_ready_sync_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_rs, uu); + + m_reset_data_xfer_timers(sscop); + sscop->vt_cc = 1; + sscop->vt_sq++; + m_initialize_mr(sscop); + send_rs(sscop, 0, sscop->uu_rs); + m_release_buffers(sscop); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_RESYNC_PEND); +} + + +/* + * p 60: READY && AA-RELEASE.request + * arg is uu. + */ +static void +sscop_ready_release_req(struct sscop *sscop, struct sscop_msg *uu) +{ + SET_UU(uu_end, uu); + + m_reset_data_xfer_timers(sscop); + sscop->vt_cc = 1; + send_end(sscop, 0, sscop->uu_end); + m_prepare_retrieval(sscop); + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_DIS_PEND); +} + +/* + * p 61: READY && ER PDU + * arg is pdu (freed). + */ +static void +sscop_ready_er(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + TIMER_RESTART(sscop, nr); + send_erak(sscop); + } else { + m_reset_data_xfer_timers(sscop); + sscop->vt_ms = pdu.sscop_ns; + m_prepare_recovery(sscop); + m_deliver_data(sscop); + + AAL_SIG(sscop, SSCOP_RECOVER_indication); + + sscop_set_state(sscop, SSCOP_IN_REC_PEND); + } + + SSCOP_MSG_FREE(msg); +} + +/* + * p 61: READY && BGN PDU + * arg is pdu (freed) + */ +static void +sscop_ready_bgn(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + TIMER_RESTART(sscop, nr); + send_bgak(sscop, sscop->uu_bgak); + SSCOP_MSG_FREE(msg); + return; + } + (void)MBUF_STRIP32(msg->m); + + m_reset_data_xfer_timers(sscop); + sscop->vt_ms = pdu.sscop_ns; + + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 0); + AAL_UU_SIGNAL(sscop, SSCOP_ESTABLISH_indication, msg, pdu.sscop_pl, 0); + + m_prepare_retrieval(sscop); + + sscop_set_state(sscop, SSCOP_IN_PEND); +} + +/* + * p 62: READY && ENDAK PDU + * arg is pdu (freed) + */ +static void +sscop_ready_endak(struct sscop *sscop, struct sscop_msg *msg) +{ + m_reset_data_xfer_timers(sscop); + MAAL_ERROR(sscop, 'F', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + m_prepare_retrieval(sscop); + SSCOP_MSG_FREE(msg); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 62: READY && BGREJ PDU + * arg is pdu (freed) + */ +static void +sscop_ready_bgrej(struct sscop *sscop, struct sscop_msg *msg) +{ + m_reset_data_xfer_timers(sscop); + MAAL_ERROR(sscop, 'D', 0); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + m_prepare_retrieval(sscop); + SSCOP_MSG_FREE(msg); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 62: READY && RS PDU + * arg is pdu (freed) + */ +static void +sscop_ready_rs(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + if(m_detect_retransmission(sscop, msg)) { + SSCOP_MSG_FREE(msg); + TIMER_RESTART(sscop, nr); + send_rsak(sscop); + return; + } + (void)MBUF_STRIP32(msg->m); + + m_reset_data_xfer_timers(sscop); + sscop->vt_ms = pdu.sscop_ns; + AAL_UU_SIGNAL(sscop, SSCOP_RESYNC_indication, msg, pdu.sscop_pl, 0); + m_prepare_retrieval(sscop); + + sscop_set_state(sscop, SSCOP_IN_RESYNC_PEND); +} + +/* + * p 62: READY && END PDU + * arg is pdu (freed) + */ +static void +sscop_ready_end(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + (void)MBUF_STRIP32(msg->m); + + m_reset_data_xfer_timers(sscop); + send_endak(sscop); + AAL_UU_SIGNAL(sscop, SSCOP_RELEASE_indication, + msg, pdu.sscop_pl, (u_int)pdu.sscop_s); + m_prepare_retrieval(sscop); + + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 63: READY && POLL expiry + */ +static void +sscop_ready_tpoll(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + sscop->vt_ps++; + send_poll(sscop); + sscop->vt_pd = 0; + m_set_poll_timer(sscop); +} + +/* + * p 63: READY && KEEP_ALIVE expiry + */ +static void +sscop_ready_tka(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + sscop->vt_ps++; + send_poll(sscop); + sscop->vt_pd = 0; + m_set_poll_timer(sscop); +} + +/* + * p 63: READY && IDLE expiry + */ +static void +sscop_ready_tidle(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + TIMER_RESTART(sscop, nr); + sscop->vt_ps++; + send_poll(sscop); + sscop->vt_pd = 0; + m_set_poll_timer(sscop); +} + +/* + * p 63: READY && NO_RESPONSE expiry + * no arg + */ +static void +sscop_ready_nr(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + m_reset_data_xfer_timers(sscop); + MAAL_ERROR(sscop, 'P', 0); + FREE_UU(uu_end); + send_end(sscop, 1, NULL); + AAL_DATA(sscop, SSCOP_RELEASE_indication, NULL, 1); + m_prepare_retrieval(sscop); + sscop_set_state(sscop, SSCOP_IDLE); +} + +/* + * p 63: READY && AA-DATA.request + * arg is message (queued). + */ +static void +sscop_ready_userdata(struct sscop *sscop, struct sscop_msg *msg) +{ + MSGQ_APPEND(&sscop->xq, msg); + + sscop_signal(sscop, SIG_PDU_Q, msg); +} + +/* + * p 64: READY && SD PDU queued up + * arg is unused. + */ +static void +sscop_ready_pduq(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + struct sscop_msg *msg; + + if(sscop->rxq != 0) { + TAILQ_FOREACH(msg, &sscop->xbuf, link) + if(msg->rexmit) + break; + ASSERT(msg != NULL); + msg->rexmit = 0; + sscop->rxq--; + send_sd(sscop, msg->m, msg->seqno); + msg->poll_seqno = sscop->vt_ps; + if(sscop->poll_after_rex && sscop->rxq == 0) + goto poll; /* -> A */ + else + goto maybe_poll; /* -> B */ + + } + if(MSGQ_EMPTY(&sscop->xq)) + return; + + if(sscop->vt_s >= sscop->vt_ms) { + /* Send windows closed */ + TIMER_STOP(sscop, idle); + TIMER_RESTART(sscop, nr); + goto poll; /* -> A */ + + } else { + msg = MSGQ_GET(&sscop->xq); + msg->seqno = sscop->vt_s; + send_sd(sscop, msg->m, msg->seqno); + msg->poll_seqno = sscop->vt_ps; + sscop->vt_s++; + MSGQ_APPEND(&sscop->xbuf, msg); + goto maybe_poll; /* -> B */ + } + + /* + * p 65: Poll handling + */ + maybe_poll: /* label B */ + sscop->vt_pd++; + if(TIMER_ISACT(sscop, poll)) { + if(sscop->vt_pd < sscop->maxpd) + return; + } else { + if(TIMER_ISACT(sscop, idle)) { + TIMER_STOP(sscop, idle); + TIMER_RESTART(sscop, nr); + } else { + TIMER_STOP(sscop, ka); + } + if(sscop->vt_pd < sscop->maxpd) { + TIMER_RESTART(sscop, poll); + return; + } + } + poll: /* label A */ + sscop->vt_ps++; + send_poll(sscop); + sscop->vt_pd = 0; + TIMER_RESTART(sscop, poll); +} + +/* + * p 67: common recovery start + */ +static void +sscop_recover(struct sscop *sscop) +{ + sscop->vt_cc = 1; + sscop->vt_sq++; + + m_initialize_mr(sscop); + send_er(sscop); + m_prepare_recovery(sscop); + + TIMER_RESTART(sscop, cc); + + sscop_set_state(sscop, SSCOP_OUT_REC_PEND); +} + +/* + * p 66: READY && SD PDU + * arg is received message. + */ +static void +sscop_ready_sd(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + u_int sn; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + msg->seqno = pdu.sscop_ns; + + /* Fix padding */ + MBUF_UNPAD(msg->m, pdu.sscop_pl); + + if(msg->seqno >= sscop->vr_mr) { + /* message outside window */ + if(sscop->vr_h < sscop->vr_mr) { + send_ustat(sscop, sscop->vr_h, sscop->vr_mr, -1); + sscop->vr_h = sscop->vr_mr; + } + SSCOP_MSG_FREE(msg); + return; + } + + if(msg->seqno == sscop->vr_r) { + if(msg->seqno == sscop->vr_h) { + sscop->vr_r = msg->seqno + 1; + sscop->vr_h = msg->seqno + 1; + + AAL_DATA(sscop, SSCOP_DATA_indication, + msg->m, msg->seqno); + msg->m = NULL; + SSCOP_MSG_FREE(msg); + + return; + } + for(;;) { + AAL_DATA(sscop, SSCOP_DATA_indication, + msg->m, msg->seqno); + msg->m = NULL; + SSCOP_MSG_FREE(msg); + + sscop->vr_r++; + if((msg = MSGQ_PEEK(&sscop->rbuf)) == NULL) + break; + sn = msg->seqno; + ASSERT(sn >= sscop->vr_r); + if(sn != sscop->vr_r) + break; + msg = MSGQ_GET(&sscop->rbuf); + } + return; + } + + /* Messages were lost */ + + /* XXX Flow control */ + if(msg->seqno == sscop->vr_h) { + QINSERT(&sscop->rbuf, msg); + sscop->vr_h++; + return; + } + if(sscop->vr_h < msg->seqno) { + QINSERT(&sscop->rbuf, msg); + send_ustat(sscop, sscop->vr_h, msg->seqno, -1); + sscop->vr_h = msg->seqno + 1; + return; + } + + if(QFIND(&sscop->rbuf, msg->seqno) == NULL) { + QINSERT(&sscop->rbuf, msg); + return; + } + + /* error: start recovery */ + SSCOP_MSG_FREE(msg); + m_reset_data_xfer_timers(sscop); + MAAL_ERROR(sscop, 'Q', 0); + sscop_recover(sscop); +} + +/* + * p 67: READY && POLL PDU + */ +static void +sscop_ready_poll(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + union seqno seqno; + u_int sn, nps; + struct SSCOP_MBUF_T *m; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + seqno.sscop_null = MBUF_STRIP32(msg->m); + + if((u_int)pdu.sscop_ns < sscop->vr_h) { + SSCOP_MSG_FREE(msg); + m_reset_data_xfer_timers(sscop); + MAAL_ERROR(sscop, 'Q', 0); + sscop_recover(sscop); + return; + } + nps = seqno.sscop_n; + + if((u_int)pdu.sscop_ns > sscop->vr_mr) + sscop->vr_h = sscop->vr_mr; + else + sscop->vr_h = pdu.sscop_ns; + + SSCOP_MSG_FREE(msg); + + /* build stat pdu */ + if((m = MBUF_ALLOC(sscop->maxstat * 4 + 12)) == NULL) { + FAILURE("sscop: cannot allocate STAT"); + return; + } + sn = sscop->vr_r; + + while(sn != sscop->vr_h) { + /* loop through burst we already have */ + for(;;) { + if(sn >= sscop->vr_h) { + seqno.sscop_null = 0; + seqno.sscop_n = sn; + MBUF_APPEND32(m, seqno.sscop_null); + goto out; + } + if(QFIND(&sscop->rbuf, sn) == NULL) + break; + sn++; + } + + /* start of a hole */ + seqno.sscop_null = 0; + seqno.sscop_n = sn; + MBUF_APPEND32(m, seqno.sscop_null); + if(MBUF_LEN(m)/4 >= sscop->maxstat) { + send_stat(sscop, nps, m); + if((m = MBUF_ALLOC(sscop->maxstat * 4 + 12)) == NULL) { + FAILURE("sscop: cannot allocate STAT"); + return; + } + seqno.sscop_null = 0; + seqno.sscop_n = sn; + MBUF_APPEND32(m, seqno.sscop_null); + } + do { + sn++; + } while(sn < sscop->vr_h && !QFIND(&sscop->rbuf, sn)); + seqno.sscop_null = 0; + seqno.sscop_n = sn; + MBUF_APPEND32(m, seqno.sscop_null); + } + out: + send_stat(sscop, nps, m); +} + +/* + * p 69: READY && USTAT PDU + * arg is msg (freed) + */ +static void +sscop_ready_ustat(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + union seqno nmr, sq1, sq2; + u_int cnt; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + nmr.sscop_null = MBUF_STRIP32(msg->m); + sq2.sscop_null = MBUF_STRIP32(msg->m); + sq1.sscop_null = MBUF_STRIP32(msg->m); + + SSCOP_MSG_FREE(msg); + + cnt = sq1.sscop_n - sq2.sscop_n; + + if((u_int)pdu.sscop_ns < sscop->vt_a || (u_int)pdu.sscop_ns >= sscop->vt_s) { + VERBERR(sscop, SSCOP_DBG_ERR, (sscop, sscop->aarg, + "USTAT: N(R) outside VT(A)...VT(S)-1: N(R)=%u VT(A)=%u " + "VT(S)=%u", (u_int)pdu.sscop_ns, sscop->vt_a, sscop->vt_s)); + goto err_f; + } + + /* Acknowledge all messages between VT(A) and N(R)-1. N(R) is the new + * next in sequence-SD-number of the receiver and means, it has all + * messages below N(R). Remove all message below N(R) from the + * transmission buffer. It may already be removed because of an + * earlier selective ACK in a STAT message. + */ + while((msg = MSGQ_PEEK(&sscop->xbuf)) != NULL && msg->seqno < (u_int)pdu.sscop_ns) { + ASSERT(msg->seqno >= sscop->vt_a); + MSGQ_REMOVE(&sscop->xbuf, msg); + SSCOP_MSG_FREE(msg); + } + + /* Update the in-sequence acknowledge and the send window */ + sscop->vt_a = pdu.sscop_ns; + sscop->vt_ms = nmr.sscop_n; + + /* check, that the range of requested re-transmissions is between + * the in-sequence-ack and the highest up-to-now transmitted SD + */ + if(sq1.sscop_n >= sq2.sscop_n + || (u_int)sq1.sscop_n < sscop->vt_a + || (u_int)sq2.sscop_n >= sscop->vt_s) { + VERBERR(sscop, SSCOP_DBG_ERR, (sscop, sscop->aarg, + "USTAT: seq1 or seq2 outside VT(A)...VT(S)-1 or seq1>=seq2:" + " seq1=%u seq2=%u VT(A)=%u VT(S)=%u", + sq1.sscop_n, sq2.sscop_n, sscop->vt_a, sscop->vt_s)); + goto err_f; + } + + /* + * Retransmit all messages from seq1 to seq2-1 + */ + do { + /* + * The message may not be in the transmit buffer if it was + * already acked by a STAT. This means, the receiver is + * confused. + */ + if((msg = QFIND(&sscop->xbuf, sq1.sscop_n)) == NULL) { + VERBERR(sscop, SSCOP_DBG_ERR, (sscop, sscop->aarg, + "USTAT: message %u not found in xmit buffer", + sq1.sscop_n)); + goto err_f; + } + + /* + * If it is not yet in the re-transmission queue, put it there + */ + if(!msg->rexmit) { + msg->rexmit = 1; + sscop->rxq++; + sscop_signal(sscop, SIG_PDU_Q, msg); + } + sq1.sscop_n++; + } while(sq1.sscop_n != sq2.sscop_n); + + /* + * report the re-transmission to the management + */ + MAAL_ERROR(sscop, 'V', cnt); + return; + + err_f: + m_reset_data_xfer_timers(sscop); + MAAL_ERROR(sscop, 'T', 0); + sscop_recover(sscop); +} + +/* + * p 70: READY && STAT PDU + * arg is msg (freed). + */ +static void +sscop_ready_stat(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + union seqno nps, nmr; + u_int len, seq1, seq2, cnt; + struct sscop_msg *m; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + nmr.sscop_null = MBUF_STRIP32(msg->m); + nps.sscop_null = MBUF_STRIP32(msg->m); + + len = MBUF_LEN(msg->m) / 4; + + if((u_int)nps.sscop_n < sscop->vt_pa + || (u_int)nps.sscop_n > sscop->vt_ps) { + SSCOP_MSG_FREE(msg); + m_reset_data_xfer_timers(sscop); + MAAL_ERROR(sscop, 'R', 0); + sscop_recover(sscop); + return; + } + + if((u_int)pdu.sscop_ns < sscop->vt_a + || (u_int)pdu.sscop_ns > sscop->vt_s) { + /* + * The in-sequence acknowledge, i.e. the receivers's next + * expected in-sequence msg is outside the window between + * the transmitters in-sequence ack and highest seqno - + * the receiver seems to be confused. + */ + VERBERR(sscop, SSCOP_DBG_ERR, (sscop, sscop->aarg, + "STAT: N(R) outside VT(A)...VT(S)-1: N(R)=%u VT(A)=%u " + "VT(S)=%u", (u_int)pdu.sscop_ns, sscop->vt_a, sscop->vt_s)); + err_H: + SSCOP_MSG_FREE(msg); + m_reset_data_xfer_timers(sscop); + MAAL_ERROR(sscop, 'S', 0); + sscop_recover(sscop); + return; + } + + /* Acknowledge all messages between VT(A) and N(R)-1. N(R) is the new + * next in sequence-SD-number of the receiver and means, it has all + * messages below N(R). Remove all message below N(R) from the + * transmission buffer. It may already be removed because of an + * earlier selective ACK in a STAT message. + */ + while((m = MSGQ_PEEK(&sscop->xbuf)) != NULL + && m->seqno < (u_int)pdu.sscop_ns) { + ASSERT(m->seqno >= sscop->vt_a); + MSGQ_REMOVE(&sscop->xbuf, m); + SSCOP_MSG_FREE(m); + } + + /* + * Update in-sequence ack, poll-ack and send window. + */ + sscop->vt_a = pdu.sscop_ns; + sscop->vt_pa = nps.sscop_n; + sscop->vt_ms = nmr.sscop_n; + + cnt = 0; + if(len > 1) { + seq1 = MBUF_GET32(msg->m); + len--; + if(seq1 >= sscop->vt_s) { + VERBERR(sscop, SSCOP_DBG_ERR, (sscop, sscop->aarg, + "STAT: seq1 >= VT(S): seq1=%u VT(S)=%u", + seq1, sscop->vt_s)); + goto err_H; + } + + for(;;) { + seq2 = MBUF_GET32(msg->m); + len--; + if(seq1 >= seq2 || seq2 > sscop->vt_s) { + VERBERR(sscop, SSCOP_DBG_ERR, (sscop, + sscop->aarg, "STAT: seq1 >= seq2 or " + "seq2 > VT(S): seq1=%u seq2=%u VT(S)=%u", + seq1, seq2, sscop->vt_s)); + goto err_H; + } + + do { + /* + * The receiver requests the re-transmission + * of some message, but has acknowledged it + * already in an earlier STAT (it isn't in the + * transmitt buffer anymore). + */ + if((m = QFIND(&sscop->xbuf, seq1)) == NULL) { + VERBERR(sscop, SSCOP_DBG_ERR, + (sscop, sscop->aarg, "STAT: message" + " %u not found in xmit buffer", + seq1)); + goto err_H; + } + if(m->poll_seqno < (u_int)nps.sscop_n + && (u_int)nps.sscop_n <= sscop->vt_ps) + if(!m->rexmit) { + m->rexmit = 1; + sscop->rxq++; + cnt++; + sscop_signal(sscop, SIG_PDU_Q, msg); + } + } while(++seq1 < seq2); + + if(len == 0) + break; + + seq2 = MBUF_GET32(msg->m); + len--; + + if(seq1 >= seq2 || seq2 > sscop->vt_s) { + VERBERR(sscop, SSCOP_DBG_ERR, (sscop, + sscop->aarg, "STAT: seq1 >= seq2 or " + "seq2 > VT(S): seq1=%u seq2=%u VT(S)=%u", + seq1, seq2, sscop->vt_s)); + goto err_H; + } + + /* OK now the sucessful transmitted messages. Note, that + * some messages may already be out of the buffer because + * of earlier STATS */ + do { + if(sscop->clear_buffers) { + if((m = QFIND(&sscop->xbuf, seq1)) != NULL) { + MSGQ_REMOVE(&sscop->xbuf, m); + SSCOP_MSG_FREE(m); + } + } + } while(++seq1 != seq2); + + if(len == 0) + break; + } + MAAL_ERROR(sscop, 'V', cnt); + } + SSCOP_MSG_FREE(msg); + + /* label L: */ + if(sscop->vt_s >= sscop->vt_ms) { + /* + * The receiver has closed the window: report to management + */ + if(sscop->credit) { + sscop->credit = 0; + MAAL_ERROR(sscop, 'W', 0); + } + } else if(!sscop->credit) { + /* + * The window was forcefully closed above, but + * now re-opened. Report to management. + */ + sscop->credit = 1; + MAAL_ERROR(sscop, 'X', 0); + } + + if(TIMER_ISACT(sscop, poll)) { + TIMER_RESTART(sscop, nr); + } else if(!TIMER_ISACT(sscop, idle)) { + TIMER_STOP(sscop, ka); + TIMER_STOP(sscop, nr); + TIMER_RESTART(sscop, idle); + } +} + +/* + * P. 73: any state & UDATA_REQUEST + * arg is pdu (queued) + */ +static void +sscop_udata_req(struct sscop *sscop, struct sscop_msg *msg) +{ + MSGQ_APPEND(&sscop->uxq, msg); + sscop_signal(sscop, SIG_UPDU_Q, msg); +} + +/* + * P. 73: any state & MDATA_REQUEST + * arg is pdu (queued) + */ +static void +sscop_mdata_req(struct sscop *sscop, struct sscop_msg *msg) +{ + MSGQ_APPEND(&sscop->mxq, msg); + sscop_signal(sscop, SIG_MPDU_Q, msg); +} + +/* + * P. 74: any state & UDATA queued + * no arg. + */ +static void +sscop_upduq(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + struct sscop_msg *msg; + + if(sscop->ll_busy) + return; + while((msg = MSGQ_GET(&sscop->uxq)) != NULL) { + send_ud(sscop, msg->m); + msg->m = NULL; + SSCOP_MSG_FREE(msg); + } +} + +/* + * P. 74: any state & MDATA queued + * no arg. + */ +static void +sscop_mpduq(struct sscop *sscop, struct sscop_msg *unused __unused) +{ + struct sscop_msg *msg; + + if(sscop->ll_busy) + return; + while((msg = MSGQ_GET(&sscop->mxq)) != NULL) { + send_md(sscop, msg->m); + msg->m = NULL; + SSCOP_MSG_FREE(msg); + } +} + +/* + * p 73: MD PDU + * arg is PDU + */ +static void +sscop_md(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + MBUF_UNPAD(msg->m, pdu.sscop_pl); + + MAAL_DATA(sscop, msg->m); + msg->m = NULL; + SSCOP_MSG_FREE(msg); +} + +/* + * p 73: UD PDU + * arg is PDU + */ +static void +sscop_ud(struct sscop *sscop, struct sscop_msg *msg) +{ + union pdu pdu; + + pdu.sscop_null = MBUF_STRIP32(msg->m); + + MBUF_UNPAD(msg->m, pdu.sscop_pl); + + AAL_DATA(sscop, SSCOP_UDATA_indication, msg->m, 0); + msg->m = NULL; + SSCOP_MSG_FREE(msg); +} + + +/* + * p 33: IDLE & RETRIEVE + * p 39: IN_PEND & RETRIEVE + * p 42: OUT_DIS_PEND & RETRIEVE + * p 48: IN_RESYNC_PEND & RETRIEVE + * p 53: REC_PEND & RETRIEVE + * p 58: IN_REC_PEND & RETRIEVE + */ +static void +sscop_retrieve(struct sscop *sscop, struct sscop_msg *msg) +{ + m_data_retrieval(sscop, msg->rexmit); + SSCOP_MSG_FREE(msg); +} + +/************************************************************/ +/* + * GENERAL EVENT HANDLING + */ + +/* + * State/event matrix. + * + * Entries marked with Z are not specified in Q.2110, but are added for + * the sake of stability. + */ +static struct { + void (*func)(struct sscop *, struct sscop_msg *); + int (*cond)(struct sscop *); +} state_matrix[SSCOP_NSTATES][SIG_NUM] = { + /* SSCOP_IDLE */ { + /* SIG_BGN */ { sscop_idle_bgn, NULL }, + /* SIG_BGAK */ { sscop_idle_bgak, NULL }, + /* SIG_END */ { sscop_idle_end, NULL }, + /* SIG_ENDAK */ { sscop_ignore_pdu, NULL }, + /* SIG_RS */ { sscop_idle_rs, NULL }, + /* SIG_RSAK */ { sscop_idle_rsak, NULL }, + /* SIG_BGREJ */ { sscop_idle_bgrej, NULL }, + /* SIG_SD */ { sscop_idle_sd, NULL }, + /* SIG_ER */ { sscop_idle_er, NULL }, + /* SIG_POLL */ { sscop_idle_poll, NULL }, + /* SIG_STAT */ { sscop_idle_stat, NULL }, + /* SIG_USTAT */ { sscop_idle_ustat, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_idle_erak, NULL }, + /* SIG_T_CC */ { NULL, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_flush_pduq, NULL }, + /* SIG_USER_DATA */ { NULL, NULL }, + /* SIG_ESTAB_REQ */ { sscop_idle_establish_req, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { NULL, NULL }, + /* SIG_RECOVER */ { NULL, NULL }, + /* SIG_SYNC_REQ */ { NULL, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { sscop_retrieve, NULL }, + }, + /* SSCOP_OUT_PEND */ { + /* SIG_BGN */ { sscop_outpend_bgn, NULL }, + /* SIG_BGAK */ { sscop_outpend_bgak, NULL }, + /* SIG_END */ { sscop_ignore_pdu, NULL }, + /* SIG_ENDAK */ { sscop_ignore_pdu, NULL }, + /* SIG_RS */ { sscop_ignore_pdu, NULL }, + /* SIG_RSAK */ { sscop_ignore_pdu, NULL }, + /* SIG_BGREJ */ { sscop_outpend_bgrej, NULL }, + /* SIG_SD */ { sscop_ignore_pdu, NULL }, + /* SIG_ER */ { sscop_ignore_pdu, NULL }, + /* SIG_POLL */ { sscop_ignore_pdu, NULL }, + /* SIG_STAT */ { sscop_ignore_pdu, NULL }, + /* SIG_USTAT */ { sscop_ignore_pdu, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_ignore_pdu, NULL }, + /* SIG_T_CC */ { sscop_outpend_tcc, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_flush_pduq, NULL }, + /* SIG_USER_DATA */ { NULL, NULL }, + /* SIG_ESTAB_REQ */ { NULL, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { sscop_outpend_release_req, NULL }, + /* SIG_RECOVER */ { NULL, NULL }, + /* SIG_SYNC_REQ */ { NULL, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { NULL, NULL }, + }, + /* SSCOP_IN_PEND */ { + /* SIG_BGN */ { sscop_inpend_bgn, NULL }, + /* SIG_BGAK */ { sscop_inpend_bgak, NULL }, + /* SIG_END */ { sscop_inpend_end, NULL }, + /* SIG_ENDAK */ { sscop_inpend_endak, NULL }, + /* SIG_RS */ { sscop_inpend_rs, NULL }, + /* SIG_RSAK */ { sscop_inpend_rsak, NULL }, + /* SIG_BGREJ */ { sscop_inpend_bgrej, NULL }, + /* SIG_SD */ { sscop_inpend_sd, NULL }, + /* SIG_ER */ { sscop_inpend_er, NULL }, + /* SIG_POLL */ { sscop_inpend_poll, NULL }, + /* SIG_STAT */ { sscop_inpend_stat, NULL }, + /* SIG_USTAT */ { sscop_inpend_ustat, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_inpend_erak, NULL }, + /* SIG_T_CC */ { NULL, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_flush_pduq, NULL }, + /* SIG_USER_DATA */ { NULL, NULL }, + /* SIG_ESTAB_REQ */ { NULL, NULL }, + /* SIG_ESTAB_RESP */ { sscop_inpend_establish_resp, NULL }, + /* SIG_RELEASE_REQ */ { sscop_inpend_release_req, NULL }, + /* SIG_RECOVER */ { NULL, NULL }, + /* SIG_SYNC_REQ */ { NULL, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { sscop_retrieve, NULL }, + }, + /* SSCOP_OUT_DIS_PEND */ { + /* SIG_BGN */ { sscop_outdis_bgn, NULL }, + /* SIG_BGAK */ { sscop_ignore_pdu, NULL }, + /* SIG_END */ { sscop_outdis_end, NULL }, + /* SIG_ENDAK */ { sscop_outdis_endak, NULL }, + /* SIG_RS */ { sscop_ignore_pdu, NULL }, + /* SIG_RSAK */ { sscop_ignore_pdu, NULL }, + /* SIG_BGREJ */ { sscop_outdis_endak, NULL }, + /* SIG_SD */ { sscop_ignore_pdu, NULL }, + /* SIG_ER */ { sscop_ignore_pdu, NULL }, + /* SIG_POLL */ { sscop_ignore_pdu, NULL }, + /* SIG_STAT */ { sscop_ignore_pdu, NULL }, + /* SIG_USTAT */ { sscop_ignore_pdu, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_ignore_pdu, NULL }, + /* SIG_T_CC */ { sscop_outdis_cc, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_flush_pduq, NULL }, + /* SIG_USER_DATA */ { NULL, NULL }, + /* SIG_ESTAB_REQ */ { sscop_outdis_establish_req, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { NULL, NULL }, + /* SIG_RECOVER */ { NULL, NULL }, + /* SIG_SYNC_REQ */ { NULL, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { sscop_retrieve, NULL }, + }, + /* SSCOP_OUT_RESYNC_PEND */ { + /* SIG_BGN */ { sscop_outsync_bgn, NULL }, + /* SIG_BGAK */ { sscop_ignore_pdu, NULL }, + /* SIG_END */ { sscop_outsync_end, NULL }, + /* SIG_ENDAK */ { sscop_outsync_endak, NULL }, + /* SIG_RS */ { sscop_outsync_rs, NULL }, + /* SIG_RSAK */ { sscop_outsync_rsak, NULL }, + /* SIG_BGREJ */ { sscop_outsync_bgrej, NULL }, + /* SIG_SD */ { sscop_ignore_pdu, NULL }, + /* SIG_ER */ { sscop_ignore_pdu, NULL }, + /* SIG_POLL */ { sscop_ignore_pdu, NULL }, + /* SIG_STAT */ { sscop_ignore_pdu, NULL }, + /* SIG_USTAT */ { sscop_ignore_pdu, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_ignore_pdu, NULL }, + /* SIG_T_CC */ { sscop_outsync_cc, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_flush_pduq, NULL }, + /* SIG_USER_DATA */ { NULL, NULL }, + /* SIG_ESTAB_REQ */ { NULL, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { sscop_outsync_release_req, NULL }, + /* SIG_RECOVER */ { NULL, NULL }, + /* SIG_SYNC_REQ */ { NULL, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { NULL, NULL }, + }, + /* SSCOP_IN_RESYNC_PEND */ { + /* SIG_BGN */ { sscop_insync_bgn, NULL }, + /* SIG_BGAK */ { sscop_insync_bgak, NULL }, + /* SIG_END */ { sscop_insync_end, NULL }, + /* SIG_ENDAK */ { sscop_insync_endak, NULL }, + /* SIG_RS */ { sscop_insync_rs, NULL }, + /* SIG_RSAK */ { sscop_insync_rsak, NULL }, + /* SIG_BGREJ */ { sscop_insync_bgrej, NULL }, + /* SIG_SD */ { sscop_insync_sd, NULL }, + /* SIG_ER */ { sscop_insync_er, NULL }, + /* SIG_POLL */ { sscop_insync_poll, NULL }, + /* SIG_STAT */ { sscop_insync_stat, NULL }, + /* SIG_USTAT */ { sscop_insync_ustat, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_insync_erak, NULL }, + /* SIG_T_CC */ { NULL, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_flush_pduq, NULL }, + /* SIG_USER_DATA */ { NULL, NULL }, + /* SIG_ESTAB_REQ */ { NULL, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { sscop_insync_release_req, NULL }, + /* SIG_RECOVER */ { NULL, NULL }, + /* SIG_SYNC_REQ */ { NULL, NULL }, + /* SIG_SYNC_RESP */ { sscop_insync_sync_resp, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { sscop_retrieve, NULL }, + }, + /* SSCOP_OUT_REC_PEND */ { + /* SIG_BGN */ { sscop_outrec_bgn, NULL }, + /* SIG_BGAK */ { sscop_outrec_bgak, NULL }, + /* SIG_END */ { sscop_outrec_end, NULL }, + /* SIG_ENDAK */ { sscop_outrec_endak, NULL }, + /* SIG_RS */ { sscop_outrec_rs, NULL }, + /* SIG_RSAK */ { sscop_outrec_rsak, NULL }, + /* SIG_BGREJ */ { sscop_outrec_bgrej, NULL }, + /* SIG_SD */ { sscop_ignore_pdu, NULL }, + /* SIG_ER */ { sscop_outrec_er, NULL }, + /* SIG_POLL */ { sscop_ignore_pdu, NULL }, + /* SIG_STAT */ { sscop_ignore_pdu, NULL }, + /* SIG_USTAT */ { sscop_ignore_pdu, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_outrec_erak, NULL }, + /* SIG_T_CC */ { sscop_outrec_cc, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_outrec_pduq, NULL }, + /* SIG_USER_DATA */ { sscop_outrec_userdata, NULL }, + /* SIG_ESTAB_REQ */ { NULL, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { sscop_outrec_release_req, NULL }, + /* SIG_RECOVER */ { NULL, NULL }, + /* SIG_SYNC_REQ */ { sscop_outrec_sync_req, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { NULL, NULL }, + }, + /* SSCOP_REC_PEND */ { + /* SIG_BGN */ { sscop_rec_bgn, NULL }, + /* SIG_BGAK */ { sscop_rec_bgak, NULL }, + /* SIG_END */ { sscop_rec_end, NULL }, + /* SIG_ENDAK */ { sscop_rec_endak, NULL }, + /* SIG_RS */ { sscop_rec_rs, NULL }, + /* SIG_RSAK */ { sscop_rec_rsak, NULL }, + /* SIG_BGREJ */ { sscop_rec_bgrej, NULL }, + /* SIG_SD */ { sscop_ignore_pdu, NULL }, + /* SIG_ER */ { sscop_rec_er, NULL }, + /* SIG_POLL */ { sscop_ignore_pdu, NULL }, + /* SIG_STAT */ { sscop_rec_stat, NULL }, + /* SIG_USTAT */ { sscop_rec_ustat, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_ignore_pdu, NULL }, + /* SIG_T_CC */ { NULL, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_rec_pduq, NULL }, + /* SIG_USER_DATA */ { NULL, NULL }, + /* SIG_ESTAB_REQ */ { NULL, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { sscop_rec_release_req, NULL }, + /* SIG_RECOVER */ { sscop_rec_recover, NULL }, + /* SIG_SYNC_REQ */ { sscop_rec_sync_req, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { sscop_retrieve, NULL }, + }, + /* SSCOP_IN_REC_PEND */ { + /* SIG_BGN */ { sscop_inrec_bgn, NULL }, + /* SIG_BGAK */ { sscop_inrec_bgak, NULL }, + /* SIG_END */ { sscop_inrec_end, NULL }, + /* SIG_ENDAK */ { sscop_inrec_endak, NULL }, + /* SIG_RS */ { sscop_inrec_rs, NULL }, + /* SIG_RSAK */ { sscop_inrec_rsak, NULL }, + /* SIG_BGREJ */ { sscop_inrec_bgrej, NULL }, + /* SIG_SD */ { sscop_inrec_sd, NULL }, + /* SIG_ER */ { sscop_inrec_er, NULL }, + /* SIG_POLL */ { sscop_inrec_poll, NULL }, + /* SIG_STAT */ { sscop_inrec_stat, NULL }, + /* SIG_USTAT */ { sscop_inrec_ustat, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_inrec_erak, NULL }, + /* SIG_T_CC */ { NULL, NULL }, + /* SIG_T_POLL */ { NULL, NULL }, + /* SIG_T_KA */ { NULL, NULL }, + /* SIG_T_NR */ { NULL, NULL }, + /* SIG_T_IDLE */ { NULL, NULL }, + /* SIG_PDU_Q */ { sscop_inrec_pduq, NULL }, + /* SIG_USER_DATA */ { NULL, NULL }, + /* SIG_ESTAB_REQ */ { NULL, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { sscop_inrec_release_req, NULL }, + /* SIG_RECOVER */ { sscop_inrec_recover, NULL }, + /* SIG_SYNC_REQ */ { sscop_inrec_sync_req, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { sscop_retrieve, NULL }, + }, + /* SSCOP_READY */ { + /* SIG_BGN */ { sscop_ready_bgn, NULL }, + /* SIG_BGAK */ { sscop_ignore_pdu, NULL }, + /* SIG_END */ { sscop_ready_end, NULL }, + /* SIG_ENDAK */ { sscop_ready_endak, NULL }, + /* SIG_RS */ { sscop_ready_rs, NULL }, + /* SIG_RSAK */ { sscop_ignore_pdu, NULL }, + /* SIG_BGREJ */ { sscop_ready_bgrej, NULL }, + /* SIG_SD */ { sscop_ready_sd, NULL }, + /* SIG_ER */ { sscop_ready_er, NULL }, + /* SIG_POLL */ { sscop_ready_poll, NULL }, + /* SIG_STAT */ { sscop_ready_stat, NULL }, + /* SIG_USTAT */ { sscop_ready_ustat, NULL }, + /* SIG_UD */ { sscop_ud, NULL }, + /* SIG_MD */ { sscop_md, NULL }, + /* SIG_ERAK */ { sscop_ignore_pdu, NULL }, + /* SIG_T_CC */ { NULL, NULL }, + /* SIG_T_POLL */ { sscop_ready_tpoll, NULL }, + /* SIG_T_KA */ { sscop_ready_tka, NULL }, + /* SIG_T_NR */ { sscop_ready_nr, NULL }, + /* SIG_T_IDLE */ { sscop_ready_tidle, NULL }, + /* SIG_PDU_Q */ { sscop_ready_pduq, c_ready_pduq }, + /* SIG_USER_DATA */ { sscop_ready_userdata, NULL }, + /* SIG_ESTAB_REQ */ { NULL, NULL }, + /* SIG_ESTAB_RESP */ { NULL, NULL }, + /* SIG_RELEASE_REQ */ { sscop_ready_release_req, NULL }, + /* SIG_RECOVER */ { NULL, NULL }, + /* SIG_SYNC_REQ */ { sscop_ready_sync_req, NULL }, + /* SIG_SYNC_RESP */ { NULL, NULL }, + /* SIG_UDATA */ { sscop_udata_req, NULL }, + /* SIG_MDATA */ { sscop_mdata_req, NULL }, + /* SIG_UPDU_Q */ { sscop_upduq, NULL }, + /* SIG_MPDU_Q */ { sscop_mpduq, NULL }, + /* SIG_RETRIEVE */ { NULL, NULL }, + } +}; + +/* + * Try to execute a signal. It is executed if + * - it is illegal (in this case it is effectively ignored) + * - it has no condition + * - its condition is true + * If it has a condition and that is false, the function does nothing and + * returns 0. + * If the signal gets executed, the signal function is responsible to release + * the message (if any). + */ +static int +sig_exec(struct sscop *sscop, u_int sig, struct sscop_msg *msg) +{ + void (*func)(struct sscop *, struct sscop_msg *); + int (*cond)(struct sscop *); + + func = state_matrix[sscop->state][sig].func; + cond = state_matrix[sscop->state][sig].cond; + + if(func == NULL) { + VERBOSE(sscop, SSCOP_DBG_BUG, (sscop, sscop->aarg, + "no handler for %s in state %s - ignored", + events[sig], states[sscop->state])); + SSCOP_MSG_FREE(msg); + return 1; + } + if(cond == NULL || (*cond)(sscop)) { + VERBOSE(sscop, SSCOP_DBG_EXEC, (sscop, sscop->aarg, + "executing %s in %s", events[sig], + states[sscop->state])); + (*func)(sscop, msg); + return 1; + } + VERBOSE(sscop, SSCOP_DBG_EXEC, (sscop, sscop->aarg, + "delaying %s in %s", events[sig], + states[sscop->state])); + + return 0; +} + +/* + * Deliver a signal to the given sscop + * If it is delivered from inside a signal handler - queue it. If not, + * execute it. After execution loop through the queue and execute all + * pending signals. Signals, that cannot be executed because of entry + * conditions are skipped. + */ +static void +sscop_signal(struct sscop *sscop, u_int sig, struct sscop_msg *msg) +{ + struct sscop_sig *s; + + VERBOSE(sscop, SSCOP_DBG_INSIG, (sscop, sscop->aarg, + "got signal %s in state %s%s", events[sig], + states[sscop->state], sscop->in_sig ? " -- queuing" : "")); + + SIG_ALLOC(s); + if(s == NULL) { + FAILURE("sscop: cannot allocate signal"); + SSCOP_MSG_FREE(msg); + return; + } + s->sig = sig; + s->msg = msg; + SIGQ_APPEND(&sscop->sigs, s); + + if(!sscop->in_sig) + handle_sigs(sscop); +} + +/* + * Loop through the signal queue until we can't execute any signals. + */ +static void +handle_sigs(struct sscop *sscop) +{ + struct sscop_sig *s; + sscop_sigq_head_t dsigs, q; + int exec; + + sscop->in_sig++; + + /* + * Copy the current signal queue to the local one and empty + * the signal queue. Then loop through the signals. After one + * pass we have a list of delayed signals because of entry + * conditions and a new list of signals. Merge them. Repeat until + * the signal queue is either empty or contains only delayed signals. + */ + SIGQ_INIT(&q); + SIGQ_INIT(&dsigs); + do { + exec = 0; + + /* + * Copy signal list and make sscop list empty + */ + SIGQ_MOVE(&sscop->sigs, &q); + + /* + * Loop through the list + */ + while((s = SIGQ_GET(&q)) != NULL) { + if(sig_exec(sscop, s->sig, s->msg)) { + exec = 1; + SIG_FREE(s); + } else { + SIGQ_APPEND(&dsigs, s); + } + } + + /* + * Merge lists by inserting delayed signals in front of + * the signal list. preserving the order. + */ + SIGQ_PREPEND(&dsigs, &sscop->sigs); + } while(exec); + sscop->in_sig--; +} + +/* + * Save a signal that should be executed only if state changes. + */ +static void +sscop_save_signal(struct sscop *sscop, u_int sig, struct sscop_msg *msg) +{ + struct sscop_sig *s; + + SIG_ALLOC(s); + if(s == NULL) { + FAILURE("sscop: cannot allocate signal"); + SSCOP_MSG_FREE(msg); + return; + } + s->sig = sig; + s->msg = msg; + SIGQ_APPEND(&sscop->saved_sigs, s); +} + +/* + * Set a new state. If signals are waiting for a state change - append them to + * the signal queue, so they get executed. + */ +static void +sscop_set_state(struct sscop *sscop, u_int nstate) +{ + VERBOSE(sscop, SSCOP_DBG_STATE, (sscop, sscop->aarg, + "changing state from %s to %s", + states[sscop->state], states[nstate])); + + sscop->state = nstate; + SIGQ_MOVE(&sscop->saved_sigs, &sscop->sigs); +} + +void +sscop_setdebug(struct sscop *sscop, u_int n) +{ + sscop->debug = n; +} + +u_int +sscop_getdebug(const struct sscop *sscop) +{ + return (sscop->debug); +} |