
/*****************************************************************************
* Copyright (c) 1990-96 Digi International Inc., All Rights Reserved
*
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
*
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/

/*
 * $Id: shead.c,v 1.3 1996/09/04 18:53:48 gary Exp $
 */

#include "types.h"
#include "stream.h"
#include "errno.h"
#include "queue.h"
#include "cnt_arry.h"
#include "commonpt.h"

#include "digilib.h"
#include "digisoh.h"
#include "sohshead.h"
#include "sohalloc.h"
#include "sohdstrm.h"

#ifndef NULL
#define NULL (char *)0
#endif

int             errno;

#ifdef	SQUEEZE
extern strm_t  *FAR * shead;
extern int      strmcnt;
#else
strm_t         **shead = NULL;
int             strmcnt = 0;
#endif

#ifndef STATIC
#define STATIC
#endif

STATIC struct module_info hdminfo = {0, "head", 0, 0, STRHIGH, STRLOW};

STATIC int      headrput(  ), headclose(  ), headwput(  ), headwsrv(  );
STATIC int      headopen(  );

static struct qinit headrinit =
{headrput, 0, 0, 0, 0, &hdminfo, 0};
static struct qinit headwinit =
{0, headwsrv, 0, 0, 0, &hdminfo, 0};

struct streamtab headinfo = {&headrinit, &headwinit, 0, 0};


extern int      gLinkError;


BOOLEAN 
shload(ULONG ulNumStreamHeadsToCreate)
/*++

Description: Create the stream head pointers.

Parameters:

	ulNumStreamHeadsToCreate - the number of stream heads to create

Return Value:

	 NTSTATUS

--*/
{

	ULONG	ulSize;
	BOOLEAN bRc = TRUE;

	// The 0 index is not used
	ulNumStreamHeadsToCreate++;
	ulSize = sizeof( strm_t *) * ulNumStreamHeadsToCreate;

	if ( shead = DigiAllocMem( NonPagedPool, ulSize  ) )
	{
		RtlZeroMemory( shead, ulSize );

		// set the stream count
		strmcnt = ulNumStreamHeadsToCreate;
	}
	else
	{
		// error handling
		DigiDump( DIGIERRORS, ( "SOH: Couldn't allocate stream heads\n" ) );

		strmcnt = 0;
		bRc = FALSE;
	}

	return(bRc);
}

void 
shunload()
/*++

Description: Get rid of the stream head memory

Parameters:

Return Value:


--*/
{


	if ( shead  )
	{
		DigiFreeMem(shead);
		shead = NULL;
	}

	return;
}



int 
shopen( int dev, int flag, void *context )
/*++

Description:

	This routine opens a STREAMS device.

Parameters:

	dev - the device number
	flag - the flag for clone open or normal
	context - a context of this open stored in q_ptr

Return Value:

	 Stream number for success
	 -1 for failure

--*/
{
	strm_t         *s;
	int             sn, r;

	/* Shead[0] is unused (so that 0 means 'no stream') */

	for ( sn = 1; ( sn < strmcnt ); sn++ )
	{
		if ( shead[sn] == ( strm_t * ) 0 )
			break;
	}

	if ( sn < strmcnt )
	{
		s = ( strm_t * ) news( headinfo.st_rdinit, headinfo.st_wrinit );

		if ( s == ( strm_t * ) NULL )
		{
			DigiDump( DIGIFLOW, ("shopen: couldn't allocate a new stream\n"));

			errno = ENOSR;
			return -1;
		}

		shead[sn] = s;
	}
	else
	{
		DigiDump( DIGIFLOW, ("shopen: all streams in use\n"));

		errno = ENOSR;
		return -1;
	}

	if ( r = sopen( s, dev, flag, flag ) )
	{
		DigiDump( DIGIFLOW, ("shopen: couldn't open a stream\n"));

		shead[sn] = 0;
		freeq( s->str_sq );
		frees( s );
		errno = r;
		return -1;
	}

	RD( shead[sn]->str_sq )->q_ptr = ( caddr_t ) context;
	shead[sn]->str_sq->q_ptr = ( caddr_t ) context;

	return sn;
}


int 
shclose( int sn )
/*++

Description:

	This routine closes a Stream.

Parameters:

	sn - the Stream number.

Return Value:

	0 for success
	-1 for failure

--*/
{
	strm_t         *s;

	/* Check 'sn' is valid */
	if ( ( uint ) sn >= (uint) strmcnt || ( s = shead[sn] ) == ( strm_t * ) 0 )
	{
		DigiDump( DIGIFLOW, ("shclose: stream number invalid\n"));

		errno = EINVAL;
		return -1;
	}

	sclose( s );
	frees( s );
	shead[sn] = ( strm_t * ) 0;
	return 0;
}

