
/*****************************************************************************
* 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
*
*****************************************************************************/

#include <ntddk.h>

// Streams Headers													  
#include "types.h"
#include "wan_proto.h"
#include "wan_control.h"

#include "dsmp.h"
#include "dgsna.h"
#include "ioctls.h"
#include "dsapi.h"
#include "dsioctl.h"
#include "digilib.h"

/*
 * Dispatch entry point for Ioctl processing
 */

VOID DgSnaReadComplete(HANDLE hOwner, HANDLE context, PVOID
				pControlBuffer,ULONG controlLength,
				PVOID	pDataBuffer, ULONG dataLength,
				NTSTATUS ioStatus);

NTSTATUS DgResetPort(PPORT_EXT   pPortExt);


/*
 **	Entry point for the Ioctl command.
 **
 */

VOID DgSnaReadRequestDpc(PKDPC pDpc,PVOID context, PVOID sa1, PVOID sa2)
{
    /*
	 * put in another receive request
	 */

	PPORT_EXT			pPortExt;
	NTSTATUS			status;
	LIST_ENTRY			* pReadBufferEntry;
	DGSNA_READ_BUFFER	* pReadBuffer;
	KIRQL				portIrql;

	pPortExt = (PORT_EXT *) context;

	DigiDump(DIGIREAD,("DgSna: Entry DgSnaReadRequestDpc, Port = %x\n",pPortExt));

	// If we have no more read buffers then exit.  This would happen if SNA is not
	// doing the IOCTLGETFRAME ioctls.  In this case the exit routine will requeue
	// the DPC so we will try again later.
	if (IsListEmpty(&pPortExt->readBuffers))	{

		DigiDump(DIGIREAD,("DgSDgSnaReadRequestDpc: No receive buffers, Port = %x\n",pPortExt));

		status = STATUS_INSUFFICIENT_RESOURCES;		
		goto Exit;

	}

	// Take the read off the list of read buffer and put it on the read pending
	// queue
	pReadBufferEntry = ExInterlockedRemoveHeadList(&pPortExt->readBuffers,
									  &pPortExt->ownership);
	pReadBuffer		 = CONTAINING_RECORD(pReadBufferEntry,
									 DGSNA_READ_BUFFER,
									 listEntry);

	ExInterlockedInsertHeadList(&pPortExt->pendedReadBuffers,
								pReadBufferEntry,
								&pPortExt->ownership);

	// Send down a new read
	status = DsGetMsgEx(pPortExt->dsPortHandle,
						0,
						pReadBuffer->buffer,
						pReadBuffer->bufferSize,
						pReadBuffer,
						DS_PRIORITY_LOW,
						DgSnaReadComplete);

	if ((status != STATUS_PENDING) && (status != STATUS_SUCCESS))
	{
		// put the buffer back on the free queue
		RemoveEntryList(pReadBufferEntry);
		InsertHeadList(&pPortExt->readBuffers,pReadBufferEntry);
	}
	else
	{
		status = STATUS_SUCCESS;
	}

Exit:

	KeAcquireSpinLock(&pPortExt->ownership,&portIrql);
	
	pPortExt->dpcReadRequestIsRunning = FALSE;

	// Is the Close routine waiting for notification
	if (pPortExt->state == ClosingState)
	{

		DigiDump(DIGIOPENCLOSE,("DgSDgSnaReadRequestDpc: Notify Close, Port = %x\n",pPortExt));

		// Notify the Close routine that the DPC is finished.
		KeSetEvent( &pPortExt->evClose,
					0,
					FALSE );

	}
	else
	{
		// If sending the read failed or there are no more read buffer, then
		// try it again.
		if (status != STATUS_SUCCESS)
		{

			DigiDump(DIGIREAD,("DgSDgSnaReadRequestDpc: Queue DPC, Port = %x\n",pPortExt));

			pPortExt->dpcReadRequestIsRunning = TRUE;

			KeSetTimer(&pPortExt->readRequestTimer,
					   pPortExt->readRequestTimeout,
					   &pPortExt->dpcReadRequest);
		}
	}

	KeReleaseSpinLock(&pPortExt->ownership,portIrql);

	DigiDump(DIGIREAD,("DgSDgSnaReadRequestDpc: Exit, Port = %x\n",pPortExt));

	return ;

}


