/* 
 * vdrop.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Revision: 1.4 $
 * $Date: 1996/04/15 21:52:17 $
 */


#include "xkernel.h"
#include "vdrop.h"
#include "vdrop_i.h"

#ifdef __STDC__

static void	protlFuncInit( Protl );
static void	sessnInit( Sessn );
static Sessn	vdropCreateSessn( Protl, Protl, Protl, Sessn );
static Sessn	vdropOpen( Protl, Protl, Protl, Part * );
static XkReturn	vdropOpenDisable( Protl, Protl, Protl, Part * );
static XkReturn	vdropOpenDone( Protl, Protl, Sessn, Protl );
static XkReturn	vdropOpenEnable( Protl, Protl, Protl, Part * );
static int	vdropControlProtl( Protl, int, char *, int );
static int	vdropControlSessn( Sessn, int, char *, int );
static XkReturn	vdropClose( Sessn );
static XkReturn	vdropPop( Sessn, Sessn, Msg *, void * );
static XkHandle	vdropPush( Sessn, Msg * );
static XkReturn	vdropProtlDemux( Protl, Sessn, Msg * );
static XkReturn	vdropSessnDemux( Protl, Sessn, Msg * );

#else

static void	protlFuncInit();
static void	sessnInit();
static Sessn	vdropCreateSessn();
static Sessn	vdropOpen();
static XkReturn	vdropOpenDisable();
static XkReturn	vdropOpenDone();
static XkReturn	vdropOpenEnable();

#endif /* __STDC__ */


int tracevdropp;


static XkReturn
readInt( self, str, nFields, line, arg )
    Protl       self;
    int         line, nFields;
    char        **str;
    VOID        *arg;
{
    return sscanf(str[2], "%d", arg) < 1  ? XK_FAILURE : XK_SUCCESS;
} /* readInt */   


static ProtlRomOpt vdropOpt[] = {
    { "interval", 3, readInt }
};


void
vdrop_init( self )
    Protl 	self;
{
    PState	*ps;
    
    xTraceP0(self, TR_GROSS_EVENTS, "init");
    xAssert(xIsProtl(self));
    if (!xIsProtl(xGetProtlDown(self, 0))) {
	xError("VDROP down vector is misconfigured");
	return;
    }
    ps = X_NEW(PState);
    self->state = (VOID *)ps;
    protlFuncInit(self);
    ps->activeMap = mapCreate(VDROP_ACT_MAP_SZ, sizeof(Sessn));
    ps->passiveMap = mapCreate(VDROP_PAS_MAP_SZ, sizeof(Protl));
}


static Sessn
vdropOpen( self, hlp, hlpType, p )
    Protl	self, hlp, hlpType;
    Part 	*p;
{
    Sessn	s, lls;
    PState	*ps = (PState *)self->state;
    
    xTraceP0(self, TR_MAJOR_EVENTS, "open");
    if ( (lls = xOpen(self, hlpType, xGetProtlDown(self, 0), p)) == ERR_SESSN ) {
	xTraceP0(self, TR_ERRORS, "open: could not open lower session");
	return ERR_SESSN;
    }
    if (mapResolve(ps->activeMap, &lls, (void **)&s) == XK_SUCCESS) {
	xTraceP0(self, TR_MORE_EVENTS, "open -- found an existing one");
    } else {
	xTraceP0(self, TR_MAJOR_EVENTS, "open -- creating a new one");
	if ((s = vdropCreateSessn(self, hlp, hlpType, lls)) == ERR_SESSN)
	    xClose(lls);
    }
    return s;
}


static Sessn
vdropCreateSessn( self, hlp, hlpType, lls )
    Protl 	self, hlp, hlpType;
    Sessn	lls;
{
    Sessn	s;
    PState	*ps = (PState *)self->state;
    SState	*ss;
    XTime	t;
    
    if ( (s = xCreateSessn(sessnInit, hlp, hlpType, self, 1, &lls))
			== ERR_SESSN ) {
	xTraceP0(self, TR_ERRORS, "xCreateSessn fails");
	return ERR_SESSN;
    }
    s->binding = mapBind(ps->activeMap, &lls, s);
    if ( s->binding == ERR_BIND ) {
	xTraceP0(self, TR_ERRORS, "mapBind fails in createSessn");
	return ERR_SESSN;
    }
    /*
     * The lower sessions' up field is made to point to this
     * vdrop session (not the protocol)
     */
    xSetUp(lls, (Protl)s);
    ss = X_NEW(SState);
    s->state = (VOID *)ss;
    ss->interval = -1;
    findProtlRomOpts(self, vdropOpt, sizeof(vdropOpt)/sizeof(vdropOpt[0]),
                    &ss->interval);
    if (ss->interval <= 0) {
        xGetTime(&t);
        ss->interval = ((t.usec/100) % (VDROP_MAX_INTERVAL - 1) + 2); 
    } /* if */
    xTraceP1(self, TR_MAJOR_EVENTS, "dropping once every %d pckts",
	    ss->interval);
    ss->count = 0;
    xTraceP1(self, TR_MAJOR_EVENTS, "open returns %x", s);
    return s;
}


