/*++
 * 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
 *
 *
 * Last Update: $Date: 10/22/97 11:56a $
 *
 * $Header: /Components/Windows/NT/Sync/Build/Mp570/ioctls.c 6     10/22/97 11:56a Darinh $
 *
 * Revision History:
 * $Log: /Components/Windows/NT/Sync/Build/Mp570/ioctls.c $
 * 
 * 6     10/22/97 11:56a Darinh
 * Changes to make multiple board installations work better.
 * 
 * 5     9/08/97 3:44p Darinh
 * A variety of changes, including Data General fixes are included.
 * 
 * 4     7/15/97 1:05p Darinh
 * Added DigiLogError statements and message file, altered code to function
 * in DStreams environment and SNA environment with a conditional
 * compilation.
 * 
 * 3     6/06/97 1:48p Darinh
 * Everything seems functional, but more testing is required.
 * 
 * 2     4/08/97 4:55p Darinh
 * Changes made to correspond to the new DigiUtil library.  It
 * successfully transmits data!  I don't know about receiving data yet.
 * 
 * 1     4/02/97 10:08a Darinh
 * Initial checkin; ISA cards initialize, but do not pass data.
 * 
--*/
#include "digilib.h"

#include <ntddk.h>
#include "def570.h"
#include "hw570.h"
#include "def64570.h"
#include "board.h"
#include "dsmp.h"
#include "proto.h"
#include "ioctls.h"
#include "io.h"
#include "hwsca.h"
#include "hwutils.h"
#include "debug.h"


PCHAR	InterfaceString[] = {	"X.21 (V.11/EIA-422)", "V.28 (V.24)", "V.35",
								"V36", "CSU/DSU", "Switched 56", "V35 or RS232",
								"EIA 530", "LOOPBACK", "NO CABLE", "Unknown" };

NTSTATUS
IoctlGetFrame (
	IN PDEVICE_EXT		pDeviceExt,
	IN PDSMP_GETFRAME	pFrame)
/*++

Description:

	This function will transfer a frame from the Device receive buffer to
	a user buffer, if there is a frame pending.
	1.	Check the Receive counter for pending frames, return if zero.
	2.	Get receive frame length.
	3.	Check if the frame will fit in the user buffer, if so, copy it, else
		return an error.
	4.	Decrement the internal and Interface record receive frame counter.

Parameters:

	pDeviceExt
		Address of the Device extension.

	pFrame
		Pointer to the User buffer to copy the received message into.

Return Value:

	None.

--*/
{
	PUCHAR		pUserBuffer = pFrame->pDataBuffer;
	USHORT		RxFrameLen = 0;
	USHORT		CopyLen = 0;
	NTSTATUS	Status = STATUS_SUCCESS;


//	DigiDump (DIGIFLOW, ("Entering IoctlGetFrame()\n"));

	// Check if we have any received frames in the queue
	if (pDeviceExt->ReceivedFrames == 0)
	{
		//  This is not an error
		goto IoctlGetFrameExit;
	}

	RxFrameLen = fnGetReceivedFrameLen (pDeviceExt);
	if (RxFrameLen > pFrame->DataMaxLength)
	{
		Status = STATUS_BUFFER_TOO_SMALL;
		goto IoctlGetFrameExit;
	}

	pFrame->DataLength = RxFrameLen;

	DigiDump (DIGIDATA, ("\n\nRF: (%d)\n\n",pDeviceExt->PortNumber));

	//
	//  Copy all of the associate buffers into the User buffer
	//
	while (RxFrameLen)
	{
		// Size to copy
		CopyLen = MIN (RxFrameLen, DEFAULT_FRAME_SIZE);

		fnReadFromCardMemory (
		(ULONG_PTR) &pDeviceExt->pRxRealSdlcBuffersArray[pDeviceExt->RxDspOut],
			pUserBuffer,
			CopyLen,
			pDeviceExt);
#ifdef DBG
		{
			USHORT i;
			for (i=0;i<CopyLen;i++)
			{
				DigiDump (DIGIDATA, ("%02x",pUserBuffer[i]));
			}
			DigiDump (DIGIDATA, ("\n\n"));
		}
#endif
			
		pUserBuffer += CopyLen;
		RxFrameLen -= CopyLen;

		UpdateInOutIndex (&pDeviceExt->RxDspOut, pDeviceExt);
	}

	--pDeviceExt->ReceivedFrames;
	--pDeviceExt->IR.ReceivedFrames;
	pDeviceExt->pRxDmaDspCda =
		&pDeviceExt->pRxRealDmaChainsArray[pDeviceExt->RxDspOut];

	// Adjust the Receive DMA Descriptor Chains
	fnAdjustRxDmacEdaDsp (pDeviceExt);


IoctlGetFrameExit:

//	DigiDump (DIGIFLOW, ("Exiting IoctlGetFrame()\n"));

	return (Status);
}



