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

   dxbslave.c

Abstract:

   Routines to access adapter memory in slave mode (i.e. direct reads and writes to 
   adapter shared memory).  The adapter extension contains a table of function 
   pointers for read/write routines which are setup using the initialization 
	routines below.  Supports both windowed and flat memory adapters.
   
   These routines abstract the windowed nature of ISA adapters, presenting all 
   adapters as if shared memory were flat, like that of PCI adapters.

Author:

   Michael Zarns

--*/

#include "digidxb.h"

NTSTATUS 
dxbMapMemory( 
	IN PDIGI_IO_RESOURCE    pResource,
	IN OUT PDXB_IO_RESOURCE pDxbMem )
/*++

Description:

	Map in a single memory resource described by pResource.  Initialize the 
	DXB_IO_RESOURCE struct as appropriate for the mapped memory block.

Parameters:

	pResources
		Digi resource list defining PCI assigned resources for this adapter.

	pDxbMem
		Dxb resource descriptor struct in the device extension to hold the addresses
		and length for this memory block.

Return Value:

	STATUS_SUCCESS or failure status.

--*/
{
	NTSTATUS Status = STATUS_SUCCESS;
	PVOID pVirtual;

	// Default to FALSE.  Only set fUnmap to TRUE if we called MmMapIoSpace and 
	// successfully got an address!
	pDxbMem->fUnmap = FALSE;

	if ( pResource->AddressSpace == 0 )
	{
		pVirtual = MmMapIoSpace( pResource->BaseAddressTranslated, pResource->Length, FALSE );
		if ( pVirtual )
			pDxbMem->fUnmap = TRUE;
	} 
	else
		pVirtual = (PVOID)pResource->BaseAddressTranslated.LowPart;

	if ( pVirtual )
	{
		pDxbMem->Physical = pResource->BaseAddress;
		pDxbMem->Length   = pDxbMem->cbWindow = pResource->Length;
		pDxbMem->Virtual  = pVirtual;
	}
	else
	{
		pDxbMem->fUnmap = FALSE;
		Status = STATUS_UNSUCCESSFUL;
	}
	return Status;
}


STATIC INLINE VOID
dxbSlaveWriteFlat(
	PDXB_DEVICE_EXTENSION pExt,
	VOID const * const    pBuf,
	ULONG const           AdapterOffset,
	ULONG const           cbWrite )
/*++

Description:

	Writes data to adapter assuming flat memory structure (i.e. no windowing) like 
	that on a PCI adapter.

Parameters:

	pExt
		The device extension defining the adapter

	pBuf
		The data to be written to the adapter

	AdapterOffset
		Offset into adapter memory at which to start writing

	cbWrite
		Count of bytes to write

Return Value:

	None

--*/
{
	CHAR const * pIn = pBuf;
	CHAR * const pStart = (PCHAR)pExt->Hardware.Memory.Virtual + AdapterOffset;
	PCHAR pOut = pStart;
	ULONG cb = cbWrite;

	ASSERT( AdapterOffset + cb <= pExt->Hardware.Memory.Length );
	ASSERT( cb != 0 );

	// Dword-Align pOut
	if ( !IsDwordAligned( pOut ) )
	{
 		if ( !IsWordAligned( pOut ) && cb >= sizeof (UCHAR) )
 		{
			// Byte-aligned.  Write a byte
			WRITE_REGISTER_UCHAR( pOut, *pIn );
			pOut += sizeof (UCHAR);
			pIn  += sizeof (UCHAR);
			cb   -= sizeof (UCHAR);
 		}

		// Word-aligned, could be Dword aligned.  Write a word only if !dword-aligned.
 		if ( !IsDwordAligned( pOut ) && cb >= sizeof (USHORT) )
 		{
			WRITE_REGISTER_USHORT( (PVOID)pOut, *(PUSHORT)pIn );
			pOut += sizeof (USHORT);
			pIn  += sizeof (USHORT);
			cb   -= sizeof (USHORT);
 		}
	}

	// Write Dwords, as many as possible
	{
		ULONG nWrite = cb / sizeof (ULONG);
		if ( nWrite )
		{
			WRITE_REGISTER_BUFFER_ULONG( (PVOID)pOut, (PVOID)pIn, nWrite );
			pOut += nWrite * sizeof (ULONG);
			pIn  += nWrite * sizeof (ULONG);
			cb   -= nWrite * sizeof (ULONG);
		}
	}

	// Write remaining bytes
	switch ( cb )
	{
		case 0:
			break;

		case sizeof (USHORT):
			WRITE_REGISTER_USHORT( (PVOID)pOut, *(PUSHORT)pIn );
			pIn  += sizeof (USHORT);
			pOut += sizeof (USHORT);
			cb   -= sizeof (USHORT);
			break;

		default:
			WRITE_REGISTER_BUFFER_UCHAR( (PVOID)pOut, (PVOID)pIn, cb );
			pOut += cb;
			pIn  += cb;
			cb   -= cb;
			break;
	}

	ASSERT( pOut == pStart + cbWrite );
	ASSERT( pIn  == (PCHAR)pBuf + cbWrite );
	ASSERT( cb == 0 );
}