VOID
DgSnaWriteComplete(IN HANDLE   hOwner,
		IN HANDLE   pContext,
		IN PVOID    pControlBuffer,
		IN ULONG    ControlLength,
		IN PVOID    pDataBuffer,
		IN ULONG    DataLength,
		IN NTSTATUS IoStatus )
{
	DGSNA_WRITE_BUFFER  * pWriteBuffer;
	PPORT_EXT pPortExt;

	KIRQL	irql;

	DigiDump(DIGIWRITE,("DgsnaWriteCompete : hOwner %x pContext %x control buf %x \
						Contol len %x databuf %x datalen %x status %x\n",
						hOwner,pContext,pControlBuffer,
						ControlLength, pDataBuffer,
						DataLength, IoStatus));

	pWriteBuffer = (DGSNA_WRITE_BUFFER  *) pContext;
	pPortExt = pWriteBuffer->pPortExt;

	KeAcquireSpinLock(&pPortExt->ownership,&irql);

	RemoveEntryList(&pWriteBuffer->listEntry);

	InsertHeadList(&pPortExt->writeBuffers,
				   &pWriteBuffer->listEntry);

	pPortExt->IR.TransmitBufferSpace += DGSNA_MAX_PACKET_SIZE;

	KeReleaseSpinLock( &pPortExt->ownership,irql);

	if(pPortExt->pUserEvent != NULL) {
		KeSetEvent (pPortExt->pUserEvent, 0, FALSE);
	}

	DigiDump(DIGIWRITE,("DgsnaWriteCompete : Exit\n"));

}



/*
 **	Sets the link configuration.
 **	Port locks are held on entry
 */


NTSTATUS
DgSnaIoctlSetLinkConfig (PPORT_EXT pPortExt,
								PLINKCHAR pLinkChars)

