/*++
*******************************************************************************
* Copyright (c) 1990-2000 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				*
*																										*
*******************************************************************************

Module Name:

   dxbv1.c

Abstract:

   This module encapsulates all DXB rev1 specifics, both commands and responses.

Author:

   Michael Zarns

--*/

#include "digidxb.h"
#pragma hdrstop
#if 0
#include "alloc.h"
#else
#undef NCLASS		/* We don't want to clash with the system's NCLASS */
#define NCLASS    10
struct clinit
{
    int32 ci_count;
    int32 ci_size;
};
struct alloc
{
    int32 n_qpairs;
    int32 n_streams;
    int32 n_mblocks;
    int32 n_classes;
    int32 n_bcalls;

    int32 n_putwait;
    int32 n_nrdy;
    int32 n_timeout;

    int32 n_loscale;
    int32 n_medscale;
    struct clinit n_classinfo[NCLASS];
};
#endif
#include "dxbshare.h"

#define DXB_V1_ALIGN_HDR		TRUE
#define DXB_V1_ALIGN_CTRL		TRUE
#define DXB_V1_ALIGN_DATA		TRUE

#define DXB_V1_PAD_HDR			(DXB_V1_ALIGN_CTRL)
#define DXB_V1_PAD_CTRL			(DXB_V1_ALIGN_DATA)
#define DXB_V1_PAD_DATA			(DXB_V1_ALIGN_HDR)

#define DXB_IOCACK       0
#define DXB_IOCNAK       1


typedef struct OPEN_BLOCK
{
	struct open_ctl Header;
	char	          facname[6] ;		/* facility Name */
} OPEN_BLOCK;


STATIC NTSTATUS
dxbV1Open(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 Channel,
	IN DS_OPEN const         *pOpen )
/*++

Description:

	This routine builds a dxb Open command message and uses dxbQueueWriteCommand to 
	send it down.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which to place the request.

	pOpen
		The client supplied Open request.

Return Value:

	STATUS_SUCCESS if successful.

--*/
{
	MSG_OPEN Msg = { 0 };
	OPEN_BLOCK OpenBlock = { 0 };

	strcpy( OpenBlock.facname, pOpen->DeviceName );

	Msg.type = MSG_OPEN_TYPE;
	Msg.chan = (UCHAR)Channel;
	Msg.cp_len = sizeof OpenBlock;
	Msg.dp_len = (USHORT)pOpen->cbUserData;

	return ( dxbQueueWriteCommand( pExt, &Msg, sizeof Msg, &OpenBlock, sizeof OpenBlock,
	                               pOpen->pProviderData, pOpen->cbUserData, FALSE, DXB_V1_ALIGN_DATA ) );
}


STATIC NTSTATUS
dxbV1Close(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 Channel,
	IN DS_CLOSE const        *pClose )
/*++

Description:

	This routine builds a dxb Close command message and uses dxbQueueWriteCommand to 
	send it down.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which to place the request.

	pClose
		The close request.  Not used.

Return Value:

	STATUS_SUCCESS if successful.

--*/
{
	MSG_CLOSE Msg = { 0 };
	UNREFERENCED_PARAMETER( pClose );
	Msg.type = MSG_CLOSE_TYPE;
	Msg.chan = (UCHAR)Channel;
	return( dxbQueueWriteCommand( pExt, &Msg, sizeof Msg, NULL, 0, 
	                              NULL, 0, FALSE, DXB_V1_ALIGN_DATA ) );
}


STATIC NTSTATUS
dxbV1Ioctl(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 Channel,
	IN DS_IOCTL const        *pIoctl )
