/*     
 *     x-kernel v3.3
 *
 *     Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 */

#include "xkernel.h"
#include "ip.h"
#include "prot/msp.h"

/*
 * these three defines must agree:  QSIZE >= 2 * (max win + 1)
 * and QSIZE a power of 2
 */
#define MSP_MAX_WINDOW  127
#define QSIZE		256
#define QIDX(x)		(x & 255)

/* the protocol makes the assumption that all packets are the same size */
#define MSP_PACKETSIZE	1024			/* assumed packet size */
						/* default max recv buffer */
#define MSP_DEF_RECVBUFSIZE	(16 * MSP_PACKETSIZE)
#define MSP_TIMEOUT	(100 * 1000)	/* default time out - 100 msec */
#define MSP_TICK	(100 * 1000)	/* event resolution - 100 msec */
					/* 500msec per PERS packet */
#define MSP_PERSTICKS	((500 * 1000) / MSP_TICK)
					/* timewait timer = 5 seconds */
#define MSP_TIMEWAIT_TIMER	((5 * 1000 * 1000 ) / MSP_TICK)


/* MSP message header definition */
typedef struct header {
    MSPport sport;		/* source */
    MSPport dport;		/* dest */
    int flags;			/* flag bits */
    u_short len;		/* length */
    MSPsws advwindow;	        /* advertised window, if ack bit set */
    MSPsws seq;		        /* sequence */
    MSPsws ackseq;		/* ack sequence, if ack bit set */
} MSPhdr;

#define HLEN (sizeof(MSPhdr))

/* protocol state */
typedef struct pstate {
    Map activemap;
    Map passivemap;
    Event timer;		/* periodic retransmission timer */
} ProtlState;

typedef struct {
    int valid;			/* entry is valid */
    XTime last_sent;		/* time of last retransmission */
    Msg   packet;		/* the packet */
} sendqelem;

typedef struct {
    int valid;			/* entry valid */
    int fin;			/* packet is FIN */
    Msg packet;			/* the packet */
} recvqelem;

/* session state */
typedef struct sstate {
    int recvbufsize;		/* receive buffer size (flow control) */
    int state;			/* current mode of session */
    int timer;			/* timer for TIMEWAIT state */
    Event event;		/* timeout event */
    MSPhdr hdr;			/* partially filled in template */
    Semaphore wait;		/* signalled for ESTABLISHED and CLOSED */
    Enable *passiveEnable;	/* enable, if sessn created by passive open */

    /* sender */
    int effective_window;	/* effective window */
    int sleepers;		/* number of sleeping senders */
    Semaphore sleep;		/* sleeping semaphore */
    MSPsws lowest_unack;	/* lowest unacknowledged seq */
    MSPsws next_send_seq;	/* next seq to send */
    sendqelem sendq[QSIZE];	/* retransmission queue */

    /* receiver */
    MSPsws next_deliver;	/* next packet to deliver to higher level */
    MSPsws max_seq_seen;	/* highest valid seq we've buffered */
    MSPsws fin_seq;		/* if valid fin received, its seq # */
    recvqelem recvq[QSIZE];	/* out-of-order packet */
} SessnState;

/* recvq and sendq elements of the session state */
#define RECVQ(sstate, seq)      (sstate->recvq[QIDX(seq)])
#define SENDQ(sstate, seq)      (sstate->sendq[QIDX(seq)])


/* active and passive maps */
typedef struct {
    Sessn   lls;
    MSPport localport;
    MSPport remoteport;
} ActiveId;

typedef MSPport PassiveId;

#define ACTIVE_MAP_SIZE  101
#define PASSIVE_MAP_SIZE 23

/* header flag bits */
#define MSP_FLAG_ACK	0x0001		/* ack if set, data if cleared */
#define MSP_FLAG_SYN	0x0002		/* synchronize */
#define MSP_FLAG_RST	0x0004		/* reset */
#define MSP_FLAG_FIN	0x0008		/* finished sending */
#define MSP_FLAG_DATA	0x0010		/* packet has data */
#define MSP_FLAG_PERS	0x0020		/* persist packet */


/* state - mode of a session */
#define MSP_STATE_CLOSED	0
#define MSP_STATE_LISTEN	1
#define MSP_STATE_SYNSENT	2
#define MSP_STATE_SYNRECVD	3
#define MSP_STATE_ESTABLISHED	4
#define MSP_STATE_FIN1		5
#define MSP_STATE_FIN2		6
#define MSP_STATE_CLOSING	7
#define MSP_STATE_TIMEWAIT	8
#define MSP_STATE_CLOSEWAIT	9
#define MSP_STATE_LASTACK	10



/* UPI function declarations */
void            msp_init(Protl);
static Sessn    mspOpen(Protl, Protl, Protl, Part *);
static XkReturn mspOpenEnable(Protl, Protl, Protl, Part *);
static XkReturn mspDemux(Protl, Sessn, Msg *);
static XkHandle mspPush(Sessn, Msg *);
static XkReturn mspPop(Sessn, Sessn, Msg *, void *);
static Sessn    mspCreateSessn(Protl, Protl, Protl, ActiveId *);
static XkReturn mspClose(Sessn);
static int      mspControlProtl(Protl, int, char *, int) ;
static int      mspControlSessn(Sessn, int, char *, int) ;
static Part    *mspGetParticipants(Sessn);

/* my helper functions */
static void	sendqEnqueue(sendqelem *, Msg *, MSPsws);
static void	sendqDequeue(sendqelem *, MSPsws);
static void	recvqEnqueue(recvqelem *, Msg *, MSPsws, int);
static void	recvqDequeue(recvqelem *, MSPsws);
static int	seqDiff(MSPsws, MSPsws);
static MSPsws computeAdvertisedWindow(SessnState *);
static void	sendPacket(Sessn, int, Msg *);
static void	activeOpen(Sessn);
static XkReturn	reset(Sessn);
static void	opened(Sessn);
static void	processAck(Sessn, MSPsws, MSPsws);
static void	processData(Sessn, MSPhdr *, Msg *);
static void	shut_down(Sessn);
static void	mspTimeout(Event, void *);

/* helpers from AST */
static void     getproc_protl(Protl);
static void     getproc_sessn(Sessn);
static long     mspHdrLoad(MSPhdr *, char *, long);
static void     mspHdrStore(MSPhdr *, char *, long);

/* trace variable used in conjunction with xTrace stmts */
int tracemspp;