NTSTATUS
IoctlPutFrame (
	IN PDEVICE_EXT		pDeviceExt,
	IN PDSMP_PUTFRAME	pFrame)
/*++

Description:

	This function transfers the data from the data buffer to the Device
	Transmit buffers.
	1.	Check the Link Service data length. This length can not less
		than two bytes and can not exceed Maximum frame size define
		in the Interface Record.
	2.	Transfer the data from the data buffer to Transmit buffers.
	3.	Then it starts the transmitter, if needed.

Parameters:

	pDeviceExt
		Address of the Device Extension.

	pFrame
		Pointer to the user buffer, and associate structure to transmit.

Return Value:

	None.

--*/
{
	USHORT		BytesCopied;
	UCHAR		RequiredBuffers = 0;
	ULONG		TxFrameLen = pFrame->DataLength;
	LONG		RequiredSpace = 0;
	PUCHAR		pUserBuffer = pFrame->pDataBuffer;
	BOOLEAN		NeedToStart = FALSE;
	BOOLEAN		LastFrame = FALSE;
	NTSTATUS	Status = STATUS_SUCCESS;


	DigiDump (DIGIFLOW, ("Entering IoctlPutFrame()\n"));

	//  Check if we need to start the transmitter
	if (pDeviceExt->TxDspOut == pDeviceExt->TxDspIn)
		NeedToStart = TRUE;

	RequiredBuffers = (UCHAR)(TxFrameLen / DEFAULT_FRAME_SIZE);
	if (TxFrameLen % DEFAULT_FRAME_SIZE)
	{
		RequiredBuffers++;
	}

	//
	//  Verify we have room on the card for this frame
	//
	RequiredSpace = RequiredBuffers * DEFAULT_FRAME_SIZE;
	if (pDeviceExt->IR.TransmitBufferSpace < RequiredSpace)
	{
		DigiDump (DIGIERRORS, ("<IoctlPutFrame> No room to transmit frame\n"));
		return (STATUS_UNSUCCESSFUL);
	}

	pDeviceExt->IR.TransmitBufferSpace -= RequiredBuffers * DEFAULT_FRAME_SIZE;
	pDeviceExt->Stats.TxBytes += TxFrameLen;
	pDeviceExt->bSdlcCtrl = *(pUserBuffer + 1);

	DigiDump (DIGIDATA, ("\n\nTF: (%d)\n\n",pDeviceExt->PortNumber));

	do
	{
		BytesCopied = (USHORT) MIN (TxFrameLen, DEFAULT_FRAME_SIZE);
		TxFrameLen -= BytesCopied;

		//
		//  Copy the Frame to an SDLC buffer on the card.
		//
		fnCopyToCardMemory (pUserBuffer,
			(ULONG_PTR) &pDeviceExt->pTxRealSdlcBuffersArray[pDeviceExt->TxDspIn],
			BytesCopied,
			pDeviceExt);

#ifdef DBG
		{
			USHORT i;

			for (i=0;i<BytesCopied;i++)
			{
				DigiDump (DIGIDATA, ("%02x",pUserBuffer[i]));
			}
			DigiDump (DIGIDATA, ("\n\n"));

		}	
#endif

		pUserBuffer += BytesCopied;

		//  Is there another frame?
		LastFrame = (TxFrameLen) ? FALSE : TRUE;

		//
		//  Update the DMA descriptor on the card.
		//
		fnUpdateDmaDsp (pDeviceExt,
			BytesCopied,
			RequiredBuffers,
			LastFrame,
			pDeviceExt->TxDspIn);

		RequiredBuffers = 0;

		//  Check for Transmit Descriptor wrap condition.
		UpdateInOutIndex (&pDeviceExt->TxDspIn, pDeviceExt);

	} while (TxFrameLen);


	pDeviceExt->pTxDmaDspEda =
		&pDeviceExt->pTxRealDmaChainsArray[pDeviceExt->TxDspIn];
	++pDeviceExt->NumberOfTxFrames;
	++pDeviceExt->Stats.TxFrames;

	if (NeedToStart)
	{
		fnSetupDma (pDeviceExt);
		fnStartTransmit (pDeviceExt);
	}
	else
	{
		EnableScaMemMap (pDeviceExt);
		WRT_REG_USHORT (&pDeviceExt->pScaTxDmac->Eda, pDeviceExt->pTxDmaDspEda);
		DisableMemory (pDeviceExt);
	}

	DigiDump (DIGIFLOW, ("Exiting IoctlPutFrame()\n"));

	return (Status);
}



