// IO.cpp -- I/O abstraction and debug for AIOWDM driver

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

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

#pragma LOCKEDDATA

ULONG iLogEvent = 0;

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

#pragma PAGEDDATA

const WCHAR HexNybble[16] = { L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F' };

#pragma PAGEDCODE

void UStrAddStr(UNICODE_STRING & ToStr, PWCHAR pFrom)
{
	PAGED_CODE();

	while ( *pFrom && (ToStr.Length < ToStr.MaximumLength) )
	{
		ToStr.Buffer[ToStr.Length >> 1] = *pFrom;
		ToStr.Length += 2;
		++pFrom;
	}
}

#pragma PAGEDCODE

void UStrAddStr(UNICODE_STRING & ToStr, PCHAR pFrom)
{
	PAGED_CODE();

	while ( *pFrom && (ToStr.Length < ToStr.MaximumLength) )
	{
		ToStr.Buffer[ToStr.Length >> 1] = *pFrom;
		ToStr.Length += 2;
		++pFrom;
	}
}

#pragma PAGEDCODE

void UStrAdd8(UNICODE_STRING & ToStr, UCHAR From)
{
	PAGED_CODE();

	WCHAR InterBuf[3];

	//Load it backwards, for big-endian.
	InterBuf[2] = 0;
	InterBuf[1] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0] = HexNybble[From & 0xF];

	UStrAddStr(ToStr, InterBuf);
}

#pragma PAGEDCODE

void UStrAdd16(UNICODE_STRING & ToStr, USHORT From)
{
	PAGED_CODE();

	WCHAR InterBuf[5];

	//Load it backwards, for big-endian.
	InterBuf[4] = 0;
	InterBuf[3] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[2] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[1] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0] = HexNybble[From & 0xF];

	UStrAddStr(ToStr, InterBuf);
}

#pragma PAGEDCODE

void UStrAdd32(UNICODE_STRING & ToStr, ULONG From)
{
	PAGED_CODE();

	WCHAR InterBuf[9];

	//Load it backwards, for big-endian.
	InterBuf[8] = 0;
	InterBuf[7] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[6] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[5] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[4] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[3] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[2] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[1] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0] = HexNybble[From & 0xF];

	UStrAddStr(ToStr, InterBuf);
}

#pragma PAGEDCODE

void UStrAdd64(UNICODE_STRING & ToStr, ULONGLONG From)
{
	PAGED_CODE();

	WCHAR InterBuf[17];

	//Load it backwards, for big-endian.
	InterBuf[0x10] = 0;
	InterBuf[0xF] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0xE] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0xD] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0xC] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0xB] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0xA] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x9] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x8] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x7] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x6] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x5] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x4] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x3] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x2] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x1] = HexNybble[From & 0xF];
	From >>= 4;
	InterBuf[0x0] = HexNybble[From & 0xF];

	UStrAddStr(ToStr, InterBuf);
}

#pragma PAGEDCODE

void UStrAddN(UNICODE_STRING & ToStr, ULONGLONG From)
{
	PAGED_CODE();

	WCHAR InterBuf[17];
	PWCHAR pInterBuf = &InterBuf[0x10];

	//Load it backwards, for big-endian.
	*pInterBuf = 0;
	do
	{
		--pInterBuf;
		*pInterBuf = HexNybble[From & 0xF];
		From >>= 4;
	} while ( From );

	//Add only the loaded portion.
	UStrAddStr(ToStr, pInterBuf);
}

#pragma PAGEDCODE

void UStrAddPtr(UNICODE_STRING & ToStr, PVOID From)
{
	PAGED_CODE();

	#ifdef _WIN64
		UStrAdd64(ToStr, ULONGLONG(From));
	#else
		UStrAdd32(ToStr, ULONG(From));
	#endif
}

#pragma PAGEDCODE

void UStrAddAddr(UNICODE_STRING & ToStr, const TBarData & Bar, ULONG Offset)
{
	PAGED_CODE();

	switch ( Bar.BarType )
	{
		case btMemory:
			UStrAddStr(ToStr, L"Mem ");
		break;
		case btIO:
			UStrAddStr(ToStr, L"I/O ");
		break;
		case btMappedIO:
			UStrAddStr(ToStr, L"Mapped I/O ");
		break;
		default:
			UStrAdd8(ToStr, UCHAR(Bar.BarType));
			UStrAddStr(ToStr, L"T ");
		//break;
	}
	if ( Bar.BarType != btMemory )
		UStrAdd16(ToStr, USHORT(Bar.UnmappedBase.QuadPart + Offset));
	else
	{
		#ifdef _WIN64
			UStrAdd64(ToStr, ULONGLONG(Bar.UnmappedBase.QuadPart + Offset));
		#else
			UStrAdd32(ToStr, ULONG(Bar.UnmappedBase.QuadPart + Offset));
		#endif
	}
}

#pragma PAGEDCODE

void UStrNullTerminate(UNICODE_STRING & ToStr)
{
	PAGED_CODE();

	if ( ToStr.Length >= ToStr.MaximumLength )
		ToStr.Length = ToStr.MaximumLength-2
	;

	ToStr.Buffer[ToStr.Length] = 0;
}

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

/*
	WriteRegDWord(UDebugKey, L"StartOff", PPIStartOff);
	WriteRegDWord(UDebugKey, L"Size", PPISize);
	WriteRegDWord(UDebugKey, L"Stride", PPIStride);
	WriteRegDWord(UDebugKey, L"PortOp", sizeof(TPORTOP));
	WriteRegDWord(UDebugKey, L"MemOp", sizeof(TMEMOP));
*/
/*
	HANDLE Key = 0;
	OBJECT_ATTRIBUTES Attr;
	InitializeObjectAttributes(&Attr, &UDebugKey, 0, NULL, NULL);
	ZwCreateKey(&Key, KEY_CREATE_SUB_KEY, &Attr, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
	ZwClose(Key);
*/

#pragma PAGEDCODE