static int
vdropControlSessn( s, opcode, buf, len )
    Sessn 	s;
    int 	opcode, len;
    char 	*buf;
{
    SState	*ss = (SState *)s->state;

    xTraceS0(s, TR_EVENTS, "controlsessn");
    switch ( opcode ) {

      case VDROP_SETINTERVAL:
	ss->interval = *(int *)buf;
	xTraceS1(s, TR_EVENTS,
		"controlsessn resets drop interval to %d",
		ss->interval);
	return 0;

      case VDROP_GETINTERVAL:
	*(int *)buf = ss->interval;
	return sizeof(int);

      default:
	/*
	 * All other opcodes are forwarded to the lower session.  
	 */
	return xControlSessn(xGetSessnDown(s, 0), opcode, buf, len);
    }
}


static int
vdropControlProtl( self, opcode, buf, len )
    Protl 	self;
    int 	opcode, len;
    char 	*buf;
{
    /*
     * All opcodes are forwarded to the lower protocol
     */
    return xControlProtl(xGetProtlDown(self, 0), opcode, buf, len);
}


static XkReturn
vdropOpenEnable( self, hlp, hlpType, p )
    Protl 	self, hlp, hlpType;
    Part 	*p;
{
    PState	*ps = (PState *)self->state;
    
    xTraceP0(self, TR_MAJOR_EVENTS, "open enable");
    return defaultVirtualOpenEnable(self, ps->passiveMap, hlp, hlpType,
				    self->down, p);
}


static XkReturn
vdropOpenDisable( self, hlp, hlpType, p )
    Protl 	self, hlp, hlpType;
    Part 	*p;
{
    PState	*ps = (PState *)self->state;

    return defaultVirtualOpenDisable(self, ps->passiveMap, hlp, hlpType,
				     self->down, p);
}


static XkReturn
vdropClose( s )
    Sessn 	s;
{
    PState	*ps;
    XkReturn	res;
    Sessn	lls;
    
    xTraceS1(s, TR_MAJOR_EVENTS, "close of session %x", s);
    xAssert(s->rcnt == 0);
    ps = (PState *)xMyProtl(s)->state;
    res = mapRemoveBinding(ps->activeMap, s->binding);
    xAssert( res != XK_FAILURE );
    lls = xGetSessnDown(s, 0);
    xAssert(xIsSessn(lls));
    xSetUp(lls, xMyProtl(s));
    xClose(lls);
    xDestroySessn(s);
    return XK_SUCCESS;
}


static XkHandle
vdropPush( s, msg )
    Sessn	s;
    Msg 	*msg;
{
    xTraceS0(s, TR_EVENTS, "push");
    return xPush(xGetSessnDown(s, 0), msg);
}


static XkReturn
vdropOpenDone( self, llp, lls, hlpType )
    Protl	self, llp, hlpType;
    Sessn	lls;
{
    Sessn	s;
    PState	*ps = (PState *)self->state;
    Enable	*e;
    
    xTraceP0(self, TR_MAJOR_EVENTS, "In openDone");
    if ( self == hlpType ) {
	xTraceP0(self, TR_ERRORS, "self == hlpType in openDone");
	return XK_FAILURE;
    }
    /*
     * check for openEnable
     */
    
    if (mapResolve(ps->passiveMap, &hlpType, (void **)&e) == XK_FAILURE) {
	/* 
	 * This shouldn't happen
	 */
	xTraceP0(self, TR_ERRORS,
		"openDone: Couldn't find hlp for incoming session");
	return XK_FAILURE;
    }
    if ( (s = vdropCreateSessn(self, e->hlp, e->hlpType, lls))
							== ERR_SESSN ) {
	return XK_FAILURE;
    }
    xDuplicate(lls);
    xTraceP0(self, TR_EVENTS,
	    "passively opened session successfully created");
    return xOpenDone(e->hlp, self, s);
}


static XkReturn
vdropProtlDemux( self, lls, m )
    Protl	self;
    Sessn	lls;
    Msg		*m;
{
    xTraceP0(self, TR_ERRORS, "protlDemux called!!");
    return XK_SUCCESS;
}

/* 
 * vdropPop and vdropSessnDemux must be used (i.e., they can't be
 * bypassed) for the UPI reference count mechanism to work properly. 
 */
static XkReturn
vdropPop( s, lls, msg, h )
    Sessn 	s, lls;
    Msg 	*msg;
    VOID	*h;
{
    SState	*ss = (SState *)s->state;

    xTraceS0(s, TR_EVENTS, "pop");
    if ((ss->interval) && ( ++ss->count >= ss->interval )) {
	xTraceS0(s, TR_MAJOR_EVENTS, "pop is dropping msg");
	ss->count = 0;
	return XK_SUCCESS;
    } else {
	return xDemux(xGetUp(s), s, msg);
    }
}

static XkReturn
vdropSessnDemux( self, lls, msg )
    Protl 	self;
    Sessn	lls;
    Msg 	*msg;
{
    xTraceS0((Sessn)self, TR_EVENTS, "session demux");
    return xPop((Sessn)self, lls, msg, 0);
}

static void
sessnInit( s )
    Sessn 	s;
{
    xAssert(xIsSessn(s));
    s->push = vdropPush;
    s->pop = vdropPop;
    s->close = vdropClose;
    s->controlsessn = vdropControlSessn;
    /* 
     * VDROP sessions will look like a protocol to lower sessions, so we
     * need a demux function
     */
    s->demux = vdropSessnDemux;
}


static void
protlFuncInit(p)
    Protl p;
{
    xAssert(xIsProtl(p));
    p->controlprotl = vdropControlProtl;
    p->open = vdropOpen;
    p->openenable = vdropOpenEnable;
    p->opendisable = vdropOpenDisable;
    p->demux = vdropProtlDemux;
    p->opendone = vdropOpenDone;
}