// shlink
int
shlink( int muxsn, int lowsn )
{
	int             muxid;
	strm_t         *mux;
	strm_t         *low;

	/* Check 'sn' is valid */
	if ( ( uint ) muxsn >= (uint) strmcnt || ( mux = shead[muxsn] ) == ( strm_t * ) 0 )
	{
		gLinkError = EINVAL;
		return -1;
	}

	/* Check 'sn' is valid */
	if ( ( uint ) lowsn >= (uint) strmcnt || ( low = shead[lowsn] ) == ( strm_t * ) 0 )
	{
		gLinkError = EINVAL;
		return -1;
	}

	muxid = slink( mux, low );

	if ( muxid < 1 )
		return -1;

	// JEFF - Commented out - it causes an endless loop
//	while ( runq(  ) );

	return muxid;
}


/************************/
/*	shpush		*/
/************************/
//int shpush(sn, m)
//int sn, m;
int 
shpush( int sn, int m )
{
	strm_t         *s;

	/* Check 'sn' is valid */
	if ( ( uint ) sn >= (uint) strmcnt || ( s = shead[sn] ) == ( strm_t * ) 0 )
	{
		errno = EINVAL;
	}

	errno = spush( s, m );

	return ( errno ) ? -1 : 0;
}


int 
shput( int sn, mblk_t * mp )
/*++

Description:

	This routine sends a message downstream.

Parameters:

	sn - the Stream number
	mp - a pointer to the message block

Return Value:

	1 if the message was sent and there is room for more
	0 if the message was sent but there is not room for more
	-1 if the Stream number was invalid

--*/
{
	strm_t         *s;
	queue_t        *q;
	int             result;
	int             send_rpt = 0;

	/* Check 'sn' is valid */
	if ( ( uint ) sn >= (uint) strmcnt || ( s = shead[sn] ) == ( strm_t * ) 0 )
	{
		DigiDump( DIGIFLOW, ("shput: stream number invalid\n"));
		DigiDump( DIGIFLOW, ("sn 0x%x , ns 0x%x ,s 0x%x\n", sn, strmcnt, s ));

		// set errno ?

		return -1;
	}

	if ( mp == ( mblk_t * ) 0 )
	{
		q = s->str_sq->q_next->q_can;

		if ( q )
		{
			return ( q->q_count < q->q_hiwat );
		}
		else
			return 1;
	}

	q = s->str_sq;

	putnext( q, mp );

	result = canput( q->q_next );

	return result;
}

mblk_t *
shget( int sn )
/*++

Description:

	This routine retrieves a message from the Stream head read queue.

Parameters:

	sn - the Stream number

Return Value:

	A pointer to the message block if one is pending
	0 if no messages are pending
	-1 if sn is invalid

--*/
{
	strm_t         *s;

	/* Check 'sn' open OK */
	if ( ( uint ) sn >= (uint) strmcnt || ( s = shead[sn] ) == ( strm_t * ) 0 )
	{
		DigiDump( DIGIFLOW, ("shget: stream number invalid\n"));
		DigiDump( DIGIFLOW, ("sn 0x%x , ns 0x%x ,s 0x%x\n", sn, strmcnt, s ));

		// set errno ???
		return (( mblk_t *)(-1));
	}

	/* Get first message on read queue */
	return getq( RD( s->str_sq ) );
}


void 
shputbq( int sn, mblk_t * mp )
/*++

Description:

	This routine places a message back on the Stream head read queue.

Parameters:

	sn - the Stream number
	mp - a pointer to the message block

Return Value:

	None

--*/
{
	strm_t         *s;

	/* Check 'sn' open OK */
	if ( ( uint ) sn >= (uint) strmcnt || ( s = shead[sn] ) == ( strm_t * ) 0 )
	{
		DigiDump( DIGIFLOW, ("shputbq: stream number invalid\n"));
		DigiDump( DIGIFLOW, ("sn 0x%x , ns 0x%x ,s 0x%x\n", sn, strmcnt, s ));

		// set errno ???
		return ;
	}

	/* Put message back onto queue */
	putbq( RD( s->str_sq ), mp );
}