NTSTATUS WriteRegDWord(UNICODE_STRING & UKeyPath, UNICODE_STRING & UValueName, ULONG Data)
{
	PAGED_CODE();

	HANDLE Key;
	OBJECT_ATTRIBUTES Attr;
	NTSTATUS Status;

	InitializeObjectAttributes(&Attr, &UKeyPath, 0, NULL, NULL);
	Status = ZwOpenKey(&Key, KEY_SET_VALUE, &Attr);
	if ( ! NT_SUCCESS(Status) ) return Status;

	Status = ZwSetValueKey(Key, &UValueName, 0, REG_DWORD, &Data, sizeof(Data));

	ZwClose(Key);

	return Status;
}

#pragma PAGEDCODE

NTSTATUS WriteRegDWord(UNICODE_STRING & UKeyPath, PWCHAR ValueName, ULONG Data)
{
	PAGED_CODE();

	UNICODE_STRING UValueName;
	RtlInitUnicodeString(&UValueName, ValueName);
	return WriteRegDWord(UKeyPath, UValueName, Data);
}

#pragma PAGEDCODE

NTSTATUS ReadRegDWord(UNICODE_STRING & UKeyPath, UNICODE_STRING & UValueName, ULONG & Data)
{
	PAGED_CODE();

	HANDLE Key;
	OBJECT_ATTRIBUTES Attr;
	NTSTATUS Status;

	ULONG Size = 16;
	PKEY_VALUE_PARTIAL_INFORMATION pKVPI = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePoolWithTag(PagedPool, Size, 7);
	if ( pKVPI == NULL ) return STATUS_MEMORY_NOT_ALLOCATED;

	InitializeObjectAttributes(&Attr, &UKeyPath, 0, NULL, NULL);
	Status = ZwOpenKey(&Key, KEY_QUERY_VALUE, &Attr);
	if ( ! NT_SUCCESS(Status) ) return Status;

	Status = ZwQueryValueKey(Key, &UValueName, KeyValuePartialInformation, pKVPI, Size, &Size);
	if ( NT_SUCCESS(Status) ) Data = *PULONG(pKVPI->Data);
	ExFreePool(pKVPI);

	ZwClose(Key);

	return Status;
}

#pragma PAGEDCODE

NTSTATUS ReadRegDWord(UNICODE_STRING & UKeyPath, PWCHAR ValueName, ULONG & Data)
{
	PAGED_CODE();

	UNICODE_STRING UValueName;
	RtlInitUnicodeString(&UValueName, ValueName);
	return ReadRegDWord(UKeyPath, UValueName, Data);
}

#pragma PAGEDCODE

NTSTATUS WriteRegStr(UNICODE_STRING & UKeyPath, UNICODE_STRING & UValueName, UNICODE_STRING & UValueData)
{
	PAGED_CODE();

	HANDLE Key;
	OBJECT_ATTRIBUTES Attr;
	NTSTATUS Status;

	InitializeObjectAttributes(&Attr, &UKeyPath, 0, NULL, NULL);
	Status = ZwOpenKey(&Key, KEY_SET_VALUE, &Attr);
	if (!NT_SUCCESS(Status))
		return Status;

	Status = ZwSetValueKey(Key,  &UValueName, 0, REG_SZ, UValueData.Buffer, UValueData.Length);

	ZwClose(Key);

	return Status;
}

#pragma PAGEDCODE

NTSTATUS WriteRegStr(UNICODE_STRING & UKeyPath, PWCHAR ValueName, UNICODE_STRING & UValueData)
{
	PAGED_CODE();

	UNICODE_STRING UValueName;
	RtlInitUnicodeString(&UValueName, ValueName);
	return WriteRegStr(UKeyPath, UValueName, UValueData);
}

#pragma PAGEDCODE

NTSTATUS WriteRegStr(UNICODE_STRING & UKeyPath, UNICODE_STRING & UValueName, PWCHAR ValueData)
{
	PAGED_CODE();

	UNICODE_STRING UValueData;
	RtlInitUnicodeString(&UValueData, ValueData);
	return WriteRegStr(UKeyPath, UValueName, UValueData);
}

#pragma PAGEDCODE

NTSTATUS WriteRegStr(UNICODE_STRING & UKeyPath, PWCHAR ValueName, PWCHAR ValueData)
{
	PAGED_CODE();

	UNICODE_STRING UValueName, UValueData;
	RtlInitUnicodeString(&UValueName, ValueName);
	RtlInitUnicodeString(&UValueData, ValueData);
	return WriteRegStr(UKeyPath, UValueName, UValueData);
}

#pragma PAGEDCODE

NTSTATUS WriteRegStr(UNICODE_STRING & UKeyPath, ULONG ValueNum, UNICODE_STRING & UValueData)
{
	PAGED_CODE();

	LOCAL_UNICODE_STRING(UValueName, 8);
	UStrAdd32(UValueName, ValueNum);
	return WriteRegStr(UKeyPath, UValueName, UValueData);
}

#pragma PAGEDCODE

NTSTATUS WriteRegStr(UNICODE_STRING & UKeyPath, ULONG ValueNum, PWCHAR ValueData)
{
	PAGED_CODE();

	LOCAL_UNICODE_STRING(UValueName, 8);
	UStrAdd32(UValueName, ValueNum);
	UNICODE_STRING UValueData;
	RtlInitUnicodeString(&UValueData, ValueData);
	return WriteRegStr(UKeyPath, UValueName, UValueData);
}

#pragma PAGEDCODE

NTSTATUS WriteRegBinary(UNICODE_STRING & UKeyPath, UNICODE_STRING & UValueName, unsigned long Len, PVOID pValueData)
{
	PAGED_CODE();

	HANDLE Key;
	OBJECT_ATTRIBUTES Attr;
	NTSTATUS Status;

	InitializeObjectAttributes(&Attr, &UKeyPath, 0, NULL, NULL);
	Status = ZwOpenKey(&Key, KEY_SET_VALUE, &Attr);
	if ( ! NT_SUCCESS(Status) )
		return Status
	;

	Status = ZwSetValueKey(Key, &UValueName, 0, REG_BINARY, pValueData, Len);

	ZwClose(Key);

	return Status;
}