{

	struct wan_tnioc	setTune	= {0};
	ULONG				error	= 0;
	HANDLE				hStream	= pPortExt->dsPortHandle;
	NTSTATUS			status	= STATUS_SUCCESS;
	ULONG 				snid;

	DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Enter\n"));

	if  (pPortExt->configured) 
	{
		if (pPortExt->ibmComServer)
			DgResetPort(pPortExt);
		else
			goto Exit;
	}

	// Close the port and reopen it

	
	snid	= pPortExt->snid;
	hStream = pPortExt->dsPortHandle;


	if (pLinkChars->FrameSize < DEFAULT_MIN_MAXDATA)
	{
		status = STATUS_INVALID_PARAMETER;
		pPortExt->information = INFO_FRAME_BUF_TOO_SMALL;
	}
	else if (pLinkChars->FrameSize > DEFAULT_MAX_MAXDATA)
	{
		status = STATUS_INVALID_PARAMETER;
		pPortExt->information = INFO_FRAME_BUF_TOO_BIG;
	}
	else if (pLinkChars->LinkOptions & LO_CLOCK)
	{
		status = STATUS_INVALID_PARAMETER;
		pPortExt->information = INFO_NO_CLOCKS;
	}

	setTune.wan_tune.WAN_options = 0;

	// Line Encoding

	if (pLinkChars->LinkOptions & LO_NRZI)
	{
		setTune.wan_tune.WAN_options |= WAN_NRZI ;
		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : LO_NRZI\n"));
	}
	else
	{
		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : LO_NRZ\n"));
	}

	//  External Clocks

	if (!pPortExt->internalClocking)
  	{
		setTune.wan_tune.WAN_hd.WAN_baud = 0;
		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Clock Ext\n"));
	}
	else
	{
		setTune.wan_tune.WAN_hd.WAN_baud = pPortExt->lineSpeed;
		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Clock internal baud %d\n",setTune.wan_tune.WAN_hd.WAN_baud));
	}

	// Constant RTS
	if (pPortExt->constantRTS)
	{
		setTune.wan_tune.WAN_hd.WAN_hdplx = 0;
		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig :  Full duplex\n"));
	}
	else
	{
		setTune.wan_tune.WAN_hd.WAN_hdplx = DGSNA_DEFAULT_RTS_DELAY;

		// Half-duplex links need to waits for CTS after raising RTS
		setTune.wan_tune.WAN_options |= WAN_WAIT_CTS ;

		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig :  Half duplex\n"));
	}

	// HDLC
	if (pLinkChars->LinkOptions & LO_HDLC)
	{
		setTune.wan_tune.WAN_options |= WAN_HDLC;
		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig :  LO_HDLC\n"));
	}
	else
	{
		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig :  NOT LO_HDLC\n"));
	}


	setTune.lli_type = LI_WANTUNE;
	setTune.lli_snid = pPortExt->snid;

	if (pLinkChars->FrameSize > DGSNA_MAX_PACKET_SIZE)
	{
		status = STATUS_INVALID_PARAMETER;
		goto Exit;
	}

	setTune.wan_tune.WAN_hd.WAN_maxframe			= pLinkChars->FrameSize + 12;
	setTune.wan_tune.WAN_hd.WAN_interface			= WAN_X21;
	setTune.wan_tune.WAN_hd.WAN_cpdef.WAN_cptype	= WAN_NONE;

	setTune.wan_tune.WAN_hd.WAN_StationAddress1		= pLinkChars->StationAddress1;
	setTune.wan_tune.WAN_hd.WAN_StationAddress2		= pLinkChars->StationAddress2;

	DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig :  Max fr sz %d snid %d\n",setTune.wan_tune.WAN_hd.WAN_maxframe, setTune.lli_snid));

	status = DsIoctl( hStream, W_SETTUNE, &setTune, sizeof setTune, &error );

	if (NT_ERROR(status))
		goto Exit;

	pPortExt->config.MaxFrameSize = (USHORT)pLinkChars->FrameSize;
	pPortExt->stationAddress1 = pLinkChars->StationAddress1;
	pPortExt->stationAddress2 = pLinkChars->StationAddress2;
	pPortExt->linkOptions = pLinkChars->LinkOptions;

    //  Should we reset statistics?
	if (pLinkChars->LinkOptions & LO_RSTAT)
	{
		pPortExt->IR.StatusEvents = 0;
		RtlZeroMemory (pPortExt->IR.StatisticsCounters,
			sizeof (pPortExt->IR.StatisticsCounters));

		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Stats reset\n"));
	}

	DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig :  return status %x\n",status));

	// Send WAN_REG
	{
		struct wan_reg wanReg = {0};
		ULONG error;
		wanReg.wan_type = WAN_REG;
		wanReg.wan_snid = snid;

		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig :  Send WAN_REG\n"));

		status = DsPutMsg( hStream, &wanReg, sizeof wanReg, NULL, 0 ,
									DS_PRIORITY_LOW);

		if (status != STATUS_SUCCESS)
		{
			DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Send WAN_REG failed status = %x\n",status));
			goto Exit;
		}

		ASSERT( NT_SUCCESS( status ) );
	}




	// Send GetMsg, to get the WC_CONNECT msg from firmware
	{
		UCHAR GetCtrl[256];
		UCHAR GetData[512];
		ULONG CtrlLen, DataLen;

		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Send GetMsg\n"));


		status = DsGetMsg( hStream,
						   &GetCtrl,
						   sizeof GetCtrl,
						   &CtrlLen,
						   &GetData,
						   sizeof GetData,
						   &DataLen,
						   DS_PRIORITY_LOW);


		{
			struct wan_ctl *pWanCtrl = (PVOID)GetCtrl;

		}
		if (status != STATUS_SUCCESS)
		{

			DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Send GetMsg failed status = %x\n",status));
			goto Exit;
		}
	}

	// Send WC_CONCNF to confirm firmware WC_CONNECT
	{
		struct wan_ctl wanCtrl = {0};
		ULONG error;
		wanCtrl.wan_type = WAN_CTL;
		wanCtrl.wan_command = WC_CONCNF;
		wanCtrl.wan_status = WAN_SUCCESS;

		DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Send WC_CONCNF\n"));

		status = DsPutMsg( hStream,
						   &wanCtrl,
						   sizeof wanCtrl,
						   NULL,
						   0,
							DS_PRIORITY_HIGH );

		ASSERT( NT_SUCCESS( status ) );
		if (status != STATUS_SUCCESS)
		{
			DigiDump(DIGIIOCTL,("DgSnaIoctlSetLinkConfig : Send WC_CONCNF failed status = %x\n",status));
			goto Exit;
		}

	}

	// put a pending request for rx data
	{
		LIST_ENTRY			* pReadBufferEntry;
		DGSNA_READ_BUFFER	* pReadBuffer;

		pReadBufferEntry = RemoveHeadList(&pPortExt->readBuffers);
		pReadBuffer = CONTAINING_RECORD(pReadBufferEntry,
						DGSNA_READ_BUFFER,listEntry );

		InsertHeadList(&pPortExt->pendedReadBuffers,
					  pReadBufferEntry);


		status = DsGetMsgEx(pPortExt->dsPortHandle,
							0,
							pReadBuffer->buffer,
							pReadBuffer->bufferSize,
							pReadBuffer,
							DS_PRIORITY_LOW,
							DgSnaReadComplete);

		if ((status == STATUS_SUCCESS) || (status == STATUS_PENDING))
		{
			 status = STATUS_SUCCESS;
  		}
		else {
			// put the buffer back on the free queue
			RemoveEntryList(pReadBufferEntry );
			InsertHeadList(&pPortExt->readBuffers,pReadBufferEntry);
			goto Exit;
		}
	}

	pPortExt->configured = 1;

Exit:

	return(status);
}

NTSTATUS
DgSnaSetModemStatus(PPORT_EXT pPortExt,PIRP pIrp)

