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