summaryrefslogtreecommitdiffstats
path: root/sys/contrib/ngatm/netnatm/saal/saal_sscop.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/ngatm/netnatm/saal/saal_sscop.c')
-rw-r--r--sys/contrib/ngatm/netnatm/saal/saal_sscop.c4947
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);
+}
OpenPOWER on IntegriCloud