{

	struct wan_mdmioc	modemIoctlBlock;
	UCHAR				modemSignals;
	NTSTATUS			status;
	ULONG				error;
	HANDLE				hStream = pPortExt->dsPortHandle;

	modemIoctlBlock.snid = pPortExt->snid;
	modemIoctlBlock.mask = modemIoctlBlock.signals = (ULONG) 0;

	modemSignals					= * (PUCHAR) pIrp->UserBuffer;
	pPortExt->IR.OutputV24Status	= modemSignals;

	DigiDump(DIGIMODEM,("DgSnaSetModemStatus : Modem Signals = %x\n",modemSignals));

	modemIoctlBlock.mask |= (W_DTR | W_RTS);

	if (modemSignals & OV24_DTR)	{
		modemIoctlBlock.signals |= W_DTR;
	} else  {
      modemIoctlBlock.signals &= ~W_DTR;
	}

	if (modemSignals & OV24_RTS)	{
      modemIoctlBlock.signals |= W_RTS;
	} else  {
      modemIoctlBlock.signals &= ~W_RTS;
	}

	if (pPortExt->constantRTS)
		modemIoctlBlock.signals |= W_RTS;

	status = DsIoctl( hStream, W_SETMODEM, &modemIoctlBlock, sizeof(modemIoctlBlock), &error );

	return(STATUS_SUCCESS);
}


NTSTATUS
DgSnaGetModemStatus(PPORT_EXT pPortExt,PIRP pIrp)
{


	struct wan_mdmioc	modemIoctlBlock;
	UCHAR				modemSignals = 0;
	ULONG				error;
	NTSTATUS			status;
	HANDLE				hStream = pPortExt->dsPortHandle;

	modemIoctlBlock.snid = pPortExt->snid;
	modemIoctlBlock.mask = modemIoctlBlock.signals = (ULONG) 0;

	status = DsIoctl( hStream, W_GETMODEM, &modemIoctlBlock,
							sizeof(modemIoctlBlock),&error );

 	if  (!NT_SUCCESS(status))
		return (status);

	if (modemIoctlBlock.signals & W_CTS)
		modemSignals |= IV24_CTS;
	if (modemIoctlBlock.signals & W_DCD)
		modemSignals |= IV24_DCD;
	if (modemIoctlBlock.signals & W_DSR)
		modemSignals |= IV24_DSR;

	pPortExt->IR.InputV24Status = modemSignals ;

	DigiDump(DIGIMODEM,("DgSnaGetModemStatus : Modem Signals = %x\n",modemSignals));

	return(status);
}



/*
 * TODO
 *		Stop the xmitter on the board
 *		Flush it on the board
 *		Flush the buffers in DxB
 *		Restart the xmitter on the board
 */


NTSTATUS
DgSnaAbortTransmit(PPORT_EXT pPortExt,PIRP pIrp)
{

	NTSTATUS status;

	DigiDump(DIGIIOCTL,("DgSnaAbortTransmit : Entry\n"));

	status = DsPurge(pPortExt->dsPortHandle,(DS_PURGE_PUTMSG | DS_PURGE_WRITE));

	return (status);
}


/*
 * TODO
 *		Stop the Receiver on the board
 *		Flush it on the board
 *		Flush the buffers in DxB
 *		Restart the Receiver on the board
 */


NTSTATUS
DgSnaAbortReceive(PPORT_EXT pPortExt,PIRP pIrp)
{

	NTSTATUS status;

	DigiDump(DIGIIOCTL,("DgSnaAbortReceive : Entry\n"));

	status = DsPurge(pPortExt->dsPortHandle,(DS_PURGE_GETMSG| DS_PURGE_READ));

	return (status);

}


