reactos/modules/rostests/unittests/isapnp/isabus.c

509 lines
12 KiB
C

/*
* PROJECT: ReactOS API Tests
* LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
* PURPOSE: ISA PnP bus register access helpers
* COPYRIGHT: Copyright 2024 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "precomp.h"
/* GLOBALS ********************************************************************/
PISAPNP_CARD IsapCard;
static PISAPNP_CARD IsapConfigureCard = NULL;
static ULONG IsapCardCount = 0;
static UCHAR IsapAddressLatch = 0;
/* PRIVATE FUNCTIONS **********************************************************/
static
inline
UCHAR
IsaBusNextLFSR(
_In_ UCHAR Lfsr,
_In_ UCHAR InputBit)
{
UCHAR NextLfsr = Lfsr >> 1;
NextLfsr |= (((Lfsr ^ NextLfsr) ^ InputBit)) << 7;
return NextLfsr;
}
static
VOID
IsaBusWriteAddressRegister(
_In_ UCHAR Value)
{
ULONG i;
IsapAddressLatch = Value;
for (i = 0; i < IsapCardCount; ++i)
{
PISAPNP_CARD Card = &IsapCard[i];
if (Card->State != IsaWaitForKey)
continue;
/* Reset the LFSR contents */
if (Card->Lfsr != Value)
{
Card->Lfsr = ISAPNP_LFSR_SEED;
Card->LfsrCount = 0;
continue;
}
/* Generate the next data pattern */
Card->Lfsr = IsaBusNextLFSR(Card->Lfsr, 0);
/* 32 bytes of the initiation key compared correctly */
if (++Card->LfsrCount == 32)
{
Card->State = IsaSleep;
}
}
}
static
VOID
IsaBusWriteDataRegister(
_In_ UCHAR Value)
{
ULONG i, j;
switch (IsapAddressLatch)
{
case ISAPNP_READPORT:
{
/* Update the address of the Read Data Port */
for (i = 0; i < IsapCardCount; ++i)
{
PISAPNP_CARD Card = &IsapCard[i];
if (Card->State != IsaIsolation)
continue;
Card->ReadDataPort = (PUCHAR)(((ULONG_PTR)Value << 2) | 3);
}
break;
}
case ISAPNP_CONFIGCONTROL:
{
if (Value & ISAPNP_CONFIG_WAIT_FOR_KEY)
{
IsapConfigureCard = NULL;
}
for (i = 0; i < IsapCardCount; ++i)
{
PISAPNP_CARD Card = &IsapCard[i];
if (Card->State != IsaWaitForKey)
{
if (Value & ISAPNP_CONFIG_RESET)
{
for (j = 0; j < Card->LogicalDevices; ++j)
{
PISAPNP_CARD_LOGICAL_DEVICE LogDev = &Card->LogDev[j];
LogDev->Registers[ISAPNP_ACTIVATE] = 0;
}
}
if (Value & ISAPNP_CONFIG_RESET_CSN)
{
Card->SelectNumberReg = 0;
}
}
if (Value & ISAPNP_CONFIG_WAIT_FOR_KEY)
{
Card->State = IsaWaitForKey;
}
}
break;
}
case ISAPNP_WAKE:
{
for (i = 0; i < IsapCardCount; ++i)
{
PISAPNP_CARD Card = &IsapCard[i];
if (Card->State == IsaWaitForKey)
continue;
if (Card->SelectNumberReg != Value)
{
if (Card->State == IsaConfgure || Card->State == IsaIsolation)
{
Card->State = IsaSleep;
if (IsapConfigureCard == Card)
{
IsapConfigureCard = NULL;
}
}
continue;
}
Card->RomIdx = 0;
Card->SerialIsolationIdx = 0;
if (Card->State == IsaSleep)
{
if (Value == 0)
{
Card->State = IsaIsolation;
Card->IsolationRead = 0;
}
else
{
Card->State = IsaConfgure;
/* Only one card can be in the configure state */
IsapConfigureCard = Card;
}
}
}
break;
}
case ISAPNP_CARDSELECTNUMBER:
{
ULONG CsnAssigned = 0;
/* Assign the CSN */
for (i = 0; i < IsapCardCount; ++i)
{
PISAPNP_CARD Card = &IsapCard[i];
if (Card->State != IsaIsolation)
continue;
ok(Value != 0, "The new CSN is zero\n");
ok(Card->SelectNumberReg != Value, "CSNs must be assigned sequentially");
Card->State = IsaConfgure;
Card->SelectNumberReg = Value;
/* Only one card can be in the configure state */
IsapConfigureCard = Card;
++CsnAssigned;
ok_eq_ulong(CsnAssigned, 1UL);
}
break;
}
case ISAPNP_LOGICALDEVICENUMBER:
{
ok(IsapConfigureCard != NULL, "Invalid write to a LDN register\n");
if (IsapConfigureCard != NULL)
{
ok(IsapConfigureCard->LogicalDevices != 0, "Write to a read-only register\n");
ok(Value < IsapConfigureCard->LogicalDevices, "Invalid write to a LDN register\n");
IsapConfigureCard->DeviceNumberReg = Value;
}
break;
}
case ISAPNP_ACTIVATE:
{
Value &= 0x01;
goto WriteDeviceRegister;
}
case ISAPNP_IORANGECHECK:
{
Value &= 0x03;
goto WriteDeviceRegister;
}
case ISAPNP_SERIALISOLATION:
case ISAPNP_RESOURCEDATA:
case ISAPNP_STATUS:
{
ok(FALSE, "Write to a read-only register %02x\n", IsapAddressLatch);
break;
}
default:
{
if (IsapAddressLatch >= 0x40)
{
PISAPNP_CARD_LOGICAL_DEVICE LogDev;
WriteDeviceRegister:
ok(IsapConfigureCard != NULL, "Invalid write to device register\n");
if (IsapConfigureCard != NULL)
{
LogDev = &IsapConfigureCard->LogDev[IsapConfigureCard->DeviceNumberReg];
LogDev->Registers[IsapAddressLatch] = Value;
}
}
else
{
ok(FALSE, "Unexpected write to register %02x\n", IsapAddressLatch);
}
break;
}
}
}
static
UCHAR
IsaBusReadSerialIsolationRegister(
_In_ PUCHAR Port)
{
ULONG i, ResponseMap = 0, ListenMap = 0;
UCHAR Result = 0xFF;
for (i = 0; i < IsapCardCount; ++i)
{
PISAPNP_CARD Card = &IsapCard[i];
if (Card->State != IsaIsolation || Card->ReadDataPort != Port)
continue;
/* The hardware on each card expects 72 pairs of reads */
if (Card->SerialIsolationIdx == RTL_BITS_OF(ISAPNP_IDENTIFIER))
continue;
Card->IsolationRead ^= 1;
if (Card->IsolationRead)
{
if (Card->PnpRom[Card->SerialIsolationIdx / 8] & (1 << (Card->SerialIsolationIdx % 8)))
Card->SerialIdResponse = 0x55;
else
Card->SerialIdResponse = 0x00;
++Card->RomIdx;
++Card->SerialIsolationIdx;
}
else
{
Card->SerialIdResponse <<= 1;
if (Card->SerialIdResponse == 0xAA)
ResponseMap |= (1 << i);
else
ListenMap |= (1 << i);
}
if ((Card->SerialIdResponse > Result) || (Result == 0xFF))
Result = Card->SerialIdResponse;
}
/* Release passive cards from the isolation state */
if (ResponseMap != 0 && ListenMap != 0)
{
for (i = 0; i < RTL_BITS_OF(ListenMap); ++i)
{
if (ListenMap & (1 << i))
{
PISAPNP_CARD Card = &IsapCard[i];
Card->State = IsaSleep;
}
}
}
return Result;
}
static
UCHAR
IsaBusReadDataPortRegister(
_In_ PUCHAR Port)
{
if (IsapAddressLatch == ISAPNP_SERIALISOLATION)
return IsaBusReadSerialIsolationRegister(Port);
if (IsapConfigureCard == NULL || IsapConfigureCard->ReadDataPort != Port)
return 0xFF;
switch (IsapAddressLatch)
{
case ISAPNP_RESOURCEDATA:
{
if (IsapConfigureCard->RomIdx >= IsapConfigureCard->RomSize)
break;
/* The resource data register may return an invalid identifier checksum byte */
if (IsapConfigureCard->RomIdx == FIELD_OFFSET(ISAPNP_IDENTIFIER, Checksum))
{
++IsapConfigureCard->RomIdx;
break;
}
return IsapConfigureCard->PnpRom[IsapConfigureCard->RomIdx++];
}
case ISAPNP_STATUS:
return 0x01; /* Resource data byte available */
case ISAPNP_CARDSELECTNUMBER:
return IsapConfigureCard->SelectNumberReg;
case ISAPNP_LOGICALDEVICENUMBER:
return IsapConfigureCard->DeviceNumberReg;
case ISAPNP_ACTIVATE:
case ISAPNP_IORANGECHECK:
goto ReadDeviceRegister;
default:
{
if (IsapAddressLatch >= 0x40)
{
PISAPNP_CARD_LOGICAL_DEVICE LogDev;
ReadDeviceRegister:
LogDev = &IsapConfigureCard->LogDev[IsapConfigureCard->DeviceNumberReg];
return LogDev->Registers[IsapAddressLatch];
}
else
{
ok(FALSE, "Unexpected read from register %02x\n", IsapAddressLatch);
}
break;
}
}
return 0xFF;
}
static
UCHAR
IsaBusPnpChecksum(
_In_ PISAPNP_IDENTIFIER Identifier)
{
UCHAR i, j, Lfsr;
Lfsr = ISAPNP_LFSR_SEED;
for (i = 0; i < FIELD_OFFSET(ISAPNP_IDENTIFIER, Checksum); ++i)
{
UCHAR Byte = ((PUCHAR)Identifier)[i];
for (j = 0; j < RTL_BITS_OF(Byte); ++j)
{
Lfsr = IsaBusNextLFSR(Lfsr, Byte);
Byte >>= 1;
}
}
return Lfsr;
}
static
UCHAR
IsaBusResourceDataChecksum(
_In_ PUCHAR PnpRom,
_In_ ULONG RomSize)
{
UNREFERENCED_PARAMETER(PnpRom);
UNREFERENCED_PARAMETER(RomSize);
/* This means "Checksummed properly" */
return 0x00;
}
static
VOID
IsaBusPlugInCard(
_Inout_ PISAPNP_CARD Card)
{
Card->State = IsaWaitForKey;
Card->Lfsr = ISAPNP_LFSR_SEED;
Card->LfsrCount = 0;
Card->SelectNumberReg = 0;
Card->ReadDataPort = NULL;
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID
IsaBusCreateCard(
_Inout_ PISAPNP_CARD Card,
_In_ PVOID PnpRom,
_In_ ULONG RomSize,
_In_ ULONG LogicalDevices)
{
Card->RomSize = RomSize;
Card->PnpRom = PnpRom;
Card->PnpRom[FIELD_OFFSET(ISAPNP_IDENTIFIER, Checksum)] = IsaBusPnpChecksum(PnpRom);
Card->PnpRom[RomSize - 1] = IsaBusResourceDataChecksum(PnpRom, RomSize);
Card->LogicalDevices = LogicalDevices;
IsaBusPlugInCard(Card);
++IsapCardCount;
}
VOID
NTAPI
WRITE_PORT_UCHAR(
_In_ PUCHAR Port,
_In_ UCHAR Value)
{
switch ((ULONG_PTR)Port)
{
case 0x279:
IsaBusWriteAddressRegister(Value);
break;
case 0xA79:
IsaBusWriteDataRegister(Value);
break;
default:
ok(FALSE, "Unexpected write to port %p %02x\n", Port, Value);
break;
}
}
UCHAR
NTAPI
READ_PORT_UCHAR(
_In_ PUCHAR Port)
{
UCHAR Result;
/* We can write only to NT Read Data Ports */
switch ((ULONG_PTR)Port)
{
case 0x2F4 | 3:
Result = IsaBusReadDataPortRegister(Port);
break;
/* Indicate that the Read Data Port is in conflict */
case 0x274 | 3:
case 0x3E4 | 3:
case 0x204 | 3:
case 0x2E4 | 3:
case 0x354 | 3:
Result = 0x00;
break;
default:
ok(FALSE, "Unexpected read from port %p\n", Port);
Result = 0xFF;
break;
}
return Result;
}