#pragma PAGEDCODE

NTSTATUS WriteRegBinary(UNICODE_STRING & UKeyPath, PWCHAR ValueName, unsigned long Len, PVOID pValueData)
{
	PAGED_CODE();

	UNICODE_STRING UValueName;
	RtlInitUnicodeString(&UValueName, ValueName);
	return WriteRegBinary(UKeyPath, UValueName, Len, pValueData);
}

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

#pragma LOCKEDCODE

VOID DoIOAction(PDEVICE_EXTENSION pdx, IOACTION Action)
{
	TPBarData pBar;
	switch ( Action.BarID )
	{
		case biMain:
			pBar = &pdx->BAR[biMain];
		break;
		case biStatus:
			pBar = &pdx->BAR[biStatus];
		break;
		case biAux:
			pBar = &pdx->BAR[biAux];
		break;
/*		bar-the-nth pulled J2H 2021-01-20
		case bar0: case bar1: case bar2: case bar3: case bar4: case bar5:
			pBar = &pdx->BAR[(Action.BarID)];
		break;
*/
		default:
			return;
		break;
	}

	switch ( Action.Operation ) // convert to case on the Operations enum...
	{
		case opIRQCLR_ADIO: // hardcoded mPCIe-ADIO Clear IRQ
			OutBar32(pdx->BAR[biMain], 0x40, InBar32(pdx->BAR[biMain], 0x40));
		break;

		case opIRQEN_ADIO: // hardcoded mPCIe-ADIO Enable IRQ
			//OutBar32(pdx->BAR[biMain], 0x40, 0x00000010L); // A/D FAF IRQ
			//OutBar32(pdx->BAR[biMain], 0x40, 0x00000200L); // D/A FAE IRQ
			OutBar32(pdx->BAR[biMain], 0x40, pdx->ADIOEnableType);
		break;

		case opIRQDIS_ADIO: // hardcoded mPCIe-ADIO Disable IRQ
			pdx->ADIOEnableType = InBar32(pdx->BAR[biMain], 0x40) & 0xFFFF;
			OutBar32(pdx->BAR[biMain], 0x40, 0x00000000L);
		break;

		case opINBYTE:
			InBar8(*pBar, Action.Offset);
		break;

		case opOUTBYTE:
			OutBar8(*pBar, Action.Offset, Action.Data);
		break;

		case opECHO: // opECHO: read then write what was read to the same place; calculates size like # does
			switch(Action.Data)
			{
				case 4: OutBar32(*pBar, Action.Offset, InBar32(*pBar, Action.Offset));
				break;
				case 2: OutBar16(*pBar, Action.Offset, InBar16(*pBar, Action.Offset));
				break;
				case 1: OutBar8(*pBar, Action.Offset, InBar8(*pBar, Action.Offset));
				break;
			}
		break;

		case opOUTFFS:
		{
			ULONG Offset = Action.Offset;
			UCHAR LenLeft = Action.Data;
			while ( LenLeft > 0 )
			{
				if ( LenLeft >= 4 )
				{
					OutBar32(*pBar, Offset, 0xFFFFFFFF);
					Offset += 4;
					LenLeft -= 4;
				}
				else if ( LenLeft >= 2 )
				{
					OutBar16(*pBar, Offset, 0xFFFF);
					Offset += 2;
					LenLeft -= 2;
				}
				else if ( LenLeft >= 1 )
				{
					OutBar8(*pBar, Offset, 0xFF);
					Offset += 1;
					LenLeft -= 1;
				}
			}
		}
		break;
	}
}

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

#pragma LOCKEDCODE
/* read on-card register to determine if the card is trying to generate an IRQ */
/* the HMAIO and ADIO hardcode FAF IRQ as the only valid source*/
UCHAR QueryIAm(PDEVICE_EXTENSION pdx, IOACTION Action)
{
	UCHAR Result = 0xFF;
	TPBarData pBar;
	switch ( Action.BarID )
	{
		case biMain:
			pBar = &pdx->BAR[biMain];
		break;
		case biStatus:
			pBar = &pdx->BAR[biStatus];
		break;
		case biAux:
			pBar = &pdx->BAR[biAux];
		break;
		default:
			return Result;
		break;
	}

	if ( Action.Operation == opIRQCLR_ADIO ) return (InBar32(pdx->BAR[biMain], 0x40) & 0xFFFF0000L)?1:0;

	if ( Action.Operation != opINBYTE ) return Result;

	Result = InBar8(*pBar, Action.Offset) & Action.Data;

	return Result;
}

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

#if DBG
#pragma LOCKEDCODE

void ReadOffIOAction(PCHAR ActionName, IOACTION & Action)
{
	PCHAR WhichBase;
	switch ( Action.BarID )
	{
		case biStatus:
			WhichBase = "status";
		break;
		case biMain:
			WhichBase = "main";
		break;
		case biAux:
			WhichBase = "aux";
		break;
		default:
			WhichBase = "???";
		//break;
	}
	switch ( Action.Operation )
	{
		case '_':
			KdPrint((DRIVERNAME " - %s = hardcoded ClearIRQfor mPCIe-ADIO\n", ActionName));
		break;
		case '-':
			KdPrint((DRIVERNAME " - %s = hardcoded DisableIRQfor mPCIe-ADIO\n", ActionName));
		break;
		case '+':
			KdPrint((DRIVERNAME " - %s = hardcoded EnableIRQfor mPCIe-ADIO\n", ActionName));
		break;

		case 'R':
			KdPrint((DRIVERNAME " - %s = read from %s base+%LX\n", ActionName, WhichBase, 0L + Action.Offset));
		break;
		case 'W':
			KdPrint((DRIVERNAME " - %s = write %02LX to %s base+%LX\n", ActionName, WhichBase, 0L + Action.Data, 0L + Action.Offset));
		break;
		case '#':
			KdPrint((DRIVERNAME " - %s = write FF to %02LX bytes starting at %s base+%LX\n", ActionName, WhichBase, 0L + Action.Data, 0L + Action.Offset));
		break;
		default:
			KdPrint((DRIVERNAME " - %s = nothing\n", ActionName));
		//break;
	}
}
#else //DBG
	#define ReadOffIOAction(Base, Action)