NTSTATUS
IoctlPutFrame (PPORT_EXT pPortExt, DSMP_PUTFRAME * pFrame)
{


	UCHAR				requiredBuffers = 0;
	ULONG				txFrameLen		= pFrame->DataLength;
	PUCHAR				pUserBuffer		= pFrame->pDataBuffer;
	NTSTATUS			status			= STATUS_SUCCESS;
	DGSNA_WRITE_BUFFER	*pWriteBuffer;
	ULONG				len;
	PLIST_ENTRY			pListEntry;
	struct wan_msg		*pWanMsg;

	DigiDump(DIGIIOCTL,("IoctlPutFrame : Entry\n"));

	KeAcquireSpinLock(&pPortExt->ownership, &pPortExt->portIrql);

	if (IsListEmpty(&pPortExt->writeBuffers)) {
		status  = STATUS_UNSUCCESSFUL;
		goto IoctlPutFrameExit;
	}

	pListEntry		= RemoveHeadList(&pPortExt->writeBuffers);

	pWriteBuffer	= CONTAINING_RECORD(pListEntry,
										DGSNA_WRITE_BUFFER,
										listEntry);

	pWanMsg					= &pWriteBuffer->controlBlock;
	pWriteBuffer->pPortExt	= pPortExt;


	pPortExt->IR.TransmitBufferSpace	-= DGSNA_MAX_PACKET_SIZE ;
	pPortExt->stats.TxBytes				+= txFrameLen;

	pWanMsg->wan_type		= WAN_DAT;
	pWanMsg->wan_command	= WC_TX;

	len = txFrameLen;

	RtlCopyMemory(pWriteBuffer->buffer,pUserBuffer,len);

	InsertHeadList(&pPortExt->pendedWriteBuffers,
									  pListEntry);

	DigiDump(DIGIIOCTL,("IoctlPutFrame : handle %x pWanMsg %x control sz %x buf %x \
							buflen %x context %x\n",pPortExt->dsPortHandle, pWanMsg,
							sizeof( struct wan_msg), pWriteBuffer->buffer,
							txFrameLen,pWriteBuffer));
	DigiDump(DIGIIOCTL,("IoctlPutFrame : sdlc addr %x sdlcctrl %x\n",pWriteBuffer->buffer[0],pWriteBuffer->buffer[1]));

	DigiDump( DIGIWRITEDATA, ("Ioctl Data: \n") );
	DigiDumpData( DIGIWRITEDATA, pWriteBuffer->buffer, len );

	KeReleaseSpinLock(&pPortExt->ownership,pPortExt->portIrql);

	status = DsPutMsgEx( pPortExt->dsPortHandle, pWanMsg, sizeof( struct wan_msg),
						 pWriteBuffer->buffer, txFrameLen ,pWriteBuffer,
						 DS_PRIORITY_LOW, DgSnaWriteComplete );

	KeAcquireSpinLock(&pPortExt->ownership,&pPortExt->portIrql);

	if ((status != STATUS_PENDING) && (status !=  STATUS_SUCCESS))
	{
		RemoveEntryList(pListEntry);
		InsertHeadList(&pPortExt->writeBuffers, pListEntry);
		DigiDump(DIGIIOCTL,("IoctlPutFrame : DsPutMsgEx returns %x%x\n",status));
	}
	else
	{
		DigiDump(DIGIIOCTL,("IoctlPutFrame : DsPutMsgEx returns %x%x\n",status));
		status = STATUS_SUCCESS;
	}

IoctlPutFrameExit:

	KeReleaseSpinLock(&pPortExt->ownership, pPortExt->portIrql);

	return (status);
}


NTSTATUS
IoctlGetFrame(PPORT_EXT pPortExt,DSMP_GETFRAME * pFrame)
{
	NTSTATUS			status = STATUS_SUCCESS;
	LIST_ENTRY			* pListEntry;
	DGSNA_READ_BUFFER 	* pReadBuffer;
	ULONG				* pLength, len;
	UCHAR				* pTempBuffer;
	PUCHAR				pUserBuffer = pFrame->pDataBuffer;

	DigiDump(DIGIIOCTL,("IoctlGetFrame : Entry\n"));

	KeAcquireSpinLock(&pPortExt->ownership,&pPortExt->portIrql);

	// No data to read.
	// This is not an error
	if (IsListEmpty(&pPortExt->completedReadBuffers))
      goto IoctlGetFrameExit;


	// Remove the first packet. check lenght. If too big,
	// put it back. return error.
	pListEntry = RemoveHeadList(&pPortExt->completedReadBuffers);

	pReadBuffer = CONTAINING_RECORD(pListEntry,
								    DGSNA_READ_BUFFER,
									listEntry);

	if (pReadBuffer->numberOfBytes > pFrame->DataMaxLength) {
		status = STATUS_BUFFER_TOO_SMALL;
		InsertHeadList(&pPortExt->completedReadBuffers,pListEntry);
		goto IoctlGetFrameExit;
	}

	pFrame->DataLength = pReadBuffer->numberOfBytes;
    pTempBuffer = pReadBuffer->buffer;

	len =  pReadBuffer->numberOfBytes;

	// Copy data in user buffer
	RtlCopyMemory( pUserBuffer,pReadBuffer->buffer,len);
	
	// Update the Interface Record
	pPortExt->IR.ReceivedFrames--;

	DigiDump( DIGIREADDATA, ( "IoctlGetFrame data:\n" ) );
	DigiDumpData( DIGIREADDATA, pUserBuffer, len );

	// Put the buffer in the available queue
	InsertHeadList(&pPortExt->readBuffers,pListEntry);

IoctlGetFrameExit:

	KeReleaseSpinLock(&pPortExt->ownership, pPortExt->portIrql);

	return(status);
}



/*
 * TODO
 * grab the locks
 */


