/* 
 * vsize.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Revision: 1.2 $
 * $Date: 1996/01/29 22:43:17 $
 */


#include "xkernel.h"
#include "romopt.h"
#include "vsize.h"
#include "vsize_i.h"

#ifdef __STDC__

static void	protlFuncInit( Protl );
static XkReturn	readCutoff( Protl, char **, int, int, VOID * );
static void	sessnInit( Sessn );
static Sessn	vsizeCreateSessn( Protl, Protl, Protl, Sessn * );
static Sessn	vsizeOpen( Protl, Protl, Protl, Part * );
static XkReturn	vsizeOpenDisable( Protl, Protl, Protl, Part * );
static XkReturn	vsizeOpenDone( Protl, Protl, Sessn, Protl );
static XkReturn	vsizeOpenEnable( Protl, Protl, Protl, Part * );
static XkReturn	vsizePop( Sessn, Sessn, Msg *, VOID * );
static int	vsizeControlProtl( Protl, int, char *, int );
static int	vsizeControlSessn( Sessn, int, char *, int );
static XkReturn	vsizeClose( Sessn );
static XkHandle	vsizePush( Sessn, Msg * );
static XkReturn	vsizeProtlDemux( Protl, Sessn, Msg * );
static XkReturn	vsizeSessnDemux( Protl, Sessn, Msg * );

#else

static void	protlFuncInit();
static XkReturn	readCutoff();
static void	sessnInit();
static Sessn	vsizeCreateSessn();

#endif /* __STDC__ */

int tracevsizep;


static ProtlRomOpt	vsizeOpts[] = {
    { "cutoff", 3, readCutoff }
};


void
vsize_init(self)
    Protl self;
{
    PSTATE	*pstate;
    int		i;
    
    xTrace0(vsizep, TR_GROSS_EVENTS, "VSIZE init");
    xAssert(xIsProtl(self));
    xAssert(self->numdown <= VSIZEMAXDOWN);
    for (i=0; i< self->numdown; i++ ) { 
        if (!xIsProtl(xGetProtlDown(self, i))) { 
	    xError("vsize down vector is misconfigured");
	    return;
        }
    }
    pstate = X_NEW(PSTATE);
    pstate->numdown = self->numdown; 
    for (i=0; i< pstate->numdown; i++ ) { 
	pstate->cutoff[i] = -1;
    }
    

    self->state = (VOID *)pstate;
    findProtlRomOpts(self, vsizeOpts, sizeof(vsizeOpts)/sizeof(ProtlRomOpt), 0);
    protlFuncInit(self);
    pstate->activeMap = mapCreate(11, sizeof(Sessn)*pstate->numdown);
    pstate->passiveMap = mapCreate(11, sizeof(PassiveId));
}


static Sessn
vsizeOpen(self, hlp, hlpType, p)
    Protl self, hlp, hlpType;
    Part *p;
{
    Sessn	s;
    Sessn	lls[VSIZEMAXDOWN];
    Part  	savedPart[3];
    PSTATE	*pstate = (PSTATE *)self->state;
    int		plen;
    int 	i,j;
    
    xTrace0(vsizep, TR_MAJOR_EVENTS, "VSIZE open");
    pstate = (PSTATE *)self->state;
    if ( ! p || partLength(p) < 1 || partLength(p) > (3)) { 
	xTrace0(vsizep, TR_ERRORS, "VSIZE open -- bad participants");
	return ERR_SESSN;
    }								
    /* 
     * Save the original participants before opening since we need to
     * use it in both opens and it will get munged in the first open
     */
    plen = partLength(p) * sizeof(Part);
    bcopy((char *)p, (char *)savedPart, plen);

    for (i=0; i<pstate->numdown; i++) {
    	lls[i] = xOpen(self, hlpType, xGetProtlDown(self, i), p);
    	if ( lls[i] == ERR_SESSN ) {
	    xTrace0(vsizep, TR_ERRORS, "vsize_open: could not open session");
    	    for (j=0; j<i; j++) {
	        xClose(lls[j]);
	    }
	    return ERR_SESSN;
        }
        bcopy((char *)savedPart, (char *)p, plen);
    }
    if (mapResolve(pstate->activeMap, lls, (void **)&s) == XK_SUCCESS) {
	xTrace0(vsizep, TR_MAJOR_EVENTS, "found an existing one");
    	for (i=0; i<pstate->numdown; i++) {
	    xClose(lls[i]);
	}
    } else {
	xTrace0(vsizep, TR_MAJOR_EVENTS, "creating a new one");
	if ( (s = vsizeCreateSessn(self, hlp, hlpType, lls)) == ERR_SESSN ) {
    	    for (i=0; i<pstate->numdown; i++) {
	        xClose(lls[i]);
	    }
	}
    }
    return s;
}


/* 
 * vsizeCreateSessn --
 * Create and initialize a new VSIZE session using the lls's in the array.
 * Assumes no session already exists corresponding to the lls's in the array
 */