/*++

Description:

	This routine builds a dxb Ioctl command message and uses dxbQueueWriteCommand to 
	send it down.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which to place the request.

	pIoctl
		The client supplied Ioctl request.

Return Value:

	STATUS_SUCCESS if successful.

--*/
{
	MSG_IOCTL Msg = { 0 };
	struct iocblk IoctlBlock = { 0 };

	IoctlBlock.ioc_cmd   = pIoctl->Command;
	IoctlBlock.ioc_id    = Channel;
	IoctlBlock.ioc_count = pIoctl->cbDataIn;

	Msg.type = MSG_IOCTL_TYPE;
	Msg.chan = (UCHAR)Channel;
	Msg.cp_len = sizeof IoctlBlock;
	Msg.dp_len = (USHORT)pIoctl->cbDataIn;

	return( dxbQueueWriteCommand( pExt, &Msg, sizeof Msg, &IoctlBlock, sizeof IoctlBlock,
	                              pIoctl->pProviderDataIn, pIoctl->cbDataIn, FALSE, DXB_V1_ALIGN_DATA ) );
}


STATIC NTSTATUS
dxbV1PutMsg(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 Channel,
	IN DS_PUTMSG const       *pPutMsg )
/*++

Description:

	This routine builds a dxb PutMsg command message and uses dxbQueueWriteCommand to 
	send it down.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which to place the request.

	pPutMsg
		The client supplied PutMsg request.

Return Value:

	STATUS_SUCCESS if successful.

--*/
{
	DXB_MSG_TYPE Msg = { 0 };
	ULONG cbMsg;
	NTSTATUS Status;
	
	ASSERT( pPutMsg->pControl != NULL && pPutMsg->cbControl != 0 ||
	        pPutMsg->pControl == NULL && pPutMsg->cbControl == 0 )

	Msg.chan = (UCHAR)Channel;

	if ( pPutMsg->pControl != NULL )
	{
		// M_PROTO and M_PCPROTO
		cbMsg = sizeof( MSG_CONTROL_DATA );

		Msg.type = MSG_CONTROL_DATA_TYPE;
		Msg.st.priority = pPutMsg->Priority == DS_PRIORITY_HIGH ? 1 : 0;
		Msg.cp_len = (USHORT)pPutMsg->cbControl;
		Msg.dp_len = (USHORT)pPutMsg->cbData;
	}
	else
	{
		// M_DATA
		cbMsg = sizeof( MSG_DATA );

		Msg.type = MSG_DATA_TYPE;
		Msg.st.data_len = (USHORT)pPutMsg->cbData;
	}

	Status = dxbQueueWriteCommand( pExt, &Msg, cbMsg,
	                               pPutMsg->pProviderControl, pPutMsg->cbControl, 
	                               pPutMsg->pProviderData, pPutMsg->cbData, 
	                               FALSE, DXB_V1_ALIGN_DATA );

	return Status;
}


STATIC NTSTATUS
dxbV1Link(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 Channel,
	IN DS_LINK const         *pLink )
/*++

Description:

	This routine builds a dxb Link command message and uses dxbQueueWriteCommand to 
	send it down.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which to place the request.

	pLink
		The client supplied Link request.

Return Value:

	STATUS_SUCCESS if successful.

--*/
{
	MSG_OOB_IOCTL Msg = { 0 };
	DXB_LINK Link = { 0 };

	Link.index = (ULONG)(ULONG_PTR)pLink->hStreamLower;

	Msg.type = MSG_OOB_IOCTL_TYPE;
	Msg.chan = (UCHAR)Channel;
	Msg.subtype = STE_LINK;
	Msg.cp_len = sizeof Link;

	return( dxbQueueWriteCommand( pExt, &Msg, sizeof Msg, &Link, sizeof Link, 
	                              NULL, 0, FALSE, DXB_V1_ALIGN_DATA ) );
}


STATIC NTSTATUS
dxbV1CreditAward(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 Channel,
	IN USHORT                nCredits )
/*++

Description:

	This routine builds a dxb credit award command message and uses 
	dxbQueueWriteCommand to send it down.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which to place the request.

	nCredits
		The number of credits to award.  Typically, the total outstanding for this stream.

Return Value:

	STATUS_SUCCESS if successful.

--*/
{
	MSG_FLOW_CONTROL Msg = { 0 };

	Msg.type = MSG_FLOW_CONTROL_TYPE;
	Msg.chan = (UCHAR)Channel;
	Msg.pos_rpt_value = nCredits;

	return( dxbQueueWriteCommand( pExt, &Msg, sizeof Msg, NULL, 0, NULL, 0, 
	                              FALSE, DXB_V1_ALIGN_DATA ) );
}