VOID
DgSnaReadComplete(HANDLE hOwner, HANDLE context, PVOID
				pControlBuffer,ULONG controlLength,
				PVOID	pDataBuffer, ULONG dataLength,
				NTSTATUS ioStatus)
{

	DGSNA_READ_BUFFER * pReadBuffer;
	PPORT_EXT			pPortExt;
	LIST_ENTRY		  * pReadBufferEntry;
	NTSTATUS			status = STATUS_SUCCESS;
	UCHAR			  * buffer;
	KIRQL				portIrql;
	BOOLEAN				i = TRUE;

	int	rxOK = 1;	// flag for checking addresses for receive

	pReadBuffer = (DGSNA_READ_BUFFER *) context;
	pPortExt	= pReadBuffer->pPortExt;

	DigiDump(DIGIREAD,("DgSnaReadComplete: Enter B=%X, P=%x\n",pReadBuffer,pPortExt));

	pReadBuffer->numberOfBytes = dataLength;
	pReadBufferEntry = &pReadBuffer->listEntry;

	KeAcquireSpinLock( &pPortExt->ownership, &portIrql);
	RemoveEntryList(pReadBufferEntry);

	// The Status must be success, otherwise, put the buffer back in the
	// read buffer pool.
	if (ioStatus != STATUS_SUCCESS)
	{
		DigiDump(DIGIREAD,("DgSnaReadComplete: NoZero Status B=%X, P=%x, S=%x\n",pReadBuffer,pPortExt,ioStatus));
		InsertHeadList(&pPortExt->readBuffers,pReadBufferEntry);
		KeReleaseSpinLock(&pPortExt->ownership, portIrql);
		goto DgSnaReadCompleteExit;
	}

	/*
	 **
	 **	Implement selective receiving based on
	 **	station addresses.
	 **	If both the station addresses are 0
	 **	receive everything, else
	 **	receive packets from only the selected
	 **	stations.
	 **/

	buffer = pReadBuffer->buffer;

	if( pPortExt->stationAddress1 || pPortExt->stationAddress2)
	{

		if (buffer[0] != 0xff)
		{


			if ((pPortExt->stationAddress1 != buffer[0]) &&
                   (pPortExt->stationAddress2 != buffer[0]))

			{
				DigiDump(DIGIREAD,("DgSnaReadComplete: Rejecting B=%X, P=%x, addr=%x, ctrl=%x\n",pReadBuffer,pPortExt,buffer[0],buffer[1]));
				InsertHeadList(&pPortExt->readBuffers,pReadBufferEntry);
				rxOK = FALSE;
			}

		}
	}

	if (rxOK)
	{

		DigiDump(DIGIREAD,("DgSnaReadComplete: Accepting B=%X, P=%x, addr=%x, ctrl=%x, len=%x\n",pReadBuffer,pPortExt,buffer[0],buffer[1],dataLength));
		DigiDump( DIGIREADDATA, ( "DgSnaReadComplete data:\n" ) );
		DigiDumpData( DIGIREADDATA, buffer, dataLength );

		InsertTailList(&pPortExt->completedReadBuffers,&pReadBuffer->listEntry);

		++pPortExt->IR.ReceivedFrames;
		++pPortExt->receivedFrames;
		++pPortExt->stats.RxFrames;

		if(pPortExt->pUserEvent != NULL) {
			KeSetEvent (pPortExt->pUserEvent, 0, FALSE);
		}
	}

	// If we are closing then don't start the read DPC
	if (pPortExt->state == OpenState)
	{
		LARGE_INTEGER Delay = {0};
		pPortExt->dpcReadRequestIsRunning = TRUE;
		DigiDump(DIGIREAD,("DgSnaReadComplete: DPC Set B=%X, P=%x\n",pReadBuffer,pPortExt));
		KeReleaseSpinLock(&pPortExt->ownership, portIrql);
		KeSetTimer(&pPortExt->readRequestTimer, Delay, &pPortExt->dpcReadRequest);
	}
	else
	{
		KeReleaseSpinLock(&pPortExt->ownership, portIrql);
	}

DgSnaReadCompleteExit:

	DigiDump(DIGIREAD,("DgSnaReadComplete: Exit B=%X, P=%x\n",pReadBuffer,pPortExt));

	return;
}



/*
 **	Entry point for the Ioctl command.
 **
 */



/*
 **		Ioctl processing Routine - Called from the entry point.
 **		Port Lock is held
 */

NTSTATUS
DgSnaProcessIoctl(PPORT_EXT pPortExt, PIRP pIrp)

