
/*M*                     
//                    INTEL CORPORATION PROPRIETARY INFORMATION
//      This software is supplied under the terms of a license agreement or
//      nondisclosure agreement with Intel Corporation and may not be copied
//      or disclosed except in accordance with the terms of that agreement.
//            Copyright (c) 1995-2000 Intel Corporation.  All Rights Reserved.
//
*M*/
/*
 * $Log:   //cospublic004/shareddata/ISC/ISC2x/COMMON/SRC/imb/imb_asyn.c.v  $
 * 
 *    Rev 1.3   Aug 03 1999 09:00:28   sganeshx
 * 1. set timeOut=0 in the function 'ImbAsyncGetMessage'
 *     to cause immediate exit from the while loop when there
 *    are no messages.
 *    
 * 
 *    Rev 1.2   31 Dec 1998 13:33:18   jpaiasno
 * fixed the problem related to missing the first 
 * Async. event record
 * 
 *    Rev 1.1   08 Dec 1998 10:55:44   jpaiasno
 * Added code to support async.
 * DPC events from the EMP console.
 * 
 *    Rev 1.5   07 Apr 1998 08:55:42   djneuhen
 * Fixed a couple of little bugs. The queued message size was
 * off by 3 (didn't account for BMC resp header), queue
 * bookkeeping when full was broken, and ImbAsyncMessageProcess
 * was telling GetSmsBuffer() that it had a zero length buffer.
 * 
 *    Rev 1.4   24 Mar 1998 15:06:30   fkhanx
 * Added function headers
 * 
 *    Rev 1.3   24 Mar 1998 14:38:50   fkhanx
 * Added copyright header
*/
#include "imb_os.h"
#include "imb_drv.h"

//
// Async message functions
//

//
// 
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// ImbAsyncInit
/////////////////////////////////////////////////////////////////////////////////////////////////

/*F*
//  Name:       ImbAsyncInit
//  Purpose:    Initialize the async message queue 
//  Context:    Called by main driver init routine
//  Returns:    void
//  Parameters:
//              asyncQ    Pointer to Asynchronous Q related info
//  Notes:      none
*F*/
void
ImbAsyncInit (
		ImbAsyncQueueInfo *asyncQ
	)
{
	//
	// init the queue info
	//
	asyncQ->startSeqNo	= 0;
	asyncQ->startOffset	= 0;
	asyncQ->count		= 0;

	ImbAsyncOsInit( asyncQ );
}


/////////////////////////////////////////////////////////////////////////////////////////////////
// ImbAsyncCopyMessage
/////////////////////////////////////////////////////////////////////////////////////////////////