#endif //DBG

#if DBG
	#define bExplainIOActions 1
#else //DBG
	#define bExplainIOActions 0
#endif //DBG

#if bExplainIOActions
#pragma PAGEDCODE

void ExplainIOAction(UNICODE_STRING & ToStr, IOACTION & Action, BOOLEAN bIsIAm)
{
	PAGED_CODE();

	if ( Action.Operation == 'N' )
	{
		UStrAddStr(ToStr, L"Nothing/Assume");
		return;
	}

	switch ( Action.BarID )
	{
		case biMain:
			UStrAddStr(ToStr, L"Main");
		break;
		case biStatus:
			UStrAddStr(ToStr, L"Status");
		break;
		case biAux:
			UStrAddStr(ToStr, L"Aux");
			break;
		case biMemMain:
			UStrAddStr(ToStr, L"MemMain");
			break;
		default:
			UStrAddStr(ToStr, L"?");
			UStrAdd8(ToStr, (UCHAR)Action.BarID);
			UStrAddStr(ToStr, L"?");
		break;
	}

	UStrAddStr(ToStr, L" Base+");
	UStrAddN(ToStr, ULONGLONG(Action.Offset));

	if ( Action.Operation == '#' )
	{
		UStrAddStr(ToStr, L" << FF × ");
		UStrAddN(ToStr, ULONGLONG(Action.Data));
		return;
	}

	switch ( Action.Operation )
	{
		case 'R':
		{
			UStrAddStr(ToStr, L" >> ");
			if ( bIsIAm )
			{
				UStrAddStr(ToStr, L"& ");
			}
			else
			{
				for ( int I = 2*Action.Data; I > 0; --I )
					UStrAddStr(ToStr, L"X")
				;
				return;
			}
		}
		break;
		case 'W':
			UStrAddStr(ToStr, L" << ");
		break;
		default:
			UStrAddStr(ToStr, L" ?");
			UStrAdd8(ToStr, UCHAR(Action.Operation));
			UStrAddStr(ToStr, L"? ");
		break;
	}

	UStrAdd8(ToStr, UCHAR(Action.Data));
	/*
	switch ( Action.Size )
	{
		case 1:
			UStrAdd8(ToStr, UCHAR(Action.Data));
		break;
		case 2:
			UStrAdd16(ToStr, USHORT(Action.Data));
		break;
		case 4:
			UStrAdd32(ToStr, ULONG(Action.Data));
		break;
	}
	*/
}
#else //bExplainIOActions
	#define ExplainIOAction(ToStr, Action, bIsIAm)
#endif //bExplainIOActions

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

#pragma LOCKEDCODE

VOID InCOSData(PDEVICE_EXTENSION pdx, PIRP Irp, ULONG COSSize)
{
	LONG PPISize = pdx->COSScheme.Size;
	ULONG Offset = pdx->COSScheme.StartOff;
	PUCHAR pDst = PUCHAR(Irp->AssociatedIrp.SystemBuffer);
	ULONG dStride = pdx->COSScheme.Stride - pdx->COSScheme.Size;
	while ( COSSize-- )
	{
		if ( PPISize == 0 )
		{
			PPISize = pdx->COSScheme.Size;
			Offset += dStride;
		}
		--PPISize;

		*(pDst++) = InBar8(pdx->BAR[biMain], Offset++);
	}
}

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

#pragma PAGEDCODE

NTSTATUS GetDIDFor(PDEVICE_EXTENSION pdx)
{
	PAGED_CODE();

	WCHAR HID[256];
	ULONG Size;
	NTSTATUS Status = IoGetDeviceProperty(pdx->Pdo, DevicePropertyHardwareID, sizeof(HID), HID, &Size);
	if (!NT_SUCCESS(Status))
		return Status
	;

	enum DevSearchState {FindingD, FindingE, FindingV, Finding_, FindingDone}
		State = FindingD;

	ULONG I;
	for (I = 0; I < Size && State != FindingDone; ++I)
		switch(HID[I])
		{
		case 'D': case 'd': if (State == FindingD) State = FindingE;    else State = FindingD; break;
		case 'E': case 'e': if (State == FindingE) State = FindingV;    else State = FindingD; break;
		case 'V': case 'v': if (State == FindingV) State = Finding_;    else State = FindingD; break;
		case '_':           if (State == Finding_) State = FindingDone; else State = FindingD; break;

		default:
			State = FindingD;
			break;
		}

	//Add ISA virtual device ID code here

	if (State != FindingDone)
		return STATUS_OBJECT_NAME_INVALID;

	pdx->DeviceID = 0;
	{for (int J = 0; J < 4; ++J, ++I)
	{
		switch(HID[I])
		{
		case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
			pdx->DeviceID = pdx->DeviceID * 0x10 + (HID[I] - '0');
			break;
		case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
			pdx->DeviceID = pdx->DeviceID * 0x10 + (HID[I] - 'A' + 10);
			break;
		case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
			pdx->DeviceID = pdx->DeviceID * 0x10 + (HID[I] - 'a' + 10);
			break;
		}
	}
	}

	return STATUS_SUCCESS;
}

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