STATIC INLINE VOID
dxbSlaveWriteDwordFlat(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 Offset,
	ULONG                 Data )
/*++

Description:

	Write a dword to adapter assuming flat memory structure (i.e. no windowing) like 
	that on a PCI adapter.

Parameters:

	pExt
		The device extension defining the adapter

	Offset
		Offset into adapter memory at which to start writing

	Data
		Data to write

Return Value:

	None

--*/
{
	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );
	WRITE_REGISTER_ULONG( (PVOID)( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset ), Data );
}


STATIC INLINE VOID
dxbSlaveWriteWordFlat(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 Offset,
	USHORT                Data )
/*++

Description:

	Write a word to adapter assuming flat memory structure (i.e. no windowing) like 
	that on a PCI adapter.

Parameters:

	pExt
		The device extension defining the adapter

	Offset
		Offset into adapter memory at which to start writing

	Data
		Data to write

Return Value:

	None

--*/
{
	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );
	WRITE_REGISTER_USHORT( (PVOID)( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset ), Data );
}


STATIC INLINE VOID
dxbSlaveWriteByteFlat(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 Offset,
	UCHAR                 Data )
/*++

Description:

	Write a byte to adapter assuming flat memory structure (i.e. no windowing) like 
	that on a PCI adapter.

Parameters:

	pExt
		The device extension defining the adapter

	Offset
		Offset into adapter memory at which to start writing

	Data
		Data to write

Return Value:

	None

--*/
{
	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );
	WRITE_REGISTER_UCHAR( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset, Data );
}


STATIC VOID
dxbSlaveWriteWindowed(
	PDXB_DEVICE_EXTENSION pExt,
	VOID CONST            *pBuf,
	ULONG                 AdapterOffset,
	ULONG                 cb )
/*++

Description:

	Writes data to adapter assuming windowed memory structure like that on a ISA or 
	MCA adapter.

Parameters:

	pExt
		The device extension defining the adapter

	pBuf
		The data to be written to the adapter

	AdapterOffset
		Offset into adapter memory at which to start writing

	cb
		Count of bytes to write

Return Value:

	None

--*/
{
	ULONG Window = AdapterOffset / pExt->Hardware.Memory.cbWindow;
	ULONG Offset = AdapterOffset % pExt->Hardware.Memory.cbWindow;
	ULONG cbLeft = cb;
	UCHAR CONST *pSrc = pBuf;
	while ( cbLeft != 0 )
	{
		ULONG cbCurrent = Offset + cbLeft <= pExt->Hardware.Memory.cbWindow ? 
		                  cbLeft : pExt->Hardware.Memory.cbWindow - Offset;
		ASSERT( Window < MAXUSHORT );
		DxbEnableWindow( pExt, (USHORT)Window );
		dxbSlaveWriteFlat( pExt, pSrc, Offset, cbCurrent );
		DxbDisableWindow( pExt );

		// Prepare for next iteration 
		cbLeft -= cbCurrent;
		pSrc   += cbCurrent;
		Offset = 0;
		Window++;
	}
	ASSERT( pSrc == (PUCHAR)pBuf + cb );
}


STATIC VOID
dxbSlaveWriteDwordWindowed(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 AdapterOffset,
	ULONG                 Data )
/*++

Description:

	Writes data to adapter assuming windowed memory structure like that on a ISA or 
	MCA adapter.

Parameters:

	pExt
		The device extension defining the adapter

	AdapterOffset
		Offset into adapter memory at which to start writing

	Data
		Data to write

Return Value:

	None

--*/
{
	ULONG Window = AdapterOffset / pExt->Hardware.Memory.cbWindow;
	ULONG Offset = AdapterOffset % pExt->Hardware.Memory.cbWindow;

	ASSERT( Offset == dxbAlign32( Offset ) );
	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );

	DxbEnableWindow( pExt, (USHORT)Window );
	WRITE_REGISTER_ULONG( (PVOID)( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset ), Data );
	DxbDisableWindow( pExt );
}


