/*
 * select_common.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Revision: 1.3 $
 * $Date: 1996/01/29 22:25:04 $
 */

/* 
 * select_common.c contains code common to both the 'select' and the
 * 'mselect' (multi-select) protocols.
 */

#include "xkernel.h"
#include "select_i.h"

int traceselectp;

#ifdef __STDC__

static void	getSessnFuncs( Sessn );
static long	hdrLoad( VOID *, char *, long, VOID * );
static void	hdrStore( VOID *, char *, long, VOID * );
static void	phdr( SelHdr * );
static XkReturn	selectCall( Sessn, Msg *, Msg * );
static XkHandle	selectPush( Sessn, Msg * );
static XkReturn	selectClose( Sessn );
static Sessn	selectCreateSessn( Protl, Protl, Protl, ActiveKey * );
static XkReturn	selectPop( Sessn, Sessn, Msg *, VOID * );
static XkReturn	selectCallPop( Sessn, Sessn, Msg *, VOID *, Msg * );

#else

static void	getSessnFuncs();
static long	hdrLoad();
static void	hdrStore();
static void	phdr();
static Sessn	selectCreateSessn();

#endif /* __STDC__ */


void
selectCommonInit( self )
    Protl self;
{
    Part 	part;
    PState 	*pstate;
    
    xTrace0(selectp, TR_GROSS_EVENTS, "SELECT common init");
    pstate = X_NEW(PState);
    self->state = (VOID *)pstate;
    pstate->passiveMap = mapCreate(101, sizeof(PassiveKey));
    pstate->activeMap = mapCreate(101, sizeof(ActiveKey));
    partInit(&part, 1);
    partPush(part, ANY_HOST, 0);
    if ( xOpenEnable(self, self, xGetProtlDown(self, 0), &part) == XK_FAILURE ) {
	xTrace0(selectp,0,"select_init: could not openenable lower protocol");
    }
}


static long
hdrLoad( hdr, src, len, arg )
    VOID	*hdr, *arg;
    char 	*src;
    long  	len;
{
    xAssert(len == sizeof(SelHdr));
    bcopy( src, hdr, len );
    ((SelHdr *)hdr)->id = ntohl(((SelHdr *)hdr)->id);
    ((SelHdr *)hdr)->status = ntohl(((SelHdr *)hdr)->status);
    return len;
}


static void
hdrStore(hdr, dst, len, arg)
    VOID	*hdr, *arg;
    char 	*dst;
    long  	len;
{
    SelHdr	h;

    xAssert( len == sizeof(SelHdr) );
    h.id = htonl(((SelHdr *)hdr)->id);
    h.status = htonl(((SelHdr *)hdr)->status);
    bcopy( (char *)&h, dst, len );
}


Sessn
selectCommonOpen( self, hlp, hlpType, p, hlpNum )
    Protl	self, hlp, hlpType;
    Part    	*p;
    long	hlpNum;
{
    Sessn   	s;
    PState 	*pstate = (PState *)self->state;
    ActiveKey	key;
    
    xTrace0(selectp, TR_MAJOR_EVENTS, "SELECT common open");
    bzero((char*)&key, sizeof(key));
    key.id = hlpNum;
    key.lls = xOpen(self, self, xGetProtlDown(self, 0), p);
    if ( key.lls == ERR_SESSN ) {
	xTrace0(selectp, TR_SOFT_ERRORS, "selectOpen: could not open lls");
	return ERR_SESSN;
    }
    if (mapResolve(pstate->activeMap, &key, (void**)&s) == XK_FAILURE) {
	/*
	 * session did not exist -- get an uninitialized one
	 */
	xTrace0(selectp, TR_MAJOR_EVENTS,
		"selectOpen -- no active session existed.  Creating new one.");
	s = selectCreateSessn(self, hlp, hlpType, &key);
    } else {
	xTrace1(selectp, TR_MAJOR_EVENTS, "Active sessn %x already exists", s);
	/*
	 * Undo the lls open
	 */
	xClose(key.lls);  
    }
    return s;
}