/******************************************************************************
GetClearIOFor retrieves from the registry the I/O operations necessary to
disable, clear, and enable IRQs. The registry entry is as follows:

	"[IAm]|[Disable]|[Clear]|[Enable]|[Optional Comments]"

[IAm] is empty or as follows:

	"[Offset]&[Mask]"

If [IAm] is nonempty, the card has an IRQ status register at base + [Offset].
If any of the bits in [Mask] are set in the register, the card is generating an
IRQ.

If [IAm] is empty AIOWDM determines the IAm nature in a fixed manner.

[Disable], [Clear], and [Enable] are each empty or match one of these patterns:

	"[Offset]<[Data]"         (write)
	"[Offset]>"               (read)
	"[Offset]#[Bytes]"        (write [Bytes] number of 0xFF bytes to sequential registers
	"[Offset]H[Size]          (Read 4, 2, or 1 byte from Offset, then write the read
	                           value back to Offset)

If a 'Disable', 'Clear', or 'Enable' field is empty, there is no such action.

[Offset] is defined as follows:

	"[barID][Data]"

[barID] is 'S', for Status BAR; 'M', for Main BAR; 'U', for Aux BAR;
If [barID] is omitted MainBAR is assumed.

[Mask], [Data], and [Bytes] are defined as follows:

	HEX nybbles, upper and lower-case supported, with neither leading 0x nor $ et al.

[Mask] and [Data] currently only support a single byte i.e. two nybbles.

[Bytes] is a valid hexadecimal number less than ((the size of the BAR)-[Offset[+1).

[Size] is '4' for a 4-byte operation, '2' for a 2-byte operation, and '1' for a
single byte operation.

[Clear] will almost always be nonempty(though on some cards the act of
disabling and reenabling IRQs clears it). The card's IRQ is cleared by
performing the specified action(writing [Data] or reading) at base + [Offset].

[Disable] and [Enable] will almost always either both be present (or both be
empty). If present, the specified action will disable or enable IRQs.

----
Example: A card has registers as follows:

	IRQ status register = base + 8, least significant bit indicates an IRQ
	Disable             = write FF to base + B
	Clear               = write anything to base + F
	Enable              = write 00 to base + B

Its registry entry might be:

	"8&01|B<FF|F<|B<00"

Another that would work:

	"8&1|B<FFFF|F<B9|B<"

Notes:
	High zeroes are ignored, thus 1 = 01.
	High nybbles are ignored, thus FFFF = FF.
	This card doesn't care about the data written to clear, thus B9 is as good
	as empty (i.e. 00).
	The default data is zero, thus empty = 00.
	You can put a pipe("|") after Enable, and put comments after that; AIOWDM
	will ignore anything after this pipe.

// consider adding additional fields after the Enable |

******************************************************************************/

#pragma PAGEDCODE