STATIC ULONG  
dxbV1EventOpenAck(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN MSG_OPEN_ACK const *  pOpenAck )
/*++

Description:

	This routine is called to handle an OPEN_ACK event from the adapter.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading control/data.

	pOpenAck
		The open response from the adapter

Return Value:

	New value for QOut.  Always == QOut.

--*/
{
	ASSERT( pOpenAck );
	ASSERT( pExt->CreditDefault.FCost <= MAXUCHAR );
	dxbStreamOnOpenComplete( pExt, Channel, pOpenAck->value, (UCHAR)pExt->CreditDefault.FCost );
	return QOut;
}


STATIC ULONG
dxbV1EventCloseAck(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN MSG_CLOSE_ACK const   *pCloseAck )
/*++

Description:

	This routine is called to handle a CLOSE_ACK event from the adapter.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading control/data.

	pCloseAck
		The response from the adapter

Return Value:

	None

--*/
{
	ASSERT( pCloseAck );
	dxbStreamOnCloseComplete( pExt, Channel );
	return QOut;
}


STATIC ULONG
dxbV1EventData(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN DXB_MSG_TYPE const    *pMsg )
/*++

Description:

	This routine is called to handle DATA and CONTROL_DATA messages from the adapter.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading control/data.

	pMsg
		The header of the message from the adapter.

Return Value:

	QOffset.  New value for QOut.

--*/
{
	ULONG cbCtrl, cbData;
	DS_PRIORITY Priority;

	ASSERT( pMsg != NULL );
	ASSERT( pMsg->type == MSG_DATA_TYPE || pMsg->type == MSG_CONTROL_DATA_TYPE );
	if ( pMsg->type == MSG_CONTROL_DATA_TYPE )
	{
		Priority = pMsg->st.priority ? DS_PRIORITY_HIGH : DS_PRIORITY_LOW;
		cbCtrl = pMsg->cp_len;
		cbData = pMsg->dp_len;
	}
	else
	{
		Priority = DS_PRIORITY_LOW;
		cbCtrl = 0;
		cbData = pMsg->st.data_len;
	}

	return dxbStreamOnDataEvent( pExt, Channel, QOut, cbCtrl, cbData, Priority );
}


STATIC ULONG
dxbV1EventIoctl(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN DXB_MSG_TYPE const    *pMsg )
/*++

Description:

	This routine is called to handle Ioctl response events from the adapter.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading ioctl response data.

	pMsg
		The header of the message from the adapter.

Return Value:

	QOffset.  New value for QOut.

--*/
{
	struct iocblk Control;
	LONG ReturnValue;
	LONG ErrorCode;
	ULONG QOffset;
	
	ASSERT( pMsg );
	ASSERT( pMsg->cp_len == sizeof Control );
	QOffset = dxbEventQueueRead( pExt, QOut, &Control, pMsg->cp_len, TRUE, DXB_V1_PAD_CTRL );

	ASSERT( pMsg->st.ioctl_resp == DXB_IOCACK ||
	        pMsg->st.ioctl_resp == DXB_IOCNAK );

	if ( pMsg->st.ioctl_resp == DXB_IOCACK )
	{
		ReturnValue = Control.ioc_rval;
		ErrorCode   = Control.ioc_error;
	}
	else
	{
		ReturnValue = -1;
		ErrorCode   = Control.ioc_error ? Control.ioc_error : EINVAL;
	}

	return dxbStreamOnIoctlComplete( pExt, Channel, QOffset, 
	                                 ReturnValue, ErrorCode, pMsg->dp_len );
}