static Sessn
selectCreateSessn( self, hlp, hlpType, key )
    Protl	self, hlp, hlpType;  		
    ActiveKey	*key;
{
    Sessn 	s;
    SState	*state;
    PState	*pstate = (PState *)self->state;
    SelHdr 	*hdr;
    
    xTrace0(selectp, TR_FUNCTIONAL_TRACE, "selectCreateSessn");
    state = X_NEW(SState);
    bzero((char *)state, sizeof(SState));
    hdr = &state->hdr;
    hdr->status = SEL_OK;
    hdr->id = key->id;
    s = xCreateSessn(getSessnFuncs, hlp, hlpType, self, 1, &key->lls);
    s->binding = mapBind(pstate->activeMap, key, s);
    if ( s->binding == ERR_BIND ) {
	xTrace0(selectp, TR_ERRORS, "selectCreateSessn -- bind failed!");
	xClose(s);
	return ERR_SESSN;
    }
    s->state = (VOID *)state;
    return s;
}


XkReturn
selectCommonOpenEnable( self, hlp, hlpType, p, hlpNum )
    Protl   	self, hlp, hlpType;
    Part	*p;
    long	hlpNum;
{
    PassiveKey	key;
    PState 	*pstate = (PState *)self->state;
    
    xTrace0(selectp, TR_MAJOR_EVENTS, "SELECT common open enable");
    key = hlpNum;
    return defaultOpenEnable(pstate->passiveMap, hlp, hlpType, &key);
}


XkReturn
selectCommonOpenDisable( self, hlp, hlpType, p, hlpNum )
    Protl   	self, hlp, hlpType;
    Part    	*p;
    long	hlpNum;
{
    PassiveKey	key;
    PState 	*pstate = (PState *)self->state;
    
    xTrace0(selectp, TR_MAJOR_EVENTS, "SELECT open disable");
    key = hlpNum;
    return defaultOpenDisable(pstate->passiveMap, hlp, hlpType, &key);
}


static XkReturn
selectClose(s)
    Sessn   s;
{
    PState		*ps = (PState *)xMyProtl(s)->state;
    XkReturn	res;

    xTrace1(selectp, TR_EVENTS, "select_close of session %x", s);
    xAssert(xIsSessn(s));
    xAssert(s->rcnt <= 0);
    xClose(xGetSessnDown(s, 0));
    res = mapRemoveBinding(ps->activeMap, s->binding);
    xAssert( res == XK_SUCCESS );
    xDestroySessn(s);
    return XK_SUCCESS;
}


static XkReturn
selectCall( self, msg, rMsg )
    Sessn	self;
    Msg     	*msg, *rMsg;
{
    SState	*state = (SState *)self->state;
    SelHdr 	rHdr;
    void	*buf;

    xTrace0(selectp, TR_EVENTS, "in selectCall");
    xIfTrace(selectp, TR_DETAILED) {
	phdr(&state->hdr);
    }
    buf = msgPush(msg, sizeof(SelHdr));
    xAssert(buf);
    hdrStore(&state->hdr, buf, sizeof(SelHdr), 0);
    if ( xCall(xGetSessnDown(self, 0), msg, rMsg) == XK_FAILURE ) {
	return XK_FAILURE;
    }
    buf = msgPop(rMsg, sizeof(SelHdr));
    if (!buf) {
	xTrace0(selectp, TR_ERRORS, "selectCall -- msgPop failed");
	return XK_FAILURE;
    }
    hdrLoad(&rHdr, buf, sizeof(SelHdr), 0);
    if ( rHdr.status == SEL_FAIL ) {
	xTrace0(selectp, TR_ERRORS, "selectCall -- reply indicates failure ");
	return XK_FAILURE;
    }
    return XK_SUCCESS;
}


static XkHandle
selectPush( self, msg )
    Sessn	self;
    Msg     	*msg;
{
    SState	*state = (SState *)self->state;
    void	*buf;

    xTrace0(selectp, TR_EVENTS, "in selectPush");
    xIfTrace(selectp, TR_DETAILED) {
	phdr(&state->hdr);
    }
    buf = msgPush(msg, sizeof(SelHdr));
    xAssert(buf);
    hdrStore(&state->hdr, buf, sizeof(SelHdr), 0);
    return xPush(xGetSessnDown(self, 0), msg);
}


