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