STATIC ULONG
dxbV1EventCreditReport(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN DXB_MSG_TYPE const    *pMsg )
/*++

Description:

	This routine is called to handle Credit or "position report" events from the 
	adapter.  These are sent to give back credits we previously used while sending
	data via PutMsg requests.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading ioctl response data.

	pMsg
		The header of the message from the adapter.

Return Value:

	None.

--*/
{
	ASSERT( pMsg );
	dxbStreamOnCreditEvent( pExt, Channel, pMsg->st.pos_rpt_value );
	return QOut;
}


STATIC ULONG
dxbV1EventEndLink( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN DXB_MSG_TYPE const    *pMsg )
/*++

Description:

	This routine is called to handle the OOB Ioctl ack END_LINK.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading ioctl response data.

	pMsg
		The header of the message from the adapter.

Return Value:

	Normalized QOffset after reading End_Link data.

--*/
{
	DXB_LINK Link;
	ULONG NewQOut;

	ASSERT( pMsg->cp_len+pMsg->dp_len == sizeof Link );
	NewQOut = dxbEventQueueRead( pExt, QOut, &Link, sizeof Link, TRUE, DXB_V1_PAD_DATA );

	dxbStreamOnLinkComplete( pExt, Channel, Link.rval, Link.error );
	return NewQOut;
}


STATIC ULONG
dxbV1EventFlush( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN DXB_MSG_TYPE const    *pMsg )
/*++

Description:

	This routine is called to handle the OOB Ioctl ack for Flush.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading ioctl response data.

	pMsg
		The header of the message from the adapter.

Return Value:

	Normalized QOffset after reading data.

--*/
{
	// Ignore response
	return dxbEventQueueRead( pExt, QOut, NULL, pMsg->cp_len+pMsg->dp_len, TRUE, DXB_V1_PAD_DATA );
}


STATIC ULONG
dxbV1EventError( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN DXB_MSG_TYPE const    *pMsg )
/*++

Description:

	This routine is called to handle the unsolicited OOB event ERROR.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading ioctl response data.

	pMsg
		The header of the message from the adapter.

Return Value:

	Normalized QOffset after reading data.

--*/
{
	return dxbEventQueueRead( pExt, QOut, NULL, pMsg->cp_len+pMsg->dp_len, TRUE, DXB_V1_PAD_DATA );
}


STATIC ULONG
dxbV1EventPcsig( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Channel,
	IN ULONG                 QOut,
	IN DXB_MSG_TYPE const    *pMsg )
/*++

Description:

	This routine is called to handle the unsolicited OOB event PCSIG.

Parameters:

	pExt
		The device extension defines the adapter.

	Channel
		The dxb channel (or stream) on which the event occurred.

	QOut
		Offset into Q at which to start reading ioctl response data.

	pMsg
		The header of the message from the adapter.

Return Value:

	Normalized QOffset after reading data.

--*/
{
	// Ignore 1 byte response
	return dxbEventQueueRead( pExt, QOut, NULL, 1, TRUE, DXB_V1_PAD_DATA );
}


STATIC INLINE ULONG 
dxbV1ReadHeader(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 QOut,
	OUT DXB_MSG_TYPE         *pHeader )
/*++

Description:

	This routine is used to read a single message header from the dxb adapter event 
	queue.  Message headers are always 4 or 8 bytes in length.  Process: Read 4 bytes, 
	then if needed, read another 4 bytes.

Parameters:

	pExt
		Device extension defining the adapter from which to read.

	QOut
		Offset into dxb event queue at which to start reading.
	
	pHeader
		Pointer to dxb message header structure which is filled in before returning.

Return Value:

	New value of QOut

--*/
{
	ULONG QOffset;
	ASSERT( QOut == dxbAlign32( QOut ) );
	QOffset = dxbEventQueueRead( pExt, QOut, pHeader, 4, FALSE, FALSE );

	switch ( pHeader->type )
	{
		case MSG_CONTROL_DATA_TYPE:
		case MSG_IOCTL_TYPE:
		case MSG_OOB_IOCTL_TYPE:
			QOffset = dxbEventQueueRead( pExt, QOffset, (PUCHAR)pHeader + 4, 4, FALSE, FALSE );
			break;

		case MSG_OPEN_ACK_TYPE:
		case MSG_CLOSE_ACK_TYPE:
		case MSG_NOP_TYPE:
		case MSG_FLOW_CONTROL_TYPE:
		case MSG_DATA_TYPE:
			break;

		default:
			SRAPRINT( DIGIERROR|DIGIBREAK,  ( "DXB!dxbV1ReadHeader: "
			          "Invalid message found in event Q, type = 0x%x!\n", 
			          pHeader->type) );
			break;
	}
	return QOffset;
}