XkReturn
selectCallDemux( self, lls, msg, rmsg )
    Protl	self;
    Sessn	lls;
    Msg		*msg, *rmsg;
{
    SelHdr 	hdr;
    PassiveKey 	pKey;
    ActiveKey	aKey;
    Sessn   	s;
    Enable	*e;
    PState 	*pstate = (PState *)self->state;
    void	*buf;

    xTrace0(selectp, TR_EVENTS , "selectDemux");

    buf = msgPop(msg, sizeof(hdr));
    if (!buf) {
	xTrace0(selectp, TR_SOFT_ERRORS, "selectDemux -- msgPop failed!");
	return XK_FAILURE;
    }
    hdrLoad(&hdr, buf, sizeof(hdr), 0);

    xIfTrace(selectp, TR_DETAILED) {
	phdr(&hdr);
    }
    bzero((char*) &aKey, sizeof(aKey));
    aKey.id = hdr.id;
    aKey.lls = lls;
    if (mapResolve(pstate->activeMap, &aKey, (void**)&s) == XK_FAILURE) {
	/*
	 * no active session exists -- check for openenable
	 */
	pKey = aKey.id;
	xTrace1(selectp, TR_EVENTS, "Checking for enable using id: %d", pKey);
	if (mapResolve(pstate->passiveMap, &pKey, (void**)&e) == XK_FAILURE) {
	    xTrace0(selectp, TR_EVENTS, "selectDemux: no openenable done");
	} else {
	    xTrace0(selectp, TR_MAJOR_EVENTS,
		    "select_demux creates new server session");
	    s = selectCreateSessn(self, e->hlp, e->hlpType, &aKey);
	    if ( s != ERR_SESSN ) {
		xDuplicate(lls);
		xOpenDone(e->hlp, self, s);
	    }
	}
    } else {
	xTrace0(selectp, TR_MORE_EVENTS, "selectDemux found existing sessn");
    }
    if ( rmsg ) {
	/* 
	 * This really is a call demux
	 */
	if ( s != ERR_SESSN && xCallPop(s, lls, msg, 0, rmsg) == XK_SUCCESS ) {
	    hdr.status = SEL_OK;
	} else {
	    xTrace0(selectp, TR_EVENTS,
		    "selectDemux -- sending failure reply");
	    hdr.status = SEL_FAIL;
	}
	buf = msgPush(rmsg, sizeof(hdr));
	xAssert(buf);
	hdrStore(&hdr, buf, sizeof(hdr), 0);
    } else {
	/* 
	 * This is really a demux, not a calldemux
	 */
	xPop(s, lls, msg, 0);
    }
    return XK_SUCCESS;
}


XkReturn
selectDemux( s, lls, msg )
    Protl	s;
    Sessn	lls;
    Msg		*msg;
{
    xTrace0(selectp, TR_EVENTS, "SELECT demux");
    return selectCallDemux(s, lls, msg, 0);
}

static XkReturn
selectCallPop( s, lls, msg, hdr, rMsg )
    Sessn	s, lls;
    Msg     	*msg, *rMsg;
    VOID	*hdr;
{
    xTrace0(selectp, TR_EVENTS, "SELECT callpop");
    return xCallDemux(xGetUp(s), s, msg, rMsg);
}

static XkReturn
selectPop( s, lls, msg, hdr )
    Sessn	s, lls;
    Msg     	*msg;
    VOID	*hdr;
{
    xTrace0(selectp, TR_EVENTS, "SELECT_pop");
    return xDemux(xGetUp(s), s, msg);
}

int
selectControlProtl(self, op, buf, len)
    Protl	self;
    int 	op, len;
    char	*buf;
{
    return xControlProtl(xGetProtlDown(self, 0), op, buf, len);
}

int
selectCommonControlSessn(self, op, buf, len)
    Sessn	self;
    int 	op, len;
    char	*buf;
{
    switch ( op ) {
      case GETMAXPACKET:
      case GETOPTPACKET:
	if ( xControlSessn(xGetSessnDown(self, 0), op, buf, len) < sizeof(int) ) {
	    return -1;
	}
	*(int *)buf -= sizeof(SelHdr);
	return (sizeof(int));
	
      default:
	return xControlSessn(xGetSessnDown(self, 0), op, buf, len);
    }
}
  

static void
phdr( h )
    SelHdr	*h;
{
    xTrace2(selectp, TR_ALWAYS, "SELECT header: id == %d  status == %s",
	    h->id,
	    (h->status == SEL_OK) ? "SEL_OK" :
	    (h->status == SEL_FAIL) ? "SEL_FAIL" :
	    "UNKNOWN");
}


static void
getSessnFuncs( s )
    Sessn	s;
{
    s->call = selectCall;
    s->push = selectPush;
    s->callpop = selectCallPop;
    s->pop = selectPop;
    s->controlsessn = selectCommonControlSessn;
    s->close = selectClose;
}
