3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 SCSI class driver routines
28 #pragma alloc_text(PAGE, ClassGetDeviceParameter)
29 #pragma alloc_text(PAGE, ClassScanForSpecial)
30 #pragma alloc_text(PAGE, ClassSetDeviceParameter)
33 // custom string match -- careful!
34 BOOLEAN NTAPI
ClasspMyStringMatches(IN PCSTR StringToMatch OPTIONAL
, IN PCSTR TargetString
)
36 ULONG length
; // strlen returns an int, not size_t (!)
39 // if no match requested, return TRUE
40 if (StringToMatch
== NULL
) {
43 // cache the string length for efficiency
44 length
= strlen(StringToMatch
);
45 // ZERO-length strings may only match zero-length strings
47 return (strlen(TargetString
) == 0);
49 // strncmp returns zero if the strings match
50 return (strncmp(StringToMatch
, TargetString
, length
) == 0);
53 VOID NTAPI
ClassGetDeviceParameter(
54 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
55 IN PWSTR SubkeyName OPTIONAL
,
56 IN PWSTR ParameterName
,
57 IN OUT PULONG ParameterValue
// also default value
61 RTL_QUERY_REGISTRY_TABLE queryTable
[2];
62 HANDLE deviceParameterHandle
;
63 HANDLE deviceSubkeyHandle
;
64 ULONG defaultParameterValue
;
69 // open the given parameter
72 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
73 PLUGPLAY_REGKEY_DEVICE
,
75 &deviceParameterHandle
);
77 if (NT_SUCCESS(status
) && (SubkeyName
!= NULL
)) {
79 UNICODE_STRING subkeyName
;
80 OBJECT_ATTRIBUTES objectAttributes
;
82 RtlInitUnicodeString(&subkeyName
, SubkeyName
);
83 InitializeObjectAttributes(&objectAttributes
,
85 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
86 deviceParameterHandle
,
89 status
= ZwOpenKey(&deviceSubkeyHandle
,
92 if (!NT_SUCCESS(status
)) {
93 ZwClose(deviceParameterHandle
);
98 if (NT_SUCCESS(status
)) {
100 RtlZeroMemory(queryTable
, sizeof(queryTable
));
102 defaultParameterValue
= *ParameterValue
;
104 queryTable
->Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
105 queryTable
->Name
= ParameterName
;
106 queryTable
->EntryContext
= ParameterValue
;
107 queryTable
->DefaultType
= REG_DWORD
;
108 queryTable
->DefaultData
= NULL
;
109 queryTable
->DefaultLength
= 0;
111 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
114 deviceParameterHandle
),
118 if (!NT_SUCCESS(status
)) {
119 *ParameterValue
= defaultParameterValue
; // use default value
123 // close what we open
127 ZwClose(deviceSubkeyHandle
);
130 ZwClose(deviceParameterHandle
);
135 } // end ClassGetDeviceParameter()
137 NTSTATUS NTAPI
ClassSetDeviceParameter(
138 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
139 IN PWSTR SubkeyName OPTIONAL
,
140 IN PWSTR ParameterName
,
141 IN ULONG ParameterValue
)
144 HANDLE deviceParameterHandle
;
145 HANDLE deviceSubkeyHandle
;
150 // open the given parameter
153 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
154 PLUGPLAY_REGKEY_DEVICE
,
155 KEY_READ
| KEY_WRITE
,
156 &deviceParameterHandle
);
158 if (NT_SUCCESS(status
) && (SubkeyName
!= NULL
)) {
160 UNICODE_STRING subkeyName
;
161 OBJECT_ATTRIBUTES objectAttributes
;
163 RtlInitUnicodeString(&subkeyName
, SubkeyName
);
164 InitializeObjectAttributes(&objectAttributes
,
166 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
167 deviceParameterHandle
,
170 status
= ZwCreateKey(&deviceSubkeyHandle
,
171 KEY_READ
| KEY_WRITE
,
174 if (!NT_SUCCESS(status
)) {
175 ZwClose(deviceParameterHandle
);
180 if (NT_SUCCESS(status
)) {
182 status
= RtlWriteRegistryValue(
184 (PWSTR
) (SubkeyName
?
186 deviceParameterHandle
),
193 // close what we open
197 ZwClose(deviceSubkeyHandle
);
200 ZwClose(deviceParameterHandle
);
205 } // end ClassSetDeviceParameter()
208 * ClassScanForSpecial
210 * This routine was written to simplify scanning for special
211 * hardware based upon id strings. it does not check the registry.
214 VOID NTAPI
ClassScanForSpecial(
215 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
216 IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList
[],
217 IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function
)
219 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
;
222 PCSTR productRevision
;
223 CHAR nullString
[] = "";
230 deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
232 if (DeviceList
== NULL
) {
235 if (Function
== NULL
) {
240 // SCSI sets offsets to -1, ATAPI sets to 0. check for both.
243 if (deviceDescriptor
->VendorIdOffset
!= 0 &&
244 deviceDescriptor
->VendorIdOffset
!= -1) {
245 vendorId
= ((PCSTR
)deviceDescriptor
);
246 vendorId
+= deviceDescriptor
->VendorIdOffset
;
248 vendorId
= nullString
;
250 if (deviceDescriptor
->ProductIdOffset
!= 0 &&
251 deviceDescriptor
->ProductIdOffset
!= -1) {
252 productId
= ((PCSTR
)deviceDescriptor
);
253 productId
+= deviceDescriptor
->ProductIdOffset
;
255 productId
= nullString
;
257 if (deviceDescriptor
->VendorIdOffset
!= 0 &&
258 deviceDescriptor
->VendorIdOffset
!= -1) {
259 productRevision
= ((PCSTR
)deviceDescriptor
);
260 productRevision
+= deviceDescriptor
->ProductRevisionOffset
;
262 productRevision
= nullString
;
266 // loop while the device list is valid (not null-filled)
269 for (;(DeviceList
->VendorId
!= NULL
||
270 DeviceList
->ProductId
!= NULL
||
271 DeviceList
->ProductRevision
!= NULL
);DeviceList
++) {
273 if (ClasspMyStringMatches(DeviceList
->VendorId
, vendorId
) &&
274 ClasspMyStringMatches(DeviceList
->ProductId
, productId
) &&
275 ClasspMyStringMatches(DeviceList
->ProductRevision
, productRevision
)
278 DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching "
279 "controller Ven: %s Prod: %s Rev: %s\n",
280 vendorId
, productId
, productRevision
));
283 // pass the context to the call back routine and exit
286 (Function
)(FdoExtension
, DeviceList
->Data
);
289 // for CHK builds, try to prevent wierd stacks by having a debug
290 // print here. it's a hack, but i know of no other way to prevent
291 // the stack from being wrong.
294 DebugPrint((16, "ClasspScanForSpecialByInquiry: "
295 "completed callback\n"));
298 } // else the strings did not match
300 } // none of the devices matched.
302 DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n",
303 FdoExtension
->DeviceObject
));
306 } // end ClasspScanForSpecialByInquiry()
309 // In order to provide better performance without the need to reboot,
310 // we need to implement a self-adjusting method to set and clear the
311 // srb flags based upon current performance.
313 // whenever there is an error, immediately grab the spin lock. the
314 // MP perf hit here is acceptable, since we're in an error path. this
315 // is also neccessary because we are guaranteed to be modifying the
316 // SRB flags here, setting SuccessfulIO to zero, and incrementing the
317 // actual error count (which is always done within this spinlock).
319 // whenever there is no error, increment a counter. if there have been
320 // errors on the device, and we've enabled dynamic perf, *and* we've
321 // just crossed the perf threshhold, then grab the spin lock and
322 // double check that the threshhold has, indeed been hit(*). then
323 // decrement the error count, and if it's dropped sufficiently, undo
324 // some of the safety changes made in the SRB flags due to the errors.
326 // * this works in all cases. even if lots of ios occur after the
327 // previous guy went in and cleared the successfulio counter, that
328 // just means that we've hit the threshhold again, and so it's proper
329 // to run the inner loop again.
334 ClasspPerfIncrementErrorCount(
335 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
338 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
342 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
344 fdoData
->Perf
.SuccessfulIO
= 0; // implicit interlock
345 errors
= InterlockedIncrement((PLONG
)&FdoExtension
->ErrorCount
);
347 if (errors
>= CLASS_ERROR_LEVEL_1
) {
350 // If the error count has exceeded the error limit, then disable
351 // any tagged queuing, multiple requests per lu queueing
352 // and sychronous data transfers.
354 // Clearing the no queue freeze flag prevents the port driver
355 // from sending multiple requests per logical unit.
358 CLEAR_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
359 CLEAR_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_QUEUE_ACTION_ENABLE
);
361 SET_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
363 DebugPrint((ClassDebugError
, "ClasspPerfIncrementErrorCount: "
364 "Too many errors; disabling tagged queuing and "
365 "synchronous data tranfers.\n"));
369 if (errors
>= CLASS_ERROR_LEVEL_2
) {
372 // If a second threshold is reached, disable disconnects.
375 SET_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_DISABLE_DISCONNECT
);
376 DebugPrint((ClassDebugError
, "ClasspPerfIncrementErrorCount: "
377 "Too many errors; disabling disconnects.\n"));
380 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
386 ClasspPerfIncrementSuccessfulIo(
387 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
390 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
396 // don't take a hit from the interlocked op unless we're in
397 // a degraded state and we've got a threshold to hit.
400 if (FdoExtension
->ErrorCount
== 0) {
404 if (fdoData
->Perf
.ReEnableThreshhold
== 0) {
408 succeeded
= InterlockedIncrement((PLONG
)&fdoData
->Perf
.SuccessfulIO
);
409 if (succeeded
< fdoData
->Perf
.ReEnableThreshhold
) {
414 // if we hit the threshold, grab the spinlock and verify we've
415 // actually done so. this allows us to ignore the spinlock 99%
419 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
422 // re-read the value, so we don't run this multiple times
423 // for a single threshhold being hit. this keeps errorcount
427 succeeded
= fdoData
->Perf
.SuccessfulIO
;
429 if ((FdoExtension
->ErrorCount
!= 0) &&
430 (fdoData
->Perf
.ReEnableThreshhold
<= succeeded
)
433 fdoData
->Perf
.SuccessfulIO
= 0; // implicit interlock
435 ASSERT(FdoExtension
->ErrorCount
> 0);
436 errors
= InterlockedDecrement((PLONG
)&FdoExtension
->ErrorCount
);
439 // note: do in reverse order of the sets "just in case"
442 if (errors
< CLASS_ERROR_LEVEL_2
) {
443 if (errors
== CLASS_ERROR_LEVEL_2
- 1) {
444 DebugPrint((ClassDebugError
, "ClasspPerfIncrementSuccessfulIo: "
445 "Error level 2 no longer required.\n"));
447 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
448 SRB_FLAGS_DISABLE_DISCONNECT
)) {
449 CLEAR_FLAG(FdoExtension
->SrbFlags
,
450 SRB_FLAGS_DISABLE_DISCONNECT
);
454 if (errors
< CLASS_ERROR_LEVEL_1
) {
455 if (errors
== CLASS_ERROR_LEVEL_1
- 1) {
456 DebugPrint((ClassDebugError
, "ClasspPerfIncrementSuccessfulIo: "
457 "Error level 1 no longer required.\n"));
459 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
460 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
)) {
461 CLEAR_FLAG(FdoExtension
->SrbFlags
,
462 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
464 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
465 SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
466 SET_FLAG(FdoExtension
->SrbFlags
,
467 SRB_FLAGS_QUEUE_ACTION_ENABLE
);
469 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
470 SRB_FLAGS_NO_QUEUE_FREEZE
)) {
471 SET_FLAG(FdoExtension
->SrbFlags
,
472 SRB_FLAGS_NO_QUEUE_FREEZE
);
475 } // end of threshhold definitely being hit for first time
477 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
481 PMDL NTAPI
BuildDeviceInputMdl(PVOID Buffer
, ULONG BufferLen
)
485 mdl
= IoAllocateMdl(Buffer
, BufferLen
, FALSE
, FALSE
, NULL
);
489 * We are reading from the device.
490 * Therefore, the device is WRITING to the locked memory.
491 * So we request IoWriteAccess.
493 MmProbeAndLockPages(mdl
, KernelMode
, IoWriteAccess
);
495 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
496 NTSTATUS status
= _SEH2_GetExceptionCode();
498 DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status
));
504 DBGWARN(("BuildReadMdl: IoAllocateMdl failed"));
510 VOID NTAPI
FreeDeviceInputMdl(PMDL Mdl
)
518 ClasspPerfResetCounters(
519 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
522 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
525 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
526 DebugPrint((ClassDebugError
, "ClasspPerfResetCounters: "
527 "Resetting all perf counters.\n"));
528 fdoData
->Perf
.SuccessfulIO
= 0;
529 FdoExtension
->ErrorCount
= 0;
531 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
532 SRB_FLAGS_DISABLE_DISCONNECT
)) {
533 CLEAR_FLAG(FdoExtension
->SrbFlags
,
534 SRB_FLAGS_DISABLE_DISCONNECT
);
536 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
537 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
)) {
538 CLEAR_FLAG(FdoExtension
->SrbFlags
,
539 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
541 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
542 SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
543 SET_FLAG(FdoExtension
->SrbFlags
,
544 SRB_FLAGS_QUEUE_ACTION_ENABLE
);
546 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
547 SRB_FLAGS_NO_QUEUE_FREEZE
)) {
548 SET_FLAG(FdoExtension
->SrbFlags
,
549 SRB_FLAGS_NO_QUEUE_FREEZE
);
551 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);