static Sessn
vsizeCreateSessn(self, hlp, hlpType, lls)
    Protl self, hlp, hlpType;
    Sessn *lls;
{
    Sessn	s;
    SSTATE	*sstate;
    PSTATE	*pstate = (PSTATE *)self->state;
    int 	i;
    
    if ( (s = xCreateSessn(sessnInit, hlp, hlpType, self, pstate->numdown, lls))
			== ERR_SESSN ) {
	xTrace0(vsizep, TR_ERRORS, "create sessn fails in vsizeOpen");
	return ERR_SESSN;
    }
    sstate = X_NEW(SSTATE);
    s->state = (VOID *)sstate;
    sstate->numdown = pstate->numdown;
    s->binding = mapBind(pstate->activeMap, lls, s);
    if ( s->binding == ERR_BIND ) {
	xTrace0(vsizep, TR_ERRORS, "mapBind fails in vsizeCreateSessn");
	xClose(s);
	return ERR_SESSN;
    }
    if ( pstate->cutoff[0] < 0 ) {
	/* 
	 * Determine our cutoff dynamically from the lower session
	 */
	for (i=0; i<pstate->numdown; i++) {
	    if ( xControlSessn(lls[i], GETOPTPACKET, (char *)&sstate->cutoff[i],
		      sizeof(sstate->cutoff)) <= 0 ) {
	        xError("VSIZE could not get lls opt packet size.");
	        xClose(s);
	        return ERR_SESSN;
	    }
	}
    } else {
	/* 
	 * Use the static cutoff specified at configuration time
	 */
	for (i=0; i<pstate->numdown; i++) {
	    sstate->cutoff[i] = pstate->cutoff[i];
	}
    }
    xTrace1(vsizep, TR_EVENTS, "new VSIZE sessn uses cutoff of %d",
	    sstate->cutoff);
    /*
     * The lower sessions' up fields are made to point to this
     * vsize session (not the protocol)
     */
    for (i=0; i<pstate->numdown; i++) {
        xSetUp(lls[i], (Protl)s);
    }

    xTrace1(vsizep, TR_MAJOR_EVENTS, "VSIZE open returns %x", s);
    return s;
}


static int
vsizeControlSessn(s, opcode, buf, len)
    Sessn s;
    int opcode;
    char *buf;
    int len;
{
    SSTATE	*sstate = (SSTATE *)s->state;
    xTrace0(vsizep, TR_EVENTS, "VSIZE controlsessn");

    switch ( opcode ) {

      /*
       * All other opcodes are forwarded to the long-pckt session.  
       */
      default:
      return xControlSessn(xGetSessnDown(s, sstate->numdown-1), opcode, buf, len);
    }

}


static int
vsizeControlProtl(self, opcode, buf, len)
    Protl self;
    int opcode;
    char *buf;
    int len;
{
    PSTATE	*pstate = (PSTATE *)self->state;
    /*
     * All opcodes are forwarded to the long-pckt protocol
     */
    return xControlProtl(xGetProtlDown(self, pstate->numdown-1), opcode, buf, len);
}


static XkReturn
vsizeOpenEnable( self, hlp, hlpType, p )
    Protl self, hlp, hlpType;
    Part *p;
{
    PSTATE	*ps = (PSTATE *)self->state;
    
    xTrace0(vsizep, TR_MAJOR_EVENTS, "VSIZE open enable");
    return defaultVirtualOpenEnable(self, ps->passiveMap, hlp, hlpType,
				    self->down, p);
}