/************************/
/*	shset		*/
/************************/
//void shset(sn, minpsz, maxpsz, hiwat, lowat)
//int minpsz, maxpsz, hiwat, lowat;
void 
shset( int sn, int minpsz, int maxpsz, int hiwat, int lowat )
{
	strm_t         *s;
	queue_t        *q;

	/* Check 'sn' open OK */
	if ( ( uint ) sn >= (uint) strmcnt || ( s = shead[sn] ) == ( strm_t * ) 0 )
		return;

	q = RD( s->str_sq );

	q->q_minpsz = (short)minpsz;
	q->q_maxpsz = (short)maxpsz;
	q->q_hiwat = (unsigned short)hiwat;
	q->q_lowat = (unsigned short)lowat;
}

/************************/
/*	shstrm		*/
/************************/
//strm_t *shstrm(sn)
//int sn;
strm_t         *
shstrm( int sn )
{
	return shead[sn];
}


// local routines ???

STATIC int 
headwsrv( queue_t * q )
/*++

Description:

	This routine is the Stream head write side service routine. It is called 
	when the Stream is back enabled.

Parameters:

	q - a pointer to the Stream's write queue

Return Value:

	None

--*/
{
	PSOH_STREAM_OBJECT pStreamObj;

	ASSERT( q->q_ptr );

	// by convention q_ptr contains a pointer to the Stream Object
	pStreamObj = (PSOH_STREAM_OBJECT)q->q_ptr;

	// clear the blocked flag
	pStreamObj->PutmsgBlocked = FALSE; 

	DigiDump( DIGIVERYLOUD, ( "DigiSoh: SohStreamsPutmsg - unblocked\n" ) );

	// set event for STREAMS thread to start when back enabled
	// ??? KeSetEvent( &gSohRuntimeEnv.evStreams,
	//						0,
	//						FALSE );

	return 0;
}

STATIC int 
headrput( queue_t * q, mblk_t * mp )
/*++

Description:

	This routine is the Stream head read side put routine. It is called 
	by down stream modules to send messages upstream.

Parameters:

	q - a pointer to the Stream's read queue
	mp - a pointer to the message block

Return Value:

	None

--*/
{
	void           *context;
	int             type;

	ASSERT( q );
	ASSERT( mp );

	context = ( void * ) q->q_ptr;
	type = mp->b_datap->db_type;

	switch ( mp->b_datap->db_type )
	{
	case M_IOCACK:
	case M_IOCNAK:
		{
			// FAR ???
			struct iocblk FAR *iocp = ( struct iocblk FAR * ) mp->b_rptr;

			// process I_LINK and I_UNLINK acknowledgements here
			switch ( iocp->ioc_cmd )
			{
			case I_LINK:
				// end_link(q, ms, mp);
				// end_link(q, context, mp);

				if ( mp->b_datap->db_type == M_IOCNAK )
				{
					if ( ( gLinkError = iocp->ioc_error ) == 0 )
					{
						// set to EINVAL if no error set

						gLinkError = EINVAL;
					}

					// ??? lblk = (struct linkblk FAR *)(mp->b_cont->b_rptr);
					// ??? sunlink(ms->strm, lblk->l_index);
				}
				else
				{
					gLinkError = 0;
				}

				freemsg( mp );

				return 0;

			case I_UNLINK:
				freemsg( mp );

				return 0;

			default:
				break;
			}

			// fall through if not I_LINK or I_UNLINK
		}

	case M_DATA:
	case M_PROTO:
	case M_PCPROTO:
		{
			// put these messages on the read queue for shget
			//
			// can I figure out a way to get this onto the competed getmsg
			// queue and set the event ???
			//
			// at least set the stream event so it runs again right way
			//
			putq( q, mp );

			break;
		}

	case M_FLUSH:
		// if mp->b_rptr & FLUSHR
		//		flushq( read q )
		//		mp->b_rptr &= ~FLUSHR
		//
		// what about completed getmsg list ???
		//
		// if mp->b_rptr & FLUSHW
		// 		qreply (q, mp)
		//
		// break

	default:
		freemsg( mp );

		break;
	}

	return 0;
}


queue_t        *
fd_to_queue( int sn )
{
	strm_t         *s;

	/* Check 'sn' is valid */
	if ( ( uint ) sn >= (uint) strmcnt || ( s = shead[sn] ) == ( strm_t * ) 0 )
		return 0;

	return RD( s->str_sq->q_next );
}


queue_t        *
fd_to_r_queue( int sn )
{
	strm_t         *s;

	/* Check 'sn' is valid */
	if ( ( uint ) sn >= (uint) strmcnt || ( s = shead[sn] ) == ( strm_t * ) 0 )
		return 0;

	return RD( s->str_sq );
}