STATIC VOID
dxbSlaveWriteWordWindowed(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 AdapterOffset,
	USHORT                Data )
/*++

Description:

	Writes data to adapter assuming windowed memory structure like that on a ISA or 
	MCA adapter.

Parameters:

	pExt
		The device extension defining the adapter

	AdapterOffset
		Offset into adapter memory at which to start writing

	Data
		Data to write

Return Value:

	None

--*/
{
	ULONG Window = AdapterOffset / pExt->Hardware.Memory.cbWindow;
	ULONG Offset = AdapterOffset % pExt->Hardware.Memory.cbWindow;

	ASSERT( Offset == dxbAlign16( Offset ) );
	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );

	DxbEnableWindow( pExt, (USHORT)Window );
	WRITE_REGISTER_USHORT( (PVOID)( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset ), Data );
	DxbDisableWindow( pExt );
}


STATIC VOID
dxbSlaveWriteByteWindowed(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 AdapterOffset,
	UCHAR                 Data )
/*++

Description:

	Writes data to adapter assuming windowed memory structure like that on a ISA or 
	MCA adapter.

Parameters:

	pExt
		The device extension defining the adapter

	AdapterOffset
		Offset into adapter memory at which to start writing

	Data
		Data to write

Return Value:

	None

--*/
{
	ULONG Window = AdapterOffset / pExt->Hardware.Memory.cbWindow;
	ULONG Offset = AdapterOffset % pExt->Hardware.Memory.cbWindow;

	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );

	DxbEnableWindow( pExt, (USHORT)Window );
	WRITE_REGISTER_UCHAR( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset, Data );
	DxbDisableWindow( pExt );
}


STATIC INLINE VOID
dxbSlaveReadFlat(
	PDXB_DEVICE_EXTENSION pExt,
	PVOID const           pBuf,
	ULONG const           AdapterOffset,
	ULONG const           cbRead )
/*++

Description:

	Read data from an adapter assuming flat memory structure (i.e. no windowing) like 
	that on a PCI adapter.

Parameters:

	pExt
		The device extension defining the adapter

	pBuf
		Buffer into which data should be copied

	Offset
		Offset into adapter memory window at which to start reading

	cb
		Count of bytes to read

Return Value:

	None

--*/
{
	CHAR const * const pStart = (PCHAR)pExt->Hardware.Memory.Virtual + AdapterOffset;
	CHAR const * pIn = pStart;
	CHAR * pOut = pBuf;
	ULONG cb = cbRead;

	ASSERT( AdapterOffset + cb <= pExt->Hardware.Memory.Length );
	ASSERT( cb != 0 );

	// Dword-Align pIn
	if ( !IsDwordAligned( pIn ) )
	{
 		if ( !IsWordAligned( pIn ) && cb >= sizeof (UCHAR) )
 		{
			// Byte-aligned.  Read a byte
			*pOut = READ_REGISTER_UCHAR( (PVOID)pIn );
			pOut++;
			pIn++;
			cb--;
 		}

		// Word-aligned.  Read a word
 		if ( !IsDwordAligned( pIn ) && cb >= sizeof (USHORT) )
 		{
			*(PUSHORT)pOut = READ_REGISTER_USHORT( (PVOID)pIn );
			pOut += sizeof (USHORT);
			pIn  += sizeof (USHORT);
			cb   -= sizeof (USHORT);
 		}
	}

	// Read Dwords, as many as possible
	{
		ULONG nRead = cb / sizeof (ULONG);
		if ( nRead )
		{
			READ_REGISTER_BUFFER_ULONG( (PVOID)pIn, (PVOID)pOut, nRead );
			pOut += nRead * sizeof (ULONG);
			pIn  += nRead * sizeof (ULONG);
			cb   -= nRead * sizeof (ULONG);
		}
	}

	// Read remaining bytes
	switch ( cb )
	{
		case 0:
			break;

		case sizeof (USHORT):
			*(PUSHORT)pOut = READ_REGISTER_USHORT( (PVOID)pIn );
			pIn  += sizeof (USHORT);
			pOut += sizeof (USHORT);
			cb   -= sizeof (USHORT);
			break;

		default:
			READ_REGISTER_BUFFER_UCHAR( (PVOID)pIn, (PVOID)pOut, cb );
			pOut += cb;
			pIn  += cb;
			cb   -= cb;
			break;
	}

	ASSERT( pIn == pStart + cbRead );
	ASSERT( pOut == (PCHAR)pBuf + cbRead );
	ASSERT( cb == 0 );
}


