1
0
This repository has been archived on 2024-07-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
2024-07-22 01:58:46 -03:00

1727 lines
43 KiB
C

/* sscop.c - SSCOP (Q.2110) protocol */
/* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <netinet/in.h> /* for htonl */
#include "atmd.h"
#include "sscop.h"
#include "queue.h"
#include "pdu.h"
/*
* This is a quite exact translation of the ITU-T SSCOP SDL diagrams. They
* are a pleasant example of how protocols _should_ be specified. It took me
* less than a week to implement this.
*
* Calls back to the SSCOP user are always done last in a sequence of actions
* to avoid reentrancy problems if the user invokes SSCOP from the callback
* function. (Exception: data indications are delivered when needed. So you
* shouldn't kill the protocol stack in the middle of this. Releasing the
* call and such is fine, though.)
*
* Sequences of an AA-RELASE.indication/confirm immediately followed by a
* AA-ESTABLISH.indication have been replaced by a new primitive called
* "restart". This way, the SSCOP user is able to distinguish conditions where
* SSCOP is in idle state after a callback from conditions where SSCOP wants to
* continue (and where the user many not want to stop SSCOP).
*
* The entity receiving management information (e.g. maa_error) must not issue
* any SSCOP primitives from that callback routine. Instead, actions must be
* queued until SSCOP finishes processing of the current event.
*
* Comments of the type xx.yy are section or figure numbers in Q.2110
*
* KNOWN BUGS: The queue and buffer management stinks, especially the "buffer
* cloning". All this ought to be rewritten to use skbuffs. Since
* this will happen anyway if SSCOP ever gets integrated into the
* kernel, we can safely ignore the problem for now.
*
* The lower layer is always assumed to be ready for sending. If
* this is not the case (shouldn't happen), the PDU should be
* discarded (or the process might be blocked for a few cycles).
*/
#define COMPONENT "SSCOP"
/* Configurable SSCOP parameters */
#define SSCOP_CF_MR 30 /* buffer size */
#define SSCOP_CF_MaxCC 10 /* max timeouts */
#define SSCOP_CF_MaxPD 100 /* SD/POLL ratio */
#define SSCOP_CF_MaxSTAT 67 /* max elements in STAT PDU, 7.7 */
#undef POLL_AFTER_RETRANSMISSION /* 20.38, #define this if needed */
/* Timers */
/*
* Assumptions: RTT = 1 sec, remote needs about 1 sec to wake up. SSCF changes
* all this anyway.
*/
#define TIMER_CC 2000000 /* 2 sec (== RTT+eps) */
#define TIMER_POLL 1000000 /* 1 sec (== RTT) */
#define TIMER_NORESP 7000000 /* 7 sec (keepalive+RTT+eps) */
#define TIMER_KEEPALIVE 5000000 /* 5 sec (> poll && > RTT) */
#define TIMER_IDLE 30000000 /* 30 sec (>> keepalive) */
/* Some helper macros */
#define START_TIMER(t) ({ STOP_TIMER(dsc->timer_##t); \
dsc->timer_##t = start_timer(dsc->cf_timer_##t, sscop_##t##_exp,dsc); })
#define STOP_TIMER(t) ({ if (t) stop_timer(t); t = NULL; })
#define MOD24(x) ((x) & ((1 << 24)-1))
#define INC24(v) (v = MOD24(v+1))
#define INC8(v) (v = (v+1) & 255)
#define NORMALIZE(x,b) (((x)-(b)) & 0xffffff)
#define NORM_RX(v) NORMALIZE((v),dsc->vr_r)
#define NORM_TX(v) NORMALIZE((v),dsc->vt_a)
#define NORM_TXP(v) NORMALIZE((v),dsc->vt_pa)
#define NEGATIVE(v) ((v) & 0x800000)
static const char *state_name[] = { "Idle","OutConnPend","InConnPend",
"OutDiscPend","OutResyPend","InResyPend","OutRecPend","RecRespPend",
"InRecPend","DataTransReady" };
static void do_diag(int severity,const char *fmt,...)
{
va_list ap;
va_start(ap,fmt);
vdiag(COMPONENT,severity,fmt,ap);
va_end(ap);
}
static void maa_error(void *arg,char code,int count)
{
/*
* arg is void * because it's also used in pdu.c, which doesn't know about
* SSCOP_DSC
*/
SSCOP_DSC *dsc = arg;
static const char *const msgs[] = {
"A:SD PDU","B:BGN PDU","C:BGAK PDU","D:BGREJ PDU","E:END PDU",
"F:ENDAK PDU","G:POLL PDU","H:STAT PDU","I:USTAT PDU","J:RS",
"K:RSAK PDU","L:ER","M:ERAK","O:VT(CC)>=MaxCC",
"P:Timer_NO_RESPONSE expiry","Q:SD or POLL, N(S) error",
"R:STAT N(PS) error","S:STAT N(R) or list elements error",
"T:USTAT N(R) or list elements error","U:PDU length violation",
"V:SD PDUs must be retransmitted","W:Lack of credit",
"X:Credit obtained","\0:Unknown error code" };
const char *const *walk;
if (dsc->ops->maa_error)
if (!dsc->ops->maa_error(dsc->user,code,count)) return;
for (walk = msgs; **walk; walk++)
if (**walk == code) break;
if (code != 'V')
diag(COMPONENT,DIAG_WARN,"layer management - error %c \"%s\"",code,
(*walk)+2);
else diag(COMPONENT,DIAG_WARN,"layer management - error %c,%d \"%s\"",code,
count,(*walk)+2);
}
static int sdu_length(BUFFER *buf)
{
return buf->length-4-SSCOP_PAD((char *) buf->data+buf->length-4);
}
static BUFFER *build_pdu(SSCOP_DSC *dsc,unsigned char type,void *data,
int num)
{
BUFFER *buf;
uint32_t *trailer;
int pad;
buf = buffer_create(num+12,dsc->vt_s); /* space for trailers */
if (data && num) {
memset((char *) buf->data+(num & ~3),0,4); /* clear padding area */
memcpy((unsigned char *) buf->data,data,num);
pad = (4-(num & 3)) & 3;
trailer = (uint32_t *) ((char *) buf->data+num+pad);
}
else {
pad = 0;
trailer = (uint32_t *) buf->data;
}
diag(COMPONENT,DIAG_DEBUG,"generating %s PDU",pdu_name[type]);
switch (type) {
case SSCOP_BGN:
case SSCOP_RS:
case SSCOP_ER:
*trailer++ = dsc->mode == sscop_qsaal1 ? SSCOP_TRAIL(0,0,0) :
SSCOP_TRAIL(0,0,dsc->vt_sq);
*trailer++ = SSCOP_TRAIL(type,pad,dsc->vr_mr);
break;
case SSCOP_BGAK:
case SSCOP_RSAK:
case SSCOP_ERAK:
*trailer++ = SSCOP_TRAIL(0,0,0);
*trailer++ = SSCOP_TRAIL(type,pad,dsc->vr_mr);
break;
case SSCOP_END: /* S bit is indicated by "num" */
*trailer++ = SSCOP_TRAIL(0,0,0);
*trailer++ = SSCOP_TRAIL(type,pad,0) | (num ? htonl(SSCOP_S_BIT) :
0);
break;
case SSCOP_BGREJ:
case SSCOP_ENDAK:
*trailer++ = SSCOP_TRAIL(0,0,0);
*trailer++ = SSCOP_TRAIL(type,pad,0);
break;
case SSCOP_POLL:
*trailer++ = SSCOP_TRAIL(0,0,dsc->vt_ps);
/* fall through */
case SSCOP_SD:
*trailer++ = SSCOP_TRAIL(type,pad,dsc->vt_s);
break;
case SSCOP_STAT:
*trailer++ = SSCOP_TRAIL(0,0,dsc->vr_ps);
/* fall through */
case SSCOP_USTAT:
*trailer++ = SSCOP_TRAIL(0,0,dsc->vr_mr);
*trailer++ = SSCOP_TRAIL(type,0,dsc->vr_r);
break;
case SSCOP_UD:
case SSCOP_MD:
*trailer++ = SSCOP_TRAIL(type,pad,0);
break;
default:
diag(COMPONENT,DIAG_FATAL,
"requested construction of unknown PDU type %d",type);
}
buf->length = (char *) trailer-(char *) buf->data;
return buf;
}
static void emit_pdu(SSCOP_DSC *dsc,BUFFER *buf)
{
BUFFER **last;
{
PDU_VARS;
if (DECOMPOSE_PDU(dsc,buf->data,buf->length))
diag(COMPONENT,DIAG_FATAL,"composed garbage");
PRINT_PDU("SEND",buf->data);
}
if (dsc->ops->cpcs_send)
dsc->ops->cpcs_send(dsc->user,buf->data,buf->length);
switch (SSCOP_TYPE((char *) buf->data+buf->length-4)) {
case SSCOP_BGN:
last = &dsc->last_bgn;
break;
case SSCOP_END:
last = &dsc->last_end;
break;
case SSCOP_RS:
last = &dsc->last_rs;
break;
case SSCOP_ER:
last = &dsc->last_er;
break;
default:
buffer_discard(buf);
return;
}
if (*last && *last != buf) {
buffer_discard(*last);
*last = NULL;
}
*last = buf;
}
static void resend_pdu(SSCOP_DSC *dsc,BUFFER *buf)
{
if (!buf) diag(COMPONENT,DIAG_FATAL,"resend buffer is NULL");
emit_pdu(dsc,buf);
}
static void send_pdu(SSCOP_DSC *dsc,unsigned char type,void *data,int size)
{
BUFFER *buf;
buf = build_pdu(dsc,type,data,size);
emit_pdu(dsc,buf);
}
static void next_state(SSCOP_DSC *dsc,SSCOP_STATE state)
{
diag(COMPONENT,DIAG_DEBUG,"entering state %s (%d)",state_name[state],
(int) state);
dsc->state = state;
}
static void bad_pdu(SSCOP_DSC *dsc,unsigned char type)
{
/* 111111 */
/* 0123456789012345 */
maa_error(dsc,"?BCxFJKDALGHIyzM"[type],0);
}
static void send_ustat(SSCOP_DSC *dsc,int first,int last)
{
uint32_t range[2];
range[0] = htonl(first);
range[1] = htonl(last);
send_pdu(dsc,SSCOP_USTAT,range,8);
}
/* --- Utility functions --------------------------------------------------- */
static void clear_receive_buffer(SSCOP_DSC *dsc)
{
queue_clear(&dsc->rx_buf);
}
static void clear_transmission_buffer(SSCOP_DSC *dsc)
{
queue_clear(&dsc->tx_buf);
}
static void clear_transmission_queue(SSCOP_DSC *dsc)
{
queue_clear(&dsc->tx_q);
}
static void clear_retransmission_queue(SSCOP_DSC *dsc)
{
queue_clear(&dsc->rt_q);
}
/* --- SSCOP subroutines --------------------------------------------------- */
static void sscop_poll_exp(void *user);
static void sscop_keepalive_exp(void *user);
static void sscop_noresp_exp(void *user);
static void release_buffers(SSCOP_DSC *dsc) /* 20.49 */
{
clear_transmission_queue(dsc);
clear_transmission_buffer(dsc);
clear_retransmission_queue(dsc);
clear_receive_buffer(dsc);
}
static void clear_transmitter(SSCOP_DSC *dsc) /* 20.49 */
{
if (!dsc->clear_buffers) {
clear_transmission_queue(dsc);
clear_transmission_buffer(dsc);
}
}
static void prepare_recovery(SSCOP_DSC *dsc) /* 20.49 */
{
if (dsc->clear_buffers) {
clear_transmission_queue(dsc);
clear_transmission_buffer(dsc);
}
clear_retransmission_queue(dsc);
}
static void prepare_retrieval(SSCOP_DSC *dsc) /* 20.49 */
{
prepare_recovery(dsc);
clear_receive_buffer(dsc);
}
static void deliver_data(SSCOP_DSC *dsc) /* 20.49 */
{
BUFFER *buf;
if (!dsc->clear_buffers) {
while ((buf = queue_get(&dsc->rx_buf))) {
if (dsc->ops->data_ind)
dsc->ops->data_ind(dsc->user,buf->data,buf->length,buf->key);
buffer_discard(buf);
}
}
clear_receive_buffer(dsc);
}
static void initialize_state_variables(SSCOP_DSC *dsc) /* 20.49 */
{
dsc->vt_s = dsc->vt_ps = dsc->vt_a = 0;
dsc->vt_pa = 1;
dsc->vt_pd = 0;
dsc->credit = 1;
dsc->vr_r = dsc->vr_h = 0;
}
static int detect_retransmission(SSCOP_DSC *dsc,int sq) /* 20.50 */
{
if (dsc->mode == sscop_qsaal1) return 0;
if (dsc->vr_sq == sq) return 1;
dsc->vr_sq = sq;
return 0;
}
static void set_poll_timer(SSCOP_DSC *dsc) /* 20.50 */
{
if (queue_peek(&dsc->tx_q) || dsc->vt_s != dsc->vt_a) START_TIMER(poll);
else START_TIMER(keepalive);
}
static void reset_data_transfer_timers(SSCOP_DSC *dsc) /* 20.51 */
{
STOP_TIMER(dsc->timer_poll);
STOP_TIMER(dsc->timer_keepalive);
STOP_TIMER(dsc->timer_noresp);
STOP_TIMER(dsc->timer_idle);
}
static void set_data_transfer_timers(SSCOP_DSC *dsc) /* 20.51 */
{
START_TIMER(poll);
START_TIMER(noresp);
}
static void initialize_vr_mr(SSCOP_DSC *dsc)
{
dsc->vr_mr = SSCOP_CF_MR;
}
static void update_vr_mr(SSCOP_DSC *dsc)
{
dsc->vr_mr = MOD24(dsc->vr_r+SSCOP_CF_MR);
}
/* --- Timer handlers ------------------------------------------------------ */
static void sscop_cc_exp(void *user)
{
SSCOP_DSC *dsc = user;
diag(COMPONENT,DIAG_DEBUG,"Timer CC has expired");
dsc->timer_cc = NULL;
switch (dsc->state) {
case sscop_outconn: /* 20.9 */
if (dsc->vt_cc < dsc->cf_max_cc) {
dsc->vt_cc++;
resend_pdu(dsc,dsc->last_bgn);
START_TIMER(cc);
return;
}
maa_error(dsc,'O',0);
send_pdu(dsc,SSCOP_END,NULL,1);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
return;
case sscop_outdisc: /* 20.15 */
if (dsc->vt_cc < dsc->cf_max_cc) {
dsc->vt_cc++;
resend_pdu(dsc,dsc->last_end);
START_TIMER(cc);
return;
}
maa_error(dsc,'O',0);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
return;
case sscop_outres: /* 20.18 */
if (dsc->vt_cc < dsc->cf_max_cc) {
dsc->vt_cc++;
resend_pdu(dsc,dsc->last_rs);
START_TIMER(cc);
return;
}
maa_error(dsc,'O',0);
send_pdu(dsc,SSCOP_END,NULL,1);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
return;
case sscop_outrec: /* 20.24 */
if (dsc->vt_cc < dsc->cf_max_cc) {
dsc->vt_cc++;
resend_pdu(dsc,dsc->last_er);
START_TIMER(cc);
return;
}
maa_error(dsc,'O',0);
send_pdu(dsc,SSCOP_END,NULL,1);
clear_receive_buffer(dsc);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
return;
default:
break;
}
diag(COMPONENT,DIAG_FATAL,"Timer CC expired in state %s",
state_name[dsc->state]);
}
static void sscop_common_exp(void *user)
{
SSCOP_DSC *dsc = user;
if (dsc->state != sscop_ready) {
diag(COMPONENT,DIAG_FATAL,"sscop_common_exp invoked in state %s",
state_name[dsc->state]);
return;
}
INC24(dsc->vt_ps);
send_pdu(dsc,SSCOP_POLL,NULL,0);
dsc->vt_pd = 0;
set_poll_timer(dsc);
}
static void sscop_poll_exp(void *user)
{
SSCOP_DSC *dsc = user;
diag(COMPONENT,DIAG_DEBUG,"Timer POLL has expired");
dsc->timer_poll = NULL;
sscop_common_exp(user);
}
static void sscop_keepalive_exp(void *user)
{
SSCOP_DSC *dsc = user;
diag(COMPONENT,DIAG_DEBUG,"Timer KEEPALIVE has expired");
dsc->timer_keepalive = NULL;
sscop_common_exp(user);
}
static void sscop_idle_exp(void *user)
{
SSCOP_DSC *dsc = user;
diag(COMPONENT,DIAG_DEBUG,"Timer IDLE has expired");
dsc->timer_idle = NULL;
START_TIMER(noresp);
sscop_common_exp(user);
}
static void sscop_noresp_exp(void *user)
{
SSCOP_DSC *dsc = user;
diag(COMPONENT,DIAG_DEBUG,"Timer NORESP has expired");
dsc->timer_noresp = NULL;
if (dsc->state != sscop_ready) {
diag(COMPONENT,DIAG_FATAL,"Timer NORESP expired in state %s",
state_name[dsc->state]);
return;
}
reset_data_transfer_timers(dsc);
maa_error(dsc,'P',0);
send_pdu(dsc,SSCOP_END,NULL,1);
prepare_retrieval(dsc);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
}
/* --- Transmit engine ----------------------------------------------------- */
static void try_to_send(SSCOP_DSC *dsc)
{
BUFFER *buf,*buf2;
if (dsc->state != sscop_ready) return;
while (queue_peek(&dsc->rt_q) || NORM_TX(dsc->vt_s) <
NORM_TX(dsc->vt_ms) || dsc->timer_idle) {
buf = queue_get(&dsc->rt_q);
if (buf) {
if (!(buf2 = queue_lookup(&dsc->tx_buf,buf->key)))
diag(COMPONENT,DIAG_FATAL,"didn't find PDU %d in TX buffer",
buf->key);
emit_pdu(dsc,buf);
buf2->extra = dsc->vt_ps;
#ifdef POLL_AFTER_RETRANSMISSION
if (queue_peek(dsc->rt_q))
/* fall through to goto */
#endif
goto B; /* sigh ... */
}
else {
if (!queue_peek(&dsc->tx_q)) return;
if (NORM_TX(dsc->vt_s) < NORM_TX(dsc->vt_ms)) {
buf = queue_get(&dsc->tx_q);
buf2 = build_pdu(dsc,SSCOP_SD,buf->data,buf->length);
buffer_discard(buf);
buf2->key = dsc->vt_s;
buf2->extra = dsc->vt_ps;
emit_pdu(dsc,buffer_clone(buf2));
queue_put(&dsc->tx_buf,buf2);
INC24(dsc->vt_s);
B:
/* B */
dsc->vt_pd++;
if (dsc->timer_poll) {
if (dsc->vt_pd < dsc->cf_max_pd) continue;
}
else {
if (!dsc->timer_idle) STOP_TIMER(dsc->timer_keepalive);
else {
STOP_TIMER(dsc->timer_idle);
START_TIMER(noresp);
}
if (dsc->vt_pd < dsc->cf_max_pd) {
START_TIMER(poll);
continue;
}
}
}
else {
STOP_TIMER(dsc->timer_idle);
START_TIMER(noresp);
}
}
/* A */
INC24(dsc->vt_ps);
send_pdu(dsc,SSCOP_POLL,NULL,0);
dsc->vt_pd = 0;
START_TIMER(poll);
}
}
/* --- Incoming non-CC PDUs in Data Transfer Ready state ------------------- */
static void start_error_recov(SSCOP_DSC *dsc,char code)
{
reset_data_transfer_timers(dsc);
maa_error(dsc,code,0);
/* D */
dsc->vt_cc = 1;
INC8(dsc->vt_sq);
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_ER,NULL,0);
prepare_recovery(dsc);
START_TIMER(cc);
next_state(dsc,sscop_outrec);
}
static void data_sd(SSCOP_DSC *dsc,int s,void *msg,int length) /* 20.38-39 */
{
BUFFER *buf;
if (NORM_RX(s) >= NORM_RX(dsc->vr_mr)) {
if (NORM_RX(dsc->vr_h) < NORM_RX(dsc->vr_mr)) {
send_ustat(dsc,dsc->vr_h,dsc->vr_mr);
dsc->vr_h = dsc->vr_mr;
}
return;
}
if (s == dsc->vr_r) {
if (dsc->ops->data_ind) dsc->ops->data_ind(dsc->user,msg,length,s);
if (s == dsc->vr_h) {
dsc->vr_r = dsc->vr_h = MOD24(s+1);
update_vr_mr(dsc);
return;
}
while (1) {
INC24(dsc->vr_r);
update_vr_mr(dsc);
buf = queue_lookup(&dsc->rx_buf,dsc->vr_r);
if (!buf) break;
queue_remove(&dsc->rx_buf,buf);
if (dsc->ops->data_ind)
dsc->ops->data_ind(dsc->user,buf->data,buf->length,buf->key);
buffer_discard(buf);
}
return;
}
buf = buffer_create(length,s);
memcpy(buf->data,msg,length);
if (s == dsc->vr_h) {
queue_put(&dsc->rx_buf,buf);
INC24(dsc->vr_h);
return;
}
if (NORM_RX(dsc->vr_h) < NORM_RX(s)) {
queue_put(&dsc->rx_buf,buf);
send_ustat(dsc,dsc->vr_h,s);
dsc->vr_h = MOD24(s+1);
return;
}
if (!queue_lookup(&dsc->rx_buf,s)) queue_put(&dsc->rx_buf,buf);
else {
buffer_discard(buf);
start_error_recov(dsc,'Q');
}
}
/*
* Some of the NORM_RXs in data_poll are certainly unnecessary. Maybe even
* all of them could be removed ...
*/
static void data_poll(SSCOP_DSC *dsc,int s) /* 20.41-42 */
{
int curr,i;
if (!dsc->list) dsc->list = alloc(dsc->cf_max_stat*sizeof(uint32_t));
if (NORM_RX(dsc->vr_h) > NORM_RX(s)) {
start_error_recov(dsc,'Q');
return;
}
dsc->vr_h = NORM_RX(dsc->vr_mr) < NORM_RX(s) ? dsc->vr_mr : s;
/* K */
curr = 0;
i = dsc->vr_r;
if (i != dsc->vr_h) {
while (NORM_RX(i) < NORM_RX(dsc->vr_h)) {
if (queue_lookup(&dsc->rx_buf,i)) {
INC24(i);
continue;
}
dsc->list[curr++] = htonl(i);
if (curr >= dsc->cf_max_stat) {
send_pdu(dsc,SSCOP_STAT,dsc->list,curr*4);
curr = 0;
dsc->list[curr++] = htonl(i);
}
do INC24(i);
while (i != dsc->vr_h && !queue_lookup(&dsc->rx_buf,i));
if (i == dsc->vr_h) break; /* append is done right b4 send_pdu */
dsc->list[curr++] = htonl(i);
INC24(i); /* short-cut, since i < VR(H) && SD.N(S) == i in RB */
}
dsc->list[curr++] = htonl(i);
}
send_pdu(dsc,SSCOP_STAT,dsc->list,curr*4);
}
static void data_ustat(SSCOP_DSC *dsc,int mr,int r,int e1,int e2) /* 20.43 */
{
BUFFER *buf,*buf2;
int seq1,seq2,i;
if (NORM_TX(dsc->vt_a) <= NORM_TX(r) && NORM_TX(r) < NORM_TX(dsc->vt_s)) {
for (i = dsc->vt_a; i != r; INC24(i)) {
buf = queue_lookup(&dsc->tx_buf,i);
if (buf) {
queue_remove(&dsc->tx_buf,buf);
buffer_discard(buf);
}
}
dsc->vt_a = r;
dsc->vt_ms = mr;
seq1 = e1;
seq2 = e2;
if (NORM_TX(dsc->vt_a) <= NORM_TX(seq1) && NORM_TX(seq1) <
NORM_TX(seq2) && NORM_TX(seq2) < NORM_TX(dsc->vt_s)) {
/* G */
while ((buf = queue_lookup(&dsc->tx_buf,seq1))) {
buf2 = buffer_clone(buf);
queue_put(&dsc->rt_q,buf2);
INC24(seq1);
if (seq1 == seq2) {
maa_error(dsc,'V',e2-e1);
return;
}
}
}
}
/* F */
start_error_recov(dsc,'T');
}
static void data_stat(SSCOP_DSC *dsc,int ps,int mr,int r,void *list,
int length) /* 20.44-46 */
{
BUFFER *buf,*buf2;
char *curr;
int i,count,seq1,seq2;
if (NEGATIVE(NORM_TXP(ps)) || NORM_TXP(ps) > NORM_TXP(dsc->vt_ps)) {
start_error_recov(dsc,'R');
return;
}
if (NORM_TX(dsc->vt_a) > NORM_TX(r) || NORM_TX(r) >
NORM_TX(dsc->vt_s)) {
/* H */
start_error_recov(dsc,'S');
return;
}
for (i = dsc->vt_a; i != r; INC24(i)) {
buf = queue_lookup(&dsc->tx_buf,i);
if (buf) {
queue_remove(&dsc->tx_buf,buf);
buffer_discard(buf);
}
}
dsc->vt_a = r;
dsc->vt_pa = ps;
dsc->vt_ms = mr;
i = length;
curr = list;
count = 0;
if (i > 1) {
seq1 = SSCOP_N(curr);
curr += 4;
i--;
if (NORM_TX(seq1) >= NORM_TX(dsc->vt_s)) {
/* H */
start_error_recov(dsc,'S');
return;
}
/* I */
do {
seq2 = SSCOP_N(curr);
curr += 4;
i--;
if (NORM_TX(seq1) >= NORM_TX(seq2) ||
NORM_TX(seq2) > NORM_TX(dsc->vt_s)) {
/* H */
start_error_recov(dsc,'S');
return;
}
do {
if (!(buf = queue_lookup(&dsc->tx_buf,seq1))) {
/* H */
start_error_recov(dsc,'S');
return;
}
if (NEGATIVE(NORM_TXP(buf->extra)) &&
NORM_TXP(ps) <= NORM_TXP(dsc->vt_ps) &&
!queue_lookup(&dsc->rt_q,seq1)) {
buf2 = buffer_clone(buf);
queue_put(&dsc->rt_q,buf2);
count++;
}
INC24(seq1);
}
while (seq1 != seq2);
/* J */
if (!i) break;
seq2 = SSCOP_N(curr);
curr += 4;
i--;
if (NORM_TX(seq1) >= NORM_TX(seq2) ||
NORM_TX(seq2) > NORM_TX(dsc->vt_s)) {
/* H */
start_error_recov(dsc,'S');
return;
}
do {
if (dsc->clear_buffers) {
buf = queue_lookup(&dsc->tx_buf,seq1);
if (buf) {
queue_remove(&dsc->tx_buf,buf);
buffer_discard(buf);
}
}
INC24(seq1);
}
while (seq1 != seq2);
}
while (i);
maa_error(dsc,'V',count);
}
/* L */
if (dsc->credit != (NORM_TX(dsc->vt_s) < NORM_TX(dsc->vt_ms)))
maa_error(dsc,(dsc->credit = !dsc->credit) ? 'X' : 'W',0);
if (dsc->timer_poll) START_TIMER(noresp);
else if (!dsc->timer_idle) {
STOP_TIMER(dsc->timer_keepalive);
STOP_TIMER(dsc->timer_noresp);
START_TIMER(idle);
}
}
/* --- Incoming PDUs ------------------------------------------------------- */
/*
* Returns 0 if the descriptor might conceivably be gone when returning,
* 1 otherwise.
*/
static int handle_sscop_pdu(SSCOP_DSC *dsc,void *msg,int size)
{
PDU_VARS;
if (DECOMPOSE_PDU(dsc,msg,size)) return 1;
PRINT_PDU("RECV",msg);
switch (type) {
case SSCOP_UD:
if (dsc->ops->unitdata) dsc->ops->unitdata(dsc->user,msg,length);
return 1;
case SSCOP_MD:
if (dsc->ops->maa_data) dsc->ops->maa_data(dsc->user,msg,length);
return 1;
default:
break;
}
switch (dsc->state) {
case sscop_idle:
switch (type) {
case SSCOP_BGREJ:
bad_pdu(dsc,type);
return 1;
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) {
send_pdu(dsc,SSCOP_BGREJ,NULL,0); /* no SSCOP-UU */
return 1;
}
dsc->vt_ms = mr;
next_state(dsc,sscop_inconn);
if (dsc->ops->estab_ind)
dsc->ops->estab_ind(dsc->user,msg,length);
return 1;
case SSCOP_ENDAK:
return 1;
case SSCOP_END:
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
return 1;
case SSCOP_ER:
case SSCOP_POLL:
case SSCOP_SD:
case SSCOP_BGAK:
case SSCOP_ERAK:
case SSCOP_STAT:
case SSCOP_USTAT:
case SSCOP_RS:
case SSCOP_RSAK:
bad_pdu(dsc,type);
send_pdu(dsc,SSCOP_END,NULL,1);
return 1;
default:
break;
}
break;
case sscop_outconn: /* 20.8-10 */
switch (type) {
case SSCOP_ENDAK:
case SSCOP_SD:
case SSCOP_ERAK:
case SSCOP_END:
case SSCOP_STAT:
case SSCOP_USTAT:
case SSCOP_POLL:
case SSCOP_ER:
case SSCOP_RSAK:
case SSCOP_RS:
return 1; /* ignore PDU */
case SSCOP_BGAK:
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
initialize_state_variables(dsc);
set_data_transfer_timers(dsc);
next_state(dsc,sscop_ready);
if (dsc->ops->estab_conf)
dsc->ops->estab_conf(dsc->user,msg,length);
return 1;
case SSCOP_BGREJ:
STOP_TIMER(dsc->timer_cc);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,msg,length,1);
return 0;
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) return 1;
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_BGAK,NULL,0);
initialize_state_variables(dsc);
set_data_transfer_timers(dsc);
next_state(dsc,sscop_ready);
if (dsc->ops->estab_conf)
dsc->ops->estab_conf(dsc->user,msg,length);
return 1;
default:
break;
}
break;
case sscop_inconn: /* 20.11-13 */
switch (type) {
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) return 1;
dsc->vt_ms = mr;
if (dsc->ops->restart)
dsc->ops->restart(dsc->user,msg,length,1);
return 1;
case SSCOP_ER:
case SSCOP_BGAK:
case SSCOP_ERAK:
case SSCOP_RSAK:
case SSCOP_RS:
bad_pdu(dsc,type);
return 1;
case SSCOP_ENDAK:
case SSCOP_BGREJ:
bad_pdu(dsc,type);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_SD:
case SSCOP_USTAT:
case SSCOP_STAT:
case SSCOP_POLL:
bad_pdu(dsc,type);
send_pdu(dsc,SSCOP_END,NULL,1);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_END:
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,msg,length,!s);
return 0;
default:
break;
}
break;
case sscop_outdisc: /* 20.14-16 */
switch (type) {
case SSCOP_SD:
case SSCOP_BGAK:
case SSCOP_POLL:
case SSCOP_STAT:
case SSCOP_USTAT:
case SSCOP_ERAK:
case SSCOP_RS:
case SSCOP_RSAK:
case SSCOP_ER:
return 1;
case SSCOP_END:
STOP_TIMER(dsc->timer_cc);
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
return 0;
case SSCOP_ENDAK:
case SSCOP_BGREJ:
STOP_TIMER(dsc->timer_cc);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
return 0;
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) {
send_pdu(dsc,SSCOP_BGAK,NULL,0);
resend_pdu(dsc,dsc->last_end);
return 1;
}
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
next_state(dsc,sscop_inconn);
if (dsc->ops->restart)
dsc->ops->restart(dsc->user,msg,length,0);
return 1;
}
case sscop_outres: /* 20.17-19 */
switch (type) {
case SSCOP_ER:
case SSCOP_POLL:
case SSCOP_STAT:
case SSCOP_USTAT:
case SSCOP_BGAK:
case SSCOP_ERAK:
case SSCOP_SD:
return 1; /* ignore */
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) {
send_pdu(dsc,SSCOP_BGAK,NULL,0);
resend_pdu(dsc,dsc->last_rs);
return 1;
}
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
next_state(dsc,sscop_inconn);
if (dsc->ops->restart)
dsc->ops->restart(dsc->user,msg,length,1);
return 1;
case SSCOP_ENDAK:
case SSCOP_BGREJ:
STOP_TIMER(dsc->timer_cc);
bad_pdu(dsc,type);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_END:
STOP_TIMER(dsc->timer_cc);
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,msg,length,!s);
return 0;
case SSCOP_RS:
if (detect_retransmission(dsc,sq)) return 1;
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_RSAK,NULL,0);
initialize_state_variables(dsc);
set_data_transfer_timers(dsc);
next_state(dsc,sscop_ready);
if (dsc->ops->res_conf) dsc->ops->res_conf(dsc->user);
return 1;
case SSCOP_RSAK:
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
initialize_state_variables(dsc);
set_data_transfer_timers(dsc);
next_state(dsc,sscop_ready);
if (dsc->ops->res_conf) dsc->ops->res_conf(dsc->user);
return 1;
default:
break;
}
break;
case sscop_inres: /* 20.20-22 */
switch (type) {
case SSCOP_SD:
case SSCOP_POLL:
case SSCOP_STAT:
case SSCOP_USTAT:
send_pdu(dsc,SSCOP_END,NULL,1);
/* fall through */
case SSCOP_ENDAK:
case SSCOP_BGREJ:
bad_pdu(dsc,type);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_END:
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,msg,length,!s);
return 0;
case SSCOP_ER:
case SSCOP_BGAK:
case SSCOP_ERAK:
case SSCOP_RSAK:
bad_pdu(dsc,type);
return 1;
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) {
bad_pdu(dsc,type);
return 1;
}
dsc->vt_ms = mr;
next_state(dsc,sscop_inconn);
if (dsc->ops->restart)
dsc->ops->restart(dsc->user,msg,length,1);
return 1;
case SSCOP_RS:
if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
return 1;
default:
break;
}
break;
case sscop_outrec: /* 20.23-26 */
switch (type) {
case SSCOP_BGAK:
case SSCOP_RSAK:
bad_pdu(dsc,type);
return 1;
case SSCOP_ERAK:
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
next_state(dsc,sscop_recresp);
deliver_data(dsc);
if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
return 1;
case SSCOP_END:
STOP_TIMER(dsc->timer_cc);
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
clear_receive_buffer(dsc);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,msg,length,!s);
return 0;
case SSCOP_ENDAK:
case SSCOP_BGREJ:
bad_pdu(dsc,type);
STOP_TIMER(dsc->timer_cc);
clear_receive_buffer(dsc);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_STAT:
case SSCOP_USTAT:
case SSCOP_POLL:
case SSCOP_SD:
return 1;
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) {
bad_pdu(dsc,type);
return 1;
}
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
clear_receive_buffer(dsc);
next_state(dsc,sscop_inconn);
if (dsc->ops->restart)
dsc->ops->restart(dsc->user,msg,length,1);
return 1;
case SSCOP_ER:
if (detect_retransmission(dsc,sq)) {
bad_pdu(dsc,type);
return 1;
}
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_ERAK,NULL,0);
deliver_data(dsc);
next_state(dsc,sscop_recresp);
if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
return 1;
case SSCOP_RS:
if (detect_retransmission(dsc,sq)) {
bad_pdu(dsc,type);
return 1;
}
STOP_TIMER(dsc->timer_cc);
dsc->vt_ms = mr;
clear_receive_buffer(dsc);
next_state(dsc,sscop_inres);
if (dsc->ops->res_ind)
dsc->ops->res_ind(dsc->user,msg,length);
return 0;
default:
break;
}
case sscop_recresp: /* 20.27-29 */
switch (type) {
case SSCOP_BGAK:
case SSCOP_RSAK:
bad_pdu(dsc,type);
return 1;
case SSCOP_ERAK:
case SSCOP_SD:
case SSCOP_POLL:
return 1;
case SSCOP_END:
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,msg,length,!s);
return 0;
case SSCOP_ENDAK:
case SSCOP_BGREJ:
bad_pdu(dsc,type);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_RS:
if (detect_retransmission(dsc,sq)) {
bad_pdu(dsc,type);
return 1;
}
dsc->vt_ms = mr;
next_state(dsc,sscop_inres);
if (dsc->ops->res_ind)
dsc->ops->res_ind(dsc->user,msg,length);
return 1;
case SSCOP_ER:
if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
else send_pdu(dsc,SSCOP_ERAK,NULL,0);
return 1;
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) {
bad_pdu(dsc,type);
return 1;
}
dsc->vt_ms = mr;
next_state(dsc,sscop_inconn);
if (dsc->ops->restart)
dsc->ops->restart(dsc->user,msg,length,1);
return 1;
case SSCOP_STAT:
case SSCOP_USTAT:
bad_pdu(dsc,type);
send_pdu(dsc,SSCOP_END,NULL,1);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
default:
break;
}
case sscop_inrec: /* 20.30-33 */
switch (type) {
case SSCOP_END:
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,msg,length,!s);
return 0;
case SSCOP_ENDAK:
case SSCOP_BGREJ:
bad_pdu(dsc,type);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_USTAT:
case SSCOP_STAT:
case SSCOP_POLL:
case SSCOP_SD:
bad_pdu(dsc,type);
send_pdu(dsc,SSCOP_END,NULL,1);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_RSAK:
case SSCOP_BGAK:
case SSCOP_ERAK:
bad_pdu(dsc,type);
return 1;
case SSCOP_RS:
if (detect_retransmission(dsc,sq)) {
bad_pdu(dsc,type);
return 1;
}
dsc->vt_ms = mr;
next_state(dsc,sscop_inres);
if (dsc->ops->res_ind)
dsc->ops->res_ind(dsc->user,msg,length);
return 1;
case SSCOP_ER:
if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
return 1;
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) {
bad_pdu(dsc,type);
return 1;
}
dsc->vt_ms = mr;
next_state(dsc,sscop_inconn);
if (dsc->ops->restart)
dsc->ops->restart(dsc->user,msg,length,1);
return 1;
default:
break;
}
case sscop_ready: /* 20.34-46 */
switch (type) {
case SSCOP_BGAK:
case SSCOP_ERAK:
case SSCOP_RSAK:
return 1;
case SSCOP_ER:
if (detect_retransmission(dsc,sq)) {
START_TIMER(noresp);
send_pdu(dsc,SSCOP_ERAK,NULL,0);
return 1;
}
reset_data_transfer_timers(dsc);
dsc->vt_ms = mr;
prepare_recovery(dsc);
next_state(dsc,sscop_inrec);
deliver_data(dsc);
if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
return 1;
case SSCOP_BGN:
if (detect_retransmission(dsc,sq)) {
START_TIMER(noresp);
send_pdu(dsc,SSCOP_BGAK,NULL,0);
return 1;
}
reset_data_transfer_timers(dsc);
dsc->vt_ms = mr;
prepare_retrieval(dsc);
next_state(dsc,sscop_inconn);
if (dsc->ops->restart)
dsc->ops->restart(dsc->user,msg,length,1);
return 1;
case SSCOP_ENDAK:
case SSCOP_BGREJ:
reset_data_transfer_timers(dsc);
bad_pdu(dsc,type);
prepare_retrieval(dsc);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,NULL,0,0);
return 0;
case SSCOP_RS:
if (detect_retransmission(dsc,sq)) {
START_TIMER(noresp);
send_pdu(dsc,SSCOP_RSAK,NULL,0);
return 1;
}
reset_data_transfer_timers(dsc);
dsc->vt_ms = mr;
prepare_retrieval(dsc);
next_state(dsc,sscop_inres);
if (dsc->ops->res_ind)
dsc->ops->res_ind(dsc->user,msg,length);
return 1;
case SSCOP_END:
reset_data_transfer_timers(dsc);
send_pdu(dsc,SSCOP_ENDAK,NULL,0);
prepare_retrieval(dsc);
next_state(dsc,sscop_idle);
if (dsc->ops->rel_ind)
dsc->ops->rel_ind(dsc->user,msg,length,!s);
return 0;
case SSCOP_SD:
data_sd(dsc,s,msg,length);
return 1;
case SSCOP_POLL:
dsc->vr_ps = ps; /* store */
data_poll(dsc,s);
return 1;
case SSCOP_USTAT:
data_ustat(dsc,mr,r,SSCOP_N(msg),SSCOP_N((char *) msg+4));
return 1;
case SSCOP_STAT:
data_stat(dsc,ps,mr,r,msg,length/4);
return 1;
default:
break;
}
}
return 1;
}
void sscop_pdu(SSCOP_DSC *dsc,void *msg,int size)
{
if (handle_sscop_pdu(dsc,msg,size))
if (dsc->state == sscop_ready) try_to_send(dsc);
}
/* --- From SSCOP user ----------------------------------------------------- */
void sscop_retrieve(SSCOP_DSC *dsc,int rn)
{
BUFFER *buf;
int i;
if (dsc->state != sscop_idle && dsc->state != sscop_inconn &&
dsc->state != sscop_outdisc && dsc->state != sscop_inres &&
dsc->state != sscop_recresp && dsc->state != sscop_inrec)
diag(COMPONENT,DIAG_FATAL,"sscop_retrieve invoked in state %s",
state_name[dsc->state]);
if (rn != SSCOP_RN_UNKNOWN)
for (i = rn == SSCOP_RN_TOTAL ? dsc->vt_a : MOD24(rn+1);
NORM_TX(dsc->vt_a) <= NORM_TX(i) && NORM_TX(i) < NORM_TX(dsc->vt_s);
INC24(i)) {
buf = queue_lookup(&dsc->tx_buf,i);
if (buf) {
queue_remove(&dsc->tx_buf,buf);
if (dsc->ops->retr_ind)
dsc->ops->retr_ind(dsc->user,buf->data,sdu_length(buf));
buffer_discard(buf);
}
}
while ((buf = queue_get(&dsc->tx_q))) {
if (dsc->ops->retr_ind)
dsc->ops->retr_ind(dsc->user,buf->data,sdu_length(buf));
buffer_discard(buf);
}
if (dsc->ops->retr_comp) dsc->ops->retr_comp(dsc->user);
}
void sscop_send(SSCOP_DSC *dsc,void *buffer,int size) /* 20.38 */
{
BUFFER *buf;
if (dsc->state != sscop_ready && (dsc->state != sscop_outrec ||
dsc->clear_buffers)) return; /* 20.23 */
buf = buffer_create(size,0);
memcpy(buf->data,buffer,size);
queue_put(&dsc->tx_q,buf);
try_to_send(dsc);
}
void sscop_estab_req(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel)
{
switch (dsc->state) {
case sscop_outdisc: /* 20.14 */
STOP_TIMER(dsc->timer_cc);
/* fall through */
case sscop_idle: /* 20.5 */
clear_transmitter(dsc);
dsc->clear_buffers = buf_rel;
dsc->vt_cc = 1;
INC8(dsc->vt_sq);
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_BGN,uu_data,uu_length);
START_TIMER(cc);
next_state(dsc,sscop_outconn);
return;
default:
break;
}
diag(COMPONENT,DIAG_FATAL,"sscop_estab_req invoked in state %s",
state_name[dsc->state]);
}
void sscop_estab_resp(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel)
/* 20.11 */
{
if (dsc->state != sscop_inconn)
diag(COMPONENT,DIAG_FATAL,"sscop_estab_resp invoked in state %s",
state_name[dsc->state]);
clear_transmitter(dsc);
dsc->clear_buffers = buf_rel;
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_BGAK,uu_data,uu_length);
initialize_state_variables(dsc);
set_data_transfer_timers(dsc);
next_state(dsc,sscop_ready);
try_to_send(dsc); /* probably not ... */
}
void sscop_rel_req(SSCOP_DSC *dsc,void *uu_data,int uu_length)
{
switch (dsc->state) {
case sscop_outrec: /* 20.24 */
clear_receive_buffer(dsc);
/* fall through */
case sscop_outconn: /* 20.9 */
case sscop_outres: /* 20.18 */
STOP_TIMER(dsc->timer_cc);
/* fall through */
case sscop_inres: /* 20.20 */
case sscop_recresp: /* 20.28 */
case sscop_inrec: /* 20.30 */
dsc->vt_cc = 1;
send_pdu(dsc,SSCOP_END,uu_data,uu_length);
START_TIMER(cc);
next_state(dsc,sscop_outdisc);
return;
case sscop_inconn: /* 20.11 */
send_pdu(dsc,SSCOP_BGREJ,uu_data,uu_length);
next_state(dsc,sscop_idle);
return;
case sscop_ready: /* 20.34 */
reset_data_transfer_timers(dsc);
dsc->vt_cc = 1;
send_pdu(dsc,SSCOP_END,uu_data,uu_length);
prepare_retrieval(dsc);
START_TIMER(cc);
next_state(dsc,sscop_outdisc);
return;
default:
break;
}
diag(COMPONENT,DIAG_FATAL,"sscop_rel_req invoked in state %s",
state_name[dsc->state]);
}
void sscop_res_req(SSCOP_DSC *dsc,void *uu_data,int uu_length)
{
switch (dsc->state) {
case sscop_outrec: /* 20.25 */
STOP_TIMER(dsc->timer_cc);
clear_receive_buffer(dsc);
/* fall through */
case sscop_recresp: /* 20.29 */
dsc->vt_cc = 1;
INC8(dsc->vt_sq);
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
clear_transmitter(dsc);
START_TIMER(cc);
next_state(dsc,sscop_outres);
return;
case sscop_inrec: /* 20.30 */
clear_transmitter(dsc);
dsc->vt_cc = 1;
INC8(dsc->vt_sq);
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
START_TIMER(cc);
next_state(dsc,sscop_outres);
return;
case sscop_ready: /* 20.34 */
reset_data_transfer_timers(dsc);
dsc->vt_cc = 1;
INC8(dsc->vt_sq);
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
release_buffers(dsc);
START_TIMER(cc);
next_state(dsc,sscop_outres);
return;
default:
break;
}
diag(COMPONENT,DIAG_FATAL,"sscop_res_req invoked in state %s",
state_name[dsc->state]);
}
void sscop_res_resp(SSCOP_DSC *dsc) /* 20.20 */
{
if (dsc->state != sscop_inres)
diag(COMPONENT,DIAG_FATAL,"sscop_res_resp invoked in state %s",
state_name[dsc->state]);
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_RSAK,NULL,0);
clear_transmitter(dsc);
initialize_state_variables(dsc);
set_data_transfer_timers(dsc);
next_state(dsc,sscop_ready);
try_to_send(dsc); /* probably not ... */
}
void sscop_rec_resp(SSCOP_DSC *dsc)
{
switch (dsc->state) {
case sscop_inrec: /* 20.30 */
initialize_vr_mr(dsc);
send_pdu(dsc,SSCOP_ERAK,NULL,0);
/* fall through */
case sscop_recresp: /* 20.28 */
if (!dsc->clear_buffers) clear_transmission_buffer(dsc);
initialize_state_variables(dsc);
set_data_transfer_timers(dsc);
next_state(dsc,sscop_ready);
try_to_send(dsc);
return;
default:
break;
}
diag(COMPONENT,DIAG_FATAL,"sscop_rec_resp invoked in state %s",
state_name[dsc->state]);
}
void sscop_unitdata(SSCOP_DSC *dsc,void *buffer,int size) /* 20.47-48 */
{
send_pdu(dsc,SSCOP_UD,buffer,size);
}
void sscop_maa_data(SSCOP_DSC *dsc,void *buffer,int size) /* 20.47-48 */
{
send_pdu(dsc,SSCOP_MD,buffer,size);
}
void start_sscop(SSCOP_DSC *dsc,SSCOP_USER_OPS *ops,void *user_data,
SSCOP_MODE mode)
{
static int initialize = 1;
if (initialize) {
pdu_maa = maa_error;
pdu_diag = do_diag;
initialize = 0;
}
dsc->ops = ops;
dsc->user = user_data;
dsc->mode = mode;
dsc->vt_sq = dsc->vr_sq = 0; /* 20.5 */
dsc->clear_buffers = 1;
dsc->state = sscop_idle;
dsc->timer_cc = dsc->timer_poll = dsc->timer_noresp =
dsc->timer_keepalive = dsc->timer_idle = NULL;
queue_init(&dsc->rx_buf);
queue_init(&dsc->tx_q);
queue_init(&dsc->tx_buf);
queue_init(&dsc->rt_q);
dsc->last_bgn = dsc->last_end = dsc->last_rs = dsc->last_er = NULL;
dsc->cf_max_cc = SSCOP_CF_MaxCC;
dsc->cf_max_pd = SSCOP_CF_MaxPD;
dsc->cf_max_stat = SSCOP_CF_MaxSTAT;
dsc->cf_timer_cc = TIMER_CC;
dsc->cf_timer_poll = TIMER_POLL;
dsc->cf_timer_noresp = TIMER_NORESP;
dsc->cf_timer_keepalive = TIMER_KEEPALIVE;
dsc->cf_timer_idle = TIMER_IDLE;
dsc->list = NULL;
}
void stop_sscop(SSCOP_DSC *dsc)
{
if (dsc->state != sscop_idle)
diag(COMPONENT,DIAG_WARN,"stopping dsc in state %s",
state_name[dsc->state]);
dsc->state = sscop_idle; /* avoid send attempts */
STOP_TIMER(dsc->timer_cc);
STOP_TIMER(dsc->timer_poll);
STOP_TIMER(dsc->timer_noresp);
STOP_TIMER(dsc->timer_keepalive);
STOP_TIMER(dsc->timer_idle);
queue_clear(&dsc->rx_buf);
queue_clear(&dsc->tx_q);
queue_clear(&dsc->tx_buf);
queue_clear(&dsc->rt_q);
if (dsc->last_bgn) buffer_discard(dsc->last_bgn);
if (dsc->last_end) buffer_discard(dsc->last_end);
if (dsc->last_rs) buffer_discard(dsc->last_rs);
if (dsc->last_er) buffer_discard(dsc->last_er);
if (dsc->list) free(dsc->list);
}