/*
 * $RCSfile: blast.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: blast.c,v $
 * Revision 1.2  1996/01/29 22:07:08  slm
 * Updated copyright and version.
 *
 * Revision 1.1  1995/07/28  22:08:26  slm
 * Initial revision
 *
 * Revision 1.42.2.2  1994/12/02  18:05:28  hkaram
 * Changed to new mapResolve interface
 *
 * Revision 1.42.2.1  1994/11/23  17:28:45  hkaram
 * New branch
 *
 * Revision 1.42  1993/12/13  21:11:29  menze
 * Modifications from UMass:
 *
 *   [ 93/11/12          yates ]
 *   Changed casting of Map manager calls so that the header file does it all.
 */

/*
 * Initialization and connection establishment / teardown routines
 */

#include "xkernel.h"
#include "blast.h"
#include "blast_internal.h"


#define checkPart(pxx, sxx, retVal) 					\
    if ( ! (pxx) || partLength(pxx) < 1 ) {				\
        xTrace1(blastp, TR_SOFT_ERRORS, "%s -- bad participants", sxx); \
	return retVal;							\
    }


#ifdef __STDC__

static XkReturn	blastClose( Sessn );
static Sessn		blastOpen( Protl, Protl, Protl, Part * );
static XkReturn	blastOpenEnable( Protl, Protl, Protl, Part * );
static XkReturn	blastOpenDisable( Protl, Protl, Protl, Part * );
static long		getRelProtNum( Protl, Protl, char * );
static void		protGetProc( Protl );
static void		sessnGetProc( Sessn );

#else

static XkReturn	blastClose();
static void	sessnGetProc();
static void	protGetProc();

#endif /* __STDC__ */


int 		traceblastp=0;
BlastMask	blastFullMask[BLAST_MAX_FRAGS + 1];


void
blast_init(self)
    Protl self;
{
    Part 	part;
    PState 	*pstate;
    int 	i;
    Protl	llp;
    
    xTrace0(blastp, TR_GROSS_EVENTS, "BLAST init");
    xAssert(xIsProtl(self));
    
    if ((llp = xGetProtlDown(self,0)) == ERR_PROTL) {
	xTrace0(blastp, TR_ERRORS, "blast couldn't access lower protocol");
	return;
    }
    protGetProc(self);
    pstate = (PState *)(self->state = xMalloc(sizeof(PState)));
    pstate->max_seq = 0;
    pstate->active_map = mapCreate(BLAST_ACTIVE_MAP_SZ, sizeof(ActiveID));
    pstate->passive_map = mapCreate(BLAST_PASSIVE_MAP_SZ, sizeof(PassiveID));
    pstate->send_map = mapCreate(BLAST_SEND_MAP_SZ, sizeof(BlastSeq));
    pstate->mstateStack = stackCreate(BLAST_MSTATE_STACK_SZ);
    semInit(&pstate->outstanding_messages, OUTSTANDING_MESSAGES);
    semInit(&pstate->createSem, 1);
    pstate->max_outstanding_messages = OUTSTANDING_MESSAGES;
    for (i=1; i <= BLAST_MAX_FRAGS; i++) {
	BLAST_FULL_MASK(blastFullMask[i], i);
    }
    partInit(&part, 1);
    partPush(part, ANY_HOST, 0);
    xOpenEnable(self, self, llp, &part);
}


static long
getRelProtNum( hlp, llp, s )
    Protl	hlp, llp;
    char	*s;
{
    long	n;

    n = relProtNum(hlp, llp);
    if ( n == -1 ) {
	xTrace3(blastp, TR_SOFT_ERRORS,
	       "%s: couldn't get prot num of %s relative to %s",
	       s, hlp->name, llp->name);
	return -1;
    }
    return n;
}