/*F*
//  Name:       ImbAsyncCopyMessage
//  Purpose:    Copy the next Async message
//  Context:    Called by ImbAsyncGetMessage
//  Returns:    TRUE if success,FALSE if failure
//  Parameters:
//              seqNo    Sequence number of next message to get
//				buf		 Pointer to buffer to copy message in
//				length   IN length of buffer passed
//						 OUT length of message copied
//              asyncQ    Pointer to Asynchronous Q related info
//  Notes:      none
*F*/
static BOOL
ImbAsyncCopyMessage (
		ImbAsyncSeq			seqNo,
		BYTE *				buf,
		DWORD *				length,
		ImbAsyncQueueInfo *	asyncQ
	)
{
	int offset = (asyncQ->startOffset + seqNo - asyncQ->startSeqNo) % asyncQ->queueSize;

	if( *length < asyncQ->queue[ offset ].length )
		return FALSE;

	CopyMemory( buf, asyncQ->queue[ offset ].data, asyncQ->queue[ offset ].length );

	*length = asyncQ->queue[ offset ].length;

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////////////
// ImbAsyncGetMessage
/////////////////////////////////////////////////////////////////////////////////////////////////

/*F*
//  Name:       ImbAsyncGetMessage
//  Purpose:    Get the next Async message
//  Context:    Called as a response to Application wanting the next async
//				message
//  Returns:    IMB_SUCCESS ,if it can get the next message
//				IMB_MSG_NOT_AVAILABLE ,if no async message available		
//  Parameters:
//              req      request buffer
//				resp	 response buffer
//				length   IN length of buffer passed
//						 OUT length of message copied
//              asyncQ    Pointer to Asynchronous Q related info
//  Notes:      none
*F*/
IMB_STATUS
ImbAsyncGetMessage (
		ImbAsyncRequest *	req,		// his request
		ImbAsyncResponse *	resp,		// data buffer
		DWORD *				length,		// IN - size of buf and OUT - size of message
		ImbAsyncQueueInfo *	asyncQ
	)
{
	IMB_STATUS	status;
	//+1 leads to first record being bypassed.. removed the +1 (pai 12/31)
	// and used it later when the buffer is copied
	ImbAsyncSeq	seq		= req->lastSeq;	// increment to seq he wants now
	DWORD		timeOut = req->timeOut;

	while( TRUE ) {

		ImbAsyncOsLock ( asyncQ );

		if( asyncQ->queue == NULL ) {
			status = IMB_MSG_NOT_AVAILABLE;
			break;
		}
		//
		// Any messages ?
		//
		if( seq < asyncQ->startSeqNo )
			seq = asyncQ->startSeqNo;

		if( seq <  asyncQ->startSeqNo + asyncQ->count ) {
			//
			// yup, copy one up
			//
			*length -= MIN_ASYNC_RESP_SIZE;

			if( ImbAsyncCopyMessage( seq, resp->data, length, asyncQ ) != TRUE ) {
				status = IMB_RESPONSE_DATA_OVERFLOW;
		
			} else {

				//incerement the sequence number here (pai 12/31)
				resp->thisSeq	= seq +1;
				*length		   += MIN_ASYNC_RESP_SIZE;
				status			= IMB_SUCCESS;
			}
			break;
		} else {
			//
			// nope, let him know the bad news
			//
			// there is no point in waiting for the next message when we periodically poll
			// hence the timeOut = 0 is added here for immediate exit; this used to force
			// the driver to hog the CPU since it was 'busy waiting'
			timeOut = 0;	
			if( timeOut == 0 ) {
				status = IMB_MSG_NOT_AVAILABLE;
				break;
			}
			//
			// wait here until we either timeout or something comes in
			// There's a potential for a race here between the unlock of the
			// queue and the sleep/wait. The unlock/relock might want to be moved into
			// the OS specific wait code.
			//
			ImbAsyncOsUnLock ( asyncQ );

			ImbAsyncOsReqWait( asyncQ, timeOut );

			timeOut = 0;	// only wait once
		}
	}
	ImbAsyncOsUnLock ( asyncQ );

	return status;
}



/////////////////////////////////////////////////////////////////////////////////////////////////
// ImbGetDpcMessage
/////////////////////////////////////////////////////////////////////////////////////////////////

/*F*
//  Name:       ImbGetDpcMessage
//  Purpose:    Get the next Async DPC  message
//  Context:    Called by Application to get the next async
//				DPC message
//  Returns:    IMB_SUCCESS ,if it can get the next message
//	
//  Parameters:
//              req      request buffer
//				resp	 response buffer
//				length   IN length of buffer passed
//						 OUT length of message copied
//              asyncQ    Pointer to Asynchronous Q related info
//  Notes:      
*F*/
/*
IMB_STATUS
ImbDpcGetMessage (
		ImbAsyncRequest *	req,		// request
		ImbAsyncResponse *	resp,		// response data buffer
		DWORD *				length,		// IN - size of buf and OUT - size of message
		ImbAsyncQueueInfo *	asyncQ
	)
{
	IMB_STATUS	status;
	ImbAsyncSeq	seq		= req->lastSeq + 1;	// increment to seq he wants now
	DWORD		timeOut = req->timeOut;

	while( TRUE ) {

		ImbAsyncOsLock ( asyncQ );

		if( asyncQ->queue == NULL ) {
			status = IMB_MSG_NOT_AVAILABLE;
			break;
		}
		//
		// Any messages ?
		//
		if( seq < asyncQ->startSeqNo )
			seq = asyncQ->startSeqNo;

		if( seq <  asyncQ->startSeqNo + asyncQ->count ) {
			//
			// yup, copy one up
			//
					if ( CheckIfDpcMessage(ayncQ,seq)
						{
							*length -= MIN_ASYNC_RESP_SIZE;

							if( ImbAsyncCopyMessage( seq, resp->data, length, asyncQ ) != TRUE ) {
							status = IMB_RESPONSE_DATA_OVERFLOW;
		
							} else {
									resp->thisSeq	= seq;
									*length		   += MIN_ASYNC_RESP_SIZE;
									status			= IMB_SUCCESS;
							}
						break;


					}
			} else {
						//
						// nope, let him know the bad news
						//
							if( timeOut == 0 ) {
								status = IMB_MSG_NOT_AVAILABLE;
							break;
			}
			//
			// wait here until we either timeout or something comes in
			// There's a potential for a race here between the unlock of the
			// queue and the sleep/wait. The unlock/relock might want to be moved into
			// the OS specific wait code.
			//
			ImbAsyncOsUnLock ( asyncQ );

			ImbAsyncOsReqWait( asyncQ, timeOut );

			timeOut = 0;	// only wait once
		}
	}
	ImbAsyncOsUnLock ( asyncQ );

	return status;
}


*/






/////////////////////////////////////////////////////////////////////////////////////////////////
// ImbAsyncQueueMessage
/////////////////////////////////////////////////////////////////////////////////////////////////

/*F*
//  Name:       ImbAsyncQueueMessage
//  Purpose:    Queue the incoming Async Message into the Aysnc Q
//  Context:    Called by ImbAsyncMessageProcess
//  Returns:    void
//  Parameters:
//              msg      incoming message
//				length	 Length of message
//              context  OS dependent Context
//  Notes:      none
*F*/
void
ImbAsyncQueueMessage (
		BmcResponse *	msg,
		DWORD			length,
		void *			context
	)
{
	DWORD offset;
	ImbAsyncQueueInfo *	asyncQ = ImbAsyncQueue( context );

	 


	if( length > sizeof( asyncQ->queue[0].data ) )
		return;

	ImbAsyncOsLock ( asyncQ );

	if( asyncQ->queue != NULL ) {
		//
		// compute the destination of the msg and copy it
		//
		offset = (asyncQ->startOffset + asyncQ->count) % asyncQ->queueSize;

		length -= MIN_BMC_RESPONSE_SIZE;	// compensate for BMC resp header

		CopyMemory( asyncQ->queue[ offset ].data, msg->data, length );

		asyncQ->queue[ offset ].length = (BYTE) length;
		//
		// do bookkeeping on the queue
		//
		if( asyncQ->count < asyncQ->queueSize ) {
			asyncQ->count++;
		} else {
			//
			// queue's full, we overwrite the oldest, so move start up one
			//
			asyncQ->startOffset = (asyncQ->startOffset + 1) % asyncQ->queueSize;
			asyncQ->startSeqNo++;
		}
		//
		// complete any pending async requests
		//
		ImbAsyncOsReqWakeup( asyncQ );
		ImbAsyncOsNewWakeup(context);
	}

	ImbAsyncOsUnLock( asyncQ );
	
}

//

//
/////////////////////////////////////////////////////////////////////////////////////////////////
// ImbAsyncMessageProcess
/////////////////////////////////////////////////////////////////////////////////////////////////

/*F*
//  Name:       ImbAsyncMessageProcess
//  Purpose:    Check to see if there are any SMS messages to processes
//  Context:    Called by the (OS specific) async message polling loop
//  Returns:    void
//  Parameters:
//              context  OS dependent Context
//  Notes:      none
*F*/

void
ImbAsyncMessageProcess (
		void *	context
	)
{
	BYTE			respBuffer[MAX_BMC_RESPONSE_SIZE];
	BmcResponse *	bmcResp		= (BmcResponse *)respBuffer;
	DWORD			length		= sizeof(respBuffer);

	if( ! ImbIf(context)->smsBufferAvail() )
		return;
	//
	// something there for us, start a timeout and try and get the buffer
	//
	ImbOsStartReq( DEFAULT_MESSAGE_TIMEOUT, context );
	

	if( GetSmsBuffer( bmcResp, &length, context ) == IMB_SUCCESS ) {
		//
		// copy the data into the queue (it will wake up waiters)
		//
		ImbAsyncQueueMessage( bmcResp, length, context );
	}
	//
	// cleanup
	//
	ImbOsEndReq( context );
}