// Control.cpp -- IOCTL handlers for AIOWDM driver

#include "stddcls.h"
#include "driver.h"
#include "ioctls.h"

void ForgetIRP(PIRP IRP);
NTSTATUS DispatchDMAPiongControl(PDEVICE_EXTENSION pDX, PIRP pIRP, PUCHAR pCmd, ULONG InSize, ULONG OutSize, ULONG & DataOut);

VOID IRQGenCancelIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

//For SDV.
DRIVER_CANCEL IRQGenCancelIrp;

///////////////////////////////////////////////////////////////////////////////

#pragma PAGEDCODE

NTSTATUS DispatchControl(PDEVICE_OBJECT fdo, PIRP Irp)
{							// DispatchControl
	PAGED_CODE();
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	NTSTATUS status = STATUS_SUCCESS;
	if ( pdx->bRealDevice )
	{
		status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
		if ( ! NT_SUCCESS(status) )
			return CompleteRequest(Irp, status, 0)
		;
	}
	ULONG info = 0;

	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
	ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
	ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
	ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;

	switch ((int)code)
	{						// by IOCTL code

		case IOCTL_QUERY_INFO:				// code == 0x800
		{						// IOCTL_QUERY_INFO

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if (cbout < sizeof(TCARDINFO))
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			PCARDINFO CIBuf = PCARDINFO(Irp->AssociatedIrp.SystemBuffer);
			CIBuf->DeviceID = pdx->DeviceID;
			CIBuf->BaseAddress = ULONG(UNATIVE(pdx->BAR[biMain].Base));
			info = sizeof(TCARDINFO);

			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_QUERY_INFO

		case IOCTL_QUERY_BAR_BASE:				// code == 0x801
		{						// IOCTL_QUERY_BAR_BASE

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if (cbout < sizeof(ULONG))
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			PULONG pBuf = PULONG(Irp->AssociatedIrp.SystemBuffer);
			switch ( *pBuf )
			{
				case 1:
					*pBuf = ULONG(UNATIVE(pdx->BAR[biStatus].Base));
				break;
				case 2:
					*pBuf = ULONG(UNATIVE(pdx->BAR[biMain].Base));
				break;
				case 3:
					*pBuf = ULONG(UNATIVE(pdx->BAR[biAux].Base));
				break;

				/* J2H: added QueryBARBase support for arbitrary BAR[n] via 0x8n */
				case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85:
					*pBuf = ULONG(UNATIVE(pdx->BAR[*pBuf - 0x80].Base));
				break;

				default:
					*pBuf = 0;
				//break;
			}
			info = sizeof(*pBuf);
			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_QUERY_BAR_BASE

		case IOCTL_QUERY_BAR_RANGE:
		{

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if ( cbout < 3 * sizeof(ULONGLONG) )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			ULONG BarID = *PULONG(Irp->AssociatedIrp.SystemBuffer);
			TPBarData pBar = NULL;
			PULONGLONG pBuf = PULONGLONG(Irp->AssociatedIrp.SystemBuffer);
			switch ( BarID )
			{
				case 1:
					pBar = &pdx->BAR[biStatus];
				break;
				case 2:
					pBar = &pdx->BAR[biMain];
				break;
				case 3:
					pBar = &pdx->BAR[biAux];
				break;

				/* J2H: added QueryBARBase support for arbitrary BAR[n] via 0x8n */
				case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85:
					pBar = &pdx->BAR[BarID - 0x80];
				break;
			}
			if ( pBar ) switch ( pBar->BarType )
			{
				case btIO:
					pBuf[0] = 1;
				break;
				case btMappedIO: case btMemory:
					pBuf[0] = 3;
				break;
				default:
					pBar = NULL;
				//break;
			}
			if ( pBar )
			{
				pBuf[1] = ULONGLONG(UNATIVE(pBar->Base));
				pBuf[2] = ULONGLONG(UNATIVE(pBar->Size));
				info = 3 * sizeof(*pBuf);
				status = STATUS_SUCCESS;
			}
			else
			{
				info = 0;
				status = STATUS_NO_MORE_FILES;
			}

		break;
		}

		case IOCTL_PORT_IN:				// code == 0x820
		{						// IOCTL_PORT_IN

			if ( (cbin < sizeof(TPORTOP)) || cbout < sizeof(TPORTOP) )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			PPORTOP POBuf = PPORTOP(Irp->AssociatedIrp.SystemBuffer);
			TBarData TarBar = pdx->BAR[biMain];
			switch ( POBuf->Size )
			{
				case 1:
					POBuf->Data8 = InBar8(TarBar, POBuf->Address);
				break;
				case 2:
					POBuf->Data16 = InBar16(TarBar, POBuf->Address);
				break;
				case 4:
					POBuf->Data32 = InBar32(TarBar, POBuf->Address);
				break;
			}
			info = sizeof(TPORTOP);

			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_PORT_IN

		case IOCTL_PORT_OUT:				// code == 0x822
		{						// IOCTL_PORT_OUT

			if (cbout < sizeof(TPORTOP))
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			PPORTOP POBuf = PPORTOP(Irp->AssociatedIrp.SystemBuffer);
			TBarData TarBar = pdx->BAR[biMain];
			switch ( POBuf->Size )
			{
				case 1:
					OutBar8(TarBar, POBuf->Address, POBuf->Data8);
				break;
				case 2:
					OutBar16(TarBar, POBuf->Address, POBuf->Data16);
				break;
				case 4:
					OutBar32(TarBar, POBuf->Address, POBuf->Data32);
				break;
			}
			info = 0;

			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_PORT_OUT


/* J2H Adding InBarxxx support*/
		case IOCTL_InBAR:			// code == 0x850
		{
			if ( (cbin < sizeof(TMEMOP)) || cbout < sizeof(TMEMOP) )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			PMEMOP POBuf = PMEMOP(Irp->AssociatedIrp.SystemBuffer);
			if ( (POBuf->BAR & 0x07) > 5 )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			TBarData TarBar;
			switch (POBuf->BAR)
			{
			case 1:
				TarBar = pdx->BAR[biStatus];
				break;
			case 2:
				TarBar = pdx->BAR[biMain];
				break;
			case 3:
				TarBar = pdx->BAR[biAux];
				break;				
			case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85:
				TarBar = pdx->BAR[POBuf->BAR - 0x80];
				break;
			default:
				TarBar = pdx->BAR[biMain];
				//break;
			}

			switch ( POBuf->Size )
			{
				case 1:
					POBuf->Data8 = InBar8(TarBar, POBuf->Offset);
				break;
				case 2:
					POBuf->Data16 = InBar16(TarBar, POBuf->Offset);
				break;
				case 4:
					POBuf->Data32 = InBar32(TarBar, POBuf->Offset);
				break;
			}
			info = sizeof(TMEMOP);
			status = STATUS_SUCCESS;
		break;
		}						// IOCTL_InBAR

/* J2H Adding OutBarxxx support */
		case IOCTL_OutBAR:				// code == 0x851
		{
			if (cbout < sizeof(TMEMOP))
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			PMEMOP POBuf = PMEMOP(Irp->AssociatedIrp.SystemBuffer);
			if ((POBuf->BAR & 0x07) > 5)
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			TBarData TarBar;
			switch (POBuf->BAR)
			{
			case 1:
				TarBar = pdx->BAR[biStatus];
				break;
			case 2:
				TarBar = pdx->BAR[biMain];
				break;
			case 3:
				TarBar = pdx->BAR[biAux];
				break;
				/* J2H: added QueryBARBase support for arbitrary BAR[n] via 0x8n */
			case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85:
				TarBar = pdx->BAR[POBuf->BAR - 0x80];
				break;
			default:
				TarBar = pdx->BAR[biMain];
				//break;
			}

			switch ( POBuf->Size )
			{
				case 1:
					OutBar8(TarBar, POBuf->Offset, POBuf->Data8);
				break;
				case 2:
					OutBar16(TarBar, POBuf->Offset, POBuf->Data16);
				break;
				case 4:
					OutBar32(TarBar, POBuf->Offset, POBuf->Data32);
				break;
			}
			info = 0;
			status = STATUS_SUCCESS;
		break;
		}						// IOCTL_OutBAR

		case IOCTL_PORTMB_OUT:
		{						// IOCTL_PORTMB_OUT

			if ( cbout < sizeof(TPORTOP)-4+1 )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			PPORTOP POBuf = PPORTOP(Irp->AssociatedIrp.SystemBuffer);
			if ( cbout < sizeof(TPORTOP)-4+POBuf->Size )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			TBarData TarBar = pdx->BAR[biMain];
			OutBar8M(TarBar, POBuf->Address, POBuf->Size, &POBuf->Data8);
			info = 0;

			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_PORTMB_OUT

		case IOCTL_PORT_INSTR:				// code == 0x821
		{						// IOCTL_PORT_INSTR

			PPORTOP POBuf;
			if ( (cbin < sizeof(*POBuf)) )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			POBuf = PPORTOP(Irp->AssociatedIrp.SystemBuffer);
			ULONG DataCt = cbout / POBuf->Size;
			if ( DataCt <= 0 )
			{
				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}
			PVOID pData = Irp->AssociatedIrp.SystemBuffer;
			TBarData TarBar = pdx->BAR[biMain];
			switch ( POBuf->Size )
			{
				case 1:
					InBarStr8(TarBar, POBuf->Address, (PUCHAR)pData, DataCt);
				break;
				case 2:
					InBarStr16(TarBar, POBuf->Address, (PUSHORT)pData, DataCt);
				break;
				case 4:
					InBarStr32(TarBar, POBuf->Address, (PULONG)pData, DataCt);
				break;
			}
			info = DataCt * POBuf->Size;

			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_PORT_INSTR

		case IOCTL_PORT_OUTSTR:				// code == 0x823
		{						// IOCTL_PORT_OUTSTR

			PPORTOP POBuf;
			if ( (cbin <= sizeof(*POBuf)) )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			POBuf = PPORTOP(Irp->AssociatedIrp.SystemBuffer);
			ULONG DataCt = (cbin - sizeof(*POBuf)) / POBuf->Size;
			if ( DataCt <= 0 )
			{
				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}
			PVOID pData = POBuf+1; //The data begins at the end of the record.
			TBarData TarBar = pdx->BAR[biMain];
			switch ( POBuf->Size )
			{
				case 1:
					OutBarStr8(TarBar, POBuf->Address, (PUCHAR)pData, DataCt);
				break;
				case 2:
					OutBarStr16(TarBar, POBuf->Address, (PUSHORT)pData, DataCt);
				break;
				case 4:
					OutBarStr32(TarBar, POBuf->Address, (PULONG)pData, DataCt);
				break;
			}
			info = 0;

			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_PORT_OUTSTR

		case IOCTL_PIO_INSTR:				// code == 0x821
		{						// IOCTL_PIO_INSTR

			PPORTOP POBuf;
			if ( (cbin < sizeof(*POBuf)) )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			POBuf = PPORTOP(Irp->AssociatedIrp.SystemBuffer);
			ULONG DataCt = cbout / POBuf->Size;
			if ( DataCt <= 0 )
			{
				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}
			MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
			PVOID pData = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
			TBarData TarBar = pdx->BAR[biMain];
			switch ( POBuf->Size )
			{
				case 1:
					InBarStr8(TarBar, POBuf->Address, (PUCHAR)pData, DataCt);
				break;
				case 2:
					InBarStr16(TarBar, POBuf->Address, (PUSHORT)pData, DataCt);
				break;
				case 4:
					InBarStr32(TarBar, POBuf->Address, (PULONG)pData, DataCt);
				break;
			}
			info = DataCt * POBuf->Size;
			MmUnlockPages(Irp->MdlAddress);

			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_PIO_INSTR

		case IOCTL_PIO_OUTSTR:				// code == 0x823
		{						// IOCTL_PIO_OUTSTR

			PPORTOP POBuf;
			if ( (cbin < sizeof(*POBuf)) )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}
			POBuf = PPORTOP(Irp->AssociatedIrp.SystemBuffer);
			ULONG DataCt = cbout / POBuf->Size;
			if ( DataCt <= 0 )
			{
				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}
			MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
			PVOID pData = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
			TBarData TarBar = pdx->BAR[biMain];
			switch ( POBuf->Size )
			{
				case 1:
					OutBarStr8(TarBar, POBuf->Address, (PUCHAR)pData, DataCt);
				break;
				case 2:
					OutBarStr16(TarBar, POBuf->Address, (PUSHORT)pData, DataCt);
				break;
				case 4:
					OutBarStr32(TarBar, POBuf->Address, (PULONG)pData, DataCt);
				break;
			}
			info = 0;
			MmUnlockPages(Irp->MdlAddress);

			status = STATUS_SUCCESS;

		break;
		}						// IOCTL_PIO_OUTSTR

		case IOCTL_IRQ_WAIT_BEGIN:				// code == 0x810
		{						// IOCTL_IRQ_WAIT_BEGIN

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if (pdx->InterruptRequested)
			{
				status = STATUS_INVALID_DEVICE_REQUEST;
			}
			else if (pdx->InterruptOccurred)
			{
				pdx->InterruptOccurred = FALSE;
				if ( cbout )
				{
					InCOSData(pdx, Irp, cbout);
					info = cbout;
				}
				status = STATUS_SUCCESS;
			}
			else
			{
				KIRQL OldIRQL;

				pdx->COSSize = cbout;
				pdx->PendingIrp = Irp;
				pdx->HasPendingIrp = TRUE;
				IoMarkIrpPending(Irp);
				IoAcquireCancelSpinLock(&OldIRQL);
				IoSetCancelRoutine(Irp, IRQGenCancelIrp);
				IoReleaseCancelSpinLock(OldIRQL);
				pdx->InterruptRequested = TRUE;
				status = STATUS_PENDING;   // do not complete Irp of course
			}

		break;
		}						// IOCTL_IRQ_WAIT_BEGIN

		case IOCTL_IRQ_WAIT_ABORT:				// code == 0x812
		{

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if (!pdx->InterruptRequested)
				status = STATUS_INVALID_DEVICE_REQUEST;
			else
			{
				KIRQL OldIRQL;

				pdx->InterruptOccurred = FALSE;
				pdx->InterruptRequested = FALSE;
				pdx->HasPendingIrp = FALSE;
				pdx->PendingIrp->IoStatus.Status = STATUS_CANCELLED;
				pdx->PendingIrp->IoStatus.Information = 0;
				IoAcquireCancelSpinLock(&OldIRQL);
				IoSetCancelRoutine(pdx->PendingIrp, NULL);
				IoReleaseCancelSpinLock(OldIRQL);
				IoCompleteRequest(pdx->PendingIrp, IO_SERIAL_INCREMENT);
				status = STATUS_SUCCESS;
			}

		break;
		}

		case IOCTL_SET_COS_SCHEME:
		{
			if ( (cbin < sizeof(ULONG)) || (cbout < sizeof(ULONG)) )
			{
				status = STATUS_INVALID_BUFFER_SIZE;
				break;
			}

			ULONG COSScheme = *PULONG(Irp->AssociatedIrp.SystemBuffer);
			if ( (COSScheme & 0xF0808080) != 0x10000000 )
			{
				status = STATUS_NOT_IMPLEMENTED;
				break;
			}

			//In the future when new versions have been added, case on version here.

			ULONG PPIStartOff = COSScheme & 0x7F;
			ULONG PPISize = (COSScheme >> 8) & 0x7F;
			ULONG PPIStride = (COSScheme >> 16) & 0x7F;

			if ( PPISize == 0 )
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if (pdx->InterruptRequested)
			{
				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			pdx->COSScheme.StartOff = UCHAR(PPIStartOff);
			pdx->COSScheme.Size = UCHAR(PPISize);
			pdx->COSScheme.Stride = UCHAR(PPIStride);

			*PULONG(Irp->AssociatedIrp.SystemBuffer) = PPISize;
			info = sizeof(PPISize);
		break;
		}

		case IOCTL_FORGET_IRP:				//code == 0x880
		{
			if ( ! pdx->bRealDevice )
				status = STATUS_OBJECT_TYPE_MISMATCH;
			else if ( ! pdx->InterruptRequested )
				status = STATUS_NO_SUCH_FILE;
			else
			{
				pdx->InterruptRequested = FALSE;
				pdx->HasPendingIrp = FALSE;
				ForgetIRP(pdx->PendingIrp);
				pdx->PendingIrp = NULL;
				status = STATUS_SUCCESS;
			}

		break;
		}

		case IOCTL_IRQ_POLL:				// code == 0x814
		{

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			//if ( InterlockedExchange(PLONG(&(pdx->InterruptOccurred)), 0) )
			if ( pdx->InterruptOccurred )
			{
				pdx->InterruptOccurred = 0;
				status = STATUS_SUCCESS;
			}
			else
				status = STATUS_NO_MORE_FILES
			;

		break;
		}

		case IOCTL_DMA_PIONG_CONTROL:				// code == 0x840
		{

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if ( cbin < 1 )
			{
				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			if ( (pdx->DMAChannels == 0) || (pdx->DMAChannel[0].DMALink[0].pBuffer == NULL) )
			{
				status = STATUS_ORDINAL_NOT_FOUND;
				break;
			}

			status = DispatchDMAPiongControl(pdx, Irp, PUCHAR(Irp->AssociatedIrp.SystemBuffer), cbin, cbout, info);
		break;
		}

		case IOCTL_DMA_PIONG_READ:				// code == 0x840
		{

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if ( ! pdx->bDMAGoing )
			{
				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			ULONG iDMAChannel;
			if ( pdx->DMAChannels == 1 )
			{
				iDMAChannel = 0;
			}
			else if ( cbin < sizeof(iDMAChannel) )
			{
				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}
			else
			{
				iDMAChannel = *PULONG(Irp->AssociatedIrp.SystemBuffer);
				if ( iDMAChannel > pdx->DMAChannels )
				{
					status = STATUS_ORDINAL_NOT_FOUND;
					break;
				}
			}
			TPDMAChannelData pChan = &(pdx->DMAChannel[iDMAChannel]);

			if ( cbout < pChan->DMASizeUsed )
			{
				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			KIRQL OldIRQL;

			TPDMAUserRead pNewRead = TPDMAUserRead(ExAllocatePoolWithTag(NonPagedPool, sizeof(*pNewRead), 9));

			MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
			pNewRead->pBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
			pNewRead->BufferSize = pChan->DMASizeUsed; //Maybe someday this won't be 1:1, and we can use cbout here instead.
			pNewRead->pPendingIRP = Irp;
			pNewRead->pNext = NULL;

			IoMarkIrpPending(Irp);
			IoAcquireCancelSpinLock(&OldIRQL);
			//`@ IoSetCancelRoutine(Irp, `);
			IoReleaseCancelSpinLock(OldIRQL);
			status = STATUS_PENDING;

			KeRaiseIrql(pdx->MyDIRQL, &OldIRQL);
			if ( pChan->pLastRead )
				pChan->pLastRead->pNext = pNewRead;
			else
				pChan->pFirstRead = pNewRead
			;
			pChan->pLastRead = pNewRead;
			KeLowerIrql(OldIRQL);
		break;
		}

		case IOCTL_GET_BENCH:				// code == 0x830
		{

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			if (cbout < 4)
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			info = 4;
			*PULONG(Irp->AssociatedIrp.SystemBuffer) = pdx->Bench;
			pdx->Bench = 0;
			status = STATUS_SUCCESS;

		break;
		}

/*
		case IOCTL_`:				// code == 0x`
		{

			if ( ! pdx->bRealDevice )
			{
				status = STATUS_OBJECT_TYPE_MISMATCH;
				break;
			}

			`

		break;
		}
*/

		default:
		{
			DbgPrint("Unknown IOCTL = %08LX\n", code);

			LOCAL_UNICODE_STRING(DebugData, 256);
			UStrAddStr(DebugData, L"Unknown IOCTL = ");
			UStrAdd32(DebugData, code);
			WriteRegStr(UDebugKey, iLogEvent++, DebugData);

			status = STATUS_INVALID_DEVICE_REQUEST;
		break;
		}

	}						// by IOCTL code

	if ( pdx->bRealDevice ) IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
	if (status == STATUS_PENDING)
		return status;
	else
		return CompleteRequest(Irp, status, info)
	;
}							// DispatchControl

///////////////////////////////////////////////////////////////////////////////

#pragma PAGEDCODE

NTSTATUS DispatchDMAPiongControl(PDEVICE_EXTENSION pDX, PIRP pIRP, PUCHAR pCmd, ULONG InSize, ULONG OutSize, ULONG & DataOut)
{
	PAGED_CODE();
	InSize;
	pIRP;
	DataOut = 0;

	switch ( pCmd[0] )
	{
		case 0x02:
		{
			ULONG BufferSize = DMA_BUFFER_SIZE;
			if ( OutSize < sizeof(BufferSize) ) return STATUS_BUFFER_TOO_SMALL;

			*PULONG(pCmd) = BufferSize;
			DataOut = sizeof(BufferSize);
		break;
		}
		case 0x10:
		{
			BOOLEAN bWasStarted;
			TPDMAUserRead pCancelRead[MAX_DMA_CHANNELS] = { NULL };
			TPDMAUserRead pCancelFreeRead[MAX_DMA_CHANNELS] = { NULL };

			KIRQL OldIRQL;
			KeRaiseIrql(pDX->MyDIRQL, &OldIRQL);
			bWasStarted = pDX->bDMAGoing;
			if ( bWasStarted )
			{
				pDX->bDMAGoing = FALSE;
				for ( ULONG iDMAChannel = 0; iDMAChannel < pDX->DMAChannels; ++iDMAChannel )
				{
					TPDMAChannelData pChan = &(pDX->DMAChannel[iDMAChannel]);
					pCancelRead[iDMAChannel] = pChan->pFirstRead;
					pChan->pFirstRead = NULL;
					pChan->pLastRead = NULL;
					pCancelFreeRead[iDMAChannel] = pChan->pFirstFreeRead;
					pChan->pFirstFreeRead = NULL;
					pChan->pLastFreeRead = NULL;
				}
			}
			KeLowerIrql(OldIRQL);

			for ( ULONG iDMAChannel = 0; iDMAChannel < pDX->DMAChannels; ++iDMAChannel )
				DMAUserReadsFreeAndComplete(pCancelFreeRead[iDMAChannel], STATUS_TRANSLATION_COMPLETE) //I wish STATUS_SUCCESS worked…
			;
			for ( ULONG iDMAChannel = 0; iDMAChannel < pDX->DMAChannels; ++iDMAChannel )
				DMAUserReadsFreeAndComplete(pCancelRead[iDMAChannel], STATUS_CANCELLED)
			;

			if ( ! bWasStarted ) return STATUS_CONNECTION_INVALID;
		break;
		}
		case 0x11:
		{
			ULONG DMASizeUsed = 0;
			PULONG pDMASizeUsed = NULL;
			if ( pDX->CardType == ctDIO144 )
			{
				if ( InSize < sizeof(DMASizeUsed) ) return STATUS_BUFFER_TOO_SMALL;
				if ( InSize >= sizeof(*pDMASizeUsed) * pDX->DMAChannels )
					pDMASizeUsed = PULONG(pCmd+1);
				else
					DMASizeUsed = *PULONG(pCmd+1)
				;
			}
			else
			{
				DMASizeUsed = InBar32(pDX->BAR[biMain], 0x20) * BYTES_PER_DMA_XFER; // FIFO AF is in "conversion pairs (0,1)(2,3)..."
			}

			if ( pDMASizeUsed )
			{
				for ( ULONG iDMAChannel = 0; iDMAChannel < pDX->DMAChannels; ++iDMAChannel )
				{
					if ( ! pDMASizeUsed[iDMAChannel] ) return STATUS_BUFFER_TOO_SMALL;
					if ( pDMASizeUsed[iDMAChannel] > DMA_BUFFER_SIZE ) return STATUS_INVALID_BUFFER_SIZE;
				}
			}
			else
			{
				if ( ! DMASizeUsed ) return STATUS_BUFFER_TOO_SMALL;
				if ( DMASizeUsed > DMA_BUFFER_SIZE ) return STATUS_INVALID_BUFFER_SIZE;
			}

			NTSTATUS Status = STATUS_SUCCESS;

			LARGE_INTEGER SleepInterval;
			SleepInterval.QuadPart = 100*10000;
			KeDelayExecutionThread(KernelMode, FALSE, &SleepInterval);
			//KIRQL OldIRQL;
			//KeRaiseIrql(pDX->MyDIRQL, &OldIRQL);
			if ( pDX->bDMAGoing )
			{
				Status = STATUS_CONNECTION_ACTIVE;
			}
			else
			{
				for ( ULONG iDMAChannel = 0; iDMAChannel < pDX->DMAChannels; ++iDMAChannel )
				{
					TPDMAChannelData pChan = &(pDX->DMAChannel[iDMAChannel]);

					pChan->iDMALink = 0;
					pChan->bDMAWrote = FALSE;
					if ( pDMASizeUsed )
						pChan->DMASizeUsed = pDMASizeUsed[iDMAChannel];
					else
						pChan->DMASizeUsed = DMASizeUsed
					;
				}
				pDX->bDMAGoing = TRUE;
				if ( pDX->bDMAAheadOfTime )
					for ( ULONG iDMAChannel = 0; iDMAChannel < pDX->DMAChannels; ++iDMAChannel )
					{
						TPDMAChannelData pChan = &(pDX->DMAChannel[iDMAChannel]);
						TPDMALink pLink = &(pChan->DMALink[pChan->iDMALink]);

						OutBar32(pDX->BAR[biStatus], iDMAChannel*0x10 + 0x0, pLink->LogicalAddress.LowPart);
						OutBar32(pDX->BAR[biStatus], iDMAChannel*0x10 + 0x4, pLink->LogicalAddress.HighPart);
						OutBar32(pDX->BAR[biStatus], iDMAChannel*0x10 + 0x8, pChan->DMASizeUsed);
						OutBar32(pDX->BAR[biStatus], iDMAChannel*0x10 + 0xC, 0x4);

						pChan->bDMAWrote = TRUE;
					}
			}
			//KeLowerIrql(OldIRQL);
			KeDelayExecutionThread(KernelMode, FALSE, &SleepInterval);

			return Status;
		break;
		}
/*
		case 0x`:
		{
			if ( InSize < 2 ) return STATUS_BUFFER_TOO_SMALL;

			ULONG iDMALink = PUCHAR(Irp->AssociatedIrp.SystemBuffer)[1];
			if ( iDMALink >= pDX->DMALinkCt ) return STATUS_TOO_MANY_LINKS;

			TPDMAChannelData pChan = &(pDX->DMAChannel[iDMA / 2]);
			TPDMALink pLink = &(pChan->DMALink[iDMALink % 2]);
			if ( pLink->pBuffer == NULL ) return STATUS_ORDINAL_NOT_FOUND;

			`
		break;
		}
*/
		default: return STATUS_ORDINAL_NOT_FOUND;
	}

	return STATUS_SUCCESS;
}

/*********************************************************************************
*      CANCEL IRP
**********************************************************************************/
VOID
IRQGenCancelIrp(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
	PDEVICE_EXTENSION extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;

	Irp->IoStatus.Information = 0;

	if (Irp == extension->PendingIrp)
	{
		extension->PendingIrp = NULL;
		extension->HasPendingIrp = FALSE;
		extension->InterruptRequested = FALSE;
		Irp->IoStatus.Information = 1;
	}
	IoReleaseCancelSpinLock(Irp->CancelIrql);

	Irp->IoStatus.Status = STATUS_CANCELLED;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
}