NTSTATUS
IoctlGetConfig (
	IN PDEVICE_EXT		pDeviceExt,
	IN PDSMP_CONFIG		pConfig)
/*++

Description:

Parameters:

	pDeviceExt
		Address of the Device Extension.

	pConfig
		Address of the User structure to fill in, and return.

Return Value:

	None.

--*/
{
	DigiDump (DIGIFLOW, ("Entering IoctlGetConfig()\n"));

	// Set the Interface type in the Config. This is not set by the user
	// for this board.  The driver gets the interface type from the board.
	pDeviceExt->Config.Interface = IoctlGetInterfaceType(pDeviceExt);

	RtlCopyMemory (pConfig, &pDeviceExt->Config, sizeof (DSMP_CONFIG));

	DigiDump (DIGIVERYLOUD, ("Current Config:\n"
		"  MaxFrameSize = 0x%x\n"
		"  BaudRate     = %d\n"
		"  Interface    = %s\n",
		pDeviceExt->Config.MaxFrameSize,
		pDeviceExt->Config.BaudRate,
		InterfaceString[pDeviceExt->Config.Interface]));

	DigiDump (DIGIFLOW, ("Exiting IoctlGetConfig()\n"));

	return (STATUS_SUCCESS);
}



NTSTATUS
IoctlSetConfig (
	IN PDEVICE_EXT		pDeviceExt,
	IN PDSMP_CONFIG		pConfig)
/*++

Description:

Parameters:

	pDeviceExt
		Address of the Device Extension.

	pConfig
		Pointer to the user's Config structure.

Return Value:

	None.

--*/
{
	LINKCHAR		LinkChars;
	NTSTATUS		Status			= STATUS_SUCCESS;

	DigiDump (DIGIFLOW, ("Entering IoctlSetConfig()\n"));

	DigiDump (DIGIINIT, ("New Config:\n"
		"  MaxFrameSize = 0x%x\n"
		"  BaudRate     = %d\n"
		"  NRZI			= %d\n"
		"  RTS HIGH 	= %d\n",
		pConfig->MaxFrameSize,
		pConfig->BaudRate,
		pConfig->LinkOptions & DSMP_LO_NRZI,
		pConfig->LinkOptions & DSMP_LO_RTS_HIGH));

	// Set the Link Characteristics
	LinkChars.FrameSize			= pConfig->MaxFrameSize;
	LinkChars.DataRate			= pConfig->BaudRate;
	LinkChars.StationAddress1	= pConfig->StationAddress1;
	LinkChars.StationAddress2	= pConfig->StationAddress2;
	LinkChars.LinkOptions		= LO_HDLC | LO_RSTAT;
	LinkChars.Reserved			= 0; 

	// A Positive Baud Rate value means the clocking is external
	pDeviceExt->InternalClocking = pConfig->BaudRate ? TRUE : FALSE;

	// Set the Link Options
	if (pConfig->LinkOptions & DSMP_LO_NRZI)
	{
		LinkChars.LinkOptions |= LO_NRZI;
	}
	if (pConfig->LinkOptions & DSMP_LO_RTS_HIGH)
	{
		LinkChars.LinkOptions |= LO_4WIRE;

	}

	pDeviceExt->CtsRequired = FALSE;
	if (pConfig->LinkOptions & DSMP_LO_WAIT_CTS)
	{

		// When talking to Sync cards on back to back cables, CTS will
		// never come high.  If we are coded with RTS HIGH we will 
		// always send the data.  Apps that want to assure CTS is high
		// before sending data should set the WAIT_CTS WAN option.
		pDeviceExt->CtsRequired = TRUE;

	}

	RtlCopyMemory (&pDeviceExt->Config, pConfig, sizeof (DSMP_CONFIG));

	// Set the Interface type in the Config. This is not set by the user
	// for this board.  The driver gets the interface type from the board.
	pDeviceExt->Config.Interface = IoctlGetInterfaceType(pDeviceExt);

	Status = fnSetLinkConfig (pDeviceExt, &LinkChars);

	//  Start the transmitter and receiver
	if (NT_SUCCESS (Status))
	{
		fnStartChannel (pDeviceExt);
	}

	DigiDump (DIGIFLOW, ("Exiting IoctlSetConfig() Status = %08x\n",Status));

	return (Status);
}