STATIC ULONG 
dxbV1ReadCData( 
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 QOut, 
	IN PVOID                 pCtrl,
	IN ULONG                 cbCtrl,
	IN PVOID                 pData,
	IN ULONG                 cbData )
{
	ULONG QOffset = QOut;

	if ( cbCtrl )
		QOffset = dxbEventQueueRead( pExt, QOffset, pCtrl, cbCtrl, TRUE, DXB_V1_PAD_CTRL );

	if ( cbData )
		QOffset = dxbEventQueueRead( pExt, QOffset, pData, cbData, TRUE, DXB_V1_PAD_DATA );

	return (ULONG)dxbAlign32( QOffset );
}


STATIC ULONG
dxbV1EventProcessor(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 QStart )
/*++

Description:

	Grab a single message from the DXB event queue and process it.

Parameters:

	pExt
		Device extension for the adapter being serviced

	QOut
		QOut offset into the dxb event queue at which to start processing.

Return Value:

	New normalized QOut value.

--*/
{
	DXB_MSG_TYPE Msg;
	ULONG QOut = QStart;

	QOut = dxbV1ReadHeader( pExt, QStart, &Msg );
	ASSERT( Msg.chan <= 255 );

	switch ( Msg.type )
	{
		case MSG_OPEN_ACK_TYPE:
			QOut = dxbV1EventOpenAck( pExt, Msg.chan, QOut, (PVOID)&Msg );
			break;

		case MSG_CLOSE_ACK_TYPE:
			QOut = dxbV1EventCloseAck( pExt, Msg.chan, QOut, (PVOID)&Msg );
			break;

		case MSG_DATA_TYPE:
		case MSG_CONTROL_DATA_TYPE:
			QOut = dxbV1EventData( pExt, Msg.chan, QOut, (PVOID)&Msg );
			break;

		case MSG_IOCTL_TYPE:
			QOut = dxbV1EventIoctl( pExt, Msg.chan, QOut, (PVOID)&Msg );
			break;

		case MSG_FLOW_CONTROL_TYPE:
			QOut = dxbV1EventCreditReport( pExt, Msg.chan, QOut, (PVOID)&Msg );
			break;

		case MSG_OOB_IOCTL_TYPE:
			switch ( Msg.st.subtype )
			{
				case STE_END_LINK:
					QOut = dxbV1EventEndLink( pExt, Msg.chan, QOut, (PVOID)&Msg );
					break;

				case M_FLUSH:
					QOut = dxbV1EventFlush( pExt, Msg.chan, QOut, (PVOID)&Msg );
					break;

				case M_ERROR:
					QOut = dxbV1EventError( pExt, Msg.chan, QOut, (PVOID)&Msg );
					break;

				case M_PCSIG:
					QOut = dxbV1EventPcsig( pExt, Msg.chan, QOut, (PVOID)&Msg );
					break;

				default:
					SRAPRINT( DIGIBREAK|DIGIERROR, 
                         ( "DXB!EvtThread: Invalid OOB_IOCTL subtype, 0x%x!\n",
                         Msg.st.subtype ) );
					break;
			}
			break;

		case MSG_NOP_TYPE:
			break;

		default:
			SRAPRINT( DIGIBREAK|DIGIERROR, 
			          ( "DXB!V1EventProcessor: unknown message in event Queue 0x%x!\n",
			          Msg.type) );
			break;
	}

	return QOut;
}