NTSTATUS GetClearIOFor(PDEVICE_EXTENSION pdx)
{
	PAGED_CODE();

	pdx->IAm = BlankIOAction;
	pdx->Disable = BlankIOAction;
	pdx->Clear = BlankIOAction;
	pdx->Enable = BlankIOAction;



/* Pseudocode (actually the Borland equivalent)
	AnsiString Content;
	PRegistry Reg = new TRegistry;
	Reg->Access = KEY_QUERY_VALUE;
	Content = Reg->ReadString(IntToHex(DeviceID, 4));
	Reg->Free();
*/
	PKEY_VALUE_PARTIAL_INFORMATION pKVPI;
	{
		HANDLE Key;
		OBJECT_ATTRIBUTES Attr;
		NTSTATUS status;

		InitializeObjectAttributes(&Attr, &servkey, 0, NULL, NULL);
		status = ZwOpenKey(&Key, KEY_QUERY_VALUE, &Attr);
		if ( ! NT_SUCCESS(status) )
			return status;

		UNICODE_STRING Value;
		char Buf[5];
		WCHAR LBuf[5];
		sprintf(Buf, "%04X", pdx->DeviceID);
		{for ( int I = 0; I < 5; ++I )
			LBuf[I] = Buf[I]
		;}
		RtlInitUnicodeString(&Value, LBuf);
		ULONG Size;
		status = ZwQueryValueKey(Key, &Value, KeyValuePartialInformation, NULL, 0, &Size);
		//We expect STATUS_BUFFER_TOO_SMALL on NT2K or STATUS_BUFFER_OVERFLOW on 98
		//we passed a zero-size buffer to get the needed buffer size
		if ( (status != STATUS_BUFFER_TOO_SMALL) && (status != STATUS_BUFFER_OVERFLOW) )
		{
			ZwClose(Key);
			return status;
		}
		pKVPI = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePoolWithTag(PagedPool, Size + 1, 7);
		if ( ! pKVPI )
		{
			ZwClose(Key);
			return STATUS_NO_MEMORY;
		}

		status = ZwQueryValueKey(Key, &Value, KeyValuePartialInformation, pKVPI, Size, &Size);

		ZwClose(Key);

		if ( ! NT_SUCCESS(status) )
		{
			ExFreePool(pKVPI);
			return status;
		}
	}
/**/



	WCHAR *IOData = (PWCHAR)pKVPI->Data;
	IOData[pKVPI->DataLength / 2 - 1] = '|';

	enum ClearIOReadState {ReadingIAm, ReadingDisable, ReadingClear, ReadingEnable}
		State = ReadingIAm;
	BOOLEAN Done = FALSE;
	PIOACTION Parent = &(pdx->IAm);
	PUCHAR Subject = &(Parent->Offset);
	ULONG NybbleCt = 0;
	while (!Done)
	{
		switch(*IOData)
		{
			case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
				*Subject = *Subject * 0x10 + UCHAR(*IOData - '0');
				++NybbleCt;
			break;
			case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
				*Subject = *Subject * 0x10 + UCHAR(*IOData - ('A' - 10));
				++NybbleCt;
			break;
			case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
				*Subject = *Subject * 0x10 + UCHAR(*IOData - ('a' - 10));
				++NybbleCt;
			break;
			case '_': // placeholder for special-cased 32-bit hardcodes for mPCIe-ADIO
				{
					switch(State)
					{
						case ReadingIAm:
							Parent->Operation = opIRQCLR_ADIO; // J2H 2021-01-19 mPCIe-ADIO "IAm" uses '_', the value of opIRQClr_ADIO, for some reason
							while(*(++IOData) != '|') ; // hardcoded: doesn't need data
							continue;
						break;
						case ReadingDisable:
							Parent->Operation = opIRQDIS_ADIO;
							while(*(++IOData) != '|') ; // hardcoded: doesn't need data
							continue;
						break;
						case ReadingClear:
							Parent->Operation = opIRQCLR_ADIO;
							while(*(++IOData) != '|') ; // hardcoded: doesn't need data
							continue;
						break;
						case ReadingEnable:
							Parent->Operation = opIRQEN_ADIO;
							while(*(++IOData) != '|') ; // hardcoded: doesn't need data
							continue;
						break;
					}
				}
			break;
			case '<': //IAm doesn't use a write
				if (State != ReadingIAm)
				{
					Parent->Operation = opOUTBYTE;
					Subject = &(Parent->Data);
					NybbleCt = 0;
				}
			break;
			case '>': //IAm doesn't use a standard read
				if (State != ReadingIAm)
				{
					Parent->Operation = opINBYTE;
					while(*(++IOData) != '|') ; //Enable/disable read has no data section
					continue;
				}
			break;
			case '&': //Only IAm uses a read with a mask
				if (State == ReadingIAm)
				{
					Parent->Operation = opINBYTE;
					Subject = &(Parent->Data);
					NybbleCt = 0;
				}
			break;
			case '#': //IAm doesn't use a write
				if (State != ReadingIAm)
				{
					Parent->Operation = opOUTFFS;
					Subject = &(Parent->Data); //Data section is actually size.
					NybbleCt = 0;
				}
			break;
			case 'H': //Echo takes size in data section
				if (State != ReadingIAm)
				{
					Parent->Operation = opECHO;
					Subject = &(Parent->Data); //Data section is actually size.
					NybbleCt = 0;
				}
			break;
			case '|':
				switch(State)
				{
					case ReadingIAm:
						State = ReadingDisable;
						Parent = &(pdx->Disable);
						Subject = &(Parent->Offset);
					break;
					case ReadingDisable:
						State = ReadingClear;
						Parent = &(pdx->Clear);
						Subject = &(Parent->Offset);
					break;
					case ReadingClear:
						State = ReadingEnable;
						Parent = &(pdx->Enable);
						Subject = &(Parent->Offset);
					break;
					case ReadingEnable:
						Done = TRUE;
					break;
				}
			break;

			case 'S':
				if ( (Subject == &(Parent->Offset)) && (*Subject == 0) )
				{
					Parent->BarID = biStatus;
				}
			break;
			case 'M':
				if ( (Subject == &(Parent->Offset)) && (*Subject == 0) )
				{
					Parent->BarID = biMain;
				}
			break;

			// J2H 2021-01-20 added to support script-access to AUX bar
			case 'U':
				if ( (Subject == &(Parent->Offset)) && (*Subject == 0) )
				{
					Parent->BarID = biAux;
				}
			break;
/* J2H pulled 2021-01-20
			// J2H 2021-01-20 added to support script-access to arbitrary BAR[n]
			case 'o': case 'p': case 'q': case 'r': case 's': case 't':
				if ( (Subject == &(Parent->Offset)) && (*Subject == 0) )
								{
									Parent->BarID = *IOData - 'o';
				}
				break;
*/

		}
		++IOData;
	}

	ExFreePool(pKVPI);

	#if bExplainIOActions
		LOCAL_UNICODE_STRING(DebugName, 256);
		LOCAL_UNICODE_STRING(DebugData, 256);
		USHORT PrefixLen;

		UStrAdd32(DebugName, iLogEvent++);
		PrefixLen = DebugName.Length;

		DebugName.Length = PrefixLen;
		UStrAddStr(DebugName, L"-IAm");
		DebugData.Length = 0;
		ExplainIOAction(DebugData, pdx->IAm, TRUE);
		WriteRegStr(UDebugKey, DebugName, DebugData);

		DebugName.Length = PrefixLen;
		UStrAddStr(DebugName, L"-Disable");
		DebugData.Length = 0;
		ExplainIOAction(DebugData, pdx->Disable, FALSE);
		WriteRegStr(UDebugKey, DebugName, DebugData);

		DebugName.Length = PrefixLen;
		UStrAddStr(DebugName, L"-Clear");
		DebugData.Length = 0;
		ExplainIOAction(DebugData, pdx->Clear, FALSE);
		WriteRegStr(UDebugKey, DebugName, DebugData);

		DebugName.Length = PrefixLen;
		UStrAddStr(DebugName, L"-Enable");
		DebugData.Length = 0;
		ExplainIOAction(DebugData, pdx->Enable, FALSE);
		WriteRegStr(UDebugKey, DebugName, DebugData);
	#endif //bExplainIOActions

	#if DBG
		if ( pdx->IAm.Operation == 'R' )
			KdPrint(("IAm = read from base+%X ANDed with $%02X\n", pdx->IAm.Offset, pdx->IAm.Data));
		else if ( pdx->IAm.Operation == '_' )
			KdPrint(("IAm = hardcoded for mPCIe-ADIO\n"));
		else
			KdPrint(("IAm = assume\n"))
		;
		switch ( pdx->Disable.Operation )
		{
			case '_':
				KdPrint(("Disable = hardcoded for mPCIe-ADIO\n"));
			break;
			case 'R':
				KdPrint(("Disable = read from base+%X\n", pdx->Disable.Offset));
			break;
			case 'W':
				KdPrint(("Disable = write %02X to base+%X\n", pdx->Disable.Data, pdx->Disable.Offset));
			break;
			case 'N':
				KdPrint(("Disable = nothing\n"));
			break;
		}
		switch ( pdx->Clear.Operation )
		{
			case '_':
				KdPrint(("Clear = hardcoded for mPCIe-ADIO\n"));
			break;
			case 'R':
				KdPrint(("Clear = read from base+%X\n", pdx->Clear.Offset));
			break;
			case 'W':
				KdPrint(("Clear = write %02X to base+%X\n", pdx->Clear.Data, pdx->Clear.Offset));
			break;
			case '#':
				KdPrint(("Clear = write FF to %02X bytes, starting at base+%X\n", pdx->Clear.Data, pdx->Clear.Offset));
			break;
			case 'N':
				KdPrint(("Clear = nothing\n"));
			break;
		}
		switch ( pdx->Enable.Operation )
		{
			case '_':
				KdPrint(("Enable = hardcoded for mPCIe-ADIO\n"));
			break;
			case 'R':
				KdPrint(("Enable = read from base+%X\n", pdx->Enable.Offset));
			break;
			case 'W':
				KdPrint(("Enable = write %02X to base+%X\n", pdx->Enable.Data, pdx->Enable.Offset));
			break;
			case 'N':
				KdPrint(("Enable = nothing\n"));
			break;
		}
	#endif //DBG

	return STATUS_SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////
//BAR management

#if DBG
	#define bLogBarFromResource 1
#else //DBG
	#define bLogBarFromResource 0
#endif //DBG

#pragma PAGEDCODE

TBarData BarFromResource(CM_PARTIAL_RESOURCE_DESCRIPTOR & Resource)
{
	PAGED_CODE();

	TBarData Result;
	RtlZeroMemory(&Result, sizeof(Result));

	#if bLogBarFromResource
		LOCAL_UNICODE_STRING(DebugName, 256);
		LOCAL_UNICODE_STRING(DebugData, 256);
		USHORT PrefixLen;

		UStrAdd32(DebugName, iLogEvent++);
		PrefixLen = DebugName.Length;
	#endif //bLogBarFromResource

	switch ( Resource.Type )
	{
		case CmResourceTypePort:
			Result.Size = Resource.u.Port.Length;
			if ( Resource.Flags & CM_RESOURCE_PORT_IO )
			{
				Result.BarType = btIO;
				Result.UnmappedBase = Resource.u.Port.Start;

				#if bLogBarFromResource
					DebugName.Length = PrefixLen;
					UStrAddStr(DebugName, L"-A: I/O");
					WriteRegDWord(UDebugKey, DebugName, ULONG(Result.UnmappedBase.QuadPart));
				#endif //bLogBarFromResource
			}
			else
			{
				Result.BarType = btMappedIO;
				Result.UnmappedBase = Resource.u.Port.Start;

				#if bLogBarFromResource
					DebugName.Length = PrefixLen;
					UStrAddStr(DebugName, L"-A: Mapped I/O");
					WriteRegDWord(UDebugKey, DebugName, ULONG(Result.UnmappedBase.QuadPart));
				#endif //bLogBarFromResource
			}
		break;
		case CmResourceTypeMemory:
			Result.BarType = btMemory;
			Result.Size = Resource.u.Memory.Length;
			Result.UnmappedBase = Resource.u.Memory.Start;

			#if bLogBarFromResource
				DebugName.Length = PrefixLen;
				UStrAddStr(DebugName, L"-A: Memory");
				WriteRegDWord(UDebugKey, DebugName, ULONG(Result.UnmappedBase.QuadPart));
			#endif //bLogBarFromResource
		break;
		default:
			#if bLogBarFromResource
				DebugName.Length = PrefixLen;
				UStrAddStr(DebugName, L"-A: Nothing");
				WriteRegDWord(UDebugKey, DebugName, 0);
			#endif //bLogBarFromResource

			return Result;
		//break;
	}
	if ( Result.BarType == btIO )
		Result.Base = PUCHAR(Result.UnmappedBase.QuadPart);
	else
		Result.Base = PUCHAR(MmMapIoSpace(Result.UnmappedBase, Result.Size, MmNonCached))
	;

	#if bLogBarFromResource
		DebugName.Length = PrefixLen;
		UStrAddStr(DebugName, L"-B: Final Base");
//		WriteRegDWord(UDebugKey, DebugName, (ULONG)(Result.Base));
	#endif //bLogBarFromResource

	return Result;
}

#pragma PAGEDCODE

void DelBar(TBarData & Bar)
{
	PAGED_CODE();

	if ( Bar.Base ) switch ( Bar.BarType )
	{
		case btMemory: case btMappedIO: MmUnmapIoSpace(Bar.Base, Bar.Size);
	}
	RtlZeroMemory(&Bar, sizeof(Bar));
}

///////////////////////////////////////////////////////////////////////////////
//BAR use

#if DBG
	#define bLogBarAccess 1
#else //DBG
	#define bLogBarAccess 0
#endif //DBG

#if bLogBarAccess
#pragma LOCKEDCODE

void LogBarAccess(const TBarData & Bar, ULONG Offset, ULONG Data, BOOLEAN bRead, UCHAR Size)
{
	if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) return;

	LOCAL_UNICODE_STRING(DebugName, 256);
	LOCAL_UNICODE_STRING(DebugData, 256);

	UStrAdd32(DebugName, iLogEvent++);

	UStrAddAddr(DebugData, Bar, Offset);
	if ( bRead )
		UStrAddStr(DebugData, L" >> ");
	else
		UStrAddStr(DebugData, L" << ")
	;
	switch ( Size )
	{
		case 1:
			UStrAdd8(DebugData, UCHAR(Data));
		break;
		case 2:
			UStrAdd16(DebugData, USHORT(Data));
		break;
		case 4:
			UStrAdd32(DebugData, ULONG(Data));
		break;
	}
	WriteRegStr(UDebugKey, DebugName, DebugData);
}