USHORT 
IoctlGetInterfaceType (
	IN PDEVICE_EXT	pDeviceExt) 
/*++

Description:

	This function translates the Port Type to the DSMP interface type.

Parameters:

	pDeviceExt
		Address of the Device extension.

Return Value:

	DSMP Interface Type.

--*/
{

	USHORT DSMP_Interface;

	// The status of the port could have changed.  On UIB interface type
	// ports a different cable could now be plugged in.
	GetPortType(pDeviceExt);

	switch (pDeviceExt->PortType)
	{

	case PT_232:
	case PT_232_RI:
		DSMP_Interface = DSMP_V28;
		break;

	case PT_V35:
		DSMP_Interface = DSMP_V35;
		break;

	case PT_530:
		DSMP_Interface = DSMP_530;
		break;

	case PT_X21:
		DSMP_Interface = DSMP_X21;
		break;

	case PT_SW56:
		DSMP_Interface = DSMP_SW56;
		break;

	case PT_TTL:
	case PT_LOOP:
		DSMP_Interface = DSMP_LOOPBACK;
		break;

	case PT_NO_CABLE:
		DSMP_Interface = DSMP_NO_CABLE;
		break;

	case PT_V35_232:
	case PT_V35_232_RI:
		DSMP_Interface = DSMP_V35RS232;
		break;

	default:
		DSMP_Interface = DSMP_UNKNOWN;
		break;
	}

	return DSMP_Interface;
}

NTSTATUS
IoctlGetModem (
	IN PDEVICE_EXT		pDeviceExt,
	IN PDSMP_MODEM		pModem)
/*++

Description:

	This function gets the DSR, DCD, and CTS status.  The status of each
	signal is returned in the user buffer (in pModem).

Parameters:

	pDeviceExt
		Address of the Device extension.

	pModem
		Pointer to the user's Modem structure.

Return Value:

	None.

--*/
{
	volatile UCHAR	DcdReg, St3, Ctl;
	UCHAR			DtrMask = 0;


	DigiDump (DIGIFLOW, ("Entering IoctlGetModem()\n"));

	if (PT_X21 == pDeviceExt->PortType)
	{
		//  DCD and CTS are not present in X.21, so we have to spoof
		//  the signals to SNA Server in order for it to give us
		//  any frames to transmit.
		pModem->State = DSMP_DCD | DSMP_CTS | DSMP_DSR ;
	}
	else
	{

		DcdReg = (*pDeviceExt->pControllerExt->pIO_IN) (
			pDeviceExt->pControllerExt->pDigiDcdIntReg);
		pModem->State = ((DcdReg & pDeviceExt->DigiDcdMask) == 0) ? DSMP_DCD : 0;

		EnableScaMemMap (pDeviceExt);
		St3 = READ_REGISTER_UCHAR ((PUCHAR) &pDeviceExt->pScaMsci->St3);
		Ctl = READ_REGISTER_UCHAR ((PUCHAR) &pDeviceExt->pScaMsci->Ctl);
		pModem->State |= ((St3 & MSCI_ST3_CTSM) == 0) ? DSMP_CTS : 0;
		pModem->State |= ((St3 & MSCI_ST3_DCDM) == 0) ? DSMP_DSR : 0;
		pModem->State |= ((Ctl & MSCI_CTL_RTS_HIGH) == 0) ? DSMP_RTS : 0;
		DisableMemory (pDeviceExt);

		//
		//  DTR cannot be read, we will return the last value we wrote
		//
		DtrMask = pDeviceExt->DigiDtrTxE & 0xC;
		pModem->State |= (pDeviceExt->DigiRegTxClkDtrCtrl & DtrMask) ? DSMP_DTR : 0;

	}

	DigiDump (DIGIFLOW, ("Exiting IoctlGetModem()\n"));

	return (STATUS_SUCCESS);
}



NTSTATUS
IoctlSetModem (
	IN PDEVICE_EXT	pDeviceExt,
	IN PDSMP_MODEM	pModem)