STATIC ULONG
dxbV1GetCmdQOut(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Returns the current Dxb command queue QOut pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	QOut

--*/
{
	return DxbAdapterReadDword( pExt, pExt->DxbCmdQ.Descriptor.QOutOffset );
}


STATIC ULONG
dxbV1GetCmdQIn(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Returns the current Dxb command queue QIn pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	QIn

--*/
{
	ASSERT( pExt->DxbCmdQ.QOffset == DxbAdapterReadDword( pExt, pExt->DxbCmdQ.Descriptor.QInOffset ) );
	return pExt->DxbCmdQ.QOffset;
}


STATIC VOID
dxbV1SetCmdQIn(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 QIn )
/*++

Description:

	Set the current Dxb command queue QIn pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	None

--*/
{
	ASSERT( QIn == dxbAlign32( QIn ) );
	ASSERT( QIn < pExt->DxbCmdQ.cbTotal );
	DxbAdapterWriteDword( pExt, pExt->DxbCmdQ.Descriptor.QInOffset, QIn );
	pExt->DxbCmdQ.QOffset = QIn;
}


STATIC ULONG
dxbV1GetEventQIn(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Returns the current Dxb event queue QIn pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	QIn

--*/
{
	ULONG QIn = DxbAdapterReadDword( pExt, pExt->DxbEventQ.Descriptor.QInOffset );

	// Validity check QIn.  If out of range, something terrible has happened and we 
	// shouldn't try to read from the event queue.  Return a value equal to our current
	// QOut value - indicating the queue is empty  and there's no data to read.
	if ( QIn >= pExt->DxbEventQ.cbTotal )
		QIn = pExt->DxbEventQ.QOffset;

	return QIn;
}


STATIC ULONG
dxbV1GetEventQOut(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Returns the current Dxb event queue QOut pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	QOut

--*/
{
#if DBG
{
	ULONG QOut = DxbAdapterReadDword( pExt, pExt->DxbEventQ.Descriptor.QOutOffset );
	if ( QOut != pExt->DxbEventQ.QOffset )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "Dxb!V1GetEventQOut: Invalid QOut read from card, 0x%x\n", QOut ) );
	}
}
#endif

	return pExt->DxbEventQ.QOffset;
}


STATIC VOID
dxbV1SetEventQOut(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 QOut )
/*++

Description:

	Set the current Dxb event queue QOut pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	None

--*/
{
	ASSERT( QOut == dxbAlign32( QOut ) );
	ASSERT( QOut < pExt->DxbEventQ.cbTotal );
	DxbAdapterWriteDword( pExt, pExt->DxbEventQ.Descriptor.QOutOffset, QOut );
	pExt->DxbEventQ.QOffset = QOut;
}


STATIC ULONG
dxbV1aGetCmdQOut(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Returns the current Dxb command queue QOut pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	QOut

--*/
{
	return READ_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + DXB_MAILBOX_HOST_CMD_OUT ) );
}


STATIC ULONG
dxbV1aGetCmdQIn(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Returns the current Dxb command queue QIn pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	QIn

--*/
{
	ASSERT( pExt->DxbCmdQ.QOffset == READ_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + DXB_MAILBOX_HOST_CMD_IN ) ) );
	return pExt->DxbCmdQ.QOffset;
}


STATIC VOID
dxbV1aSetCmdQIn(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 QIn )
/*++

Description:

	Set the current Dxb command queue QIn pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	None

--*/
{
	ASSERT( QIn == dxbAlign32( QIn ) );
	ASSERT( QIn < pExt->DxbCmdQ.cbTotal );
	WRITE_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + DXB_MAILBOX_HOST_CMD_IN ), QIn );
	pExt->DxbCmdQ.QOffset = QIn;
}


