3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 SCSI class driver routines
27 #pragma alloc_text(PAGE, ClassGetDeviceParameter)
28 #pragma alloc_text(PAGE, ClassScanForSpecial)
29 #pragma alloc_text(PAGE, ClassSetDeviceParameter)
32 // custom string match -- careful!
33 BOOLEAN NTAPI
ClasspMyStringMatches(IN PCSTR StringToMatch OPTIONAL
, IN PCSTR TargetString
)
35 ULONG length
; // strlen returns an int, not size_t (!)
38 // if no match requested, return TRUE
39 if (StringToMatch
== NULL
) {
42 // cache the string length for efficiency
43 length
= strlen(StringToMatch
);
44 // ZERO-length strings may only match zero-length strings
46 return (strlen(TargetString
) == 0);
48 // strncmp returns zero if the strings match
49 return (strncmp(StringToMatch
, TargetString
, length
) == 0);
52 VOID NTAPI
ClassGetDeviceParameter(
53 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
54 IN PWSTR SubkeyName OPTIONAL
,
55 IN PWSTR ParameterName
,
56 IN OUT PULONG ParameterValue
// also default value
60 RTL_QUERY_REGISTRY_TABLE queryTable
[2];
61 HANDLE deviceParameterHandle
;
62 HANDLE deviceSubkeyHandle
;
63 ULONG defaultParameterValue
;
68 // open the given parameter
71 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
72 PLUGPLAY_REGKEY_DEVICE
,
74 &deviceParameterHandle
);
76 if (NT_SUCCESS(status
) && (SubkeyName
!= NULL
)) {
78 UNICODE_STRING subkeyName
;
79 OBJECT_ATTRIBUTES objectAttributes
;
81 RtlInitUnicodeString(&subkeyName
, SubkeyName
);
82 InitializeObjectAttributes(&objectAttributes
,
84 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
85 deviceParameterHandle
,
88 status
= ZwOpenKey(&deviceSubkeyHandle
,
91 if (!NT_SUCCESS(status
)) {
92 ZwClose(deviceParameterHandle
);
97 if (NT_SUCCESS(status
)) {
99 RtlZeroMemory(queryTable
, sizeof(queryTable
));
101 defaultParameterValue
= *ParameterValue
;
103 queryTable
->Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
104 queryTable
->Name
= ParameterName
;
105 queryTable
->EntryContext
= ParameterValue
;
106 queryTable
->DefaultType
= REG_DWORD
;
107 queryTable
->DefaultData
= NULL
;
108 queryTable
->DefaultLength
= 0;
110 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
113 deviceParameterHandle
),
117 if (!NT_SUCCESS(status
)) {
118 *ParameterValue
= defaultParameterValue
; // use default value
122 // close what we open
126 ZwClose(deviceSubkeyHandle
);
129 ZwClose(deviceParameterHandle
);
134 } // end ClassGetDeviceParameter()
136 NTSTATUS NTAPI
ClassSetDeviceParameter(
137 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
138 IN PWSTR SubkeyName OPTIONAL
,
139 IN PWSTR ParameterName
,
140 IN ULONG ParameterValue
)
143 HANDLE deviceParameterHandle
;
144 HANDLE deviceSubkeyHandle
;
149 // open the given parameter
152 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
153 PLUGPLAY_REGKEY_DEVICE
,
154 KEY_READ
| KEY_WRITE
,
155 &deviceParameterHandle
);
157 if (NT_SUCCESS(status
) && (SubkeyName
!= NULL
)) {
159 UNICODE_STRING subkeyName
;
160 OBJECT_ATTRIBUTES objectAttributes
;
162 RtlInitUnicodeString(&subkeyName
, SubkeyName
);
163 InitializeObjectAttributes(&objectAttributes
,
165 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
166 deviceParameterHandle
,
169 status
= ZwCreateKey(&deviceSubkeyHandle
,
170 KEY_READ
| KEY_WRITE
,
173 if (!NT_SUCCESS(status
)) {
174 ZwClose(deviceParameterHandle
);
179 if (NT_SUCCESS(status
)) {
181 status
= RtlWriteRegistryValue(
183 (PWSTR
) (SubkeyName
?
185 deviceParameterHandle
),
192 // close what we open
196 ZwClose(deviceSubkeyHandle
);
199 ZwClose(deviceParameterHandle
);
204 } // end ClassSetDeviceParameter()
207 * ClassScanForSpecial
209 * This routine was written to simplify scanning for special
210 * hardware based upon id strings. it does not check the registry.
213 VOID NTAPI
ClassScanForSpecial(
214 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
215 IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList
[],
216 IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function
)
218 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
;
221 PCSTR productRevision
;
222 CHAR nullString
[] = "";
229 deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
231 if (DeviceList
== NULL
) {
234 if (Function
== NULL
) {
239 // SCSI sets offsets to -1, ATAPI sets to 0. check for both.
242 if (deviceDescriptor
->VendorIdOffset
!= 0 &&
243 deviceDescriptor
->VendorIdOffset
!= -1) {
244 vendorId
= ((PCSTR
)deviceDescriptor
);
245 vendorId
+= deviceDescriptor
->VendorIdOffset
;
247 vendorId
= nullString
;
249 if (deviceDescriptor
->ProductIdOffset
!= 0 &&
250 deviceDescriptor
->ProductIdOffset
!= -1) {
251 productId
= ((PCSTR
)deviceDescriptor
);
252 productId
+= deviceDescriptor
->ProductIdOffset
;
254 productId
= nullString
;
256 if (deviceDescriptor
->VendorIdOffset
!= 0 &&
257 deviceDescriptor
->VendorIdOffset
!= -1) {
258 productRevision
= ((PCSTR
)deviceDescriptor
);
259 productRevision
+= deviceDescriptor
->ProductRevisionOffset
;
261 productRevision
= nullString
;
265 // loop while the device list is valid (not null-filled)
268 for (;(DeviceList
->VendorId
!= NULL
||
269 DeviceList
->ProductId
!= NULL
||
270 DeviceList
->ProductRevision
!= NULL
);DeviceList
++) {
272 if (ClasspMyStringMatches(DeviceList
->VendorId
, vendorId
) &&
273 ClasspMyStringMatches(DeviceList
->ProductId
, productId
) &&
274 ClasspMyStringMatches(DeviceList
->ProductRevision
, productRevision
)
277 DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching "
278 "controller Ven: %s Prod: %s Rev: %s\n",
279 vendorId
, productId
, productRevision
));
282 // pass the context to the call back routine and exit
285 (Function
)(FdoExtension
, DeviceList
->Data
);
288 // for CHK builds, try to prevent wierd stacks by having a debug
289 // print here. it's a hack, but i know of no other way to prevent
290 // the stack from being wrong.
293 DebugPrint((16, "ClasspScanForSpecialByInquiry: "
294 "completed callback\n"));
297 } // else the strings did not match
299 } // none of the devices matched.
301 DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n",
302 FdoExtension
->DeviceObject
));
305 } // end ClasspScanForSpecialByInquiry()
308 // In order to provide better performance without the need to reboot,
309 // we need to implement a self-adjusting method to set and clear the
310 // srb flags based upon current performance.
312 // whenever there is an error, immediately grab the spin lock. the
313 // MP perf hit here is acceptable, since we're in an error path. this
314 // is also neccessary because we are guaranteed to be modifying the
315 // SRB flags here, setting SuccessfulIO to zero, and incrementing the
316 // actual error count (which is always done within this spinlock).
318 // whenever there is no error, increment a counter. if there have been
319 // errors on the device, and we've enabled dynamic perf, *and* we've
320 // just crossed the perf threshhold, then grab the spin lock and
321 // double check that the threshhold has, indeed been hit(*). then
322 // decrement the error count, and if it's dropped sufficiently, undo
323 // some of the safety changes made in the SRB flags due to the errors.
325 // * this works in all cases. even if lots of ios occur after the
326 // previous guy went in and cleared the successfulio counter, that
327 // just means that we've hit the threshhold again, and so it's proper
328 // to run the inner loop again.
333 ClasspPerfIncrementErrorCount(
334 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
337 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
341 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
343 fdoData
->Perf
.SuccessfulIO
= 0; // implicit interlock
344 errors
= InterlockedIncrement((PLONG
)&FdoExtension
->ErrorCount
);
346 if (errors
>= CLASS_ERROR_LEVEL_1
) {
349 // If the error count has exceeded the error limit, then disable
350 // any tagged queuing, multiple requests per lu queueing
351 // and sychronous data transfers.
353 // Clearing the no queue freeze flag prevents the port driver
354 // from sending multiple requests per logical unit.
357 CLEAR_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
358 CLEAR_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_QUEUE_ACTION_ENABLE
);
360 SET_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
362 DebugPrint((ClassDebugError
, "ClasspPerfIncrementErrorCount: "
363 "Too many errors; disabling tagged queuing and "
364 "synchronous data tranfers.\n"));
368 if (errors
>= CLASS_ERROR_LEVEL_2
) {
371 // If a second threshold is reached, disable disconnects.
374 SET_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_DISABLE_DISCONNECT
);
375 DebugPrint((ClassDebugError
, "ClasspPerfIncrementErrorCount: "
376 "Too many errors; disabling disconnects.\n"));
379 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
385 ClasspPerfIncrementSuccessfulIo(
386 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
389 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
395 // don't take a hit from the interlocked op unless we're in
396 // a degraded state and we've got a threshold to hit.
399 if (FdoExtension
->ErrorCount
== 0) {
403 if (fdoData
->Perf
.ReEnableThreshhold
== 0) {
407 succeeded
= InterlockedIncrement((PLONG
)&fdoData
->Perf
.SuccessfulIO
);
408 if (succeeded
< fdoData
->Perf
.ReEnableThreshhold
) {
413 // if we hit the threshold, grab the spinlock and verify we've
414 // actually done so. this allows us to ignore the spinlock 99%
418 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
421 // re-read the value, so we don't run this multiple times
422 // for a single threshhold being hit. this keeps errorcount
426 succeeded
= fdoData
->Perf
.SuccessfulIO
;
428 if ((FdoExtension
->ErrorCount
!= 0) &&
429 (fdoData
->Perf
.ReEnableThreshhold
<= succeeded
)
432 fdoData
->Perf
.SuccessfulIO
= 0; // implicit interlock
434 ASSERT(FdoExtension
->ErrorCount
> 0);
435 errors
= InterlockedDecrement((PLONG
)&FdoExtension
->ErrorCount
);
438 // note: do in reverse order of the sets "just in case"
441 if (errors
< CLASS_ERROR_LEVEL_2
) {
442 if (errors
== CLASS_ERROR_LEVEL_2
- 1) {
443 DebugPrint((ClassDebugError
, "ClasspPerfIncrementSuccessfulIo: "
444 "Error level 2 no longer required.\n"));
446 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
447 SRB_FLAGS_DISABLE_DISCONNECT
)) {
448 CLEAR_FLAG(FdoExtension
->SrbFlags
,
449 SRB_FLAGS_DISABLE_DISCONNECT
);
453 if (errors
< CLASS_ERROR_LEVEL_1
) {
454 if (errors
== CLASS_ERROR_LEVEL_1
- 1) {
455 DebugPrint((ClassDebugError
, "ClasspPerfIncrementSuccessfulIo: "
456 "Error level 1 no longer required.\n"));
458 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
459 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
)) {
460 CLEAR_FLAG(FdoExtension
->SrbFlags
,
461 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
463 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
464 SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
465 SET_FLAG(FdoExtension
->SrbFlags
,
466 SRB_FLAGS_QUEUE_ACTION_ENABLE
);
468 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
469 SRB_FLAGS_NO_QUEUE_FREEZE
)) {
470 SET_FLAG(FdoExtension
->SrbFlags
,
471 SRB_FLAGS_NO_QUEUE_FREEZE
);
474 } // end of threshhold definitely being hit for first time
476 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
480 PMDL NTAPI
BuildDeviceInputMdl(PVOID Buffer
, ULONG BufferLen
)
484 mdl
= IoAllocateMdl(Buffer
, BufferLen
, FALSE
, FALSE
, NULL
);
488 * We are reading from the device.
489 * Therefore, the device is WRITING to the locked memory.
490 * So we request IoWriteAccess.
492 MmProbeAndLockPages(mdl
, KernelMode
, IoWriteAccess
);
494 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
495 NTSTATUS status
= _SEH2_GetExceptionCode();
497 DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status
));
503 DBGWARN(("BuildReadMdl: IoAllocateMdl failed"));
509 VOID NTAPI
FreeDeviceInputMdl(PMDL Mdl
)
517 ClasspPerfResetCounters(
518 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
521 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
524 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
525 DebugPrint((ClassDebugError
, "ClasspPerfResetCounters: "
526 "Resetting all perf counters.\n"));
527 fdoData
->Perf
.SuccessfulIO
= 0;
528 FdoExtension
->ErrorCount
= 0;
530 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
531 SRB_FLAGS_DISABLE_DISCONNECT
)) {
532 CLEAR_FLAG(FdoExtension
->SrbFlags
,
533 SRB_FLAGS_DISABLE_DISCONNECT
);
535 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
536 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
)) {
537 CLEAR_FLAG(FdoExtension
->SrbFlags
,
538 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
540 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
541 SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
542 SET_FLAG(FdoExtension
->SrbFlags
,
543 SRB_FLAGS_QUEUE_ACTION_ENABLE
);
545 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
546 SRB_FLAGS_NO_QUEUE_FREEZE
)) {
547 SET_FLAG(FdoExtension
->SrbFlags
,
548 SRB_FLAGS_NO_QUEUE_FREEZE
);
550 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);