/*++

Description:

	This function will setup DTR and RTS.  The signals' status are stored
	in the IRP's user buffer.

Parameters:

	pDeviceExt
		Address of the Device extension.

	pModem
		Pointer to the user's Modem structure.

Return Value:

	None.

--*/
{
	DSMP_MODEM		GetModem;
	NTSTATUS		Status = STATUS_SUCCESS;
	volatile UCHAR	Ctl;
	UCHAR			Mask = pModem->Mask;
	UCHAR			DtrMask = 0;


	DigiDump (DIGIFLOW, ("Entering IoctlSetModem()\n"));

	//
	//  Modify DTR
	//
	DtrMask = pDeviceExt->DigiDtrTxE & 0xC;

	DigiDump (DIGIVERYLOUD, ("DTR is %s\n",
		(pDeviceExt->DigiRegTxClkDtrCtrl & DtrMask) ? "ON" : "OFF"));

	if (Mask & DSMP_DTR)
	{
		DigiDump (DIGIVERYLOUD, ("Raising DTR\n"));
		fnWriteDTR (pDeviceExt,RAISE);
	}
	else
	{
		DigiDump (DIGIVERYLOUD, ("Lowering DTR\n"));
		fnWriteDTR (pDeviceExt,LOWER);
	}


	//
	//  Modify RTS
	//
	EnableScaMemMap (pDeviceExt);

	// Setup RTS signal
	Ctl = READ_REGISTER_UCHAR ((PUCHAR) &pDeviceExt->pScaMsci->Ctl);

	DigiDump (DIGIVERYLOUD, ("RTS is %s, ",
		(Ctl & MSCI_CTL_RTS_HIGH) ? "OFF" : "ON"));

	if ((Mask & DSMP_RTS) && (Ctl & MSCI_CTL_RTS_HIGH))
	{
		DigiDump (DIGIVERYLOUD, ("Raising RTS\n"));
		Ctl &= ~MSCI_CTL_RTS_HIGH;
		WRT_REG_UCHAR (&pDeviceExt->pScaMsci->Ctl, Ctl);
	}
	else if ((!(Mask & DSMP_RTS)) && (!(Ctl & MSCI_CTL_RTS_HIGH)))
	{
		DigiDump (DIGIVERYLOUD, ("Lowering RTS\n"));
		Ctl |= MSCI_CTL_RTS_HIGH;
		WRT_REG_UCHAR (&pDeviceExt->pScaMsci->Ctl, Ctl);
	}
	else
	{
		DigiDump (DIGIVERYLOUD, ("No change for RTS.\n"));
	}

	DisableMemory (pDeviceExt);


	//
	//  Now we need to update the State of the Modem signals
	//
	Status = IoctlGetModem (pDeviceExt, &GetModem);
	pModem->State = GetModem.State;


	DigiDump (DIGIFLOW, ("Exiting IoctlSetModem()\n"));

	return (Status);
}



NTSTATUS
IoctlGetStats (
	IN PDEVICE_EXT		pDeviceExt,
	IN PDSMP_STATS		pStats)
/*++

Description:

Parameters:

	pDeviceExt
		Address of the Device Extension.

	pStats
		Pointer to the user's Statistics structure.

Return Value:

	None.

--*/
{
	DigiDump (DIGIFLOW, ("Entering IoctlGetStats()\n"));

	RtlCopyMemory (pStats, &pDeviceExt->Stats, sizeof (DSMP_STATS));

	DigiDump (DIGIVERYLOUD, ("Statistics for port %d (pDeviceExt: 0x%x)\n"
		"Tx Frames   = 0x%08x\n"
		"Tx Bytes    = 0x%08x\n"
		"Tx Underrun = 0x%08x\n"
		"Tx Abort    = 0x%08x (not used)\n"
		"Rx Frames   = 0x%08x\n"
		"Rx Bytes    = 0x%08x\n"
		"Rx Overrun  = 0x%08x\n"
		"Rx CRC      = 0x%08x\n"
		"Rx No Buf   = 0x%08x (not used)\n"
		"Rx No Flow  = 0x%08x (not used)\n"
		"Rx Overflow = 0x%08x\n"
		"Rx Abort    = 0x%08x\n",
		pDeviceExt->PortNumber,
		pDeviceExt,
		pDeviceExt->Stats.TxFrames,
		pDeviceExt->Stats.TxBytes,
		pDeviceExt->Stats.TxUnderrun,
		pDeviceExt->Stats.TxAbort,
		pDeviceExt->Stats.RxFrames,
		pDeviceExt->Stats.RxBytes,
		pDeviceExt->Stats.RxOverrun,
		pDeviceExt->Stats.RxCrc,
		pDeviceExt->Stats.RxNoBuf,
		pDeviceExt->Stats.RxNoFlow,
		pDeviceExt->Stats.RxOverflow,
		pDeviceExt->Stats.RxAbort));

	DigiDump (DIGIFLOW, ("Exiting IoctlGetStats()\n"));

	return (STATUS_SUCCESS);
}



