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
IsaBusNextLFSR(_In_ UCHAR Lfsr,_In_ UCHAR InputBit)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
IsaBusWriteAddressRegister(_In_ UCHAR Value)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
IsaBusWriteDataRegister(_In_ UCHAR Value)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
IsaBusReadSerialIsolationRegister(_In_ PUCHAR Port)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
IsaBusReadDataPortRegister(_In_ PUCHAR Port)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
IsaBusPnpChecksum(_In_ PISAPNP_IDENTIFIER Identifier)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
IsaBusResourceDataChecksum(_In_ PUCHAR PnpRom,_In_ ULONG RomSize)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
IsaBusPlugInCard(_Inout_ PISAPNP_CARD Card)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
IsaBusCreateCard(_Inout_ PISAPNP_CARD Card,_In_ PVOID PnpRom,_In_ ULONG RomSize,_In_ ULONG LogicalDevices)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
WRITE_PORT_UCHAR(_In_ PUCHAR Port,_In_ UCHAR Value)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
READ_PORT_UCHAR(_In_ PUCHAR Port)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