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