NTSTATUS
IoctlZeroStats (
	IN PDEVICE_EXT		pDeviceExt)
/*++

Description:

Parameters:

	pDeviceExt
		Address of the Device Extension

Return Value:

	None.

--*/
{
	DigiDump (DIGIFLOW, ("Entering IoctlZeroStats()\n"));

	DigiDump (DIGIVERYLOUD,
		("Before Clearing Statistics for port %d (pDeviceExt: 0x%x)\n"
		"Tx Frames   = 0x%08x\n"
		"Tx Bytes    = 0x%08x\n"
		"Tx Underrun = 0x%08x\n"
		"Tx Abort    = 0x%08x (not used)\n"
		"Rx Frames   = 0x%08x\n"
		"Rx Bytes    = 0x%08x\n"
		"Rx Overrun  = 0x%08x\n"
		"Rx CRC      = 0x%08x\n"
		"Rx No Buf   = 0x%08x (not used)\n"
		"Rx No Flow  = 0x%08x (not used)\n"
		"Rx Overflow = 0x%08x\n"
		"Rx Abort    = 0x%08x\n",
		pDeviceExt->PortNumber,
		pDeviceExt,
		pDeviceExt->Stats.TxFrames,
		pDeviceExt->Stats.TxBytes,
		pDeviceExt->Stats.TxUnderrun,
		pDeviceExt->Stats.TxAbort,
		pDeviceExt->Stats.RxFrames,
		pDeviceExt->Stats.RxBytes,
		pDeviceExt->Stats.RxOverrun,
		pDeviceExt->Stats.RxCrc,
		pDeviceExt->Stats.RxNoBuf,
		pDeviceExt->Stats.RxNoFlow,
		pDeviceExt->Stats.RxOverflow,
		pDeviceExt->Stats.RxAbort));

	RtlZeroMemory (&pDeviceExt->Stats, sizeof (DSMP_STATS));

	DigiDump (DIGIFLOW, ("Exiting IoctlZeroStats()\n"));

	return (STATUS_SUCCESS);
}



NTSTATUS
IoctlCopyCardMemory (
	IN PDEVICE_EXT		pDeviceExt,
	IN PDSMP_GETFRAME	pFrame)
/*++

Description:

	This function will copy as much of card memory as possible into
	the supplied buffer.

Parameters:

	pDeviceExt
		Address of the Device extension.

	pFrame
		Pointer to the User buffer to copy the board memory into.

Return Value:

	None.

--*/
{
	PCONTROLLER_EXT	pControllerExt = pDeviceExt->pControllerExt;
	PUCHAR		pUserBuffer = pFrame->pDataBuffer;
	ULONG		CardMemAddress = 0, DataLength = 0;
	SHORT		CopyLen = 0;
	USHORT		NumberOfWindows = pControllerExt->NumberOfWindows;
	LONG		UserBufferLen = pFrame->DataMaxLength;
	NTSTATUS	Status = STATUS_SUCCESS;


	DigiDump (DIGIFLOW, ("Entering IoctlCopyCardMemory ()\n"));

	if (PCIBus == pControllerExt->BusType)
		NumberOfWindows = 16;

	//
	//  Copy all of the windows of memory into the user buffer (or as
	//  many as possible).
	//
	while (NumberOfWindows && UserBufferLen)
	{
		// Size to copy
		if (PCIBus == pControllerExt->BusType)
		{
			CopyLen = 0x4000;
		}
		else
		{
			CopyLen = (SHORT) MIN (pControllerExt->WindowSize, UserBufferLen);
		}

		fnReadFromCardMemory (
			CardMemAddress,
			pUserBuffer,
			CopyLen,
			pDeviceExt);

		//  New User buffer position to copy to.
		pUserBuffer += CopyLen;

		//  Less User buffer space available.
		UserBufferLen -= CopyLen;

		//  Counting bytes copied.
		DataLength += CopyLen;

		//  Advance the pointer in memory where we are copying from.
		CardMemAddress += CopyLen;

		--NumberOfWindows;
	}

	pFrame->DataLength = DataLength;

	DigiDump (DIGIFLOW, ("Exiting IoctlCopyCardMemory ()\n"));

	return (STATUS_SUCCESS);
}