static Sessn
blastOpen(self, hlp, hlpType, p)
    Protl self, hlp, hlpType;
    Part *p;
{
    Sessn	s;
    Sessn	lls = ERR_SESSN;
    ActiveID 	active_id;
    PState 	*pstate = (PState *)self->state;
    
    xTrace0(blastp, TR_MAJOR_EVENTS, "BLAST open");
    checkPart(p, "blastOpen", ERR_SESSN);
    semWait(&pstate->createSem);
    if ( (active_id.prot = getRelProtNum(hlpType, self, "blastOpen")) == -1 ) {
	return ERR_SESSN;
    }
    if ( (lls = xOpen(self, self, xGetProtlDown(self, 0), p)) == ERR_SESSN ) {
	xTrace0(blastp, TR_MAJOR_EVENTS, "blast open: could not open lls");
	s = ERR_SESSN;
    } else {
	xTrace0(blastp, TR_MAJOR_EVENTS, "blast_open successfully opened lls");
	active_id.lls = lls;
	xIfTrace(blastp, TR_MORE_EVENTS) {
	    blastShowActiveKey(&active_id, "blast_open");
	}
	/*
	 * is there an existing session?
	 */
	if (mapResolve(pstate->active_map, &active_id, (void **)&s) ==
	    XK_FAILURE) {
	    xTrace0(blastp, TR_MAJOR_EVENTS,
		    "blast_open creating new session");
	    s = blastCreateSessn(self, hlp, hlpType, &active_id);
	    if ( s == ERR_SESSN ) {
		xClose(lls);
	    }
	} else {
	    xTrace0(blastp, TR_MAJOR_EVENTS, "blast_open: session exists");
	    xClose(lls);
	}
    }
    semSignal(&pstate->createSem);
    return s;
}    


Sessn
blastCreateSessn( self, hlp, hlpType, key )
    Protl self;
    Protl hlp, hlpType;
    ActiveID *key;
{
    PState	*pstate;
    SState 	*state;
    BLAST_HDR	*hdr;
    Sessn	s;

    pstate = (PState *)self->state;
    state = (SState *) xMalloc(sizeof(SState));
    bzero((char *)state, sizeof(SState));
    state->prot_id = key->prot;
    state->rec_map = mapCreate(BLAST_REC_MAP_SZ, sizeof(BlastSeq));
    /*
     * fill in header for messages that don't require fragmentation
     */
    hdr = &state->short_hdr;
    hdr->op = BLAST_SEND;
    hdr->prot_id = key->prot;
    hdr->seq = 0; /* protocol guarantees that 0 can never be a real seq */
    BLAST_MASK_CLEAR(hdr->mask);
    hdr->num_frag = 0;
    /*
     * Determine the maximum size of datagrams which this session
     * supports.  
     */
    if (xControlSessn(key->lls, GETOPTPACKET, (char *)&state->fragmentSize,
		 sizeof(state->fragmentSize)) < 0) {
	xTrace0(blastp, TR_ERRORS,
		"Blast could not get opt packet size from lls");
	if (xControlSessn(key->lls, GETMAXPACKET, (char *)&state->fragmentSize,
		     sizeof(state->fragmentSize)) < 0) {
	    xTrace0(blastp, TR_ERRORS,
		    "Blast could not get max packet size from lls");
	    xFree((char *)state);
	    return ERR_SESSN;
	}
    }
    xTrace1(blastp, TR_MAJOR_EVENTS,
	    "fragment size (from llp): %d", state->fragmentSize);
    xTrace1(blastp, TR_MAJOR_EVENTS, "blast hdr len: %d", BLASTHLEN);
    state->fragmentSize -= BLASTHLEN;
    xTrace2(blastp, TR_MAJOR_EVENTS,
	    "Blast fragmenting into packets of size %d, max dgm: %d",
	    state->fragmentSize, state->fragmentSize * BLAST_MAX_FRAGS);
    state->per_packet = PER_PACKET;
    /*
     * create session and bind to address
     */
    s = xCreateSessn(sessnGetProc, hlp, hlpType, self, 1, &key->lls);
    s->state = (VOID *)state;
    state->self = s;
    s->binding = mapBind(pstate->active_map, key, s);
    /*
     * just to be paranoid
     */
    if (s->binding == ERR_BIND) {
	xTrace0(blastp, TR_ERRORS, "blast_open: could not bind session");
	blastClose(s);
	return ERR_SESSN;
    }
    xTrace1(blastp, TR_MAJOR_EVENTS, "blast_open returns %x", s);
    return s;
}


