4948 lines
106 KiB
C
4948 lines
106 KiB
C
/*
|
|
* 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);
|
|
}
|