STATIC INLINE ULONG
dxbSlaveReadDwordFlat(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 Offset )
/*++

Description:

	Read data from an adapter assuming flat memory structure (i.e. no windowing) like 
	that on a PCI adapter.

Parameters:

	pExt
		The device extension defining the adapter

	Offset
		Offset into adapter memory window at which to start reading

Return Value:

	Data read from adapter

--*/
{
	ASSERT( Offset == dxbAlign32( Offset ) );
	ASSERT( Offset + sizeof (ULONG) <= pExt->Hardware.Memory.Length );
	return READ_REGISTER_ULONG( (PVOID)( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset ) );
}


STATIC INLINE USHORT
dxbSlaveReadWordFlat(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 Offset )
/*++

Description:

	Read data from an adapter assuming flat memory structure (i.e. no windowing) like 
	that on a PCI adapter.

Parameters:

	pExt
		The device extension defining the adapter

	Offset
		Offset into adapter memory window at which to start reading

Return Value:

	Data read from adapter

--*/
{
	ASSERT( Offset == dxbAlign16( Offset ) );
	ASSERT( Offset + sizeof (USHORT) <= pExt->Hardware.Memory.Length );
	return READ_REGISTER_USHORT( (PVOID)( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset ) );
}


STATIC INLINE UCHAR
dxbSlaveReadByteFlat(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 Offset )
/*++

Description:

	Read data from an adapter assuming flat memory structure (i.e. no windowing) like 
	that on a PCI adapter.

Parameters:

	pExt
		The device extension defining the adapter

	Offset
		Offset into adapter memory window at which to start reading

Return Value:

	Data read from adapter

--*/
{
	ASSERT( Offset + sizeof (UCHAR) <= pExt->Hardware.Memory.Length );
	return READ_REGISTER_UCHAR( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset );
}


STATIC VOID
dxbSlaveReadWindowed(
	PDXB_DEVICE_EXTENSION pExt,
	PVOID                 pBuf,
	ULONG                 AdapterOffset,
	ULONG                 cb )
/*++

Description:

	Read data from an adapter assuming windowed memory structure like that on a ISA or 
	MCA adapter.

Parameters:

	pExt
		The device extension defining the adapter

	pBuf
		Buffer into which data should be copied

	AdapterOffset
		Offset into adapter memory at which to start read

	cb
		Count of bytes to read

Return Value:

	None

--*/
{
	ULONG Window = AdapterOffset / pExt->Hardware.Memory.cbWindow;
	ULONG Offset = AdapterOffset % pExt->Hardware.Memory.cbWindow;
	ULONG cbLeft = cb;
	PUCHAR pDest = pBuf;

	while ( cbLeft != 0 )
	{
		ULONG cbCurrent = Offset + cbLeft <= pExt->Hardware.Memory.cbWindow ? 
		                  cbLeft : pExt->Hardware.Memory.cbWindow - Offset;
		ASSERT( Window < MAXUSHORT );
		DxbEnableWindow( pExt, (USHORT)Window );
		dxbSlaveReadFlat( pExt, pDest, Offset, cbCurrent );
		DxbDisableWindow( pExt );

		// Prepare for next iteration 
		cbLeft -= cbCurrent;
		pDest  += cbCurrent;
		Offset = 0;
		Window++;
	}
	ASSERT( pDest == (PUCHAR)pBuf + cb );
}


STATIC ULONG
dxbSlaveReadDwordWindowed(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 AdapterOffset )
/*++

Description:

	Reads data from the adapter assuming windowed memory structure like that on a ISA or
	MCA adapter.

Parameters:

	pExt
		The device extension defining the adapter

	AdapterOffset
		Offset into adapter memory at which to start writing

Return Value:

	Data read from adapter.

--*/
{
	ULONG Data;
	ULONG Window = AdapterOffset / pExt->Hardware.Memory.cbWindow;
	ULONG Offset = AdapterOffset % pExt->Hardware.Memory.cbWindow;

	ASSERT( Offset == dxbAlign32( Offset ) );
	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );

	DxbEnableWindow( pExt, (USHORT)Window );
	Data = READ_REGISTER_ULONG( (PVOID)( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset ) );
	DxbDisableWindow( pExt );
	return Data;
}