#pragma LOCKEDCODE

void DbgLogString(PWCHAR msg)
{
	if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) return;

	LOCAL_UNICODE_STRING(DebugData, 256);
	UStrAddStr(DebugData, msg);
	WriteRegStr(UDebugKey, iLogEvent++, DebugData);
}

#pragma LOCKEDCODE

void LogBarAccess8M(const TBarData & Bar, ULONG Offset, PUCHAR pData, BOOLEAN bRead, UCHAR Size)
{
	if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) return;

	LOCAL_UNICODE_STRING(DebugName, 256);
	LOCAL_UNICODE_STRING(DebugData, 256);

	UStrAdd32(DebugName, iLogEvent++);

	UStrAddAddr(DebugData, Bar, Offset);
	if ( bRead )
		UStrAddStr(DebugData, L" >> ");
	else
		UStrAddStr(DebugData, L" << ")
	;
	while ( Size-- )
	{
		UStrAdd8(DebugData, *(pData++));
		if ( Size ) UStrAddStr(DebugName, L" ");
	}
	WriteRegStr(UDebugKey, DebugName, DebugData);
}
#else
	#define LogBarAccess(Bar, Offset, Data, bRead, Size)
	#define DbgLogString(msg)
#endif //bLogBarAccess

#pragma LOCKEDCODE

