Merge r55012 adding Wine3D control panel as per Amine's request.
[reactos.git] / drivers / storage / classpnp / utils.c
1 /*++
2
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4
5 Module Name:
6
7 utils.c
8
9 Abstract:
10
11 SCSI class driver routines
12
13 Environment:
14
15 kernel mode only
16
17 Notes:
18
19
20 Revision History:
21
22 --*/
23
24 #include "classp.h"
25 #include "debug.h"
26
27
28
29 #ifdef ALLOC_PRAGMA
30 #pragma alloc_text(PAGE, ClassGetDeviceParameter)
31 #pragma alloc_text(PAGE, ClassScanForSpecial)
32 #pragma alloc_text(PAGE, ClassSetDeviceParameter)
33 #endif
34
35
36
37 // custom string match -- careful!
38 BOOLEAN ClasspMyStringMatches(IN PCHAR StringToMatch OPTIONAL, IN PCHAR TargetString)
39 {
40 ULONG length; // strlen returns an int, not size_t (!)
41 PAGED_CODE();
42 ASSERT(TargetString);
43 // if no match requested, return TRUE
44 if (StringToMatch == NULL) {
45 return TRUE;
46 }
47 // cache the string length for efficiency
48 length = strlen(StringToMatch);
49 // ZERO-length strings may only match zero-length strings
50 if (length == 0) {
51 return (strlen(TargetString) == 0);
52 }
53 // strncmp returns zero if the strings match
54 return (strncmp(StringToMatch, TargetString, length) == 0);
55 }
56
57
58 VOID ClassGetDeviceParameter(
59 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
60 IN PWSTR SubkeyName OPTIONAL,
61 IN PWSTR ParameterName,
62 IN OUT PULONG ParameterValue // also default value
63 )
64 {
65 NTSTATUS status;
66 RTL_QUERY_REGISTRY_TABLE queryTable[2];
67 HANDLE deviceParameterHandle;
68 HANDLE deviceSubkeyHandle;
69 ULONG defaultParameterValue;
70
71 PAGED_CODE();
72
73 //
74 // open the given parameter
75 //
76
77 status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
78 PLUGPLAY_REGKEY_DEVICE,
79 KEY_READ,
80 &deviceParameterHandle);
81
82 if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
83
84 UNICODE_STRING subkeyName;
85 OBJECT_ATTRIBUTES objectAttributes;
86
87 RtlInitUnicodeString(&subkeyName, SubkeyName);
88 InitializeObjectAttributes(&objectAttributes,
89 &subkeyName,
90 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
91 deviceParameterHandle,
92 NULL);
93
94 status = ZwOpenKey(&deviceSubkeyHandle,
95 KEY_READ,
96 &objectAttributes);
97 if (!NT_SUCCESS(status)) {
98 ZwClose(deviceParameterHandle);
99 }
100
101 }
102
103 if (NT_SUCCESS(status)) {
104
105 RtlZeroMemory(queryTable, sizeof(queryTable));
106
107 defaultParameterValue = *ParameterValue;
108
109 queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
110 queryTable->Name = ParameterName;
111 queryTable->EntryContext = ParameterValue;
112 queryTable->DefaultType = REG_DWORD;
113 queryTable->DefaultData = NULL;
114 queryTable->DefaultLength = 0;
115
116 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
117 (PWSTR)(SubkeyName ?
118 deviceSubkeyHandle :
119 deviceParameterHandle),
120 queryTable,
121 NULL,
122 NULL);
123 if (!NT_SUCCESS(status)) {
124 *ParameterValue = defaultParameterValue; // use default value
125 }
126
127 //
128 // close what we open
129 //
130
131 if (SubkeyName) {
132 ZwClose(deviceSubkeyHandle);
133 }
134
135 ZwClose(deviceParameterHandle);
136 }
137
138 return;
139
140 } // end ClassGetDeviceParameter()
141
142
143 NTSTATUS ClassSetDeviceParameter(
144 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
145 IN PWSTR SubkeyName OPTIONAL,
146 IN PWSTR ParameterName,
147 IN ULONG ParameterValue)
148 {
149 NTSTATUS status;
150 HANDLE deviceParameterHandle;
151 HANDLE deviceSubkeyHandle;
152
153 PAGED_CODE();
154
155 //
156 // open the given parameter
157 //
158
159 status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
160 PLUGPLAY_REGKEY_DEVICE,
161 KEY_READ | KEY_WRITE,
162 &deviceParameterHandle);
163
164 if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
165
166 UNICODE_STRING subkeyName;
167 OBJECT_ATTRIBUTES objectAttributes;
168
169 RtlInitUnicodeString(&subkeyName, SubkeyName);
170 InitializeObjectAttributes(&objectAttributes,
171 &subkeyName,
172 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
173 deviceParameterHandle,
174 NULL);
175
176 status = ZwCreateKey(&deviceSubkeyHandle,
177 KEY_READ | KEY_WRITE,
178 &objectAttributes,
179 0, NULL, 0, NULL);
180 if (!NT_SUCCESS(status)) {
181 ZwClose(deviceParameterHandle);
182 }
183
184 }
185
186 if (NT_SUCCESS(status)) {
187
188 status = RtlWriteRegistryValue(
189 RTL_REGISTRY_HANDLE,
190 (PWSTR) (SubkeyName ?
191 deviceSubkeyHandle :
192 deviceParameterHandle),
193 ParameterName,
194 REG_DWORD,
195 &ParameterValue,
196 sizeof(ULONG));
197
198 //
199 // close what we open
200 //
201
202 if (SubkeyName) {
203 ZwClose(deviceSubkeyHandle);
204 }
205
206 ZwClose(deviceParameterHandle);
207 }
208
209 return status;
210
211 } // end ClassSetDeviceParameter()
212
213
214 /*
215 * ClassScanForSpecial
216 *
217 * This routine was written to simplify scanning for special
218 * hardware based upon id strings. it does not check the registry.
219 */
220
221 VOID ClassScanForSpecial(
222 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
223 IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList[],
224 IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function)
225 {
226 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
227 PUCHAR vendorId;
228 PUCHAR productId;
229 PUCHAR productRevision;
230 UCHAR nullString[] = "";
231 ULONG j;
232
233 PAGED_CODE();
234 ASSERT(DeviceList);
235 ASSERT(Function);
236
237 deviceDescriptor = FdoExtension->DeviceDescriptor;
238
239 if (DeviceList == NULL) {
240 return;
241 }
242 if (Function == NULL) {
243 return;
244 }
245
246 //
247 // SCSI sets offsets to -1, ATAPI sets to 0. check for both.
248 //
249
250 if (deviceDescriptor->VendorIdOffset != 0 &&
251 deviceDescriptor->VendorIdOffset != -1) {
252 vendorId = ((PUCHAR)deviceDescriptor);
253 vendorId += deviceDescriptor->VendorIdOffset;
254 } else {
255 vendorId = nullString;
256 }
257 if (deviceDescriptor->ProductIdOffset != 0 &&
258 deviceDescriptor->ProductIdOffset != -1) {
259 productId = ((PUCHAR)deviceDescriptor);
260 productId += deviceDescriptor->ProductIdOffset;
261 } else {
262 productId = nullString;
263 }
264 if (deviceDescriptor->VendorIdOffset != 0 &&
265 deviceDescriptor->VendorIdOffset != -1) {
266 productRevision = ((PUCHAR)deviceDescriptor);
267 productRevision += deviceDescriptor->ProductRevisionOffset;
268 } else {
269 productRevision = nullString;
270 }
271
272 //
273 // loop while the device list is valid (not null-filled)
274 //
275
276 for (;(DeviceList->VendorId != NULL ||
277 DeviceList->ProductId != NULL ||
278 DeviceList->ProductRevision != NULL);DeviceList++) {
279
280 if (ClasspMyStringMatches(DeviceList->VendorId, vendorId) &&
281 ClasspMyStringMatches(DeviceList->ProductId, productId) &&
282 ClasspMyStringMatches(DeviceList->ProductRevision, productRevision)
283 ) {
284
285 DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching "
286 "controller Ven: %s Prod: %s Rev: %s\n",
287 vendorId, productId, productRevision));
288
289 //
290 // pass the context to the call back routine and exit
291 //
292
293 (Function)(FdoExtension, DeviceList->Data);
294
295 //
296 // for CHK builds, try to prevent wierd stacks by having a debug
297 // print here. it's a hack, but i know of no other way to prevent
298 // the stack from being wrong.
299 //
300
301 DebugPrint((16, "ClasspScanForSpecialByInquiry: "
302 "completed callback\n"));
303 return;
304
305 } // else the strings did not match
306
307 } // none of the devices matched.
308
309 DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n",
310 FdoExtension->DeviceObject));
311 return;
312
313 } // end ClasspScanForSpecialByInquiry()
314
315
316 //
317 // In order to provide better performance without the need to reboot,
318 // we need to implement a self-adjusting method to set and clear the
319 // srb flags based upon current performance.
320 //
321 // whenever there is an error, immediately grab the spin lock. the
322 // MP perf hit here is acceptable, since we're in an error path. this
323 // is also neccessary because we are guaranteed to be modifying the
324 // SRB flags here, setting SuccessfulIO to zero, and incrementing the
325 // actual error count (which is always done within this spinlock).
326 //
327 // whenever there is no error, increment a counter. if there have been
328 // errors on the device, and we've enabled dynamic perf, *and* we've
329 // just crossed the perf threshhold, then grab the spin lock and
330 // double check that the threshhold has, indeed been hit(*). then
331 // decrement the error count, and if it's dropped sufficiently, undo
332 // some of the safety changes made in the SRB flags due to the errors.
333 //
334 // * this works in all cases. even if lots of ios occur after the
335 // previous guy went in and cleared the successfulio counter, that
336 // just means that we've hit the threshhold again, and so it's proper
337 // to run the inner loop again.
338 //
339
340 VOID
341 ClasspPerfIncrementErrorCount(
342 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
343 )
344 {
345 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
346 KIRQL oldIrql;
347 ULONG errors;
348
349 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
350
351 fdoData->Perf.SuccessfulIO = 0; // implicit interlock
352 errors = InterlockedIncrement(&FdoExtension->ErrorCount);
353
354 if (errors >= CLASS_ERROR_LEVEL_1) {
355
356 //
357 // If the error count has exceeded the error limit, then disable
358 // any tagged queuing, multiple requests per lu queueing
359 // and sychronous data transfers.
360 //
361 // Clearing the no queue freeze flag prevents the port driver
362 // from sending multiple requests per logical unit.
363 //
364
365 CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
366 CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
367
368 SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
369
370 DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: "
371 "Too many errors; disabling tagged queuing and "
372 "synchronous data tranfers.\n"));
373
374 }
375
376 if (errors >= CLASS_ERROR_LEVEL_2) {
377
378 //
379 // If a second threshold is reached, disable disconnects.
380 //
381
382 SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
383 DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: "
384 "Too many errors; disabling disconnects.\n"));
385 }
386
387 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
388 return;
389 }
390
391 VOID
392 ClasspPerfIncrementSuccessfulIo(
393 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
394 )
395 {
396 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
397 KIRQL oldIrql;
398 ULONG errors;
399 ULONG succeeded = 0;
400
401 //
402 // don't take a hit from the interlocked op unless we're in
403 // a degraded state and we've got a threshold to hit.
404 //
405
406 if (FdoExtension->ErrorCount == 0) {
407 return;
408 }
409
410 if (fdoData->Perf.ReEnableThreshhold == 0) {
411 return;
412 }
413
414 succeeded = InterlockedIncrement(&fdoData->Perf.SuccessfulIO);
415 if (succeeded < fdoData->Perf.ReEnableThreshhold) {
416 return;
417 }
418
419 //
420 // if we hit the threshold, grab the spinlock and verify we've
421 // actually done so. this allows us to ignore the spinlock 99%
422 // of the time.
423 //
424
425 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
426
427 //
428 // re-read the value, so we don't run this multiple times
429 // for a single threshhold being hit. this keeps errorcount
430 // somewhat useful.
431 //
432
433 succeeded = fdoData->Perf.SuccessfulIO;
434
435 if ((FdoExtension->ErrorCount != 0) &&
436 (fdoData->Perf.ReEnableThreshhold <= succeeded)
437 ) {
438
439 fdoData->Perf.SuccessfulIO = 0; // implicit interlock
440
441 ASSERT(FdoExtension->ErrorCount > 0);
442 errors = InterlockedDecrement(&FdoExtension->ErrorCount);
443
444 //
445 // note: do in reverse order of the sets "just in case"
446 //
447
448 if (errors < CLASS_ERROR_LEVEL_2) {
449 if (errors == CLASS_ERROR_LEVEL_2 - 1) {
450 DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: "
451 "Error level 2 no longer required.\n"));
452 }
453 if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
454 SRB_FLAGS_DISABLE_DISCONNECT)) {
455 CLEAR_FLAG(FdoExtension->SrbFlags,
456 SRB_FLAGS_DISABLE_DISCONNECT);
457 }
458 }
459
460 if (errors < CLASS_ERROR_LEVEL_1) {
461 if (errors == CLASS_ERROR_LEVEL_1 - 1) {
462 DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: "
463 "Error level 1 no longer required.\n"));
464 }
465 if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
466 SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
467 CLEAR_FLAG(FdoExtension->SrbFlags,
468 SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
469 }
470 if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
471 SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
472 SET_FLAG(FdoExtension->SrbFlags,
473 SRB_FLAGS_QUEUE_ACTION_ENABLE);
474 }
475 if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
476 SRB_FLAGS_NO_QUEUE_FREEZE)) {
477 SET_FLAG(FdoExtension->SrbFlags,
478 SRB_FLAGS_NO_QUEUE_FREEZE);
479 }
480 }
481 } // end of threshhold definitely being hit for first time
482
483 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
484 return;
485 }
486
487
488 PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen)
489 {
490 PMDL mdl;
491
492 mdl = IoAllocateMdl(Buffer, BufferLen, FALSE, FALSE, NULL);
493 if (mdl){
494 _SEH2_TRY {
495 /*
496 * We are reading from the device.
497 * Therefore, the device is WRITING to the locked memory.
498 * So we request IoWriteAccess.
499 */
500 MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess);
501
502 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
503 NTSTATUS status = _SEH2_GetExceptionCode();
504
505 DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status));
506 IoFreeMdl(mdl);
507 mdl = NULL;
508 } _SEH2_END;
509 }
510 else {
511 DBGWARN(("BuildReadMdl: IoAllocateMdl failed"));
512 }
513
514 return mdl;
515 }
516
517
518 VOID FreeDeviceInputMdl(PMDL Mdl)
519 {
520 MmUnlockPages(Mdl);
521 IoFreeMdl(Mdl);
522 }
523
524
525 #if 0
526 VOID
527 ClasspPerfResetCounters(
528 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
529 )
530 {
531 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
532 KIRQL oldIrql;
533
534 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
535 DebugPrint((ClassDebugError, "ClasspPerfResetCounters: "
536 "Resetting all perf counters.\n"));
537 fdoData->Perf.SuccessfulIO = 0;
538 FdoExtension->ErrorCount = 0;
539
540 if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
541 SRB_FLAGS_DISABLE_DISCONNECT)) {
542 CLEAR_FLAG(FdoExtension->SrbFlags,
543 SRB_FLAGS_DISABLE_DISCONNECT);
544 }
545 if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
546 SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
547 CLEAR_FLAG(FdoExtension->SrbFlags,
548 SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
549 }
550 if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
551 SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
552 SET_FLAG(FdoExtension->SrbFlags,
553 SRB_FLAGS_QUEUE_ACTION_ENABLE);
554 }
555 if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
556 SRB_FLAGS_NO_QUEUE_FREEZE)) {
557 SET_FLAG(FdoExtension->SrbFlags,
558 SRB_FLAGS_NO_QUEUE_FREEZE);
559 }
560 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
561 return;
562 }
563 #endif
564
565