xref: /reactos/drivers/bus/isapnp/hardware.c (revision 139a3d66)
1 /*
2  * PROJECT:         ReactOS ISA PnP Bus driver
3  * FILE:            hardware.c
4  * PURPOSE:         Hardware support code
5  * PROGRAMMERS:     Cameron Gutman (cameron.gutman@reactos.org)
6  *                  Hervé Poussineau
7  */
8 
9 #include <isapnp.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 static
15 inline
16 VOID
17 WriteAddress(
18     IN USHORT Address)
19 {
20     WRITE_PORT_UCHAR((PUCHAR)ISAPNP_ADDRESS, Address);
21 }
22 
23 static
24 inline
25 VOID
26 WriteData(
27     IN USHORT Data)
28 {
29     WRITE_PORT_UCHAR((PUCHAR)ISAPNP_WRITE_DATA, Data);
30 }
31 
32 static
33 inline
34 UCHAR
35 ReadData(
36     IN PUCHAR ReadDataPort)
37 {
38     return READ_PORT_UCHAR(ReadDataPort);
39 }
40 
41 static
42 inline
43 VOID
44 WriteByte(
45     IN USHORT Address,
46     IN USHORT Value)
47 {
48     WriteAddress(Address);
49     WriteData(Value);
50 }
51 
52 static
53 inline
54 UCHAR
55 ReadByte(
56     IN PUCHAR ReadDataPort,
57     IN USHORT Address)
58 {
59     WriteAddress(Address);
60     return ReadData(ReadDataPort);
61 }
62 
63 static
64 inline
65 USHORT
66 ReadWord(
67     IN PUCHAR ReadDataPort,
68     IN USHORT Address)
69 {
70     return ((ReadByte(ReadDataPort, Address) << 8) |
71             (ReadByte(ReadDataPort, Address + 1)));
72 }
73 
74 static
75 inline
76 VOID
77 SetReadDataPort(
78     IN PUCHAR ReadDataPort)
79 {
80     WriteByte(ISAPNP_READPORT, ((ULONG_PTR)ReadDataPort >> 2));
81 }
82 
83 static
84 inline
85 VOID
86 EnterIsolationState(VOID)
87 {
88     WriteAddress(ISAPNP_SERIALISOLATION);
89 }
90 
91 static
92 inline
93 VOID
94 WaitForKey(VOID)
95 {
96     WriteByte(ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_WAIT_FOR_KEY);
97 }
98 
99 static
100 inline
101 VOID
102 ResetCsn(VOID)
103 {
104     WriteByte(ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_RESET_CSN);
105 }
106 
107 static
108 inline
109 VOID
110 Wake(
111     IN USHORT Csn)
112 {
113     WriteByte(ISAPNP_WAKE, Csn);
114 }
115 
116 static
117 inline
118 USHORT
119 ReadResourceData(
120     IN PUCHAR ReadDataPort)
121 {
122     return ReadByte(ReadDataPort, ISAPNP_RESOURCEDATA);
123 }
124 
125 static
126 inline
127 USHORT
128 ReadStatus(
129     IN PUCHAR ReadDataPort)
130 {
131     return ReadByte(ReadDataPort, ISAPNP_STATUS);
132 }
133 
134 static
135 inline
136 VOID
137 WriteCsn(
138     IN USHORT Csn)
139 {
140     WriteByte(ISAPNP_CARDSELECTNUMBER, Csn);
141 }
142 
143 static
144 inline
145 VOID
146 WriteLogicalDeviceNumber(
147     IN USHORT LogDev)
148 {
149     WriteByte(ISAPNP_LOGICALDEVICENUMBER, LogDev);
150 }
151 
152 static
153 inline
154 VOID
155 ActivateDevice(
156     IN USHORT LogDev)
157 {
158     WriteLogicalDeviceNumber(LogDev);
159     WriteByte(ISAPNP_ACTIVATE, 1);
160 }
161 
162 static
163 inline
164 VOID
165 DeactivateDevice(
166     IN USHORT LogDev)
167 {
168     WriteLogicalDeviceNumber(LogDev);
169     WriteByte(ISAPNP_ACTIVATE, 0);
170 }
171 
172 static
173 inline
174 USHORT
175 ReadIoBase(
176     IN PUCHAR ReadDataPort,
177     IN USHORT Index)
178 {
179     return ReadWord(ReadDataPort, ISAPNP_IOBASE(Index));
180 }
181 
182 static
183 inline
184 USHORT
185 ReadIrqNo(
186     IN PUCHAR ReadDataPort,
187     IN USHORT Index)
188 {
189     return ReadByte(ReadDataPort, ISAPNP_IRQNO(Index));
190 }
191 
192 static
193 inline
194 USHORT
195 ReadIrqType(
196     IN PUCHAR ReadDataPort,
197     IN USHORT Index)
198 {
199     return ReadByte(ReadDataPort, ISAPNP_IRQTYPE(Index));
200 }
201 
202 static
203 inline
204 USHORT
205 ReadDmaChannel(
206     IN PUCHAR ReadDataPort,
207     IN USHORT Index)
208 {
209     return ReadByte(ReadDataPort, ISAPNP_DMACHANNEL(Index));
210 }
211 
212 static
213 inline
214 VOID
215 HwDelay(VOID)
216 {
217     KeStallExecutionProcessor(1000);
218 }
219 
220 static
221 inline
222 UCHAR
223 NextLFSR(
224     IN UCHAR Lfsr,
225     IN UCHAR InputBit)
226 {
227     UCHAR NextLfsr = Lfsr >> 1;
228 
229     NextLfsr |= (((Lfsr ^ NextLfsr) ^ InputBit)) << 7;
230 
231     return NextLfsr;
232 }
233 
234 static
235 VOID
236 SendKey(VOID)
237 {
238     UCHAR i, Lfsr;
239 
240     HwDelay();
241     WriteAddress(0x00);
242     WriteAddress(0x00);
243 
244     Lfsr = ISAPNP_LFSR_SEED;
245     for (i = 0; i < 32; i++)
246     {
247         WriteAddress(Lfsr);
248         Lfsr = NextLFSR(Lfsr, 0);
249     }
250 }
251 
252 static
253 USHORT
254 PeekByte(
255     IN PUCHAR ReadDataPort)
256 {
257     USHORT i;
258 
259     for (i = 0; i < 20; i++)
260     {
261         if (ReadStatus(ReadDataPort) & 0x01)
262             return ReadResourceData(ReadDataPort);
263 
264         HwDelay();
265     }
266 
267     return 0xFF;
268 }
269 
270 static
271 VOID
272 Peek(
273     IN PUCHAR ReadDataPort,
274     IN OUT PVOID Buffer,
275     IN ULONG Length)
276 {
277     USHORT i, Byte;
278 
279     for (i = 0; i < Length; i++)
280     {
281         Byte = PeekByte(ReadDataPort);
282         if (Buffer)
283             *((PUCHAR)Buffer + i) = Byte;
284     }
285 }
286 
287 static
288 USHORT
289 IsaPnpChecksum(
290     IN PISAPNP_IDENTIFIER Identifier)
291 {
292     UCHAR i, j, Lfsr, Byte;
293 
294     Lfsr = ISAPNP_LFSR_SEED;
295     for (i = 0; i < 8; i++)
296     {
297         Byte = *(((PUCHAR)Identifier) + i);
298         for (j = 0; j < 8; j++)
299         {
300             Lfsr = NextLFSR(Lfsr, Byte);
301             Byte >>= 1;
302         }
303     }
304 
305     return Lfsr;
306 }
307 
308 static
309 BOOLEAN
310 ReadTags(
311     IN PUCHAR ReadDataPort,
312     IN USHORT LogDev,
313     IN OUT PISAPNP_LOGICAL_DEVICE LogDevice)
314 {
315     BOOLEAN res = FALSE;
316     PVOID Buffer;
317     USHORT Tag, TagLen, MaxLen;
318     ULONG NumberOfIo = 0, NumberOfIrq = 0, NumberOfDma = 0;
319 
320     LogDev += 1;
321 
322     while (TRUE)
323     {
324         Tag = PeekByte(ReadDataPort);
325         if (ISAPNP_IS_SMALL_TAG(Tag))
326         {
327             TagLen = ISAPNP_SMALL_TAG_LEN(Tag);
328             Tag = ISAPNP_SMALL_TAG_NAME(Tag);
329         }
330         else
331         {
332             TagLen = PeekByte(ReadDataPort) + (PeekByte(ReadDataPort) << 8);
333             Tag = ISAPNP_LARGE_TAG_NAME(Tag);
334         }
335         if (Tag == ISAPNP_TAG_END)
336             break;
337 
338         Buffer = NULL;
339         if (Tag == ISAPNP_TAG_LOGDEVID)
340         {
341             MaxLen = sizeof(LogDevice->LogDevId);
342             Buffer = &LogDevice->LogDevId;
343             LogDev--;
344         }
345         else if (Tag == ISAPNP_TAG_IRQ && NumberOfIrq < ARRAYSIZE(LogDevice->Irq))
346         {
347             MaxLen = sizeof(LogDevice->Irq[NumberOfIrq].Description);
348             Buffer = &LogDevice->Irq[NumberOfIrq].Description;
349             NumberOfIrq++;
350         }
351         else if (Tag == ISAPNP_TAG_IOPORT && NumberOfIo < ARRAYSIZE(LogDevice->Io))
352         {
353             MaxLen = sizeof(LogDevice->Io[NumberOfIo].Description);
354             Buffer = &LogDevice->Io[NumberOfIo].Description;
355             NumberOfIo++;
356         }
357         else if (Tag == ISAPNP_TAG_DMA && NumberOfDma < ARRAYSIZE(LogDevice->Dma))
358         {
359             MaxLen = sizeof(LogDevice->Dma[NumberOfDma].Description);
360             Buffer = &LogDevice->Dma[NumberOfDma].Description;
361             NumberOfDma++;
362         }
363         else if (LogDev == 0)
364         {
365             DPRINT1("Found unknown tag 0x%x (len %d)\n", Tag, TagLen);
366         }
367 
368         if (Buffer && LogDev == 0)
369         {
370             res = TRUE;
371             if (MaxLen > TagLen)
372             {
373                 Peek(ReadDataPort, Buffer, TagLen);
374             }
375             else
376             {
377                 Peek(ReadDataPort, Buffer, MaxLen);
378                 Peek(ReadDataPort, NULL, TagLen - MaxLen);
379             }
380         }
381         else
382         {
383             /* We don't want to read informations on this
384              * logical device, or we don't know the tag. */
385             Peek(ReadDataPort, NULL, TagLen);
386         }
387     };
388 
389     return res;
390 }
391 
392 static
393 INT
394 TryIsolate(
395     IN PUCHAR ReadDataPort)
396 {
397     ISAPNP_IDENTIFIER Identifier;
398     USHORT i, j;
399     BOOLEAN Seen55aa, SeenLife;
400     INT Csn = 0;
401     USHORT Byte, Data;
402 
403     DPRINT("Setting read data port: 0x%p\n", ReadDataPort);
404 
405     WaitForKey();
406     SendKey();
407 
408     ResetCsn();
409     HwDelay();
410     HwDelay();
411 
412     WaitForKey();
413     SendKey();
414     Wake(0x00);
415 
416     SetReadDataPort(ReadDataPort);
417     HwDelay();
418 
419     while (TRUE)
420     {
421         EnterIsolationState();
422         HwDelay();
423 
424         RtlZeroMemory(&Identifier, sizeof(Identifier));
425 
426         Seen55aa = SeenLife = FALSE;
427         for (i = 0; i < 9; i++)
428         {
429             Byte = 0;
430             for (j = 0; j < 8; j++)
431             {
432                 Data = ReadData(ReadDataPort);
433                 HwDelay();
434                 Data = ((Data << 8) | ReadData(ReadDataPort));
435                 HwDelay();
436                 Byte >>= 1;
437 
438                 if (Data != 0xFFFF)
439                 {
440                     SeenLife = TRUE;
441                     if (Data == 0x55AA)
442                     {
443                         Byte |= 0x80;
444                         Seen55aa = TRUE;
445                     }
446                 }
447             }
448             *(((PUCHAR)&Identifier) + i) = Byte;
449         }
450 
451         if (!Seen55aa)
452         {
453             if (Csn)
454             {
455                 DPRINT("Found no more cards\n");
456             }
457             else
458             {
459                 if (SeenLife)
460                 {
461                     DPRINT("Saw life but no cards, trying new read port\n");
462                     Csn = -1;
463                 }
464                 else
465                 {
466                     DPRINT("Saw no sign of life, abandoning isolation\n");
467                 }
468             }
469             break;
470         }
471 
472         if (Identifier.Checksum != IsaPnpChecksum(&Identifier))
473         {
474             DPRINT("Bad checksum, trying next read data port\n");
475             Csn = -1;
476             break;
477         }
478 
479         Csn++;
480 
481         WriteCsn(Csn);
482         HwDelay();
483 
484         Wake(0x00);
485         HwDelay();
486     }
487 
488     WaitForKey();
489 
490     if (Csn > 0)
491     {
492         DPRINT("Found %d cards at read port 0x%p\n", Csn, ReadDataPort);
493     }
494 
495     return Csn;
496 }
497 
498 VOID
499 DeviceActivation(
500     IN PISAPNP_LOGICAL_DEVICE IsaDevice,
501     IN BOOLEAN Activate)
502 {
503     WaitForKey();
504     SendKey();
505     Wake(IsaDevice->CSN);
506 
507     if (Activate)
508         ActivateDevice(IsaDevice->LDN);
509     else
510         DeactivateDevice(IsaDevice->LDN);
511 
512     HwDelay();
513 
514     WaitForKey();
515 }
516 
517 NTSTATUS
518 ProbeIsaPnpBus(
519     IN PISAPNP_FDO_EXTENSION FdoExt)
520 {
521     PISAPNP_LOGICAL_DEVICE LogDevice;
522     ISAPNP_IDENTIFIER Identifier;
523     USHORT Csn;
524     USHORT LogDev;
525     ULONG i;
526 
527     ASSERT(FdoExt->ReadDataPort);
528 
529     for (Csn = 1; Csn <= 0xFF; Csn++)
530     {
531         for (LogDev = 0; LogDev <= 0xFF; LogDev++)
532         {
533             LogDevice = ExAllocatePool(NonPagedPool, sizeof(ISAPNP_LOGICAL_DEVICE));
534             if (!LogDevice)
535                 return STATUS_NO_MEMORY;
536 
537             RtlZeroMemory(LogDevice, sizeof(ISAPNP_LOGICAL_DEVICE));
538 
539             LogDevice->CSN = Csn;
540             LogDevice->LDN = LogDev;
541 
542             WaitForKey();
543             SendKey();
544             Wake(Csn);
545 
546             Peek(FdoExt->ReadDataPort, &Identifier, sizeof(Identifier));
547 
548             if (Identifier.VendorId & 0x80)
549             {
550                 ExFreePool(LogDevice);
551                 return STATUS_SUCCESS;
552             }
553 
554             if (!ReadTags(FdoExt->ReadDataPort, LogDev, LogDevice))
555                 break;
556 
557             WriteLogicalDeviceNumber(LogDev);
558 
559             LogDevice->VendorId[0] = ((LogDevice->LogDevId.VendorId >> 2) & 0x1f) + 'A' - 1,
560             LogDevice->VendorId[1] = (((LogDevice->LogDevId.VendorId & 0x3) << 3) | ((LogDevice->LogDevId.VendorId >> 13) & 0x7)) + 'A' - 1,
561             LogDevice->VendorId[2] = ((LogDevice->LogDevId.VendorId >> 8) & 0x1f) + 'A' - 1,
562             LogDevice->ProdId = RtlUshortByteSwap(LogDevice->LogDevId.ProdId);
563             LogDevice->SerialNumber = Identifier.Serial;
564             for (i = 0; i < ARRAYSIZE(LogDevice->Io); i++)
565                 LogDevice->Io[i].CurrentBase = ReadIoBase(FdoExt->ReadDataPort, i);
566             for (i = 0; i < ARRAYSIZE(LogDevice->Irq); i++)
567             {
568                 LogDevice->Irq[i].CurrentNo = ReadIrqNo(FdoExt->ReadDataPort, i);
569                 LogDevice->Irq[i].CurrentType = ReadIrqType(FdoExt->ReadDataPort, i);
570             }
571             for (i = 0; i < ARRAYSIZE(LogDevice->Dma); i++)
572             {
573                 LogDevice->Dma[i].CurrentChannel = ReadDmaChannel(FdoExt->ReadDataPort, i);
574             }
575 
576             DPRINT1("Detected ISA PnP device - VID: '%3s' PID: 0x%x SN: 0x%08x IoBase: 0x%x IRQ:0x%x\n",
577                     LogDevice->VendorId, LogDevice->ProdId, LogDevice->SerialNumber, LogDevice->Io[0].CurrentBase, LogDevice->Irq[0].CurrentNo);
578 
579             WaitForKey();
580 
581             InsertTailList(&FdoExt->DeviceListHead, &LogDevice->ListEntry);
582             FdoExt->DeviceCount++;
583         }
584     }
585 
586     return STATUS_SUCCESS;
587 }
588 
589 NTSTATUS
590 NTAPI
591 IsaHwTryReadDataPort(
592     IN PUCHAR ReadDataPort)
593 {
594     return TryIsolate(ReadDataPort) > 0 ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
595 }
596 
597 NTSTATUS
598 NTAPI
599 IsaHwActivateDevice(
600     IN PISAPNP_LOGICAL_DEVICE LogicalDevice)
601 {
602     DeviceActivation(LogicalDevice,
603                      TRUE);
604 
605     return STATUS_SUCCESS;
606 }
607 
608 NTSTATUS
609 NTAPI
610 IsaHwDeactivateDevice(
611     IN PISAPNP_LOGICAL_DEVICE LogicalDevice)
612 {
613     DeviceActivation(LogicalDevice,
614                      FALSE);
615 
616     return STATUS_SUCCESS;
617 }
618 
619 NTSTATUS
620 NTAPI
621 IsaHwFillDeviceList(
622     IN PISAPNP_FDO_EXTENSION FdoExt)
623 {
624     return ProbeIsaPnpBus(FdoExt);
625 }
626