UCHAR InBar8(const TBarData & Bar, ULONG Offset)
{
	UCHAR Result;
	PUCHAR Addr = PUCHAR(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		Result = READ_REGISTER_UCHAR(Addr);
	else
		Result = READ_PORT_UCHAR(Addr)
	;
	#if bLogBarAccess
		LogBarAccess(Bar, Offset, Result, TRUE, 1);
	#endif //bLogBarAccess
	return Result;
}

#pragma LOCKEDCODE

USHORT InBar16(const TBarData & Bar, ULONG Offset)
{
	USHORT Result;
	PUSHORT Addr = PUSHORT(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		Result = READ_REGISTER_USHORT(Addr);
	else
		Result = READ_PORT_USHORT(Addr)
	;
	#if bLogBarAccess
		LogBarAccess(Bar, Offset, Result, TRUE, 2);
	#endif //bLogBarAccess
	return Result;
}

#pragma LOCKEDCODE

ULONG InBar32(const TBarData & Bar, ULONG Offset)
{
	ULONG Result;
	PULONG Addr = PULONG(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		Result = READ_REGISTER_ULONG(Addr);
	else
		Result = READ_PORT_ULONG(Addr)
	;
	#if bLogBarAccess
		LogBarAccess(Bar, Offset, Result, TRUE, 4);
	#endif //bLogBarAccess
	return Result;
}

#pragma LOCKEDCODE

void InBarStr8(const TBarData & Bar, ULONG Offset, PUCHAR pIntoBuf, ULONG Count)
{
	PUCHAR Addr = PUCHAR(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		READ_REGISTER_BUFFER_UCHAR(Addr, pIntoBuf, Count);
	else
		READ_PORT_BUFFER_UCHAR(Addr, pIntoBuf, Count)
	;
}

#pragma LOCKEDCODE

void InBarStr16(const TBarData & Bar, ULONG Offset, PUSHORT pIntoBuf, ULONG Count)
{
	PUSHORT Addr = PUSHORT(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		READ_REGISTER_BUFFER_USHORT(Addr, pIntoBuf, Count);
	else
		READ_PORT_BUFFER_USHORT(Addr, pIntoBuf, Count)
	;
}

#pragma LOCKEDCODE

void InBarStr32(const TBarData & Bar, ULONG Offset, PULONG pIntoBuf, ULONG Count)
{
	PULONG Addr = PULONG(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		READ_REGISTER_BUFFER_ULONG(Addr, pIntoBuf, Count);
	else
		READ_PORT_BUFFER_ULONG(Addr, pIntoBuf, Count)
	;
}

#pragma LOCKEDCODE

void OutBar8(const TBarData & Bar, ULONG Offset, UCHAR Data)
{
	PUCHAR Addr = PUCHAR(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		WRITE_REGISTER_UCHAR(Addr, Data);
	else
		WRITE_PORT_UCHAR(Addr, Data)
	;
	#if bLogBarAccess
		LogBarAccess(Bar, Offset, Data, FALSE, 1);
	#endif //bLogBarAccess
}

#pragma LOCKEDCODE

void OutBar16(const TBarData & Bar, ULONG Offset, USHORT Data)
{
	PUSHORT Addr = PUSHORT(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		WRITE_REGISTER_USHORT(Addr, Data);
	else
		WRITE_PORT_USHORT(Addr, Data)
	;
	#if bLogBarAccess
		LogBarAccess(Bar, Offset, Data, FALSE, 2);
	#endif //bLogBarAccess
}

#pragma LOCKEDCODE

void OutBar32(const TBarData & Bar, ULONG Offset, ULONG Data)
{
	PULONG Addr = PULONG(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		WRITE_REGISTER_ULONG(Addr, Data);
	else
		WRITE_PORT_ULONG(Addr, Data)
	;
	#if bLogBarAccess
		LogBarAccess(Bar, Offset, Data, FALSE, 4);
	#endif //bLogBarAccess
}

#pragma LOCKEDCODE

void OutBarStr8(const TBarData & Bar, ULONG Offset, PUCHAR pFromBuf, ULONG Count)
{
	PUCHAR Addr = PUCHAR(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
	{
		while ( Count-- )
			WRITE_REGISTER_UCHAR(Addr, *(pFromBuf++))
		;
	}
	else
		WRITE_PORT_BUFFER_UCHAR(Addr, pFromBuf, Count)
	;
}

#pragma LOCKEDCODE

void OutBarStr16(const TBarData & Bar, ULONG Offset, PUSHORT pFromBuf, ULONG Count)
{
	PUSHORT Addr = PUSHORT(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
	{
		while ( Count-- )
			WRITE_REGISTER_USHORT(Addr, *(pFromBuf++))
		;
	}
	else
		WRITE_PORT_BUFFER_USHORT(Addr, pFromBuf, Count)
	;
}

#pragma LOCKEDCODE

void OutBarStr32(const TBarData & Bar, ULONG Offset, PULONG pFromBuf, ULONG Count)
{
	PULONG Addr = PULONG(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
	{
		while ( Count-- )
			WRITE_REGISTER_ULONG(Addr, *(pFromBuf++))
		;
	}
	else
		WRITE_PORT_BUFFER_ULONG(Addr, pFromBuf, Count)
	;
}

#pragma LOCKEDCODE

void OutBar8M(const TBarData & Bar, ULONG Offset, UCHAR Count, PUCHAR pData)
{
	PUCHAR Addr = PUCHAR(Bar.Base + Offset);
	if ( Bar.BarType == btMemory )
		while ( Count-- )
			WRITE_REGISTER_UCHAR(Addr++, *(pData++))
		;
	else
		while ( Count-- )
			WRITE_PORT_UCHAR(Addr++, *(pData++))
	;
	#if bLogBarAccess
		LogBarAccess8M(Bar, Offset, pData, FALSE, Count);
	#endif //bLogBarAccess
}