{

	IO_STACK_LOCATION  *pIrpSp;				// Current IRP stack position.
	NTSTATUS			status;				// Called function return value.
	LONG				inputBufferLength = 0, outputBufferLength = 0;
	PMDL				pMdl = NULL, pNextMdl = NULL;

	pIrpSp = IoGetCurrentIrpStackLocation(pIrp);

	inputBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
	outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;

	pIrp->IoStatus.Information = 0L;

	switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode)
	{
		case	IOCTLSETEVENTHANDLE:

			DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLSETEVENTHANDLE\n"));

			status = STATUS_SUCCESS;
			if (pIrp->UserEvent != NULL)
			{
					pPortExt->pUserEvent = pIrp->UserEvent;
					KeSetEvent (pPortExt->pUserEvent, 0, FALSE);
					status = STATUS_SUCCESS;
			}
			else
			{
				status = STATUS_INVALID_PARAMETER;
				pIrp->IoStatus.Information = INFO_SET_EVENT_NO_EVENT;

				DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLSETEVENTHANDLE Failed\n"));
			}
			break;

		case IOCTLSETLINK: {

			PLINKCHAR	pLinkChars = (PLINKCHAR) pIrp->AssociatedIrp.SystemBuffer;

			DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLSETLINK\n"));

			if (sizeof (LINKCHAR) == inputBufferLength)
			{
				status = DgSnaIoctlSetLinkConfig (pPortExt, pLinkChars);

				if (!NT_SUCCESS (status))
				{
					pIrp->IoStatus.Information = pPortExt->information;
				}
			}
			else
			{
				status = STATUS_INVALID_PARAMETER;
				pIrp->IoStatus.Information = INFO_LINKCHAR_BUF_WRONG_SIZE;
				DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLSETLINK Failes\n"));
			}

			break;
		}


		case IOCTLSETV24OUTPUTSTATUS :

			// This updates the Modem signals in the IR (not ER)
			DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLSETV24OPSTATUS\n"));
			status = DgSnaSetModemStatus(pPortExt,pIrp);
			break;

		case IOCTLTXFRAME: {

			DSMP_PUTFRAME	Frame;

			DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLTXFRAME\n"));

			status = STATUS_SUCCESS;

			if (inputBufferLength < sizeof (USHORT))
			{
				status = STATUS_BUFFER_TOO_SMALL;
				pPortExt->information = INFO_TX_FRAME_TOO_SMALL;
			}
			else if (inputBufferLength > pPortExt->config.MaxFrameSize)
			{
				status = STATUS_INVALID_PARAMETER;
				pPortExt->information = INFO_TX_FRAME_TOO_BIG;
			}
			else
			{
				RtlZeroMemory (&Frame, sizeof (Frame));
				Frame.DataLength = inputBufferLength;

				Frame.DataLength =  pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
				Frame.pDataBuffer = pIrp->AssociatedIrp.SystemBuffer;

				IoctlPutFrame (pPortExt, &Frame);

			}

			break;

		}
		case IOCTLABORTTX :

			DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLABORTTX\n"));

			status = DgSnaAbortTransmit(pPortExt,pIrp);
			break;

		case IOCTLABORTRX	:

			DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLABORTRX\n"));

			status = DgSnaAbortReceive(pPortExt,pIrp);
			break;

		case IOCTLGETINTERFACERECORD:

			if (sizeof (IR) == outputBufferLength)
			{
				status = STATUS_SUCCESS;

				KeAcquireSpinLock(&pPortExt->ownership, &pPortExt->portIrql);

				RtlCopyMemory (MmGetSystemAddressForMdl (pIrp->MdlAddress),
							   &pPortExt->IR,
							   sizeof (IR));

				pPortExt->IR.StatusEvents = 0;
				RtlZeroMemory (pPortExt->IR.StatisticsCounters,
							   sizeof (pPortExt->IR.StatisticsCounters));

				KeReleaseSpinLock(&pPortExt->ownership, pPortExt->portIrql);

			}
			else
			{
				status = STATUS_INVALID_PARAMETER;
				pIrp->IoStatus.Information = INFO_READ_IR_BUFFER_WRONG_SIZE;
			}

		break;


		case IOCTLGETV24STATUS :

			DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: IOCTLGETV24STAT\n"));
			status = DgSnaGetModemStatus(pPortExt,pIrp);
			break;

		case IOCTLRXFRAME	:
		{

			DSMP_GETFRAME		Frame;

			RtlZeroMemory (&Frame, sizeof (Frame));
			Frame.DataMaxLength = outputBufferLength;

			if (outputBufferLength >= sizeof (USHORT))
			{

				Frame.pDataBuffer = MmGetSystemAddressForMdl (pIrp->MdlAddress);
				status = IoctlGetFrame (pPortExt, &Frame);

				if (NT_SUCCESS (status))
					pIrp->IoStatus.Information = Frame.DataLength;
			}
			else
			{
				status = STATUS_BUFFER_TOO_SMALL;
			}

			break;
		}


		default:
			DigiDump(DIGIIOCTL,("DgSnaProcessIoctl: Unknown Ioctl\n"));
			status = STATUS_INVALID_PARAMETER;
			break;

	}

	return status;
} // function ends