static XkReturn
blastOpenEnable(self, hlp, hlpType, p)
    Protl self, hlp, hlpType;
    Part *p;
{
    PassiveID 	key;
    PState 	*pstate;
    
    xTrace0(blastp, TR_MAJOR_EVENTS, "BLAST open enable");
    checkPart(p, "blastOpenEnable", XK_FAILURE);
    pstate = (PState *)self->state;
    if ( (key = getRelProtNum(hlpType, self, "BLAST openEnable")) == -1 ) {
	return XK_FAILURE;
    }
    xTrace1(blastp, TR_MAJOR_EVENTS,
	    "blast_openenable: ext_id.prot_id = %d", key);
    return defaultOpenEnable(pstate->passive_map, hlp, hlpType, &key);
}


static XkReturn
blastOpenDisable( self, hlp, hlpType, p )
    Protl self, hlp, hlpType;
    Part *p;
{
    PassiveID   key;
    PState      *pstate;
    
    xTrace0(blastp, TR_MAJOR_EVENTS, "BLAST open disable");
    checkPart(p, "blastOpenDisable", XK_FAILURE);
    pstate = (PState *)self->state;
    if ( (key = getRelProtNum(hlpType, self, "BLAST openDisable")) == -1 ) {
	return XK_FAILURE;
    }
    return defaultOpenDisable(pstate->passive_map, hlp, hlpType, &key);
}


/* 
 * Decrement the internal reference count and call blastClose if appropriate
 */
void
blastDecIrc( s )
    Sessn	s;
{
    SState	*ss = (SState *)s->state;

    xAssert(ss->ircnt > 0);
    if ( --ss->ircnt == 0 && s->rcnt == 0 ) {
	blastClose(s);
    }
}


/*
 * blastClose: blast does no caching of sessions -- when a session's
 * reference count drops to zero it is destroyed.
 */
static XkReturn
blastClose(s)
    Sessn s;
{
    SState *ss;
    PState	*ps;
    Sessn	lls;
    
    xTrace1(blastp, TR_MAJOR_EVENTS, "blast_close of session %x", s);

    if (!s) return(XK_SUCCESS); 
    ss = (SState *)s->state;
    ps = (PState *)s->myprotl->state;

    xAssert(s->rcnt == 0);
    xTrace1(blastp, TR_EVENTS, "blast_close ircnt == %d", ss->ircnt);
    if ( ss->ircnt ) {
	return XK_SUCCESS;
    }
    if (s->binding) {
	mapRemoveBinding(ps->active_map,s->binding);
    }

    /* free blast state */
    if (ss->rec_map) {
        blast_mapFlush(ss->rec_map);
        mapClose(ss->rec_map);
    }

    if ((lls = xGetSessnDown(s, 0)) != ERR_SESSN) {
	xClose(lls);
    }
    xDestroySessn(s);
    return XK_SUCCESS;
}

    	
MSG_STATE *
blastNewMstate(s)
    Sessn s;
{
    PState	*ps;
    MSG_STATE	*mstate;
    
    ps = (PState *)xMyProtl(s)->state;
    mstate = (MSG_STATE *)stackPop(ps->mstateStack);
    if ( mstate == 0 ) {
	xTrace0(blastp, TR_MORE_EVENTS, "blast_pop: new_state created ");
	mstate = X_NEW(MSG_STATE);
    }
    bzero((char *)mstate, sizeof(MSG_STATE));
    mstate->rcnt = 1;
    /* 
     * Add a reference count for this message state
     */
    ((SState *)s->state)->ircnt++;
    xTrace1(blastp, TR_MORE_EVENTS, "blast receive returns %x", mstate);
    return mstate;
}


static void
sessnGetProc(s)
    Sessn s;
{
    s->push = blastPush;
    s->controlsessn = blastControlSessn;
    s->close = blastClose;
    s->pop = blastPop;
}


static void
protGetProc(p)
    Protl p;
{
    p->controlprotl = blastControlProtl;
    p->open = blastOpen;
    p->openenable = blastOpenEnable;
    p->demux = blastDemux;
    p->opendisable = blastOpenDisable;
}