static XkReturn
vsizeOpenDisable(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
vsizeClose(s)
    Sessn s;
{
    PSTATE		*pstate;
    XkReturn	res;
    int i;
    
    xTrace1(vsizep, TR_MAJOR_EVENTS, "VSIZE close of session %x", s);
    xAssert(s->rcnt <= 0);
    pstate = (PSTATE *)s->myprotl->state;
    if ( s->binding != ERR_BIND ) {
	res = mapRemoveBinding(pstate->activeMap, s->binding);
	xAssert( res != XK_FAILURE );
    }
    for (i=0; i<pstate->numdown; i++) { 
        xSetUp(xGetSessnDown(s, i), xMyProtl(s));
        xClose(xGetSessnDown(s, i));
    }
    xDestroySessn(s);
    return XK_SUCCESS;
}


static XkHandle
vsizePush(s, msg)
    Sessn s;
    Msg *msg;
{
    SSTATE	*sstate;
    int i;
    
    xTrace0(vsizep, TR_EVENTS, "in vsize push");
    sstate = (SSTATE *)s->state;
    for (i=0; i<sstate->numdown-1; i++) {
        if ( msgLength(msg) <= sstate->cutoff[i] ) {
	    xTrace1(vsizep, TR_MORE_EVENTS,
		"vsize_push: pushing to session[%d]",i);
	    xAssert(xIsSessn(xGetSessnDown(s, i)));
	    return xPush(xGetSessnDown(s, i), msg);
        }
    }
    xTrace1(vsizep, TR_MORE_EVENTS,
	"vsize_push: pushing to session[%d]",sstate->numdown-1);
    xAssert(xIsSessn(xGetSessnDown(s, sstate->numdown-1)));
    return xPush(xGetSessnDown(s, sstate->numdown-1), msg);
}


static XkReturn
vsizeOpenDone(self, llp, lls, hlpType)
    Protl self, llp, hlpType;
    Sessn lls;
{
    Sessn	s;
    Part 	*p;
    PSTATE	*pstate;
    Sessn	llsArray[VSIZEMAXDOWN];
    Enable	*e;
    int		i,j;
    int		llsIndex;
    
    xTrace0(vsizep, TR_MAJOR_EVENTS, "In VSIZE openDone");
    if ( self == hlpType ) {
	xTrace0(vsizep, TR_ERRORS, "self == hlpType in vsizeOpenDone");
	return XK_FAILURE;
    }
    pstate = (PSTATE *)self->state;
    /* 
     * Figure out which of my lower protocols owns the lower session
     */

    llsIndex=-1;
    for (i=0; i<pstate->numdown; i++) {
        if ( llp == xGetProtlDown(self, i) ) {
	    llsIndex = i;
        } 
    }
    if (llsIndex == -1) {
	xError("Impossible lower level session in vsize openDone");
	return XK_FAILURE;
    }
    /*
     * check for open enables
     */
    if (mapResolve(pstate->passiveMap, &hlpType, (void **)&e) == XK_FAILURE) {
	/* 
	 * This shouldn't happen
	 */
	xTrace0(vsizep, TR_ERRORS,
		"vsizeOpenDone: Couldn't find hlp for incoming session");
	return XK_FAILURE;
    }
    /* 
     * Open the lls for the other protocol (not the one which owns
     * lls) using participants from a control op on lls
     */
    p = xGetParticipants(lls);
    if (!p) {
	xTrace0(vsizep, TR_ERRORS,
		"Could not get participants from lls in vsizeOpenDone");
	return XK_FAILURE;
    }
    for (i=0; i<pstate->numdown; i++) {
	if (i == llsIndex) {
            llsArray[i] = lls;
	} else {
            llsArray[i] = xOpen(self, hlpType, xGetProtlDown(self, i), p);
            if ( llsArray[i] == ERR_SESSN ) {
	        xTrace1(vsizep, TR_ERRORS, "vsizeOpenDone couldn't open lls[%d]",i);
                for (j=0; j<i; j++) {
	            xClose(llsArray[j]);
                }
	        return XK_FAILURE;
            }
        }
    }
    xDuplicate(lls);
    if ( (s = vsizeCreateSessn(self, e->hlp, e->hlpType, llsArray))
		== ERR_SESSN ) {
        for (i=0; i<pstate->numdown; i++) {
	    xClose(llsArray[i]);
        }
	return XK_FAILURE;
    }
    xTrace0(vsizep, TR_EVENTS,
	    "vsize Passively opened session successfully created");
    return xOpenDone(e->hlp, self, s);
}

static XkReturn
vsizeProtlDemux( self, lls, m )
    Protl	self;
    Sessn	lls;
    Msg		*m;
{
    xTrace0(vsizep, TR_ERRORS, "vsizeProtlDemux called!!");
    return XK_SUCCESS;
}

/* 
 * vsizePop and vsizeSessnDemux must be used (i.e., they can't be
 * bypassed) for the UPI reference count mechanism to work properly. 
 */
static XkReturn
vsizePop(self, lls, msg, h)
    Sessn self;
    Sessn lls;
    Msg *msg;
    VOID *h;
{
    xTrace0(vsizep, TR_EVENTS, "vsize Pop");
    return xDemux(xGetUp(self), self, msg);
}

static XkReturn
vsizeSessnDemux(self, lls, msg)
    Protl self;
    Sessn lls;
    Msg *msg;
{
    xTrace0(vsizep, TR_EVENTS, "vsize Session Demux");
    return xPop((Sessn)self, lls, msg, 0);
}

static void
sessnInit(s)
    Sessn s;
{
    xAssert(xIsSessn(s));
    s->push = vsizePush;
    s->pop = vsizePop;
    s->close = vsizeClose;
    s->controlsessn = vsizeControlSessn;
    /* 
     * VSIZE sessions will look like a protocol to lower sessions, so we
     * need a demux function
     */
    s->demux = vsizeSessnDemux;
}

static void
protlFuncInit(p)
    Protl p;
{
    xAssert(xIsProtl(p));
    p->controlprotl = vsizeControlProtl;
    p->open = vsizeOpen;
    p->openenable = vsizeOpenEnable;
    p->opendisable = vsizeOpenDisable;
    p->demux = vsizeProtlDemux;
    p->opendone = vsizeOpenDone;
}


static XkReturn
readCutoff( self, str, nFields, line, arg )
    Protl	self;
    char	**str;
    int		nFields, line;
    VOID	*arg;
{
    PSTATE	*ps = (PSTATE *)self->state;
    int i,val;

    for (i=2; i<nFields; i++) {
#ifdef XKMACHKERNEL
        val = sscanf1 (str[i], "%d", &ps->cutoff[i-2]);
#else
        val = sscanf (str[i], "%d", &ps->cutoff[i-2]);
#endif
        if (val < 1) {
	    return XK_FAILURE;
	}
    }
    return XK_SUCCESS;
}	