DgSnaProcessCommands(PDEVICE_OBJECT  pDeviceObject, PIRP pIrp)
{


	NTSTATUS    status;                 /* Called function return value. */
	KIRQL       oldCancelIrql;          /* Save prior irq level. */
	PPORT_EXT	pPortExt;

	pIrp->IoStatus.Information	= 0;
	pPortExt					= pDeviceObject->DeviceExtension;

	// Process the Ioctl
	status = DgSnaProcessIoctl(pPortExt, pIrp);

	if (status == STATUS_PENDING)
	{

		// We should never send pending back on this IOCTL interface.
		// In Debug mode we will ASSERT. In the release code we will
		// send back STATUS_UNSUCCESSFUL
		ASSERT(0);

		status = STATUS_UNSUCCESSFUL;

	}

	pIrp->IoStatus.Status = status;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);

	DigiDump(DIGIIOCTL,("DgSnaProcess: Commands returns %x\n",status));

	return (status);
}




NTSTATUS DgResetPort(PPORT_EXT   pPortExt)
{
	KIRQL closeIrql;
	NTSTATUS status;
	ULONG       errorCode;
	ULONG 		snid;
	HANDLE		hStream = NULL;

	DigiDump(DIGIIOCTL,("Entry:DgResetPort pExt = 0x%x\n",pPortExt));
	KeAcquireSpinLock(&pPortExt->ownership, &closeIrql);
	pPortExt->state = ClosingState;

	// If the DPC is running we must delay the close till it is finished.
	if (pPortExt->dpcReadRequestIsRunning == TRUE)
	{

		DigiDump(DIGIIOCTL,("DgResetPort : The DPC is running Port = %x\n",pPortExt));

		// Reset the Close Event
		KeResetEvent(&pPortExt->evClose );

		KeReleaseSpinLock(&pPortExt->ownership,closeIrql);

		// Wait till the DPC is finished
		KeWaitForSingleObject( &pPortExt->evClose, Executive, KernelMode, FALSE, NULL );

	}
	else
	{
		KeReleaseSpinLock(&pPortExt->ownership,closeIrql);
	}

	DigiDump(DIGIIOCTL,("DgResetPort : Close The Port = %x\n",pPortExt));

	// Closing the Device will finish all the outstanding IRPs.
	DsClose(pPortExt->dsPortHandle);
	pPortExt->dsPortHandle =  NULL;

	// Now all IOCTLs are complete.  We need to put the completed read buffers
	// back in the pool.

	KeAcquireSpinLock(&pPortExt->ownership, &closeIrql);

	while(!IsListEmpty(&pPortExt->completedReadBuffers)){

		LIST_ENTRY * p;

		p  = RemoveHeadList(&pPortExt->completedReadBuffers);
		InsertHeadList( &pPortExt->readBuffers,p);

	} 

	KeReleaseSpinLock(&pPortExt->ownership,closeIrql);

	pPortExt->state = ResetState;

	status = DsOpen( pPortExt->dsAdapterHandle,
                     pPortExt->deviceName,
                     (HANDLE)pPortExt,
                     pPortExt,
                     sizeof (pPortExt),
                     &pPortExt->dsPortHandle,
                     &errorCode);

	if (status != STATUS_SUCCESS)
	{
		DigiDump(DIGIIOCTL,("DgReset:  DsOpen failed, status %x\n",status));
		goto ResetExit;
	}


	// Send WAN_SID
	snid	= pPortExt->snid;
	hStream = pPortExt->dsPortHandle;

	{
		struct wan_sid sid	= {0};
		sid.wan_type		= WAN_SID;
		sid.wan_snid		= snid;

		DigiDump(DIGIIOCTL,("DgReset:Send WAN_SID\n"));

		status = DsPutMsg( hStream,
						   &sid,
						   sizeof (sid),
						   NULL,
						   0,
						   DS_PRIORITY_LOW );

		ASSERT( NT_SUCCESS( status ) );
		if (status != STATUS_SUCCESS){
			DigiDump(DIGIIOCTL,("DgReset: WAN_SID failed, status %x\n",status));
			goto ResetExit;
		}
		
	}	

	// clear the Interface Record
	
	RtlZeroMemory (&pPortExt->IR, sizeof (IR));
	pPortExt->IR.TransmitBufferSpace = DGSNA_MAX_WRITE_ENTRIES * DGSNA_MAX_PACKET_SIZE;

ResetExit:
	if (status != STATUS_SUCCESS){
		if (pPortExt->dsPortHandle){
			DsClose( pPortExt->dsPortHandle);
			pPortExt->dsPortHandle = NULL;
			}
		if (pPortExt->dsAdapterHandle){
			DsCloseDevice( pPortExt->dsAdapterHandle);
			pPortExt->dsAdapterHandle = NULL;
			}
		pPortExt->state = IdleState;
	}
	else
	{
		pPortExt->state = OpenState;
	}


	DigiDump(DIGIIOCTL,("DgReset:Exit, status %x\n",status));

	return(status);
}