Add config interface start (PciReadWriteConfigSpace, PciReadSlotConfig)
[reactos.git] / reactos / drivers / bus / pcix / utils.c
1 /*
2 * PROJECT: ReactOS PCI Bus Driver
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/bus/pci/utils.c
5 * PURPOSE: Utility/Helper Support Code
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <pci.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 RTL_RANGE_LIST PciIsaBitExclusionList;
18 RTL_RANGE_LIST PciVgaAndIsaBitExclusionList;
19
20 /* FUNCTIONS ******************************************************************/
21
22 BOOLEAN
23 NTAPI
24 PciUnicodeStringStrStr(IN PUNICODE_STRING InputString,
25 IN PCUNICODE_STRING EqualString,
26 IN BOOLEAN CaseInSensitive)
27 {
28 UNICODE_STRING PartialString;
29 LONG EqualChars, TotalChars;
30
31 /* Build a partial string with the smaller substring */
32 PartialString.Length = EqualString->Length;
33 PartialString.MaximumLength = InputString->MaximumLength;;
34 PartialString.Buffer = InputString->Buffer;
35
36 /* Check how many characters that need comparing */
37 EqualChars = 0;
38 TotalChars = (InputString->Length - EqualString->Length) / sizeof(WCHAR);
39
40 /* If the substring is bigger, just fail immediately */
41 if (TotalChars < 0) return FALSE;
42
43 /* Keep checking each character */
44 while (!RtlEqualUnicodeString(EqualString, &PartialString, CaseInSensitive))
45 {
46 /* Continue checking until all the required characters are equal */
47 PartialString.Buffer++;
48 PartialString.MaximumLength -= sizeof(WCHAR);
49 if (++EqualChars > TotalChars) return FALSE;
50 }
51
52 /* The string is equal */
53 return TRUE;
54 }
55
56 BOOLEAN
57 NTAPI
58 PciStringToUSHORT(IN PWCHAR String,
59 OUT PUSHORT Value)
60 {
61 USHORT Short;
62 ULONG Low, High, Length;
63 WCHAR Char;
64
65 /* Initialize everything to zero */
66 Short = 0;
67 Length = 0;
68 while (TRUE)
69 {
70 /* Get the character and set the high byte based on the previous one */
71 Char = *String++;
72 High = 16 * Short;
73
74 /* Check for numbers */
75 if ( Char >= '0' && Char <= '9' )
76 {
77 /* Convert them to a byte */
78 Low = Char - '0';
79 }
80 else if ( Char >= 'A' && Char <= 'F' )
81 {
82 /* Convert upper-case hex letters into a byte */
83 Low = Char - '7';
84 }
85 else if ( Char >= 'a' && Char <= 'f' )
86 {
87 /* Convert lower-case hex letters into a byte */
88 Low = Char - 'W';
89 }
90 else
91 {
92 /* Invalid string, fail the conversion */
93 return FALSE;
94 }
95
96 /* Combine the high and low byte */
97 Short = High | Low;
98
99 /* If 4 letters have been reached, the 16-bit integer should exist */
100 if (++Length >= 4)
101 {
102 /* Return it to the caller */
103 *Value = Short;
104 return TRUE;
105 }
106 }
107 }
108
109 BOOLEAN
110 NTAPI
111 PciIsSuiteVersion(IN USHORT SuiteMask)
112 {
113 ULONGLONG Mask = 0;
114 RTL_OSVERSIONINFOEXW VersionInfo;
115
116 /* Initialize the version information */
117 RtlZeroMemory(&VersionInfo, sizeof(RTL_OSVERSIONINFOEXW));
118 VersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
119 VersionInfo.wSuiteMask = SuiteMask;
120
121 /* Set the comparison mask and return if the passed suite mask matches */
122 VER_SET_CONDITION(Mask, VER_SUITENAME, VER_AND);
123 return NT_SUCCESS(RtlVerifyVersionInfo(&VersionInfo, VER_SUITENAME, Mask));
124 }
125
126 BOOLEAN
127 NTAPI
128 PciIsDatacenter(VOID)
129 {
130 BOOLEAN Result;
131 PVOID Value;
132 ULONG ResultLength;
133 NTSTATUS Status;
134
135 /* Assume this isn't Datacenter */
136 Result = FALSE;
137
138 /* First, try opening the setup key */
139 Status = PciGetRegistryValue(L"",
140 L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\setupdd",
141 0,
142 REG_BINARY,
143 &Value,
144 &ResultLength);
145 if (!NT_SUCCESS(Status))
146 {
147 /* This is not an in-progress Setup boot, so query the suite version */
148 Result = PciIsSuiteVersion(VER_SUITE_DATACENTER);
149 }
150 else
151 {
152 /* This scenario shouldn't happen yet, since SetupDD isn't used */
153 UNIMPLEMENTED;
154 while (TRUE);
155 }
156
157 /* Return if this is Datacenter or not */
158 return Result;
159 }
160
161 BOOLEAN
162 NTAPI
163 PciOpenKey(IN PWCHAR KeyName,
164 IN HANDLE RootKey,
165 IN ACCESS_MASK DesiredAccess,
166 OUT PHANDLE KeyHandle,
167 OUT PNTSTATUS KeyStatus)
168 {
169 NTSTATUS Status;
170 OBJECT_ATTRIBUTES ObjectAttributes;
171 UNICODE_STRING KeyString;
172 PAGED_CODE();
173
174 /* Initialize the object attributes */
175 RtlInitUnicodeString(&KeyString, KeyName);
176 InitializeObjectAttributes(&ObjectAttributes,
177 &KeyString,
178 OBJ_CASE_INSENSITIVE,
179 RootKey,
180 NULL);
181
182 /* Open the key, returning a boolean, and the status, if requested */
183 Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
184 if (KeyStatus) *KeyStatus = Status;
185 return NT_SUCCESS(Status);
186 }
187
188 NTSTATUS
189 NTAPI
190 PciGetRegistryValue(IN PWCHAR ValueName,
191 IN PWCHAR KeyName,
192 IN HANDLE RootHandle,
193 IN ULONG Type,
194 OUT PVOID *OutputBuffer,
195 OUT PULONG OutputLength)
196 {
197 NTSTATUS Status;
198 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
199 ULONG NeededLength, ActualLength;
200 UNICODE_STRING ValueString;
201 HANDLE KeyHandle;
202 BOOLEAN Result;
203
204 /* So we know what to free at the end of the body */
205 PartialInfo = NULL;
206 KeyHandle = NULL;
207 do
208 {
209 /* Open the key by name, rooted off the handle passed */
210 Result = PciOpenKey(KeyName,
211 RootHandle,
212 KEY_QUERY_VALUE,
213 &KeyHandle,
214 &Status);
215 if (!Result) break;
216
217 /* Query for the size that's needed for the value that was passed in */
218 RtlInitUnicodeString(&ValueString, ValueName);
219 Status = ZwQueryValueKey(KeyHandle,
220 &ValueString,
221 KeyValuePartialInformation,
222 NULL,
223 0,
224 &NeededLength);
225 ASSERT(!NT_SUCCESS(Status));
226 if (Status != STATUS_BUFFER_TOO_SMALL) break;
227
228 /* Allocate an appropriate buffer for the size that was returned */
229 ASSERT(NeededLength != 0);
230 Status = STATUS_INSUFFICIENT_RESOURCES;
231 PartialInfo = ExAllocatePoolWithTag(PagedPool,
232 NeededLength,
233 PCI_POOL_TAG);
234 if (!PartialInfo) break;
235
236 /* Query the actual value information now that the size is known */
237 Status = ZwQueryValueKey(KeyHandle,
238 &ValueString,
239 KeyValuePartialInformation,
240 PartialInfo,
241 NeededLength,
242 &ActualLength);
243 if (!NT_SUCCESS(Status)) break;
244
245 /* Make sure it's of the type that the caller expects */
246 Status = STATUS_INVALID_PARAMETER;
247 if (PartialInfo->Type != Type) break;
248
249 /* Subtract the registry-specific header, to get the data size */
250 ASSERT(NeededLength == ActualLength);
251 NeededLength -= sizeof(KEY_VALUE_PARTIAL_INFORMATION);
252
253 /* Allocate a buffer to hold the data and return it to the caller */
254 Status = STATUS_INSUFFICIENT_RESOURCES;
255 *OutputBuffer = ExAllocatePoolWithTag(PagedPool,
256 NeededLength,
257 PCI_POOL_TAG);
258 if (!*OutputBuffer) break;
259
260 /* Copy the data into the buffer and return its length to the caller */
261 RtlCopyMemory(*OutputBuffer, PartialInfo->Data, NeededLength);
262 if (OutputLength) *OutputLength = NeededLength;
263 Status = STATUS_SUCCESS;
264 } while (0);
265
266 /* Close any opened keys and free temporary allocations */
267 if (KeyHandle) ZwClose(KeyHandle);
268 if (PartialInfo) ExFreePoolWithTag(PartialInfo, 0);
269 return Status;
270 }
271
272 NTSTATUS
273 NTAPI
274 PciBuildDefaultExclusionLists(VOID)
275 {
276 ULONG Start;
277 NTSTATUS Status;
278 ASSERT(PciIsaBitExclusionList.Count == 0);
279 ASSERT(PciVgaAndIsaBitExclusionList.Count == 0);
280
281 /* Initialize the range lists */
282 RtlInitializeRangeList(&PciIsaBitExclusionList);
283 RtlInitializeRangeList(&PciVgaAndIsaBitExclusionList);
284
285 /* Loop x86 I/O ranges */
286 for (Start = 0x100; Start <= 0xFEFF; Start += 0x400)
287 {
288 /* Add the ISA I/O ranges */
289 Status = RtlAddRange(&PciIsaBitExclusionList,
290 Start,
291 Start + 0x2FF,
292 0,
293 RTL_RANGE_LIST_ADD_IF_CONFLICT,
294 NULL,
295 NULL);
296 if (!NT_SUCCESS(Status)) break;
297
298 /* Add the ISA I/O ranges */
299 Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
300 Start,
301 Start + 0x2AF,
302 0,
303 RTL_RANGE_LIST_ADD_IF_CONFLICT,
304 NULL,
305 NULL);
306 if (!NT_SUCCESS(Status)) break;
307
308 /* Add the VGA I/O range for Monochrome Video */
309 Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
310 Start + 0x2BC,
311 Start + 0x2BF,
312 0,
313 RTL_RANGE_LIST_ADD_IF_CONFLICT,
314 NULL,
315 NULL);
316 if (!NT_SUCCESS(Status)) break;
317
318 /* Add the VGA I/O range for certain CGA adapters */
319 Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
320 Start + 0x2E0,
321 Start + 0x2FF,
322 0,
323 RTL_RANGE_LIST_ADD_IF_CONFLICT,
324 NULL,
325 NULL);
326 if (!NT_SUCCESS(Status)) break;
327
328 /* Success, ranges added done */
329 };
330
331 RtlFreeRangeList(&PciIsaBitExclusionList);
332 RtlFreeRangeList(&PciVgaAndIsaBitExclusionList);
333 return Status;
334 }
335
336 PPCI_FDO_EXTENSION
337 NTAPI
338 PciFindParentPciFdoExtension(IN PDEVICE_OBJECT DeviceObject,
339 IN PKEVENT Lock)
340 {
341 PPCI_FDO_EXTENSION FoundExtension;
342
343 /* Assume we'll find nothing */
344 FoundExtension = NULL;
345
346 /* Check if a lock was specified */
347 if (Lock)
348 {
349 /* Wait for the lock to be released */
350 KeEnterCriticalRegion();
351 KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
352 }
353
354 /* Now search for the extension */
355 if (PciFdoExtensionListHead.Next)
356 {
357 /* This case should not be hit yet */
358 UNIMPLEMENTED;
359 while (TRUE);
360 }
361
362 /* Check if we had acquired a lock previously */
363 if (Lock)
364 {
365 /* Release it */
366 KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
367 KeLeaveCriticalRegion();
368 }
369
370 /* Return which extension was found, if any */
371 return FoundExtension;
372 }
373
374 VOID
375 NTAPI
376 PciInsertEntryAtTail(IN PSINGLE_LIST_ENTRY ListHead,
377 IN PPCI_FDO_EXTENSION DeviceExtension,
378 IN PKEVENT Lock)
379 {
380 PSINGLE_LIST_ENTRY NextEntry;
381 PAGED_CODE();
382
383 /* Check if a lock was specified */
384 if (Lock)
385 {
386 /* Wait for the lock to be released */
387 KeEnterCriticalRegion();
388 KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
389 }
390
391 /* Loop the list until we get to the end, then insert this entry there */
392 for (NextEntry = ListHead; NextEntry->Next; NextEntry = NextEntry->Next);
393 NextEntry->Next = &DeviceExtension->List;
394
395 /* Check if we had acquired a lock previously */
396 if (Lock)
397 {
398 /* Release it */
399 KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
400 KeLeaveCriticalRegion();
401 }
402 }
403
404 VOID
405 NTAPI
406 PciInsertEntryAtHead(IN PSINGLE_LIST_ENTRY ListHead,
407 IN PSINGLE_LIST_ENTRY Entry,
408 IN PKEVENT Lock)
409 {
410 PAGED_CODE();
411
412 /* Check if a lock was specified */
413 if (Lock)
414 {
415 /* Wait for the lock to be released */
416 KeEnterCriticalRegion();
417 KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
418 }
419
420 /* Make the entry point to the current head and make the head point to it */
421 Entry->Next = ListHead->Next;
422 ListHead->Next = Entry;
423
424 /* Check if we had acquired a lock previously */
425 if (Lock)
426 {
427 /* Release it */
428 KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
429 KeLeaveCriticalRegion();
430 }
431 }
432
433 VOID
434 NTAPI
435 PcipLinkSecondaryExtension(IN PSINGLE_LIST_ENTRY List,
436 IN PVOID Lock,
437 IN PPCI_SECONDARY_EXTENSION SecondaryExtension,
438 IN PCI_SIGNATURE ExtensionType,
439 IN PVOID Destructor)
440 {
441 PAGED_CODE();
442
443 /* Setup the extension data, and insert it into the primary's list */
444 SecondaryExtension->ExtensionType = ExtensionType;
445 SecondaryExtension->Destructor = Destructor;
446 PciInsertEntryAtHead(List, &SecondaryExtension->List, Lock);
447 }
448
449 NTSTATUS
450 NTAPI
451 PciGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,
452 IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
453 OUT PVOID *OutputBuffer)
454 {
455 NTSTATUS Status;
456 ULONG BufferLength, ResultLength;
457 PVOID Buffer;
458 do
459 {
460 /* Query the requested property size */
461 Status = IoGetDeviceProperty(DeviceObject,
462 DeviceProperty,
463 0,
464 NULL,
465 &BufferLength);
466 if (Status != STATUS_BUFFER_TOO_SMALL)
467 {
468 /* Call should've failed with buffer too small! */
469 DPRINT1("PCI - Unexpected status from GetDeviceProperty, saw %08X, expected %08X.\n",
470 Status,
471 STATUS_BUFFER_TOO_SMALL);
472 *OutputBuffer = NULL;
473 ASSERTMSG(FALSE, "PCI Successfully did the impossible!");
474 break;
475 }
476
477 /* Allocate the required buffer */
478 Buffer = ExAllocatePoolWithTag(PagedPool, BufferLength, 'BicP');
479 if (!Buffer)
480 {
481 /* No memory, fail the request */
482 DPRINT1("PCI - Failed to allocate DeviceProperty buffer (%d bytes).\n", BufferLength);
483 Status = STATUS_INSUFFICIENT_RESOURCES;
484 break;
485 }
486
487 /* Do the actual property query call */
488 Status = IoGetDeviceProperty(DeviceObject,
489 DeviceProperty,
490 BufferLength,
491 Buffer,
492 &ResultLength);
493 if (!NT_SUCCESS(Status)) break;
494
495 /* Return the buffer to the caller */
496 ASSERT(BufferLength == ResultLength);
497 *OutputBuffer = Buffer;
498 return STATUS_SUCCESS;
499 } while (FALSE);
500
501 /* Failure path */
502 return STATUS_UNSUCCESSFUL;
503 }
504
505 NTSTATUS
506 NTAPI
507 PciSendIoctl(IN PDEVICE_OBJECT DeviceObject,
508 IN ULONG IoControlCode,
509 IN PVOID InputBuffer,
510 IN ULONG InputBufferLength,
511 IN PVOID OutputBuffer,
512 IN ULONG OutputBufferLength)
513 {
514 PIRP Irp;
515 NTSTATUS Status;
516 KEVENT Event;
517 IO_STATUS_BLOCK IoStatusBlock;
518 PDEVICE_OBJECT AttachedDevice;
519 PAGED_CODE();
520
521 /* Initialize the pending IRP event */
522 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
523
524 /* Get a reference to the root PDO (ACPI) */
525 AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
526 if (!AttachedDevice) return STATUS_INVALID_PARAMETER;
527
528 /* Build the requested IOCTL IRP */
529 Irp = IoBuildDeviceIoControlRequest(IoControlCode,
530 AttachedDevice,
531 InputBuffer,
532 InputBufferLength,
533 OutputBuffer,
534 OutputBufferLength,
535 0,
536 &Event,
537 &IoStatusBlock);
538 if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
539
540 /* Send the IOCTL to the driver */
541 Status = IoCallDriver(AttachedDevice, Irp);
542 if (Status == STATUS_PENDING)
543 {
544 /* Wait for a response */
545 KeWaitForSingleObject(&Event,
546 Executive,
547 KernelMode,
548 FALSE,
549 NULL);
550 Status = Irp->IoStatus.Status;
551 }
552
553 /* Take away the reference we took and return the result to the caller */
554 ObDereferenceObject(AttachedDevice);
555 return Status;
556 }
557
558 PPCI_SECONDARY_EXTENSION
559 NTAPI
560 PciFindNextSecondaryExtension(IN PSINGLE_LIST_ENTRY ListHead,
561 IN PCI_SIGNATURE ExtensionType)
562 {
563 PSINGLE_LIST_ENTRY NextEntry;
564 PPCI_SECONDARY_EXTENSION Extension;
565
566 /* Scan the list */
567 for (NextEntry = ListHead; NextEntry; NextEntry = NextEntry->Next)
568 {
569 /* Grab each extension and check if it's the one requested */
570 Extension = CONTAINING_RECORD(NextEntry, PCI_SECONDARY_EXTENSION, List);
571 if (Extension->ExtensionType == ExtensionType) return Extension;
572 }
573
574 /* Nothing was found */
575 return NULL;
576 }
577
578 /* EOF */