STATIC ULONG
dxbV1aGetEventQIn(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Returns the current Dxb event queue QIn pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	QIn

--*/
{
	ULONG QIn = READ_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + DXB_MAILBOX_HOST_EVENT_IN ) );

	// Validity check QIn.  If out of range, something terrible has happened and we 
	// shouldn't try to read from the event queue.  Return a value equal to our current
	// QOut value - indicating the queue is empty  and there's no data to read.
	if ( QIn >= pExt->DxbEventQ.cbTotal )
		QIn = pExt->DxbEventQ.QOffset;

	return QIn;
}


STATIC ULONG
dxbV1aGetEventQOut(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Returns the current Dxb event queue QOut pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	QOut

--*/
{
#if DBG
{
	ULONG QOut = READ_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + DXB_MAILBOX_HOST_EVENT_OUT ) );
	if ( QOut != pExt->DxbEventQ.QOffset )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "Dxb!V1GetEventQOut: Invalid QOut read from card, 0x%x\n", QOut ) );
	}
}
#endif

	return pExt->DxbEventQ.QOffset;
}


STATIC VOID
dxbV1aSetEventQOut(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 QOut )
/*++

Description:

	Set the current Dxb event queue QOut pointer.

Parameters:

	pExt
		Defines the adapter

Return Value:

	None

--*/
{
	ASSERT( QOut == dxbAlign32( QOut ) );
	ASSERT( QOut < pExt->DxbEventQ.cbTotal );
	WRITE_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + DXB_MAILBOX_HOST_EVENT_OUT ), QOut );
	pExt->DxbEventQ.QOffset = QOut;
}


DXB_CODEC DxbV1 = 
{
	dxbV1Open,
	dxbV1Close,
	dxbV1PutMsg,
	dxbV1Ioctl,
	dxbV1Link,
	dxbV1CreditAward,
	dxbV1EventProcessor,
	dxbV1ReadCData,
	dxbV1GetCmdQIn,
	dxbV1GetCmdQOut,
	dxbV1SetCmdQIn,
	dxbV1GetEventQIn,
	dxbV1GetEventQOut,
	dxbV1SetEventQOut
};


// This version uses PLX mailboxes to transfer CMD and EVENT queue IN and OUT pointers
DXB_CODEC DxbV1a = 
{
	dxbV1Open,
	dxbV1Close,
	dxbV1PutMsg,
	dxbV1Ioctl,
	dxbV1Link,
	dxbV1CreditAward,
	dxbV1EventProcessor,
	dxbV1ReadCData,
	dxbV1aGetCmdQIn,
	dxbV1aGetCmdQOut,
	dxbV1aSetCmdQIn,
	dxbV1aGetEventQIn,
	dxbV1aGetEventQOut,
	dxbV1aSetEventQOut
};


// Credit limit.  nCredits can never exceed this value.  In dxbv1, limit defined by firmware.
#define V1_CREDIT_MAX			0x4650

// Fixed cost per packet.  Overhead added to each packet to prevent flooding with too 
// many short packets.
#define V1_CREDIT_FCOST			0x40

// Count of credits assumed at open.  DxbV1 firmware assumes this much.
#define V1_CREDIT_OPEN			0x4650

// Count of credits to be automatically sent immediately after open.  See also V2_CREDIT_OPEN.
#define V1_CREDIT_OPEN_AWARD	0x0

VOID 
dxbV1Init( 
	PDXB_DEVICE_EXTENSION pExt )
{
	pExt->Message = DxbV1;
	dxbCreditInit( &pExt->CreditDefault, 
	               V1_CREDIT_OPEN, V1_CREDIT_OPEN_AWARD, V1_CREDIT_MAX, V1_CREDIT_FCOST );
}

VOID 
dxbV1aInit( 
	PDXB_DEVICE_EXTENSION pExt )
{
	// Use PLX Mailboxes to transfer queue pointers.
	pExt->Message = DxbV1a;
	// All else is the same between V1 and V1a.
	dxbCreditInit( &pExt->CreditDefault, 
	               V1_CREDIT_OPEN, V1_CREDIT_OPEN_AWARD, V1_CREDIT_MAX, V1_CREDIT_FCOST );
}
