1 /* 2 * PROJECT: ReactOS API Tests 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: ISA PnP bus register access helpers 5 * COPYRIGHT: Copyright 2024 Dmitry Borisov <di.sean@protonmail.com> 6 */ 7 8 /* INCLUDES *******************************************************************/ 9 10 #include "precomp.h" 11 12 /* GLOBALS ********************************************************************/ 13 14 PISAPNP_CARD IsapCard; 15 16 static PISAPNP_CARD IsapConfigureCard = NULL; 17 static ULONG IsapCardCount = 0; 18 static UCHAR IsapAddressLatch = 0; 19 20 /* PRIVATE FUNCTIONS **********************************************************/ 21 22 static 23 inline 24 UCHAR 25 IsaBusNextLFSR( 26 _In_ UCHAR Lfsr, 27 _In_ UCHAR InputBit) 28 { 29 UCHAR NextLfsr = Lfsr >> 1; 30 31 NextLfsr |= (((Lfsr ^ NextLfsr) ^ InputBit)) << 7; 32 33 return NextLfsr; 34 } 35 36 static 37 VOID 38 IsaBusWriteAddressRegister( 39 _In_ UCHAR Value) 40 { 41 ULONG i; 42 43 IsapAddressLatch = Value; 44 45 for (i = 0; i < IsapCardCount; ++i) 46 { 47 PISAPNP_CARD Card = &IsapCard[i]; 48 49 if (Card->State != IsaWaitForKey) 50 continue; 51 52 /* Reset the LFSR contents */ 53 if (Card->Lfsr != Value) 54 { 55 Card->Lfsr = ISAPNP_LFSR_SEED; 56 Card->LfsrCount = 0; 57 continue; 58 } 59 60 /* Generate the next data pattern */ 61 Card->Lfsr = IsaBusNextLFSR(Card->Lfsr, 0); 62 63 /* 32 bytes of the initiation key compared correctly */ 64 if (++Card->LfsrCount == 32) 65 { 66 Card->State = IsaSleep; 67 } 68 } 69 } 70 71 static 72 VOID 73 IsaBusWriteDataRegister( 74 _In_ UCHAR Value) 75 { 76 ULONG i, j; 77 78 switch (IsapAddressLatch) 79 { 80 case ISAPNP_READPORT: 81 { 82 /* Update the address of the Read Data Port */ 83 for (i = 0; i < IsapCardCount; ++i) 84 { 85 PISAPNP_CARD Card = &IsapCard[i]; 86 87 if (Card->State != IsaIsolation) 88 continue; 89 90 Card->ReadDataPort = (PUCHAR)(((ULONG_PTR)Value << 2) | 3); 91 } 92 break; 93 } 94 95 case ISAPNP_CONFIGCONTROL: 96 { 97 if (Value & ISAPNP_CONFIG_WAIT_FOR_KEY) 98 { 99 IsapConfigureCard = NULL; 100 } 101 102 for (i = 0; i < IsapCardCount; ++i) 103 { 104 PISAPNP_CARD Card = &IsapCard[i]; 105 106 if (Card->State != IsaWaitForKey) 107 { 108 if (Value & ISAPNP_CONFIG_RESET) 109 { 110 for (j = 0; j < Card->LogicalDevices; ++j) 111 { 112 PISAPNP_CARD_LOGICAL_DEVICE LogDev = &Card->LogDev[j]; 113 114 LogDev->Registers[ISAPNP_ACTIVATE] = 0; 115 } 116 } 117 if (Value & ISAPNP_CONFIG_RESET_CSN) 118 { 119 Card->SelectNumberReg = 0; 120 } 121 } 122 if (Value & ISAPNP_CONFIG_WAIT_FOR_KEY) 123 { 124 Card->State = IsaWaitForKey; 125 } 126 } 127 break; 128 } 129 130 case ISAPNP_WAKE: 131 { 132 for (i = 0; i < IsapCardCount; ++i) 133 { 134 PISAPNP_CARD Card = &IsapCard[i]; 135 136 if (Card->State == IsaWaitForKey) 137 continue; 138 139 if (Card->SelectNumberReg != Value) 140 { 141 if (Card->State == IsaConfgure || Card->State == IsaIsolation) 142 { 143 Card->State = IsaSleep; 144 145 if (IsapConfigureCard == Card) 146 { 147 IsapConfigureCard = NULL; 148 } 149 } 150 151 continue; 152 } 153 154 Card->RomIdx = 0; 155 Card->SerialIsolationIdx = 0; 156 157 if (Card->State == IsaSleep) 158 { 159 if (Value == 0) 160 { 161 Card->State = IsaIsolation; 162 163 Card->IsolationRead = 0; 164 } 165 else 166 { 167 Card->State = IsaConfgure; 168 169 /* Only one card can be in the configure state */ 170 IsapConfigureCard = Card; 171 } 172 } 173 } 174 175 break; 176 } 177 178 case ISAPNP_CARDSELECTNUMBER: 179 { 180 ULONG CsnAssigned = 0; 181 182 /* Assign the CSN */ 183 for (i = 0; i < IsapCardCount; ++i) 184 { 185 PISAPNP_CARD Card = &IsapCard[i]; 186 187 if (Card->State != IsaIsolation) 188 continue; 189 190 ok(Value != 0, "The new CSN is zero\n"); 191 ok(Card->SelectNumberReg != Value, "CSNs must be assigned sequentially"); 192 193 Card->State = IsaConfgure; 194 Card->SelectNumberReg = Value; 195 196 /* Only one card can be in the configure state */ 197 IsapConfigureCard = Card; 198 199 ++CsnAssigned; 200 ok_eq_ulong(CsnAssigned, 1UL); 201 } 202 break; 203 } 204 205 case ISAPNP_LOGICALDEVICENUMBER: 206 { 207 ok(IsapConfigureCard != NULL, "Invalid write to a LDN register\n"); 208 209 if (IsapConfigureCard != NULL) 210 { 211 ok(IsapConfigureCard->LogicalDevices != 0, "Write to a read-only register\n"); 212 ok(Value < IsapConfigureCard->LogicalDevices, "Invalid write to a LDN register\n"); 213 214 IsapConfigureCard->DeviceNumberReg = Value; 215 } 216 break; 217 } 218 219 case ISAPNP_ACTIVATE: 220 { 221 Value &= 0x01; 222 goto WriteDeviceRegister; 223 } 224 225 case ISAPNP_IORANGECHECK: 226 { 227 Value &= 0x03; 228 goto WriteDeviceRegister; 229 } 230 231 case ISAPNP_SERIALISOLATION: 232 case ISAPNP_RESOURCEDATA: 233 case ISAPNP_STATUS: 234 { 235 ok(FALSE, "Write to a read-only register %02x\n", IsapAddressLatch); 236 break; 237 } 238 239 default: 240 { 241 if (IsapAddressLatch >= 0x40) 242 { 243 PISAPNP_CARD_LOGICAL_DEVICE LogDev; 244 245 WriteDeviceRegister: 246 ok(IsapConfigureCard != NULL, "Invalid write to device register\n"); 247 248 if (IsapConfigureCard != NULL) 249 { 250 LogDev = &IsapConfigureCard->LogDev[IsapConfigureCard->DeviceNumberReg]; 251 252 LogDev->Registers[IsapAddressLatch] = Value; 253 } 254 } 255 else 256 { 257 ok(FALSE, "Unexpected write to register %02x\n", IsapAddressLatch); 258 } 259 break; 260 } 261 } 262 } 263 264 static 265 UCHAR 266 IsaBusReadSerialIsolationRegister( 267 _In_ PUCHAR Port) 268 { 269 ULONG i, ResponseMap = 0, ListenMap = 0; 270 UCHAR Result = 0xFF; 271 272 for (i = 0; i < IsapCardCount; ++i) 273 { 274 PISAPNP_CARD Card = &IsapCard[i]; 275 276 if (Card->State != IsaIsolation || Card->ReadDataPort != Port) 277 continue; 278 279 /* The hardware on each card expects 72 pairs of reads */ 280 if (Card->SerialIsolationIdx == RTL_BITS_OF(ISAPNP_IDENTIFIER)) 281 continue; 282 283 Card->IsolationRead ^= 1; 284 285 if (Card->IsolationRead) 286 { 287 if (Card->PnpRom[Card->SerialIsolationIdx / 8] & (1 << (Card->SerialIsolationIdx % 8))) 288 Card->SerialIdResponse = 0x55; 289 else 290 Card->SerialIdResponse = 0x00; 291 292 ++Card->RomIdx; 293 ++Card->SerialIsolationIdx; 294 } 295 else 296 { 297 Card->SerialIdResponse <<= 1; 298 299 if (Card->SerialIdResponse == 0xAA) 300 ResponseMap |= (1 << i); 301 else 302 ListenMap |= (1 << i); 303 } 304 305 if ((Card->SerialIdResponse > Result) || (Result == 0xFF)) 306 Result = Card->SerialIdResponse; 307 } 308 309 /* Release passive cards from the isolation state */ 310 if (ResponseMap != 0 && ListenMap != 0) 311 { 312 for (i = 0; i < RTL_BITS_OF(ListenMap); ++i) 313 { 314 if (ListenMap & (1 << i)) 315 { 316 PISAPNP_CARD Card = &IsapCard[i]; 317 318 Card->State = IsaSleep; 319 } 320 } 321 } 322 323 return Result; 324 } 325 326 static 327 UCHAR 328 IsaBusReadDataPortRegister( 329 _In_ PUCHAR Port) 330 { 331 if (IsapAddressLatch == ISAPNP_SERIALISOLATION) 332 return IsaBusReadSerialIsolationRegister(Port); 333 334 if (IsapConfigureCard == NULL || IsapConfigureCard->ReadDataPort != Port) 335 return 0xFF; 336 337 switch (IsapAddressLatch) 338 { 339 case ISAPNP_RESOURCEDATA: 340 { 341 if (IsapConfigureCard->RomIdx >= IsapConfigureCard->RomSize) 342 break; 343 344 /* The resource data register may return an invalid identifier checksum byte */ 345 if (IsapConfigureCard->RomIdx == FIELD_OFFSET(ISAPNP_IDENTIFIER, Checksum)) 346 { 347 ++IsapConfigureCard->RomIdx; 348 break; 349 } 350 351 return IsapConfigureCard->PnpRom[IsapConfigureCard->RomIdx++]; 352 } 353 354 case ISAPNP_STATUS: 355 return 0x01; /* Resource data byte available */ 356 357 case ISAPNP_CARDSELECTNUMBER: 358 return IsapConfigureCard->SelectNumberReg; 359 360 case ISAPNP_LOGICALDEVICENUMBER: 361 return IsapConfigureCard->DeviceNumberReg; 362 363 case ISAPNP_ACTIVATE: 364 case ISAPNP_IORANGECHECK: 365 goto ReadDeviceRegister; 366 367 default: 368 { 369 if (IsapAddressLatch >= 0x40) 370 { 371 PISAPNP_CARD_LOGICAL_DEVICE LogDev; 372 373 ReadDeviceRegister: 374 LogDev = &IsapConfigureCard->LogDev[IsapConfigureCard->DeviceNumberReg]; 375 376 return LogDev->Registers[IsapAddressLatch]; 377 } 378 else 379 { 380 ok(FALSE, "Unexpected read from register %02x\n", IsapAddressLatch); 381 } 382 break; 383 } 384 } 385 386 return 0xFF; 387 } 388 389 static 390 UCHAR 391 IsaBusPnpChecksum( 392 _In_ PISAPNP_IDENTIFIER Identifier) 393 { 394 UCHAR i, j, Lfsr; 395 396 Lfsr = ISAPNP_LFSR_SEED; 397 for (i = 0; i < FIELD_OFFSET(ISAPNP_IDENTIFIER, Checksum); ++i) 398 { 399 UCHAR Byte = ((PUCHAR)Identifier)[i]; 400 401 for (j = 0; j < RTL_BITS_OF(Byte); ++j) 402 { 403 Lfsr = IsaBusNextLFSR(Lfsr, Byte); 404 Byte >>= 1; 405 } 406 } 407 408 return Lfsr; 409 } 410 411 static 412 UCHAR 413 IsaBusResourceDataChecksum( 414 _In_ PUCHAR PnpRom, 415 _In_ ULONG RomSize) 416 { 417 UNREFERENCED_PARAMETER(PnpRom); 418 UNREFERENCED_PARAMETER(RomSize); 419 420 /* This means "Checksummed properly" */ 421 return 0x00; 422 } 423 424 static 425 VOID 426 IsaBusPlugInCard( 427 _Inout_ PISAPNP_CARD Card) 428 { 429 Card->State = IsaWaitForKey; 430 Card->Lfsr = ISAPNP_LFSR_SEED; 431 Card->LfsrCount = 0; 432 Card->SelectNumberReg = 0; 433 Card->ReadDataPort = NULL; 434 } 435 436 /* PUBLIC FUNCTIONS ***********************************************************/ 437 438 VOID 439 IsaBusCreateCard( 440 _Inout_ PISAPNP_CARD Card, 441 _In_ PVOID PnpRom, 442 _In_ ULONG RomSize, 443 _In_ ULONG LogicalDevices) 444 { 445 Card->RomSize = RomSize; 446 Card->PnpRom = PnpRom; 447 Card->PnpRom[FIELD_OFFSET(ISAPNP_IDENTIFIER, Checksum)] = IsaBusPnpChecksum(PnpRom); 448 Card->PnpRom[RomSize - 1] = IsaBusResourceDataChecksum(PnpRom, RomSize); 449 Card->LogicalDevices = LogicalDevices; 450 451 IsaBusPlugInCard(Card); 452 453 ++IsapCardCount; 454 } 455 456 VOID 457 NTAPI 458 WRITE_PORT_UCHAR( 459 _In_ PUCHAR Port, 460 _In_ UCHAR Value) 461 { 462 switch ((ULONG_PTR)Port) 463 { 464 case 0x279: 465 IsaBusWriteAddressRegister(Value); 466 break; 467 468 case 0xA79: 469 IsaBusWriteDataRegister(Value); 470 break; 471 472 default: 473 ok(FALSE, "Unexpected write to port %p %02x\n", Port, Value); 474 break; 475 } 476 } 477 478 UCHAR 479 NTAPI 480 READ_PORT_UCHAR( 481 _In_ PUCHAR Port) 482 { 483 UCHAR Result; 484 485 /* We can write only to NT Read Data Ports */ 486 switch ((ULONG_PTR)Port) 487 { 488 case 0x2F4 | 3: 489 Result = IsaBusReadDataPortRegister(Port); 490 break; 491 492 /* Indicate that the Read Data Port is in conflict */ 493 case 0x274 | 3: 494 case 0x3E4 | 3: 495 case 0x204 | 3: 496 case 0x2E4 | 3: 497 case 0x354 | 3: 498 Result = 0x00; 499 break; 500 501 default: 502 ok(FALSE, "Unexpected read from port %p\n", Port); 503 Result = 0xFF; 504 break; 505 } 506 507 return Result; 508 } 509