STATIC USHORT
dxbSlaveReadWordWindowed(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 AdapterOffset )
/*++

Description:

	Reads data from the adapter assuming windowed memory structure like that on a ISA or
	MCA adapter.

Parameters:

	pExt
		The device extension defining the adapter

	AdapterOffset
		Offset into adapter memory at which to start writing

Return Value:

	Data read from adapter.

--*/
{
	USHORT Data;
	ULONG Window = AdapterOffset / pExt->Hardware.Memory.cbWindow;
	ULONG Offset = AdapterOffset % pExt->Hardware.Memory.cbWindow;

	ASSERT( Offset == dxbAlign16( Offset ) );
	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );

	DxbEnableWindow( pExt, (USHORT)Window );
	Data = READ_REGISTER_USHORT( (PVOID)( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset ) );
	DxbDisableWindow( pExt );
	return Data;
}


STATIC UCHAR
dxbSlaveReadByteWindowed(
	PDXB_DEVICE_EXTENSION pExt,
	ULONG                 AdapterOffset )
/*++

Description:

	Reads data from the adapter assuming windowed memory structure like that on a ISA or
	MCA adapter.

Parameters:

	pExt
		The device extension defining the adapter

	AdapterOffset
		Offset into adapter memory at which to start writing

Return Value:

	Data read from adapter.

--*/
{
	UCHAR Data;
	ULONG Window = AdapterOffset / pExt->Hardware.Memory.cbWindow;
	ULONG Offset = AdapterOffset % pExt->Hardware.Memory.cbWindow;

	ASSERT( Offset + sizeof Data <= pExt->Hardware.Memory.Length );

	DxbEnableWindow( pExt, (USHORT)Window );
	Data = READ_REGISTER_UCHAR( (PUCHAR)pExt->Hardware.Memory.Virtual + Offset );
	DxbDisableWindow( pExt );
	return Data;
}


VOID 
DxbSlaveSetAdapterWindowed( 
	PDXB_DEVICE_EXTENSION      pExt,
	PDXB_MEMORY_WINDOW_ENABLE  pEnable, 
	PDXB_MEMORY_WINDOW_DISABLE pDisable )
/*++

Description:

	Initialize the device extension table of function pointers for slave memory access
	for a windowed adapter.

Parameters:

	pExt
		The device extension defining the adapter

	pEnable
		Routine which should be used for enabling an adapter memory window

	pDisable
		Routine which should be used for disabling an adapter memory window

Return Value:

	None

--*/
{
	pExt->Adapter.Read  = dxbSlaveReadWindowed;
	pExt->Adapter.Write = dxbSlaveWriteWindowed;

	pExt->Adapter.ReadByte  = dxbSlaveReadByteWindowed;
	pExt->Adapter.WriteByte = dxbSlaveWriteByteWindowed;

	pExt->Adapter.ReadWord  = dxbSlaveReadWordWindowed;
	pExt->Adapter.WriteWord = dxbSlaveWriteWordWindowed;

	pExt->Adapter.ReadDword  = dxbSlaveReadDwordWindowed;
	pExt->Adapter.WriteDword = dxbSlaveWriteDwordWindowed;

	pExt->Adapter.Enable  = pEnable;
	pExt->Adapter.Disable = pDisable;
}


VOID 
DxbSlaveSetAdapterFlatMemory( 
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Initialize the device extension table of function pointers for slave memory access
	for a flat memory adapter.

Parameters:

	pExt
		The device extension defining the adapter

Return Value:

	None

--*/
{
	pExt->Adapter.Read    = dxbSlaveReadFlat;
	pExt->Adapter.Write   = dxbSlaveWriteFlat;

	pExt->Adapter.ReadByte  = dxbSlaveReadByteFlat;
	pExt->Adapter.WriteByte = dxbSlaveWriteByteFlat;

	pExt->Adapter.ReadWord  = dxbSlaveReadWordFlat;
	pExt->Adapter.WriteWord = dxbSlaveWriteWordFlat;

	pExt->Adapter.ReadDword  = dxbSlaveReadDwordFlat;
	pExt->Adapter.WriteDword = dxbSlaveWriteDwordFlat;

	pExt->Adapter.Enable  = NULL;
	pExt->Adapter.Disable = NULL;
}


