1 /*++
2
3 Copyright (C) Microsoft Corporation, 1991 - 2010
4
5 Module Name:
6
7 class.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 #define CLASS_INIT_GUID 1
25 #define DEBUG_MAIN_SOURCE 1
26
27 #include "classp.h"
28 #include "debug.h"
29 #include <process.h>
30 #include <devpkey.h>
31 #include <ntiologc.h>
32
33
34 #ifdef DEBUG_USE_WPP
35 #include "class.tmh"
36 #endif
37
38 #ifdef ALLOC_PRAGMA
39 #pragma alloc_text(INIT, DriverEntry)
40 #pragma alloc_text(PAGE, ClassAddDevice)
41 #pragma alloc_text(PAGE, ClassClaimDevice)
42 #pragma alloc_text(PAGE, ClassCreateDeviceObject)
43 #pragma alloc_text(PAGE, ClassDispatchPnp)
44 #pragma alloc_text(PAGE, ClassGetDescriptor)
45 #pragma alloc_text(PAGE, ClassGetPdoId)
46 #pragma alloc_text(PAGE, ClassInitialize)
47 #pragma alloc_text(PAGE, ClassInitializeEx)
48 #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
49 #pragma alloc_text(PAGE, ClassMarkChildMissing)
50 #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
51 #pragma alloc_text(PAGE, ClassModeSense)
52 #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
53 #pragma alloc_text(PAGE, ClassPnpStartDevice)
54 #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
55 #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
56 #pragma alloc_text(PAGE, ClassRemoveDevice)
57 #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
58 #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
59 #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
60 #pragma alloc_text(PAGE, ClassUnload)
61 #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
62 #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
63 #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
64 #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
65 #pragma alloc_text(PAGE, ClasspScanForClassHacks)
66 #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
67 #pragma alloc_text(PAGE, ClasspModeSense)
68 #pragma alloc_text(PAGE, ClasspIsPortable)
69 #pragma alloc_text(PAGE, ClassAcquireChildLock)
70 #pragma alloc_text(PAGE, ClassDetermineTokenOperationCommandSupport)
71 #pragma alloc_text(PAGE, ClassDeviceProcessOffloadRead)
72 #pragma alloc_text(PAGE, ClassDeviceProcessOffloadWrite)
73 #pragma alloc_text(PAGE, ClasspServicePopulateTokenTransferRequest)
74 #pragma alloc_text(PAGE, ClasspServiceWriteUsingTokenTransferRequest)
75 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
76 #pragma alloc_text(PAGE, ClassModeSenseEx)
77 #endif
78 #endif
79
80 #ifdef _MSC_VER
81 #pragma prefast(disable:28159, "There are certain cases when we have to bugcheck...")
82 #endif
83
84 IO_COMPLETION_ROUTINE ClassCheckVerifyComplete;
85
86
87 ULONG ClassPnpAllowUnload = TRUE;
88 ULONG ClassMaxInterleavePerCriticalIo = CLASS_MAX_INTERLEAVE_PER_CRITICAL_IO;
89 CONST LARGE_INTEGER Magic10000 = {{0xe219652c, 0xd1b71758}};
90 GUID StoragePredictFailureDPSGuid = WDI_STORAGE_PREDICT_FAILURE_DPS_GUID;
91
92 #define FirstDriveLetter 'C'
93 #define LastDriveLetter 'Z'
94
95 BOOLEAN UseQPCTime = FALSE;
96
97 //
98 // Keep track of whether security cookie is initialized or not. This is
99 // required by SDL.
100 //
101
102 BOOLEAN InitSecurityCookie = FALSE;
103
104 //
105 // List Identifier for offload data transfer operations
106 //
107 ULONG MaxTokenOperationListIdentifier = MAX_TOKEN_LIST_IDENTIFIERS;
108 volatile ULONG TokenOperationListIdentifier = (ULONG)-1;
109
110 //
111 // List of FDOs that have enabled idle power management.
112 //
113 LIST_ENTRY IdlePowerFDOList = {0};
114 KGUARDED_MUTEX IdlePowerFDOListMutex;
115
116 //
117 // Handle used to register for power setting notifications.
118 //
119 PVOID PowerSettingNotificationHandle;
120
121 //
122 // Handle used to register for screen state setting notifications.
123 //
124 PVOID ScreenStateNotificationHandle;
125
126 //
127 // Disk idle timeout in milliseconds.
128 // We default this to 0xFFFFFFFF as this is what the power manager considers
129 // "never" and ensures we do not set a disk idle timeout until the power
130 // manager calls us back with a different value.
131 //
132 ULONG DiskIdleTimeoutInMS = 0xFFFFFFFF;
133
134
DllUnload(VOID)135 NTSTATUS DllUnload(VOID)
136 {
137 DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_INFO_LEVEL, "classpnp.sys is now unloading\n");
138
139 if (PowerSettingNotificationHandle) {
140 PoUnregisterPowerSettingCallback(PowerSettingNotificationHandle);
141 PowerSettingNotificationHandle = NULL;
142 }
143
144 if (ScreenStateNotificationHandle) {
145 PoUnregisterPowerSettingCallback(ScreenStateNotificationHandle);
146 ScreenStateNotificationHandle = NULL;
147 }
148
149
150 return STATUS_SUCCESS;
151 }
152
153
154 /*++////////////////////////////////////////////////////////////////////////////
155
156 DriverEntry()
157
158 Routine Description:
159
160 Temporary entry point needed to initialize the class system dll.
161 It doesn't do anything.
162
163 Arguments:
164
165 DriverObject - Pointer to the driver object created by the system.
166
167 Return Value:
168
169 STATUS_SUCCESS
170
171 --*/
172 NTSTATUS
173 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)174 DriverEntry(
175 IN PDRIVER_OBJECT DriverObject,
176 IN PUNICODE_STRING RegistryPath
177 )
178 {
179 UNREFERENCED_PARAMETER(DriverObject);
180 UNREFERENCED_PARAMETER(RegistryPath);
181
182 return STATUS_SUCCESS;
183 }
184
185
186
187 /*++////////////////////////////////////////////////////////////////////////////
188
189 ClassInitialize()
190
191 Routine Description:
192
193 This routine is called by a class driver during its
194 DriverEntry routine to initialize the driver.
195
196 Arguments:
197
198 Argument1 - Driver Object.
199 Argument2 - Registry Path.
200 InitializationData - Device-specific driver's initialization data.
201
202 Return Value:
203
204 A valid return code for a DriverEntry routine.
205
206 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)207 _IRQL_requires_max_(PASSIVE_LEVEL)
208 _Must_inspect_result_
209 ULONG
210 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
211 ClassInitialize(
212 _In_ PVOID Argument1,
213 _In_ PVOID Argument2,
214 _In_ PCLASS_INIT_DATA InitializationData
215 )
216 {
217 PDRIVER_OBJECT DriverObject = Argument1;
218 PUNICODE_STRING RegistryPath = Argument2;
219
220 PCLASS_DRIVER_EXTENSION driverExtension;
221
222 NTSTATUS status;
223
224
225
226 PAGED_CODE();
227
228 //
229 // Initialize the security cookie if needed.
230 //
231 #ifndef __REACTOS__
232 if (InitSecurityCookie == FALSE) {
233 __security_init_cookie();
234 InitSecurityCookie = TRUE;
235 }
236 #endif
237
238
239 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "\n\nSCSI Class Driver\n"));
240
241 ClasspInitializeDebugGlobals();
242
243 //
244 // Validate the length of this structure. This is effectively a
245 // version check.
246 //
247
248 if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
249
250 //
251 // This DebugPrint is to help third-party driver writers
252 //
253
254 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class driver wrong version\n"));
255 return (ULONG) STATUS_REVISION_MISMATCH;
256 }
257
258 //
259 // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
260 // are not required entry points.
261 //
262
263 if ((!InitializationData->FdoData.ClassDeviceControl) ||
264 (!((InitializationData->FdoData.ClassReadWriteVerification) ||
265 (InitializationData->ClassStartIo))) ||
266 (!InitializationData->ClassAddDevice) ||
267 (!InitializationData->FdoData.ClassStartDevice)) {
268
269 //
270 // This DebugPrint is to help third-party driver writers
271 //
272
273 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
274 "ClassInitialize: Class device-specific driver missing required "
275 "FDO entry\n"));
276
277 return (ULONG) STATUS_REVISION_MISMATCH;
278 }
279
280 if ((InitializationData->ClassEnumerateDevice) &&
281 ((!InitializationData->PdoData.ClassDeviceControl) ||
282 (!InitializationData->PdoData.ClassStartDevice) ||
283 (!((InitializationData->PdoData.ClassReadWriteVerification) ||
284 (InitializationData->ClassStartIo))))) {
285
286 //
287 // This DebugPrint is to help third-party driver writers
288 //
289
290 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class device-specific missing "
291 "required PDO entry\n"));
292
293 return (ULONG) STATUS_REVISION_MISMATCH;
294 }
295
296 if((InitializationData->FdoData.ClassStopDevice == NULL) ||
297 ((InitializationData->ClassEnumerateDevice != NULL) &&
298 (InitializationData->PdoData.ClassStopDevice == NULL))) {
299
300 //
301 // This DebugPrint is to help third-party driver writers
302 //
303
304 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class device-specific missing "
305 "required PDO entry\n"));
306 NT_ASSERT(FALSE);
307 return (ULONG) STATUS_REVISION_MISMATCH;
308 }
309
310 //
311 // Setup the default power handlers if the class driver didn't provide
312 // any.
313 //
314
315 if(InitializationData->FdoData.ClassPowerDevice == NULL) {
316 InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
317 }
318
319 if((InitializationData->ClassEnumerateDevice != NULL) &&
320 (InitializationData->PdoData.ClassPowerDevice == NULL)) {
321 InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
322 }
323
324 //
325 // warn that unload is not supported
326 //
327 // ISSUE-2000/02/03-peterwie
328 // We should think about making this a fatal error.
329 //
330
331 if(InitializationData->ClassUnload == NULL) {
332
333 //
334 // This DebugPrint is to help third-party driver writers
335 //
336
337 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassInitialize: driver does not support unload %wZ\n",
338 RegistryPath));
339 }
340
341 //
342 // Create an extension for the driver object
343 //
344 #ifdef _MSC_VER
345 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
346 #endif
347 status = IoAllocateDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY, sizeof(CLASS_DRIVER_EXTENSION), (PVOID *)&driverExtension);
348
349 if(NT_SUCCESS(status)) {
350
351 //
352 // Copy the registry path into the driver extension so we can use it later
353 //
354
355 driverExtension->RegistryPath.Length = RegistryPath->Length;
356 driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
357
358 driverExtension->RegistryPath.Buffer =
359 ExAllocatePoolWithTag(PagedPool,
360 RegistryPath->MaximumLength,
361 '1CcS');
362
363 if(driverExtension->RegistryPath.Buffer == NULL) {
364
365 status = STATUS_INSUFFICIENT_RESOURCES;
366 return status;
367 }
368
369 RtlCopyUnicodeString(
370 &(driverExtension->RegistryPath),
371 RegistryPath);
372
373 //
374 // Copy the initialization data into the driver extension so we can reuse
375 // it during our add device routine
376 //
377
378 RtlCopyMemory(
379 &(driverExtension->InitData),
380 InitializationData,
381 sizeof(CLASS_INIT_DATA));
382
383 driverExtension->DeviceCount = 0;
384
385 ClassInitializeDispatchTables(driverExtension);
386
387 } else if (status == STATUS_OBJECT_NAME_COLLISION) {
388
389 //
390 // The extension already exists - get a pointer to it
391 //
392 #ifdef _MSC_VER
393 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
394 #endif
395 driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
396
397 NT_ASSERT(driverExtension != NULL);
398
399 } else {
400
401 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class driver extension could not be "
402 "allocated %lx\n", status));
403 return status;
404 }
405
406
407 //
408 // Update driver object with entry points.
409 //
410
411 #ifdef _MSC_VER
412 #pragma prefast(push)
413 #pragma prefast(disable:28175, "Accessing DRIVER_OBJECT fileds is OK here since this function " \
414 "is supposed to be invoked from DriverEntry only")
415 #endif
416 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassGlobalDispatch;
417 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassGlobalDispatch;
418 DriverObject->MajorFunction[IRP_MJ_READ] = ClassGlobalDispatch;
419 DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassGlobalDispatch;
420 DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassGlobalDispatch;
421 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassGlobalDispatch;
422 DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassGlobalDispatch;
423 DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassGlobalDispatch;
424 DriverObject->MajorFunction[IRP_MJ_PNP] = ClassGlobalDispatch;
425 DriverObject->MajorFunction[IRP_MJ_POWER] = ClassGlobalDispatch;
426 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassGlobalDispatch;
427
428 if (InitializationData->ClassStartIo) {
429 DriverObject->DriverStartIo = ClasspStartIo;
430 }
431
432 if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) {
433 DriverObject->DriverUnload = ClassUnload;
434 } else {
435 DriverObject->DriverUnload = NULL;
436 }
437
438 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
439 #ifdef _MSC_VER
440 #pragma prefast(pop)
441 #endif
442
443
444 //
445 // Register for event tracing
446 //
447 if (driverExtension->EtwHandle == 0) {
448 status = EtwRegister(&StoragePredictFailureDPSGuid,
449 NULL,
450 NULL,
451 &driverExtension->EtwHandle);
452 if (!NT_SUCCESS(status)) {
453 driverExtension->EtwHandle = 0;
454 }
455 WPP_INIT_TRACING(DriverObject, RegistryPath);
456 }
457
458
459 //
460 // Ensure these are only initialized once.
461 //
462 if (IdlePowerFDOList.Flink == NULL) {
463 InitializeListHead(&IdlePowerFDOList);
464 KeInitializeGuardedMutex(&IdlePowerFDOListMutex);
465 }
466
467 status = STATUS_SUCCESS;
468 return status;
469 } // end ClassInitialize()
470
471 /*++////////////////////////////////////////////////////////////////////////////
472
473 ClassInitializeEx()
474
475 Routine Description:
476
477 This routine is allows the caller to do any extra initialization or
478 setup that is not done in ClassInitialize. The operation is
479 controlled by the GUID that is passed and the contents of the Data
480 parameter is dependent upon the GUID.
481
482 This is the list of supported operations:
483
484 GUID_CLASSPNP_QUERY_REGINFOEX == CLASS_QUERY_WMI_REGINFO_EX_LIST
485
486 Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
487 callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
488 former callback allows the driver to specify the name of the
489 mof resource.
490
491 GUID_CLASSPNP_SENSEINFO2 == CLASS_INTERPRET_SENSE_INFO2
492
493 Initialize classpnp to callback into class drive for interpretation
494 of all sense info, and to indicate the count of "history" to keep
495 for each packet.
496
497 GUID_CLASSPNP_WORKING_SET == CLASS_WORKING_SET
498
499 Allow class driver to override the min and max working set transfer
500 packet value used in classpnp.
501
502 GUID_CLASSPNP_SRB_SUPPORT == ULONG
503
504 Allow class driver to provide supported SRB types.
505
506 Arguments:
507
508 DriverObject
509 Guid
510 Data
511
512 Return Value:
513
514 Status Code
515
516 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)517 _IRQL_requires_max_(PASSIVE_LEVEL)
518 _Must_inspect_result_
519 ULONG
520 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
521 ClassInitializeEx(
522 _In_ PDRIVER_OBJECT DriverObject,
523 _In_ LPGUID Guid,
524 _In_ PVOID Data
525 )
526 {
527 PCLASS_DRIVER_EXTENSION driverExtension;
528
529 NTSTATUS status;
530
531 PAGED_CODE();
532
533 #ifdef _MSC_VER
534 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
535 #endif
536 driverExtension = IoGetDriverObjectExtension( DriverObject, CLASS_DRIVER_EXTENSION_KEY );
537
538 if (driverExtension == NULL)
539 {
540 NT_ASSERT(FALSE);
541 return (ULONG)STATUS_UNSUCCESSFUL;
542 }
543
544 if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
545 {
546 PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
547
548 //
549 // Indicate the device supports PCLASS_QUERY_REGINFO_EX
550 // callback instead of PCLASS_QUERY_REGINFO callback.
551 //
552 List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
553
554 if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
555 {
556 driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
557 driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
558 status = STATUS_SUCCESS;
559 } else {
560 status = STATUS_INVALID_PARAMETER;
561 }
562 }
563 else if (IsEqualGUID(Guid, &ClassGuidWorkingSet))
564 {
565 PCLASS_WORKING_SET infoOriginal = (PCLASS_WORKING_SET)Data;
566 PCLASS_WORKING_SET info = NULL;
567
568 // only try to allocate memory for cached copy if size is correct
569 if (infoOriginal->Size != sizeof(CLASS_WORKING_SET))
570 {
571 // incorrect size -- client programming error
572 status = STATUS_INVALID_PARAMETER;
573 }
574 else
575 {
576 info = ExAllocatePoolWithTag(NonPagedPoolNx,
577 sizeof(CLASS_WORKING_SET),
578 CLASS_TAG_WORKING_SET
579 );
580 if (info == NULL)
581 {
582 status = STATUS_INSUFFICIENT_RESOURCES;
583 }
584 else
585 {
586 // cache the structure internally
587 RtlCopyMemory(info, infoOriginal, sizeof(CLASS_WORKING_SET));
588 status = STATUS_SUCCESS;
589 }
590 }
591 // if we successfully cached a copy, validate all the data within
592 if (NT_SUCCESS(status))
593 {
594 if (info->Size != sizeof(CLASS_WORKING_SET))
595 {
596 // incorrect size -- client programming error
597 status = STATUS_INVALID_PARAMETER;
598 }
599 else if (info->XferPacketsWorkingSetMaximum > CLASS_WORKING_SET_MAXIMUM)
600 {
601 // too many requested in the working set
602 status = STATUS_INVALID_PARAMETER;
603 }
604 else if (info->XferPacketsWorkingSetMinimum > CLASS_WORKING_SET_MAXIMUM)
605 {
606 // too many requested in the working set
607 status = STATUS_INVALID_PARAMETER;
608 }
609 else if (driverExtension->InitData.FdoData.DeviceType != FILE_DEVICE_CD_ROM)
610 {
611 // classpnp developer wants to restrict this code path
612 // for now to CDROM devices only.
613 status = STATUS_INVALID_DEVICE_REQUEST;
614 }
615 else if (driverExtension->WorkingSet != NULL)
616 {
617 // not allowed to change it once it is set for a driver
618 status = STATUS_INVALID_PARAMETER;
619 NT_ASSERT(FALSE);
620 }
621 }
622 // save results or cleanup
623 if (NT_SUCCESS(status))
624 {
625 driverExtension->WorkingSet = info; info = NULL;
626 }
627 else
628 {
629 FREE_POOL( info );
630 }
631 }
632 else if (IsEqualGUID(Guid, &ClassGuidSenseInfo2))
633 {
634 PCLASS_INTERPRET_SENSE_INFO2 infoOriginal = (PCLASS_INTERPRET_SENSE_INFO2)Data;
635 PCLASS_INTERPRET_SENSE_INFO2 info = NULL;
636
637 // only try to allocate memory for cached copy if size is correct
638 if (infoOriginal->Size != sizeof(CLASS_INTERPRET_SENSE_INFO2))
639 {
640 // incorrect size -- client programming error
641 status = STATUS_INVALID_PARAMETER;
642 }
643 else
644 {
645 info = ExAllocatePoolWithTag(NonPagedPoolNx,
646 sizeof(CLASS_INTERPRET_SENSE_INFO2),
647 CLASS_TAG_SENSE2
648 );
649 if (info == NULL)
650 {
651 status = STATUS_INSUFFICIENT_RESOURCES;
652 }
653 else
654 {
655 // cache the structure internally
656 RtlCopyMemory(info, infoOriginal, sizeof(CLASS_INTERPRET_SENSE_INFO2));
657 status = STATUS_SUCCESS;
658 }
659 }
660
661 // if we successfully cached a copy, validate all the data within
662 if (NT_SUCCESS(status))
663 {
664 if (info->Size != sizeof(CLASS_INTERPRET_SENSE_INFO2))
665 {
666 // incorrect size -- client programming error
667 status = STATUS_INVALID_PARAMETER;
668 }
669 else if (info->HistoryCount > CLASS_INTERPRET_SENSE_INFO2_MAXIMUM_HISTORY_COUNT)
670 {
671 // incorrect count -- client programming error
672 status = STATUS_INVALID_PARAMETER;
673 }
674 else if (info->Compress == NULL)
675 {
676 // Compression of the history is required to be supported
677 status = STATUS_INVALID_PARAMETER;
678 }
679 else if (info->HistoryCount == 0)
680 {
681 // History count cannot be zero
682 status = STATUS_INVALID_PARAMETER;
683 }
684 else if (info->Interpret == NULL)
685 {
686 // Updated interpret sense info function is required
687 status = STATUS_INVALID_PARAMETER;
688 }
689 else if (driverExtension->InitData.FdoData.DeviceType != FILE_DEVICE_CD_ROM)
690 {
691 // classpnp developer wants to restrict this code path
692 // for now to CDROM devices only.
693 status = STATUS_INVALID_DEVICE_REQUEST;
694 }
695 else if (driverExtension->InterpretSenseInfo != NULL)
696 {
697 // not allowed to change it once it is set for a driver
698 status = STATUS_INVALID_PARAMETER;
699 NT_ASSERT(FALSE);
700 }
701 }
702
703 // save results or cleanup
704 if (NT_SUCCESS(status))
705 {
706 driverExtension->InterpretSenseInfo = info; info = NULL;
707 }
708 else
709 {
710 FREE_POOL( info );
711 }
712 }
713 else if (IsEqualGUID(Guid, &ClassGuidSrbSupport))
714 {
715 ULONG srbSupport = *((PULONG)Data);
716
717 //
718 // Validate that at least one of the supported bit flags is set. Assume
719 // all class drivers support SCSI_REQUEST_BLOCK as a class driver that
720 // supports only extended SRB is not feasible.
721 //
722 if ((srbSupport &
723 (CLASS_SRB_SCSI_REQUEST_BLOCK | CLASS_SRB_STORAGE_REQUEST_BLOCK)) != 0) {
724 driverExtension->SrbSupport = srbSupport;
725 status = STATUS_SUCCESS;
726
727 //
728 // Catch cases of a class driver reporting only extended SRB support
729 //
730 if ((driverExtension->SrbSupport & CLASS_SRB_SCSI_REQUEST_BLOCK) == 0) {
731 NT_ASSERT(FALSE);
732 }
733 } else {
734 status = STATUS_INVALID_PARAMETER;
735 }
736 }
737 else
738 {
739 status = STATUS_NOT_SUPPORTED;
740 }
741
742 return status;
743
744 } // end ClassInitializeEx()
745
746 /*++////////////////////////////////////////////////////////////////////////////
747
748 ClassUnload()
749
750 Routine Description:
751
752 called when there are no more references to the driver. this allows
753 drivers to be updated without rebooting.
754
755 Arguments:
756
757 DriverObject - a pointer to the driver object that is being unloaded
758
759 Status:
760
761 --*/
762 VOID
763 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassUnload(IN PDRIVER_OBJECT DriverObject)764 ClassUnload(
765 IN PDRIVER_OBJECT DriverObject
766 )
767 {
768 PCLASS_DRIVER_EXTENSION driverExtension;
769
770 PAGED_CODE();
771
772 NT_ASSERT( DriverObject->DeviceObject == NULL );
773
774 #ifdef _MSC_VER
775 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
776 #endif
777 driverExtension = IoGetDriverObjectExtension( DriverObject, CLASS_DRIVER_EXTENSION_KEY );
778
779
780 if (driverExtension == NULL)
781 {
782 NT_ASSERT(FALSE);
783 return;
784 }
785
786 NT_ASSERT(driverExtension->RegistryPath.Buffer != NULL);
787 NT_ASSERT(driverExtension->InitData.ClassUnload != NULL);
788
789 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassUnload: driver unloading %wZ\n",
790 &driverExtension->RegistryPath));
791
792 //
793 // attempt to process the driver's unload routine first.
794 //
795
796 driverExtension->InitData.ClassUnload(DriverObject);
797
798 //
799 // free own allocated resources and return
800 //
801
802 FREE_POOL( driverExtension->WorkingSet );
803 FREE_POOL( driverExtension->InterpretSenseInfo );
804 FREE_POOL( driverExtension->RegistryPath.Buffer );
805 driverExtension->RegistryPath.Length = 0;
806 driverExtension->RegistryPath.MaximumLength = 0;
807
808
809 //
810 // Unregister ETW
811 //
812 if (driverExtension->EtwHandle != 0) {
813 EtwUnregister(driverExtension->EtwHandle);
814 driverExtension->EtwHandle = 0;
815
816 WPP_CLEANUP(DriverObject);
817 }
818
819
820 return;
821 } // end ClassUnload()
822
823 /*++////////////////////////////////////////////////////////////////////////////
824
825 ClassAddDevice()
826
827 Routine Description:
828
829 SCSI class driver add device routine. This is called by pnp when a new
830 physical device come into being.
831
832 This routine will call out to the class driver to verify that it should
833 own this device then will create and attach a device object and then hand
834 it to the driver to initialize and create symbolic links
835
836 Arguments:
837
838 DriverObject - a pointer to the driver object that this is being created for
839 PhysicalDeviceObject - a pointer to the physical device object
840
841 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
842 STATUS_SUCCESS if the creation and attachment was successful
843 status of device creation and initialization
844
845 --*/
846 NTSTATUS
847 #ifdef _MSC_VER
848 #pragma prefast(suppress:28152, "We expect the class driver to clear the DO_DEVICE_INITIALIZING flag in its AddDevice routine.")
849 #endif
850 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassAddDevice(IN PDRIVER_OBJECT DriverObject,IN PDEVICE_OBJECT PhysicalDeviceObject)851 ClassAddDevice(
852 IN PDRIVER_OBJECT DriverObject,
853 IN PDEVICE_OBJECT PhysicalDeviceObject
854 )
855 {
856 #ifdef _MSC_VER
857 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
858 #endif
859 PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
860
861 NTSTATUS status;
862
863 PAGED_CODE();
864
865
866 status = driverExtension->InitData.ClassAddDevice(DriverObject,
867 PhysicalDeviceObject);
868
869 return status;
870 } // end ClassAddDevice()
871
872 /*++////////////////////////////////////////////////////////////////////////////
873
874 ClassDispatchPnp()
875
876 Routine Description:
877
878 Storage class driver pnp routine. This is called by the io system when
879 a PNP request is sent to the device.
880
881 Arguments:
882
883 DeviceObject - pointer to the device object
884
885 Irp - pointer to the io request packet
886
887 Return Value:
888
889 status
890
891 --*/
892 NTSTATUS
893 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassDispatchPnp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)894 ClassDispatchPnp(
895 IN PDEVICE_OBJECT DeviceObject,
896 IN PIRP Irp
897 )
898 {
899 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
900 BOOLEAN isFdo = commonExtension->IsFdo;
901
902 PCLASS_DRIVER_EXTENSION driverExtension;
903 PCLASS_INIT_DATA initData;
904 PCLASS_DEV_INFO devInfo;
905
906 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
907
908 NTSTATUS status = Irp->IoStatus.Status;
909 BOOLEAN completeRequest = TRUE;
910 BOOLEAN lockReleased = FALSE;
911
912
913 PAGED_CODE();
914
915 //
916 // Extract all the useful information out of the driver object
917 // extension
918 //
919
920 #ifdef _MSC_VER
921 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
922 #endif
923 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
924
925 if (driverExtension){
926
927 initData = &(driverExtension->InitData);
928
929 if(isFdo) {
930 devInfo = &(initData->FdoData);
931 } else {
932 devInfo = &(initData->PdoData);
933 }
934
935 ClassAcquireRemoveLock(DeviceObject, Irp);
936
937 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
938 DeviceObject, Irp,
939 irpStack->MinorFunction,
940 isFdo ? "fdo" : "pdo",
941 DeviceObject));
942 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
943 DeviceObject, Irp,
944 commonExtension->PreviousState,
945 commonExtension->CurrentState));
946
947
948 switch(irpStack->MinorFunction) {
949
950 case IRP_MN_START_DEVICE: {
951
952 //
953 // if this is sent to the FDO we should forward it down the
954 // attachment chain before we start the FDO.
955 //
956
957 if (isFdo) {
958 status = ClassForwardIrpSynchronous(commonExtension, Irp);
959 }
960 else {
961 status = STATUS_SUCCESS;
962 }
963
964 if (NT_SUCCESS(status)){
965 status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
966 }
967
968 break;
969 }
970
971
972 case IRP_MN_QUERY_DEVICE_RELATIONS: {
973
974 DEVICE_RELATION_TYPE type =
975 irpStack->Parameters.QueryDeviceRelations.Type;
976
977 PDEVICE_RELATIONS deviceRelations = NULL;
978
979
980 if(!isFdo) {
981
982 if(type == TargetDeviceRelation) {
983
984 //
985 // Device relations has one entry built in to it's size.
986 //
987
988 status = STATUS_INSUFFICIENT_RESOURCES;
989
990 deviceRelations = ExAllocatePoolWithTag(PagedPool,
991 sizeof(DEVICE_RELATIONS),
992 '2CcS');
993
994 if(deviceRelations != NULL) {
995
996 RtlZeroMemory(deviceRelations,
997 sizeof(DEVICE_RELATIONS));
998
999 Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
1000
1001 deviceRelations->Count = 1;
1002 deviceRelations->Objects[0] = DeviceObject;
1003 ObReferenceObject(deviceRelations->Objects[0]);
1004
1005 status = STATUS_SUCCESS;
1006 }
1007
1008 } else {
1009 //
1010 // PDO's just complete enumeration requests without altering
1011 // the status.
1012 //
1013
1014 status = Irp->IoStatus.Status;
1015 }
1016
1017 break;
1018
1019 } else if (type == BusRelations) {
1020
1021 NT_ASSERT(commonExtension->IsInitialized);
1022
1023 //
1024 // Make sure we support enumeration
1025 //
1026
1027 if(initData->ClassEnumerateDevice == NULL) {
1028
1029 //
1030 // Just send the request down to the lower driver. Perhaps
1031 // It can enumerate children.
1032 //
1033
1034 } else {
1035
1036 //
1037 // Re-enumerate the device
1038 //
1039
1040 status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
1041
1042 if(!NT_SUCCESS(status)) {
1043 completeRequest = TRUE;
1044 break;
1045 }
1046 }
1047 }
1048
1049
1050 IoCopyCurrentIrpStackLocationToNext(Irp);
1051 ClassReleaseRemoveLock(DeviceObject, Irp);
1052 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1053 completeRequest = FALSE;
1054
1055 break;
1056 }
1057
1058 case IRP_MN_QUERY_ID: {
1059
1060 BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
1061 UNICODE_STRING unicodeString;
1062
1063
1064 if(isFdo) {
1065
1066
1067 //
1068 // FDO's should just forward the query down to the lower
1069 // device objects
1070 //
1071
1072 IoCopyCurrentIrpStackLocationToNext(Irp);
1073 ClassReleaseRemoveLock(DeviceObject, Irp);
1074
1075 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1076 completeRequest = FALSE;
1077 break;
1078 }
1079
1080 //
1081 // PDO's need to give an answer - this is easy for now
1082 //
1083
1084 RtlInitUnicodeString(&unicodeString, NULL);
1085
1086 status = ClassGetPdoId(DeviceObject,
1087 idType,
1088 &unicodeString);
1089
1090 if(status == STATUS_NOT_IMPLEMENTED) {
1091 //
1092 // The driver doesn't implement this ID (whatever it is).
1093 // Use the status out of the IRP so that we don't mangle a
1094 // response from someone else.
1095 //
1096
1097 status = Irp->IoStatus.Status;
1098 } else if(NT_SUCCESS(status)) {
1099 Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
1100 } else {
1101 Irp->IoStatus.Information = (ULONG_PTR) NULL;
1102 }
1103
1104 break;
1105 }
1106
1107 case IRP_MN_QUERY_STOP_DEVICE:
1108 case IRP_MN_QUERY_REMOVE_DEVICE: {
1109
1110
1111 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
1112 DeviceObject, Irp,
1113 ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
1114 "STOP" : "REMOVE")));
1115
1116 //
1117 // If this device is in use for some reason (paging, etc...)
1118 // then we need to fail the request.
1119 //
1120
1121 if(commonExtension->PagingPathCount != 0) {
1122
1123 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): device is in paging "
1124 "path and cannot be removed\n",
1125 DeviceObject, Irp));
1126 status = STATUS_DEVICE_BUSY;
1127 break;
1128 }
1129
1130
1131 //
1132 // Check with the class driver to see if the query operation
1133 // can succeed.
1134 //
1135
1136 if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
1137 status = devInfo->ClassStopDevice(DeviceObject,
1138 irpStack->MinorFunction);
1139 } else {
1140 status = devInfo->ClassRemoveDevice(DeviceObject,
1141 irpStack->MinorFunction);
1142 }
1143
1144 if(NT_SUCCESS(status)) {
1145
1146 //
1147 // ASSERT that we never get two queries in a row, as
1148 // this will severly mess up the state machine
1149 //
1150 NT_ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
1151 commonExtension->PreviousState = commonExtension->CurrentState;
1152 commonExtension->CurrentState = irpStack->MinorFunction;
1153
1154 if(isFdo) {
1155 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
1156 "%s irp\n", DeviceObject, Irp,
1157 ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
1158 "STOP" : "REMOVE")));
1159 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1160 }
1161 }
1162
1163
1164 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Final status == %x\n",
1165 DeviceObject, Irp, status));
1166
1167 break;
1168 }
1169
1170 case IRP_MN_CANCEL_STOP_DEVICE:
1171 case IRP_MN_CANCEL_REMOVE_DEVICE: {
1172
1173
1174 //
1175 // Check with the class driver to see if the query or cancel
1176 // operation can succeed.
1177 //
1178
1179 if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
1180 status = devInfo->ClassStopDevice(DeviceObject,
1181 irpStack->MinorFunction);
1182 NT_ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should never be failed\n", NT_SUCCESS(status));
1183 } else {
1184 status = devInfo->ClassRemoveDevice(DeviceObject,
1185 irpStack->MinorFunction);
1186 NT_ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should never be failed\n", NT_SUCCESS(status));
1187 }
1188
1189 Irp->IoStatus.Status = status;
1190
1191 //
1192 // We got a CANCEL - roll back to the previous state only
1193 // if the current state is the respective QUERY state.
1194 //
1195
1196 if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
1197 (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
1198 ) ||
1199 ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
1200 (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
1201 )
1202 ) {
1203
1204 commonExtension->CurrentState =
1205 commonExtension->PreviousState;
1206 commonExtension->PreviousState = 0xff;
1207
1208 }
1209
1210
1211 if(isFdo) {
1212
1213
1214 IoCopyCurrentIrpStackLocationToNext(Irp);
1215 ClassReleaseRemoveLock(DeviceObject, Irp);
1216 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1217 completeRequest = FALSE;
1218 } else {
1219 status = STATUS_SUCCESS;
1220 }
1221
1222 break;
1223 }
1224
1225 case IRP_MN_STOP_DEVICE: {
1226
1227
1228 //
1229 // These all mean nothing to the class driver currently. The
1230 // port driver will handle all queueing when necessary.
1231 //
1232
1233 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
1234 DeviceObject, Irp,
1235 (isFdo ? "fdo" : "pdo")
1236 ));
1237
1238 NT_ASSERT(commonExtension->PagingPathCount == 0);
1239
1240 //
1241 // ISSUE-2000/02/03-peterwie
1242 // if we stop the timer here then it means no class driver can
1243 // do i/o in its ClassStopDevice routine. This is because the
1244 // retry (among other things) is tied into the tick handler
1245 // and disabling retries could cause the class driver to deadlock.
1246 // Currently no class driver we're aware of issues i/o in its
1247 // Stop routine but this is a case we may want to defend ourself
1248 // against.
1249 //
1250
1251 ClasspDisableTimer((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension);
1252
1253
1254 status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
1255
1256 NT_ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should never be failed\n", NT_SUCCESS(status));
1257
1258 if(isFdo) {
1259 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1260 }
1261
1262 if(NT_SUCCESS(status)) {
1263 commonExtension->CurrentState = irpStack->MinorFunction;
1264 commonExtension->PreviousState = 0xff;
1265 }
1266
1267
1268 break;
1269 }
1270
1271 case IRP_MN_REMOVE_DEVICE:
1272 case IRP_MN_SURPRISE_REMOVAL: {
1273 UCHAR removeType = irpStack->MinorFunction;
1274
1275 //
1276 // Log a sytem event when non-removable disks are surprise-removed.
1277 //
1278 if (isFdo &&
1279 (removeType == IRP_MN_SURPRISE_REMOVAL)) {
1280
1281 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1282 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
1283 BOOLEAN logSurpriseRemove = TRUE;
1284 STORAGE_BUS_TYPE busType = fdoExtension->DeviceDescriptor->BusType;
1285
1286 //
1287 // Don't log an event for VHDs
1288 //
1289 if (busType == BusTypeFileBackedVirtual) {
1290 logSurpriseRemove = FALSE;
1291
1292 } else if (fdoData->HotplugInfo.MediaRemovable) {
1293 logSurpriseRemove = FALSE;
1294
1295 } else if (fdoData->HotplugInfo.DeviceHotplug && ( busType == BusTypeUsb || busType == BusType1394)) {
1296
1297 /*
1298 This device is reported as DeviceHotplug but since the busType is Usb or 1394, don't log an event
1299 Note that some storage arrays may report DeviceHotplug and we would like to log an event in those cases
1300 */
1301
1302 logSurpriseRemove = FALSE;
1303 }
1304
1305 if (logSurpriseRemove) {
1306
1307 ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_WARNING_DISK_SURPRISE_REMOVED);
1308 }
1309
1310 }
1311
1312 if (commonExtension->PagingPathCount != 0) {
1313 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
1314 }
1315
1316 //
1317 // Release the lock for this IRP before calling in.
1318 //
1319 ClassReleaseRemoveLock(DeviceObject, Irp);
1320 lockReleased = TRUE;
1321
1322 /*
1323 * Set IsRemoved before propagating the REMOVE down the stack.
1324 * This keeps class-initiated I/O (e.g. the MCN irp) from getting sent
1325 * after we propagate the remove.
1326 */
1327 commonExtension->IsRemoved = REMOVE_PENDING;
1328
1329 /*
1330 * If a timer was started on the device, stop it.
1331 */
1332 ClasspDisableTimer((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension);
1333
1334 /*
1335 * "Fire-and-forget" the remove irp to the lower stack.
1336 * Don't touch the irp (or the irp stack!) after this.
1337 */
1338 if (isFdo) {
1339
1340
1341 IoCopyCurrentIrpStackLocationToNext(Irp);
1342 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1343 NT_ASSERT(NT_SUCCESS(status));
1344 completeRequest = FALSE;
1345 }
1346 else {
1347 status = STATUS_SUCCESS;
1348 }
1349
1350 /*
1351 * Do our own cleanup and call the class driver's remove
1352 * cleanup routine.
1353 * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
1354 * so don't touch the extension after this.
1355 */
1356 commonExtension->PreviousState = commonExtension->CurrentState;
1357 commonExtension->CurrentState = removeType;
1358 ClassRemoveDevice(DeviceObject, removeType);
1359
1360 break;
1361 }
1362
1363 case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
1364
1365 DEVICE_USAGE_NOTIFICATION_TYPE type = irpStack->Parameters.UsageNotification.Type;
1366 BOOLEAN setPagable;
1367
1368
1369 switch(type) {
1370
1371 case DeviceUsageTypePaging: {
1372
1373 if ((irpStack->Parameters.UsageNotification.InPath) &&
1374 (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
1375
1376 //
1377 // Device isn't started. Don't allow adding a
1378 // paging file, but allow a removal of one.
1379 //
1380
1381 status = STATUS_DEVICE_NOT_READY;
1382 break;
1383 }
1384
1385 NT_ASSERT(commonExtension->IsInitialized);
1386
1387 /*
1388 * Ensure that this user thread is not suspended while we are holding the PathCountEvent.
1389 */
1390 KeEnterCriticalRegion();
1391
1392 (VOID)KeWaitForSingleObject(&commonExtension->PathCountEvent,
1393 Executive, KernelMode,
1394 FALSE, NULL);
1395 status = STATUS_SUCCESS;
1396
1397 //
1398 // If the volume is removable we should try to lock it in
1399 // place or unlock it once per paging path count
1400 //
1401
1402 if (commonExtension->IsFdo){
1403 status = ClasspEjectionControl(
1404 DeviceObject,
1405 Irp,
1406 InternalMediaLock,
1407 (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
1408 }
1409
1410 if (!NT_SUCCESS(status)){
1411 KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
1412 KeLeaveCriticalRegion();
1413 break;
1414 }
1415
1416 //
1417 // if removing last paging device, need to set DO_POWER_PAGABLE
1418 // bit here, and possible re-set it below on failure.
1419 //
1420
1421 setPagable = FALSE;
1422
1423 if ((!irpStack->Parameters.UsageNotification.InPath) &&
1424 (commonExtension->PagingPathCount == 1)) {
1425
1426 //
1427 // removing last paging file
1428 // must have DO_POWER_PAGABLE bits set, but only
1429 // if none set the DO_POWER_INRUSH bit and no other special files
1430 //
1431
1432 if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1433 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
1434 "paging file removed, but "
1435 "DO_POWER_INRUSH was set, so NOT "
1436 "setting DO_POWER_PAGABLE\n",
1437 DeviceObject, Irp));
1438 } else if ((commonExtension->HibernationPathCount == 0) &&
1439 (commonExtension->DumpPathCount == 0)) {
1440 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
1441 "paging file removed, "
1442 "setting DO_POWER_PAGABLE\n",
1443 DeviceObject, Irp));
1444 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1445 setPagable = TRUE;
1446 }
1447
1448 }
1449
1450 //
1451 // forward the irp before finishing handling the
1452 // special cases
1453 //
1454
1455 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1456
1457 //
1458 // now deal with the failure and success cases.
1459 // note that we are not allowed to fail the irp
1460 // once it is sent to the lower drivers.
1461 //
1462
1463 if (NT_SUCCESS(status)) {
1464
1465 IoAdjustPagingPathCount(
1466 (volatile LONG *)&commonExtension->PagingPathCount,
1467 irpStack->Parameters.UsageNotification.InPath);
1468
1469 if (irpStack->Parameters.UsageNotification.InPath) {
1470 if (commonExtension->PagingPathCount == 1) {
1471 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): "
1472 "Clearing PAGABLE bit\n",
1473 DeviceObject, Irp));
1474 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1475
1476
1477 }
1478
1479 }
1480
1481 } else {
1482
1483 //
1484 // cleanup the changes done above
1485 //
1486
1487 if (setPagable == TRUE) {
1488 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Unsetting "
1489 "PAGABLE bit due to irp failure\n",
1490 DeviceObject, Irp));
1491 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1492 setPagable = FALSE;
1493 }
1494
1495 //
1496 // relock or unlock the media if needed.
1497 //
1498
1499 if (commonExtension->IsFdo) {
1500
1501 ClasspEjectionControl(
1502 DeviceObject,
1503 Irp,
1504 InternalMediaLock,
1505 (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
1506 }
1507 }
1508
1509 //
1510 // set the event so the next one can occur.
1511 //
1512
1513 KeSetEvent(&commonExtension->PathCountEvent,
1514 IO_NO_INCREMENT, FALSE);
1515 KeLeaveCriticalRegion();
1516 break;
1517 }
1518
1519 case DeviceUsageTypeHibernation: {
1520
1521 //
1522 // if removing last hiber device, need to set DO_POWER_PAGABLE
1523 // bit here, and possible re-set it below on failure.
1524 //
1525
1526 setPagable = FALSE;
1527
1528 if ((!irpStack->Parameters.UsageNotification.InPath) &&
1529 (commonExtension->HibernationPathCount == 1)) {
1530
1531 //
1532 // removing last hiber file
1533 // must have DO_POWER_PAGABLE bits set, but only
1534 // if none set the DO_POWER_INRUSH bit and no other special files
1535 //
1536
1537 if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1538 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
1539 "hiber file removed, but "
1540 "DO_POWER_INRUSH was set, so NOT "
1541 "setting DO_POWER_PAGABLE\n",
1542 DeviceObject, Irp));
1543 } else if ((commonExtension->PagingPathCount == 0) &&
1544 (commonExtension->DumpPathCount == 0)) {
1545 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
1546 "hiber file removed, "
1547 "setting DO_POWER_PAGABLE\n",
1548 DeviceObject, Irp));
1549 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1550 setPagable = TRUE;
1551 }
1552
1553 }
1554
1555 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1556 if (!NT_SUCCESS(status)) {
1557
1558 if (setPagable == TRUE) {
1559 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Unsetting "
1560 "PAGABLE bit due to irp failure\n",
1561 DeviceObject, Irp));
1562 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1563 setPagable = FALSE;
1564 }
1565
1566 } else {
1567
1568 IoAdjustPagingPathCount(
1569 (volatile LONG *)&commonExtension->HibernationPathCount,
1570 irpStack->Parameters.UsageNotification.InPath
1571 );
1572
1573 if ((irpStack->Parameters.UsageNotification.InPath) &&
1574 (commonExtension->HibernationPathCount == 1)) {
1575 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): "
1576 "Clearing PAGABLE bit\n",
1577 DeviceObject, Irp));
1578 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1579 }
1580 }
1581
1582 break;
1583 }
1584
1585 case DeviceUsageTypeDumpFile: {
1586
1587 //
1588 // if removing last dump device, need to set DO_POWER_PAGABLE
1589 // bit here, and possible re-set it below on failure.
1590 //
1591
1592 setPagable = FALSE;
1593
1594 if ((!irpStack->Parameters.UsageNotification.InPath) &&
1595 (commonExtension->DumpPathCount == 1)) {
1596
1597 //
1598 // removing last dump file
1599 // must have DO_POWER_PAGABLE bits set, but only
1600 // if none set the DO_POWER_INRUSH bit and no other special files
1601 //
1602
1603 if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1604 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
1605 "dump file removed, but "
1606 "DO_POWER_INRUSH was set, so NOT "
1607 "setting DO_POWER_PAGABLE\n",
1608 DeviceObject, Irp));
1609 } else if ((commonExtension->PagingPathCount == 0) &&
1610 (commonExtension->HibernationPathCount == 0)) {
1611 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
1612 "dump file removed, "
1613 "setting DO_POWER_PAGABLE\n",
1614 DeviceObject, Irp));
1615 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1616 setPagable = TRUE;
1617 }
1618
1619 }
1620
1621 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1622 if (!NT_SUCCESS(status)) {
1623
1624 if (setPagable == TRUE) {
1625 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Unsetting "
1626 "PAGABLE bit due to irp failure\n",
1627 DeviceObject, Irp));
1628 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1629 setPagable = FALSE;
1630 }
1631
1632 } else {
1633
1634 IoAdjustPagingPathCount(
1635 (volatile LONG *)&commonExtension->DumpPathCount,
1636 irpStack->Parameters.UsageNotification.InPath
1637 );
1638
1639 if ((irpStack->Parameters.UsageNotification.InPath) &&
1640 (commonExtension->DumpPathCount == 1)) {
1641 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): "
1642 "Clearing PAGABLE bit\n",
1643 DeviceObject, Irp));
1644 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1645 }
1646 }
1647
1648 break;
1649 }
1650
1651 case DeviceUsageTypeBoot: {
1652
1653 if (isFdo) {
1654 PCLASS_PRIVATE_FDO_DATA fdoData;
1655
1656 fdoData = ((PFUNCTIONAL_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->PrivateFdoData;
1657
1658
1659 //
1660 // If boot disk has removal policy as RemovalPolicyExpectSurpriseRemoval (e.g. disk is hotplug-able),
1661 // change the removal policy to RemovalPolicyExpectOrderlyRemoval.
1662 // This will cause the write cache of disk to be enabled on subsequent start Fdo (next boot).
1663 //
1664 if ((fdoData != NULL) &&
1665 fdoData->HotplugInfo.DeviceHotplug) {
1666
1667 fdoData->HotplugInfo.DeviceHotplug = FALSE;
1668 fdoData->HotplugInfo.MediaRemovable = FALSE;
1669
1670 ClassSetDeviceParameter((PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension,
1671 CLASSP_REG_SUBKEY_NAME,
1672 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
1673 RemovalPolicyExpectOrderlyRemoval);
1674 }
1675 }
1676
1677 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1678 break;
1679 }
1680
1681 default: {
1682 status = STATUS_INVALID_PARAMETER;
1683 break;
1684 }
1685 }
1686 break;
1687 }
1688
1689 case IRP_MN_QUERY_CAPABILITIES: {
1690
1691 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1692 DeviceObject, Irp));
1693
1694 if(!isFdo) {
1695
1696 status = ClassQueryPnpCapabilities(
1697 DeviceObject,
1698 irpStack->Parameters.DeviceCapabilities.Capabilities
1699 );
1700
1701 break;
1702
1703 } else {
1704
1705 PDEVICE_CAPABILITIES deviceCapabilities;
1706 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1707 PCLASS_PRIVATE_FDO_DATA fdoData;
1708
1709 fdoExtension = DeviceObject->DeviceExtension;
1710 fdoData = fdoExtension->PrivateFdoData;
1711 deviceCapabilities =
1712 irpStack->Parameters.DeviceCapabilities.Capabilities;
1713
1714 //
1715 // forward the irp before handling the special cases
1716 //
1717
1718 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1719 if (!NT_SUCCESS(status)) {
1720 break;
1721 }
1722
1723 //
1724 // we generally want to remove the device from the hotplug
1725 // applet, which requires the SR-OK bit to be set.
1726 // only when the user specifies that they are capable of
1727 // safely removing things do we want to clear this bit
1728 // (saved in WriteCacheEnableOverride)
1729 //
1730 // setting of this bit is done either above, or by the
1731 // lower driver.
1732 //
1733 // note: may not be started, so check we have FDO data first.
1734 //
1735
1736 if (fdoData &&
1737 fdoData->HotplugInfo.WriteCacheEnableOverride) {
1738 if (deviceCapabilities->SurpriseRemovalOK) {
1739 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "Classpnp: Clearing SR-OK bit in "
1740 "device capabilities due to hotplug "
1741 "device or media\n"));
1742 }
1743 deviceCapabilities->SurpriseRemovalOK = FALSE;
1744 }
1745 break;
1746
1747 } // end QUERY_CAPABILITIES for FDOs
1748
1749 break;
1750
1751
1752 } // end QUERY_CAPABILITIES
1753
1754 default: {
1755
1756 if (isFdo){
1757
1758
1759 IoCopyCurrentIrpStackLocationToNext(Irp);
1760
1761 ClassReleaseRemoveLock(DeviceObject, Irp);
1762 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1763
1764 completeRequest = FALSE;
1765 }
1766
1767 break;
1768 }
1769 }
1770 }
1771 else {
1772 NT_ASSERT(driverExtension);
1773 status = STATUS_INTERNAL_ERROR;
1774 }
1775
1776 if (completeRequest){
1777 Irp->IoStatus.Status = status;
1778
1779 if (!lockReleased){
1780 ClassReleaseRemoveLock(DeviceObject, Irp);
1781 }
1782
1783 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1784
1785 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
1786 }
1787 else {
1788 /*
1789 * The irp is already completed so don't touch it.
1790 * This may be a remove so don't touch the device extension.
1791 */
1792 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
1793 }
1794
1795 return status;
1796 } // end ClassDispatchPnp()
1797
1798
1799 /*++////////////////////////////////////////////////////////////////////////////
1800
1801 ClassPnpStartDevice()
1802
1803 Routine Description:
1804
1805 Storage class driver routine for IRP_MN_START_DEVICE requests.
1806 This routine kicks off any device specific initialization
1807
1808 Arguments:
1809
1810 DeviceObject - a pointer to the device object
1811
1812 Irp - a pointer to the io request packet
1813
1814 Return Value:
1815
1816 none
1817
1818 --*/
ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)1819 NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
1820 {
1821 PCLASS_DRIVER_EXTENSION driverExtension;
1822 PCLASS_INIT_DATA initData;
1823
1824 PCLASS_DEV_INFO devInfo;
1825
1826 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1827 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1828 BOOLEAN isFdo = commonExtension->IsFdo;
1829
1830 BOOLEAN isMountedDevice = TRUE;
1831 BOOLEAN isPortable = FALSE;
1832
1833 NTSTATUS status = STATUS_SUCCESS;
1834 PDEVICE_POWER_DESCRIPTOR powerDescriptor = NULL;
1835
1836
1837 PAGED_CODE();
1838
1839 #ifdef _MSC_VER
1840 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
1841 #endif
1842 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
1843
1844 initData = &(driverExtension->InitData);
1845 if(isFdo) {
1846 devInfo = &(initData->FdoData);
1847 } else {
1848 devInfo = &(initData->PdoData);
1849 }
1850
1851 NT_ASSERT(devInfo->ClassInitDevice != NULL);
1852 NT_ASSERT(devInfo->ClassStartDevice != NULL);
1853
1854 if (!commonExtension->IsInitialized){
1855
1856 //
1857 // perform FDO/PDO specific initialization
1858 //
1859
1860 if (isFdo){
1861 STORAGE_PROPERTY_ID propertyId;
1862
1863 //
1864 // allocate a private extension for class data
1865 //
1866
1867 if (fdoExtension->PrivateFdoData == NULL) {
1868 fdoExtension->PrivateFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
1869 sizeof(CLASS_PRIVATE_FDO_DATA),
1870 CLASS_TAG_PRIVATE_DATA
1871 );
1872 }
1873
1874 if (fdoExtension->PrivateFdoData == NULL) {
1875 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate for private fdo data\n"));
1876 return STATUS_INSUFFICIENT_RESOURCES;
1877 }
1878
1879 RtlZeroMemory(fdoExtension->PrivateFdoData, sizeof(CLASS_PRIVATE_FDO_DATA));
1880
1881
1882 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
1883 //
1884 // Allocate a structure to hold more data than what we can put in FUNCTIONAL_DEVICE_EXTENSION.
1885 // This structure's memory is managed by classpnp, so it is more extensible.
1886 //
1887 if (fdoExtension->AdditionalFdoData == NULL) {
1888 fdoExtension->AdditionalFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
1889 sizeof(ADDITIONAL_FDO_DATA),
1890 CLASSPNP_POOL_TAG_ADDITIONAL_DATA
1891 );
1892 }
1893
1894 if (fdoExtension->AdditionalFdoData == NULL) {
1895 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate memory for the additional data structure.\n"));
1896 return STATUS_INSUFFICIENT_RESOURCES;
1897 }
1898
1899 RtlZeroMemory(fdoExtension->AdditionalFdoData, sizeof(ADDITIONAL_FDO_DATA));
1900 #endif
1901
1902 status = ClasspInitializeTimer(fdoExtension);
1903 if (NT_SUCCESS(status) == FALSE) {
1904 FREE_POOL(fdoExtension->PrivateFdoData);
1905 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
1906 FREE_POOL(fdoExtension->AdditionalFdoData);
1907 #endif
1908 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Failed to initialize tick timer\n"));
1909 return STATUS_INSUFFICIENT_RESOURCES;
1910 }
1911
1912 //
1913 // allocate LowerLayerSupport for class data
1914 //
1915
1916 if (fdoExtension->FunctionSupportInfo == NULL) {
1917 fdoExtension->FunctionSupportInfo = (PCLASS_FUNCTION_SUPPORT_INFO)ExAllocatePoolWithTag(NonPagedPoolNx,
1918 sizeof(CLASS_FUNCTION_SUPPORT_INFO),
1919 '3BcS'
1920 );
1921 }
1922
1923 if (fdoExtension->FunctionSupportInfo == NULL) {
1924 ClasspDeleteTimer(fdoExtension);
1925 FREE_POOL(fdoExtension->PrivateFdoData);
1926 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
1927 FREE_POOL(fdoExtension->AdditionalFdoData);
1928 #endif
1929 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate for FunctionSupportInfo\n"));
1930 return STATUS_INSUFFICIENT_RESOURCES;
1931 }
1932
1933 //
1934 // initialize the struct's various fields.
1935 //
1936 RtlZeroMemory(fdoExtension->FunctionSupportInfo, sizeof(CLASS_FUNCTION_SUPPORT_INFO));
1937 KeInitializeSpinLock(&fdoExtension->FunctionSupportInfo->SyncLock);
1938
1939 //
1940 // intialize the CommandStatus to -1 indicates that no effort made yet to retrieve the info.
1941 // Possible values of CommandStatus (data type: NTSTATUS):
1942 // -1: It's not attempted yet to retrieve the information.
1943 // success: Command sent and succeeded, information cached in FdoExtension.
1944 // failed/warning: Command is either not supported or failed by device or lower level driver.
1945 // The command should not be attempted again.
1946 //
1947 fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus = -1;
1948 fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus = -1;
1949 fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = -1;
1950 fdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = -1;
1951 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = -1;
1952
1953
1954 KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
1955 KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
1956 ClasspRetryRequestDpc,
1957 DeviceObject);
1958 KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
1959 fdoExtension->PrivateFdoData->Retry.Granularity = KeQueryTimeIncrement();
1960 commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
1961 InitializeListHead(&fdoExtension->PrivateFdoData->DeferredClientIrpList);
1962
1963 KeInitializeSpinLock(&fdoExtension->PrivateFdoData->SpinLock);
1964
1965 //
1966 // keep a pointer to the senseinfo2 stuff locally also (used in every read/write).
1967 //
1968 fdoExtension->PrivateFdoData->InterpretSenseInfo = driverExtension->InterpretSenseInfo;
1969
1970 fdoExtension->PrivateFdoData->MaxNumberOfIoRetries = NUM_IO_RETRIES;
1971
1972 //
1973 // Initialize release queue extended SRB
1974 //
1975 status = InitializeStorageRequestBlock(&(fdoExtension->PrivateFdoData->ReleaseQueueSrb.SrbEx),
1976 STORAGE_ADDRESS_TYPE_BTL8,
1977 sizeof(fdoExtension->PrivateFdoData->ReleaseQueueSrb.ReleaseQueueSrbBuffer),
1978 0);
1979 if (!NT_SUCCESS(status)) {
1980 NT_ASSERT(FALSE);
1981 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,
1982 "ClassPnpStartDevice: fail to initialize release queue extended SRB 0x%x\n", status));
1983 return status;
1984 }
1985
1986
1987 /*
1988 * Anchor the FDO in our static list.
1989 * Pnp is synchronized, so we shouldn't need any synchronization here.
1990 */
1991 InsertTailList(&AllFdosList, &fdoExtension->PrivateFdoData->AllFdosListEntry);
1992
1993 //
1994 // NOTE: the old interface allowed the class driver to allocate
1995 // this. this was unsafe for low-memory conditions. allocate one
1996 // unconditionally now, and modify our internal functions to use
1997 // our own exclusively as it is the only safe way to do this.
1998 //
1999
2000 status = ClasspAllocateReleaseQueueIrp(fdoExtension);
2001 if (!NT_SUCCESS(status)) {
2002 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate the private release queue irp\n"));
2003 return status;
2004 }
2005
2006 status = ClasspAllocatePowerProcessIrp(fdoExtension);
2007 if (!NT_SUCCESS(status)) {
2008 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate the power process irp\n"));
2009 return status;
2010 }
2011
2012 //
2013 // Call port driver to get miniport properties for disk devices
2014 // It's ok for this call to fail
2015 //
2016
2017 if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
2018 (!TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
2019
2020 propertyId = StorageMiniportProperty;
2021
2022 status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
2023 &propertyId,
2024 (PVOID *)&fdoExtension->MiniportDescriptor);
2025
2026 //
2027 // function ClassGetDescriptor returns succeed with buffer "fdoExtension->MiniportDescriptor" allocated.
2028 //
2029 if ( NT_SUCCESS(status) &&
2030 (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport &&
2031 fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetUSBport) ) {
2032 //
2033 // field "IoTimeoutValue" supported for either Storport or USBStor
2034 //
2035 fdoExtension->MiniportDescriptor->IoTimeoutValue = 0;
2036 }
2037
2038
2039
2040 }
2041
2042 //
2043 // Call port driver to get adapter capabilities.
2044 //
2045
2046 propertyId = StorageAdapterProperty;
2047
2048 status = ClassGetDescriptor(
2049 commonExtension->LowerDeviceObject,
2050 &propertyId,
2051 (PVOID *)&fdoExtension->AdapterDescriptor);
2052 if (!NT_SUCCESS(status)) {
2053 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: ClassGetDescriptor [ADAPTER] failed %lx\n", status));
2054 return status;
2055 }
2056
2057 //
2058 // Call port driver to get device descriptor.
2059 //
2060
2061 propertyId = StorageDeviceProperty;
2062
2063 status = ClassGetDescriptor(
2064 commonExtension->LowerDeviceObject,
2065 &propertyId,
2066 (PVOID *)&fdoExtension->DeviceDescriptor);
2067 if (NT_SUCCESS(status)){
2068
2069 ClasspScanForSpecialInRegistry(fdoExtension);
2070 ClassScanForSpecial(fdoExtension, ClassBadItems, ClasspScanForClassHacks);
2071
2072 //
2073 // allow perf to be re-enabled after a given number of failed IOs
2074 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
2075 //
2076
2077 {
2078 ULONG t = CLASS_PERF_RESTORE_MINIMUM;
2079
2080 ClassGetDeviceParameter(fdoExtension,
2081 CLASSP_REG_SUBKEY_NAME,
2082 CLASSP_REG_PERF_RESTORE_VALUE_NAME,
2083 &t);
2084 if (t >= CLASS_PERF_RESTORE_MINIMUM) {
2085 fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
2086 }
2087 }
2088
2089 //
2090 // compatibility comes first. writable cd media will not
2091 // get a SYNCH_CACHE on power down.
2092 //
2093 if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
2094 SET_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_SYNC_CACHE);
2095 }
2096
2097
2098 //
2099 // Test if the device is portable and updated the characteristics if so
2100 //
2101 status = ClasspIsPortable(fdoExtension,
2102 &isPortable);
2103
2104 if (NT_SUCCESS(status) && (isPortable == TRUE)) {
2105 DeviceObject->Characteristics |= FILE_PORTABLE_DEVICE;
2106 }
2107
2108 //
2109 // initialize the hotplug information only after the ScanForSpecial
2110 // routines, as it relies upon the hack flags.
2111 //
2112 status = ClasspInitializeHotplugInfo(fdoExtension);
2113 if (NT_SUCCESS(status)){
2114 /*
2115 * Allocate/initialize TRANSFER_PACKETs and related resources.
2116 */
2117 status = InitializeTransferPackets(DeviceObject);
2118 }
2119 else {
2120 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClassPnpStartDevice: Could not initialize hotplug information %lx\n", status));
2121 }
2122 }
2123 else {
2124 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: ClassGetDescriptor [DEVICE] failed %lx\n", status));
2125 return status;
2126 }
2127
2128
2129 if (NT_SUCCESS(status)) {
2130
2131 //
2132 // Retrieve info on whether async notification is supported by port drivers
2133 //
2134 propertyId = StorageDevicePowerProperty;
2135
2136 status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
2137 &propertyId,
2138 (PVOID *)&powerDescriptor);
2139 if (NT_SUCCESS(status) && (powerDescriptor != NULL)) {
2140 fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported = powerDescriptor->AsynchronousNotificationSupported;
2141 fdoExtension->FunctionSupportInfo->IdlePower.D3ColdSupported = powerDescriptor->D3ColdSupported;
2142 fdoExtension->FunctionSupportInfo->IdlePower.NoVerifyDuringIdlePower = powerDescriptor->NoVerifyDuringIdlePower;
2143 FREE_POOL(powerDescriptor);
2144 } else {
2145 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: ClassGetDescriptor [DevicePower] failed %lx\n", status));
2146
2147 //
2148 // Ignore error as device power property is optional
2149 //
2150 status = STATUS_SUCCESS;
2151 }
2152 }
2153 }
2154
2155 //
2156 // ISSUE - drivers need to disable write caching on the media
2157 // if hotplug and !useroverride. perhaps we should
2158 // allow registration of a callback to enable/disable
2159 // write cache instead.
2160 //
2161
2162 if (NT_SUCCESS(status)){
2163 status = devInfo->ClassInitDevice(DeviceObject);
2164 }
2165
2166 if (commonExtension->IsFdo) {
2167 fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
2168
2169 //
2170 // initialization for disk device
2171 //
2172 if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
2173 (!TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
2174
2175 ULONG accessAlignmentNotSupported = 0;
2176 ULONG qerrOverrideMode = QERR_SET_ZERO_ODX_OR_TP_ONLY;
2177 ULONG legacyErrorHandling = FALSE;
2178
2179 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,
2180 "ClassPnpStartDevice: Enabling idle timer for %p\n", DeviceObject));
2181 // Initialize idle timer for disk devices
2182 ClasspInitializeIdleTimer(fdoExtension);
2183
2184 if (ClasspIsObsoletePortDriver(fdoExtension) == FALSE) {
2185 // get INQUIRY VPD support information. It's safe to send command as everything is ready in ClassInitDevice().
2186 ClasspGetInquiryVpdSupportInfo(fdoExtension);
2187
2188 // Query and cache away Logical Block Provisioning info in the FDO extension.
2189 // The cached information will be used in responding to some IOCTLs
2190 ClasspGetLBProvisioningInfo(fdoExtension);
2191
2192 //
2193 // Query and cache away Block Device ROD Limits info in the FDO extension.
2194 //
2195 if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits) {
2196 ClassDetermineTokenOperationCommandSupport(DeviceObject);
2197 }
2198
2199 //
2200 // See if the user has specified a particular QERR override
2201 // mode. "Override" meaning setting QERR = 0 via Mode Select.
2202 // 0 = Only when ODX or Thin Provisioning are supported (default)
2203 // 1 = Always
2204 // 2 = Never (or any value >= 2)
2205 //
2206 ClassGetDeviceParameter(fdoExtension,
2207 CLASSP_REG_SUBKEY_NAME,
2208 CLASSP_REG_QERR_OVERRIDE_MODE,
2209 &qerrOverrideMode);
2210
2211 //
2212 // If this device is thinly provisioned or supports ODX, we
2213 // may need to force QERR to zero. The user may have also
2214 // specified that we should always or never do this.
2215 //
2216 if (qerrOverrideMode == QERR_SET_ZERO_ALWAYS ||
2217 (qerrOverrideMode == QERR_SET_ZERO_ODX_OR_TP_ONLY &&
2218 (ClasspIsThinProvisioned(fdoExtension->FunctionSupportInfo) ||
2219 NT_SUCCESS(ClasspValidateOffloadSupported(DeviceObject, NULL))))) {
2220
2221 ClasspZeroQERR(DeviceObject);
2222 }
2223
2224 } else {
2225
2226 //
2227 // Since this device has been exposed by a legacy miniport (e.g. SCSIPort miniport)
2228 // set its LB Provisioning command status to an error status that will be surfaced
2229 // up to the caller of a TRIM/Unmap command.
2230 //
2231 fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = STATUS_UNSUCCESSFUL;
2232 fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus = STATUS_UNSUCCESSFUL;
2233 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = STATUS_UNSUCCESSFUL;
2234 }
2235
2236 // Get registry setting of failing the IOCTL for AccessAlignment Property.
2237 ClassGetDeviceParameter(fdoExtension,
2238 CLASSP_REG_SUBKEY_NAME,
2239 CLASSP_REG_ACCESS_ALIGNMENT_NOT_SUPPORTED,
2240 &accessAlignmentNotSupported);
2241
2242 if (accessAlignmentNotSupported > 0) {
2243 fdoExtension->FunctionSupportInfo->RegAccessAlignmentQueryNotSupported = TRUE;
2244 }
2245
2246 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
2247
2248
2249 //
2250 // See if the user has specified legacy error handling.
2251 //
2252 ClassGetDeviceParameter(fdoExtension,
2253 CLASSP_REG_SUBKEY_NAME,
2254 CLASSP_REG_LEGACY_ERROR_HANDLING,
2255 &legacyErrorHandling);
2256
2257 if (legacyErrorHandling) {
2258 //
2259 // Legacy error handling means that the maximum number of
2260 // retries allowd for an IO request is 8 instead of 4.
2261 //
2262 fdoExtension->PrivateFdoData->MaxNumberOfIoRetries = LEGACY_NUM_IO_RETRIES;
2263 fdoExtension->PrivateFdoData->LegacyErrorHandling = TRUE;
2264 }
2265 #else
2266 UNREFERENCED_PARAMETER(legacyErrorHandling);
2267 #endif
2268
2269
2270 //
2271 // Get the copy offload max target duration value.
2272 // This function will set the default value if one hasn't been
2273 // specified in the registry.
2274 //
2275 ClasspGetCopyOffloadMaxDuration(DeviceObject,
2276 REG_DISK_CLASS_CONTROL,
2277 &(fdoExtension->PrivateFdoData->CopyOffloadMaxTargetDuration));
2278
2279 }
2280
2281 }
2282 }
2283
2284 if (!NT_SUCCESS(status)){
2285
2286 //
2287 // Just bail out - the remove that comes down will clean up the
2288 // initialized scraps.
2289 //
2290
2291 return status;
2292 } else {
2293 commonExtension->IsInitialized = TRUE;
2294 }
2295
2296 //
2297 // If device requests autorun functionality or a once a second callback
2298 // then enable the once per second timer. Exception is if media change
2299 // detection is desired but device supports async notification.
2300 //
2301 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
2302 // called in the context of the ClassInitDevice callback. If called
2303 // after then this check will have already been made and the
2304 // once a second timer will not have been enabled.
2305 //
2306 if ((isFdo) &&
2307 ((initData->ClassTick != NULL) ||
2308 ((fdoExtension->MediaChangeDetectionInfo != NULL) &&
2309 (fdoExtension->FunctionSupportInfo != NULL) &&
2310 (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) ||
2311 ((fdoExtension->FailurePredictionInfo != NULL) &&
2312 (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
2313 {
2314 ClasspEnableTimer(fdoExtension);
2315
2316 //
2317 // In addition, we may change our polling behavior when the screen is
2318 // off so register for screen state notification if we haven't already
2319 // done so.
2320 //
2321 if (ScreenStateNotificationHandle == NULL) {
2322 PoRegisterPowerSettingCallback(DeviceObject,
2323 &GUID_CONSOLE_DISPLAY_STATE,
2324 &ClasspPowerSettingCallback,
2325 NULL,
2326 &ScreenStateNotificationHandle);
2327 }
2328 }
2329
2330 //
2331 // NOTE: the timer looks at commonExtension->CurrentState now
2332 // to prevent Media Change Notification code from running
2333 // until the device is started, but allows the device
2334 // specific tick handler to run. therefore it is imperative
2335 // that commonExtension->CurrentState not be updated until
2336 // the device specific startdevice handler has finished.
2337 //
2338
2339 status = devInfo->ClassStartDevice(DeviceObject);
2340
2341 if (NT_SUCCESS(status)){
2342 commonExtension->CurrentState = IRP_MN_START_DEVICE;
2343
2344 if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
2345 isMountedDevice = FALSE;
2346 }
2347
2348 if (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) {
2349
2350 isMountedDevice = FALSE;
2351 }
2352
2353 //
2354 // Register for mounted device interface if this is a
2355 // sfloppy device.
2356 //
2357 if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
2358 (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
2359
2360 isMountedDevice = TRUE;
2361 }
2362
2363 if(isMountedDevice) {
2364 ClasspRegisterMountedDeviceInterface(DeviceObject);
2365 }
2366
2367 if(commonExtension->IsFdo) {
2368 IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
2369
2370 //
2371 // Tell Storport (Usbstor or SD) to enable idle power management for this
2372 // device, assuming the user hasn't turned it off in the registry.
2373 //
2374 if (fdoExtension->FunctionSupportInfo != NULL &&
2375 fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled == FALSE &&
2376 fdoExtension->MiniportDescriptor != NULL &&
2377 (fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetStorport ||
2378 fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSDport ||
2379 fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetUSBport)) {
2380 ULONG disableIdlePower= 0;
2381 ClassGetDeviceParameter(fdoExtension,
2382 CLASSP_REG_SUBKEY_NAME,
2383 CLASSP_REG_DISBALE_IDLE_POWER_NAME,
2384 &disableIdlePower);
2385
2386 if (!disableIdlePower) {
2387 ClasspEnableIdlePower(DeviceObject);
2388 }
2389 }
2390 }
2391 }
2392 else {
2393 ClasspDisableTimer(fdoExtension);
2394 }
2395
2396
2397 return status;
2398 }
2399
2400
2401 /*++////////////////////////////////////////////////////////////////////////////
2402
2403 ClassReadWrite()
2404
2405 Routine Description:
2406
2407 This is the system entry point for read and write requests. The
2408 device-specific handler is invoked to perform any validation necessary.
2409
2410 If the device object is a PDO (partition object) then the request will
2411 simply be adjusted for Partition0 and issued to the lower device driver.
2412
2413 IF the device object is an FDO (paritition 0 object), the number of bytes
2414 in the request are checked against the maximum byte counts that the adapter
2415 supports and requests are broken up into
2416 smaller sizes if necessary.
2417
2418 Arguments:
2419
2420 DeviceObject - a pointer to the device object for this request
2421
2422 Irp - IO request
2423
2424 Return Value:
2425
2426 NT Status
2427
2428 --*/
2429 NTSTATUS
2430 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassReadWrite(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)2431 ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
2432 {
2433 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
2434 PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
2435 PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
2436 ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
2437 ULONG isRemoved;
2438 NTSTATUS status;
2439
2440 /*
2441 * Grab the remove lock. If we can't acquire it, bail out.
2442 */
2443 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
2444 if (isRemoved) {
2445 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
2446 Irp->IoStatus.Information = 0;
2447 ClassReleaseRemoveLock(DeviceObject, Irp);
2448 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2449 status = STATUS_DEVICE_DOES_NOT_EXIST;
2450 }
2451 else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
2452 (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
2453 !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
2454
2455 /*
2456 * DO_VERIFY_VOLUME is set for the device object,
2457 * but this request is not itself a verify request.
2458 * So fail this request.
2459 */
2460 if (Irp->Tail.Overlay.Thread != NULL) {
2461 IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
2462 }
2463 Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
2464 Irp->IoStatus.Information = 0;
2465 ClassReleaseRemoveLock(DeviceObject, Irp);
2466 ClassCompleteRequest(DeviceObject, Irp, 0);
2467 status = STATUS_VERIFY_REQUIRED;
2468 }
2469 else {
2470
2471 /*
2472 * Since we've bypassed the verify-required tests we don't need to repeat
2473 * them with this IRP - in particular we don't want to worry about
2474 * hitting them at the partition 0 level if the request has come through
2475 * a non-zero partition.
2476 */
2477 currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
2478
2479 /*
2480 * Call the miniport driver's pre-pass filter to check if we
2481 * should continue with this transfer.
2482 */
2483 NT_ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
2484 status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
2485 // Code Analysis cannot analyze the code paths specific to clients.
2486 _Analysis_assume_(status != STATUS_PENDING);
2487 if (!NT_SUCCESS(status)){
2488 NT_ASSERT(Irp->IoStatus.Status == status);
2489 Irp->IoStatus.Information = 0;
2490 ClassReleaseRemoveLock(DeviceObject, Irp);
2491 ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
2492 }
2493 else if (status == STATUS_PENDING){
2494 /*
2495 * ClassReadWriteVerification queued this request.
2496 * So don't touch the irp anymore.
2497 */
2498 }
2499 else {
2500
2501 if (transferByteCount == 0) {
2502 /*
2503 * Several parts of the code turn 0 into 0xffffffff,
2504 * so don't process a zero-length request any further.
2505 */
2506 Irp->IoStatus.Status = STATUS_SUCCESS;
2507 Irp->IoStatus.Information = 0;
2508 ClassReleaseRemoveLock(DeviceObject, Irp);
2509 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2510 status = STATUS_SUCCESS;
2511 }
2512 else {
2513 /*
2514 * If the driver has its own StartIo routine, call it.
2515 */
2516 if (commonExtension->DriverExtension->InitData.ClassStartIo) {
2517 IoMarkIrpPending(Irp);
2518 IoStartPacket(DeviceObject, Irp, NULL, NULL);
2519 status = STATUS_PENDING;
2520 }
2521 else {
2522 /*
2523 * The driver does not have its own StartIo routine.
2524 * So process this request ourselves.
2525 */
2526
2527 /*
2528 * Add partition byte offset to make starting byte relative to
2529 * beginning of disk.
2530 */
2531 currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
2532 commonExtension->StartingOffset.QuadPart;
2533
2534 if (commonExtension->IsFdo){
2535
2536 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
2537 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
2538
2539 /*
2540 * Add in any skew for the disk manager software.
2541 */
2542 currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
2543 commonExtension->PartitionZeroExtension->DMByteSkew;
2544
2545 //
2546 // In case DEV_USE_16BYTE_CDB flag is not set, R/W request will be translated into READ/WRITE 10 SCSI command.
2547 // These SCSI commands have 4 bytes in "Starting LBA" field.
2548 // Requests cannot be represented in these SCSI commands should be failed.
2549 //
2550 if (!TEST_FLAG(fdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
2551 LARGE_INTEGER startingLba;
2552
2553 startingLba.QuadPart = currentIrpStack->Parameters.Read.ByteOffset.QuadPart >> fdoExtension->SectorShift;
2554
2555 if (startingLba.QuadPart > MAXULONG) {
2556 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
2557 Irp->IoStatus.Information = 0;
2558 ClassReleaseRemoveLock(DeviceObject, Irp);
2559 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2560 status = STATUS_INVALID_PARAMETER;
2561 goto Exit;
2562 }
2563 }
2564
2565 #if DBG
2566 //
2567 // Record the caller if:
2568 // 1. the disk is currently off
2569 // 2. the operation is a read, (likely resulting in disk spinnage)
2570 // 3. the operation is marked WT (likely resulting in disk spinnage)
2571 //
2572 if((fdoExtension->DevicePowerState == PowerDeviceD3) && // disk is off
2573 ((currentIrpStack->MajorFunction == IRP_MJ_READ) || // It's a read.
2574 (TEST_FLAG(currentIrpStack->Flags, SL_WRITE_THROUGH))) ) { // they *really* want it to go to disk.
2575
2576 SnapDiskStartup();
2577 }
2578 #endif
2579
2580 /*
2581 * Perform the actual transfer(s) on the hardware
2582 * to service this request.
2583 */
2584 if (ClasspIsIdleRequestSupported(fdoData, Irp)) {
2585 ClasspMarkIrpAsIdle(Irp, TRUE);
2586 status = ClasspEnqueueIdleRequest(DeviceObject, Irp);
2587 } else {
2588 UCHAR uniqueAddr = 0;
2589
2590 //
2591 // Since we're touching fdoData after servicing the transfer packet, this opens us up to
2592 // a potential window, where the device may be removed between the time that
2593 // ServiceTransferPacket completes and we've had a chance to access fdoData. In order
2594 // to guard against this, we acquire the removelock an additional time here. This
2595 // acquire is guaranteed to succeed otherwise we wouldn't be here (because of the
2596 // outer acquire).
2597 // The sequence of events we're guarding against with this remLock acquire is:
2598 // 1. This UL IRP acquired the lock.
2599 // 2. Device gets surprised removed, then gets IRP_MN_REMOVE_DEVICE; ClassRemoveDevice
2600 // waits for the above RemoveLock.
2601 // 3. ServiceTransferRequest breaks the UL IRP into DL IRPs.
2602 // 4. DL IRPs complete with STATUS_NO_SUCH_DEVICE and TransferPktComplete completes the UL
2603 // IRP with STATUS_NO_SUCH_DEVICE; releases the RemoveLock.
2604 // 5. ClassRemoveDevice is now unblocked, continues running and frees resources (including
2605 // fdoData).
2606 // 6. Finally ClassReadWrite gets to run again and accesses a freed fdoData when trying to
2607 // check/update idle-related fields.
2608 //
2609 ClassAcquireRemoveLock(DeviceObject, (PVOID)&uniqueAddr);
2610
2611 ClasspMarkIrpAsIdle(Irp, FALSE);
2612 status = ServiceTransferRequest(DeviceObject, Irp, FALSE);
2613 if (fdoData->IdlePrioritySupported == TRUE) {
2614 fdoData->LastNonIdleIoTime = ClasspGetCurrentTime();
2615 }
2616
2617 ClassReleaseRemoveLock(DeviceObject, (PVOID)&uniqueAddr);
2618 }
2619 }
2620 else {
2621 /*
2622 * This is a child PDO enumerated for our FDO by e.g. disk.sys
2623 * and owned by e.g. partmgr. Send it down to the next device
2624 * and the same irp will come back to us for the FDO.
2625 */
2626 IoCopyCurrentIrpStackLocationToNext(Irp);
2627 ClassReleaseRemoveLock(DeviceObject, Irp);
2628 status = IoCallDriver(lowerDeviceObject, Irp);
2629 }
2630 }
2631 }
2632 }
2633 }
2634
2635 Exit:
2636 return status;
2637 }
2638
2639
InterpretCapacityData(PDEVICE_OBJECT Fdo,PREAD_CAPACITY_DATA_EX ReadCapacityData)2640 VOID InterpretCapacityData(PDEVICE_OBJECT Fdo, PREAD_CAPACITY_DATA_EX ReadCapacityData)
2641 {
2642 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
2643 ULONG cylinderSize;
2644 ULONG bytesPerSector;
2645 LARGE_INTEGER lastSector;
2646 LARGE_INTEGER largeInt;
2647
2648 bytesPerSector = ClasspCalculateLogicalSectorSize(Fdo, ReadCapacityData->BytesPerBlock);
2649
2650 fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
2651 WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
2652
2653 /*
2654 * LogicalBlockAddress is the last sector of the logical drive, in big-endian.
2655 * It tells us the size of the drive (#sectors is lastSector+1).
2656 */
2657
2658 largeInt = ReadCapacityData->LogicalBlockAddress;
2659 REVERSE_BYTES_QUAD(&lastSector, &largeInt);
2660
2661 if (fdoExt->DMActive){
2662 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassReadDriveCapacity: reducing number of sectors by %d\n", fdoExt->DMSkew));
2663 lastSector.QuadPart -= fdoExt->DMSkew;
2664 }
2665
2666 /*
2667 * Check to see if we have a geometry we should be using already.
2668 * If not, we set part of the disk geometry to garbage values that will be filled in by the caller (e.g. disk.sys).
2669 *
2670 * So the first call to ClassReadDriveCapacity always sets a meaningless geometry.
2671 * TracksPerCylinder and SectorsPerTrack are kind of meaningless anyway wrt I/O,
2672 * because I/O is always targeted to a logical sector number.
2673 * All that really matters is BytesPerSector and the number of sectors.
2674 */
2675 cylinderSize = fdoExt->DiskGeometry.TracksPerCylinder * fdoExt->DiskGeometry.SectorsPerTrack;
2676 if (cylinderSize == 0){
2677 fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
2678 fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
2679 cylinderSize = fdoExt->DiskGeometry.TracksPerCylinder * fdoExt->DiskGeometry.SectorsPerTrack;
2680 }
2681
2682 /*
2683 * Calculate number of cylinders.
2684 * If there are zero cylinders, then the device lied AND it's
2685 * smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
2686 * this can fit into a single LONGLONG, so create another usable
2687 * geometry, even if it's unusual looking.
2688 * This allows small, non-standard devices, such as Sony's Memory Stick, to show up as having a partition.
2689 */
2690 fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector.QuadPart + 1)/cylinderSize);
2691 if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
2692 fdoExt->DiskGeometry.SectorsPerTrack = 1;
2693 fdoExt->DiskGeometry.TracksPerCylinder = 1;
2694 fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector.QuadPart + 1;
2695 }
2696
2697 /*
2698 * Calculate media capacity in bytes.
2699 * For this purpose we treat the entire LUN as is if it is one partition. Disk will deal with actual partitioning.
2700 */
2701 fdoExt->CommonExtension.PartitionLength.QuadPart =
2702 ((LONGLONG)(lastSector.QuadPart + 1)) << fdoExt->SectorShift;
2703
2704 /*
2705 * Is this removable or fixed media
2706 */
2707 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
2708 fdoExt->DiskGeometry.MediaType = RemovableMedia;
2709 }
2710 else {
2711 fdoExt->DiskGeometry.MediaType = FixedMedia;
2712 }
2713
2714 }
2715
2716 /*++////////////////////////////////////////////////////////////////////////////
2717
2718 ClassReadDriveCapacity()
2719
2720 Routine Description:
2721
2722 This routine sends a READ CAPACITY to the requested device, updates
2723 the geometry information in the device object and returns
2724 when it is complete. This routine is synchronous.
2725
2726 This routine must be called with the remove lock held or some other
2727 assurance that the Fdo will not be removed while processing.
2728
2729 Arguments:
2730
2731 DeviceObject - Supplies a pointer to the device object that represents
2732 the device whose capacity is to be read.
2733
2734 Return Value:
2735
2736 Status is returned.
2737
2738 --*/
2739 _Must_inspect_result_
2740 NTSTATUS
2741 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassReadDriveCapacity(_In_ PDEVICE_OBJECT Fdo)2742 ClassReadDriveCapacity(_In_ PDEVICE_OBJECT Fdo)
2743 {
2744 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
2745 PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
2746 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
2747 READ_CAPACITY_DATA_EX PTRALIGN readCapacityData = {0};
2748 PTRANSFER_PACKET pkt;
2749 NTSTATUS status;
2750 PMDL driveCapMdl = NULL;
2751 KEVENT event;
2752 IRP pseudoIrp = {0};
2753 ULONG readCapacityDataSize;
2754 BOOLEAN use16ByteCdb;
2755 BOOLEAN match = TRUE;
2756
2757 use16ByteCdb = TEST_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB);
2758
2759 RetryRequest:
2760
2761 if (use16ByteCdb) {
2762 readCapacityDataSize = sizeof(READ_CAPACITY_DATA_EX);
2763 } else {
2764 readCapacityDataSize = sizeof(READ_CAPACITY_DATA);
2765 }
2766
2767 if (driveCapMdl != NULL) {
2768 FreeDeviceInputMdl(driveCapMdl);
2769 driveCapMdl = NULL;
2770 }
2771
2772 //
2773 // Allocate the MDL based on the Read Capacity command.
2774 //
2775 driveCapMdl = BuildDeviceInputMdl(&readCapacityData, readCapacityDataSize);
2776 if (driveCapMdl == NULL) {
2777 status = STATUS_INSUFFICIENT_RESOURCES;
2778 goto SafeExit;
2779 }
2780
2781 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
2782 if (pkt == NULL) {
2783 status = STATUS_INSUFFICIENT_RESOURCES;
2784 goto SafeExit;
2785 }
2786
2787 //
2788 // Our engine needs an "original irp" to write the status back to
2789 // and to count down packets (one in this case).
2790 // Just use a pretend irp for this.
2791 //
2792
2793 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
2794 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
2795 pseudoIrp.IoStatus.Information = 0;
2796 pseudoIrp.MdlAddress = driveCapMdl;
2797
2798 //
2799 // Set this up as a SYNCHRONOUS transfer, submit it,
2800 // and wait for the packet to complete. The result
2801 // status will be written to the original irp.
2802 //
2803
2804 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
2805 SetupDriveCapacityTransferPacket(pkt,
2806 &readCapacityData,
2807 readCapacityDataSize,
2808 &event,
2809 &pseudoIrp,
2810 use16ByteCdb);
2811 SubmitTransferPacket(pkt);
2812 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
2813
2814 status = pseudoIrp.IoStatus.Status;
2815
2816 //
2817 // If we got an UNDERRUN, retry exactly once.
2818 // (The transfer_packet engine didn't retry because the result
2819 // status was success).
2820 //
2821
2822 if (NT_SUCCESS(status) &&
2823 (pseudoIrp.IoStatus.Information < readCapacityDataSize)) {
2824
2825 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...",
2826 (ULONG)pseudoIrp.IoStatus.Information, readCapacityDataSize));
2827
2828 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
2829 if (pkt) {
2830 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
2831 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
2832 pseudoIrp.IoStatus.Information = 0;
2833 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
2834 SetupDriveCapacityTransferPacket(pkt,
2835 &readCapacityData,
2836 readCapacityDataSize,
2837 &event,
2838 &pseudoIrp,
2839 use16ByteCdb);
2840 SubmitTransferPacket(pkt);
2841 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
2842 status = pseudoIrp.IoStatus.Status;
2843 if (pseudoIrp.IoStatus.Information < readCapacityDataSize){
2844 status = STATUS_DEVICE_BUSY;
2845 }
2846 } else {
2847 status = STATUS_INSUFFICIENT_RESOURCES;
2848 }
2849 }
2850
2851 if (NT_SUCCESS(status)) {
2852 //
2853 // The request succeeded. Check for 8 byte LBA support.
2854 //
2855
2856 if (use16ByteCdb == FALSE) {
2857
2858 PREAD_CAPACITY_DATA readCapacity;
2859
2860 //
2861 // Check whether the device supports 8 byte LBA. If the device supports
2862 // it then retry the request using 16 byte CDB.
2863 //
2864
2865 readCapacity = (PREAD_CAPACITY_DATA) &readCapacityData;
2866
2867 if (readCapacity->LogicalBlockAddress == 0xFFFFFFFF) {
2868 //
2869 // Device returned max size for last LBA. Need to send
2870 // 16 byte request to get the size.
2871 //
2872 use16ByteCdb = TRUE;
2873 goto RetryRequest;
2874
2875 } else {
2876 //
2877 // Convert the 4 byte LBA (READ_CAPACITY_DATA) to 8 byte LBA (READ_CAPACITY_DATA_EX)
2878 // format for ease of use. This is the only format stored in the device extension.
2879 //
2880
2881 RtlMoveMemory((PUCHAR)(&readCapacityData) + sizeof(ULONG), readCapacity, sizeof(READ_CAPACITY_DATA));
2882 RtlZeroMemory((PUCHAR)(&readCapacityData), sizeof(ULONG));
2883
2884 }
2885 } else {
2886 //
2887 // Device completed 16 byte command successfully, it supports 8-byte LBA.
2888 //
2889
2890 SET_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB);
2891 }
2892
2893 //
2894 // Read out and store the drive information.
2895 //
2896
2897 InterpretCapacityData(Fdo, &readCapacityData);
2898
2899 //
2900 // Before caching the new drive capacity, compare it with the
2901 // cached capacity for any change.
2902 //
2903
2904 if (fdoData->IsCachedDriveCapDataValid == TRUE) {
2905
2906 match = (BOOLEAN) RtlEqualMemory(&fdoData->LastKnownDriveCapacityData,
2907 &readCapacityData, sizeof(READ_CAPACITY_DATA_EX));
2908 }
2909
2910 //
2911 // Store the readCapacityData in private FDO data.
2912 // This is so that runtime memory failures don't cause disk.sys to put
2913 // the paging disk in an error state. Also this is used in
2914 // IOCTL_STORAGE_READ_CAPACITY.
2915 //
2916 fdoData->LastKnownDriveCapacityData = readCapacityData;
2917 fdoData->IsCachedDriveCapDataValid = TRUE;
2918
2919 if (match == FALSE) {
2920 if (commonExtension->CurrentState != IRP_MN_START_DEVICE)
2921 {
2922 //
2923 // This can happen if a disk reports Parameters Changed / Capacity Data Changed sense data.
2924 // NT_ASSERT(!"Drive capacity has changed while the device wasn't started!");
2925 //
2926 } else {
2927 //
2928 // state of (commonExtension->CurrentState == IRP_MN_START_DEVICE) indicates that the device has been started.
2929 // UpdateDiskPropertiesWorkItemActive is used as a flag to ensure we only have one work item updating the disk
2930 // properties at a time.
2931 //
2932 if (InterlockedCompareExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 1, 0) == 0)
2933 {
2934 PIO_WORKITEM workItem;
2935
2936 workItem = IoAllocateWorkItem(Fdo);
2937
2938 if (workItem) {
2939 //
2940 // The disk capacity has changed, send notification to the disk driver.
2941 // Start a work item to notify the disk class driver asynchronously.
2942 //
2943 IoQueueWorkItem(workItem, ClasspUpdateDiskProperties, DelayedWorkQueue, workItem);
2944 } else {
2945 InterlockedExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 0);
2946 }
2947 }
2948 }
2949 }
2950
2951 } else {
2952 //
2953 // The request failed.
2954 //
2955
2956 //
2957 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
2958 // what happens when the disk's sector size is bigger than
2959 // 512 bytes and we hit this code path? this is untested.
2960 //
2961 // If the read capacity fails, set the geometry to reasonable parameter
2962 // so things don't fail at unexpected places. Zero the geometry
2963 // except for the bytes per sector and sector shift.
2964 //
2965
2966 //
2967 // This request can sometimes fail legitimately
2968 // (e.g. when a SCSI device is attached but turned off)
2969 // so this is not necessarily a device/driver bug.
2970 //
2971
2972 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassReadDriveCapacity on Fdo %p failed with status %ul.", Fdo, status));
2973
2974 //
2975 // Write in a default disk geometry which we HOPE is right (??).
2976 //
2977
2978 RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
2979 fdoExt->DiskGeometry.BytesPerSector = 512;
2980 fdoExt->SectorShift = 9;
2981 fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
2982
2983 //
2984 // Is this removable or fixed media
2985 //
2986
2987 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
2988 fdoExt->DiskGeometry.MediaType = RemovableMedia;
2989 } else {
2990 fdoExt->DiskGeometry.MediaType = FixedMedia;
2991 }
2992 }
2993
2994 SafeExit:
2995
2996
2997 //
2998 // In case DEV_USE_16BYTE_CDB flag is not set, classpnp translates R/W request into READ/WRITE 10 SCSI command.
2999 // These SCSI commands have 2 bytes in "Transfer Blocks" field.
3000 // Make sure this max length (0xFFFF * sector size) is respected during request split.
3001 //
3002 if (!TEST_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB)) {
3003 ULONG cdb10MaxBlocks = ((ULONG)USHORT_MAX) << fdoExt->SectorShift;
3004
3005 fdoData->HwMaxXferLen = min(cdb10MaxBlocks, fdoData->HwMaxXferLen);
3006 }
3007
3008 if (driveCapMdl != NULL) {
3009 FreeDeviceInputMdl(driveCapMdl);
3010 }
3011
3012 //
3013 // If the request failed for some reason then invalidate the cached
3014 // capacity data for removable devices. So that we won't return
3015 // wrong capacity in IOCTL_STORAGE_READ_CAPACITY
3016 //
3017
3018 if (!NT_SUCCESS(status) && (fdoExt->DiskGeometry.MediaType == RemovableMedia)) {
3019 fdoData->IsCachedDriveCapDataValid = FALSE;
3020 }
3021
3022 //
3023 // Don't let memory failures (either here or in the port driver) in the ReadDriveCapacity call
3024 // put the paging disk in an error state such that paging fails.
3025 // Return the last known drive capacity (which may possibly be slightly out of date, even on
3026 // fixed media, e.g. for storage cabinets that can grow a logical disk).
3027 //
3028 if ((status == STATUS_INSUFFICIENT_RESOURCES) &&
3029 (fdoData->IsCachedDriveCapDataValid) &&
3030 (fdoExt->DiskGeometry.MediaType == FixedMedia)) {
3031 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassReadDriveCapacity: defaulting to cached DriveCapacity data"));
3032 InterpretCapacityData(Fdo, &fdoData->LastKnownDriveCapacityData);
3033 status = STATUS_SUCCESS;
3034 }
3035
3036 return status;
3037 }
3038
3039
3040 /*++////////////////////////////////////////////////////////////////////////////
3041
3042 ClassSendStartUnit()
3043
3044 Routine Description:
3045
3046 Send command to SCSI unit to start or power up.
3047 Because this command is issued asynchronounsly, that is, without
3048 waiting on it to complete, the IMMEDIATE flag is not set. This
3049 means that the CDB will not return until the drive has powered up.
3050 This should keep subsequent requests from being submitted to the
3051 device before it has completely spun up.
3052
3053 This routine is called from the InterpretSense routine, when a
3054 request sense returns data indicating that a drive must be
3055 powered up.
3056
3057 This routine may also be called from a class driver's error handler,
3058 or anytime a non-critical start device should be sent to the device.
3059
3060 Arguments:
3061
3062 Fdo - The functional device object for the stopped device.
3063
3064 Return Value:
3065
3066 None.
3067
3068 --*/
3069 VOID
3070 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassSendStartUnit(_In_ PDEVICE_OBJECT Fdo)3071 ClassSendStartUnit(
3072 _In_ PDEVICE_OBJECT Fdo
3073 )
3074 {
3075 PIO_STACK_LOCATION irpStack;
3076 PIRP irp;
3077 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
3078 PSCSI_REQUEST_BLOCK srb;
3079 PCOMPLETION_CONTEXT context;
3080 PCDB cdb;
3081 NTSTATUS status;
3082 PSTORAGE_REQUEST_BLOCK srbEx;
3083
3084 //
3085 // Allocate Srb from nonpaged pool.
3086 //
3087
3088 context = ExAllocatePoolWithTag(NonPagedPoolNx,
3089 sizeof(COMPLETION_CONTEXT),
3090 '6CcS');
3091
3092 if (context == NULL) {
3093
3094 //
3095 // ISSUE-2000/02/03-peterwie
3096 // This code path was inheritted from the NT 4.0 class2.sys driver.
3097 // It needs to be changed to survive low-memory conditions.
3098 //
3099
3100 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
3101 }
3102
3103 //
3104 // Save the device object in the context for use by the completion
3105 // routine.
3106 //
3107
3108 context->DeviceObject = Fdo;
3109
3110 srb = &context->Srb.Srb;
3111 if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
3112 srbEx = &context->Srb.SrbEx;
3113 status = InitializeStorageRequestBlock(srbEx,
3114 STORAGE_ADDRESS_TYPE_BTL8,
3115 sizeof(context->Srb.SrbExBuffer),
3116 1,
3117 SrbExDataTypeScsiCdb16);
3118 if (!NT_SUCCESS(status)) {
3119 FREE_POOL(context);
3120 NT_ASSERT(FALSE);
3121 return;
3122 }
3123
3124 srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
3125
3126 } else {
3127
3128 //
3129 // Zero out srb.
3130 //
3131
3132 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
3133
3134 //
3135 // Write length to SRB.
3136 //
3137
3138 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
3139
3140 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
3141 }
3142
3143 //
3144 // Set timeout value large enough for drive to spin up.
3145 //
3146
3147 SrbSetTimeOutValue(srb, START_UNIT_TIMEOUT);
3148
3149 //
3150 // Set the transfer length.
3151 //
3152
3153 SrbAssignSrbFlags(srb,
3154 (SRB_FLAGS_NO_DATA_TRANSFER |
3155 SRB_FLAGS_DISABLE_AUTOSENSE |
3156 SRB_FLAGS_DISABLE_SYNCH_TRANSFER));
3157
3158 //
3159 // Build the start unit CDB.
3160 //
3161
3162 SrbSetCdbLength(srb, 6);
3163 cdb = SrbGetCdb(srb);
3164
3165 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
3166 cdb->START_STOP.Start = 1;
3167 cdb->START_STOP.Immediate = 0;
3168 cdb->START_STOP.LogicalUnitNumber = srb->Lun;
3169
3170 //
3171 // Build the asynchronous request to be sent to the port driver.
3172 // Since this routine is called from a DPC the IRP should always be
3173 // available.
3174 //
3175
3176 irp = IoAllocateIrp(Fdo->StackSize, FALSE);
3177
3178 if (irp == NULL) {
3179
3180 //
3181 // ISSUE-2000/02/03-peterwie
3182 // This code path was inheritted from the NT 4.0 class2.sys driver.
3183 // It needs to be changed to survive low-memory conditions.
3184 //
3185
3186 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
3187
3188 }
3189
3190 ClassAcquireRemoveLock(Fdo, irp);
3191
3192 IoSetCompletionRoutine(irp,
3193 (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
3194 context,
3195 TRUE,
3196 TRUE,
3197 TRUE);
3198
3199 irpStack = IoGetNextIrpStackLocation(irp);
3200 irpStack->MajorFunction = IRP_MJ_SCSI;
3201 SrbSetOriginalRequest(srb, irp);
3202
3203 //
3204 // Store the SRB address in next stack for port driver.
3205 //
3206
3207 irpStack->Parameters.Scsi.Srb = srb;
3208
3209 //
3210 // Call the port driver with the IRP.
3211 //
3212
3213 IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
3214
3215 return;
3216
3217 } // end StartUnit()
3218
3219 /*++////////////////////////////////////////////////////////////////////////////
3220
3221 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
3222
3223 Routine Description:
3224
3225 This routine is called when an asynchronous I/O request
3226 which was issused by the class driver completes. Examples of such requests
3227 are release queue or START UNIT. This routine releases the queue if
3228 necessary. It then frees the context and the IRP.
3229
3230 Arguments:
3231
3232 DeviceObject - The device object for the logical unit; however since this
3233 is the top stack location the value is NULL.
3234
3235 Irp - Supplies a pointer to the Irp to be processed.
3236
3237 Context - Supplies the context to be used to process this request.
3238
3239 Return Value:
3240
3241 None.
3242
3243 --*/
3244 NTSTATUS
3245 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassAsynchronousCompletion(PDEVICE_OBJECT DeviceObject,PIRP Irp,PVOID Context)3246 ClassAsynchronousCompletion(
3247 PDEVICE_OBJECT DeviceObject,
3248 PIRP Irp,
3249 PVOID Context
3250 )
3251 {
3252 PCOMPLETION_CONTEXT context = Context;
3253 PSCSI_REQUEST_BLOCK srb;
3254 ULONG srbFunction;
3255 ULONG srbFlags;
3256
3257 if (context == NULL) {
3258 return STATUS_INVALID_PARAMETER;
3259 }
3260
3261 if (DeviceObject == NULL) {
3262
3263 DeviceObject = context->DeviceObject;
3264 }
3265
3266 srb = &context->Srb.Srb;
3267
3268 if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
3269 srbFunction = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction;
3270 srbFlags = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFlags;
3271 } else {
3272 srbFunction = srb->Function;
3273 srbFlags = srb->SrbFlags;
3274 }
3275
3276 //
3277 // If this is an execute srb, then check the return status and make sure.
3278 // the queue is not frozen.
3279 //
3280
3281 if (srbFunction == SRB_FUNCTION_EXECUTE_SCSI) {
3282
3283 //
3284 // Check for a frozen queue.
3285 //
3286
3287 if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
3288
3289 //
3290 // Unfreeze the queue getting the device object from the context.
3291 //
3292
3293 ClassReleaseQueue(context->DeviceObject);
3294 }
3295 }
3296
3297 { // free port-allocated sense buffer if we can detect
3298
3299 if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
3300
3301 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
3302 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
3303 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
3304 }
3305
3306 } else {
3307
3308 NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
3309
3310 }
3311 }
3312
3313
3314 //
3315 // Free the context and the Irp.
3316 //
3317
3318 if (Irp->MdlAddress != NULL) {
3319 MmUnlockPages(Irp->MdlAddress);
3320 IoFreeMdl(Irp->MdlAddress);
3321
3322 Irp->MdlAddress = NULL;
3323 }
3324
3325 ClassReleaseRemoveLock(DeviceObject, Irp);
3326
3327 FREE_POOL(context);
3328
3329 IoFreeIrp(Irp);
3330
3331 //
3332 // Indicate the I/O system should stop processing the Irp completion.
3333 //
3334
3335 return STATUS_MORE_PROCESSING_REQUIRED;
3336
3337 } // end ClassAsynchronousCompletion()
3338
3339
3340 NTSTATUS
ServiceTransferRequest(PDEVICE_OBJECT Fdo,PIRP Irp,BOOLEAN PostToDpc)3341 ServiceTransferRequest(
3342 PDEVICE_OBJECT Fdo,
3343 PIRP Irp,
3344 BOOLEAN PostToDpc
3345 )
3346
3347 /*++
3348
3349 Routine description:
3350
3351 This routine processes Io requests, splitting them if they
3352 are larger than what the hardware can handle at a time. If
3353 there isn't enough memory available, the request is placed
3354 in a queue, to be processed at a later time
3355
3356 If this is a high priority paging request, all regular Io
3357 are throttled to provide Mm with better thoroughput
3358
3359 Arguments:
3360
3361 Fdo - The functional device object processing the request
3362 Irp - The Io request to be processed
3363 PostToDpc - Flag that indicates that this IRP must be posted to a DPC
3364
3365 Return Value:
3366
3367 STATUS_SUCCESS if successful, an error code otherwise
3368
3369 --*/
3370
3371 {
3372 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
3373 PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
3374 PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
3375 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
3376 IO_PAGING_PRIORITY priority = (TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) ? IoGetPagingIoPriority(Irp) : IoPagingPriorityInvalid;
3377 BOOLEAN deferClientIrp = FALSE;
3378 BOOLEAN driverUsesStartIO = (commonExtension->DriverExtension->InitData.ClassStartIo != NULL);
3379 KIRQL oldIrql;
3380 NTSTATUS status = STATUS_UNSUCCESSFUL;
3381 PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
3382
3383 /*
3384 * Initialize IRP status for the master IRP to
3385 * - STATUS_FT_READ_FROM_COPY if it's a copy-specific read
3386 * - STATUS_SUCCESS otherwise.
3387 *
3388 * This is required. When Classpnp determines the status for the master IRP
3389 * when completing child IRPs, the call to IoSetMasterIrpStatus
3390 * will be functioning properly (See TransferPktComplete function)
3391 *
3392 * Note:
3393 * If the IRP is a copy-specific read, File System already initialized the IRP status
3394 * to be STATUS_FT_READ_FROM_COPY. However, this can be changed when the IRP arrives
3395 * at Classpnp. It's possible that other drivers in the stack may initialize the
3396 * IRP status field to other values before forwarding the IRP down the stack.
3397 * To be defensive, we initialize the IRP status to either STATUS_FT_READ_FROM_COPY
3398 * if it's a copy-specific read, or STATUS_SUCCESS otherwise.
3399 */
3400 if (currentIrpStack->MajorFunction == IRP_MJ_READ &&
3401 TEST_FLAG(currentIrpStack->Flags, SL_KEY_SPECIFIED) &&
3402 IsKeyReadCopyNumber(currentIrpStack->Parameters.Read.Key)) {
3403 Irp->IoStatus.Status = STATUS_FT_READ_FROM_COPY;
3404 } else {
3405 Irp->IoStatus.Status = STATUS_SUCCESS;
3406 }
3407
3408 //
3409 // If this is a high priority request, hold off all other Io requests
3410 //
3411
3412 if (priority == IoPagingPriorityHigh)
3413 {
3414 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
3415
3416 if (fdoData->NumHighPriorityPagingIo == 0)
3417 {
3418 //
3419 // Entering throttle mode
3420 //
3421
3422 KeQuerySystemTime(&fdoData->ThrottleStartTime);
3423 }
3424
3425 fdoData->NumHighPriorityPagingIo++;
3426 fdoData->MaxInterleavedNormalIo += ClassMaxInterleavePerCriticalIo;
3427
3428 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
3429 }
3430 else
3431 {
3432 if (fdoData->NumHighPriorityPagingIo != 0)
3433 {
3434 //
3435 // This request wasn't flagged as critical and atleast one critical request
3436 // is currently outstanding. Queue this request until all of those are done
3437 // but only if the interleave threshold has been reached
3438 //
3439
3440 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
3441
3442 if (fdoData->NumHighPriorityPagingIo != 0)
3443 {
3444 if (fdoData->MaxInterleavedNormalIo == 0)
3445 {
3446 deferClientIrp = TRUE;
3447 }
3448 else
3449 {
3450 fdoData->MaxInterleavedNormalIo--;
3451 }
3452 }
3453
3454 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
3455 }
3456 }
3457
3458 if (!deferClientIrp)
3459 {
3460 PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
3461 ULONG entireXferLen = currentSp->Parameters.Read.Length;
3462 PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
3463 LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
3464 PTRANSFER_PACKET pkt;
3465 SINGLE_LIST_ENTRY pktList;
3466 PSINGLE_LIST_ENTRY slistEntry;
3467 ULONG hwMaxXferLen;
3468 ULONG numPackets;
3469 ULONG i;
3470
3471
3472 /*
3473 * We precomputed fdoData->HwMaxXferLen using (MaximumPhysicalPages-1).
3474 * If the buffer is page-aligned, that's one less page crossing so we can add the page back in.
3475 * Note: adapters that return MaximumPhysicalPages=0x10 depend on this to
3476 * transfer aligned 64K requests in one piece.
3477 * Also note: make sure adding PAGE_SIZE back in doesn't wrap to zero.
3478 */
3479 if (((ULONG_PTR)bufPtr & (PAGE_SIZE-1)) || (fdoData->HwMaxXferLen > 0xffffffff-PAGE_SIZE)){
3480 hwMaxXferLen = fdoData->HwMaxXferLen;
3481 }
3482 else {
3483 NT_ASSERT((PAGE_SIZE%fdoExt->DiskGeometry.BytesPerSector) == 0);
3484 hwMaxXferLen = min(fdoData->HwMaxXferLen+PAGE_SIZE, adapterDesc->MaximumTransferLength);
3485 }
3486
3487 /*
3488 * Compute the number of hw xfers we'll have to do.
3489 * Calculate this without allowing for an overflow condition.
3490 */
3491 NT_ASSERT(hwMaxXferLen >= PAGE_SIZE);
3492 numPackets = entireXferLen/hwMaxXferLen;
3493 if (entireXferLen % hwMaxXferLen){
3494 numPackets++;
3495 }
3496
3497 /*
3498 * Use our 'simple' slist functions since we don't need interlocked.
3499 */
3500 SimpleInitSlistHdr(&pktList);
3501
3502 if (driverUsesStartIO) {
3503 /*
3504 * special case: StartIO-based writing must stay serialized, so just
3505 * re-use one packet.
3506 */
3507 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
3508 if (pkt) {
3509 SimplePushSlist(&pktList, (PSINGLE_LIST_ENTRY)&pkt->SlistEntry);
3510 i = 1;
3511 } else {
3512 i = 0;
3513 }
3514 } else {
3515 /*
3516 * First get all the TRANSFER_PACKETs that we'll need at once.
3517 */
3518 for (i = 0; i < numPackets; i++){
3519 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
3520 if (pkt){
3521 SimplePushSlist(&pktList, (PSINGLE_LIST_ENTRY)&pkt->SlistEntry);
3522 }
3523 else {
3524 break;
3525 }
3526 }
3527 }
3528
3529
3530 if ((i == numPackets) &&
3531 (!driverUsesStartIO)) {
3532 NTSTATUS pktStat;
3533
3534 /*
3535 * The IoStatus.Information field will be incremented to the
3536 * transfer length as the pieces complete.
3537 */
3538 Irp->IoStatus.Information = 0;
3539
3540 /*
3541 * Store the number of transfer pieces inside the original IRP.
3542 * It will be used to count down the pieces as they complete.
3543 */
3544 Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
3545
3546 /*
3547 * For the common 1-packet case, we want to allow for an optimization by BlkCache
3548 * (and also potentially synchronous storage drivers) which may complete the
3549 * downward request synchronously.
3550 * In that synchronous completion case, we want to _not_ mark the original irp pending
3551 * and thereby save on the top-level APC.
3552 * It's critical to coordinate this with the completion routine so that we mark the original irp
3553 * pending if-and-only-if we return STATUS_PENDING for it.
3554 */
3555 if (numPackets > 1){
3556 IoMarkIrpPending(Irp);
3557 status = STATUS_PENDING;
3558 }
3559 else {
3560 status = STATUS_SUCCESS;
3561 }
3562
3563 /*
3564 * Transmit the pieces of the transfer.
3565 */
3566 while (entireXferLen > 0){
3567 ULONG thisPieceLen = MIN(hwMaxXferLen, entireXferLen);
3568
3569 /*
3570 * Set up a TRANSFER_PACKET for this piece and send it.
3571 */
3572 slistEntry = SimplePopSlist(&pktList);
3573 NT_ASSERT(slistEntry);
3574 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
3575 SetupReadWriteTransferPacket( pkt,
3576 bufPtr,
3577 thisPieceLen,
3578 targetLocation,
3579 Irp);
3580
3581 //
3582 // If the IRP needs to be split, then we need to use a partial MDL.
3583 // This prevents problems if the same MDL is mapped multiple times.
3584 //
3585 if (numPackets > 1) {
3586 pkt->UsePartialMdl = TRUE;
3587 }
3588
3589 /*
3590 * When an IRP is completed, the completion routine checks to see if there
3591 * is a deferred IRP ready to sent down (assuming that there are no non-idle
3592 * requests waiting to be serviced). If such a deferred IRP is available, it
3593 * is sent down using this routine. However, if the lower driver completes
3594 * the request inline, there is a potential for multiple deferred IRPs being
3595 * sent down in the context of the same completion thread, thus exhausting
3596 * the call stack.
3597 * In order to prevent this from happening, we need to ensure that deferred
3598 * IRPs that are dequeued in the context of a request's completion routine
3599 * get posted to a DPC.
3600 */
3601 if (PostToDpc) {
3602
3603 pkt->RetryIn100nsUnits = 0;
3604 TransferPacketQueueRetryDpc(pkt);
3605 status = STATUS_PENDING;
3606
3607 } else {
3608
3609 pktStat = SubmitTransferPacket(pkt);
3610
3611 /*
3612 * If any of the packets completes with pending, we MUST return pending.
3613 * Also, if a packet completes with an error, return pending; this is because
3614 * in the completion routine we mark the original irp pending if the packet failed
3615 * (since we may retry, thereby switching threads).
3616 */
3617 if (pktStat != STATUS_SUCCESS){
3618 status = STATUS_PENDING;
3619 }
3620 }
3621
3622 entireXferLen -= thisPieceLen;
3623 bufPtr += thisPieceLen;
3624 targetLocation.QuadPart += thisPieceLen;
3625 }
3626 NT_ASSERT(SimpleIsSlistEmpty(&pktList));
3627 }
3628 else if (i >= 1){
3629 /*
3630 * We were unable to get all the TRANSFER_PACKETs we need,
3631 * but we did get at least one.
3632 * That means that we are in extreme low-memory stress.
3633 * We'll try doing this transfer using a single packet.
3634 * The port driver is certainly also in stress, so use one-page
3635 * transfers.
3636 */
3637
3638 /*
3639 * Free all but one of the TRANSFER_PACKETs.
3640 */
3641 while (i-- > 1){
3642 slistEntry = SimplePopSlist(&pktList);
3643 NT_ASSERT(slistEntry);
3644 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
3645 EnqueueFreeTransferPacket(Fdo, pkt);
3646 }
3647
3648 /*
3649 * Get the single TRANSFER_PACKET that we'll be using.
3650 */
3651 slistEntry = SimplePopSlist(&pktList);
3652 NT_ASSERT(slistEntry);
3653 NT_ASSERT(SimpleIsSlistEmpty(&pktList));
3654 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
3655
3656 if (!driverUsesStartIO) {
3657 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%p.", pkt));
3658 }
3659
3660 /*
3661 * Set the number of transfer packets (one)
3662 * inside the original irp.
3663 */
3664 Irp->IoStatus.Information = 0;
3665 Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
3666 IoMarkIrpPending(Irp);
3667
3668 /*
3669 * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
3670 */
3671 SetupReadWriteTransferPacket( pkt,
3672 bufPtr,
3673 entireXferLen,
3674 targetLocation,
3675 Irp);
3676
3677 InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
3678 StepLowMemRetry(pkt);
3679 status = STATUS_PENDING;
3680 }
3681 else {
3682 /*
3683 * We were unable to get ANY TRANSFER_PACKETs.
3684 * Defer this client irp until some TRANSFER_PACKETs free up.
3685 */
3686 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "No packets available in ServiceTransferRequest - deferring transfer (Irp=%p)...", Irp));
3687
3688 if (priority == IoPagingPriorityHigh)
3689 {
3690 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
3691
3692 if (fdoData->MaxInterleavedNormalIo < ClassMaxInterleavePerCriticalIo)
3693 {
3694 fdoData->MaxInterleavedNormalIo = 0;
3695 }
3696 else
3697 {
3698 fdoData->MaxInterleavedNormalIo -= ClassMaxInterleavePerCriticalIo;
3699 }
3700
3701 fdoData->NumHighPriorityPagingIo--;
3702
3703 if (fdoData->NumHighPriorityPagingIo == 0)
3704 {
3705 LARGE_INTEGER period;
3706
3707 //
3708 // Exiting throttle mode
3709 //
3710
3711 KeQuerySystemTime(&fdoData->ThrottleStopTime);
3712
3713 period.QuadPart = fdoData->ThrottleStopTime.QuadPart - fdoData->ThrottleStartTime.QuadPart;
3714 fdoData->LongestThrottlePeriod.QuadPart = max(fdoData->LongestThrottlePeriod.QuadPart, period.QuadPart);
3715 }
3716
3717 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
3718 }
3719
3720 deferClientIrp = TRUE;
3721 }
3722 }
3723 _Analysis_assume_(deferClientIrp);
3724 if (deferClientIrp)
3725 {
3726 IoMarkIrpPending(Irp);
3727 EnqueueDeferredClientIrp(Fdo, Irp);
3728 status = STATUS_PENDING;
3729 }
3730
3731 NT_ASSERT(status != STATUS_UNSUCCESSFUL);
3732
3733 return status;
3734 }
3735
3736
3737 /*++////////////////////////////////////////////////////////////////////////////
3738
3739 ClassIoComplete()
3740
3741 Routine Description:
3742
3743 This routine executes when the port driver has completed a request.
3744 It looks at the SRB status in the completing SRB and if not success
3745 it checks for valid request sense buffer information. If valid, the
3746 info is used to update status with more precise message of type of
3747 error. This routine deallocates the SRB.
3748
3749 This routine should only be placed on the stack location for a class
3750 driver FDO.
3751
3752 Arguments:
3753
3754 Fdo - Supplies the device object which represents the logical
3755 unit.
3756
3757 Irp - Supplies the Irp which has completed.
3758
3759 Context - Supplies a pointer to the SRB.
3760
3761 Return Value:
3762
3763 NT status
3764
3765 --*/
3766 NTSTATUS
3767 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassIoComplete(IN PDEVICE_OBJECT Fdo,IN PIRP Irp,IN PVOID Context)3768 ClassIoComplete(
3769 IN PDEVICE_OBJECT Fdo,
3770 IN PIRP Irp,
3771 IN PVOID Context
3772 )
3773 {
3774 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
3775 PSCSI_REQUEST_BLOCK srb = Context;
3776 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
3777 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
3778 NTSTATUS status;
3779 BOOLEAN retry;
3780 BOOLEAN callStartNextPacket;
3781 ULONG srbFlags;
3782 ULONG srbFunction;
3783
3784 NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
3785
3786 if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
3787 srbFlags = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFlags;
3788 srbFunction = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction;
3789 } else {
3790 srbFlags = srb->SrbFlags;
3791 srbFunction = srb->Function;
3792 }
3793
3794 #if DBG
3795 if (srbFunction == SRB_FUNCTION_FLUSH) {
3796 DBGLOGFLUSHINFO(fdoData, FALSE, FALSE, TRUE);
3797 }
3798 #endif
3799
3800 //
3801 // Check SRB status for success of completing request.
3802 //
3803
3804 if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
3805 LONGLONG retryInterval;
3806
3807 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
3808
3809 //
3810 // Release the queue if it is frozen.
3811 //
3812
3813 if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
3814 ClassReleaseQueue(Fdo);
3815 }
3816 retry = InterpretSenseInfoWithoutHistory(
3817 Fdo,
3818 Irp,
3819 srb,
3820 irpStack->MajorFunction,
3821 ((irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) ?
3822 irpStack->Parameters.DeviceIoControl.IoControlCode :
3823 0),
3824 MAXIMUM_RETRIES -
3825 ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
3826 &status,
3827 &retryInterval);
3828
3829 //
3830 // For Persistent Reserve requests, make sure user gets back partial data.
3831 //
3832
3833 if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL &&
3834 (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN ||
3835 irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_OUT) &&
3836 status == STATUS_DATA_OVERRUN) {
3837
3838 status = STATUS_SUCCESS;
3839 retry = FALSE;
3840 }
3841
3842 //
3843 // If the status is verified required and the this request
3844 // should bypass verify required then retry the request.
3845 //
3846
3847 if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
3848 status == STATUS_VERIFY_REQUIRED) {
3849
3850 status = STATUS_IO_DEVICE_ERROR;
3851 retry = TRUE;
3852 }
3853
3854 #ifndef __REACTOS__
3855 #pragma warning(suppress:4213) // okay to cast Arg4 as a ulong for this use case
3856 if (retry && ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4)--) {
3857 #else
3858 if (retry && (*(ULONG *)&irpStack->Parameters.Others.Argument4)--) {
3859 #endif
3860
3861
3862 //
3863 // Retry request.
3864 //
3865
3866 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "Retry request %p\n", Irp));
3867
3868 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
3869 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
3870 }
3871
3872 RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
3873 return STATUS_MORE_PROCESSING_REQUIRED;
3874 }
3875
3876 } else {
3877
3878 //
3879 // Set status for successful request
3880 //
3881 fdoData->LoggedTURFailureSinceLastIO = FALSE;
3882 ClasspPerfIncrementSuccessfulIo(fdoExtension);
3883 status = STATUS_SUCCESS;
3884 } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
3885
3886
3887 //
3888 // ensure we have returned some info, and it matches what the
3889 // original request wanted for PAGING operations only
3890 //
3891
3892 if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
3893 NT_ASSERT(Irp->IoStatus.Information != 0);
3894 NT_ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
3895 }
3896
3897 //
3898 // remember if the caller wanted to skip calling IoStartNextPacket.
3899 // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
3900 // calls. this setting only affects device objects with StartIo routines.
3901 //
3902
3903 callStartNextPacket = !TEST_FLAG(srbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
3904 if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
3905 callStartNextPacket = FALSE;
3906 }
3907
3908 //
3909 // Free MDL if allocated.
3910 //
3911
3912 if (TEST_FLAG(srbFlags, SRB_CLASS_FLAGS_FREE_MDL)) {
3913 SrbClearSrbFlags(srb, SRB_CLASS_FLAGS_FREE_MDL);
3914 IoFreeMdl(Irp->MdlAddress);
3915 Irp->MdlAddress = NULL;
3916 }
3917
3918
3919 //
3920 // Free the srb
3921 //
3922
3923 if (!TEST_FLAG(srbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
3924
3925 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
3926 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
3927 }
3928
3929 if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
3930 ClassFreeOrReuseSrb(fdoExtension, srb);
3931 }
3932 else {
3933 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
3934 FREE_POOL(srb);
3935 }
3936
3937 } else {
3938
3939 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete: Not Freeing srb @ %p because "
3940 "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
3941 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
3942 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete: Not Freeing sensebuffer @ %p "
3943 " because SRB_CLASS_FLAGS_PERSISTANT set\n",
3944 srb->SenseInfoBuffer));
3945 }
3946
3947 }
3948
3949 //
3950 // Set status in completing IRP.
3951 //
3952
3953 Irp->IoStatus.Status = status;
3954
3955 //
3956 // Set the hard error if necessary.
3957 //
3958
3959 if (!NT_SUCCESS(status) &&
3960 IoIsErrorUserInduced(status) &&
3961 (Irp->Tail.Overlay.Thread != NULL)
3962 ) {
3963
3964 //
3965 // Store DeviceObject for filesystem, and clear
3966 // in IoStatus.Information field.
3967 //
3968
3969 IoSetHardErrorOrVerifyDevice(Irp, Fdo);
3970 Irp->IoStatus.Information = 0;
3971 }
3972
3973 //
3974 // If disk firmware update succeeded, log a system event.
3975 //
3976
3977 if (NT_SUCCESS(status) &&
3978 (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) &&
3979 (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_FIRMWARE_ACTIVATE)) {
3980
3981 ClasspLogSystemEventWithDeviceNumber(Fdo, IO_WARNING_DISK_FIRMWARE_UPDATED);
3982 }
3983
3984 //
3985 // If pending has be returned for this irp then mark the current stack as
3986 // pending.
3987 //
3988
3989 if (Irp->PendingReturned) {
3990 IoMarkIrpPending(Irp);
3991 }
3992
3993 if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
3994 if (callStartNextPacket) {
3995 KIRQL oldIrql;
3996 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
3997 IoStartNextPacket(Fdo, TRUE); // Yes, some IO must now be cancellable.
3998 KeLowerIrql(oldIrql);
3999 }
4000 }
4001
4002 ClassReleaseRemoveLock(Fdo, Irp);
4003
4004 return status;
4005
4006 } // end ClassIoComplete()
4007
4008
4009 /*++////////////////////////////////////////////////////////////////////////////
4010
4011 ClassSendSrbSynchronous()
4012
4013 Routine Description:
4014
4015 This routine is called by SCSI device controls to complete an
4016 SRB and send it to the port driver synchronously (ie wait for
4017 completion). The CDB is already completed along with the SRB CDB
4018 size and request timeout value.
4019
4020 Arguments:
4021
4022 Fdo - Supplies the functional device object which represents the target.
4023
4024 Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
4025
4026 BufferAddress - Supplies the address of the buffer.
4027
4028 BufferLength - Supplies the length in bytes of the buffer.
4029
4030 WriteToDevice - Indicates the data should be transfer to the device.
4031
4032 Return Value:
4033
4034 NTSTATUS indicating the final results of the operation.
4035
4036 If NT_SUCCESS(), then the amount of usable data is contained in the field
4037 Srb->DataTransferLength
4038
4039 --*/
4040 NTSTATUS
4041 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
4042 ClassSendSrbSynchronous(
4043 _In_ PDEVICE_OBJECT Fdo,
4044 _Inout_ PSCSI_REQUEST_BLOCK _Srb,
4045 _In_reads_bytes_opt_(BufferLength) PVOID BufferAddress,
4046 _In_ ULONG BufferLength,
4047 _In_ BOOLEAN WriteToDevice
4048 )
4049 {
4050
4051 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
4052 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
4053 IO_STATUS_BLOCK ioStatus = {0};
4054 PIRP irp;
4055 PIO_STACK_LOCATION irpStack;
4056 KEVENT event;
4057 PVOID senseInfoBuffer = NULL;
4058 ULONG senseInfoBufferLength = SENSE_BUFFER_SIZE_EX;
4059 ULONG retryCount = MAXIMUM_RETRIES;
4060 NTSTATUS status;
4061 BOOLEAN retry;
4062 PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
4063
4064 //
4065 // NOTE: This code is only pagable because we are not freezing
4066 // the queue. Allowing the queue to be frozen from a pagable
4067 // routine could leave the queue frozen as we try to page in
4068 // the code to unfreeze the queue. The result would be a nice
4069 // case of deadlock. Therefore, since we are unfreezing the
4070 // queue regardless of the result, just set the NO_FREEZE_QUEUE
4071 // flag in the SRB.
4072 //
4073
4074 NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
4075 NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
4076
4077 if (Srb->Function != SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
4078 //
4079 // Write length to SRB.
4080 //
4081
4082 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
4083
4084 //
4085 // Set SCSI bus address.
4086 //
4087
4088 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
4089 }
4090
4091 //
4092 // The Srb->Function should have been set corresponding to SrbType.
4093 //
4094
4095 NT_ASSERT( ((fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_SCSI_REQUEST_BLOCK) && (Srb->Function == SRB_FUNCTION_EXECUTE_SCSI)) ||
4096 ((fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) && (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK)) );
4097
4098
4099 //
4100 // Sense buffer is in aligned nonpaged pool.
4101 //
4102
4103 #if defined(_ARM_) || defined(_ARM64_)
4104
4105 //
4106 // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
4107 // based platforms. We are taking the conservative approach here.
4108 //
4109 senseInfoBufferLength = ALIGN_UP_BY(senseInfoBufferLength,KeGetRecommendedSharedDataAlignment());
4110
4111 #endif
4112
4113 senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
4114 senseInfoBufferLength,
4115 '7CcS');
4116
4117 if (senseInfoBuffer == NULL) {
4118
4119 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous: Can't allocate request sense "
4120 "buffer\n"));
4121 return(STATUS_INSUFFICIENT_RESOURCES);
4122 }
4123
4124
4125 //
4126 // Enable auto request sense.
4127 //
4128 SrbSetSenseInfoBufferLength(Srb, SENSE_BUFFER_SIZE_EX);
4129 SrbSetSenseInfoBuffer(Srb, senseInfoBuffer);
4130
4131 SrbSetDataBuffer(Srb, BufferAddress);
4132
4133
4134 //
4135 // Start retries here.
4136 //
4137
4138 retry:
4139
4140 //
4141 // use fdoextension's flags by default.
4142 // do not move out of loop, as the flag may change due to errors
4143 // sending this command.
4144 //
4145
4146 SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
4147
4148 if (BufferAddress != NULL) {
4149 if (WriteToDevice) {
4150 SrbSetSrbFlags(Srb, SRB_FLAGS_DATA_OUT);
4151 } else {
4152 SrbSetSrbFlags(Srb, SRB_FLAGS_DATA_IN);
4153 }
4154 }
4155
4156
4157 //
4158 // Initialize the QueueAction field.
4159 //
4160
4161 SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
4162
4163 //
4164 // Disable synchronous transfer for these requests.
4165 //
4166 SrbSetSrbFlags(Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
4167
4168 //
4169 // Set the event object to the unsignaled state.
4170 // It will be used to signal request completion.
4171 //
4172
4173 KeInitializeEvent(&event, NotificationEvent, FALSE);
4174
4175 //
4176 // Build device I/O control request with METHOD_NEITHER data transfer.
4177 // We'll queue a completion routine to cleanup the MDL's and such ourself.
4178 //
4179
4180 irp = IoAllocateIrp(
4181 (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
4182 FALSE);
4183
4184 if (irp == NULL) {
4185 FREE_POOL(senseInfoBuffer);
4186 SrbSetSenseInfoBuffer(Srb, NULL);
4187 SrbSetSenseInfoBufferLength(Srb, 0);
4188 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
4189 return(STATUS_INSUFFICIENT_RESOURCES);
4190 }
4191
4192 //
4193 // Get next stack location.
4194 //
4195
4196 irpStack = IoGetNextIrpStackLocation(irp);
4197
4198 //
4199 // Set up SRB for execute scsi request. Save SRB address in next stack
4200 // for the port driver.
4201 //
4202
4203 irpStack->MajorFunction = IRP_MJ_SCSI;
4204 irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;
4205
4206 IoSetCompletionRoutine(irp,
4207 ClasspSendSynchronousCompletion,
4208 Srb,
4209 TRUE,
4210 TRUE,
4211 TRUE);
4212
4213 irp->UserIosb = &ioStatus;
4214 irp->UserEvent = &event;
4215
4216 if (BufferAddress) {
4217 //
4218 // Build an MDL for the data buffer and stick it into the irp. The
4219 // completion routine will unlock the pages and free the MDL.
4220 //
4221
4222 irp->MdlAddress = IoAllocateMdl( BufferAddress,
4223 BufferLength,
4224 FALSE,
4225 FALSE,
4226 irp );
4227 if (irp->MdlAddress == NULL) {
4228 FREE_POOL(senseInfoBuffer);
4229 SrbSetSenseInfoBuffer(Srb, NULL);
4230 SrbSetSenseInfoBufferLength(Srb, 0);
4231 IoFreeIrp( irp );
4232 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
4233 return STATUS_INSUFFICIENT_RESOURCES;
4234 }
4235
4236 _SEH2_TRY {
4237
4238 //
4239 // the io manager unlocks these pages upon completion
4240 //
4241
4242 MmProbeAndLockPages( irp->MdlAddress,
4243 KernelMode,
4244 (WriteToDevice ? IoReadAccess :
4245 IoWriteAccess));
4246
4247 #ifdef _MSC_VER
4248 #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
4249 #endif
4250 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
4251 status = _SEH2_GetExceptionCode();
4252
4253 FREE_POOL(senseInfoBuffer);
4254 SrbSetSenseInfoBuffer(Srb, NULL);
4255 SrbSetSenseInfoBufferLength(Srb, 0);
4256 IoFreeMdl(irp->MdlAddress);
4257 IoFreeIrp(irp);
4258
4259 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous: Exception %lx "
4260 "locking buffer\n", status));
4261 _SEH2_YIELD(return status);
4262 } _SEH2_END;
4263 }
4264
4265 //
4266 // Set the transfer length.
4267 //
4268
4269 SrbSetDataTransferLength(Srb, BufferLength);
4270
4271 //
4272 // Zero out status.
4273 //
4274
4275 SrbSetScsiStatus(Srb, 0);
4276 Srb->SrbStatus = 0;
4277 SrbSetNextSrb(Srb, NULL);
4278
4279 //
4280 // Set up IRP Address.
4281 //
4282
4283 SrbSetOriginalRequest(Srb, irp);
4284
4285 //
4286 // Call the port driver with the request and wait for it to complete.
4287 //
4288
4289 status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
4290
4291 if (status == STATUS_PENDING) {
4292 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
4293 status = ioStatus.Status;
4294 }
4295
4296 // NT_ASSERT(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_PENDING);
4297 NT_ASSERT(status != STATUS_PENDING);
4298 NT_ASSERT(!(Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN));
4299
4300 //
4301 // Clear the IRP address in SRB as IRP has been freed at this time
4302 // and don't want to leave any references that may be accessed.
4303 //
4304
4305 SrbSetOriginalRequest(Srb, NULL);
4306
4307 //
4308 // Check that request completed without error.
4309 //
4310
4311 if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
4312
4313 LONGLONG retryInterval;
4314
4315 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb,
4316 DBGGETSCSIOPSTR(Srb),
4317 DBGGETSRBSTATUSSTR(Srb), (ULONG)Srb->SrbStatus, status,
4318 DBGGETSENSECODESTR(Srb),
4319 DBGGETADSENSECODESTR(Srb),
4320 DBGGETADSENSEQUALIFIERSTR(Srb)));
4321
4322 //
4323 // assert that the queue is not frozen
4324 //
4325
4326 NT_ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
4327
4328 //
4329 // Update status and determine if request should be retried.
4330 //
4331
4332 retry = InterpretSenseInfoWithoutHistory(Fdo,
4333 NULL, // no valid irp exists
4334 (PSCSI_REQUEST_BLOCK)Srb,
4335 IRP_MJ_SCSI,
4336 0,
4337 MAXIMUM_RETRIES - retryCount,
4338 &status,
4339 &retryInterval);
4340
4341 if (retry) {
4342
4343 BOOLEAN validSense = FALSE;
4344 UCHAR additionalSenseCode = 0;
4345
4346 if (status == STATUS_DEVICE_NOT_READY) {
4347
4348 validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
4349 SrbGetSenseInfoBufferLength(Srb),
4350 SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
4351 NULL,
4352 &additionalSenseCode,
4353 NULL);
4354 }
4355
4356 if ((validSense && additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) ||
4357 (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
4358
4359 LARGE_INTEGER delay;
4360
4361 //
4362 // Delay for at least 2 seconds.
4363 //
4364
4365 if (retryInterval < 2*1000*1000*10) {
4366 retryInterval = 2*1000*1000*10;
4367 }
4368
4369 delay.QuadPart = -retryInterval;
4370
4371 //
4372 // Stall for a while to let the device become ready
4373 //
4374
4375 KeDelayExecutionThread(KernelMode, FALSE, &delay);
4376
4377 }
4378
4379
4380 //
4381 // If retries are not exhausted then retry this operation.
4382 //
4383
4384 if (retryCount--) {
4385
4386 if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
4387 FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
4388 }
4389
4390 goto retry;
4391 }
4392 }
4393
4394
4395 } else {
4396 fdoData->LoggedTURFailureSinceLastIO = FALSE;
4397 status = STATUS_SUCCESS;
4398 }
4399
4400 //
4401 // required even though we allocated our own, since the port driver may
4402 // have allocated one also
4403 //
4404
4405 if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
4406 FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
4407 }
4408
4409 FREE_POOL(senseInfoBuffer);
4410 SrbSetSenseInfoBuffer(Srb, NULL);
4411 SrbSetSenseInfoBufferLength(Srb, 0);
4412
4413 return status;
4414 }
4415
4416
4417 /*++////////////////////////////////////////////////////////////////////////////
4418
4419 ClassInterpretSenseInfo()
4420
4421 Routine Description:
4422
4423 This routine interprets the data returned from the SCSI
4424 request sense. It determines the status to return in the
4425 IRP and whether this request can be retried.
4426
4427 Arguments:
4428
4429 Fdo - Supplies the device object associated with this request.
4430
4431 Srb - Supplies the scsi request block which failed.
4432
4433 MajorFunctionCode - Supplies the function code to be used for logging.
4434
4435 IoDeviceCode - Supplies the device code to be used for logging.
4436
4437 RetryCount - Number of times that the request has been retried.
4438
4439 Status - Returns the status for the request.
4440
4441 RetryInterval - Number of seconds before the request should be retried.
4442 Zero indicates the request should be immediately retried.
4443
4444 Return Value:
4445
4446 BOOLEAN TRUE: Drivers should retry this request.
4447 FALSE: Drivers should not retry this request.
4448
4449 --*/
4450 BOOLEAN
4451 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
4452 ClassInterpretSenseInfo(
4453 _In_ PDEVICE_OBJECT Fdo,
4454 _In_ PSCSI_REQUEST_BLOCK _Srb,
4455 _In_ UCHAR MajorFunctionCode,
4456 _In_ ULONG IoDeviceCode,
4457 _In_ ULONG RetryCount,
4458 _Out_ NTSTATUS *Status,
4459 _Out_opt_ _Deref_out_range_(0,100) ULONG *RetryInterval
4460 )
4461 {
4462 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
4463 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
4464 PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
4465 PVOID senseBuffer = SrbGetSenseInfoBuffer(Srb);
4466 BOOLEAN retry = TRUE;
4467 BOOLEAN logError = FALSE;
4468 BOOLEAN unhandledError = FALSE;
4469 BOOLEAN incrementErrorCount = FALSE;
4470
4471 //
4472 // NOTE: This flag must be used only for read/write requests that
4473 // fail with a unexpected retryable error.
4474 //
4475 BOOLEAN logRetryableError = TRUE;
4476
4477 //
4478 // Indicates if we should log this error in our internal log.
4479 //
4480 BOOLEAN logErrorInternal = TRUE;
4481
4482 ULONGLONG badSector = 0;
4483 ULONG uniqueId = 0;
4484
4485 NTSTATUS logStatus;
4486
4487 ULONGLONG readSector;
4488 ULONG index;
4489
4490 ULONG retryInterval = 0;
4491 KIRQL oldIrql;
4492 PCDB cdb = SrbGetCdb(Srb);
4493 UCHAR cdbOpcode = 0;
4494 ULONG cdbLength = SrbGetCdbLength(Srb);
4495
4496 #if DBG
4497 BOOLEAN isReservationConflict = FALSE;
4498 #endif
4499
4500 if (cdb) {
4501 cdbOpcode = cdb->CDB6GENERIC.OperationCode;
4502 }
4503
4504 *Status = STATUS_IO_DEVICE_ERROR;
4505 logStatus = -1;
4506
4507 if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
4508
4509 //
4510 // Log anything remotely incorrect about paging i/o
4511 //
4512
4513 logError = TRUE;
4514 uniqueId = 301;
4515 logStatus = IO_WARNING_PAGING_FAILURE;
4516 }
4517
4518 //
4519 // Check that request sense buffer is valid.
4520 //
4521
4522 NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
4523
4524
4525 //
4526 // must handle the SRB_STATUS_INTERNAL_ERROR case first,
4527 // as it has all the flags set.
4528 //
4529
4530 if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
4531
4532 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
4533 "ClassInterpretSenseInfo: Internal Error code is %x\n",
4534 SrbGetSystemStatus(Srb)));
4535
4536 retry = FALSE;
4537 *Status = SrbGetSystemStatus(Srb);
4538
4539 } else if (SrbGetScsiStatus(Srb) == SCSISTAT_RESERVATION_CONFLICT) {
4540
4541 //
4542 // Need to reserve STATUS_DEVICE_BUSY to convey reservation conflict
4543 // for read/write requests as there are upper level components that
4544 // have built-in assumptions that STATUS_DEVICE_BUSY implies reservation
4545 // conflict.
4546 //
4547 *Status = STATUS_DEVICE_BUSY;
4548 retry = FALSE;
4549 logError = FALSE;
4550 #if DBG
4551 isReservationConflict = TRUE;
4552 #endif
4553
4554 } else {
4555
4556 BOOLEAN validSense = FALSE;
4557
4558 if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && senseBuffer) {
4559
4560 UCHAR errorCode = 0;
4561 UCHAR senseKey = 0;
4562 UCHAR addlSenseCode = 0;
4563 UCHAR addlSenseCodeQual = 0;
4564 BOOLEAN isIncorrectLengthValid = FALSE;
4565 BOOLEAN incorrectLength = FALSE;
4566 BOOLEAN isInformationValid = FALSE;
4567 ULONGLONG information = 0;
4568
4569
4570 validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
4571 SrbGetSenseInfoBufferLength(Srb),
4572 SCSI_SENSE_OPTIONS_NONE,
4573 &senseKey,
4574 &addlSenseCode,
4575 &addlSenseCodeQual);
4576
4577 if (!validSense && !IsSenseDataFormatValueValid(senseBuffer)) {
4578
4579 NT_ASSERT(FALSE);
4580
4581 validSense = ScsiGetFixedSenseKeyAndCodes(senseBuffer,
4582 SrbGetSenseInfoBufferLength(Srb),
4583 &senseKey,
4584 &addlSenseCode,
4585 &addlSenseCodeQual);
4586 }
4587
4588 if (!validSense) {
4589 goto __ClassInterpretSenseInfo_ProcessingInvalidSenseBuffer;
4590 }
4591
4592 errorCode = ScsiGetSenseErrorCode(senseBuffer);
4593
4594 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Error code is %x\n", errorCode));
4595 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Sense key is %x\n", senseKey));
4596 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Additional sense code is %x\n", addlSenseCode));
4597 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Additional sense code qualifier is %x\n", addlSenseCodeQual));
4598
4599 if (IsDescriptorSenseDataFormat(senseBuffer)) {
4600
4601 //
4602 // Sense data in Descriptor format
4603 //
4604
4605 PVOID startBuffer = NULL;
4606 UCHAR startBufferLength = 0;
4607
4608
4609 if (ScsiGetSenseDescriptor(senseBuffer,
4610 SrbGetSenseInfoBufferLength(Srb),
4611 &startBuffer,
4612 &startBufferLength)) {
4613 UCHAR outType;
4614 PVOID outBuffer = NULL;
4615 UCHAR outBufferLength = 0;
4616 BOOLEAN foundBlockCommandType = FALSE;
4617 BOOLEAN foundInformationType = FALSE;
4618 UCHAR descriptorLength = 0;
4619
4620 UCHAR typeList[2] = {SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION,
4621 SCSI_SENSE_DESCRIPTOR_TYPE_BLOCK_COMMAND};
4622
4623 while ((!foundBlockCommandType || !foundInformationType) &&
4624 ScsiGetNextSenseDescriptorByType(startBuffer,
4625 startBufferLength,
4626 typeList,
4627 ARRAYSIZE(typeList),
4628 &outType,
4629 &outBuffer,
4630 &outBufferLength)) {
4631
4632 descriptorLength = ScsiGetSenseDescriptorLength(outBuffer);
4633
4634 if (outBufferLength < descriptorLength) {
4635
4636 // Descriptor data is truncated.
4637 // Complete searching descriptors. Exit the loop now.
4638 break;
4639 }
4640
4641 if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_BLOCK_COMMAND) {
4642
4643 //
4644 // Block Command type
4645 //
4646
4647 if (!foundBlockCommandType) {
4648
4649 foundBlockCommandType = TRUE;
4650
4651 if (ScsiValidateBlockCommandSenseDescriptor(outBuffer, outBufferLength)) {
4652 incorrectLength = ((PSCSI_SENSE_DESCRIPTOR_BLOCK_COMMAND)outBuffer)->IncorrectLength;
4653 isIncorrectLengthValid = TRUE;
4654 }
4655 } else {
4656
4657 //
4658 // A Block Command descriptor is already found earlier.
4659 //
4660 // T10 SPC specification only allows one descriptor for Block Command Descriptor type.
4661 // Assert here to catch devices that violate this rule. Ignore this descriptor.
4662 //
4663 NT_ASSERT(FALSE);
4664 }
4665
4666 } else if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) {
4667
4668 //
4669 // Information type
4670 //
4671
4672 if (!foundInformationType) {
4673
4674 foundInformationType = TRUE;
4675
4676 if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) {
4677 REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information));
4678 isInformationValid = TRUE;
4679 }
4680 } else {
4681
4682 //
4683 // A Information descriptor is already found earlier.
4684 //
4685 // T10 SPC specification only allows one descriptor for Information Descriptor type.
4686 // Assert here to catch devices that violate this rule. Ignore this descriptor.
4687 //
4688 NT_ASSERT(FALSE);
4689 }
4690
4691 } else {
4692
4693 //
4694 // ScsiGetNextDescriptorByType should only return a type that is specified by us.
4695 //
4696 NT_ASSERT(FALSE);
4697 break;
4698 }
4699
4700 //
4701 // Advance to start address of next descriptor
4702 //
4703 startBuffer = (PUCHAR)outBuffer + descriptorLength;
4704 startBufferLength = outBufferLength - descriptorLength;
4705 }
4706 }
4707 } else {
4708
4709 //
4710 // Sense data in Fixed format
4711 //
4712
4713 incorrectLength = ((PFIXED_SENSE_DATA)(senseBuffer))->IncorrectLength;
4714 REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseBuffer)->Information));
4715 isInformationValid = TRUE;
4716 isIncorrectLengthValid = TRUE;
4717 }
4718
4719
4720 switch (senseKey) {
4721
4722 case SCSI_SENSE_NO_SENSE: {
4723
4724 //
4725 // Check other indicators.
4726 //
4727
4728 if (isIncorrectLengthValid && incorrectLength) {
4729
4730 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4731 "Incorrect length detected.\n"));
4732 *Status = STATUS_INVALID_BLOCK_LENGTH ;
4733 retry = FALSE;
4734
4735 } else {
4736
4737 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4738 "No specific sense key\n"));
4739 *Status = STATUS_IO_DEVICE_ERROR;
4740 retry = TRUE;
4741 }
4742
4743 break;
4744 } // end SCSI_SENSE_NO_SENSE
4745
4746 case SCSI_SENSE_RECOVERED_ERROR: {
4747
4748 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4749 "Recovered error\n"));
4750 *Status = STATUS_SUCCESS;
4751 retry = FALSE;
4752 logError = TRUE;
4753 uniqueId = 258;
4754
4755 switch(addlSenseCode) {
4756 case SCSI_ADSENSE_TRACK_ERROR:
4757 case SCSI_ADSENSE_SEEK_ERROR: {
4758 logStatus = IO_ERR_SEEK_ERROR;
4759 break;
4760 }
4761
4762 case SCSI_ADSENSE_REC_DATA_NOECC:
4763 case SCSI_ADSENSE_REC_DATA_ECC: {
4764 logStatus = IO_RECOVERED_VIA_ECC;
4765 break;
4766 }
4767
4768 case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
4769
4770 UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
4771
4772 *((PULONG)wmiEventData) = sizeof(UCHAR);
4773 wmiEventData[sizeof(ULONG)] = addlSenseCodeQual;
4774
4775 //
4776 // Don't log another eventlog if we have already logged once
4777 // NOTE: this should have been interlocked, but the structure
4778 // was publicly defined to use a BOOLEAN (char). Since
4779 // media only reports these errors once per X minutes,
4780 // the potential race condition is nearly non-existant.
4781 // the worst case is duplicate log entries, so ignore.
4782 //
4783
4784 logError = FALSE;
4785 if (fdoExtension->FailurePredicted == 0) {
4786 logError = TRUE;
4787 }
4788 fdoExtension->FailureReason = addlSenseCodeQual;
4789 logStatus = IO_WRN_FAILURE_PREDICTED;
4790
4791 ClassNotifyFailurePredicted(fdoExtension,
4792 (PUCHAR)wmiEventData,
4793 sizeof(wmiEventData),
4794 FALSE, // do not log error
4795 4, // unique error value
4796 SrbGetPathId(Srb),
4797 SrbGetTargetId(Srb),
4798 SrbGetLun(Srb));
4799
4800 fdoExtension->FailurePredicted = TRUE;
4801 break;
4802 }
4803
4804 default: {
4805 logStatus = IO_ERR_CONTROLLER_ERROR;
4806 break;
4807 }
4808
4809 } // end switch(addlSenseCode)
4810
4811 if (isIncorrectLengthValid && incorrectLength) {
4812
4813 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4814 "Incorrect length detected.\n"));
4815 *Status = STATUS_INVALID_BLOCK_LENGTH ;
4816 }
4817
4818
4819 break;
4820 } // end SCSI_SENSE_RECOVERED_ERROR
4821
4822 case SCSI_SENSE_NOT_READY: {
4823
4824 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4825 "Device not ready\n"));
4826 *Status = STATUS_DEVICE_NOT_READY;
4827
4828 switch (addlSenseCode) {
4829
4830 case SCSI_ADSENSE_LUN_NOT_READY: {
4831
4832 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4833 "Lun not ready\n"));
4834
4835 retryInterval = NOT_READY_RETRY_INTERVAL;
4836
4837 switch (addlSenseCodeQual) {
4838
4839 case SCSI_SENSEQ_BECOMING_READY: {
4840 DEVICE_EVENT_BECOMING_READY notReady = {0};
4841
4842 logRetryableError = FALSE;
4843 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4844 "In process of becoming ready\n"));
4845
4846 notReady.Version = 1;
4847 notReady.Reason = 1;
4848 notReady.Estimated100msToReady = retryInterval * 10;
4849 ClassSendNotification(fdoExtension,
4850 &GUID_IO_DEVICE_BECOMING_READY,
4851 sizeof(DEVICE_EVENT_BECOMING_READY),
4852 ¬Ready);
4853 break;
4854 }
4855
4856 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
4857 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4858 "Manual intervention required\n"));
4859 *Status = STATUS_NO_MEDIA_IN_DEVICE;
4860 retry = FALSE;
4861 break;
4862 }
4863
4864 case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
4865 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4866 "Format in progress\n"));
4867 retry = FALSE;
4868 break;
4869 }
4870
4871 case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
4872 DEVICE_EVENT_BECOMING_READY notReady = {0};
4873
4874 logRetryableError = FALSE;
4875 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4876 "Operation In Progress\n"));
4877
4878 notReady.Version = 1;
4879 notReady.Reason = 2;
4880 notReady.Estimated100msToReady = retryInterval * 10;
4881 ClassSendNotification(fdoExtension,
4882 &GUID_IO_DEVICE_BECOMING_READY,
4883 sizeof(DEVICE_EVENT_BECOMING_READY),
4884 ¬Ready);
4885
4886 break;
4887 }
4888
4889 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
4890 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4891 "Long write in progress\n"));
4892 //
4893 // This has been seen as a transcient failure on some cdrom
4894 // drives. The cdrom class driver is going to override this
4895 // setting but has no way of dropping the retry interval
4896 //
4897 retry = FALSE;
4898 retryInterval = 1;
4899 break;
4900 }
4901
4902 case SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS: {
4903 logRetryableError = FALSE;
4904 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4905 "The device (%p) is busy allocating space.\n",
4906 Fdo));
4907
4908 //
4909 // This indicates that a thinly-provisioned device has hit
4910 // a temporary resource exhaustion and is busy allocating
4911 // more space. We need to retry the request as the device
4912 // will eventually be able to service it.
4913 //
4914 *Status = STATUS_RETRY;
4915 retry = TRUE;
4916
4917 break;
4918 }
4919
4920 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
4921
4922 if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
4923 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
4924
4925 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
4926 "ClassInterpretSenseInfo: "
4927 "not ready, cause unknown\n"));
4928 /*
4929 Many non-WHQL certified drives (mostly CD-RW) return
4930 this when they have no media instead of the obvious
4931 choice of:
4932
4933 SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
4934
4935 These drives should not pass WHQL certification due
4936 to this discrepency.
4937
4938 */
4939 retry = FALSE;
4940 break;
4941
4942 } else {
4943
4944 //
4945 // Treat this as init command required and fall through.
4946 //
4947 }
4948 }
4949
4950 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
4951 default: {
4952 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4953 "Initializing command required\n"));
4954 retryInterval = 0; // go back to default
4955 logRetryableError = FALSE;
4956
4957 //
4958 // This sense code/additional sense code
4959 // combination may indicate that the device
4960 // needs to be started. Send an start unit if this
4961 // is a disk device.
4962 //
4963 if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT) &&
4964 !TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_LOW_PRIORITY)){
4965
4966 ClassSendStartUnit(Fdo);
4967 }
4968 break;
4969 }
4970
4971 } // end switch (addlSenseCodeQual)
4972 break;
4973 }
4974
4975 case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
4976 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4977 "No Media in device.\n"));
4978 *Status = STATUS_NO_MEDIA_IN_DEVICE;
4979 retry = FALSE;
4980
4981 //
4982 // signal MCN that there isn't any media in the device
4983 //
4984 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
4985 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4986 "No Media in a non-removable device %p\n",
4987 Fdo));
4988 }
4989
4990 if (addlSenseCodeQual == 0xCC){
4991 /*
4992 * The IMAPI filter returns this ASCQ value when it is burning CD-R media.
4993 * We want to indicate that the media is not present to most applications;
4994 * but RSM has to know that the media is still in the drive (i.e. the drive is not free).
4995 */
4996 ClassSetMediaChangeState(fdoExtension, MediaUnavailable, FALSE);
4997 }
4998 else {
4999 ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
5000 }
5001
5002 break;
5003 }
5004 } // end switch (addlSenseCode)
5005
5006 break;
5007 } // end SCSI_SENSE_NOT_READY
5008
5009 case SCSI_SENSE_MEDIUM_ERROR: {
5010 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5011 "Medium Error (bad block)\n"));
5012 *Status = STATUS_DEVICE_DATA_ERROR;
5013
5014 retry = FALSE;
5015 logError = TRUE;
5016 uniqueId = 256;
5017 logStatus = IO_ERR_BAD_BLOCK;
5018
5019 //
5020 // Check if this error is due to unknown format
5021 //
5022 if (addlSenseCode == SCSI_ADSENSE_INVALID_MEDIA) {
5023
5024 switch (addlSenseCodeQual) {
5025
5026 case SCSI_SENSEQ_UNKNOWN_FORMAT: {
5027
5028 *Status = STATUS_UNRECOGNIZED_MEDIA;
5029
5030 //
5031 // Log error only if this is a paging request
5032 //
5033 if (!TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
5034 logError = FALSE;
5035 }
5036 break;
5037 }
5038
5039 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
5040
5041 *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
5042 logError = FALSE;
5043 break;
5044
5045 }
5046 default: {
5047 break;
5048 }
5049 } // end switch addlSenseCodeQual
5050
5051 } // end SCSI_ADSENSE_INVALID_MEDIA
5052
5053 break;
5054
5055 } // end SCSI_SENSE_MEDIUM_ERROR
5056
5057 case SCSI_SENSE_HARDWARE_ERROR: {
5058 BOOLEAN logHardwareError = TRUE;
5059
5060 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5061 "Hardware error\n"));
5062
5063 if (fdoData->LegacyErrorHandling == FALSE) {
5064 //
5065 // Hardware errors indicate something has seriously gone
5066 // wrong with the device and retries are very unlikely to
5067 // succeed so fail this request back immediately.
5068 //
5069 retry = FALSE;
5070 *Status = STATUS_DEVICE_HARDWARE_ERROR;
5071 logError = FALSE;
5072
5073 } else {
5074 //
5075 // Revert to legacy behavior. That is, retry everything by default.
5076 //
5077 retry = TRUE;
5078 *Status = STATUS_IO_DEVICE_ERROR;
5079 logError = TRUE;
5080 uniqueId = 257;
5081 logStatus = IO_ERR_CONTROLLER_ERROR;
5082 logHardwareError = FALSE;
5083
5084 //
5085 // This indicates the possibility of a dropped FC packet.
5086 //
5087 if ((addlSenseCode == SCSI_ADSENSE_LOGICAL_UNIT_ERROR && addlSenseCodeQual == SCSI_SENSEQ_TIMEOUT_ON_LOGICAL_UNIT) ||
5088 (addlSenseCode == SCSI_ADSENSE_DATA_TRANSFER_ERROR && addlSenseCodeQual == SCSI_SENSEQ_INITIATOR_RESPONSE_TIMEOUT)) {
5089 //
5090 // Fail requests that report this error back to the application.
5091 //
5092 retry = FALSE;
5093
5094 //
5095 // Log a more descriptive error and avoid a second
5096 // error message (IO_ERR_CONTROLLER_ERROR) being logged.
5097 //
5098 logHardwareError = TRUE;
5099 logError = FALSE;
5100 }
5101 }
5102
5103 //
5104 // If CRC error was returned, retry after a slight delay.
5105 //
5106 if (addlSenseCode == SCSI_ADSENSE_LUN_COMMUNICATION &&
5107 addlSenseCodeQual == SCSI_SESNEQ_COMM_CRC_ERROR) {
5108 retry = TRUE;
5109 retryInterval = 1;
5110 logHardwareError = FALSE;
5111 logError = FALSE;
5112 }
5113
5114 //
5115 // Hardware errors warrant a more descriptive error.
5116 // Specifically, we need to ensure this disk is easily
5117 // identifiable.
5118 //
5119 if (logHardwareError) {
5120 UCHAR senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
5121 UCHAR senseBufferSize = 0;
5122
5123 if (ScsiGetTotalSenseByteCountIndicated(senseBuffer,
5124 senseInfoBufferLength,
5125 &senseBufferSize)) {
5126
5127 senseBufferSize = min(senseBufferSize, senseInfoBufferLength);
5128
5129 } else {
5130 //
5131 // it's smaller than required to read the total number of
5132 // valid bytes, so just use the SenseInfoBufferLength field.
5133 //
5134 senseBufferSize = senseInfoBufferLength;
5135 }
5136
5137 ClasspQueueLogIOEventWithContextWorker(Fdo,
5138 senseBufferSize,
5139 senseBuffer,
5140 SRB_STATUS(Srb->SrbStatus),
5141 SrbGetScsiStatus(Srb),
5142 (ULONG)IO_ERROR_IO_HARDWARE_ERROR,
5143 cdbLength,
5144 cdb,
5145 NULL);
5146 }
5147
5148 break;
5149 } // end SCSI_SENSE_HARDWARE_ERROR
5150
5151 case SCSI_SENSE_ILLEGAL_REQUEST: {
5152
5153 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5154 "Illegal SCSI request\n"));
5155 *Status = STATUS_INVALID_DEVICE_REQUEST;
5156 retry = FALSE;
5157
5158 switch (addlSenseCode) {
5159
5160 case SCSI_ADSENSE_NO_SENSE: {
5161
5162 switch (addlSenseCodeQual) {
5163
5164 //
5165 // 1. Duplicate List Identifier
5166 //
5167 case SCSI_SENSEQ_OPERATION_IS_IN_PROGRESS: {
5168
5169 //
5170 // XCOPY, READ BUFFER and CHANGE ALIASES return this sense combination under
5171 // certain conditions. Since these commands aren't sent down natively by the
5172 // Windows OS, return the default error for them and only handle this sense
5173 // combination for offload data transfer commands.
5174 //
5175 if (ClasspIsOffloadDataTransferCommand(cdb)) {
5176
5177 TracePrint((TRACE_LEVEL_ERROR,
5178 TRACE_FLAG_IOCTL,
5179 "ClassInterpretSenseInfo (%p): Duplicate List Identifier (command %x, parameter field offset 0x%016llx)\n",
5180 Fdo,
5181 cdbOpcode,
5182 information));
5183
5184 NT_ASSERTMSG("Duplicate list identifier specified", FALSE);
5185
5186 //
5187 // The host should ensure that it uses unique list id for each TokenOperation request.
5188 //
5189 *Status = STATUS_OPERATION_IN_PROGRESS;
5190 }
5191 break;
5192 }
5193 }
5194 break;
5195 }
5196
5197 case SCSI_ADSENSE_LUN_COMMUNICATION: {
5198
5199 switch (addlSenseCodeQual) {
5200
5201 //
5202 // 1. Source/Destination pairing can't communicate with each other or the copy manager.
5203 //
5204 case SCSI_SENSEQ_UNREACHABLE_TARGET: {
5205
5206 TracePrint((TRACE_LEVEL_ERROR,
5207 TRACE_FLAG_IOCTL,
5208 "ClassInterpretSenseInfo (%p): Source-Destination LUNs can't communicate (command %x)\n",
5209 Fdo,
5210 cdbOpcode));
5211
5212 *Status = STATUS_DEVICE_UNREACHABLE;
5213 break;
5214 }
5215 }
5216 break;
5217 }
5218
5219 case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
5220
5221 switch (addlSenseCodeQual) {
5222
5223 //
5224 // 1. Sum of logical block fields in all block device range descriptors is greater than number
5225 // of logical blocks in the ROD minus block offset into ROD
5226 //
5227 case SCSI_SENSEQ_DATA_UNDERRUN: {
5228
5229 TracePrint((TRACE_LEVEL_ERROR,
5230 TRACE_FLAG_IOCTL,
5231 "ClassInterpretSenseInfo (%p): Host specified a transfer length greater than what is represented by the token (considering the offset) [command %x]\n",
5232 Fdo,
5233 cdbOpcode));
5234
5235 NT_ASSERTMSG("Host specified blocks to write beyond what is represented by the token", FALSE);
5236
5237 *Status = STATUS_DATA_OVERRUN;
5238 break;
5239 }
5240 }
5241 break;
5242 }
5243
5244 //
5245 // 1. Parameter data truncation (e.g. last descriptor was not fully specified)
5246 //
5247 case SCSI_ADSENSE_PARAMETER_LIST_LENGTH: {
5248
5249 TracePrint((TRACE_LEVEL_ERROR,
5250 TRACE_FLAG_IOCTL,
5251 "ClassInterpretSenseInfo (%p): Target truncated the block device range descriptors in the parameter list (command %x)\n",
5252 Fdo,
5253 cdbOpcode));
5254
5255 NT_ASSERTMSG("Parameter data truncation", FALSE);
5256
5257 *Status = STATUS_DATA_OVERRUN;
5258 break;
5259 }
5260
5261 case SCSI_ADSENSE_ILLEGAL_COMMAND: {
5262 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5263 "Illegal command\n"));
5264 break;
5265 }
5266
5267 case SCSI_ADSENSE_ILLEGAL_BLOCK: {
5268
5269 LARGE_INTEGER logicalBlockAddr;
5270 LARGE_INTEGER lastLBA;
5271 ULONG numTransferBlocks = 0;
5272
5273 logicalBlockAddr.QuadPart = 0;
5274
5275 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Illegal block address\n"));
5276
5277 *Status = STATUS_NONEXISTENT_SECTOR;
5278
5279 if (Fdo->DeviceType == FILE_DEVICE_DISK) {
5280
5281 if (IS_SCSIOP_READWRITE(cdbOpcode) && cdb) {
5282
5283 if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
5284 REVERSE_BYTES_QUAD(&logicalBlockAddr, &cdb->CDB16.LogicalBlock);
5285 REVERSE_BYTES(&numTransferBlocks, &cdb->CDB16.TransferLength);
5286 } else {
5287 REVERSE_BYTES(&logicalBlockAddr.LowPart, &cdb->CDB10.LogicalBlockByte0);
5288 REVERSE_BYTES_SHORT((PUSHORT)&numTransferBlocks, &cdb->CDB10.TransferBlocksMsb);
5289 }
5290
5291 REVERSE_BYTES_QUAD(&lastLBA, &fdoData->LastKnownDriveCapacityData.LogicalBlockAddress);
5292
5293 if ((logicalBlockAddr.QuadPart > lastLBA.QuadPart) ||
5294 ((logicalBlockAddr.QuadPart + numTransferBlocks - 1) > lastLBA.QuadPart)) {
5295
5296 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5297 "Request beyond boundary. Last LBA: 0x%I64X Read LBA: 0x%I64X Length: 0x%X\n",
5298 (__int64) lastLBA.QuadPart, (__int64) logicalBlockAddr.QuadPart, numTransferBlocks));
5299 } else {
5300 //
5301 // Should only retry these if the request was
5302 // truly within our expected size.
5303 //
5304 // Fujitsu IDE drives have been observed to
5305 // return this error transiently for a legal LBA;
5306 // manual retry in the debugger then works, so
5307 // there is a good chance that a programmed retry
5308 // will also work.
5309 //
5310
5311 retry = TRUE;
5312 retryInterval = 5;
5313 }
5314 } else if (ClasspIsOffloadDataTransferCommand(cdb)) {
5315
5316 //
5317 // 1. Number of logical blocks of block device range descriptor exceeds capacity of the medium
5318 //
5319 TracePrint((TRACE_LEVEL_ERROR,
5320 TRACE_FLAG_IOCTL,
5321 "ClassInterpretSenseInfo (%p): LBA out of range (command %x, parameter field offset 0x%016llx)\n",
5322 Fdo,
5323 cdbOpcode,
5324 information));
5325
5326 NT_ASSERTMSG("Number of blocks specified exceeds LUN capacity", FALSE);
5327 }
5328 }
5329 break;
5330 }
5331
5332 //
5333 // 1. Generic error - cause not reportable
5334 // 2. Insufficient resources to create ROD
5335 // 3. Insufficient resources to create Token
5336 // 4. Max number of tokens exceeded
5337 // 5. Remote Token creation not supported
5338 // 6. Token expired
5339 // 7. Token unknown
5340 // 8. Unsupported Token type
5341 // 9. Token corrupt
5342 // 10. Token revoked
5343 // 11. Token cancelled
5344 // 12. Remote Token usage not supported
5345 //
5346 case SCSI_ADSENSE_INVALID_TOKEN: {
5347
5348 TracePrint((TRACE_LEVEL_ERROR,
5349 TRACE_FLAG_IOCTL,
5350 "ClassInterpretSenseInfo (%p): Invalid/Expired/Modified token specified (command %x, parameter field offset 0x%016llx)\n",
5351 Fdo,
5352 cdbOpcode,
5353 information));
5354
5355 *Status = STATUS_INVALID_TOKEN;
5356 break;
5357 }
5358
5359 case SCSI_ADSENSE_INVALID_CDB: {
5360 if (ClasspIsOffloadDataTransferCommand(cdb)) {
5361
5362 //
5363 // 1. Mismatched I_T nexus and list identifier
5364 //
5365 TracePrint((TRACE_LEVEL_ERROR,
5366 TRACE_FLAG_IOCTL,
5367 "ClassInterpretSenseInfo (%p): Incorrect I_T nexus likely used (command %x)\n",
5368 Fdo,
5369 cdbOpcode));
5370
5371 //
5372 // The host should ensure that it sends TokenOperation and ReceiveTokenInformation for the same
5373 // list Id using the same I_T nexus.
5374 //
5375 *Status = STATUS_INVALID_INITIATOR_TARGET_PATH;
5376
5377 } else {
5378
5379 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5380 "Invalid CDB\n"));
5381
5382 //
5383 // Note: the retry interval is not typically used.
5384 // it is set here only because a ClassErrorHandler
5385 // cannot set the retryInterval, and the error may
5386 // require a few commands to be sent to clear whatever
5387 // caused this condition (i.e. disk clears the write
5388 // cache, requiring at least two commands)
5389 //
5390 // hopefully, this shortcoming can be changed for
5391 // blackcomb.
5392 //
5393
5394 retryInterval = 3;
5395 }
5396 break;
5397 }
5398
5399 case SCSI_ADSENSE_INVALID_LUN: {
5400 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5401 "Invalid LUN\n"));
5402 *Status = STATUS_NO_SUCH_DEVICE;
5403 break;
5404 }
5405
5406 case SCSI_ADSENSE_INVALID_FIELD_PARAMETER_LIST: {
5407
5408 switch (addlSenseCodeQual) {
5409
5410 //
5411 // 1. Alignment violation (e.g. copy manager is unable to copy because destination offset is NOT aligned to LUN's granularity/alignment)
5412 //
5413 case SCSI_SENSEQ_INVALID_RELEASE_OF_PERSISTENT_RESERVATION: {
5414
5415 TracePrint((TRACE_LEVEL_ERROR,
5416 TRACE_FLAG_IOCTL,
5417 "ClassInterpretSenseInfo (%p): Alignment violation for command %x.\n",
5418 Fdo,
5419 cdbOpcode));
5420
5421 NT_ASSERTMSG("Specified offset is not aligned to LUN's granularity", FALSE);
5422
5423 *Status = STATUS_INVALID_OFFSET_ALIGNMENT;
5424 break;
5425 }
5426
5427 //
5428 // 1. Number of block device range descriptors is greater than maximum range descriptors
5429 //
5430 case SCSI_SENSEQ_TOO_MANY_SEGMENT_DESCRIPTORS: {
5431
5432 TracePrint((TRACE_LEVEL_ERROR,
5433 TRACE_FLAG_IOCTL,
5434 "ClassInterpretSenseInfo (%p): Too many descriptors in parameter list for command %x (parameter field offset 0x%016llx)\n",
5435 Fdo,
5436 cdbOpcode,
5437 information));
5438
5439 NT_ASSERTMSG("Too many descriptors specified", FALSE);
5440
5441 *Status = STATUS_TOO_MANY_SEGMENT_DESCRIPTORS;
5442 break;
5443 }
5444
5445 default: {
5446
5447 if (ClasspIsOffloadDataTransferCommand(cdb)) {
5448
5449 //
5450 // 1. (Various) Invalid parameter length
5451 // 2. Requested inactivity timeout is greater than maximum inactivity timeout
5452 // 3. Same LBA is included in more than one block device range descriptor (overlapping LBAs)
5453 // 4. Total number of logical blocks of all block range descriptors is greater than the maximum transfer size
5454 // 5. Total number of logical blocks of all block range descriptors is greater than maximum token transfer size
5455 // (e.g. WriteUsingToken descriptors specify a cumulative total block count that exceeds the PopulateToken that created the token)
5456 // 6. Block offset into ROD specified an offset that is greater than or equal to the number of logical blocks in the ROD
5457 // 7. Number of logical blocks in a block device range descriptor is greater than maximum transfer length in blocks
5458 //
5459 TracePrint((TRACE_LEVEL_ERROR,
5460 TRACE_FLAG_IOCTL,
5461 "ClassInterpretSenseInfo (%p): Illegal field in parameter list for command %x (parameter field offset 0x%016llx) [AddSense %x, AddSenseQ %x]\n",
5462 Fdo,
5463 cdbOpcode,
5464 information,
5465 addlSenseCode,
5466 addlSenseCodeQual));
5467
5468 NT_ASSERTMSG("Invalid field in parameter list", FALSE);
5469
5470 *Status = STATUS_INVALID_FIELD_IN_PARAMETER_LIST;
5471 }
5472
5473 break;
5474 }
5475 }
5476 break;
5477 }
5478
5479 case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
5480 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5481 "Copy protection failure\n"));
5482
5483 *Status = STATUS_COPY_PROTECTION_FAILURE;
5484
5485 switch (addlSenseCodeQual) {
5486 case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
5487 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5488 "ClassInterpretSenseInfo: "
5489 "Authentication failure\n"));
5490 *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
5491 break;
5492 case SCSI_SENSEQ_KEY_NOT_PRESENT:
5493 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5494 "ClassInterpretSenseInfo: "
5495 "Key not present\n"));
5496 *Status = STATUS_CSS_KEY_NOT_PRESENT;
5497 break;
5498 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
5499 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5500 "ClassInterpretSenseInfo: "
5501 "Key not established\n"));
5502 *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
5503 break;
5504 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
5505 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5506 "ClassInterpretSenseInfo: "
5507 "Read of scrambled sector w/o "
5508 "authentication\n"));
5509 *Status = STATUS_CSS_SCRAMBLED_SECTOR;
5510 break;
5511 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
5512 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5513 "ClassInterpretSenseInfo: "
5514 "Media region does not logical unit "
5515 "region\n"));
5516 *Status = STATUS_CSS_REGION_MISMATCH;
5517 break;
5518 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
5519 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5520 "ClassInterpretSenseInfo: "
5521 "Region set error -- region may "
5522 "be permanent\n"));
5523 *Status = STATUS_CSS_RESETS_EXHAUSTED;
5524 break;
5525 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
5526
5527 break;
5528 }
5529
5530 case SCSI_ADSENSE_MUSIC_AREA: {
5531 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5532 "Music area\n"));
5533 break;
5534 }
5535
5536 case SCSI_ADSENSE_DATA_AREA: {
5537 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5538 "Data area\n"));
5539 break;
5540 }
5541
5542 case SCSI_ADSENSE_VOLUME_OVERFLOW: {
5543 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5544 "Volume overflow\n"));
5545 break;
5546 }
5547
5548 } // end switch (addlSenseCode)
5549
5550 break;
5551 } // end SCSI_SENSE_ILLEGAL_REQUEST
5552
5553 case SCSI_SENSE_UNIT_ATTENTION: {
5554
5555 ULONG count;
5556
5557 //
5558 // A media change may have occured so increment the change
5559 // count for the physical device
5560 //
5561
5562 count = InterlockedIncrement((volatile LONG *)&fdoExtension->MediaChangeCount);
5563 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
5564 "Media change count for device %d incremented to %#lx\n",
5565 fdoExtension->DeviceNumber, count));
5566
5567
5568 switch (addlSenseCode) {
5569 case SCSI_ADSENSE_MEDIUM_CHANGED: {
5570 logRetryableError = FALSE;
5571 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
5572 "Media changed\n"));
5573
5574 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
5575 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
5576 "Media Changed on non-removable device %p\n",
5577 Fdo));
5578 }
5579 ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
5580 break;
5581 }
5582
5583 case SCSI_ADSENSE_BUS_RESET: {
5584 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5585 "Bus reset\n"));
5586 break;
5587 }
5588
5589 case SCSI_ADSENSE_PARAMETERS_CHANGED: {
5590 logRetryableError = FALSE;
5591 if (addlSenseCodeQual == SCSI_SENSEQ_CAPACITY_DATA_CHANGED) {
5592 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5593 "Device capacity changed (e.g. thinly provisioned LUN). Retry the request.\n"));
5594
5595 ClassQueueCapacityChangedEventWorker(Fdo);
5596
5597 //
5598 // Retry with 1 second delay as ClassQueueCapacityChangedEventWorker may trigger a couple of commands sent to disk.
5599 //
5600 retryInterval = 1;
5601 retry = TRUE;
5602 }
5603 break;
5604 }
5605
5606 case SCSI_ADSENSE_LB_PROVISIONING: {
5607
5608 switch (addlSenseCodeQual) {
5609
5610 case SCSI_SENSEQ_SOFT_THRESHOLD_REACHED: {
5611
5612 logRetryableError = FALSE;
5613 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5614 "Device (%p) has hit a soft threshold.\n",
5615 Fdo));
5616
5617 //
5618 // This indicates that a resource provisioned or thinly
5619 // provisioned device has hit a soft threshold. Queue a
5620 // worker thread to log a system event and then retry the
5621 // original request.
5622 //
5623 ClassQueueThresholdEventWorker(Fdo);
5624 break;
5625 }
5626 default: {
5627 retry = FALSE;
5628 break;
5629 }
5630
5631 } // end switch (addlSenseCodeQual)
5632 break;
5633 }
5634
5635 case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: {
5636
5637 if (addlSenseCodeQual == SCSI_SENSEQ_MICROCODE_CHANGED) {
5638 //
5639 // Device firmware has been changed. Retry the request.
5640 //
5641 logRetryableError = TRUE;
5642 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5643 "Device firmware has been changed.\n"));
5644
5645 retryInterval = 1;
5646 retry = TRUE;
5647 } else {
5648 //
5649 // Device information has changed, we need to rescan the
5650 // bus for changed information such as the capacity.
5651 //
5652 logRetryableError = FALSE;
5653 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5654 "Device information changed. Invalidate the bus\n"));
5655
5656 if (addlSenseCodeQual == SCSI_SENSEQ_INQUIRY_DATA_CHANGED) {
5657
5658 ClassQueueProvisioningTypeChangedEventWorker(Fdo);
5659 }
5660
5661 if (addlSenseCodeQual == SCSI_SENSEQ_INQUIRY_DATA_CHANGED ||
5662 addlSenseCodeQual == SCSI_SENSEQ_OPERATING_DEFINITION_CHANGED) {
5663
5664 //
5665 // Since either the LB provisioning type changed, or the block/slab size
5666 // changed, next time anyone trying to query the FunctionSupportInfo, we
5667 // will requery the device.
5668 //
5669 InterlockedIncrement((volatile LONG *)&fdoExtension->FunctionSupportInfo->ChangeRequestCount);
5670 }
5671
5672 IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
5673 retryInterval = 5;
5674 }
5675 break;
5676 }
5677
5678 case SCSI_ADSENSE_OPERATOR_REQUEST: {
5679 switch (addlSenseCodeQual) {
5680
5681 case SCSI_SENSEQ_MEDIUM_REMOVAL: {
5682 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5683 "Ejection request received!\n"));
5684 ClassSendEjectionNotification(fdoExtension);
5685 break;
5686 }
5687
5688 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
5689 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5690 "Operator selected write permit?! "
5691 "(unsupported!)\n"));
5692 break;
5693 }
5694
5695 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
5696 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5697 "Operator selected write protect?! "
5698 "(unsupported!)\n"));
5699 break;
5700 }
5701 }
5702 }
5703
5704 case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
5705
5706 UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
5707
5708 *((PULONG)wmiEventData) = sizeof(UCHAR);
5709 wmiEventData[sizeof(ULONG)] = addlSenseCodeQual;
5710
5711 //
5712 // Don't log another eventlog if we have already logged once
5713 // NOTE: this should have been interlocked, but the structure
5714 // was publicly defined to use a BOOLEAN (char). Since
5715 // media only reports these errors once per X minutes,
5716 // the potential race condition is nearly non-existant.
5717 // the worst case is duplicate log entries, so ignore.
5718 //
5719
5720 logError = FALSE;
5721 if (fdoExtension->FailurePredicted == 0) {
5722 logError = TRUE;
5723 }
5724 fdoExtension->FailureReason = addlSenseCodeQual;
5725 logStatus = IO_WRN_FAILURE_PREDICTED;
5726
5727 ClassNotifyFailurePredicted(fdoExtension,
5728 (PUCHAR)wmiEventData,
5729 sizeof(wmiEventData),
5730 FALSE, // do not log error
5731 4, // unique error value
5732 SrbGetPathId(Srb),
5733 SrbGetTargetId(Srb),
5734 SrbGetLun(Srb));
5735
5736 fdoExtension->FailurePredicted = TRUE;
5737
5738 //
5739 // Since this is a Unit Attention we need to make
5740 // sure we retry this request.
5741 //
5742 retry = TRUE;
5743
5744 break;
5745 }
5746
5747 default: {
5748 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5749 "Unit attention\n"));
5750 break;
5751 }
5752
5753 } // end switch (addlSenseCode)
5754
5755 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
5756 {
5757
5758 if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
5759 {
5760 //
5761 // Set bit to indicate that media may have changed
5762 // and volume needs verification.
5763 //
5764
5765 SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
5766
5767 *Status = STATUS_VERIFY_REQUIRED;
5768 retry = FALSE;
5769 }
5770 else {
5771 *Status = STATUS_IO_DEVICE_ERROR;
5772 }
5773 }
5774 else
5775 {
5776 *Status = STATUS_IO_DEVICE_ERROR;
5777 }
5778
5779 break;
5780
5781 } // end SCSI_SENSE_UNIT_ATTENTION
5782
5783 case SCSI_SENSE_DATA_PROTECT: {
5784
5785 retry = FALSE;
5786
5787 if (addlSenseCode == SCSI_ADSENSE_WRITE_PROTECT)
5788 {
5789 switch (addlSenseCodeQual) {
5790
5791 case SCSI_SENSEQ_SPACE_ALLOC_FAILED_WRITE_PROTECT: {
5792
5793 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5794 "Device's (%p) resources are exhausted.\n",
5795 Fdo));
5796
5797 ClassQueueResourceExhaustionEventWorker(Fdo);
5798
5799 //
5800 // This indicates that a thinly-provisioned device has
5801 // hit a permanent resource exhaustion. We need to
5802 // return this status code so that patmgr can take the
5803 // disk offline.
5804 //
5805 *Status = STATUS_DISK_RESOURCES_EXHAUSTED;
5806 break;
5807 }
5808 default:
5809 {
5810 break;
5811 }
5812
5813 } // end switch addlSenseCodeQual
5814 }
5815 else
5816 {
5817 if (IS_SCSIOP_WRITE(cdbOpcode)) {
5818 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5819 "Media write protected\n"));
5820 *Status = STATUS_MEDIA_WRITE_PROTECTED;
5821 } else {
5822 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5823 "Access denied\n"));
5824 *Status = STATUS_ACCESS_DENIED;
5825 }
5826 }
5827 break;
5828 } // end SCSI_SENSE_DATA_PROTECT
5829
5830 case SCSI_SENSE_BLANK_CHECK: {
5831 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5832 "Media blank check\n"));
5833 retry = FALSE;
5834 *Status = STATUS_NO_DATA_DETECTED;
5835 break;
5836 } // end SCSI_SENSE_BLANK_CHECK
5837
5838 case SCSI_SENSE_COPY_ABORTED: {
5839
5840 switch (addlSenseCode) {
5841
5842 case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
5843
5844 switch (addlSenseCodeQual) {
5845
5846 //
5847 // 1. Target truncated the data transfer.
5848 //
5849 case SCSI_SENSEQ_DATA_UNDERRUN: {
5850
5851 TracePrint((TRACE_LEVEL_WARNING,
5852 TRACE_FLAG_IOCTL,
5853 "ClassInterpretSenseInfo (%p): Data transfer was truncated (command %x)\n",
5854 Fdo,
5855 cdbOpcode));
5856
5857 *Status = STATUS_SUCCESS;
5858 retry = FALSE;
5859 break;
5860 }
5861 }
5862 break;
5863 }
5864 }
5865 break;
5866 }
5867
5868 case SCSI_SENSE_ABORTED_COMMAND: {
5869 if (ClasspIsOffloadDataTransferCommand(cdb)) {
5870
5871 switch (addlSenseCode) {
5872
5873 case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
5874
5875 switch (addlSenseCodeQual) {
5876
5877 //
5878 // 1. Target truncated the data transfer.
5879 //
5880 case SCSI_SENSEQ_DATA_UNDERRUN: {
5881
5882 TracePrint((TRACE_LEVEL_WARNING,
5883 TRACE_FLAG_IOCTL,
5884 "ClassInterpretSenseInfo (%p): Target has truncated the data transfer (command %x)\n",
5885 Fdo,
5886 cdbOpcode));
5887
5888 *Status = STATUS_SUCCESS;
5889 retry = FALSE;
5890 break;
5891 }
5892 }
5893 break;
5894 }
5895
5896 case SCSI_ADSENSE_RESOURCE_FAILURE: {
5897
5898 switch (addlSenseCodeQual) {
5899
5900 //
5901 // 1. Copy manager wasn't able to finish the operation because of insuffient resources
5902 // (e.g. microsnapshot failure on read, no space on write, etc.)
5903 //
5904 case SCSI_SENSEQ_INSUFFICIENT_RESOURCES: {
5905
5906 TracePrint((TRACE_LEVEL_ERROR,
5907 TRACE_FLAG_IOCTL,
5908 "ClassInterpretSenseInfo (%p): Target has insufficient resources (command %x)\n",
5909 Fdo,
5910 cdb->CDB6GENERIC.OperationCode));
5911
5912 *Status = STATUS_INSUFFICIENT_RESOURCES;
5913 retry = FALSE;
5914 break;
5915 }
5916 }
5917 break;
5918 }
5919 }
5920 } else {
5921 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5922 "Command aborted\n"));
5923 *Status = STATUS_IO_DEVICE_ERROR;
5924 retryInterval = 1;
5925 }
5926 break;
5927 } // end SCSI_SENSE_ABORTED_COMMAND
5928
5929 default: {
5930 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5931 "Unrecognized sense code\n"));
5932 *Status = STATUS_IO_DEVICE_ERROR;
5933 break;
5934 }
5935
5936 } // end switch (senseKey)
5937
5938
5939
5940 //
5941 // Try to determine bad sector information from sense data
5942 //
5943
5944 if (((IS_SCSIOP_READWRITE(cdbOpcode)) ||
5945 (cdbOpcode == SCSIOP_VERIFY) ||
5946 (cdbOpcode == SCSIOP_VERIFY16)) && cdb) {
5947
5948 if (isInformationValid)
5949 {
5950 readSector = 0;
5951 badSector = information;
5952
5953 if (cdbOpcode == SCSIOP_READ16 || cdbOpcode == SCSIOP_WRITE16 || cdbOpcode == SCSIOP_VERIFY16) {
5954 REVERSE_BYTES_QUAD(&readSector, &(cdb->AsByte[2]));
5955 } else {
5956 REVERSE_BYTES(&readSector, &(cdb->AsByte[2]));
5957 }
5958
5959 if (cdbOpcode == SCSIOP_READ || cdbOpcode == SCSIOP_WRITE || cdbOpcode == SCSIOP_VERIFY) {
5960 REVERSE_BYTES_SHORT(&index, &(cdb->CDB10.TransferBlocksMsb));
5961 } else if (cdbOpcode == SCSIOP_READ6 || cdbOpcode == SCSIOP_WRITE6) {
5962 index = cdb->CDB6READWRITE.TransferBlocks;
5963 } else if(cdbOpcode == SCSIOP_READ12 || cdbOpcode == SCSIOP_WRITE12) {
5964 REVERSE_BYTES(&index, &(cdb->CDB12.TransferLength));
5965 } else {
5966 REVERSE_BYTES(&index, &(cdb->CDB16.TransferLength));
5967 }
5968
5969 //
5970 // Make sure the bad sector is within the read sectors.
5971 //
5972
5973 if (!(badSector >= readSector && badSector < readSector + index)) {
5974 badSector = readSector;
5975 }
5976 }
5977 }
5978 }
5979
5980 __ClassInterpretSenseInfo_ProcessingInvalidSenseBuffer:
5981
5982 if (!validSense) {
5983
5984 //
5985 // Request sense buffer not valid. No sense information
5986 // to pinpoint the error. Return general request fail.
5987 //
5988
5989 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5990 "Request sense info not valid. SrbStatus %2x\n",
5991 SRB_STATUS(Srb->SrbStatus)));
5992 retry = TRUE;
5993
5994
5995 switch (SRB_STATUS(Srb->SrbStatus)) {
5996 case SRB_STATUS_ABORTED: {
5997
5998 //
5999 // Update the error count for the device.
6000 //
6001
6002 incrementErrorCount = TRUE;
6003 *Status = STATUS_IO_TIMEOUT;
6004 retryInterval = 1;
6005 retry = TRUE;
6006 break;
6007 }
6008
6009 case SRB_STATUS_ERROR: {
6010
6011 *Status = STATUS_IO_DEVICE_ERROR;
6012 if (SrbGetScsiStatus(Srb) == SCSISTAT_GOOD) {
6013
6014 //
6015 // This is some strange return code. Update the error
6016 // count for the device.
6017 //
6018
6019 incrementErrorCount = TRUE;
6020
6021 } else if (SrbGetScsiStatus(Srb) == SCSISTAT_BUSY) {
6022
6023 *Status = STATUS_DEVICE_NOT_READY;
6024 logRetryableError = FALSE;
6025 }
6026
6027 break;
6028 }
6029
6030 case SRB_STATUS_INVALID_REQUEST: {
6031 *Status = STATUS_INVALID_DEVICE_REQUEST;
6032 retry = FALSE;
6033 break;
6034 }
6035
6036 case SRB_STATUS_INVALID_PATH_ID:
6037 case SRB_STATUS_NO_DEVICE:
6038 case SRB_STATUS_NO_HBA:
6039 case SRB_STATUS_INVALID_LUN:
6040 case SRB_STATUS_INVALID_TARGET_ID: {
6041 *Status = STATUS_NO_SUCH_DEVICE;
6042 retry = FALSE;
6043 break;
6044 }
6045
6046 case SRB_STATUS_SELECTION_TIMEOUT: {
6047 logError = TRUE;
6048 logStatus = IO_ERR_NOT_READY;
6049 uniqueId = 260;
6050 *Status = STATUS_DEVICE_NOT_CONNECTED;
6051 retry = FALSE;
6052 break;
6053 }
6054
6055 case SRB_STATUS_TIMEOUT:
6056 case SRB_STATUS_COMMAND_TIMEOUT: {
6057
6058 //
6059 // Update the error count for the device.
6060 //
6061 incrementErrorCount = TRUE;
6062 *Status = STATUS_IO_TIMEOUT;
6063 break;
6064 }
6065
6066 case SRB_STATUS_PARITY_ERROR:
6067 case SRB_STATUS_UNEXPECTED_BUS_FREE:
6068
6069 //
6070 // Update the error count for the device
6071 // and fall through to below
6072 //
6073 incrementErrorCount = TRUE;
6074
6075 case SRB_STATUS_BUS_RESET: {
6076
6077 *Status = STATUS_IO_DEVICE_ERROR;
6078 logRetryableError = FALSE;
6079 break;
6080 }
6081
6082 case SRB_STATUS_DATA_OVERRUN: {
6083
6084 *Status = STATUS_DATA_OVERRUN;
6085 retry = FALSE;
6086
6087 //
6088 // For some commands, we allocate a buffer that may be
6089 // larger than necessary. In these cases, the SRB may be
6090 // returned with SRB_STATUS_DATA_OVERRUN to indicate a
6091 // buffer *underrun*. However, the command was still
6092 // successful so we ensure STATUS_SUCCESS is returned.
6093 // We will also prevent these errors from causing noise in
6094 // the error logs.
6095 //
6096 if ((cdbOpcode == SCSIOP_MODE_SENSE && SrbGetDataTransferLength(Srb) <= cdb->MODE_SENSE.AllocationLength) ||
6097 (cdbOpcode == SCSIOP_INQUIRY && SrbGetDataTransferLength(Srb) <= cdb->CDB6INQUIRY.AllocationLength)) {
6098 *Status = STATUS_SUCCESS;
6099 logErrorInternal = FALSE;
6100 logError = FALSE;
6101 } else if (cdbOpcode == SCSIOP_MODE_SENSE10) {
6102 USHORT allocationLength;
6103 REVERSE_BYTES_SHORT(&(cdb->MODE_SENSE10.AllocationLength), &allocationLength);
6104 if (SrbGetDataTransferLength(Srb) <= allocationLength) {
6105 *Status = STATUS_SUCCESS;
6106 logErrorInternal = FALSE;
6107 logError = FALSE;
6108 }
6109 } else if (ClasspIsReceiveTokenInformation(cdb)) {
6110 ULONG allocationLength;
6111 REVERSE_BYTES(&(cdb->RECEIVE_TOKEN_INFORMATION.AllocationLength), &allocationLength);
6112 if (SrbGetDataTransferLength(Srb) <= allocationLength) {
6113 *Status = STATUS_SUCCESS;
6114 logErrorInternal = FALSE;
6115 logError = FALSE;
6116 }
6117 }
6118
6119 break;
6120 }
6121
6122 case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
6123
6124 //
6125 // Update the error count for the device.
6126 //
6127
6128 incrementErrorCount = TRUE;
6129 *Status = STATUS_IO_DEVICE_ERROR;
6130
6131 //
6132 // If there was phase sequence error then limit the number of
6133 // retries.
6134 //
6135
6136 if (RetryCount > 1 ) {
6137 retry = FALSE;
6138 }
6139
6140 break;
6141 }
6142
6143 case SRB_STATUS_REQUEST_FLUSHED: {
6144
6145 //
6146 // If the status needs verification bit is set. Then set
6147 // the status to need verification and no retry; otherwise,
6148 // just retry the request.
6149 //
6150
6151 if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
6152
6153 *Status = STATUS_VERIFY_REQUIRED;
6154 retry = FALSE;
6155
6156 } else {
6157 *Status = STATUS_IO_DEVICE_ERROR;
6158 logRetryableError = FALSE;
6159 }
6160
6161 break;
6162 }
6163
6164
6165 default: {
6166 logError = TRUE;
6167 logStatus = IO_ERR_CONTROLLER_ERROR;
6168 uniqueId = 259;
6169 *Status = STATUS_IO_DEVICE_ERROR;
6170 unhandledError = TRUE;
6171 logRetryableError = FALSE;
6172 break;
6173 }
6174 }
6175
6176
6177 //
6178 // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
6179 // we know from a previous poll when the device will be ready (ETA)
6180 // we should delay the retry more appropriately than just guessing.
6181 //
6182 /*
6183 if (fdoExtension->MediaChangeDetectionInfo &&
6184 fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
6185 TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
6186 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
6187 ) {
6188 // check if Gesn.ReadyTime if greater than current tick count
6189 // if so, delay that long (from 1 to 30 seconds max?)
6190 // else, leave the guess of time alone.
6191 }
6192 */
6193
6194 }
6195
6196 }
6197
6198 if (incrementErrorCount) {
6199
6200 //
6201 // if any error count occurred, delay the retry of this io by
6202 // at least one second, if caller supports it.
6203 //
6204
6205 if (retryInterval == 0) {
6206 retryInterval = 1;
6207 }
6208 ClasspPerfIncrementErrorCount(fdoExtension);
6209 }
6210
6211 //
6212 // If there is a class specific error handler call it.
6213 //
6214
6215 if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
6216
6217 SCSI_REQUEST_BLOCK tempSrb = {0};
6218 PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Srb;
6219
6220 //
6221 // If class driver does not support extended SRB and this is
6222 // an extended SRB, convert to legacy SRB and pass to class
6223 // driver.
6224 //
6225 if ((Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
6226 ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
6227 CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
6228 ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Srb);
6229 srbPtr = &tempSrb;
6230 }
6231
6232 fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
6233 srbPtr,
6234 Status,
6235 &retry);
6236 }
6237
6238 //
6239 // If the caller wants to know the suggested retry interval tell them.
6240 //
6241
6242 if (ARGUMENT_PRESENT(RetryInterval)) {
6243 *RetryInterval = retryInterval;
6244 }
6245
6246 //
6247 // The RESERVE(6) / RELEASE(6) commands are optional. So
6248 // if they aren't supported, try the 10-byte equivalents
6249 //
6250
6251 cdb = SrbGetCdb(Srb);
6252 if (cdb) {
6253 cdbOpcode = cdb->CDB6GENERIC.OperationCode;
6254 }
6255
6256 if ((cdbOpcode == SCSIOP_RESERVE_UNIT ||
6257 cdbOpcode == SCSIOP_RELEASE_UNIT) && cdb)
6258 {
6259 if (*Status == STATUS_INVALID_DEVICE_REQUEST)
6260 {
6261 SrbSetCdbLength(Srb, 10);
6262 cdb->CDB10.OperationCode = (cdb->CDB6GENERIC.OperationCode == SCSIOP_RESERVE_UNIT) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
6263
6264 SET_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6);
6265 retry = TRUE;
6266 }
6267 }
6268
6269 #if DBG
6270
6271 //
6272 // Ensure that for read/write requests, only return STATUS_DEVICE_BUSY if
6273 // reservation conflict.
6274 //
6275 if (IS_SCSIOP_READWRITE(cdbOpcode) && (*Status == STATUS_DEVICE_BUSY)) {
6276 NT_ASSERT(isReservationConflict == TRUE);
6277 }
6278
6279 #endif
6280
6281 /*
6282 * LOG the error:
6283 * If logErrorInternal is set, log the error in our internal log.
6284 * If logError is set, also log the error in the system log.
6285 */
6286 if (logErrorInternal || logError) {
6287 ULONG totalSize;
6288 ULONG senseBufferSize = 0;
6289 IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
6290 CLASS_ERROR_LOG_DATA staticErrLogData = {0};
6291 SENSE_DATA convertedSenseBuffer = {0};
6292 UCHAR convertedSenseBufferLength = 0;
6293 BOOLEAN senseDataConverted = FALSE;
6294
6295 //
6296 // Logic below assumes that IO_ERROR_LOG_PACKET + CLASS_ERROR_LOG_DATA
6297 // is less than ERROR_LOG_MAXIMUM_SIZE which is not true for extended SRB.
6298 // Given that classpnp currently does not use >16 byte CDB, we'll convert
6299 // an extended SRB to SCSI_REQUEST_BLOCK instead of changing this code.
6300 // More changes will need to be made when classpnp starts using >16 byte
6301 // CDBs.
6302 //
6303
6304 //
6305 // Calculate the total size of the error log entry.
6306 // add to totalSize in the order that they are used.
6307 // the advantage to calculating all the sizes here is
6308 // that we don't have to do a bunch of extraneous checks
6309 // later on in this code path.
6310 //
6311 totalSize = sizeof(IO_ERROR_LOG_PACKET) // required
6312 + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
6313
6314 //
6315 // also save any available extra sense data, up to the maximum errlog
6316 // packet size . WMI should be used for real-time analysis.
6317 // the event log should only be used for post-mortem debugging.
6318 //
6319 if ((TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) && senseBuffer) {
6320
6321 UCHAR validSenseBytes = 0;
6322 UCHAR senseInfoBufferLength = 0;
6323
6324 senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
6325
6326 //
6327 // If sense data is in Descriptor format, convert it to Fixed format
6328 // for the private log.
6329 //
6330
6331 if (IsDescriptorSenseDataFormat(senseBuffer)) {
6332
6333 convertedSenseBufferLength = sizeof(convertedSenseBuffer);
6334
6335 senseDataConverted = ScsiConvertToFixedSenseFormat(senseBuffer,
6336 senseInfoBufferLength,
6337 (PVOID)&convertedSenseBuffer,
6338 convertedSenseBufferLength);
6339 }
6340
6341 //
6342 // For System Log, copy the maximum amount of available sense data
6343 //
6344
6345 if (ScsiGetTotalSenseByteCountIndicated(senseBuffer,
6346 senseInfoBufferLength,
6347 &validSenseBytes)) {
6348
6349 //
6350 // If it is able to determine number of valid bytes,
6351 // copy the maximum amount of available
6352 // sense data that can be saved into the the errlog.
6353 //
6354
6355 //
6356 // set to save the most sense buffer possible
6357 //
6358
6359 senseBufferSize = max(validSenseBytes, sizeof(staticErrLogData.SenseData));
6360 senseBufferSize = min(senseBufferSize, senseInfoBufferLength);
6361
6362 } else {
6363 //
6364 // it's smaller than required to read the total number of
6365 // valid bytes, so just use the SenseInfoBufferLength field.
6366 //
6367 senseBufferSize = senseInfoBufferLength;
6368 }
6369
6370 /*
6371 * Bump totalSize by the number of extra senseBuffer bytes
6372 * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
6373 * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
6374 */
6375 if (senseBufferSize > sizeof(staticErrLogData.SenseData)){
6376 totalSize += senseBufferSize-sizeof(staticErrLogData.SenseData);
6377 if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
6378 senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
6379 totalSize = ERROR_LOG_MAXIMUM_SIZE;
6380 }
6381 }
6382 }
6383
6384 //
6385 // If we've used up all of our retry attempts, set the final status to
6386 // reflect the appropriate result.
6387 //
6388 // ISSUE: the test below should also check RetryCount to determine if we will actually retry,
6389 // but there is no easy test because we'd have to consider the original retry count
6390 // for the op; besides, InterpretTransferPacketError sometimes ignores the retry
6391 // decision returned by this function. So just ErrorRetried to be true in the majority case.
6392 //
6393 if (retry){
6394 staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
6395 staticErrLogData.ErrorRetried = TRUE;
6396 } else {
6397 staticErrLogEntry.FinalStatus = *Status;
6398 }
6399
6400 //
6401 // Don't log generic IO_WARNING_PAGING_FAILURE message if either the
6402 // I/O is retried, or it completed successfully.
6403 //
6404 if (logStatus == IO_WARNING_PAGING_FAILURE &&
6405 (retry || NT_SUCCESS(*Status)) ) {
6406 logError = FALSE;
6407 }
6408
6409 if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
6410 staticErrLogData.ErrorPaging = TRUE;
6411 }
6412 if (unhandledError) {
6413 staticErrLogData.ErrorUnhandled = TRUE;
6414 }
6415
6416 //
6417 // Calculate the device offset if there is a geometry.
6418 //
6419 staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
6420 staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
6421 if (logStatus == -1){
6422 staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
6423 } else {
6424 staticErrLogEntry.ErrorCode = logStatus;
6425 }
6426
6427 /*
6428 * The dump data follows the IO_ERROR_LOG_PACKET
6429 */
6430 staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET);
6431
6432 staticErrLogEntry.SequenceNumber = 0;
6433 staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
6434 staticErrLogEntry.IoControlCode = IoDeviceCode;
6435 staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
6436 staticErrLogEntry.UniqueErrorValue = uniqueId;
6437
6438 KeQueryTickCount(&staticErrLogData.TickCount);
6439 staticErrLogData.PortNumber = (ULONG)-1;
6440
6441 /*
6442 * Save the entire contents of the SRB.
6443 */
6444 if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
6445 ClasspConvertToScsiRequestBlock(&staticErrLogData.Srb, (PSTORAGE_REQUEST_BLOCK)Srb);
6446 } else {
6447 staticErrLogData.Srb = *(PSCSI_REQUEST_BLOCK)Srb;
6448 }
6449
6450 /*
6451 * For our private log, save just the default length of the SENSE_DATA.
6452 */
6453
6454 if ((senseBufferSize != 0) && senseBuffer) {
6455
6456 //
6457 // If sense buffer is in Fixed format, put it in the private log
6458 //
6459 // If sense buffer is in Descriptor format, put it in the private log if conversion to Fixed format
6460 // succeeded. Otherwise, do not put it in the private log.
6461 //
6462 // If sense buffer is in unknown format, the device or the driver probably does not populate
6463 // the first byte of sense data, we probably still want to log error in this case assuming
6464 // it's fixed format, so that its sense key, its additional sense code, and its additional sense code
6465 // qualifier would be shown in the debugger extension output. By doing so, it minimizes any potential
6466 // negative impacts to our ability to diagnose issue.
6467 //
6468 if (IsDescriptorSenseDataFormat(senseBuffer)) {
6469 if (senseDataConverted) {
6470 RtlCopyMemory(&staticErrLogData.SenseData, &convertedSenseBuffer, min(convertedSenseBufferLength, sizeof(staticErrLogData.SenseData)));
6471 }
6472 } else {
6473 RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(staticErrLogData.SenseData)));
6474 }
6475 }
6476
6477 /*
6478 * Save the error log in our context.
6479 * We only save the default sense buffer length.
6480 */
6481 if (logErrorInternal) {
6482 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
6483 fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
6484 fdoData->ErrorLogNextIndex++;
6485 fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
6486 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
6487 }
6488
6489 /*
6490 * Log an event if an IO is being retried for reasons that may indicate
6491 * a transient/permanent problem with the I_T_L nexus. But log the event
6492 * only once per retried IO.
6493 */
6494 if (!IS_SCSIOP_READWRITE(cdbOpcode) ||
6495 !retry ||
6496 (RetryCount != 0)) {
6497
6498 logRetryableError = FALSE;
6499 }
6500
6501 if (logRetryableError) {
6502
6503 logError = TRUE;
6504 }
6505
6506 /*
6507 * If logError is set, also save this log in the system's error log.
6508 * But make sure we don't log TUR failures over and over
6509 * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
6510 */
6511
6512 if (logError)
6513 {
6514 //
6515 // We do not want to log certain system events repetitively
6516 //
6517
6518 cdb = SrbGetCdb(Srb);
6519 if (cdb) {
6520 switch (cdb->CDB10.OperationCode)
6521 {
6522 case SCSIOP_TEST_UNIT_READY:
6523 {
6524 if (fdoData->LoggedTURFailureSinceLastIO)
6525 {
6526 logError = FALSE;
6527 }
6528 else
6529 {
6530 fdoData->LoggedTURFailureSinceLastIO = TRUE;
6531 }
6532
6533 break;
6534 }
6535
6536 case SCSIOP_SYNCHRONIZE_CACHE:
6537 {
6538 if (fdoData->LoggedSYNCFailure)
6539 {
6540 logError = FALSE;
6541 }
6542 else
6543 {
6544 fdoData->LoggedSYNCFailure = TRUE;
6545 }
6546
6547 break;
6548 }
6549 }
6550 }
6551 }
6552
6553 if (logError){
6554
6555 if (logRetryableError) {
6556
6557 NT_ASSERT(IS_SCSIOP_READWRITE(cdbOpcode));
6558
6559 //
6560 // A large Disk TimeOutValue (like 60 seconds) results in giving a command a
6561 // large window to complete in. However, if the target returns a retryable error
6562 // just prior to the command timing out, and if multiple retries kick in, it may
6563 // take a significantly long time for the request to complete back to the
6564 // application, leading to a user perception of a hung system. So log an event
6565 // for retried IO so that an admin can help explain the reason for this behavior.
6566 //
6567 ClasspQueueLogIOEventWithContextWorker(Fdo,
6568 senseBufferSize,
6569 senseBuffer,
6570 SRB_STATUS(Srb->SrbStatus),
6571 SrbGetScsiStatus(Srb),
6572 (ULONG)IO_WARNING_IO_OPERATION_RETRIED,
6573 cdbLength,
6574 cdb,
6575 NULL);
6576
6577 } else {
6578
6579 PIO_ERROR_LOG_PACKET errorLogEntry;
6580 PCLASS_ERROR_LOG_DATA errlogData;
6581
6582 errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
6583 if (errorLogEntry){
6584 errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
6585
6586 *errorLogEntry = staticErrLogEntry;
6587 *errlogData = staticErrLogData;
6588
6589 /*
6590 * For the system log, copy as much of the sense buffer as possible.
6591 */
6592 if ((senseBufferSize != 0) && senseBuffer) {
6593 RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
6594 }
6595
6596 /*
6597 * Write the error log packet to the system error logging thread.
6598 * It will be freed by the kernel.
6599 */
6600 IoWriteErrorLogEntry(errorLogEntry);
6601 }
6602 }
6603 }
6604 }
6605
6606 return retry;
6607
6608 } // end ClassInterpretSenseInfo()
6609
6610
6611 /*++////////////////////////////////////////////////////////////////////////////
6612
6613 ClassModeSense()
6614
6615 Routine Description:
6616
6617 This routine sends a mode sense command to a target ID and returns
6618 when it is complete.
6619
6620 Arguments:
6621
6622 Fdo - Supplies the functional device object associated with this request.
6623
6624 ModeSenseBuffer - Supplies a buffer to store the sense data.
6625
6626 Length - Supplies the length in bytes of the mode sense buffer.
6627
6628 PageMode - Supplies the page or pages of mode sense data to be retrived.
6629
6630 Return Value:
6631
6632 Length of the transferred data is returned.
6633
6634 --*/
6635 ULONG
6636 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
6637 ClassModeSense(
6638 _In_ PDEVICE_OBJECT Fdo,
6639 _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
6640 _In_ ULONG Length,
6641 _In_ UCHAR PageMode
6642 )
6643 {
6644 PAGED_CODE();
6645
6646 return ClasspModeSense(Fdo,
6647 ModeSenseBuffer,
6648 Length,
6649 PageMode,
6650 MODE_SENSE_CURRENT_VALUES);
6651 }
6652
6653
6654 ULONG
6655 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
6656 ClassModeSenseEx(
6657 _In_ PDEVICE_OBJECT Fdo,
6658 _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
6659 _In_ ULONG Length,
6660 _In_ UCHAR PageMode,
6661 _In_ UCHAR PageControl
6662 )
6663 {
6664 PAGED_CODE();
6665 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
6666 return ClasspModeSense(Fdo,
6667 ModeSenseBuffer,
6668 Length,
6669 PageMode,
6670 PageControl);
6671 #else
6672 UNREFERENCED_PARAMETER(Fdo);
6673 UNREFERENCED_PARAMETER(ModeSenseBuffer);
6674 UNREFERENCED_PARAMETER(Length);
6675 UNREFERENCED_PARAMETER(PageMode);
6676 UNREFERENCED_PARAMETER(PageControl);
6677 return 0;
6678 #endif
6679 }
6680
6681 ULONG ClasspModeSense(
6682 _In_ PDEVICE_OBJECT Fdo,
6683 _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
6684 _In_ ULONG Length,
6685 _In_ UCHAR PageMode,
6686 _In_ UCHAR PageControl
6687 )
6688 /*
6689 Routine Description:
6690
6691 This routine sends a mode sense command to a target ID and returns
6692 when it is complete.
6693
6694 Arguments:
6695
6696 Fdo - Supplies the functional device object associated with this request.
6697
6698 ModeSenseBuffer - Supplies a buffer to store the sense data.
6699
6700 Length - Supplies the length in bytes of the mode sense buffer.
6701
6702 PageMode - Supplies the page or pages of mode sense data to be retrived.
6703
6704 PageControl - Supplies the page control value of the request, which is
6705 one of the following:
6706 MODE_SENSE_CURRENT_VALUES
6707 MODE_SENSE_CHANGEABLE_VALUES
6708 MODE_SENSE_DEFAULT_VAULES
6709 MODE_SENSE_SAVED_VALUES
6710
6711 Return Value:
6712
6713 Length of the transferred data is returned.
6714
6715 --*/
6716 {
6717 ULONG lengthTransferred = 0;
6718 PMDL senseBufferMdl;
6719
6720 PAGED_CODE();
6721
6722 senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
6723 if (senseBufferMdl){
6724
6725 TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
6726 if (pkt){
6727 KEVENT event;
6728 IRP pseudoIrp = {0};
6729
6730 /*
6731 * Store the number of packets servicing the irp (one)
6732 * inside the original IRP. It will be used to counted down
6733 * to zero when the packet completes.
6734 * Initialize the original IRP's status to success.
6735 * If the packet fails, we will set it to the error status.
6736 */
6737 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
6738 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
6739 pseudoIrp.IoStatus.Information = 0;
6740 pseudoIrp.MdlAddress = senseBufferMdl;
6741
6742 /*
6743 * Set this up as a SYNCHRONOUS transfer, submit it,
6744 * and wait for the packet to complete. The result
6745 * status will be written to the original irp.
6746 */
6747 NT_ASSERT(Length <= 0x0ff);
6748 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
6749 SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, 0, &pseudoIrp, PageControl);
6750 SubmitTransferPacket(pkt);
6751 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
6752
6753 if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
6754 lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
6755 }
6756 else {
6757 /*
6758 * This request can sometimes fail legitimately
6759 * (e.g. when a SCSI device is attached but turned off)
6760 * so this is not necessarily a device/driver bug.
6761 */
6762 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClasspModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
6763 }
6764 }
6765
6766 FreeDeviceInputMdl(senseBufferMdl);
6767 }
6768
6769 return lengthTransferred;
6770 }
6771
6772 /*++////////////////////////////////////////////////////////////////////////////
6773
6774 ClassFindModePage()
6775
6776 Routine Description:
6777
6778 This routine scans through the mode sense data and finds the requested
6779 mode sense page code.
6780
6781 Arguments:
6782 ModeSenseBuffer - Supplies a pointer to the mode sense data.
6783
6784 Length - Indicates the length of valid data.
6785
6786 PageMode - Supplies the page mode to be searched for.
6787
6788 Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
6789
6790 Return Value:
6791
6792 A pointer to the the requested mode page. If the mode page was not found
6793 then NULL is return.
6794
6795 --*/
6796 PVOID
6797 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
6798 ClassFindModePage(
6799 _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
6800 _In_ ULONG Length,
6801 _In_ UCHAR PageMode,
6802 _In_ BOOLEAN Use6Byte
6803 )
6804 {
6805 PUCHAR limit;
6806 ULONG parameterHeaderLength;
6807 PVOID result = NULL;
6808
6809 limit = (PUCHAR)ModeSenseBuffer + Length;
6810 parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
6811
6812 if (Length >= parameterHeaderLength) {
6813
6814 PMODE_PARAMETER_HEADER10 modeParam10;
6815 ULONG blockDescriptorLength;
6816
6817 /*
6818 * Skip the mode select header and block descriptors.
6819 */
6820 if (Use6Byte){
6821 blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
6822 }
6823 else {
6824 modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
6825 blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
6826 }
6827
6828 ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
6829
6830 //
6831 // ModeSenseBuffer now points at pages. Walk the pages looking for the
6832 // requested page until the limit is reached.
6833 //
6834
6835 while (ModeSenseBuffer +
6836 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < (PCHAR)limit) {
6837
6838 if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
6839
6840 /*
6841 * found the mode page. make sure it's safe to touch it all
6842 * before returning the pointer to caller
6843 */
6844
6845 if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > (PCHAR)limit) {
6846 /*
6847 * Return NULL since the page is not safe to access in full
6848 */
6849 result = NULL;
6850 }
6851 else {
6852 result = ModeSenseBuffer;
6853 }
6854 break;
6855 }
6856
6857 //
6858 // Advance to the next page which is 4-byte-aligned offset after this page.
6859 //
6860 ModeSenseBuffer +=
6861 ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
6862 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
6863
6864 }
6865 }
6866
6867 return result;
6868 } // end ClassFindModePage()
6869
6870
6871 NTSTATUS
6872 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
6873 ClassModeSelect(
6874 _In_ PDEVICE_OBJECT Fdo,
6875 _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
6876 _In_ ULONG Length,
6877 _In_ BOOLEAN SavePages
6878 )
6879 {
6880 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
6881 return ClasspModeSelect(Fdo,
6882 ModeSelectBuffer,
6883 Length,
6884 SavePages);
6885 #else
6886 UNREFERENCED_PARAMETER(Fdo);
6887 UNREFERENCED_PARAMETER(ModeSelectBuffer);
6888 UNREFERENCED_PARAMETER(Length);
6889 UNREFERENCED_PARAMETER(SavePages);
6890 return STATUS_NOT_SUPPORTED;
6891 #endif
6892 }
6893
6894 /*++
6895 ClasspModeSelect()
6896
6897 Routine Description:
6898
6899 This routine sends a mode select command to a target ID and returns
6900 when it is complete.
6901
6902 Arguments:
6903
6904 Fdo - Supplies the functional device object associated with this request.
6905
6906 ModeSelectBuffer - Supplies a buffer to the select data.
6907
6908 Length - Supplies the length in bytes of the mode select buffer.
6909
6910 SavePages - Specifies the value of the save pages (SP) bit in the mode
6911 select command.
6912
6913 Return Value:
6914
6915 NTSTATUS code of the request.
6916
6917 --*/
6918 NTSTATUS
6919 ClasspModeSelect(
6920 _In_ PDEVICE_OBJECT Fdo,
6921 _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
6922 _In_ ULONG Length,
6923 _In_ BOOLEAN SavePages
6924 )
6925 {
6926
6927 PMDL senseBufferMdl;
6928 NTSTATUS status = STATUS_UNSUCCESSFUL;
6929
6930 senseBufferMdl = BuildDeviceInputMdl(ModeSelectBuffer, Length);
6931 if (senseBufferMdl) {
6932
6933 TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
6934 if (pkt){
6935 KEVENT event;
6936 IRP pseudoIrp = {0};
6937
6938 /*
6939 * Store the number of packets servicing the irp (one)
6940 * inside the original IRP. It will be used to counted down
6941 * to zero when the packet completes.
6942 * Initialize the original IRP's status to success.
6943 * If the packet fails, we will set it to the error status.
6944 */
6945 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
6946 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
6947 pseudoIrp.IoStatus.Information = 0;
6948 pseudoIrp.MdlAddress = senseBufferMdl;
6949
6950 /*
6951 * Set this up as a SYNCHRONOUS transfer, submit it,
6952 * and wait for the packet to complete. The result
6953 * status will be written to the original irp.
6954 */
6955 NT_ASSERT(Length <= 0x0ff);
6956 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
6957 SetupModeSelectTransferPacket(pkt, &event, ModeSelectBuffer, (UCHAR)Length, SavePages, &pseudoIrp);
6958 SubmitTransferPacket(pkt);
6959 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
6960
6961 if (!NT_SUCCESS(pseudoIrp.IoStatus.Status)){
6962 /*
6963 * This request can sometimes fail legitimately
6964 * (e.g. when a SCSI device is attached but turned off)
6965 * so this is not necessarily a device/driver bug.
6966 */
6967 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassModeSelect on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
6968 }
6969
6970 status = pseudoIrp.IoStatus.Status;
6971 }
6972
6973 FreeDeviceInputMdl(senseBufferMdl);
6974 } else {
6975 status = STATUS_INSUFFICIENT_RESOURCES;
6976 }
6977
6978 return status;
6979 }
6980
6981 /*++////////////////////////////////////////////////////////////////////////////
6982
6983 ClassSendSrbAsynchronous()
6984
6985 Routine Description:
6986
6987 This routine takes a partially built Srb and an Irp and sends it down to
6988 the port driver.
6989
6990 This routine must be called with the remove lock held for the specified
6991 Irp.
6992
6993 Arguments:
6994
6995 Fdo - Supplies the functional device object for the orginal request.
6996
6997 Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
6998 CDB and the SRB timeout value must be filled in. The SRB must not be
6999 allocated from zone.
7000
7001 Irp - Supplies the requesting Irp.
7002
7003 BufferAddress - Supplies a pointer to the buffer to be transfered.
7004
7005 BufferLength - Supplies the length of data transfer.
7006
7007 WriteToDevice - Indicates the data transfer will be from system memory to
7008 device.
7009
7010 Return Value:
7011
7012 Returns STATUS_PENDING if the request is dispatched (since the
7013 completion routine may change the irp's status value we cannot simply
7014 return the value of the dispatch)
7015
7016 or returns a status value to indicate why it failed.
7017
7018 --*/
7019 _Success_(return == STATUS_PENDING)
7020 NTSTATUS
7021 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
7022 ClassSendSrbAsynchronous(
7023 _In_ PDEVICE_OBJECT Fdo,
7024 _Inout_ __on_failure(__drv_freesMem(Mem)) __drv_aliasesMem PSCSI_REQUEST_BLOCK _Srb,
7025 _In_ PIRP Irp,
7026 _In_reads_bytes_opt_(BufferLength) __drv_aliasesMem PVOID BufferAddress,
7027 _In_ ULONG BufferLength,
7028 _In_ BOOLEAN WriteToDevice
7029 )
7030 {
7031
7032 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
7033 PIO_STACK_LOCATION irpStack;
7034 PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
7035
7036 ULONG savedFlags;
7037
7038 if (Srb->Function != SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
7039 //
7040 // Write length to SRB.
7041 //
7042
7043 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
7044
7045 //
7046 // Set SCSI bus address.
7047 //
7048
7049 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
7050 }
7051
7052 //
7053 // This is a violation of the SCSI spec but it is required for
7054 // some targets.
7055 //
7056
7057 // Srb->Cdb[1] |= deviceExtension->Lun << 5;
7058
7059 //
7060 // Indicate auto request sense by specifying buffer and size.
7061 //
7062
7063 SrbSetSenseInfoBuffer(Srb, fdoExtension->SenseData);
7064 SrbSetSenseInfoBufferLength(Srb, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(fdoExtension));
7065
7066 SrbSetDataBuffer(Srb, BufferAddress);
7067
7068 //
7069 // Set the transfer length.
7070 //
7071 SrbSetDataTransferLength(Srb, BufferLength);
7072
7073 //
7074 // Save the class driver specific flags away.
7075 //
7076
7077 savedFlags = SrbGetSrbFlags(Srb) & SRB_FLAGS_CLASS_DRIVER_RESERVED;
7078
7079 //
7080 // Allow the caller to specify that they do not wish
7081 // IoStartNextPacket() to be called in the completion routine.
7082 //
7083
7084 SET_FLAG(savedFlags, (SrbGetSrbFlags(Srb) & SRB_FLAGS_DONT_START_NEXT_PACKET));
7085
7086 //
7087 // If caller wants to this request to be tagged, save this fact.
7088 //
7089
7090 if ( TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_QUEUE_ACTION_ENABLE) &&
7091 ( SRB_SIMPLE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ||
7092 SRB_HEAD_OF_QUEUE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ||
7093 SRB_ORDERED_QUEUE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ) ) {
7094
7095 SET_FLAG(savedFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
7096 if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_NO_QUEUE_FREEZE)) {
7097 SET_FLAG(savedFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
7098 }
7099 }
7100
7101 if (BufferAddress != NULL) {
7102
7103 //
7104 // Build Mdl if necessary.
7105 //
7106
7107 if (Irp->MdlAddress == NULL) {
7108
7109 PMDL mdl;
7110
7111 mdl = IoAllocateMdl(BufferAddress,
7112 BufferLength,
7113 FALSE,
7114 FALSE,
7115 Irp);
7116
7117 if ((mdl == NULL) || (Irp->MdlAddress == NULL)) {
7118
7119 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
7120
7121 //
7122 // ClassIoComplete() would have free'd the srb
7123 //
7124
7125 if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
7126 FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
7127 }
7128 ClassFreeOrReuseSrb(fdoExtension, (PSCSI_REQUEST_BLOCK)Srb);
7129 ClassReleaseRemoveLock(Fdo, Irp);
7130 ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
7131
7132 return STATUS_INSUFFICIENT_RESOURCES;
7133 }
7134
7135 SET_FLAG(savedFlags, SRB_CLASS_FLAGS_FREE_MDL);
7136
7137 MmBuildMdlForNonPagedPool(Irp->MdlAddress);
7138
7139 } else {
7140
7141 //
7142 // Make sure the buffer requested matches the MDL.
7143 //
7144
7145 NT_ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
7146 }
7147
7148 //
7149 // Set read flag.
7150 //
7151
7152 SrbAssignSrbFlags(Srb, WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN);
7153
7154 } else {
7155
7156 //
7157 // Clear flags.
7158 //
7159
7160 SrbAssignSrbFlags(Srb, SRB_FLAGS_NO_DATA_TRANSFER);
7161 }
7162
7163 //
7164 // Restore saved flags.
7165 //
7166
7167 SrbSetSrbFlags(Srb, savedFlags);
7168
7169 //
7170 // Disable synchronous transfer for these requests.
7171 //
7172
7173 SrbSetSrbFlags(Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
7174
7175 //
7176 // Zero out status.
7177 //
7178
7179 SrbSetScsiStatus(Srb, 0);
7180 Srb->SrbStatus = 0;
7181
7182 SrbSetNextSrb(Srb, NULL);
7183
7184 //
7185 // Save a few parameters in the current stack location.
7186 //
7187
7188 irpStack = IoGetCurrentIrpStackLocation(Irp);
7189
7190 //
7191 // Save retry count in current Irp stack.
7192 //
7193
7194 irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
7195
7196 //
7197 // Set up IoCompletion routine address.
7198 //
7199
7200 IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
7201
7202 //
7203 // Get next stack location and
7204 // set major function code.
7205 //
7206
7207 irpStack = IoGetNextIrpStackLocation(Irp);
7208
7209 irpStack->MajorFunction = IRP_MJ_SCSI;
7210
7211 //
7212 // Save SRB address in next stack for port driver.
7213 //
7214
7215 irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;
7216
7217 //
7218 // Set up Irp Address.
7219 //
7220
7221 SrbSetOriginalRequest(Srb, Irp);
7222
7223 //
7224 // Call the port driver to process the request.
7225 //
7226
7227 IoMarkIrpPending(Irp);
7228
7229 IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
7230
7231 return STATUS_PENDING;
7232
7233 } // end ClassSendSrbAsynchronous()
7234
7235 /*++////////////////////////////////////////////////////////////////////////////
7236
7237 ClassDeviceControlDispatch()
7238
7239 Routine Description:
7240
7241 The routine is the common class driver device control dispatch entry point.
7242 This routine is invokes the device-specific drivers DeviceControl routine,
7243 (which may call the Class driver's common DeviceControl routine).
7244
7245 Arguments:
7246
7247 DeviceObject - Supplies a pointer to the device object for this request.
7248
7249 Irp - Supplies the Irp making the request.
7250
7251 Return Value:
7252
7253 Returns the status returned from the device-specific driver.
7254
7255 --*/
7256 NTSTATUS
7257 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
7258 ClassDeviceControlDispatch(
7259 PDEVICE_OBJECT DeviceObject,
7260 PIRP Irp
7261 )
7262 {
7263
7264 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7265 ULONG isRemoved;
7266
7267 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
7268 _Analysis_assume_(isRemoved);
7269 if(isRemoved) {
7270
7271 ClassReleaseRemoveLock(DeviceObject, Irp);
7272
7273 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
7274 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7275 return STATUS_DEVICE_DOES_NOT_EXIST;
7276 }
7277
7278 //
7279 // Call the class specific driver DeviceControl routine.
7280 // If it doesn't handle it, it will call back into ClassDeviceControl.
7281 //
7282
7283 NT_ASSERT(commonExtension->DevInfo->ClassDeviceControl);
7284
7285 return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
7286 } // end ClassDeviceControlDispatch()
7287
7288
7289 /*++////////////////////////////////////////////////////////////////////////////
7290
7291 ClassDeviceControl()
7292
7293 Routine Description:
7294
7295 The routine is the common class driver device control dispatch function.
7296 This routine is called by a class driver when it get an unrecognized
7297 device control request. This routine will perform the correct action for
7298 common requests such as lock media. If the device request is unknown it
7299 passed down to the next level.
7300
7301 This routine must be called with the remove lock held for the specified
7302 irp.
7303
7304 Arguments:
7305
7306 DeviceObject - Supplies a pointer to the device object for this request.
7307
7308 Irp - Supplies the Irp making the request.
7309
7310 Return Value:
7311
7312 Returns back a STATUS_PENDING or a completion status.
7313
7314 --*/
7315 NTSTATUS
7316 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
7317 ClassDeviceControl(
7318 _In_ PDEVICE_OBJECT DeviceObject,
7319 _Inout_ PIRP Irp
7320 )
7321 {
7322 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7323
7324 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
7325 PIO_STACK_LOCATION nextStack = NULL;
7326
7327 ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
7328
7329 PSCSI_REQUEST_BLOCK srb = NULL;
7330 PCDB cdb = NULL;
7331
7332 NTSTATUS status;
7333 ULONG modifiedIoControlCode = 0;
7334 GUID activityId = {0};
7335
7336
7337 //
7338 // If this is a pass through I/O control, set the minor function code
7339 // and device address and pass it to the port driver.
7340 //
7341
7342 if ( (controlCode == IOCTL_SCSI_PASS_THROUGH) ||
7343 (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ||
7344 (controlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
7345 (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX) ) {
7346
7347
7348
7349 //
7350 // Validiate the user buffer for SCSI pass through.
7351 // For pass through EX: as the handler will validate the size anyway,
7352 // do not apply the similar check and leave the work to the handler.
7353 //
7354 if ( (controlCode == IOCTL_SCSI_PASS_THROUGH) ||
7355 (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ) {
7356
7357 #if BUILD_WOW64_ENABLED && defined(_WIN64)
7358
7359 if (IoIs32bitProcess(Irp)) {
7360
7361 if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
7362
7363 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
7364
7365 ClassReleaseRemoveLock(DeviceObject, Irp);
7366 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7367
7368 status = STATUS_INVALID_PARAMETER;
7369 goto SetStatusAndReturn;
7370 }
7371 }
7372 else
7373
7374 #endif
7375
7376 {
7377 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
7378 sizeof(SCSI_PASS_THROUGH)) {
7379
7380 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
7381
7382 ClassReleaseRemoveLock(DeviceObject, Irp);
7383 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7384
7385 status = STATUS_INVALID_PARAMETER;
7386 goto SetStatusAndReturn;
7387 }
7388 }
7389 }
7390
7391
7392 IoCopyCurrentIrpStackLocationToNext(Irp);
7393
7394 nextStack = IoGetNextIrpStackLocation(Irp);
7395 nextStack->MinorFunction = 1;
7396
7397 ClassReleaseRemoveLock(DeviceObject, Irp);
7398
7399 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
7400 goto SetStatusAndReturn;
7401
7402 }
7403
7404 Irp->IoStatus.Information = 0;
7405
7406
7407 switch (controlCode) {
7408
7409 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
7410
7411 PMOUNTDEV_UNIQUE_ID uniqueId;
7412
7413 if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
7414 status = STATUS_INVALID_PARAMETER;
7415 break;
7416 }
7417
7418 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7419 sizeof(MOUNTDEV_UNIQUE_ID)) {
7420
7421 status = STATUS_BUFFER_TOO_SMALL;
7422 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
7423 break;
7424 }
7425
7426 uniqueId = Irp->AssociatedIrp.SystemBuffer;
7427 RtlZeroMemory(uniqueId, sizeof(MOUNTDEV_UNIQUE_ID));
7428 uniqueId->UniqueIdLength =
7429 commonExtension->MountedDeviceInterfaceName.Length;
7430
7431 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7432 sizeof(USHORT) + uniqueId->UniqueIdLength) {
7433
7434 status = STATUS_BUFFER_OVERFLOW;
7435 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
7436 break;
7437 }
7438
7439 RtlCopyMemory(uniqueId->UniqueId,
7440 commonExtension->MountedDeviceInterfaceName.Buffer,
7441 uniqueId->UniqueIdLength);
7442
7443 status = STATUS_SUCCESS;
7444 Irp->IoStatus.Information = sizeof(USHORT) +
7445 uniqueId->UniqueIdLength;
7446 break;
7447 }
7448
7449 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
7450
7451 PMOUNTDEV_NAME name;
7452
7453 NT_ASSERT(commonExtension->DeviceName.Buffer);
7454
7455 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7456 sizeof(MOUNTDEV_NAME)) {
7457
7458 status = STATUS_BUFFER_TOO_SMALL;
7459 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
7460 break;
7461 }
7462
7463 name = Irp->AssociatedIrp.SystemBuffer;
7464 RtlZeroMemory(name, sizeof(MOUNTDEV_NAME));
7465 name->NameLength = commonExtension->DeviceName.Length;
7466
7467 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7468 sizeof(USHORT) + name->NameLength) {
7469
7470 status = STATUS_BUFFER_OVERFLOW;
7471 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
7472 break;
7473 }
7474
7475 RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
7476 name->NameLength);
7477
7478 status = STATUS_SUCCESS;
7479 Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
7480 break;
7481 }
7482
7483 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
7484
7485 PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
7486 WCHAR driveLetterNameBuffer[10] = {0};
7487 RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
7488 PWSTR valueName;
7489 UNICODE_STRING driveLetterName;
7490
7491 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7492 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
7493
7494 status = STATUS_BUFFER_TOO_SMALL;
7495 Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
7496 break;
7497 }
7498
7499 valueName = ExAllocatePoolWithTag(
7500 PagedPool,
7501 commonExtension->DeviceName.Length + sizeof(WCHAR),
7502 '8CcS');
7503
7504 if (!valueName) {
7505 status = STATUS_INSUFFICIENT_RESOURCES;
7506 break;
7507 }
7508
7509 RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
7510 commonExtension->DeviceName.Length);
7511 valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
7512
7513 driveLetterName.Buffer = driveLetterNameBuffer;
7514 driveLetterName.MaximumLength = sizeof(driveLetterNameBuffer);
7515 driveLetterName.Length = 0;
7516
7517 queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
7518 RTL_QUERY_REGISTRY_DIRECT |
7519 RTL_QUERY_REGISTRY_TYPECHECK;
7520 queryTable[0].Name = valueName;
7521 queryTable[0].EntryContext = &driveLetterName;
7522 queryTable->DefaultType = (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
7523
7524 status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
7525 L"\\Registry\\Machine\\System\\DISK",
7526 queryTable, NULL, NULL);
7527
7528 if (!NT_SUCCESS(status)) {
7529 FREE_POOL(valueName);
7530 break;
7531 }
7532
7533 if (driveLetterName.Length == 4 &&
7534 driveLetterName.Buffer[0] == '%' &&
7535 driveLetterName.Buffer[1] == ':') {
7536
7537 driveLetterName.Buffer[0] = 0xFF;
7538
7539 } else if (driveLetterName.Length != 4 ||
7540 driveLetterName.Buffer[0] < FirstDriveLetter ||
7541 driveLetterName.Buffer[0] > LastDriveLetter ||
7542 driveLetterName.Buffer[1] != ':') {
7543
7544 status = STATUS_NOT_FOUND;
7545 FREE_POOL(valueName);
7546 break;
7547 }
7548
7549 suggestedName = Irp->AssociatedIrp.SystemBuffer;
7550 RtlZeroMemory(suggestedName, sizeof(MOUNTDEV_SUGGESTED_LINK_NAME));
7551 suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
7552 suggestedName->NameLength = 28;
7553
7554 Irp->IoStatus.Information =
7555 FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
7556
7557 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7558 Irp->IoStatus.Information) {
7559
7560 Irp->IoStatus.Information =
7561 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
7562 status = STATUS_BUFFER_OVERFLOW;
7563 FREE_POOL(valueName);
7564 break;
7565 }
7566
7567 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
7568 L"\\Registry\\Machine\\System\\DISK",
7569 valueName);
7570
7571 FREE_POOL(valueName);
7572
7573 RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
7574 suggestedName->Name[12] = driveLetterName.Buffer[0];
7575 suggestedName->Name[13] = ':';
7576
7577 //
7578 // NT_SUCCESS(status) based on RtlQueryRegistryValues
7579 //
7580 status = STATUS_SUCCESS;
7581
7582 break;
7583 }
7584
7585 default:
7586 status = STATUS_PENDING;
7587 break;
7588 }
7589
7590 if (status != STATUS_PENDING) {
7591 ClassReleaseRemoveLock(DeviceObject, Irp);
7592 Irp->IoStatus.Status = status;
7593
7594
7595 IoCompleteRequest(Irp, IO_NO_INCREMENT);
7596 return status;
7597 }
7598
7599 if (commonExtension->IsFdo){
7600
7601 PULONG_PTR function;
7602 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
7603 size_t sizeNeeded;
7604
7605 //
7606 // Allocate a SCSI SRB for handling various IOCTLs.
7607 // NOTE - there is a case where an IOCTL is sent to classpnp before AdapterDescriptor
7608 // is initialized. In this case, default to legacy SRB.
7609 //
7610 if ((fdoExtension->AdapterDescriptor != NULL) &&
7611 (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
7612 sizeNeeded = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
7613 } else {
7614 sizeNeeded = sizeof(SCSI_REQUEST_BLOCK);
7615 }
7616
7617 srb = ExAllocatePoolWithTag(NonPagedPoolNx,
7618 sizeNeeded +
7619 (sizeof(ULONG_PTR) * 2),
7620 '9CcS');
7621
7622 if (srb == NULL) {
7623
7624 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
7625 ClassReleaseRemoveLock(DeviceObject, Irp);
7626 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7627 status = STATUS_INSUFFICIENT_RESOURCES;
7628 goto SetStatusAndReturn;
7629 }
7630
7631 if ((fdoExtension->AdapterDescriptor != NULL) &&
7632 (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
7633 status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srb,
7634 STORAGE_ADDRESS_TYPE_BTL8,
7635 CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
7636 1,
7637 SrbExDataTypeScsiCdb16);
7638 if (NT_SUCCESS(status)) {
7639 ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
7640 function = (PULONG_PTR)((PCHAR)srb + sizeNeeded);
7641 } else {
7642 //
7643 // Should not occur.
7644 //
7645 NT_ASSERT(FALSE);
7646 goto SetStatusAndReturn;
7647 }
7648 } else {
7649 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
7650 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
7651 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
7652 function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
7653 }
7654
7655 //
7656 // Save the function code and the device object in the memory after
7657 // the SRB.
7658 //
7659
7660 *function = (ULONG_PTR) DeviceObject;
7661 function++;
7662 *function = (ULONG_PTR) controlCode;
7663
7664 } else {
7665 srb = NULL;
7666 }
7667
7668 //
7669 // Change the device type to storage for the switch statement, but only
7670 // if from a legacy device type
7671 //
7672
7673 if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE << 16)) ||
7674 ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE << 16)) ||
7675 ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
7676 ) {
7677
7678 modifiedIoControlCode = (controlCode & ~0xffff0000);
7679 modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
7680
7681 } else {
7682
7683 modifiedIoControlCode = controlCode;
7684
7685 }
7686
7687 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
7688
7689
7690 switch (modifiedIoControlCode) {
7691
7692 case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
7693
7694 FREE_POOL(srb);
7695
7696 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7697 sizeof(STORAGE_HOTPLUG_INFO)) {
7698
7699 //
7700 // Indicate unsuccessful status and no data transferred.
7701 //
7702
7703 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
7704 Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
7705
7706 ClassReleaseRemoveLock(DeviceObject, Irp);
7707 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7708 status = STATUS_BUFFER_TOO_SMALL;
7709
7710 } else if (!commonExtension->IsFdo) {
7711
7712
7713 //
7714 // Just forward this down and return
7715 //
7716
7717 IoCopyCurrentIrpStackLocationToNext(Irp);
7718
7719 ClassReleaseRemoveLock(DeviceObject, Irp);
7720 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
7721
7722 } else {
7723
7724 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
7725 PSTORAGE_HOTPLUG_INFO info;
7726
7727 fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
7728 info = Irp->AssociatedIrp.SystemBuffer;
7729
7730 *info = fdoExtension->PrivateFdoData->HotplugInfo;
7731 Irp->IoStatus.Status = STATUS_SUCCESS;
7732 Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
7733 ClassReleaseRemoveLock(DeviceObject, Irp);
7734 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7735 status = STATUS_SUCCESS;
7736 }
7737 break;
7738 }
7739
7740 case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
7741
7742 FREE_POOL(srb);
7743
7744 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
7745 sizeof(STORAGE_HOTPLUG_INFO)) {
7746
7747 //
7748 // Indicate unsuccessful status and no data transferred.
7749 //
7750
7751 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
7752
7753 ClassReleaseRemoveLock(DeviceObject, Irp);
7754 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7755 status = STATUS_INFO_LENGTH_MISMATCH;
7756 goto SetStatusAndReturn;
7757
7758 }
7759
7760 if (!commonExtension->IsFdo) {
7761
7762
7763 //
7764 // Just forward this down and return
7765 //
7766
7767 IoCopyCurrentIrpStackLocationToNext(Irp);
7768
7769 ClassReleaseRemoveLock(DeviceObject, Irp);
7770 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
7771
7772 } else {
7773
7774 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
7775 PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
7776
7777 status = STATUS_SUCCESS;
7778
7779 if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
7780 {
7781 status = STATUS_INVALID_PARAMETER_1;
7782 }
7783
7784 if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
7785 {
7786 status = STATUS_INVALID_PARAMETER_2;
7787 }
7788
7789 if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
7790 {
7791 status = STATUS_INVALID_PARAMETER_3;
7792 }
7793
7794 if (NT_SUCCESS(status))
7795 {
7796 if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
7797 {
7798 fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride = info->WriteCacheEnableOverride;
7799
7800 //
7801 // Store the user-defined override in the registry
7802 //
7803
7804 ClassSetDeviceParameter(fdoExtension,
7805 CLASSP_REG_SUBKEY_NAME,
7806 CLASSP_REG_WRITE_CACHE_VALUE_NAME,
7807 info->WriteCacheEnableOverride);
7808 }
7809
7810 fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
7811
7812 //
7813 // Store the user-defined override in the registry
7814 //
7815
7816 ClassSetDeviceParameter(fdoExtension,
7817 CLASSP_REG_SUBKEY_NAME,
7818 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
7819 (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
7820 }
7821
7822 Irp->IoStatus.Status = status;
7823
7824 ClassReleaseRemoveLock(DeviceObject, Irp);
7825 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7826 }
7827
7828 break;
7829 }
7830
7831 case IOCTL_STORAGE_CHECK_VERIFY:
7832 case IOCTL_STORAGE_CHECK_VERIFY2: {
7833
7834 PIRP irp2 = NULL;
7835 PIO_STACK_LOCATION newStack;
7836
7837 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
7838
7839 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceIoControl: Check verify\n"));
7840
7841 //
7842 // If a buffer for a media change count was provided, make sure it's
7843 // big enough to hold the result
7844 //
7845
7846 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
7847
7848 //
7849 // If the buffer is too small to hold the media change count
7850 // then return an error to the caller
7851 //
7852
7853 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7854 sizeof(ULONG)) {
7855
7856 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "DeviceIoControl: media count "
7857 "buffer too small\n"));
7858
7859 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
7860 Irp->IoStatus.Information = sizeof(ULONG);
7861
7862 FREE_POOL(srb);
7863
7864 ClassReleaseRemoveLock(DeviceObject, Irp);
7865 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7866
7867 status = STATUS_BUFFER_TOO_SMALL;
7868 goto SetStatusAndReturn;
7869 }
7870 }
7871
7872 if (!commonExtension->IsFdo) {
7873
7874
7875 //
7876 // If this is a PDO then we should just forward the request down
7877 //
7878 NT_ASSERT(!srb);
7879
7880 IoCopyCurrentIrpStackLocationToNext(Irp);
7881
7882 ClassReleaseRemoveLock(DeviceObject, Irp);
7883
7884 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
7885
7886 goto SetStatusAndReturn;
7887
7888 } else {
7889
7890 fdoExtension = DeviceObject->DeviceExtension;
7891
7892 }
7893
7894 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
7895
7896 //
7897 // The caller has provided a valid buffer. Allocate an additional
7898 // irp and stick the CheckVerify completion routine on it. We will
7899 // then send this down to the port driver instead of the irp the
7900 // caller sent in
7901 //
7902
7903 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceIoControl: Check verify wants "
7904 "media count\n"));
7905
7906 //
7907 // Allocate a new irp to send the TestUnitReady to the port driver
7908 //
7909
7910 irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
7911
7912 if (irp2 == NULL) {
7913 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
7914 Irp->IoStatus.Information = 0;
7915 FREE_POOL(srb);
7916 ClassReleaseRemoveLock(DeviceObject, Irp);
7917 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7918 status = STATUS_INSUFFICIENT_RESOURCES;
7919 goto SetStatusAndReturn;
7920
7921 break;
7922 }
7923
7924 //
7925 // Make sure to acquire the lock for the new irp.
7926 //
7927
7928 ClassAcquireRemoveLock(DeviceObject, irp2);
7929
7930 irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
7931 IoSetNextIrpStackLocation(irp2);
7932
7933 //
7934 // Set the top stack location and shove the master Irp into the
7935 // top location
7936 //
7937
7938 newStack = IoGetCurrentIrpStackLocation(irp2);
7939 newStack->Parameters.Others.Argument1 = Irp;
7940 newStack->DeviceObject = DeviceObject;
7941
7942 //
7943 // Stick the check verify completion routine onto the stack
7944 // and prepare the irp for the port driver
7945 //
7946
7947 IoSetCompletionRoutine(irp2,
7948 ClassCheckVerifyComplete,
7949 NULL,
7950 TRUE,
7951 TRUE,
7952 TRUE);
7953
7954 IoSetNextIrpStackLocation(irp2);
7955 newStack = IoGetCurrentIrpStackLocation(irp2);
7956 newStack->DeviceObject = DeviceObject;
7957 newStack->MajorFunction = irpStack->MajorFunction;
7958 newStack->MinorFunction = irpStack->MinorFunction;
7959 newStack->Flags = irpStack->Flags;
7960
7961
7962 //
7963 // Mark the master irp as pending - whether the lower level
7964 // driver completes it immediately or not this should allow it
7965 // to go all the way back up.
7966 //
7967
7968 IoMarkIrpPending(Irp);
7969
7970 Irp = irp2;
7971
7972 }
7973
7974 //
7975 // Test Unit Ready
7976 //
7977
7978 SrbSetCdbLength(srb, 6);
7979 cdb = SrbGetCdb(srb);
7980 cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
7981
7982 //
7983 // Set timeout value.
7984 //
7985
7986 SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
7987
7988 //
7989 // If this was a CV2 then mark the request as low-priority so we don't
7990 // spin up the drive just to satisfy it.
7991 //
7992
7993 if (controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
7994 SrbSetSrbFlags(srb, SRB_CLASS_FLAGS_LOW_PRIORITY);
7995 }
7996
7997 //
7998 // Since this routine will always hand the request to the
7999 // port driver if there isn't a data transfer to be done
8000 // we don't have to worry about completing the request here
8001 // on an error
8002 //
8003
8004 //
8005 // This routine uses a completion routine so we don't want to release
8006 // the remove lock until then.
8007 //
8008
8009 status = ClassSendSrbAsynchronous(DeviceObject,
8010 srb,
8011 Irp,
8012 NULL,
8013 0,
8014 FALSE);
8015
8016 break;
8017 }
8018
8019 case IOCTL_STORAGE_MEDIA_REMOVAL:
8020 case IOCTL_STORAGE_EJECTION_CONTROL: {
8021
8022 PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
8023
8024 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoControl: ejection control\n"));
8025
8026 FREE_POOL(srb);
8027
8028 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
8029 sizeof(PREVENT_MEDIA_REMOVAL)) {
8030
8031 //
8032 // Indicate unsuccessful status and no data transferred.
8033 //
8034
8035 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
8036
8037 ClassReleaseRemoveLock(DeviceObject, Irp);
8038 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8039 status = STATUS_INFO_LENGTH_MISMATCH;
8040 goto SetStatusAndReturn;
8041 }
8042
8043 if (!commonExtension->IsFdo) {
8044
8045
8046 //
8047 // Just forward this down and return
8048 //
8049
8050 IoCopyCurrentIrpStackLocationToNext(Irp);
8051
8052 ClassReleaseRemoveLock(DeviceObject, Irp);
8053 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8054 }
8055 else {
8056
8057 // i don't believe this assertion is valid. this is a request
8058 // from user-mode, so they could request this for any device
8059 // they want? also, we handle it properly.
8060 // NT_ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
8061 status = ClasspEjectionControl(
8062 DeviceObject,
8063 Irp,
8064 ((modifiedIoControlCode ==
8065 IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
8066 SimpleMediaLock),
8067 mediaRemoval->PreventMediaRemoval);
8068
8069 Irp->IoStatus.Status = status;
8070 ClassReleaseRemoveLock(DeviceObject, Irp);
8071 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8072 }
8073
8074 break;
8075 }
8076
8077 case IOCTL_STORAGE_MCN_CONTROL: {
8078
8079 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoControl: MCN control\n"));
8080
8081 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
8082 sizeof(PREVENT_MEDIA_REMOVAL)) {
8083
8084 //
8085 // Indicate unsuccessful status and no data transferred.
8086 //
8087
8088 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
8089 Irp->IoStatus.Information = 0;
8090
8091 FREE_POOL(srb);
8092
8093 ClassReleaseRemoveLock(DeviceObject, Irp);
8094 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8095 status = STATUS_INFO_LENGTH_MISMATCH;
8096 goto SetStatusAndReturn;
8097 }
8098
8099 if (!commonExtension->IsFdo) {
8100
8101
8102 //
8103 // Just forward this down and return
8104 //
8105
8106 FREE_POOL(srb);
8107
8108 IoCopyCurrentIrpStackLocationToNext(Irp);
8109
8110 ClassReleaseRemoveLock(DeviceObject, Irp);
8111 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8112
8113 } else {
8114
8115 //
8116 // Call to the FDO - handle the ejection control.
8117 //
8118
8119 status = ClasspMcnControl(DeviceObject->DeviceExtension,
8120 Irp,
8121 srb);
8122 }
8123 goto SetStatusAndReturn;
8124 }
8125
8126 case IOCTL_STORAGE_RESERVE:
8127 case IOCTL_STORAGE_RELEASE: {
8128
8129 //
8130 // Reserve logical unit.
8131 //
8132
8133 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
8134
8135 if (!commonExtension->IsFdo) {
8136
8137
8138 IoCopyCurrentIrpStackLocationToNext(Irp);
8139
8140 ClassReleaseRemoveLock(DeviceObject, Irp);
8141 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8142 goto SetStatusAndReturn;
8143
8144 } else {
8145 fdoExtension = DeviceObject->DeviceExtension;
8146 }
8147
8148 if (TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6))
8149 {
8150 SrbSetCdbLength(srb, 10);
8151 cdb = SrbGetCdb(srb);
8152 cdb->CDB10.OperationCode = (modifiedIoControlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
8153 }
8154 else
8155 {
8156 SrbSetCdbLength(srb, 6);
8157 cdb = SrbGetCdb(srb);
8158 cdb->CDB6GENERIC.OperationCode = (modifiedIoControlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT : SCSIOP_RELEASE_UNIT;
8159 }
8160
8161 //
8162 // Set timeout value.
8163 //
8164
8165 SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
8166
8167 //
8168 // Send reserves as tagged requests.
8169 //
8170
8171 if ( IOCTL_STORAGE_RESERVE == modifiedIoControlCode ) {
8172 SrbSetSrbFlags(srb, SRB_FLAGS_QUEUE_ACTION_ENABLE);
8173 SrbSetRequestAttribute(srb, SRB_SIMPLE_TAG_REQUEST);
8174 }
8175
8176 status = ClassSendSrbAsynchronous(DeviceObject,
8177 srb,
8178 Irp,
8179 NULL,
8180 0,
8181 FALSE);
8182
8183 break;
8184 }
8185
8186 case IOCTL_STORAGE_PERSISTENT_RESERVE_IN:
8187 case IOCTL_STORAGE_PERSISTENT_RESERVE_OUT: {
8188
8189 if (!commonExtension->IsFdo) {
8190
8191 IoCopyCurrentIrpStackLocationToNext(Irp);
8192
8193 ClassReleaseRemoveLock(DeviceObject, Irp);
8194 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8195 goto SetStatusAndReturn;
8196 }
8197
8198 //
8199 // Process Persistent Reserve
8200 //
8201
8202 status = ClasspPersistentReserve(DeviceObject, Irp, srb);
8203
8204 break;
8205
8206 }
8207
8208 case IOCTL_STORAGE_EJECT_MEDIA:
8209 case IOCTL_STORAGE_LOAD_MEDIA:
8210 case IOCTL_STORAGE_LOAD_MEDIA2:{
8211
8212 //
8213 // Eject media.
8214 //
8215
8216 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
8217
8218 if (!commonExtension->IsFdo) {
8219
8220
8221 IoCopyCurrentIrpStackLocationToNext(Irp);
8222
8223 ClassReleaseRemoveLock(DeviceObject, Irp);
8224
8225 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8226 goto SetStatusAndReturn;
8227 } else {
8228 fdoExtension = DeviceObject->DeviceExtension;
8229 }
8230
8231 if (commonExtension->PagingPathCount != 0) {
8232
8233 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClassDeviceControl: call to eject paging device - "
8234 "failure\n"));
8235
8236 status = STATUS_FILES_OPEN;
8237 Irp->IoStatus.Status = status;
8238
8239 Irp->IoStatus.Information = 0;
8240
8241 FREE_POOL(srb);
8242
8243 ClassReleaseRemoveLock(DeviceObject, Irp);
8244 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8245 goto SetStatusAndReturn;
8246 }
8247
8248 //
8249 // Synchronize with ejection control and ejection cleanup code as
8250 // well as other eject/load requests.
8251 //
8252
8253 KeEnterCriticalRegion();
8254 (VOID)KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
8255 UserRequest,
8256 KernelMode,
8257 FALSE,
8258 NULL);
8259
8260 if (fdoExtension->ProtectedLockCount != 0) {
8261
8262 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClassDeviceControl: call to eject protected locked "
8263 "device - failure\n"));
8264
8265 status = STATUS_DEVICE_BUSY;
8266 Irp->IoStatus.Status = status;
8267 Irp->IoStatus.Information = 0;
8268
8269 FREE_POOL(srb);
8270
8271 ClassReleaseRemoveLock(DeviceObject, Irp);
8272 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8273
8274 KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
8275 IO_NO_INCREMENT,
8276 FALSE);
8277 KeLeaveCriticalRegion();
8278
8279 goto SetStatusAndReturn;
8280 }
8281
8282 SrbSetCdbLength(srb, 6);
8283 cdb = SrbGetCdb(srb);
8284
8285 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
8286 cdb->START_STOP.LoadEject = 1;
8287
8288 if (modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
8289 cdb->START_STOP.Start = 0;
8290 } else {
8291 cdb->START_STOP.Start = 1;
8292 }
8293
8294 //
8295 // Set timeout value.
8296 //
8297
8298 SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
8299 status = ClassSendSrbAsynchronous(DeviceObject,
8300 srb,
8301 Irp,
8302 NULL,
8303 0,
8304 FALSE);
8305
8306 KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
8307 KeLeaveCriticalRegion();
8308
8309 break;
8310 }
8311
8312 case IOCTL_STORAGE_FIND_NEW_DEVICES: {
8313
8314 FREE_POOL(srb);
8315
8316 if (commonExtension->IsFdo) {
8317
8318 IoInvalidateDeviceRelations(
8319 ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
8320 BusRelations);
8321
8322 status = STATUS_SUCCESS;
8323 Irp->IoStatus.Status = status;
8324
8325 ClassReleaseRemoveLock(DeviceObject, Irp);
8326 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8327 }
8328 else {
8329
8330
8331 IoCopyCurrentIrpStackLocationToNext(Irp);
8332
8333 ClassReleaseRemoveLock(DeviceObject, Irp);
8334 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8335 }
8336 break;
8337 }
8338
8339 case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
8340
8341 FREE_POOL(srb);
8342
8343 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
8344 sizeof(STORAGE_DEVICE_NUMBER)) {
8345
8346 PSTORAGE_DEVICE_NUMBER deviceNumber =
8347 Irp->AssociatedIrp.SystemBuffer;
8348 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
8349 commonExtension->PartitionZeroExtension;
8350
8351 deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
8352 deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
8353 deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
8354
8355 status = STATUS_SUCCESS;
8356 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
8357
8358 } else {
8359 status = STATUS_BUFFER_TOO_SMALL;
8360 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
8361 }
8362
8363 Irp->IoStatus.Status = status;
8364 ClassReleaseRemoveLock(DeviceObject, Irp);
8365 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8366
8367 break;
8368 }
8369
8370
8371 case IOCTL_STORAGE_READ_CAPACITY: {
8372
8373 FREE_POOL(srb);
8374
8375 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
8376 sizeof(STORAGE_READ_CAPACITY)) {
8377
8378 //
8379 // Indicate unsuccessful status and no data transferred.
8380 //
8381
8382 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
8383 Irp->IoStatus.Information = sizeof(STORAGE_READ_CAPACITY);
8384
8385 ClassReleaseRemoveLock(DeviceObject, Irp);
8386 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8387 status = STATUS_BUFFER_TOO_SMALL;
8388 break;
8389 }
8390
8391 if (!commonExtension->IsFdo) {
8392
8393
8394 //
8395 // Just forward this down and return
8396 //
8397
8398 IoCopyCurrentIrpStackLocationToNext(Irp);
8399
8400 ClassReleaseRemoveLock(DeviceObject, Irp);
8401 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8402 }
8403 else {
8404
8405 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = DeviceObject->DeviceExtension;
8406 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
8407 PSTORAGE_READ_CAPACITY readCapacity = Irp->AssociatedIrp.SystemBuffer;
8408 LARGE_INTEGER diskLength;
8409
8410 status = ClassReadDriveCapacity(DeviceObject);
8411 if (NT_SUCCESS(status) && fdoData->IsCachedDriveCapDataValid) {
8412
8413 readCapacity->Version = sizeof(STORAGE_READ_CAPACITY);
8414 readCapacity->Size = sizeof(STORAGE_READ_CAPACITY);
8415
8416 REVERSE_BYTES(&readCapacity->BlockLength,
8417 &fdoData->LastKnownDriveCapacityData.BytesPerBlock);
8418 REVERSE_BYTES_QUAD(&readCapacity->NumberOfBlocks,
8419 &fdoData->LastKnownDriveCapacityData.LogicalBlockAddress);
8420 readCapacity->NumberOfBlocks.QuadPart++;
8421
8422 readCapacity->DiskLength = fdoExt->CommonExtension.PartitionLength;
8423
8424 //
8425 // Make sure the lengths are equal.
8426 // Remove this after testing.
8427 //
8428 diskLength.QuadPart = readCapacity->NumberOfBlocks.QuadPart *
8429 readCapacity->BlockLength;
8430
8431 Irp->IoStatus.Status = STATUS_SUCCESS;
8432 Irp->IoStatus.Information = sizeof(STORAGE_READ_CAPACITY);
8433
8434 } else {
8435 //
8436 // Read capacity request failed.
8437 //
8438 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClassDeviceControl: ClassReadDriveCapacity failed: 0x%X IsCachedDriveCapDataValid: %d\n",
8439 status, fdoData->IsCachedDriveCapDataValid));
8440 Irp->IoStatus.Status = status;
8441 Irp->IoStatus.Information = 0;
8442 }
8443 ClassReleaseRemoveLock(DeviceObject, Irp);
8444 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8445 }
8446
8447 break;
8448 }
8449
8450 case IOCTL_STORAGE_QUERY_PROPERTY: {
8451
8452 PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
8453
8454 if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_PROPERTY_QUERY)) {
8455
8456 status = STATUS_INFO_LENGTH_MISMATCH;
8457 Irp->IoStatus.Status = status;
8458 ClassReleaseRemoveLock(DeviceObject, Irp);
8459 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8460 FREE_POOL(srb);
8461 break;
8462 }
8463
8464 if (!commonExtension->IsFdo) {
8465
8466
8467 IoCopyCurrentIrpStackLocationToNext(Irp);
8468
8469 ClassReleaseRemoveLock(DeviceObject, Irp);
8470 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8471 FREE_POOL(srb);
8472 break;
8473 }
8474
8475 //
8476 // Determine PropertyId type and either call appropriate routine
8477 // or pass request to lower drivers.
8478 //
8479
8480 switch ( query->PropertyId ) {
8481
8482 case StorageDeviceUniqueIdProperty: {
8483
8484 status = ClasspDuidQueryProperty(DeviceObject, Irp);
8485 break;
8486 }
8487
8488 case StorageDeviceWriteCacheProperty: {
8489
8490 status = ClasspWriteCacheProperty(DeviceObject, Irp, srb);
8491 break;
8492 }
8493
8494 // these propertyId has been implemented in some port driver and filter drivers.
8495 // to keep the backwards compatibility, classpnp will send the request down if it's supported by lower layer.
8496 // otherwise, classpnp sends SCSI command and then interprets the result.
8497 case StorageAccessAlignmentProperty: {
8498
8499 status = ClasspAccessAlignmentProperty(DeviceObject, Irp, srb);
8500 break;
8501 }
8502
8503 case StorageDeviceSeekPenaltyProperty: {
8504
8505 status = ClasspDeviceSeekPenaltyProperty(DeviceObject, Irp, srb);
8506 break;
8507 }
8508
8509 case StorageDeviceTrimProperty: {
8510
8511 status = ClasspDeviceTrimProperty(DeviceObject, Irp, srb);
8512 break;
8513 }
8514
8515 case StorageDeviceLBProvisioningProperty: {
8516
8517 status = ClasspDeviceLBProvisioningProperty(DeviceObject, Irp, srb);
8518 break;
8519 }
8520
8521 case StorageDeviceCopyOffloadProperty: {
8522
8523 status = ClasspDeviceCopyOffloadProperty(DeviceObject, Irp, srb);
8524 break;
8525 }
8526
8527 case StorageDeviceMediumProductType: {
8528
8529 status = ClasspDeviceMediaTypeProperty(DeviceObject, Irp, srb);
8530 break;
8531 }
8532
8533 default: {
8534
8535 //
8536 // Copy the Irp stack parameters to the next stack location.
8537 //
8538
8539 IoCopyCurrentIrpStackLocationToNext(Irp);
8540
8541 ClassReleaseRemoveLock(DeviceObject, Irp);
8542 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8543 break;
8544 }
8545 } // end switch
8546
8547 FREE_POOL(srb);
8548 break;
8549 }
8550
8551 case IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT: {
8552
8553 FREE_POOL(srb);
8554
8555 if (!commonExtension->IsFdo) {
8556
8557
8558 IoCopyCurrentIrpStackLocationToNext(Irp);
8559
8560 ClassReleaseRemoveLock(DeviceObject, Irp);
8561 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8562 break;
8563 }
8564
8565 //
8566 // Process priority hit request
8567 //
8568
8569 status = ClasspPriorityHint(DeviceObject, Irp);
8570 break;
8571 }
8572
8573 case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: {
8574
8575 PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
8576
8577 if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) {
8578
8579 status = STATUS_INVALID_PARAMETER;
8580 Irp->IoStatus.Status = status;
8581 ClassReleaseRemoveLock(DeviceObject, Irp);
8582 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8583 FREE_POOL(srb);
8584 break;
8585 }
8586
8587 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
8588 (sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES) + dsmAttributes->ParameterBlockLength + dsmAttributes->DataSetRangesLength)) {
8589
8590 status = STATUS_INVALID_PARAMETER;
8591 Irp->IoStatus.Status = status;
8592 ClassReleaseRemoveLock(DeviceObject, Irp);
8593 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8594 FREE_POOL(srb);
8595 break;
8596 }
8597
8598 if (!commonExtension->IsFdo) {
8599
8600
8601 IoCopyCurrentIrpStackLocationToNext(Irp);
8602
8603 ClassReleaseRemoveLock(DeviceObject, Irp);
8604 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8605 FREE_POOL(srb);
8606 break;
8607 }
8608
8609 switch(dsmAttributes->Action) {
8610
8611 // only process Trim action in class layer if possible.
8612 case DeviceDsmAction_Trim: {
8613 status = ClasspDeviceTrimProcess(DeviceObject, Irp, &activityId, srb);
8614 break;
8615 }
8616
8617 case DeviceDsmAction_OffloadRead: {
8618 status = ClassDeviceProcessOffloadRead(DeviceObject, Irp, srb);
8619 break;
8620 }
8621
8622 case DeviceDsmAction_OffloadWrite: {
8623 status = ClassDeviceProcessOffloadWrite(DeviceObject, Irp, srb);
8624 break;
8625 }
8626
8627 case DeviceDsmAction_Allocation: {
8628 status = ClasspDeviceGetLBAStatus(DeviceObject, Irp, srb);
8629 break;
8630 }
8631
8632
8633 default: {
8634
8635
8636 //
8637 // Copy the Irp stack parameters to the next stack location.
8638 //
8639
8640 IoCopyCurrentIrpStackLocationToNext(Irp);
8641
8642 ClassReleaseRemoveLock(DeviceObject, Irp);
8643 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8644 break;
8645 }
8646 } // end switch
8647
8648 FREE_POOL(srb);
8649 break;
8650 }
8651
8652 case IOCTL_STORAGE_GET_LB_PROVISIONING_MAP_RESOURCES: {
8653
8654 if (commonExtension->IsFdo) {
8655
8656 status = ClassDeviceGetLBProvisioningResources(DeviceObject, Irp, srb);
8657
8658 } else {
8659
8660 IoCopyCurrentIrpStackLocationToNext(Irp);
8661
8662 ClassReleaseRemoveLock(DeviceObject, Irp);
8663 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8664 }
8665
8666 FREE_POOL(srb);
8667
8668 break;
8669 }
8670
8671 case IOCTL_STORAGE_EVENT_NOTIFICATION: {
8672
8673 FREE_POOL(srb);
8674
8675 status = ClasspStorageEventNotification(DeviceObject, Irp);
8676 break;
8677 }
8678
8679 #if (NTDDI_VERSION >= NTDDI_WINTRHESHOLD)
8680 case IOCTL_STORAGE_FIRMWARE_GET_INFO: {
8681 FREE_POOL(srb);
8682
8683 status = ClassDeviceHwFirmwareGetInfoProcess(DeviceObject, Irp);
8684 break;
8685 }
8686
8687 case IOCTL_STORAGE_FIRMWARE_DOWNLOAD: {
8688 if (!commonExtension->IsFdo) {
8689
8690 IoCopyCurrentIrpStackLocationToNext(Irp);
8691
8692 ClassReleaseRemoveLock(DeviceObject, Irp);
8693 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8694 goto SetStatusAndReturn;
8695 }
8696
8697 status = ClassDeviceHwFirmwareDownloadProcess(DeviceObject, Irp, srb);
8698 break;
8699 }
8700
8701 case IOCTL_STORAGE_FIRMWARE_ACTIVATE: {
8702 if (!commonExtension->IsFdo) {
8703
8704 IoCopyCurrentIrpStackLocationToNext(Irp);
8705
8706 ClassReleaseRemoveLock(DeviceObject, Irp);
8707 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8708 goto SetStatusAndReturn;
8709 }
8710
8711 status = ClassDeviceHwFirmwareActivateProcess(DeviceObject, Irp, srb);
8712 break;
8713 }
8714 #endif
8715
8716
8717 default: {
8718
8719 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
8720 controlCode, DeviceObject));
8721
8722
8723 //
8724 // Pass the device control to the next driver.
8725 //
8726
8727 FREE_POOL(srb);
8728
8729 //
8730 // Copy the Irp stack parameters to the next stack location.
8731 //
8732
8733 IoCopyCurrentIrpStackLocationToNext(Irp);
8734
8735 ClassReleaseRemoveLock(DeviceObject, Irp);
8736 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8737 break;
8738 }
8739
8740 } // end switch( ...
8741
8742 SetStatusAndReturn:
8743
8744 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
8745
8746 return status;
8747 } // end ClassDeviceControl()
8748
8749 /*++////////////////////////////////////////////////////////////////////////////
8750
8751 ClassShutdownFlush()
8752
8753 Routine Description:
8754
8755 This routine is called for a shutdown and flush IRPs. These are sent by the
8756 system before it actually shuts down or when the file system does a flush.
8757 If it exists, the device-specific driver's routine will be invoked. If there
8758 wasn't one specified, the Irp will be completed with an Invalid device request.
8759
8760 Arguments:
8761
8762 DriverObject - Pointer to device object to being shutdown by system.
8763
8764 Irp - IRP involved.
8765
8766 Return Value:
8767
8768 NT Status
8769
8770 --*/
8771 NTSTATUS
8772 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
8773 ClassShutdownFlush(
8774 IN PDEVICE_OBJECT DeviceObject,
8775 IN PIRP Irp
8776 )
8777 {
8778 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
8779
8780 ULONG isRemoved;
8781
8782 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
8783 _Analysis_assume_(isRemoved);
8784 if(isRemoved) {
8785
8786 ClassReleaseRemoveLock(DeviceObject, Irp);
8787
8788 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
8789
8790 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8791
8792 return STATUS_DEVICE_DOES_NOT_EXIST;
8793 }
8794
8795 if (commonExtension->DevInfo->ClassShutdownFlush) {
8796
8797 //
8798 // Call the device-specific driver's routine.
8799 //
8800
8801 return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
8802 }
8803
8804 //
8805 // Device-specific driver doesn't support this.
8806 //
8807
8808 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
8809
8810 ClassReleaseRemoveLock(DeviceObject, Irp);
8811 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8812
8813 return STATUS_INVALID_DEVICE_REQUEST;
8814 } // end ClassShutdownFlush()
8815
8816 /*++////////////////////////////////////////////////////////////////////////////
8817
8818 ClasspIsPortable()
8819
8820 Routine Description:
8821
8822 This routine is called during start device to determine whether the PDO
8823 for a stack reports itself as portable.
8824
8825 Arguments:
8826
8827 FdoExtension - Pointer to FDO whose PDO we check for portability.
8828
8829 IsPortable - Boolean pointer in which to store result.
8830
8831 Return Value:
8832
8833 NT Status
8834
8835 --*/
8836 NTSTATUS
8837 ClasspIsPortable(
8838 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
8839 _Out_ PBOOLEAN IsPortable
8840 )
8841 {
8842 DEVPROP_BOOLEAN isInternal = DEVPROP_FALSE;
8843 BOOLEAN isPortable = FALSE;
8844 ULONG size = 0;
8845 NTSTATUS status = STATUS_SUCCESS;
8846 DEVPROPTYPE type = DEVPROP_TYPE_EMPTY;
8847
8848 PAGED_CODE();
8849
8850 *IsPortable = FALSE;
8851
8852 //
8853 // Check to see if the underlying device
8854 // object is in local machine container
8855 //
8856
8857 status = IoGetDevicePropertyData(FdoExtension->LowerPdo,
8858 &DEVPKEY_Device_InLocalMachineContainer,
8859 0,
8860 0,
8861 sizeof(isInternal),
8862 &isInternal,
8863 &size,
8864 &type);
8865
8866 if (!NT_SUCCESS(status)) {
8867 goto cleanup;
8868 }
8869
8870 NT_ASSERT(size == sizeof(isInternal));
8871 NT_ASSERT(type == DEVPROP_TYPE_BOOLEAN);
8872
8873 //
8874 // Volume is hot-pluggable if the disk pdo
8875 // container id differs from that of root device
8876 //
8877
8878 if (isInternal == DEVPROP_TRUE) {
8879 goto cleanup;
8880 }
8881
8882 isPortable = TRUE;
8883
8884 //
8885 // Examine the bus type to ensure
8886 // that this really is a fixed disk
8887 //
8888
8889 if (FdoExtension->DeviceDescriptor->BusType == BusTypeFibre ||
8890 FdoExtension->DeviceDescriptor->BusType == BusTypeiScsi ||
8891 FdoExtension->DeviceDescriptor->BusType == BusTypeRAID) {
8892
8893 isPortable = FALSE;
8894 }
8895
8896 *IsPortable = isPortable;
8897
8898 cleanup:
8899
8900 return status;
8901 }
8902
8903 /*++////////////////////////////////////////////////////////////////////////////
8904
8905 ClassCreateDeviceObject()
8906
8907 Routine Description:
8908
8909 This routine creates an object for the physical device specified and
8910 sets up the deviceExtension's function pointers for each entry point
8911 in the device-specific driver.
8912
8913 Arguments:
8914
8915 DriverObject - Pointer to driver object created by system.
8916
8917 ObjectNameBuffer - Dir. name of the object to create.
8918
8919 LowerDeviceObject - Pointer to the lower device object
8920
8921 IsFdo - should this be an fdo or a pdo
8922
8923 DeviceObject - Pointer to the device object pointer we will return.
8924
8925 Return Value:
8926
8927 NTSTATUS
8928
8929 --*/
8930
8931 _IRQL_requires_max_(PASSIVE_LEVEL)
8932 _Must_inspect_result_
8933 _Post_satisfies_(return <= 0)
8934 SCSIPORT_API
8935 NTSTATUS
8936 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
8937 ClassCreateDeviceObject(
8938 _In_ PDRIVER_OBJECT DriverObject,
8939 _In_z_ PCCHAR ObjectNameBuffer,
8940 _In_ PDEVICE_OBJECT LowerDevice,
8941 _In_ BOOLEAN IsFdo,
8942 _Outptr_result_nullonfailure_
8943 _At_(*DeviceObject, __drv_allocatesMem(Mem) __drv_aliasesMem)
8944 PDEVICE_OBJECT *DeviceObject
8945 )
8946 {
8947 BOOLEAN isPartitionable;
8948 STRING ntNameString;
8949 UNICODE_STRING ntUnicodeString;
8950 NTSTATUS status;
8951 PDEVICE_OBJECT deviceObject = NULL;
8952
8953 ULONG characteristics;
8954 SIZE_T rundownSize = ExSizeOfRundownProtectionCacheAware();
8955 PCHAR rundownAddr = NULL;
8956 ULONG devExtSize;
8957
8958 #ifdef _MSC_VER
8959 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
8960 #endif
8961 PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
8962
8963 PCLASS_DEV_INFO devInfo;
8964
8965 PAGED_CODE();
8966
8967 _Analysis_assume_(driverExtension != NULL);
8968
8969 *DeviceObject = NULL;
8970 RtlInitUnicodeString(&ntUnicodeString, NULL);
8971
8972 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassCreateFdo: Create device object\n"));
8973
8974 NT_ASSERT(LowerDevice);
8975
8976 //
8977 // Make sure that if we're making PDO's we have an enumeration routine
8978 //
8979
8980 isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
8981
8982 NT_ASSERT(IsFdo || isPartitionable);
8983
8984 //
8985 // Grab the correct dev-info structure out of the init data
8986 //
8987
8988 if (IsFdo) {
8989 devInfo = &(driverExtension->InitData.FdoData);
8990 } else {
8991 devInfo = &(driverExtension->InitData.PdoData);
8992 }
8993
8994 characteristics = devInfo->DeviceCharacteristics;
8995
8996 if (ARGUMENT_PRESENT(ObjectNameBuffer)) {
8997 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
8998
8999 RtlInitString(&ntNameString, ObjectNameBuffer);
9000
9001 status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
9002
9003 if (!NT_SUCCESS(status)) {
9004
9005 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
9006 "ClassCreateFdo: Cannot convert string %s\n",
9007 ObjectNameBuffer));
9008
9009 ntUnicodeString.Buffer = NULL;
9010 return status;
9011 }
9012 } else {
9013 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassCreateFdo: Object will be unnamed\n"));
9014
9015 if (IsFdo == FALSE) {
9016
9017 //
9018 // PDO's have to have some sort of name.
9019 //
9020
9021 SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
9022 }
9023
9024 RtlInitUnicodeString(&ntUnicodeString, NULL);
9025 }
9026
9027 devExtSize = devInfo->DeviceExtensionSize +
9028 (ULONG)sizeof(CLASS_PRIVATE_COMMON_DATA) + (ULONG)rundownSize;
9029 status = IoCreateDevice(DriverObject,
9030 devExtSize,
9031 &ntUnicodeString,
9032 devInfo->DeviceType,
9033 characteristics,
9034 FALSE,
9035 &deviceObject);
9036
9037 if (!NT_SUCCESS(status)) {
9038
9039 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassCreateFdo: Can not create device object %lx\n",
9040 status));
9041 NT_ASSERT(deviceObject == NULL);
9042
9043 //
9044 // buffer is not used any longer here.
9045 //
9046
9047 if (ntUnicodeString.Buffer != NULL) {
9048 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassCreateFdo: Freeing unicode name buffer\n"));
9049 FREE_POOL(ntUnicodeString.Buffer);
9050 RtlInitUnicodeString(&ntUnicodeString, NULL);
9051 }
9052
9053 } else {
9054
9055 PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
9056
9057 RtlZeroMemory(
9058 deviceObject->DeviceExtension,
9059 devExtSize);
9060
9061 //
9062 // Setup version code
9063 //
9064
9065 commonExtension->Version = 0x03;
9066
9067 //
9068 // Setup the remove lock and event
9069 //
9070
9071 commonExtension->IsRemoved = NO_REMOVE;
9072
9073 #if DBG
9074
9075 commonExtension->RemoveLock = 0;
9076
9077 #endif
9078
9079 KeInitializeEvent(&commonExtension->RemoveEvent,
9080 SynchronizationEvent,
9081 FALSE);
9082
9083
9084 ClasspInitializeRemoveTracking(deviceObject);
9085
9086 //
9087 // Initialize the PrivateCommonData
9088 //
9089
9090 commonExtension->PrivateCommonData = (PCLASS_PRIVATE_COMMON_DATA)
9091 ((PCHAR)deviceObject->DeviceExtension + devInfo->DeviceExtensionSize);
9092 rundownAddr = (PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA);
9093 ExInitializeRundownProtectionCacheAware((PEX_RUNDOWN_REF_CACHE_AWARE)rundownAddr, rundownSize);
9094 commonExtension->PrivateCommonData->RemoveLockFailAcquire = 0;
9095
9096 //
9097 // Acquire the lock once. This reference will be released when the
9098 // remove IRP has been received.
9099 //
9100
9101 ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
9102
9103 //
9104 // Store a pointer to the driver extension so we don't have to do
9105 // lookups to get it.
9106 //
9107
9108 commonExtension->DriverExtension = driverExtension;
9109
9110 //
9111 // Fill in entry points
9112 //
9113
9114 commonExtension->DevInfo = devInfo;
9115
9116 //
9117 // Initialize some of the common values in the structure
9118 //
9119
9120 commonExtension->DeviceObject = deviceObject;
9121
9122 commonExtension->LowerDeviceObject = NULL;
9123
9124 commonExtension->DispatchTable = driverExtension->DeviceMajorFunctionTable;
9125
9126 if(IsFdo) {
9127
9128 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
9129
9130 commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
9131
9132 //
9133 // Set the initial device object flags.
9134 //
9135
9136 SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
9137 //
9138 // Clear the PDO list
9139 //
9140
9141 commonExtension->ChildList = NULL;
9142
9143 commonExtension->DriverData =
9144 ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
9145
9146 //
9147 // The disk class driver creates only FDO. The partition number
9148 // for the FDO must be 0.
9149 //
9150
9151 if ((isPartitionable == TRUE) ||
9152 (devInfo->DeviceType == FILE_DEVICE_DISK)) {
9153
9154 commonExtension->PartitionNumber = 0;
9155 } else {
9156 commonExtension->PartitionNumber = (ULONG) (-1L);
9157 }
9158
9159 fdoExtension->DevicePowerState = PowerDeviceD0;
9160
9161 KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
9162 SynchronizationEvent,
9163 TRUE);
9164
9165 KeInitializeEvent(&fdoExtension->ChildLock,
9166 SynchronizationEvent,
9167 TRUE);
9168
9169 status = ClasspAllocateReleaseRequest(deviceObject);
9170
9171 if(!NT_SUCCESS(status)) {
9172 IoDeleteDevice(deviceObject);
9173 *DeviceObject = NULL;
9174
9175 if (ntUnicodeString.Buffer != NULL) {
9176 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassCreateFdo: Freeing unicode name buffer\n"));
9177 FREE_POOL(ntUnicodeString.Buffer);
9178 RtlInitUnicodeString(&ntUnicodeString, NULL);
9179 }
9180
9181 return status;
9182 }
9183
9184 } else {
9185
9186 PPHYSICAL_DEVICE_EXTENSION pdoExtension =
9187 deviceObject->DeviceExtension;
9188
9189 PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
9190 LowerDevice->DeviceExtension;
9191
9192 SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
9193
9194 commonExtension->PartitionZeroExtension = p0Extension;
9195
9196 //
9197 // Stick this onto the PDO list
9198 //
9199
9200 ClassAddChild(p0Extension, pdoExtension, TRUE);
9201
9202 commonExtension->DriverData = (PVOID) (pdoExtension + 1);
9203
9204 //
9205 // Get the top of stack for the lower device - this allows
9206 // filters to get stuck in between the partitions and the
9207 // physical disk.
9208 //
9209
9210 commonExtension->LowerDeviceObject =
9211 IoGetAttachedDeviceReference(LowerDevice);
9212
9213 //
9214 // Pnp will keep a reference to the lower device object long
9215 // after this partition has been deleted. Dereference now so
9216 // we don't have to deal with it later.
9217 //
9218
9219 ObDereferenceObject(commonExtension->LowerDeviceObject);
9220 }
9221
9222 KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
9223
9224 commonExtension->IsFdo = IsFdo;
9225
9226 commonExtension->DeviceName = ntUnicodeString;
9227
9228 commonExtension->PreviousState = 0xff;
9229
9230 InitializeDictionary(&(commonExtension->FileObjectDictionary));
9231
9232 commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
9233
9234 if (commonExtension->DriverExtension->InitData.ClassStartIo) {
9235 IoSetStartIoAttributes(deviceObject, TRUE, TRUE);
9236 }
9237 }
9238
9239 *DeviceObject = deviceObject;
9240
9241 return status;
9242 } // end ClassCreateDeviceObject()
9243
9244 /*++////////////////////////////////////////////////////////////////////////////
9245
9246 ClassClaimDevice()
9247
9248 Routine Description:
9249
9250 This function claims a device in the port driver. The port driver object
9251 is updated with the correct driver object if the device is successfully
9252 claimed.
9253
9254 Arguments:
9255
9256 LowerDeviceObject - Supplies the base port device object.
9257
9258 Release - Indicates the logical unit should be released rather than claimed.
9259
9260 Return Value:
9261
9262 Returns a status indicating success or failure of the operation.
9263
9264 --*/
9265 _IRQL_requires_max_(PASSIVE_LEVEL)
9266 NTSTATUS
9267 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9268 ClassClaimDevice(
9269 _In_ PDEVICE_OBJECT LowerDeviceObject,
9270 _In_ BOOLEAN Release
9271 )
9272 {
9273 IO_STATUS_BLOCK ioStatus;
9274 PIRP irp;
9275 PIO_STACK_LOCATION irpStack;
9276 KEVENT event;
9277 NTSTATUS status;
9278 SCSI_REQUEST_BLOCK srb = {0};
9279
9280 PAGED_CODE();
9281
9282 //
9283 // WORK ITEM - MPIO related. Need to think about how to handle.
9284 //
9285
9286 srb.Length = sizeof(SCSI_REQUEST_BLOCK);
9287
9288 srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
9289 SRB_FUNCTION_CLAIM_DEVICE;
9290
9291 //
9292 // Set the event object to the unsignaled state.
9293 // It will be used to signal request completion
9294 //
9295
9296 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
9297
9298 //
9299 // Build synchronous request with no transfer.
9300 //
9301
9302 irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
9303 LowerDeviceObject,
9304 NULL,
9305 0,
9306 NULL,
9307 0,
9308 TRUE,
9309 &event,
9310 &ioStatus);
9311
9312 if (irp == NULL) {
9313 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassClaimDevice: Can't allocate Irp\n"));
9314 return STATUS_INSUFFICIENT_RESOURCES;
9315 }
9316
9317 irpStack = IoGetNextIrpStackLocation(irp);
9318
9319 //
9320 // Save SRB address in next stack for port driver.
9321 //
9322
9323 irpStack->Parameters.Scsi.Srb = &srb;
9324
9325 //
9326 // Set up IRP Address.
9327 //
9328
9329 srb.OriginalRequest = irp;
9330
9331 //
9332 // Call the port driver with the request and wait for it to complete.
9333 //
9334
9335 status = IoCallDriver(LowerDeviceObject, irp);
9336 if (status == STATUS_PENDING) {
9337
9338 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
9339 status = ioStatus.Status;
9340 }
9341
9342 //
9343 // If this is a release request, then just decrement the reference count
9344 // and return. The status does not matter.
9345 //
9346
9347 if (Release) {
9348
9349 // ObDereferenceObject(LowerDeviceObject);
9350 return STATUS_SUCCESS;
9351 }
9352
9353 if (!NT_SUCCESS(status)) {
9354 return status;
9355 }
9356
9357 NT_ASSERT(srb.DataBuffer != NULL);
9358 NT_ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
9359
9360 return status;
9361 } // end ClassClaimDevice()
9362
9363 /*++////////////////////////////////////////////////////////////////////////////
9364
9365 ClassInternalIoControl()
9366
9367 Routine Description:
9368
9369 This routine passes internal device controls to the port driver.
9370 Internal device controls are used by higher level drivers both for ioctls
9371 and to pass through scsi requests.
9372
9373 If the IoControlCode does not match any of the handled ioctls and is
9374 a valid system address then the request will be treated as an SRB and
9375 passed down to the lower driver. If the IoControlCode is not a valid
9376 system address the ioctl will be failed.
9377
9378 Callers must therefore be extremely cautious to pass correct, initialized
9379 values to this function.
9380
9381 Arguments:
9382
9383 DeviceObject - Supplies a pointer to the device object for this request.
9384
9385 Irp - Supplies the Irp making the request.
9386
9387 Return Value:
9388
9389 Returns back a STATUS_PENDING or a completion status.
9390
9391 --*/
9392 NTSTATUS
9393 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9394 ClassInternalIoControl(
9395 IN PDEVICE_OBJECT DeviceObject,
9396 IN PIRP Irp
9397 )
9398 {
9399 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
9400
9401 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
9402 PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
9403
9404 ULONG isRemoved;
9405
9406 PSCSI_REQUEST_BLOCK srb;
9407
9408 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
9409 _Analysis_assume_(isRemoved);
9410 if(isRemoved) {
9411
9412 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
9413
9414 ClassReleaseRemoveLock(DeviceObject, Irp);
9415
9416 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
9417
9418 return STATUS_DEVICE_DOES_NOT_EXIST;
9419 }
9420
9421 //
9422 // Get a pointer to the SRB.
9423 //
9424
9425 srb = irpStack->Parameters.Scsi.Srb;
9426
9427 //
9428 // Set the parameters in the next stack location.
9429 //
9430
9431 if(commonExtension->IsFdo) {
9432 nextStack->Parameters.Scsi.Srb = srb;
9433 nextStack->MajorFunction = IRP_MJ_SCSI;
9434 nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
9435
9436 } else {
9437
9438 IoCopyCurrentIrpStackLocationToNext(Irp);
9439 }
9440
9441 ClassReleaseRemoveLock(DeviceObject, Irp);
9442
9443 return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
9444 } // end ClassInternalIoControl()
9445
9446 /*++////////////////////////////////////////////////////////////////////////////
9447
9448 ClassQueryTimeOutRegistryValue()
9449
9450 Routine Description:
9451
9452 This routine determines whether a reg key for a user-specified timeout
9453 value exists. This should be called at initialization time.
9454
9455 Arguments:
9456
9457 DeviceObject - Pointer to the device object we are retrieving the timeout
9458 value for
9459
9460 Return Value:
9461
9462 None, but it sets a new default timeout for a class of devices.
9463
9464 --*/
9465 _IRQL_requires_max_(PASSIVE_LEVEL)
9466 ULONG
9467 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9468 ClassQueryTimeOutRegistryValue(
9469 _In_ PDEVICE_OBJECT DeviceObject
9470 )
9471 {
9472 //
9473 // Find the appropriate reg. key
9474 //
9475
9476 #ifdef _MSC_VER
9477 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
9478 #endif
9479 PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
9480
9481 PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
9482
9483 PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
9484 PWSTR path;
9485 NTSTATUS status;
9486 LONG timeOut = 0;
9487 ULONG zero = 0;
9488 ULONG size;
9489
9490 PAGED_CODE();
9491
9492 if (!registryPath) {
9493 return 0;
9494 }
9495
9496 parameters = ExAllocatePoolWithTag(NonPagedPoolNx,
9497 sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
9498 '1BcS');
9499
9500 if (!parameters) {
9501 return 0;
9502 }
9503
9504 size = registryPath->MaximumLength + sizeof(WCHAR);
9505 path = ExAllocatePoolWithTag(NonPagedPoolNx, size, '2BcS');
9506
9507 if (!path) {
9508 FREE_POOL(parameters);
9509 return 0;
9510 }
9511
9512 RtlZeroMemory(path,size);
9513 RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
9514
9515
9516 //
9517 // Check for the Timeout value.
9518 //
9519
9520 RtlZeroMemory(parameters,
9521 (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
9522
9523 parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
9524 parameters[0].Name = L"TimeOutValue";
9525 parameters[0].EntryContext = &timeOut;
9526 parameters[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD;
9527 parameters[0].DefaultData = &zero;
9528 parameters[0].DefaultLength = sizeof(ULONG);
9529
9530 status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
9531 path,
9532 parameters,
9533 NULL,
9534 NULL);
9535
9536 if (!(NT_SUCCESS(status))) {
9537 timeOut = 0;
9538 }
9539
9540 FREE_POOL(parameters);
9541 FREE_POOL(path);
9542
9543 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
9544 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
9545 timeOut));
9546
9547
9548 return timeOut;
9549
9550 } // end ClassQueryTimeOutRegistryValue()
9551
9552
9553 /*++////////////////////////////////////////////////////////////////////////////
9554
9555 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
9556
9557 Routine Description:
9558
9559 This routine executes when the port driver has completed a check verify
9560 ioctl. It will set the status of the master Irp, copy the media change
9561 count and complete the request.
9562
9563 Arguments:
9564
9565 Fdo - Supplies the functional device object which represents the logical unit.
9566
9567 Irp - Supplies the Irp which has completed.
9568
9569 Context - NULL
9570
9571 Return Value:
9572
9573 NT status
9574
9575 --*/
9576 NTSTATUS
9577 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9578 ClassCheckVerifyComplete(
9579 IN PDEVICE_OBJECT Fdo,
9580 IN PIRP Irp,
9581 IN PVOID Context
9582 )
9583 {
9584 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
9585 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
9586
9587 PIRP originalIrp;
9588
9589 UNREFERENCED_PARAMETER(Context);
9590
9591 ASSERT_FDO(Fdo);
9592
9593 originalIrp = irpStack->Parameters.Others.Argument1;
9594
9595 //
9596 // Copy the media change count and status
9597 //
9598
9599 *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
9600 fdoExtension->MediaChangeCount;
9601
9602 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClassCheckVerifyComplete - Media change count for"
9603 "device %d is %lx - saved as %lx\n",
9604 fdoExtension->DeviceNumber,
9605 fdoExtension->MediaChangeCount,
9606 *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
9607
9608 originalIrp->IoStatus.Status = Irp->IoStatus.Status;
9609 originalIrp->IoStatus.Information = sizeof(ULONG);
9610
9611 ClassReleaseRemoveLock(Fdo, originalIrp);
9612 ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
9613
9614 IoFreeIrp(Irp);
9615
9616 return STATUS_MORE_PROCESSING_REQUIRED;
9617
9618 } // end ClassCheckVerifyComplete()
9619
9620
9621 /*++////////////////////////////////////////////////////////////////////////////
9622
9623 ClassGetDescriptor()
9624
9625 Routine Description:
9626
9627 This routine will perform a query for the specified property id and will
9628 allocate a non-paged buffer to store the data in. It is the responsibility
9629 of the caller to ensure that this buffer is freed.
9630
9631 This routine must be run at IRQL_PASSIVE_LEVEL
9632
9633 Arguments:
9634
9635 DeviceObject - the device to query
9636 DeviceInfo - a location to store a pointer to the buffer we allocate
9637
9638 Return Value:
9639
9640 status
9641 if status is unsuccessful *DeviceInfo will be set to NULL, else the
9642 buffer allocated on behalf of the caller.
9643
9644 --*/
9645 _IRQL_requires_max_(PASSIVE_LEVEL)
9646 NTSTATUS
9647 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9648 ClassGetDescriptor(
9649 _In_ PDEVICE_OBJECT DeviceObject,
9650 _In_ PSTORAGE_PROPERTY_ID PropertyId,
9651 _Outptr_ PVOID *Descriptor
9652 )
9653 {
9654 STORAGE_PROPERTY_QUERY query = {0};
9655 IO_STATUS_BLOCK ioStatus;
9656
9657 PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
9658 ULONG length;
9659
9660 PAGED_CODE();
9661
9662 //
9663 // Set the passed-in descriptor pointer to NULL as default
9664 //
9665
9666 *Descriptor = NULL;
9667
9668 query.PropertyId = *PropertyId;
9669 query.QueryType = PropertyStandardQuery;
9670
9671 //
9672 // On the first pass we just want to get the first few
9673 // bytes of the descriptor so we can read it's size
9674 //
9675
9676 descriptor = (PVOID)&query;
9677
9678 NT_ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
9679
9680 ClassSendDeviceIoControlSynchronous(
9681 IOCTL_STORAGE_QUERY_PROPERTY,
9682 DeviceObject,
9683 &query,
9684 sizeof(STORAGE_PROPERTY_QUERY),
9685 sizeof(ULONG) * 2,
9686 FALSE,
9687 &ioStatus
9688 );
9689
9690 if(!NT_SUCCESS(ioStatus.Status)) {
9691
9692 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: error %lx trying to "
9693 "query properties #1\n", ioStatus.Status));
9694 return ioStatus.Status;
9695 }
9696
9697 if (descriptor->Size == 0) {
9698
9699 //
9700 // This DebugPrint is to help third-party driver writers
9701 //
9702
9703 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: size returned was zero?! (status "
9704 "%x\n", ioStatus.Status));
9705 return STATUS_UNSUCCESSFUL;
9706
9707 }
9708
9709 //
9710 // This time we know how much data there is so we can
9711 // allocate a buffer of the correct size
9712 //
9713
9714 length = descriptor->Size;
9715 NT_ASSERT(length >= sizeof(STORAGE_PROPERTY_QUERY));
9716 length = max(length, sizeof(STORAGE_PROPERTY_QUERY));
9717
9718 descriptor = ExAllocatePoolWithTag(NonPagedPoolNx, length, '4BcS');
9719
9720 if(descriptor == NULL) {
9721
9722 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: unable to memory for descriptor "
9723 "(%d bytes)\n", length));
9724 return STATUS_INSUFFICIENT_RESOURCES;
9725 }
9726
9727 //
9728 // setup the query again, as it was overwritten above
9729 //
9730
9731 RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
9732 query.PropertyId = *PropertyId;
9733 query.QueryType = PropertyStandardQuery;
9734
9735 //
9736 // copy the input to the new outputbuffer
9737 //
9738
9739 RtlZeroMemory(descriptor, length);
9740
9741 RtlCopyMemory(descriptor,
9742 &query,
9743 sizeof(STORAGE_PROPERTY_QUERY)
9744 );
9745
9746 ClassSendDeviceIoControlSynchronous(
9747 IOCTL_STORAGE_QUERY_PROPERTY,
9748 DeviceObject,
9749 descriptor,
9750 sizeof(STORAGE_PROPERTY_QUERY),
9751 length,
9752 FALSE,
9753 &ioStatus
9754 );
9755
9756 if(!NT_SUCCESS(ioStatus.Status)) {
9757
9758 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: error %lx trying to "
9759 "query properties #1\n", ioStatus.Status));
9760 FREE_POOL(descriptor);
9761 return ioStatus.Status;
9762 }
9763
9764 //
9765 // return the memory we've allocated to the caller
9766 //
9767
9768 *Descriptor = descriptor;
9769 return ioStatus.Status;
9770 } // end ClassGetDescriptor()
9771
9772
9773 /*++////////////////////////////////////////////////////////////////////////////
9774
9775 ClassSignalCompletion()
9776
9777 Routine Description:
9778
9779 This completion routine will signal the event given as context and then
9780 return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
9781 the responsibility of the routine waiting on the event to complete the
9782 request and free the event.
9783
9784 Arguments:
9785
9786 DeviceObject - a pointer to the device object
9787
9788 Irp - a pointer to the irp
9789
9790 Event - a pointer to the event to signal
9791
9792 Return Value:
9793
9794 STATUS_MORE_PROCESSING_REQUIRED
9795
9796 --*/
9797 NTSTATUS
9798 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9799 ClassSignalCompletion(
9800 IN PDEVICE_OBJECT DeviceObject,
9801 IN PIRP Irp,
9802 IN PVOID Context
9803 )
9804 {
9805 PKEVENT Event = (PKEVENT)Context;
9806
9807 UNREFERENCED_PARAMETER(DeviceObject);
9808 UNREFERENCED_PARAMETER(Irp);
9809
9810 if (Context == NULL) {
9811 NT_ASSERT(Context != NULL);
9812 return STATUS_INVALID_PARAMETER;
9813 }
9814
9815 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
9816
9817 return STATUS_MORE_PROCESSING_REQUIRED;
9818 } // end ClassSignalCompletion()
9819
9820 /*++////////////////////////////////////////////////////////////////////////////
9821
9822 ClassPnpQueryFdoRelations()
9823
9824 Routine Description:
9825
9826 This routine will call the driver's enumeration routine to update the
9827 list of PDO's. It will then build a response to the
9828 IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
9829 the irp.
9830
9831 Arguments:
9832
9833 Fdo - a pointer to the functional device object we are enumerating
9834
9835 Irp - a pointer to the enumeration request
9836
9837 Return Value:
9838
9839 status
9840
9841 --*/
9842 NTSTATUS
9843 ClassPnpQueryFdoRelations(
9844 IN PDEVICE_OBJECT Fdo,
9845 IN PIRP Irp
9846 )
9847 {
9848 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
9849 #ifdef _MSC_VER
9850 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
9851 #endif
9852 PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
9853
9854 PAGED_CODE();
9855
9856 _Analysis_assume_(driverExtension != NULL);
9857
9858 //
9859 // If there's already an enumeration in progress then don't start another
9860 // one.
9861 //
9862
9863 if(InterlockedIncrement((volatile LONG *)&(fdoExtension->EnumerationInterlock)) == 1) {
9864 driverExtension->InitData.ClassEnumerateDevice(Fdo);
9865 }
9866
9867 Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
9868 Fdo,
9869 BusRelations,
9870 (PDEVICE_RELATIONS *) &Irp->IoStatus.Information);
9871 InterlockedDecrement((volatile LONG *)&(fdoExtension->EnumerationInterlock));
9872
9873 return Irp->IoStatus.Status;
9874 } // end ClassPnpQueryFdoRelations()
9875
9876 /*++////////////////////////////////////////////////////////////////////////////
9877
9878 ClassMarkChildrenMissing()
9879
9880 Routine Description:
9881
9882 This routine will call ClassMarkChildMissing() for all children.
9883 It acquires the ChildLock before calling ClassMarkChildMissing().
9884
9885 Arguments:
9886
9887 Fdo - the "bus's" device object, such as the disk FDO for non-removable
9888 disks with multiple partitions.
9889
9890 Return Value:
9891
9892 None
9893
9894 --*/
9895 _IRQL_requires_max_(PASSIVE_LEVEL)
9896 VOID
9897 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9898 ClassMarkChildrenMissing(
9899 _In_ PFUNCTIONAL_DEVICE_EXTENSION Fdo
9900 )
9901 {
9902 PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
9903 PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
9904
9905 PAGED_CODE();
9906
9907 ClassAcquireChildLock(Fdo);
9908
9909 while (nextChild){
9910 PPHYSICAL_DEVICE_EXTENSION tmpChild;
9911
9912 /*
9913 * ClassMarkChildMissing will also dequeue the child extension.
9914 * So get the next pointer before calling ClassMarkChildMissing.
9915 */
9916 tmpChild = nextChild;
9917 nextChild = tmpChild->CommonExtension.ChildList;
9918 ClassMarkChildMissing(tmpChild, FALSE);
9919 }
9920 ClassReleaseChildLock(Fdo);
9921 return;
9922 } // end ClassMarkChildrenMissing()
9923
9924 /*++////////////////////////////////////////////////////////////////////////////
9925
9926 ClassMarkChildMissing()
9927
9928 Routine Description:
9929
9930 This routine will make an active child "missing." If the device has never
9931 been enumerated then it will be deleted on the spot. If the device has
9932 not been enumerated then it will be marked as missing so that we can
9933 not report it in the next device enumeration.
9934
9935 Arguments:
9936
9937 Child - the child device to be marked as missing.
9938
9939 AcquireChildLock - TRUE if the child lock should be acquired before removing
9940 the missing child. FALSE if the child lock is already
9941 acquired by this thread.
9942
9943 Return Value:
9944
9945 returns whether or not the child device object has previously been reported
9946 to PNP.
9947
9948 --*/
9949 _IRQL_requires_max_(PASSIVE_LEVEL)
9950 BOOLEAN
9951 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9952 ClassMarkChildMissing(
9953 _In_ PPHYSICAL_DEVICE_EXTENSION Child,
9954 _In_ BOOLEAN AcquireChildLock
9955 )
9956 {
9957 BOOLEAN returnValue = Child->IsEnumerated;
9958
9959 PAGED_CODE();
9960 ASSERT_PDO(Child->DeviceObject);
9961
9962 Child->IsMissing = TRUE;
9963
9964 //
9965 // Make sure this child is not in the active list.
9966 //
9967
9968 ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
9969 Child,
9970 AcquireChildLock);
9971
9972 if(Child->IsEnumerated == FALSE) {
9973 PCOMMON_DEVICE_EXTENSION commonExtension = Child->DeviceObject->DeviceExtension;
9974 commonExtension->IsRemoved = REMOVE_PENDING;
9975 ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
9976 }
9977
9978 return returnValue;
9979 } // end ClassMarkChildMissing()
9980
9981 /*++////////////////////////////////////////////////////////////////////////////
9982
9983 ClassRetrieveDeviceRelations()
9984
9985 Routine Description:
9986
9987 This routine will allocate a buffer to hold the specified list of
9988 relations. It will then fill in the list with referenced device pointers
9989 and will return the request.
9990
9991 Arguments:
9992
9993 Fdo - pointer to the FDO being queried
9994
9995 RelationType - what type of relations are being queried
9996
9997 DeviceRelations - a location to store a pointer to the response
9998
9999 Return Value:
10000
10001 status
10002
10003 --*/
10004 NTSTATUS
10005 ClassRetrieveDeviceRelations(
10006 IN PDEVICE_OBJECT Fdo,
10007 IN DEVICE_RELATION_TYPE RelationType,
10008 OUT PDEVICE_RELATIONS *DeviceRelations
10009 )
10010 {
10011 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
10012
10013 ULONG count = 0;
10014 ULONG i;
10015
10016 PPHYSICAL_DEVICE_EXTENSION nextChild;
10017
10018 ULONG relationsSize;
10019 PDEVICE_RELATIONS deviceRelations = NULL;
10020 PDEVICE_RELATIONS oldRelations = *DeviceRelations;
10021
10022 NTSTATUS status;
10023
10024 UNREFERENCED_PARAMETER(RelationType);
10025
10026 PAGED_CODE();
10027
10028 ClassAcquireChildLock(fdoExtension);
10029
10030 nextChild = fdoExtension->CommonExtension.ChildList;
10031
10032 //
10033 // Count the number of PDO's attached to this disk
10034 //
10035
10036 while (nextChild != NULL) {
10037 PCOMMON_DEVICE_EXTENSION commonExtension;
10038
10039 commonExtension = &(nextChild->CommonExtension);
10040
10041 NT_ASSERTMSG("ClassPnp internal error: missing child on active list\n",
10042 (nextChild->IsMissing == FALSE));
10043
10044 nextChild = commonExtension->ChildList;
10045
10046 count++;
10047 };
10048
10049 //
10050 // If relations already exist in the QDR, adjust the current count
10051 // to include the previous list.
10052 //
10053
10054 if (oldRelations) {
10055 count += oldRelations->Count;
10056 }
10057
10058 relationsSize = (sizeof(DEVICE_RELATIONS) +
10059 (count * sizeof(PDEVICE_OBJECT)));
10060
10061 deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
10062
10063 if (deviceRelations == NULL) {
10064
10065 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_ENUM, "ClassRetrieveDeviceRelations: unable to allocate "
10066 "%d bytes for device relations\n", relationsSize));
10067
10068 ClassReleaseChildLock(fdoExtension);
10069
10070 return STATUS_INSUFFICIENT_RESOURCES;
10071 }
10072
10073 RtlZeroMemory(deviceRelations, relationsSize);
10074
10075 if (oldRelations) {
10076
10077 //
10078 // Copy the old relations to the new list and free the old list.
10079 //
10080
10081 for (i = 0; i < oldRelations->Count; i++) {
10082 deviceRelations->Objects[i] = oldRelations->Objects[i];
10083 }
10084
10085 FREE_POOL(oldRelations);
10086 }
10087
10088 nextChild = fdoExtension->CommonExtension.ChildList;
10089 i = count;
10090
10091 while (nextChild != NULL) {
10092 PCOMMON_DEVICE_EXTENSION commonExtension;
10093
10094 commonExtension = &(nextChild->CommonExtension);
10095
10096 NT_ASSERTMSG("ClassPnp internal error: missing child on active list\n",
10097 (nextChild->IsMissing == FALSE));
10098
10099 _Analysis_assume_(i >= 1);
10100 deviceRelations->Objects[--i] = nextChild->DeviceObject;
10101
10102 status = ObReferenceObjectByPointer(
10103 nextChild->DeviceObject,
10104 0,
10105 NULL,
10106 KernelMode);
10107 if (!NT_SUCCESS(status)) {
10108 NT_ASSERT(!"Error referencing child device by pointer");
10109 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM,
10110 "ClassRetrieveDeviceRelations: Error referencing child "
10111 "device %p by pointer\n", nextChild->DeviceObject));
10112
10113 }
10114 nextChild->IsEnumerated = TRUE;
10115 nextChild = commonExtension->ChildList;
10116 }
10117
10118 NT_ASSERTMSG("Child list has changed: ", i == 0);
10119
10120 deviceRelations->Count = count;
10121 *DeviceRelations = deviceRelations;
10122
10123 ClassReleaseChildLock(fdoExtension);
10124 return STATUS_SUCCESS;
10125 } // end ClassRetrieveDeviceRelations()
10126
10127 /*++////////////////////////////////////////////////////////////////////////////
10128
10129 ClassGetPdoId()
10130
10131 Routine Description:
10132
10133 This routine will call into the driver to retrieve a copy of one of it's
10134 id strings.
10135
10136 Arguments:
10137
10138 Pdo - a pointer to the pdo being queried
10139
10140 IdType - which type of id string is being queried
10141
10142 IdString - an allocated unicode string structure which the driver
10143 can fill in.
10144
10145 Return Value:
10146
10147 status
10148
10149 --*/
10150 NTSTATUS
10151 ClassGetPdoId(
10152 IN PDEVICE_OBJECT Pdo,
10153 IN BUS_QUERY_ID_TYPE IdType,
10154 IN PUNICODE_STRING IdString
10155 )
10156 {
10157 #ifdef _MSC_VER
10158 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
10159 #endif
10160 PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
10161
10162 _Analysis_assume_(driverExtension != NULL);
10163
10164 ASSERT_PDO(Pdo);
10165 NT_ASSERT(driverExtension->InitData.ClassQueryId);
10166
10167 PAGED_CODE();
10168
10169 return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
10170 } // end ClassGetPdoId()
10171
10172 /*++////////////////////////////////////////////////////////////////////////////
10173
10174 ClassQueryPnpCapabilities()
10175
10176 Routine Description:
10177
10178 This routine will call into the class driver to retrieve it's pnp
10179 capabilities.
10180
10181 Arguments:
10182
10183 PhysicalDeviceObject - The physical device object to retrieve properties
10184 for.
10185
10186 Return Value:
10187
10188 status
10189
10190 --*/
10191 NTSTATUS
10192 ClassQueryPnpCapabilities(
10193 IN PDEVICE_OBJECT DeviceObject,
10194 IN PDEVICE_CAPABILITIES Capabilities
10195 )
10196 {
10197 PCLASS_DRIVER_EXTENSION driverExtension =
10198 ClassGetDriverExtension(DeviceObject->DriverObject);
10199 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
10200
10201 PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
10202
10203 PAGED_CODE();
10204
10205 NT_ASSERT(DeviceObject);
10206 NT_ASSERT(Capabilities);
10207
10208 if(commonExtension->IsFdo) {
10209 queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
10210 } else {
10211 queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
10212 }
10213
10214 if(queryRoutine) {
10215 return queryRoutine(DeviceObject,
10216 Capabilities);
10217 } else {
10218 return STATUS_NOT_IMPLEMENTED;
10219 }
10220 } // end ClassQueryPnpCapabilities()
10221
10222 /*++////////////////////////////////////////////////////////////////////////////
10223
10224 ClassInvalidateBusRelations()
10225
10226 Routine Description:
10227
10228 This routine re-enumerates the devices on the "bus". It will call into
10229 the driver's ClassEnumerate routine to update the device objects
10230 immediately. It will then schedule a bus re-enumeration for pnp by calling
10231 IoInvalidateDeviceRelations.
10232
10233 Arguments:
10234
10235 Fdo - a pointer to the functional device object for this bus
10236
10237 Return Value:
10238
10239 none
10240
10241 --*/
10242 _IRQL_requires_max_(PASSIVE_LEVEL)
10243 VOID
10244 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10245 ClassInvalidateBusRelations(
10246 _In_ PDEVICE_OBJECT Fdo
10247 )
10248 {
10249 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
10250 #ifdef _MSC_VER
10251 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
10252 #endif
10253 PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
10254
10255 NTSTATUS status = STATUS_SUCCESS;
10256
10257 PAGED_CODE();
10258
10259 _Analysis_assume_(driverExtension != NULL);
10260
10261 ASSERT_FDO(Fdo);
10262 NT_ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
10263
10264 if(InterlockedIncrement((volatile LONG *)&(fdoExtension->EnumerationInterlock)) == 1) {
10265 status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
10266 }
10267 InterlockedDecrement((volatile LONG *)&(fdoExtension->EnumerationInterlock));
10268
10269 if(!NT_SUCCESS(status)) {
10270
10271 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_ENUM, "ClassInvalidateBusRelations: EnumerateDevice routine "
10272 "returned %lx\n", status));
10273 }
10274
10275 IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
10276
10277 return;
10278 } // end ClassInvalidateBusRelations()
10279
10280 /*++////////////////////////////////////////////////////////////////////////////
10281
10282 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
10283
10284 Routine Description:
10285
10286 This routine is called to handle the "removal" of a device. It will
10287 forward the request downwards if necesssary, call into the driver
10288 to release any necessary resources (memory, events, etc) and then
10289 will delete the device object.
10290
10291 Arguments:
10292
10293 DeviceObject - a pointer to the device object being removed
10294
10295 RemoveType - indicates what type of remove this is (regular or surprise).
10296
10297 Return Value:
10298
10299 status
10300
10301 --*/
10302 _IRQL_requires_max_(PASSIVE_LEVEL)
10303 NTSTATUS
10304 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10305 ClassRemoveDevice(
10306 _In_ PDEVICE_OBJECT DeviceObject,
10307 _In_ UCHAR RemoveType
10308 )
10309 {
10310 #ifdef _MSC_VER
10311 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
10312 #endif
10313 PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
10314 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
10315 PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
10316 BOOLEAN proceedWithRemove = TRUE;
10317 NTSTATUS status;
10318 PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
10319
10320
10321 PAGED_CODE();
10322
10323 _Analysis_assume_(driverExtension != NULL);
10324
10325 /*
10326 * Deregister from WMI.
10327 */
10328 if (commonExtension->IsFdo ||
10329 driverExtension->InitData.PdoData.ClassWmiInfo.GuidRegInfo) {
10330 status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
10331 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
10332 }
10333
10334 /*
10335 * If we exposed a "shingle" (a named device interface openable by CreateFile)
10336 * then delete it now.
10337 */
10338 if (commonExtension->MountedDeviceInterfaceName.Buffer){
10339 (VOID)IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
10340 RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
10341 RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
10342 }
10343
10344 //
10345 // If this is a surprise removal we leave the device around - which means
10346 // we don't have to (or want to) drop the remove lock and wait for pending
10347 // requests to complete.
10348 //
10349
10350 if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10351
10352 //
10353 // Release the lock we acquired when the device object was created.
10354 //
10355
10356 ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
10357
10358 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - Reference count is now %d\n",
10359 commonExtension->RemoveLock));
10360
10361 //
10362 // The RemoveLockRundown allows fast protection of the device object
10363 // structure that is torn down by a single thread. While in the rundown process (the call to
10364 // ExWaitForRundownProtectionReleaseCacheAware returns), the rundown object becomes
10365 // invalid and the subsequent calls to ExAcquireRundownProtectionCacheAware will return FALSE.
10366 // ExReInitializeRundownProtectionCacheAware needs to be called to re-initialize the
10367 // RemoveLockRundown protection.
10368 //
10369
10370 removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
10371 ((PCHAR)commonExtension->PrivateCommonData +
10372 sizeof(CLASS_PRIVATE_COMMON_DATA));
10373 ExWaitForRundownProtectionReleaseCacheAware(removeLockRundown);
10374
10375 KeSetEvent(&commonExtension->RemoveEvent,
10376 IO_NO_INCREMENT,
10377 FALSE);
10378
10379 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - removing device %p\n", DeviceObject));
10380
10381 if (commonExtension->IsFdo) {
10382
10383 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - FDO %p has received a "
10384 "remove request.\n", DeviceObject));
10385
10386 } else {
10387 PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
10388
10389 if (pdoExtension->IsMissing) {
10390 /*
10391 * The child partition PDO is missing, so we are going to go ahead
10392 * and delete it for the remove.
10393 */
10394 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
10395 } else {
10396 /*
10397 * We got a remove for a child partition PDO which is not actually missing.
10398 * So we will NOT actually delete it.
10399 */
10400 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
10401
10402 ExReInitializeRundownProtectionCacheAware(removeLockRundown);
10403
10404 //
10405 // Reacquire the remove lock for the next time this comes around.
10406 //
10407
10408 ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
10409
10410 //
10411 // the device wasn't missing so it's not really been removed.
10412 //
10413
10414 commonExtension->IsRemoved = NO_REMOVE;
10415
10416 IoInvalidateDeviceRelations(
10417 commonExtension->PartitionZeroExtension->LowerPdo,
10418 BusRelations);
10419
10420 proceedWithRemove = FALSE;
10421 }
10422 }
10423 }
10424
10425
10426 if (proceedWithRemove) {
10427
10428 /*
10429 * Call the class driver's remove handler.
10430 * All this is supposed to do is clean up its data and device interfaces.
10431 */
10432 NT_ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
10433 status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
10434 NT_ASSERT(NT_SUCCESS(status));
10435 status = STATUS_SUCCESS;
10436 UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
10437
10438 if (commonExtension->IsFdo) {
10439 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
10440
10441 ClasspDisableTimer(fdoExtension);
10442
10443 if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10444
10445 PPHYSICAL_DEVICE_EXTENSION child;
10446
10447 //
10448 // If this FDO is idle power managed, remove it from the
10449 // list of idle power managed FDOs.
10450 //
10451 if (fdoExtension->FunctionSupportInfo &&
10452 fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled) {
10453 PIDLE_POWER_FDO_LIST_ENTRY fdoEntry;
10454
10455 KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
10456 fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)IdlePowerFDOList.Flink;
10457 while ((PLIST_ENTRY)fdoEntry != &IdlePowerFDOList) {
10458 PIDLE_POWER_FDO_LIST_ENTRY nextEntry = (PIDLE_POWER_FDO_LIST_ENTRY)fdoEntry->ListEntry.Flink;
10459 if (fdoEntry->Fdo == DeviceObject) {
10460 RemoveEntryList(&(fdoEntry->ListEntry));
10461 ExFreePool(fdoEntry);
10462 break;
10463 }
10464 fdoEntry = nextEntry;
10465 }
10466 KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
10467 }
10468
10469 //
10470 // Cleanup the media detection resources now that the class driver
10471 // has stopped it's timer (if any) and we can be sure they won't
10472 // call us to do detection again.
10473 //
10474
10475 ClassCleanupMediaChangeDetection(fdoExtension);
10476
10477 //
10478 // Cleanup any Failure Prediction stuff
10479 //
10480 FREE_POOL(fdoExtension->FailurePredictionInfo);
10481
10482 /*
10483 * Ordinarily all child PDOs will be removed by the time
10484 * that the parent gets the REMOVE_DEVICE.
10485 * However, if a child PDO has been created but has not
10486 * been announced in a QueryDeviceRelations, then it is
10487 * just a private data structure unknown to pnp, and we have
10488 * to delete it ourselves.
10489 */
10490 ClassAcquireChildLock(fdoExtension);
10491 child = ClassRemoveChild(fdoExtension, NULL, FALSE);
10492 while (child) {
10493 PCOMMON_DEVICE_EXTENSION childCommonExtension = child->DeviceObject->DeviceExtension;
10494
10495 //
10496 // Yank the pdo. This routine will unlink the device from the
10497 // pdo list so NextPdo will point to the next one when it's
10498 // complete.
10499 //
10500 child->IsMissing = TRUE;
10501 childCommonExtension->IsRemoved = REMOVE_PENDING;
10502 ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
10503 child = ClassRemoveChild(fdoExtension, NULL, FALSE);
10504 }
10505 ClassReleaseChildLock(fdoExtension);
10506 }
10507 else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
10508 /*
10509 * This is a surprise-remove on the parent FDO.
10510 * We will mark the child PDOs as missing so that they
10511 * will actually get deleted when they get a REMOVE_DEVICE.
10512 */
10513 ClassMarkChildrenMissing(fdoExtension);
10514 }
10515
10516 if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10517
10518 ClasspFreeReleaseRequest(DeviceObject);
10519
10520 //
10521 // Free FDO-specific data structs
10522 //
10523 if (fdoExtension->PrivateFdoData) {
10524 //
10525 // Only remove the entry if the list has been initialized, or
10526 // else we will access invalid memory.
10527 //
10528 PLIST_ENTRY allFdosListEntry = &fdoExtension->PrivateFdoData->AllFdosListEntry;
10529 if (allFdosListEntry->Flink && allFdosListEntry->Blink) {
10530 //
10531 // Remove the FDO from the static list.
10532 // Pnp is synchronized so this shouldn't need any synchronization.
10533 //
10534 RemoveEntryList(allFdosListEntry);
10535 }
10536 InitializeListHead(allFdosListEntry);
10537
10538 DestroyAllTransferPackets(DeviceObject);
10539
10540 //
10541 // Delete the tick timer now.
10542 //
10543 ClasspDeleteTimer(fdoExtension);
10544
10545
10546 FREE_POOL(fdoExtension->PrivateFdoData->PowerProcessIrp);
10547 FREE_POOL(fdoExtension->PrivateFdoData->FreeTransferPacketsLists);
10548 FREE_POOL(fdoExtension->PrivateFdoData);
10549 }
10550
10551 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
10552 FREE_POOL(fdoExtension->AdditionalFdoData);
10553 #endif
10554
10555 if (commonExtension->DeviceName.Buffer) {
10556 FREE_POOL(commonExtension->DeviceName.Buffer);
10557 RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
10558 }
10559
10560 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
10561 if (fdoExtension->FunctionSupportInfo != NULL) {
10562 FREE_POOL(fdoExtension->FunctionSupportInfo->HwFirmwareInfo);
10563 }
10564 #endif
10565 FREE_POOL(fdoExtension->FunctionSupportInfo);
10566
10567 FREE_POOL(fdoExtension->MiniportDescriptor);
10568
10569 FREE_POOL(fdoExtension->AdapterDescriptor);
10570
10571 FREE_POOL(fdoExtension->DeviceDescriptor);
10572
10573 //
10574 // Detach our device object from the stack - there's no reason
10575 // to hold off our cleanup any longer.
10576 //
10577
10578 IoDetachDevice(lowerDeviceObject);
10579 }
10580 }
10581 else {
10582 /*
10583 * This is a child partition PDO.
10584 * We have already determined that it was previously marked
10585 * as missing. So if this is a REMOVE_DEVICE, we will actually
10586 * delete it.
10587 */
10588 if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10589 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
10590 PPHYSICAL_DEVICE_EXTENSION pdoExtension = (PPHYSICAL_DEVICE_EXTENSION)commonExtension;
10591
10592 //
10593 // See if this device is in the child list (if this was a suprise
10594 // removal it might be) and remove it.
10595 //
10596 ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
10597 }
10598 }
10599
10600 commonExtension->PartitionLength.QuadPart = 0;
10601
10602 if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10603
10604 ClasspUninitializeRemoveTracking(DeviceObject);
10605
10606 IoDeleteDevice(DeviceObject);
10607 }
10608 }
10609
10610 return STATUS_SUCCESS;
10611 } // end ClassRemoveDevice()
10612
10613 /*++////////////////////////////////////////////////////////////////////////////
10614
10615 ClassGetDriverExtension()
10616
10617 Routine Description:
10618
10619 This routine will return the classpnp's driver extension.
10620
10621 Arguments:
10622
10623 DriverObject - the driver object for which to get classpnp's extension
10624
10625 Return Value:
10626
10627 Either NULL if none, or a pointer to the driver extension
10628
10629 --*/
10630 __drv_aliasesMem
10631 _IRQL_requires_max_(DISPATCH_LEVEL)
10632 PCLASS_DRIVER_EXTENSION
10633 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10634 ClassGetDriverExtension(
10635 _In_ PDRIVER_OBJECT DriverObject
10636 )
10637 {
10638 #ifdef _MSC_VER
10639 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
10640 #endif
10641 return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
10642 } // end ClassGetDriverExtension()
10643
10644 /*++////////////////////////////////////////////////////////////////////////////
10645
10646 ClasspStartIo()
10647
10648 Routine Description:
10649
10650 This routine wraps the class driver's start io routine. If the device
10651 is being removed it will complete any requests with
10652 STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
10653
10654 Arguments:
10655
10656 Return Value:
10657
10658 none
10659
10660 --*/
10661 VOID
10662 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10663 ClasspStartIo(
10664 IN PDEVICE_OBJECT DeviceObject,
10665 IN PIRP Irp
10666 )
10667 {
10668 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
10669
10670 //
10671 // We're already holding the remove lock so just check the variable and
10672 // see what's going on.
10673 //
10674
10675 if(commonExtension->IsRemoved) {
10676
10677 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
10678
10679 #ifdef _MSC_VER
10680 #pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
10681 #endif
10682 ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
10683
10684 ClassReleaseRemoveLock(DeviceObject, Irp);
10685 ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
10686 IoStartNextPacket(DeviceObject, TRUE); // Some IO is cancellable
10687
10688 #ifdef _MSC_VER
10689 #pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
10690 #endif
10691 ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
10692
10693 return;
10694 }
10695
10696 commonExtension->DriverExtension->InitData.ClassStartIo(
10697 DeviceObject,
10698 Irp);
10699
10700 return;
10701 } // ClasspStartIo()
10702
10703 /*++////////////////////////////////////////////////////////////////////////////
10704
10705 ClassUpdateInformationInRegistry()
10706
10707 Routine Description:
10708
10709 This routine has knowledge about the layout of the device map information
10710 in the registry. It will update this information to include a value
10711 entry specifying the dos device name that is assumed to get assigned
10712 to this NT device name. For more information on this assigning of the
10713 dos device name look in the drive support routine in the hal that assigns
10714 all dos names.
10715
10716 Since some versions of some device's firmware did not work and some
10717 vendors did not bother to follow the specification, the entire inquiry
10718 information must also be stored in the registry so than someone can
10719 figure out the firmware version.
10720
10721 Arguments:
10722
10723 DeviceObject - A pointer to the device object for the tape device.
10724
10725 Return Value:
10726
10727 None
10728
10729 --*/
10730 _IRQL_requires_max_(PASSIVE_LEVEL)
10731 VOID
10732 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10733 ClassUpdateInformationInRegistry(
10734 _In_ PDEVICE_OBJECT Fdo,
10735 _In_ PCHAR DeviceName,
10736 _In_ ULONG DeviceNumber,
10737 _In_reads_bytes_opt_(InquiryDataLength) PINQUIRYDATA InquiryData,
10738 _In_ ULONG InquiryDataLength
10739 )
10740 {
10741 NTSTATUS status;
10742 SCSI_ADDRESS scsiAddress = {0};
10743 OBJECT_ATTRIBUTES objectAttributes = {0};
10744 STRING string;
10745 UNICODE_STRING unicodeName = {0};
10746 UNICODE_STRING unicodeRegistryPath = {0};
10747 UNICODE_STRING unicodeData = {0};
10748 HANDLE targetKey;
10749 IO_STATUS_BLOCK ioStatus;
10750 UCHAR buffer[256] = {0};
10751
10752 PAGED_CODE();
10753
10754 NT_ASSERT(DeviceName);
10755 targetKey = NULL;
10756
10757 _SEH2_TRY {
10758
10759 //
10760 // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
10761 //
10762
10763 ClassSendDeviceIoControlSynchronous(
10764 IOCTL_SCSI_GET_ADDRESS,
10765 Fdo,
10766 &scsiAddress,
10767 0,
10768 sizeof(SCSI_ADDRESS),
10769 FALSE,
10770 &ioStatus
10771 );
10772
10773 if (!NT_SUCCESS(ioStatus.Status)) {
10774
10775 status = ioStatus.Status;
10776 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
10777 "UpdateInformationInRegistry: Get Address failed %lx\n",
10778 status));
10779 _SEH2_LEAVE;
10780
10781 } else {
10782
10783 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
10784 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
10785 scsiAddress.PortNumber,
10786 scsiAddress.PathId,
10787 scsiAddress.TargetId,
10788 scsiAddress.Lun));
10789
10790 }
10791
10792 status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer,
10793 sizeof(buffer)-1,
10794 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
10795 scsiAddress.PortNumber,
10796 scsiAddress.PathId,
10797 scsiAddress.TargetId,
10798 scsiAddress.Lun);
10799
10800 if (!NT_SUCCESS(status)) {
10801 _SEH2_LEAVE;
10802 }
10803
10804 RtlInitString(&string, (PCSZ)buffer);
10805
10806 status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
10807 &string,
10808 TRUE);
10809
10810 if (!NT_SUCCESS(status)) {
10811 _SEH2_LEAVE;
10812 }
10813
10814 //
10815 // Open the registry key for the scsi information for this
10816 // scsibus, target, lun.
10817 //
10818
10819 InitializeObjectAttributes(&objectAttributes,
10820 &unicodeRegistryPath,
10821 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
10822 NULL,
10823 NULL);
10824
10825 status = ZwOpenKey(&targetKey,
10826 KEY_READ | KEY_WRITE,
10827 &objectAttributes);
10828
10829 if (!NT_SUCCESS(status)) {
10830 _SEH2_LEAVE;
10831 }
10832
10833 //
10834 // Now construct and attempt to create the registry value
10835 // specifying the device name in the appropriate place in the
10836 // device map.
10837 //
10838
10839 RtlInitUnicodeString(&unicodeName, L"DeviceName");
10840
10841 status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer, sizeof(buffer)-1, "%s%d", DeviceName, DeviceNumber);
10842 if (!NT_SUCCESS(status)) {
10843 _SEH2_LEAVE;
10844 }
10845
10846 RtlInitString(&string, (PCSZ)buffer);
10847 status = RtlAnsiStringToUnicodeString(&unicodeData,
10848 &string,
10849 TRUE);
10850 if (NT_SUCCESS(status)) {
10851 status = ZwSetValueKey(targetKey,
10852 &unicodeName,
10853 0,
10854 REG_SZ,
10855 unicodeData.Buffer,
10856 unicodeData.Length);
10857 }
10858
10859 //
10860 // if they sent in data, update the registry
10861 //
10862
10863 if (NT_SUCCESS(status) && InquiryDataLength) {
10864
10865 NT_ASSERT(InquiryData);
10866
10867 RtlInitUnicodeString(&unicodeName, L"InquiryData");
10868 status = ZwSetValueKey(targetKey,
10869 &unicodeName,
10870 0,
10871 REG_BINARY,
10872 InquiryData,
10873 InquiryDataLength);
10874 }
10875
10876 // that's all, except to clean up.
10877
10878 } _SEH2_FINALLY {
10879
10880 if (!NT_SUCCESS(status)) {
10881 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
10882 "Failure to update information in registry: %08x\n",
10883 status
10884 ));
10885 }
10886
10887 if (unicodeData.Buffer) {
10888 RtlFreeUnicodeString(&unicodeData);
10889 }
10890 if (unicodeRegistryPath.Buffer) {
10891 RtlFreeUnicodeString(&unicodeRegistryPath);
10892 }
10893 if (targetKey) {
10894 ZwClose(targetKey);
10895 }
10896
10897 } _SEH2_END;
10898
10899 } // end ClassUpdateInformationInRegistry()
10900
10901 /*++////////////////////////////////////////////////////////////////////////////
10902
10903 ClasspSendSynchronousCompletion()
10904
10905 Routine Description:
10906
10907 This completion routine will set the user event in the irp after
10908 freeing the irp and the associated MDL (if any).
10909
10910 Arguments:
10911
10912 DeviceObject - the device object which requested the completion routine
10913
10914 Irp - the irp being completed
10915
10916 Context - unused
10917
10918 Return Value:
10919
10920 STATUS_MORE_PROCESSING_REQUIRED
10921
10922 --*/
10923 NTSTATUS
10924 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10925 ClasspSendSynchronousCompletion(
10926 IN PDEVICE_OBJECT DeviceObject,
10927 IN PIRP Irp,
10928 IN PVOID Context
10929 )
10930 {
10931 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClasspSendSynchronousCompletion: %p %p %p\n",
10932 DeviceObject, Irp, Context));
10933 //
10934 // First set the status and information fields in the io status block
10935 // provided by the caller.
10936 //
10937
10938 *(Irp->UserIosb) = Irp->IoStatus;
10939
10940 //
10941 // Unlock the pages for the data buffer.
10942 //
10943
10944 if(Irp->MdlAddress) {
10945 MmUnlockPages(Irp->MdlAddress);
10946 IoFreeMdl(Irp->MdlAddress);
10947 }
10948
10949 //
10950 // Signal the caller's event.
10951 //
10952
10953 KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
10954
10955 //
10956 // Free the MDL and the IRP.
10957 //
10958
10959 IoFreeIrp(Irp);
10960
10961 return STATUS_MORE_PROCESSING_REQUIRED;
10962 } // end ClasspSendSynchronousCompletion()
10963
10964 /*++
10965
10966 ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
10967
10968 --*/
10969 VOID
10970 ClasspRegisterMountedDeviceInterface(
10971 IN PDEVICE_OBJECT DeviceObject
10972 )
10973 {
10974
10975 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
10976 BOOLEAN isFdo = commonExtension->IsFdo;
10977 PDEVICE_OBJECT pdo;
10978 UNICODE_STRING interfaceName;
10979
10980 NTSTATUS status;
10981
10982 PAGED_CODE();
10983
10984 if(isFdo) {
10985
10986 PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
10987
10988 functionalExtension =
10989 (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
10990 pdo = functionalExtension->LowerPdo;
10991 } else {
10992 pdo = DeviceObject;
10993 }
10994
10995 #ifdef _MSC_VER
10996 #pragma prefast(suppress:6014, "The allocated memory that interfaceName points to will be freed in ClassRemoveDevice().")
10997 #endif
10998 status = IoRegisterDeviceInterface(
10999 pdo,
11000 &MOUNTDEV_MOUNTED_DEVICE_GUID,
11001 NULL,
11002 &interfaceName
11003 );
11004
11005 if(NT_SUCCESS(status)) {
11006
11007 //
11008 // Copy the interface name before setting the interface state - the
11009 // name is needed by the components we notify.
11010 //
11011
11012 commonExtension->MountedDeviceInterfaceName = interfaceName;
11013 status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
11014
11015 if(!NT_SUCCESS(status)) {
11016 RtlFreeUnicodeString(&interfaceName);
11017 }
11018 }
11019
11020 if(!NT_SUCCESS(status)) {
11021 RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
11022 NULL);
11023 }
11024 return;
11025 } // end ClasspRegisterMountedDeviceInterface()
11026
11027 /*++////////////////////////////////////////////////////////////////////////////
11028
11029 ClassSendDeviceIoControlSynchronous()
11030
11031 Routine Description:
11032
11033 This routine is based upon IoBuildDeviceIoControlRequest(). It has been
11034 modified to reduce code and memory by not double-buffering the io, using
11035 the same buffer for both input and output, allocating and deallocating
11036 the mdl on behalf of the caller, and waiting for the io to complete.
11037
11038 This routine also works around the rare cases in which APC's are disabled.
11039 Since IoBuildDeviceIoControl() used APC's to signal completion, this had
11040 led to a number of difficult-to-detect hangs, where the irp was completed,
11041 but the event passed to IoBuild..() was still being waited upon by the
11042 caller.
11043
11044 Arguments:
11045
11046 IoControlCode - the IOCTL to send
11047
11048 TargetDeviceObject - the device object that should handle the ioctl
11049
11050 Buffer - the input and output buffer, or NULL if no input/output
11051
11052 InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
11053
11054 OutputBufferLength - the number of bytes to be filled in upon success
11055
11056 InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
11057
11058 IoStatus - the status block that contains the results of the operation
11059
11060 Return Value:
11061
11062 --*/
11063 VOID
11064 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11065 ClassSendDeviceIoControlSynchronous(
11066 _In_ ULONG IoControlCode,
11067 _In_ PDEVICE_OBJECT TargetDeviceObject,
11068 _Inout_updates_opt_(_Inexpressible_(max(InputBufferLength, OutputBufferLength))) PVOID Buffer,
11069 _In_ ULONG InputBufferLength,
11070 _In_ ULONG OutputBufferLength,
11071 _In_ BOOLEAN InternalDeviceIoControl,
11072 _Out_ PIO_STATUS_BLOCK IoStatus
11073 )
11074 {
11075 PIRP irp;
11076 PIO_STACK_LOCATION irpSp;
11077 ULONG method;
11078
11079 PAGED_CODE();
11080
11081 irp = NULL;
11082 method = IoControlCode & 3;
11083
11084 #if DBG // Begin Argument Checking (nop in fre version)
11085
11086 NT_ASSERT(ARGUMENT_PRESENT(IoStatus));
11087
11088 if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
11089 NT_ASSERT(ARGUMENT_PRESENT(Buffer));
11090 }
11091 else {
11092 NT_ASSERT(!ARGUMENT_PRESENT(Buffer));
11093 }
11094 #endif
11095
11096 //
11097 // Begin by allocating the IRP for this request. Do not charge quota to
11098 // the current process for this IRP.
11099 //
11100
11101 irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
11102 if (!irp) {
11103 IoStatus->Information = 0;
11104 IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
11105 return;
11106 }
11107
11108 //
11109 // Get a pointer to the stack location of the first driver which will be
11110 // invoked. This is where the function codes and the parameters are set.
11111 //
11112
11113 irpSp = IoGetNextIrpStackLocation(irp);
11114
11115 //
11116 // Set the major function code based on the type of device I/O control
11117 // function the caller has specified.
11118 //
11119
11120 if (InternalDeviceIoControl) {
11121 irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
11122 } else {
11123 irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
11124 }
11125
11126 //
11127 // Copy the caller's parameters to the service-specific portion of the
11128 // IRP for those parameters that are the same for all four methods.
11129 //
11130
11131 irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
11132 irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
11133 irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
11134
11135 //
11136 // Get the method bits from the I/O control code to determine how the
11137 // buffers are to be passed to the driver.
11138 //
11139
11140 switch (method)
11141 {
11142 //
11143 // case 0
11144 //
11145 case METHOD_BUFFERED:
11146 {
11147 if ((InputBufferLength != 0) || (OutputBufferLength != 0))
11148 {
11149 irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
11150 max(InputBufferLength, OutputBufferLength),
11151 CLASS_TAG_DEVICE_CONTROL);
11152 if (irp->AssociatedIrp.SystemBuffer == NULL)
11153 {
11154 IoFreeIrp(irp);
11155
11156 IoStatus->Information = 0;
11157 IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
11158 return;
11159 }
11160
11161 if (InputBufferLength != 0)
11162 {
11163 RtlCopyMemory(irp->AssociatedIrp.SystemBuffer, Buffer, InputBufferLength);
11164 }
11165 }
11166
11167 irp->UserBuffer = Buffer;
11168
11169 break;
11170 }
11171
11172 //
11173 // case 1, case 2
11174 //
11175 case METHOD_IN_DIRECT:
11176 case METHOD_OUT_DIRECT:
11177 {
11178 if (InputBufferLength != 0)
11179 {
11180 irp->AssociatedIrp.SystemBuffer = Buffer;
11181 }
11182
11183 if (OutputBufferLength != 0)
11184 {
11185 irp->MdlAddress = IoAllocateMdl(Buffer,
11186 OutputBufferLength,
11187 FALSE,
11188 FALSE,
11189 (PIRP) NULL);
11190 if (irp->MdlAddress == NULL)
11191 {
11192 IoFreeIrp(irp);
11193
11194 IoStatus->Information = 0;
11195 IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
11196 return;
11197 }
11198
11199 _SEH2_TRY
11200 {
11201 MmProbeAndLockPages(irp->MdlAddress,
11202 KernelMode,
11203 (method == METHOD_IN_DIRECT) ? IoReadAccess : IoWriteAccess);
11204 }
11205 #ifdef _MSC_VER
11206 #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
11207 #endif
11208 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
11209 {
11210 IoFreeMdl(irp->MdlAddress);
11211 IoFreeIrp(irp);
11212
11213 IoStatus->Information = 0;
11214 IoStatus->Status = _SEH2_GetExceptionCode();
11215 _SEH2_YIELD(return);
11216 } _SEH2_END;
11217 }
11218
11219 break;
11220 }
11221
11222 //
11223 // case 3
11224 //
11225 case METHOD_NEITHER:
11226 {
11227 NT_ASSERT(!"ClassSendDeviceIoControlSynchronous does not support METHOD_NEITHER Ioctls");
11228
11229 IoFreeIrp(irp);
11230
11231 IoStatus->Information = 0;
11232 IoStatus->Status = STATUS_NOT_SUPPORTED;
11233 return;
11234 }
11235 }
11236
11237 irp->Tail.Overlay.Thread = PsGetCurrentThread();
11238
11239 //
11240 // send the irp synchronously
11241 //
11242
11243 ClassSendIrpSynchronous(TargetDeviceObject, irp);
11244
11245 //
11246 // copy the iostatus block for the caller
11247 //
11248
11249 *IoStatus = irp->IoStatus;
11250
11251 //
11252 // free any allocated resources
11253 //
11254
11255 switch (method) {
11256 case METHOD_BUFFERED: {
11257
11258 NT_ASSERT(irp->UserBuffer == Buffer);
11259
11260 //
11261 // first copy the buffered result, if any
11262 // Note that there are no security implications in
11263 // not checking for success since only drivers can
11264 // call into this routine anyways...
11265 //
11266
11267 if (OutputBufferLength != 0) {
11268 #ifdef _MSC_VER
11269 #pragma warning(suppress: 6386) // Buffer's size is max(InputBufferLength, OutputBufferLength)
11270 #endif
11271 RtlCopyMemory(Buffer, // irp->UserBuffer
11272 irp->AssociatedIrp.SystemBuffer,
11273 OutputBufferLength
11274 );
11275 }
11276
11277 //
11278 // then free the memory allocated to buffer the io
11279 //
11280
11281 if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
11282 FREE_POOL(irp->AssociatedIrp.SystemBuffer);
11283 }
11284 break;
11285 }
11286
11287 case METHOD_IN_DIRECT:
11288 case METHOD_OUT_DIRECT: {
11289
11290 //
11291 // we alloc a mdl if there is an output buffer specified
11292 // free it here after unlocking the pages
11293 //
11294
11295 if (OutputBufferLength != 0) {
11296 NT_ASSERT(irp->MdlAddress != NULL);
11297 MmUnlockPages(irp->MdlAddress);
11298 IoFreeMdl(irp->MdlAddress);
11299 irp->MdlAddress = (PMDL) NULL;
11300 }
11301 break;
11302 }
11303
11304 case METHOD_NEITHER: {
11305 NT_ASSERT(!"Code is out of date");
11306 break;
11307 }
11308 }
11309
11310 //
11311 // we always have allocated an irp. free it here.
11312 //
11313
11314 IoFreeIrp(irp);
11315 irp = (PIRP) NULL;
11316
11317 //
11318 // return the io status block's status to the caller
11319 //
11320
11321 return;
11322 } // end ClassSendDeviceIoControlSynchronous()
11323
11324 /*++////////////////////////////////////////////////////////////////////////////
11325
11326 ClassForwardIrpSynchronous()
11327
11328 Routine Description:
11329
11330 Forwards a given irp to the next lower device object.
11331
11332 Arguments:
11333
11334 CommonExtension - the common class extension
11335
11336 Irp - the request to forward down the stack
11337
11338 Return Value:
11339
11340 --*/
11341 NTSTATUS
11342 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11343 ClassForwardIrpSynchronous(
11344 _In_ PCOMMON_DEVICE_EXTENSION CommonExtension,
11345 _In_ PIRP Irp
11346 )
11347 {
11348 IoCopyCurrentIrpStackLocationToNext(Irp);
11349 return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
11350 } // end ClassForwardIrpSynchronous()
11351
11352 /*++////////////////////////////////////////////////////////////////////////////
11353
11354 ClassSendIrpSynchronous()
11355
11356 Routine Description:
11357
11358 This routine sends the given irp to the given device object, and waits for
11359 it to complete. On debug versions, will print out a debug message and
11360 optionally assert for "lost" irps based upon classpnp's globals
11361
11362 Arguments:
11363
11364 TargetDeviceObject - the device object to handle this irp
11365
11366 Irp - the request to be sent
11367
11368 Return Value:
11369
11370 --*/
11371 NTSTATUS
11372 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11373 ClassSendIrpSynchronous(
11374 _In_ PDEVICE_OBJECT TargetDeviceObject,
11375 _In_ PIRP Irp
11376 )
11377 {
11378 KEVENT event;
11379 NTSTATUS status;
11380
11381 NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
11382 NT_ASSERT(TargetDeviceObject != NULL);
11383 NT_ASSERT(Irp != NULL);
11384 NT_ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
11385
11386 //
11387 // ISSUE-2000/02/20-henrygab What if APCs are disabled?
11388 // May need to enter critical section before IoCallDriver()
11389 // until the event is hit?
11390 //
11391
11392 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
11393 IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
11394 TRUE, TRUE, TRUE);
11395
11396 status = IoCallDriver(TargetDeviceObject, Irp);
11397 _Analysis_assume_(status!=STATUS_PENDING);
11398 if (status == STATUS_PENDING) {
11399
11400 #if DBG
11401 LARGE_INTEGER timeout;
11402
11403 timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
11404 ClasspnpGlobals.SecondsToWaitForIrps);
11405
11406 do {
11407 status = KeWaitForSingleObject(&event,
11408 Executive,
11409 KernelMode,
11410 FALSE,
11411 &timeout);
11412
11413
11414 if (status == STATUS_TIMEOUT) {
11415
11416 //
11417 // This DebugPrint should almost always be investigated by the
11418 // party who sent the irp and/or the current owner of the irp.
11419 // Synchronous Irps should not take this long (currently 30
11420 // seconds) without good reason. This points to a potentially
11421 // serious problem in the underlying device stack.
11422 //
11423
11424 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendIrpSynchronous: (%p) irp %p did not "
11425 "complete within %x seconds\n",
11426 TargetDeviceObject, Irp,
11427 ClasspnpGlobals.SecondsToWaitForIrps
11428 ));
11429
11430 if (ClasspnpGlobals.BreakOnLostIrps != 0) {
11431 NT_ASSERT(!" - Irp failed to complete within 30 seconds - ");
11432 }
11433 }
11434
11435
11436 } while (status==STATUS_TIMEOUT);
11437 #else
11438 (VOID)KeWaitForSingleObject(&event,
11439 Executive,
11440 KernelMode,
11441 FALSE,
11442 NULL);
11443 #endif
11444
11445 status = Irp->IoStatus.Status;
11446 }
11447
11448 return status;
11449 } // end ClassSendIrpSynchronous()
11450
11451 /*++////////////////////////////////////////////////////////////////////////////
11452
11453 ClassGetVpb()
11454
11455 Routine Description:
11456
11457 This routine returns the current VPB (Volume Parameter Block) for the
11458 given device object.
11459 The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
11460 of DEVICE_OBJECT; hence this exported function.
11461
11462 Arguments:
11463
11464 DeviceObject - the device to get the VPB for
11465
11466 Return Value:
11467
11468 the VPB, or NULL if none.
11469
11470 --*/
11471 PVPB
11472 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11473 ClassGetVpb(
11474 _In_ PDEVICE_OBJECT DeviceObject
11475 )
11476 {
11477 #ifdef _MSC_VER
11478 #pragma prefast(suppress:28175)
11479 #endif
11480 return DeviceObject->Vpb;
11481 } // end ClassGetVpb()
11482
11483 /*++
11484
11485 ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
11486
11487 --*/
11488 NTSTATUS
11489 ClasspAllocateReleaseRequest(
11490 IN PDEVICE_OBJECT Fdo
11491 )
11492 {
11493 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
11494
11495 PAGED_CODE();
11496
11497 KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
11498
11499 fdoExtension->ReleaseQueueNeeded = FALSE;
11500 fdoExtension->ReleaseQueueInProgress = FALSE;
11501 fdoExtension->ReleaseQueueIrpFromPool = FALSE;
11502
11503 //
11504 // The class driver is responsible for allocating a properly sized irp,
11505 // or ClassReleaseQueue will attempt to do it on the first error.
11506 //
11507
11508 fdoExtension->ReleaseQueueIrp = NULL;
11509
11510 //
11511 // Write length to SRB.
11512 //
11513
11514 fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
11515
11516 return STATUS_SUCCESS;
11517 } // end ClasspAllocateReleaseRequest()
11518
11519 /*++
11520
11521 ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
11522
11523 --*/
11524 VOID
11525 ClasspFreeReleaseRequest(
11526 IN PDEVICE_OBJECT Fdo
11527 )
11528 {
11529 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
11530
11531 PAGED_CODE();
11532
11533 //KIRQL oldIrql;
11534
11535 NT_ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
11536
11537 //
11538 // free anything the driver allocated
11539 //
11540
11541 if (fdoExtension->ReleaseQueueIrp) {
11542 if (fdoExtension->ReleaseQueueIrpFromPool) {
11543 FREE_POOL(fdoExtension->ReleaseQueueIrp);
11544 } else {
11545 IoFreeIrp(fdoExtension->ReleaseQueueIrp);
11546 }
11547 fdoExtension->ReleaseQueueIrp = NULL;
11548 }
11549
11550 //
11551 // free anything that we allocated
11552 //
11553
11554 if ((fdoExtension->PrivateFdoData) &&
11555 (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
11556
11557 FREE_POOL(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
11558 fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
11559 }
11560
11561 return;
11562 } // end ClasspFreeReleaseRequest()
11563
11564 /*++////////////////////////////////////////////////////////////////////////////
11565
11566 ClassReleaseQueue()
11567
11568 Routine Description:
11569
11570 This routine issues an internal device control command
11571 to the port driver to release a frozen queue. The call
11572 is issued asynchronously as ClassReleaseQueue will be invoked
11573 from the IO completion DPC (and will have no context to
11574 wait for a synchronous call to complete).
11575
11576 This routine must be called with the remove lock held.
11577
11578 Arguments:
11579
11580 Fdo - The functional device object for the device with the frozen queue.
11581
11582 Return Value:
11583
11584 None.
11585
11586 --*/
11587 VOID
11588 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11589 ClassReleaseQueue(
11590 _In_ PDEVICE_OBJECT Fdo
11591 )
11592 {
11593 ClasspReleaseQueue(Fdo, NULL);
11594 return;
11595 } // end ClassReleaseQueue()
11596
11597 /*++////////////////////////////////////////////////////////////////////////////
11598
11599 ClasspAllocateReleaseQueueIrp()
11600
11601 Routine Description:
11602
11603 This routine allocates the release queue irp held in classpnp's private
11604 extension. This was added to allow no-memory conditions to be more
11605 survivable.
11606
11607 Return Value:
11608
11609 NT_SUCCESS value.
11610
11611 Notes:
11612
11613 Does not grab the spinlock. Should only be called from StartDevice()
11614 routine. May be called elsewhere for poorly-behaved drivers that cause
11615 the queue to lockup before the device is started. This should *never*
11616 occur, since it's illegal to send a request to a non-started PDO. This
11617 condition is checked for in ClasspReleaseQueue().
11618
11619 --*/
11620 NTSTATUS
11621 ClasspAllocateReleaseQueueIrp(
11622 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
11623 )
11624 {
11625 UCHAR lowerStackSize;
11626
11627 //
11628 // do an initial check w/o the spinlock
11629 //
11630
11631 if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
11632 return STATUS_SUCCESS;
11633 }
11634
11635
11636 lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
11637
11638 //
11639 // don't allocate one if one is in progress! this means whoever called
11640 // this routine didn't check if one was in progress.
11641 //
11642
11643 NT_ASSERT(!(FdoExtension->ReleaseQueueInProgress));
11644
11645 FdoExtension->PrivateFdoData->ReleaseQueueIrp =
11646 ExAllocatePoolWithTag(NonPagedPoolNx,
11647 IoSizeOfIrp(lowerStackSize),
11648 CLASS_TAG_RELEASE_QUEUE
11649 );
11650
11651 if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
11652 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate for "
11653 "release queue irp\n"));
11654 return STATUS_INSUFFICIENT_RESOURCES;
11655 }
11656 IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
11657 IoSizeOfIrp(lowerStackSize),
11658 lowerStackSize);
11659 FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
11660
11661 return STATUS_SUCCESS;
11662 }
11663
11664 /*++////////////////////////////////////////////////////////////////////////////
11665
11666 ClasspAllocatePowerProcessIrp()
11667
11668 Routine Description:
11669
11670 This routine allocates the power process irp.
11671 This routine should be called after PrivateFdoData is allocated.
11672
11673 Return Value:
11674
11675 NTSTATUS value.
11676
11677 Notes:
11678
11679 Should only be called from StartDevice()
11680
11681 --*/
11682 NTSTATUS
11683 ClasspAllocatePowerProcessIrp(
11684 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
11685 )
11686 {
11687 UCHAR stackSize;
11688
11689 NT_ASSERT(FdoExtension->PrivateFdoData != NULL);
11690
11691 stackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1;
11692
11693 FdoExtension->PrivateFdoData->PowerProcessIrp = ExAllocatePoolWithTag(NonPagedPoolNx,
11694 IoSizeOfIrp(stackSize),
11695 CLASS_TAG_POWER
11696 );
11697
11698 if (FdoExtension->PrivateFdoData->PowerProcessIrp == NULL) {
11699
11700 return STATUS_INSUFFICIENT_RESOURCES;
11701 } else {
11702
11703 IoInitializeIrp(FdoExtension->PrivateFdoData->PowerProcessIrp,
11704 IoSizeOfIrp(stackSize),
11705 stackSize);
11706
11707 return STATUS_SUCCESS;
11708 }
11709 }
11710
11711
11712 /*++////////////////////////////////////////////////////////////////////////////
11713
11714 ClasspReleaseQueue()
11715
11716 Routine Description:
11717
11718 This routine issues an internal device control command
11719 to the port driver to release a frozen queue. The call
11720 is issued asynchronously as ClassReleaseQueue will be invoked
11721 from the IO completion DPC (and will have no context to
11722 wait for a synchronous call to complete).
11723
11724 This routine must be called with the remove lock held.
11725
11726 Arguments:
11727
11728 Fdo - The functional device object for the device with the frozen queue.
11729
11730 ReleaseQueueIrp - If this irp is supplied then the test to determine whether
11731 a release queue request is in progress will be ignored.
11732 The irp provided must be the IRP originally allocated
11733 for release queue requests (so this parameter can only
11734 really be provided by the release queue completion
11735 routine.)
11736
11737 Return Value:
11738
11739 None.
11740
11741 --*/
11742 VOID
11743 ClasspReleaseQueue(
11744 IN PDEVICE_OBJECT Fdo,
11745 IN PIRP ReleaseQueueIrp OPTIONAL
11746 )
11747 {
11748 PIO_STACK_LOCATION irpStack;
11749 PIRP irp;
11750 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
11751 PDEVICE_OBJECT lowerDevice;
11752 PSTORAGE_REQUEST_BLOCK_HEADER srb;
11753 KIRQL currentIrql;
11754 ULONG function;
11755
11756 lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
11757
11758 //
11759 // we raise irql seperately so we're not swapped out or suspended
11760 // while holding the release queue irp in this routine. this lets
11761 // us release the spin lock before lowering irql.
11762 //
11763
11764 KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql);
11765
11766 KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
11767
11768 //
11769 // make sure that if they passed us an irp, it matches our allocated irp.
11770 //
11771
11772 NT_ASSERT((ReleaseQueueIrp == NULL) ||
11773 (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
11774
11775 //
11776 // ASSERT that we've already allocated this. (should not occur)
11777 // try to allocate it anyways, then finally bugcheck if
11778 // there's still no memory...
11779 //
11780
11781 NT_ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
11782 if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
11783 ClasspAllocateReleaseQueueIrp(fdoExtension);
11784 }
11785 if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
11786 KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
11787 }
11788
11789 if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
11790
11791 //
11792 // Someone is already using the irp - just set the flag to indicate that
11793 // we need to release the queue again.
11794 //
11795
11796 fdoExtension->ReleaseQueueNeeded = TRUE;
11797 KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
11798 KeLowerIrql(currentIrql);
11799 return;
11800
11801 }
11802
11803 //
11804 // Mark that there is a release queue in progress and drop the spinlock.
11805 //
11806
11807 fdoExtension->ReleaseQueueInProgress = TRUE;
11808 if (ReleaseQueueIrp) {
11809 irp = ReleaseQueueIrp;
11810 } else {
11811 irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
11812 }
11813
11814 if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
11815 srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->ReleaseQueueSrb.SrbEx);
11816 } else {
11817 srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->ReleaseQueueSrb);
11818 }
11819
11820 KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
11821
11822 NT_ASSERT(irp != NULL);
11823
11824 irpStack = IoGetNextIrpStackLocation(irp);
11825
11826 irpStack->MajorFunction = IRP_MJ_SCSI;
11827
11828 SrbSetOriginalRequest(srb, irp);
11829
11830 //
11831 // Store the SRB address in next stack for port driver.
11832 //
11833
11834 irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb;
11835
11836 //
11837 // If this device is removable then flush the queue. This will also
11838 // release it.
11839 //
11840
11841 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
11842 function = SRB_FUNCTION_FLUSH_QUEUE;
11843 }
11844 else {
11845 function = SRB_FUNCTION_RELEASE_QUEUE;
11846 }
11847
11848 if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
11849 ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = function;
11850 } else {
11851 srb->Function = (UCHAR)function;
11852 }
11853
11854 ClassAcquireRemoveLock(Fdo, irp);
11855
11856 IoSetCompletionRoutine(irp,
11857 ClassReleaseQueueCompletion,
11858 Fdo,
11859 TRUE,
11860 TRUE,
11861 TRUE);
11862
11863 IoCallDriver(lowerDevice, irp);
11864
11865 KeLowerIrql(currentIrql);
11866
11867 return;
11868
11869 } // end ClassReleaseQueue()
11870
11871 /*++////////////////////////////////////////////////////////////////////////////
11872
11873 ClassReleaseQueueCompletion()
11874
11875 Routine Description:
11876
11877 This routine is called when an asynchronous I/O request
11878 which was issused by the class driver completes. Examples of such requests
11879 are release queue or START UNIT. This routine releases the queue if
11880 necessary. It then frees the context and the IRP.
11881
11882 Arguments:
11883
11884 DeviceObject - The device object for the logical unit; however since this
11885 is the top stack location the value is NULL.
11886
11887 Irp - Supplies a pointer to the Irp to be processed.
11888
11889 Context - Supplies the context to be used to process this request.
11890
11891 Return Value:
11892
11893 None.
11894
11895 --*/
11896 NTSTATUS
11897 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11898 ClassReleaseQueueCompletion(
11899 PDEVICE_OBJECT DeviceObject,
11900 PIRP Irp,
11901 PVOID Context
11902 )
11903 {
11904 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
11905 KIRQL oldIrql;
11906
11907 BOOLEAN releaseQueueNeeded;
11908
11909 if (Context == NULL) {
11910 NT_ASSERT(Context != NULL);
11911 return STATUS_INVALID_PARAMETER;
11912 }
11913
11914 DeviceObject = Context;
11915
11916 fdoExtension = DeviceObject->DeviceExtension;
11917
11918 ClassReleaseRemoveLock(DeviceObject, Irp);
11919
11920 //
11921 // Grab the spinlock and clear the release queue in progress flag so others
11922 // can run. Save (and clear) the state of the release queue needed flag
11923 // so that we can issue a new release queue outside the spinlock.
11924 //
11925
11926 KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
11927
11928 releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
11929
11930 fdoExtension->ReleaseQueueNeeded = FALSE;
11931 fdoExtension->ReleaseQueueInProgress = FALSE;
11932
11933 KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
11934
11935 //
11936 // If we need a release queue then issue one now. Another processor may
11937 // have already started one in which case we'll try to issue this one after
11938 // it is done - but we should never recurse more than one deep.
11939 //
11940
11941 if(releaseQueueNeeded) {
11942 ClasspReleaseQueue(DeviceObject, Irp);
11943 }
11944
11945 //
11946 // Indicate the I/O system should stop processing the Irp completion.
11947 //
11948
11949 return STATUS_MORE_PROCESSING_REQUIRED;
11950
11951 } // ClassAsynchronousCompletion()
11952
11953
11954 /*++////////////////////////////////////////////////////////////////////////////
11955
11956 ClassAcquireChildLock()
11957
11958 Routine Description:
11959
11960 This routine acquires the lock protecting children PDOs. It may be
11961 acquired recursively by the same thread, but must be release by the
11962 thread once for each acquisition.
11963
11964 Arguments:
11965
11966 FdoExtension - the device whose child list is protected.
11967
11968 Return Value:
11969
11970 None
11971
11972 --*/
11973 _IRQL_requires_max_(PASSIVE_LEVEL)
11974 VOID
11975 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11976 ClassAcquireChildLock(
11977 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
11978 )
11979 {
11980 PAGED_CODE();
11981
11982 if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
11983 (VOID)KeWaitForSingleObject(&FdoExtension->ChildLock,
11984 Executive, KernelMode,
11985 FALSE, NULL);
11986
11987 NT_ASSERT(FdoExtension->ChildLockOwner == NULL);
11988 NT_ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
11989
11990 FdoExtension->ChildLockOwner = KeGetCurrentThread();
11991 } else {
11992 NT_ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
11993 }
11994
11995 FdoExtension->ChildLockAcquisitionCount++;
11996 return;
11997 }
11998
11999 /*++////////////////////////////////////////////////////////////////////////////
12000
12001 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
12002
12003 Routine Description:
12004
12005 This routine releases the lock protecting children PDOs. It must be
12006 called once for each time ClassAcquireChildLock was called.
12007
12008 Arguments:
12009
12010 FdoExtension - the device whose child list is protected
12011
12012 Return Value:
12013
12014 None.
12015
12016 --*/
12017 VOID
12018 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
12019 ClassReleaseChildLock(
12020 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
12021 )
12022 {
12023 NT_ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
12024 NT_ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
12025
12026 FdoExtension->ChildLockAcquisitionCount -= 1;
12027
12028 if(FdoExtension->ChildLockAcquisitionCount == 0) {
12029 FdoExtension->ChildLockOwner = NULL;
12030 KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
12031 }
12032
12033 return;
12034 } // end ClassReleaseChildLock(
12035
12036 /*++////////////////////////////////////////////////////////////////////////////
12037
12038 ClassAddChild()
12039
12040 Routine Description:
12041
12042 This routine will insert a new child into the head of the child list.
12043
12044 Arguments:
12045
12046 Parent - the child's parent (contains the head of the list)
12047 Child - the child to be inserted.
12048 AcquireLock - whether the child lock should be acquired (TRUE) or whether
12049 it's already been acquired by or on behalf of the caller
12050 (FALSE).
12051
12052 Return Value:
12053
12054 None.
12055
12056 --*/
12057 VOID
12058 ClassAddChild(
12059 _In_ PFUNCTIONAL_DEVICE_EXTENSION Parent,
12060 _In_ PPHYSICAL_DEVICE_EXTENSION Child,
12061 _In_ BOOLEAN AcquireLock
12062 )
12063 {
12064 if(AcquireLock) {
12065 ClassAcquireChildLock(Parent);
12066 }
12067
12068 #if DBG
12069 //
12070 // Make sure this child's not already in the list.
12071 //
12072 {
12073 PPHYSICAL_DEVICE_EXTENSION testChild;
12074
12075 for (testChild = Parent->CommonExtension.ChildList;
12076 testChild != NULL;
12077 testChild = testChild->CommonExtension.ChildList) {
12078
12079 NT_ASSERT(testChild != Child);
12080 }
12081 }
12082 #endif
12083
12084 Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
12085 Parent->CommonExtension.ChildList = Child;
12086
12087 if(AcquireLock) {
12088 ClassReleaseChildLock(Parent);
12089 }
12090 return;
12091 } // end ClassAddChild()
12092
12093 /*++////////////////////////////////////////////////////////////////////////////
12094
12095 ClassRemoveChild()
12096
12097 Routine Description:
12098
12099 This routine will remove a child from the child list.
12100
12101 Arguments:
12102
12103 Parent - the parent to be removed from.
12104
12105 Child - the child to be removed or NULL if the first child should be
12106 removed.
12107
12108 AcquireLock - whether the child lock should be acquired (TRUE) or whether
12109 it's already been acquired by or on behalf of the caller
12110 (FALSE).
12111
12112 Return Value:
12113
12114 A pointer to the child which was removed or NULL if no such child could
12115 be found in the list (or if Child was NULL but the list is empty).
12116
12117 --*/
12118 PPHYSICAL_DEVICE_EXTENSION
12119 ClassRemoveChild(
12120 IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
12121 IN PPHYSICAL_DEVICE_EXTENSION Child,
12122 IN BOOLEAN AcquireLock
12123 )
12124 {
12125 if(AcquireLock) {
12126 ClassAcquireChildLock(Parent);
12127 }
12128
12129 TRY {
12130 PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
12131
12132 //
12133 // If the list is empty then bail out now.
12134 //
12135
12136 if(Parent->CommonExtension.ChildList == NULL) {
12137 Child = NULL;
12138 LEAVE;
12139 }
12140
12141 //
12142 // If the caller specified a child then find the child object before
12143 // it. If none was specified then the FDO is the child object before
12144 // the one we want to remove.
12145 //
12146
12147 if(Child != NULL) {
12148
12149 //
12150 // Scan through the child list to find the entry which points to
12151 // this one.
12152 //
12153
12154 do {
12155 NT_ASSERT(previousChild != &Child->CommonExtension);
12156
12157 if(previousChild->ChildList == Child) {
12158 break;
12159 }
12160
12161 previousChild = &previousChild->ChildList->CommonExtension;
12162 } while(previousChild != NULL);
12163
12164 if(previousChild == NULL) {
12165 Child = NULL;
12166 LEAVE;
12167 }
12168 }
12169
12170 //
12171 // Save the next child away then unlink it from the list.
12172 //
12173
12174 Child = previousChild->ChildList;
12175 previousChild->ChildList = Child->CommonExtension.ChildList;
12176 Child->CommonExtension.ChildList = NULL;
12177
12178 } FINALLY {
12179 if(AcquireLock) {
12180 ClassReleaseChildLock(Parent);
12181 }
12182 }
12183 return Child;
12184 } // end ClassRemoveChild()
12185
12186
12187 /*++
12188
12189 ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
12190
12191 --*/
12192 VOID
12193 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
12194 ClasspRetryRequestDpc(
12195 IN PKDPC Dpc,
12196 IN PVOID DeferredContext,
12197 IN PVOID Arg1,
12198 IN PVOID Arg2
12199 )
12200 {
12201 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
12202 PCOMMON_DEVICE_EXTENSION commonExtension;
12203 PCLASS_PRIVATE_FDO_DATA fdoData;
12204 PCLASS_RETRY_INFO retryList;
12205 KIRQL irql;
12206 PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)DeferredContext;
12207
12208 UNREFERENCED_PARAMETER(Dpc);
12209 UNREFERENCED_PARAMETER(Arg1);
12210 UNREFERENCED_PARAMETER(Arg2);
12211
12212 if (DeferredContext == NULL) {
12213 NT_ASSERT(DeferredContext != NULL);
12214 return;
12215 }
12216
12217 commonExtension = DeviceObject->DeviceExtension;
12218 NT_ASSERT(commonExtension->IsFdo);
12219 fdoExtension = DeviceObject->DeviceExtension;
12220 fdoData = fdoExtension->PrivateFdoData;
12221
12222
12223 KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
12224 {
12225 LARGE_INTEGER now;
12226 KeQueryTickCount(&now);
12227
12228 //
12229 // if CurrentTick is less than now
12230 // fire another DPC
12231 // else
12232 // retry entire list
12233 // endif
12234 //
12235
12236 if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
12237
12238 ClasspRetryDpcTimer(fdoData);
12239 retryList = NULL;
12240
12241 } else {
12242
12243 retryList = fdoData->Retry.ListHead;
12244 fdoData->Retry.ListHead = NULL;
12245 fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
12246 fdoData->Retry.Tick.QuadPart = (LONGLONG)0;
12247
12248 }
12249 }
12250 KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
12251
12252 while (retryList != NULL) {
12253
12254 PIRP irp;
12255
12256
12257 irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
12258 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: -- %p\n", irp));
12259 retryList = retryList->Next;
12260 #if DBG
12261 irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
12262 irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
12263 irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
12264 irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
12265 #endif
12266
12267
12268 if (NO_REMOVE == InterlockedCompareExchange((volatile LONG *)&commonExtension->IsRemoved, REMOVE_PENDING, REMOVE_PENDING)) {
12269
12270 IoCallDriver(commonExtension->LowerDeviceObject, irp);
12271
12272 } else {
12273
12274 PIO_STACK_LOCATION irpStack;
12275
12276 //
12277 // Ensure that we don't skip a completion routine (equivalent of sending down a request
12278 // to a device after it has received a remove and it completes the request. We need to
12279 // mimic that behavior here).
12280 //
12281 IoSetNextIrpStackLocation(irp);
12282
12283 irpStack = IoGetCurrentIrpStackLocation(irp);
12284
12285 if (irpStack->MajorFunction == IRP_MJ_SCSI) {
12286
12287 PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
12288
12289 if (srb) {
12290 srb->SrbStatus = SRB_STATUS_NO_DEVICE;
12291 }
12292 }
12293
12294 //
12295 // Ensure that no retries will take place. This takes care of requests that are either
12296 // not IRP_MJ_SCSI or for ones where the SRB was passed in to the completion routine
12297 // as a context as opposed to an argument on the IRP stack location.
12298 //
12299 irpStack->Parameters.Others.Argument4 = (PVOID)0;
12300
12301 irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
12302 irp->IoStatus.Information = 0;
12303 IoCompleteRequest(irp, IO_NO_INCREMENT);
12304 }
12305
12306 }
12307 return;
12308
12309 } // end ClasspRetryRequestDpc()
12310
12311 /*++
12312
12313 ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
12314
12315 --*/
12316 VOID
12317 ClassRetryRequest(
12318 IN PDEVICE_OBJECT SelfDeviceObject,
12319 IN PIRP Irp,
12320 _In_ _In_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS) // 100 seconds; already an assert on this...
12321 IN LONGLONG TimeDelta100ns // in 100ns units
12322 )
12323 {
12324 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
12325 PCLASS_PRIVATE_FDO_DATA fdoData;
12326 PCLASS_RETRY_INFO retryInfo;
12327 LARGE_INTEGER delta;
12328 KIRQL irql;
12329
12330 //
12331 // this checks we aren't destroying irps
12332 //
12333 NT_ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
12334
12335 fdoExtension = SelfDeviceObject->DeviceExtension;
12336
12337 if (!fdoExtension->CommonExtension.IsFdo) {
12338
12339 //
12340 // this debug print/assertion should ALWAYS be investigated.
12341 // ClassRetryRequest can currently only be used by FDO's
12342 //
12343
12344 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
12345 NT_ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
12346 return;
12347
12348 }
12349
12350 fdoData = fdoExtension->PrivateFdoData;
12351
12352 if (TimeDelta100ns < 0) {
12353 NT_ASSERT(!"ClassRetryRequest - must use positive delay");
12354 TimeDelta100ns *= -1;
12355 }
12356
12357 /*
12358 * We are going to queue the irp and send it down in a timer DPC.
12359 * This means that we may be causing the irp to complete on a different thread than the issuing thread.
12360 * So mark the irp pending.
12361 */
12362 IoMarkIrpPending(Irp);
12363
12364 //
12365 // prepare what we can out of the loop
12366 //
12367
12368 retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
12369 RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
12370
12371 delta.QuadPart = (TimeDelta100ns / fdoData->Retry.Granularity);
12372 if (TimeDelta100ns % fdoData->Retry.Granularity) {
12373 delta.QuadPart ++; // round up to next tick
12374 }
12375 if (delta.QuadPart == (LONGLONG)0) {
12376 delta.QuadPart = MINIMUM_RETRY_UNITS;
12377 }
12378
12379 //
12380 // now determine if we should fire another DPC or not
12381 //
12382
12383 KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
12384
12385 //
12386 // always add request to the list
12387 //
12388
12389 retryInfo->Next = fdoData->Retry.ListHead;
12390 fdoData->Retry.ListHead = retryInfo;
12391
12392 if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
12393
12394 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: +++ %p\n", Irp));
12395
12396 //
12397 // must be exactly one item on list
12398 //
12399
12400 NT_ASSERT(fdoData->Retry.ListHead != NULL);
12401 NT_ASSERT(fdoData->Retry.ListHead->Next == NULL);
12402
12403 //
12404 // if currentDelta is zero, always fire a DPC
12405 //
12406
12407 KeQueryTickCount(&fdoData->Retry.Tick);
12408 fdoData->Retry.Tick.QuadPart += delta.QuadPart;
12409 fdoData->Retry.Delta.QuadPart = delta.QuadPart;
12410 ClasspRetryDpcTimer(fdoData);
12411
12412 } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
12413
12414 //
12415 // if delta is greater than the list's current delta,
12416 // increase the DPC handling time by difference
12417 // and update the delta to new larger value
12418 // allow the DPC to re-fire itself if needed
12419 //
12420
12421 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: ++ %p\n", Irp));
12422
12423 //
12424 // must be at least two items on list
12425 //
12426
12427 NT_ASSERT(fdoData->Retry.ListHead != NULL);
12428 NT_ASSERT(fdoData->Retry.ListHead->Next != NULL);
12429
12430 fdoData->Retry.Tick.QuadPart -= fdoData->Retry.Delta.QuadPart;
12431 fdoData->Retry.Tick.QuadPart += delta.QuadPart;
12432
12433 fdoData->Retry.Delta.QuadPart = delta.QuadPart;
12434
12435 } else {
12436
12437 //
12438 // just inserting it on the list was enough
12439 //
12440
12441 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: ++ %p\n", Irp));
12442
12443 }
12444
12445
12446 KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
12447
12448
12449 } // end ClassRetryRequest()
12450
12451 /*++
12452
12453 ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
12454
12455 --*/
12456 VOID
12457 ClasspRetryDpcTimer(
12458 IN PCLASS_PRIVATE_FDO_DATA FdoData
12459 )
12460 {
12461 LARGE_INTEGER fire;
12462
12463 NT_ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
12464 NT_ASSERT(FdoData->Retry.ListHead != NULL); // never fire an empty list
12465
12466 //
12467 // fire == (CurrentTick - now) * (100ns per tick)
12468 //
12469 // NOTE: Overflow is nearly impossible and is ignored here
12470 //
12471
12472 KeQueryTickCount(&fire);
12473 fire.QuadPart = FdoData->Retry.Tick.QuadPart - fire.QuadPart;
12474 fire.QuadPart *= FdoData->Retry.Granularity;
12475
12476 //
12477 // fire is now multiples of 100ns until should fire the timer.
12478 // if timer should already have expired, or would fire too quickly,
12479 // fire it in some arbitrary number of ticks to prevent infinitely
12480 // recursing.
12481 //
12482
12483 if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
12484 fire.QuadPart = MINIMUM_RETRY_UNITS;
12485 }
12486
12487 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
12488 "ClassRetry: ======= %I64x ticks\n",
12489 fire.QuadPart));
12490
12491 //
12492 // must use negative to specify relative time to fire
12493 //
12494
12495 fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
12496
12497 //
12498 // set the timer, since this is the first addition
12499 //
12500
12501 KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
12502
12503 return;
12504 } // end ClasspRetryDpcTimer()
12505
12506 NTSTATUS
12507 ClasspInitializeHotplugInfo(
12508 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
12509 )
12510 {
12511 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
12512 DEVICE_REMOVAL_POLICY deviceRemovalPolicy = 0;
12513 NTSTATUS status;
12514 ULONG resultLength = 0;
12515 ULONG writeCacheOverride;
12516
12517 PAGED_CODE();
12518
12519 //
12520 // start with some default settings
12521 //
12522 RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
12523
12524 //
12525 // set the size (aka version)
12526 //
12527
12528 fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
12529
12530 //
12531 // set if the device has removable media
12532 //
12533
12534 if (FdoExtension->DeviceDescriptor->RemovableMedia) {
12535 fdoData->HotplugInfo.MediaRemovable = TRUE;
12536 } else {
12537 fdoData->HotplugInfo.MediaRemovable = FALSE;
12538 }
12539
12540 //
12541 // this refers to devices which, for reasons not yet understood,
12542 // do not fail PREVENT_MEDIA_REMOVAL requests even though they
12543 // have no way to lock the media into the drive. this allows
12544 // the filesystems to turn off delayed-write caching for these
12545 // devices as well.
12546 //
12547
12548 if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
12549 FDO_HACK_CANNOT_LOCK_MEDIA)) {
12550 fdoData->HotplugInfo.MediaHotplug = TRUE;
12551 } else {
12552 fdoData->HotplugInfo.MediaHotplug = FALSE;
12553 }
12554
12555 //
12556 // Query the default removal policy from the kernel
12557 //
12558
12559 status = IoGetDeviceProperty(FdoExtension->LowerPdo,
12560 DevicePropertyRemovalPolicy,
12561 sizeof(DEVICE_REMOVAL_POLICY),
12562 (PVOID)&deviceRemovalPolicy,
12563 &resultLength);
12564 if (!NT_SUCCESS(status)) {
12565 return status;
12566 }
12567
12568 if (resultLength != sizeof(DEVICE_REMOVAL_POLICY)) {
12569 return STATUS_UNSUCCESSFUL;
12570 }
12571
12572 //
12573 // Look into the registry to see if the user has chosen
12574 // to override the default setting for the removal policy.
12575 // User can override only if the default removal policy is
12576 // orderly or suprise removal.
12577
12578 if ((deviceRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
12579 (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)) {
12580
12581 DEVICE_REMOVAL_POLICY userRemovalPolicy = 0;
12582
12583 ClassGetDeviceParameter(FdoExtension,
12584 CLASSP_REG_SUBKEY_NAME,
12585 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
12586 (PULONG)&userRemovalPolicy);
12587
12588 //
12589 // Validate the override value and use it only if it is an
12590 // allowed value.
12591 //
12592 if ((userRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
12593 (userRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)) {
12594 deviceRemovalPolicy = userRemovalPolicy;
12595 }
12596 }
12597
12598 //
12599 // use this info to set the DeviceHotplug setting
12600 // don't rely on DeviceCapabilities, since it can't properly
12601 // determine device relations, etc. let the kernel figure this
12602 // stuff out instead.
12603 //
12604
12605 if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
12606 fdoData->HotplugInfo.DeviceHotplug = TRUE;
12607 } else {
12608 fdoData->HotplugInfo.DeviceHotplug = FALSE;
12609 }
12610
12611 //
12612 // this refers to the *filesystem* caching, but has to be included
12613 // here since it's a per-device setting. this may change to be
12614 // stored by the system in the future.
12615 //
12616
12617 writeCacheOverride = FALSE;
12618 ClassGetDeviceParameter(FdoExtension,
12619 CLASSP_REG_SUBKEY_NAME,
12620 CLASSP_REG_WRITE_CACHE_VALUE_NAME,
12621 &writeCacheOverride);
12622
12623 if (writeCacheOverride) {
12624 fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
12625 } else {
12626 fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
12627 }
12628
12629 return STATUS_SUCCESS;
12630 }
12631
12632 VOID
12633 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
12634 ClasspScanForClassHacks(
12635 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
12636 IN ULONG_PTR Data
12637 )
12638 {
12639 PAGED_CODE();
12640
12641 //
12642 // remove invalid flags and save
12643 //
12644
12645 CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
12646 SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
12647 return;
12648 }
12649
12650 VOID
12651 ClasspScanForSpecialInRegistry(
12652 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
12653 )
12654 {
12655 HANDLE deviceParameterHandle; // device instance key
12656 HANDLE classParameterHandle; // classpnp subkey
12657 OBJECT_ATTRIBUTES objectAttributes = {0};
12658 UNICODE_STRING subkeyName;
12659 NTSTATUS status;
12660
12661 //
12662 // seeded in the ENUM tree by ClassInstaller
12663 //
12664 ULONG deviceHacks;
12665 RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0}; // null terminated array
12666
12667 PAGED_CODE();
12668
12669 deviceParameterHandle = NULL;
12670 classParameterHandle = NULL;
12671 deviceHacks = 0;
12672
12673 status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
12674 PLUGPLAY_REGKEY_DEVICE,
12675 KEY_WRITE,
12676 &deviceParameterHandle
12677 );
12678
12679 if (!NT_SUCCESS(status)) {
12680 goto cleanupScanForSpecial;
12681 }
12682
12683 RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
12684 InitializeObjectAttributes(&objectAttributes,
12685 &subkeyName,
12686 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
12687 deviceParameterHandle,
12688 NULL
12689 );
12690
12691 status = ZwOpenKey( &classParameterHandle,
12692 KEY_READ,
12693 &objectAttributes
12694 );
12695
12696 if (!NT_SUCCESS(status)) {
12697 goto cleanupScanForSpecial;
12698 }
12699
12700 //
12701 // Setup the structure to read
12702 //
12703
12704 queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
12705 queryTable[0].Name = CLASSP_REG_HACK_VALUE_NAME;
12706 queryTable[0].EntryContext = &deviceHacks;
12707 queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD;
12708 queryTable[0].DefaultData = &deviceHacks;
12709 queryTable[0].DefaultLength = 0;
12710
12711 //
12712 // read values
12713 //
12714
12715 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
12716 (PWSTR)classParameterHandle,
12717 &queryTable[0],
12718 NULL,
12719 NULL
12720 );
12721 if (!NT_SUCCESS(status)) {
12722 goto cleanupScanForSpecial;
12723 }
12724
12725 //
12726 // remove unknown values and save...
12727 //
12728
12729 CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
12730 SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
12731
12732
12733 cleanupScanForSpecial:
12734
12735 if (deviceParameterHandle) {
12736 ZwClose(deviceParameterHandle);
12737 }
12738
12739 if (classParameterHandle) {
12740 ZwClose(classParameterHandle);
12741 }
12742
12743 //
12744 // we should modify the system hive to include another key for us to grab
12745 // settings from. in this case: Classpnp\HackFlags
12746 //
12747 // the use of a DWORD value for the HackFlags allows 32 hacks w/o
12748 // significant use of the registry, and also reduces OEM exposure.
12749 //
12750 // definition of bit flags:
12751 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
12752 // cannot actually prevent removal.
12753 // 0x00000002 -- Device hard-hangs or times out for GESN requests.
12754 // 0xfffffffc -- Currently reserved, may be used later.
12755 //
12756
12757 return;
12758 }
12759
12760 /*++////////////////////////////////////////////////////////////////////////////
12761
12762 ClasspUpdateDiskProperties()
12763
12764 Routine Description:
12765
12766 This routine will send IOCTL_DISK_UPDATE_PROPERTIES to top of stack - Partition Manager
12767 to invalidate the cached geometry.
12768
12769 Arguments:
12770
12771 Fdo - The device object whose capacity needs to be verified.
12772
12773 Return Value:
12774
12775 NTSTATUS
12776
12777 --*/
12778
12779 VOID
12780 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
12781 ClasspUpdateDiskProperties(
12782 IN PDEVICE_OBJECT Fdo,
12783 IN PVOID Context
12784 )
12785 {
12786 PDEVICE_OBJECT topOfStack;
12787 IO_STATUS_BLOCK ioStatus;
12788 NTSTATUS status = STATUS_SUCCESS;
12789 KEVENT event;
12790 PIRP irp = NULL;
12791 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
12792 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
12793 PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;
12794
12795 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
12796
12797 topOfStack = IoGetAttachedDeviceReference(Fdo);
12798
12799 //
12800 // Send down irp to update properties
12801 //
12802
12803 irp = IoBuildDeviceIoControlRequest(
12804 IOCTL_DISK_UPDATE_PROPERTIES,
12805 topOfStack,
12806 NULL,
12807 0,
12808 NULL,
12809 0,
12810 FALSE,
12811 &event,
12812 &ioStatus);
12813
12814 if (irp != NULL) {
12815
12816
12817 status = IoCallDriver(topOfStack, irp);
12818 if (status == STATUS_PENDING) {
12819 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
12820 status = ioStatus.Status;
12821 }
12822
12823 } else {
12824 status = STATUS_INSUFFICIENT_RESOURCES;
12825 }
12826
12827 InterlockedExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 0);
12828
12829 if (!NT_SUCCESS(status)) {
12830 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClasspUpdateDiskProperties: Disk property update for fdo %p failed with status 0x%X.\n", Fdo, status));
12831 } else {
12832 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspUpdateDiskProperties: Drive capacity has changed for %p.\n", Fdo));
12833 }
12834 ObDereferenceObject(topOfStack);
12835
12836 if (WorkItem != NULL) {
12837 IoFreeWorkItem(WorkItem);
12838 }
12839
12840 return;
12841 }
12842
12843 BOOLEAN
12844 InterpretSenseInfoWithoutHistory(
12845 _In_ PDEVICE_OBJECT Fdo,
12846 _In_opt_ PIRP OriginalRequest,
12847 _In_ PSCSI_REQUEST_BLOCK Srb,
12848 UCHAR MajorFunctionCode,
12849 ULONG IoDeviceCode,
12850 ULONG PreviousRetryCount,
12851 _Out_ NTSTATUS * Status,
12852 _Out_opt_ _Deref_out_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
12853 LONGLONG * RetryIn100nsUnits
12854 )
12855 {
12856 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
12857 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
12858 LONGLONG tmpRetry = 0;
12859 BOOLEAN retry = FALSE;
12860
12861 if (fdoData->InterpretSenseInfo != NULL)
12862 {
12863 SCSI_REQUEST_BLOCK tempSrb = {0};
12864 PSCSI_REQUEST_BLOCK srbPtr = Srb;
12865
12866 // SAL annotations and ClassInitializeEx() both validate this
12867 NT_ASSERT(fdoData->InterpretSenseInfo->Interpret != NULL);
12868
12869 //
12870 // If class driver does not support extended SRB and this is
12871 // an extended SRB, convert to legacy SRB and pass to class
12872 // driver.
12873 //
12874 if ((Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
12875 ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
12876 CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
12877 ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Srb);
12878 srbPtr = &tempSrb;
12879 }
12880
12881 retry = fdoData->InterpretSenseInfo->Interpret(Fdo,
12882 OriginalRequest,
12883 srbPtr,
12884 MajorFunctionCode,
12885 IoDeviceCode,
12886 PreviousRetryCount,
12887 NULL,
12888 Status,
12889 &tmpRetry);
12890 }
12891 else
12892 {
12893 ULONG seconds = 0;
12894
12895 retry = ClassInterpretSenseInfo(Fdo,
12896 Srb,
12897 MajorFunctionCode,
12898 IoDeviceCode,
12899 PreviousRetryCount,
12900 Status,
12901 &seconds);
12902 tmpRetry = ((LONGLONG)seconds) * 1000 * 1000 * 10;
12903 }
12904
12905
12906 if (RetryIn100nsUnits != NULL)
12907 {
12908 *RetryIn100nsUnits = tmpRetry;
12909 }
12910 return retry;
12911 }
12912
12913 VOID
12914 ClasspGetInquiryVpdSupportInfo(
12915 _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
12916 )
12917 {
12918 NTSTATUS status;
12919 PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
12920 SCSI_REQUEST_BLOCK srb = {0};
12921 PCDB cdb;
12922 PVPD_SUPPORTED_PAGES_PAGE supportedPages = NULL;
12923 UCHAR bufferLength = VPD_MAX_BUFFER_SIZE;
12924 ULONG allocationBufferLength = bufferLength;
12925 UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
12926 PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
12927
12928 #if defined(_ARM_) || defined(_ARM64_)
12929 //
12930 // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
12931 // based platforms. We are taking the conservative approach here.
12932 //
12933 allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
12934 supportedPages = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
12935 allocationBufferLength,
12936 '3CcS'
12937 );
12938
12939 #else
12940 supportedPages = ExAllocatePoolWithTag(NonPagedPoolNx,
12941 bufferLength,
12942 '3CcS'
12943 );
12944 #endif
12945
12946 if (supportedPages == NULL) {
12947 // memory allocation failure.
12948 return;
12949 }
12950
12951 RtlZeroMemory(supportedPages, allocationBufferLength);
12952
12953 // prepare the Srb
12954 if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
12955
12956 #ifdef _MSC_VER
12957 #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
12958 #endif
12959 status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbExBuffer,
12960 STORAGE_ADDRESS_TYPE_BTL8,
12961 sizeof(srbExBuffer),
12962 1,
12963 SrbExDataTypeScsiCdb16);
12964
12965 if (!NT_SUCCESS(status)) {
12966 //
12967 // Should not happen. Revert to legacy SRB.
12968 NT_ASSERT(FALSE);
12969 srb.Length = SCSI_REQUEST_BLOCK_SIZE;
12970 srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
12971 srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&srb;
12972 } else {
12973 ((PSTORAGE_REQUEST_BLOCK)srbExBuffer)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
12974 srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)srbExBuffer;
12975 }
12976
12977 } else {
12978 srb.Length = SCSI_REQUEST_BLOCK_SIZE;
12979 srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
12980 srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&srb;
12981 }
12982
12983 SrbSetTimeOutValue(srbHeader, FdoExtension->TimeOutValue);
12984 SrbSetRequestTag(srbHeader, SP_UNTAGGED);
12985 SrbSetRequestAttribute(srbHeader, SRB_SIMPLE_TAG_REQUEST);
12986 SrbAssignSrbFlags(srbHeader, FdoExtension->SrbFlags);
12987 SrbSetCdbLength(srbHeader, 6);
12988
12989 cdb = SrbGetCdb(srbHeader);
12990 cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
12991 cdb->CDB6INQUIRY3.EnableVitalProductData = 1; //EVPD bit
12992 cdb->CDB6INQUIRY3.PageCode = VPD_SUPPORTED_PAGES;
12993 cdb->CDB6INQUIRY3.AllocationLength = bufferLength; //AllocationLength field in CDB6INQUIRY3 is only one byte.
12994
12995 status = ClassSendSrbSynchronous(commonExtension->DeviceObject,
12996 (PSCSI_REQUEST_BLOCK)srbHeader,
12997 supportedPages,
12998 allocationBufferLength,
12999 FALSE);
13000
13001 //
13002 // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
13003 // buffer was larger than necessary.
13004 //
13005 if (status == STATUS_DATA_OVERRUN && SrbGetDataTransferLength(srbHeader) < bufferLength)
13006 {
13007 status = STATUS_SUCCESS;
13008 }
13009
13010 if ( NT_SUCCESS(status) &&
13011 (supportedPages->PageLength > 0) &&
13012 (supportedPages->SupportedPageList[0] > 0) ) {
13013 // ataport treats all INQUIRY command as standard INQUIRY command, thus fills invalid info
13014 // If VPD INQUIRY is supported, the first page reported (additional length field for standard INQUIRY data) should be '00'
13015 status = STATUS_NOT_SUPPORTED;
13016 }
13017
13018 if (NT_SUCCESS(status)) {
13019 int i;
13020
13021 for (i = 0; i < supportedPages->PageLength; i++) {
13022 if ( (i > 0) && (supportedPages->SupportedPageList[i] <= supportedPages->SupportedPageList[i - 1]) ) {
13023 // shall be in ascending order beginning with page code 00h.
13024 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
13025 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits = FALSE;
13026 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics = FALSE;
13027 FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning = FALSE;
13028
13029
13030 break;
13031 }
13032 switch (supportedPages->SupportedPageList[i]) {
13033 case VPD_THIRD_PARTY_COPY:
13034 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = TRUE;
13035 break;
13036
13037 case VPD_BLOCK_LIMITS:
13038 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits = TRUE;
13039 break;
13040
13041 case VPD_BLOCK_DEVICE_CHARACTERISTICS:
13042 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics = TRUE;
13043 break;
13044
13045 case VPD_LOGICAL_BLOCK_PROVISIONING:
13046 FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning = TRUE;
13047 break;
13048
13049 }
13050 }
13051 }
13052
13053 ExFreePool(supportedPages);
13054
13055 return;
13056 }
13057
13058 //
13059 // ClasspGetLBProvisioningInfo
13060 //
13061 // Description: This function does the work to get Logical Block Provisioning
13062 // (LBP) info for a given LUN, including UNMAP support and parameters. It
13063 // will attempt to get both the Logical Block Provisioning (0xB2) VPD page
13064 // and the Block Limits (0xB0) VPD page and cache the relevant values in
13065 // the given FDO extension.
13066 //
13067 // After calling this function, you can use the ClasspIsThinProvisioned()
13068 // function to determine if the device is thinly provisioned and the
13069 // ClasspSupportsUnmap() function to determine if the device supports the
13070 // UNMAP command.
13071 //
13072 // Arguments:
13073 // - FdoExtension: The FDO extension associated with the LUN for which Thin
13074 // Provisioning info is desired. The Thin Provisioning info is stored
13075 // in the FunctionSupportInfo member of this FDO extension.
13076 //
13077 // Returns:
13078 // - STATUS_INVALID_PARAMETER if the given FDO extension has not been
13079 // allocated properly.
13080 // - STATUS_INSUFFICIENT_RESOURCES if this function was unable to allocate
13081 // an SRB used to get the necessary VPD pages.
13082 // - STATUS_SUCCESS in all other cases. If any of the incidental functions
13083 // don't return STATUS_SUCCESS this function will just assume Thin
13084 // Provisioning is not enabled and return STATUS_SUCCESS.
13085 //
13086 NTSTATUS
13087 ClasspGetLBProvisioningInfo(
13088 _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
13089 )
13090 {
13091 PSCSI_REQUEST_BLOCK srb = NULL;
13092 ULONG srbSize = 0;
13093
13094 //
13095 // Make sure we actually have data structures to work with.
13096 //
13097 if (FdoExtension == NULL ||
13098 FdoExtension->FunctionSupportInfo == NULL) {
13099 return STATUS_INVALID_PARAMETER;
13100 }
13101
13102 //
13103 // Allocate an SRB for querying the device for LBP-related info if either
13104 // the Logical Block Provisioning (0xB2) or Block Limits (0xB0) VPD page
13105 // exists.
13106 //
13107 if ((FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == TRUE) ||
13108 (FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits == TRUE)) {
13109
13110 if ((FdoExtension->AdapterDescriptor != NULL) &&
13111 (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
13112 srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
13113 } else {
13114 srbSize = sizeof(SCSI_REQUEST_BLOCK);
13115 }
13116
13117 srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, '0DcS');
13118
13119 if (srb == NULL) {
13120 return STATUS_INSUFFICIENT_RESOURCES;
13121 }
13122 }
13123
13124 //
13125 // Get the Logical Block Provisioning VPD page (0xB2). This function will
13126 // set some default values if the LBP VPD page is not supported.
13127 //
13128 ClasspDeviceGetLBProvisioningVPDPage(FdoExtension->DeviceObject, srb);
13129
13130
13131 //
13132 // Get the Block Limits VPD page (0xB0), which may override the default
13133 // UNMAP parameter values set above.
13134 //
13135 ClasspDeviceGetBlockLimitsVPDPage(FdoExtension,
13136 srb,
13137 srbSize,
13138 &FdoExtension->FunctionSupportInfo->BlockLimitsData);
13139
13140 FREE_POOL(srb);
13141
13142 return STATUS_SUCCESS;
13143 }
13144
13145
13146
13147 _IRQL_requires_(PASSIVE_LEVEL)
13148 _IRQL_requires_same_
13149 NTSTATUS
13150 ClassDetermineTokenOperationCommandSupport(
13151 _In_ PDEVICE_OBJECT DeviceObject
13152 )
13153
13154 /*++
13155
13156 Routine Description:
13157
13158 This routine determines if Token Operation and Receive ROD Token Information commands
13159 are supported by this device and updates the internal structures to reflect this.
13160 In addition, it also queries the registry to determine the maximum listIdentifier
13161 to use for TokenOperation commands.
13162
13163 This function must be called at IRQL == PASSIVE_LEVEL.
13164
13165 Arguments:
13166
13167 DeviceObject - The device object for which we want to determine command support.
13168
13169 Return Value:
13170
13171 Nothing
13172
13173 --*/
13174
13175 {
13176 NTSTATUS status = STATUS_SUCCESS;
13177
13178 PAGED_CODE();
13179
13180 TracePrint((TRACE_LEVEL_VERBOSE,
13181 TRACE_FLAG_PNP,
13182 "ClassDetermineTokenOperationCommandSupport (%p): Entering function.\n",
13183 DeviceObject));
13184
13185 //
13186 // Send down Inquiry for VPD_THIRD_PARTY_COPY_PAGE and cache away the device parameters
13187 // from WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR.
13188 //
13189 status = ClasspGetBlockDeviceTokenLimitsInfo(DeviceObject);
13190
13191 if (NT_SUCCESS(status)) {
13192
13193 ULONG maxListIdentifier = MaxTokenOperationListIdentifier;
13194
13195 //
13196 // Query the maximum list identifier to use for TokenOperation commands.
13197 //
13198 if (NT_SUCCESS(ClasspGetMaximumTokenListIdentifier(DeviceObject, REG_DISK_CLASS_CONTROL, &maxListIdentifier))) {
13199 if (maxListIdentifier >= MIN_TOKEN_LIST_IDENTIFIERS) {
13200
13201 NT_ASSERT(maxListIdentifier <= MAX_TOKEN_LIST_IDENTIFIERS);
13202 MaxTokenOperationListIdentifier = maxListIdentifier;
13203 }
13204 }
13205
13206 }
13207
13208 TracePrint((TRACE_LEVEL_VERBOSE,
13209 TRACE_FLAG_PNP,
13210 "ClassDetermineTokenOperationCommandSupport (%p): Exiting function with status %x.\n",
13211 DeviceObject,
13212 status));
13213
13214 return status;
13215 }
13216
13217
13218 _IRQL_requires_same_
13219 NTSTATUS
13220 ClasspGetBlockDeviceTokenLimitsInfo(
13221 _Inout_ PDEVICE_OBJECT DeviceObject
13222 )
13223
13224 /*++
13225
13226 Routine Description:
13227
13228 This routine does the work to get the Block Device Token Limits info for a given LUN.
13229 This is done by sending Inquiry for the Third Party Copy VPD page.
13230
13231 Arguments:
13232
13233 DeviceObject - The FDO associated with the LUN for which Block Device Token
13234 limits info is desired.
13235
13236 Return Value:
13237
13238 STATUS_DEVICE_FEATURE_NOT_SUPPORTED if either the Inquiry fails or validations fail.
13239 STATUS_SUCCESS otherwise.
13240
13241 --*/
13242
13243 {
13244 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
13245 NTSTATUS status;
13246 PSCSI_REQUEST_BLOCK srb = NULL;
13247 ULONG srbSize = 0;
13248 USHORT pageLength = 0;
13249 USHORT descriptorLength = 0;
13250 PVOID dataBuffer = NULL;
13251 UCHAR bufferLength = VPD_MAX_BUFFER_SIZE;
13252 ULONG allocationBufferLength = bufferLength;
13253 PCDB cdb;
13254 ULONG dataTransferLength = 0;
13255 PVPD_THIRD_PARTY_COPY_PAGE operatingParameters = NULL;
13256 PWINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR blockDeviceTokenLimits = NULL;
13257
13258 TracePrint((TRACE_LEVEL_VERBOSE,
13259 TRACE_FLAG_PNP,
13260 "ClasspGetBlockDeviceTokenLimitsInfo (%p): Entering function.\n",
13261 DeviceObject));
13262
13263 //
13264 // Allocate an SRB for querying the device for LBP-related info.
13265 //
13266 if ((fdoExtension->AdapterDescriptor != NULL) &&
13267 (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
13268 srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
13269 } else {
13270 srbSize = sizeof(SCSI_REQUEST_BLOCK);
13271 }
13272
13273 srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, CLASSPNP_POOL_TAG_SRB);
13274
13275 if (!srb) {
13276
13277 TracePrint((TRACE_LEVEL_ERROR,
13278 TRACE_FLAG_PNP,
13279 "ClasspGetBlockDeviceTokenLimitsInfo (%p): Couldn't allocate SRB.\n",
13280 DeviceObject));
13281
13282 status = STATUS_INSUFFICIENT_RESOURCES;
13283 goto __ClasspGetBlockDeviceTokenLimitsInfo_Exit;
13284 }
13285
13286 #if defined(_ARM_) || defined(_ARM64_)
13287 //
13288 // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
13289 // based platforms. We are taking the conservative approach here.
13290 //
13291 allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment());
13292 dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, CLASSPNP_POOL_TAG_VPD);
13293 #else
13294 dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CLASSPNP_POOL_TAG_VPD);
13295 #endif
13296
13297 if (!dataBuffer) {
13298
13299 TracePrint((TRACE_LEVEL_ERROR,
13300 TRACE_FLAG_PNP,
13301 "ClasspGetBlockDeviceTokenLimitsInfo (%p): Couldn't allocate dataBuffer.\n",
13302 DeviceObject));
13303
13304 status = STATUS_INSUFFICIENT_RESOURCES;
13305 goto __ClasspGetBlockDeviceTokenLimitsInfo_Exit;
13306 }
13307
13308 operatingParameters = (PVPD_THIRD_PARTY_COPY_PAGE)dataBuffer;
13309
13310 RtlZeroMemory(dataBuffer, allocationBufferLength);
13311
13312 if ((fdoExtension->AdapterDescriptor != NULL) &&
13313 (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
13314 status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srb,
13315 STORAGE_ADDRESS_TYPE_BTL8,
13316 CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
13317 1,
13318 SrbExDataTypeScsiCdb16);
13319 if (NT_SUCCESS(status)) {
13320
13321 ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
13322
13323 } else {
13324
13325 //
13326 // Should not occur. Revert to legacy SRB.
13327 //
13328 NT_ASSERT(FALSE);
13329
13330 TracePrint((TRACE_LEVEL_WARNING,
13331 TRACE_FLAG_PNP,
13332 "ClasspGetBlockDeviceTokenLimitsInfo (%p): Falling back to using SRB (instead of SRB_EX).\n",
13333 DeviceObject));
13334
13335 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
13336 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
13337 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
13338 }
13339 } else {
13340
13341 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
13342 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
13343 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
13344 }
13345
13346 SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
13347 SrbSetRequestTag(srb, SP_UNTAGGED);
13348 SrbSetRequestAttribute(srb, SRB_SIMPLE_TAG_REQUEST);
13349 SrbAssignSrbFlags(srb, fdoExtension->SrbFlags);
13350
13351 SrbSetCdbLength(srb, 6);
13352
13353 cdb = SrbGetCdb(srb);
13354 cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
13355 cdb->CDB6INQUIRY3.EnableVitalProductData = 1;
13356 cdb->CDB6INQUIRY3.PageCode = VPD_THIRD_PARTY_COPY;
13357 cdb->CDB6INQUIRY3.AllocationLength = bufferLength;
13358
13359 status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
13360 srb,
13361 dataBuffer,
13362 allocationBufferLength,
13363 FALSE);
13364
13365 //
13366 // Handle the case where we get back STATUS_DATA_OVERRUN because the input
13367 // buffer was larger than necessary.
13368 //
13369 dataTransferLength = SrbGetDataTransferLength(srb);
13370
13371 if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength) {
13372
13373 status = STATUS_SUCCESS;
13374 }
13375
13376 if (NT_SUCCESS(status)) {
13377
13378 REVERSE_BYTES_SHORT(&pageLength, &operatingParameters->PageLength);
13379
13380 } else {
13381 TracePrint((TRACE_LEVEL_ERROR,
13382 TRACE_FLAG_PNP,
13383 "ClasspGetBlockDeviceTokenLimitsInfo (%p): Inquiry for TPC VPD failed with %x.\n",
13384 DeviceObject,
13385 status));
13386 }
13387
13388 if ((NT_SUCCESS(status)) &&
13389 (pageLength >= sizeof(WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR))) {
13390
13391 USHORT descriptorType;
13392
13393 blockDeviceTokenLimits = (PWINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR)operatingParameters->ThirdPartyCopyDescriptors;
13394 REVERSE_BYTES_SHORT(&descriptorType, &blockDeviceTokenLimits->DescriptorType);
13395 REVERSE_BYTES_SHORT(&descriptorLength, &blockDeviceTokenLimits->DescriptorLength);
13396
13397 if ((descriptorType == BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR_TYPE_WINDOWS) &&
13398 (VPD_PAGE_HEADER_SIZE + descriptorLength == sizeof(WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR))) {
13399
13400 fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = TRUE;
13401
13402 REVERSE_BYTES_SHORT(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors, &blockDeviceTokenLimits->MaximumRangeDescriptors);
13403 REVERSE_BYTES(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer, &blockDeviceTokenLimits->MaximumInactivityTimer);
13404 REVERSE_BYTES(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.DefaultInactivityTimer, &blockDeviceTokenLimits->DefaultInactivityTimer);
13405 REVERSE_BYTES_QUAD(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize, &blockDeviceTokenLimits->MaximumTokenTransferSize);
13406 REVERSE_BYTES_QUAD(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount, &blockDeviceTokenLimits->OptimalTransferCount);
13407
13408 TracePrint((TRACE_LEVEL_INFORMATION,
13409 TRACE_FLAG_PNP,
13410 "ClasspGetBlockDeviceTokenLimitsInfo (%p): %s %s (rev %s) reported following parameters: \
13411 \n\t\t\tMaxRangeDescriptors: %u\n\t\t\tMaxIAT: %u\n\t\t\tDefaultIAT: %u \
13412 \n\t\t\tMaxTokenTransferSize: %I64u\n\t\t\tOptimalTransferCount: %I64u\n\t\t\tOptimalTransferLengthGranularity: %u \
13413 \n\t\t\tOptimalTransferLength: %u\n\t\t\tMaxTransferLength: %u\n",
13414 DeviceObject,
13415 (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->VendorIdOffset),
13416 (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductIdOffset),
13417 (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductRevisionOffset),
13418 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors,
13419 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer,
13420 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.DefaultInactivityTimer,
13421 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize,
13422 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount,
13423 fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLengthGranularity,
13424 fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLength,
13425 fdoExtension->FunctionSupportInfo->BlockLimitsData.MaximumTransferLength));
13426 } else {
13427
13428 TracePrint((TRACE_LEVEL_WARNING,
13429 TRACE_FLAG_PNP,
13430 "ClasspGetBlockDeviceTokenLimitsInfo (%p): ThirdPartyCopy VPD data doesn't have Windows OffloadDataTransfer descriptor.\n",
13431 DeviceObject));
13432
13433 fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
13434 status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
13435 }
13436 } else {
13437
13438 TracePrint((TRACE_LEVEL_WARNING,
13439 TRACE_FLAG_PNP,
13440 "ClasspGetBlockDeviceTokenLimitsInfo (%p): TPC VPD data didn't return TPC descriptors of interest.\n",
13441 DeviceObject));
13442
13443 fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
13444 status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
13445 }
13446
13447 __ClasspGetBlockDeviceTokenLimitsInfo_Exit:
13448
13449 FREE_POOL(dataBuffer);
13450 FREE_POOL(srb);
13451 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = status;
13452
13453 TracePrint((TRACE_LEVEL_VERBOSE,
13454 TRACE_FLAG_PNP,
13455 "ClasspGetBlockDeviceTokenLimitsInfo (%p): Exiting function with status %x.\n",
13456 DeviceObject,
13457 status));
13458
13459 return status;
13460 }
13461
13462
13463 _IRQL_requires_max_(APC_LEVEL)
13464 _IRQL_requires_min_(PASSIVE_LEVEL)
13465 _IRQL_requires_same_
13466 NTSTATUS
13467 ClassDeviceProcessOffloadRead(
13468 _In_ PDEVICE_OBJECT DeviceObject,
13469 _In_ PIRP Irp,
13470 _Inout_ PSCSI_REQUEST_BLOCK Srb
13471 )
13472
13473 /*++
13474
13475 Routine Description:
13476
13477 This routine services IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES for CopyOffload
13478 Read. If the device supports copy offload, it performs the translation of the
13479 IOCTL into the appropriate SCSI commands to complete the operation.
13480
13481 This function must be called at IRQL < DISPATCH_LEVEL.
13482
13483 Arguments:
13484
13485 DeviceObject - Supplies the device object associated with this request
13486 Irp - The IRP to be processed
13487 Srb - An SRB that can be optimally used to process this request
13488
13489 Return Value:
13490
13491 NTSTATUS code
13492
13493 --*/
13494
13495 {
13496 PIO_STACK_LOCATION irpStack;
13497 NTSTATUS status;
13498 PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
13499 PDEVICE_DSM_OFFLOAD_READ_PARAMETERS offloadReadParameters;
13500 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
13501
13502 UNREFERENCED_PARAMETER(Srb);
13503
13504 //
13505 // This function must be called at less than dispatch level.
13506 // Fail if IRQL >= DISPATCH_LEVEL.
13507 //
13508 PAGED_CODE();
13509
13510 TracePrint((TRACE_LEVEL_VERBOSE,
13511 TRACE_FLAG_IOCTL,
13512 "ClassDeviceProcessOffloadRead (%p): Entering function. Irp %p\n",
13513 DeviceObject,
13514 Irp));
13515
13516
13517 irpStack = IoGetCurrentIrpStackLocation (Irp);
13518
13519 //
13520 // Validations
13521 //
13522 status = ClasspValidateOffloadSupported(DeviceObject, Irp);
13523 if (!NT_SUCCESS(status)) {
13524 goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13525 }
13526
13527 if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
13528
13529 NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
13530 TracePrint((TRACE_LEVEL_ERROR,
13531 TRACE_FLAG_IOCTL,
13532 "ClassDeviceProcessOffloadRead (%p): Called at raised IRQL.\n",
13533 DeviceObject));
13534
13535 status = STATUS_INVALID_LEVEL;
13536 goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13537 }
13538
13539 //
13540 // Ensure that this DSM IOCTL was generated in kernel
13541 //
13542 if (Irp->RequestorMode != KernelMode) {
13543
13544 TracePrint((TRACE_LEVEL_ERROR,
13545 TRACE_FLAG_IOCTL,
13546 "ClassDeviceProcessOffloadRead (%p): Called from user mode.\n",
13547 DeviceObject));
13548
13549 status = STATUS_ACCESS_DENIED;
13550 goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13551 }
13552
13553 status = ClasspValidateOffloadInputParameters(DeviceObject, Irp);
13554 if (!NT_SUCCESS(status)) {
13555 goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13556 }
13557
13558 dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
13559
13560 //
13561 // Validate that we were passed in correct sized parameter block.
13562 //
13563 if (dsmAttributes->ParameterBlockLength < sizeof(DEVICE_DSM_OFFLOAD_READ_PARAMETERS)) {
13564
13565 TracePrint((TRACE_LEVEL_ERROR,
13566 TRACE_FLAG_IOCTL,
13567 "ClassDeviceProcessOffloadRead (%p): Parameter block size (%u) too small. Required %u.\n",
13568 DeviceObject,
13569 dsmAttributes->ParameterBlockLength,
13570 sizeof(DEVICE_DSM_OFFLOAD_READ_PARAMETERS)));
13571
13572 status = STATUS_INVALID_PARAMETER;
13573 goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13574 }
13575
13576 offloadReadParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
13577
13578 fdoExtension = DeviceObject->DeviceExtension;
13579
13580 //
13581 // If the request TTL is greater than the max supported by this storage, the target will
13582 // end up failing this command, so might as well do the check up front.
13583 //
13584 if ((fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer > 0) &&
13585 (offloadReadParameters->TimeToLive > fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer)) {
13586
13587 TracePrint((TRACE_LEVEL_ERROR,
13588 TRACE_FLAG_IOCTL,
13589 "ClassDeviceProcessOffloadRead (%p): Requested TTL (%u) greater than max supported (%u).\n",
13590 DeviceObject,
13591 offloadReadParameters->TimeToLive,
13592 fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer));
13593
13594 status = STATUS_INVALID_PARAMETER;
13595 goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13596 }
13597
13598 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_OFFLOAD_READ_OUTPUT)) {
13599
13600 TracePrint((TRACE_LEVEL_ERROR,
13601 TRACE_FLAG_IOCTL,
13602 "ClassDeviceProcessOffloadRead (%p): Output buffer size (%u) too small.\n",
13603 DeviceObject,
13604 irpStack->Parameters.DeviceIoControl.OutputBufferLength));
13605
13606 status = STATUS_BUFFER_TOO_SMALL;
13607 goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13608 }
13609
13610
13611
13612 status = ClasspServicePopulateTokenTransferRequest(DeviceObject, Irp);
13613
13614 if (status == STATUS_PENDING) {
13615 goto __ClassDeviceProcessOffloadRead_Exit;
13616 }
13617
13618 __ClassDeviceProcessOffloadRead_CompleteAndExit:
13619 ClasspCompleteOffloadRequest(DeviceObject, Irp, status);
13620 __ClassDeviceProcessOffloadRead_Exit:
13621 TracePrint((TRACE_LEVEL_VERBOSE,
13622 TRACE_FLAG_IOCTL,
13623 "ClassDeviceProcessOffloadRead (%p): Exiting function Irp %p with status %x.\n",
13624 DeviceObject,
13625 Irp,
13626 status));
13627
13628 return status;
13629 }
13630
13631 VOID
13632 ClasspCompleteOffloadRequest(
13633 _In_ PDEVICE_OBJECT DeviceObject,
13634 _In_ PIRP Irp,
13635 _In_ NTSTATUS CompletionStatus)
13636 {
13637 NTSTATUS status = CompletionStatus;
13638
13639
13640 Irp->IoStatus.Status = status;
13641
13642 ClassReleaseRemoveLock(DeviceObject, Irp);
13643 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
13644
13645 return;
13646 }
13647
13648
13649 _IRQL_requires_max_(APC_LEVEL)
13650 _IRQL_requires_min_(PASSIVE_LEVEL)
13651 _IRQL_requires_same_
13652 NTSTATUS
13653 ClassDeviceProcessOffloadWrite(
13654 _In_ PDEVICE_OBJECT DeviceObject,
13655 _In_ PIRP Irp,
13656 _Inout_ PSCSI_REQUEST_BLOCK Srb
13657 )
13658
13659 /*++
13660
13661 Routine Description:
13662
13663 This routine services IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES for CopyOffload
13664 Write. If the device supports copy offload, it performs the translation of the
13665 IOCTL into the appropriate SCSI commands to complete the operation.
13666
13667 This function must be called at IRQL < DISPATCH_LEVEL.
13668
13669 Arguments:
13670
13671 DeviceObject - Supplies the device object associated with this request
13672 Irp - The IRP to be processed
13673 Srb - An SRB that can be optinally used to process this request
13674
13675 Return Value:
13676
13677 NTSTATUS code
13678
13679 --*/
13680
13681 {
13682 PIO_STACK_LOCATION irpStack;
13683 NTSTATUS status;
13684 PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
13685
13686 UNREFERENCED_PARAMETER(Srb);
13687
13688 //
13689 // This function must be called at less than dispatch level.
13690 //
13691 PAGED_CODE();
13692
13693 TracePrint((TRACE_LEVEL_VERBOSE,
13694 TRACE_FLAG_IOCTL,
13695 "ClassDeviceProcessOffloadWrite (%p): Entering function. Irp %p\n",
13696 DeviceObject,
13697 Irp));
13698
13699
13700 irpStack = IoGetCurrentIrpStackLocation(Irp);
13701
13702 //
13703 // Validations
13704 //
13705 status = ClasspValidateOffloadSupported(DeviceObject, Irp);
13706 if (!NT_SUCCESS(status)) {
13707 goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13708 }
13709
13710 if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
13711
13712 NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
13713 TracePrint((TRACE_LEVEL_ERROR,
13714 TRACE_FLAG_IOCTL,
13715 "ClassDeviceProcessOffloadWrite (%p): Called at raised IRQL.\n",
13716 DeviceObject));
13717
13718 status = STATUS_INVALID_LEVEL;
13719 goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13720 }
13721
13722 //
13723 // Ensure that this DSM IOCTL was generated in kernel
13724 //
13725 if (Irp->RequestorMode != KernelMode) {
13726
13727 TracePrint((TRACE_LEVEL_ERROR,
13728 TRACE_FLAG_IOCTL,
13729 "ClassDeviceProcessOffloadWrite (%p): Called from user mode.\n",
13730 DeviceObject));
13731
13732 status = STATUS_ACCESS_DENIED;
13733 goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13734 }
13735
13736 status = ClasspValidateOffloadInputParameters(DeviceObject, Irp);
13737 if (!NT_SUCCESS(status)) {
13738 goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13739 }
13740
13741 dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
13742
13743 //
13744 // Validate that we were passed in correct sized parameter block.
13745 //
13746 if (dsmAttributes->ParameterBlockLength < sizeof(DEVICE_DSM_OFFLOAD_WRITE_PARAMETERS)) {
13747
13748 TracePrint((TRACE_LEVEL_ERROR,
13749 TRACE_FLAG_IOCTL,
13750 "ClassDeviceProcessOffloadWrite (%p): Parameter block size (%u) too small. Required %u.\n",
13751 DeviceObject,
13752 dsmAttributes->ParameterBlockLength,
13753 sizeof(DEVICE_DSM_OFFLOAD_WRITE_PARAMETERS)));
13754
13755 status = STATUS_INVALID_PARAMETER;
13756 goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13757 }
13758
13759 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_OFFLOAD_WRITE_OUTPUT)) {
13760
13761 TracePrint((TRACE_LEVEL_ERROR,
13762 TRACE_FLAG_IOCTL,
13763 "ClassDeviceProcessOffloadWrite (%p): Output buffer size (%u) too small.\n",
13764 DeviceObject,
13765 irpStack->Parameters.DeviceIoControl.OutputBufferLength));
13766
13767 status = STATUS_BUFFER_TOO_SMALL;
13768 goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13769 }
13770
13771
13772
13773 status = ClasspServiceWriteUsingTokenTransferRequest(DeviceObject, Irp);
13774
13775 if (status == STATUS_PENDING) {
13776 goto __ClassDeviceProcessOffloadWrite_Exit;
13777 }
13778
13779 __ClassDeviceProcessOffloadWrite_CompleteAndExit:
13780 ClasspCompleteOffloadRequest(DeviceObject, Irp, status);
13781 __ClassDeviceProcessOffloadWrite_Exit:
13782 TracePrint((TRACE_LEVEL_VERBOSE,
13783 TRACE_FLAG_IOCTL,
13784 "ClassDeviceProcessOffloadWrite (%p): Exiting function Irp %p with status %x\n",
13785 DeviceObject,
13786 Irp,
13787 status));
13788
13789 return status;
13790 }
13791
13792
13793 _IRQL_requires_max_(APC_LEVEL)
13794 _IRQL_requires_min_(PASSIVE_LEVEL)
13795 _IRQL_requires_same_
13796 NTSTATUS
13797 ClasspServicePopulateTokenTransferRequest(
13798 _In_ PDEVICE_OBJECT Fdo,
13799 _In_ PIRP Irp
13800 )
13801
13802 /*++
13803
13804 Routine description:
13805
13806 This routine processes offload read requests by building the SRB
13807 for PopulateToken and its result (i.e. token).
13808
13809 Arguments:
13810
13811 Fdo - The functional device object processing the request
13812 Irp - The Io request to be processed
13813
13814 Return Value:
13815
13816 STATUS_SUCCESS if successful, an error code otherwise
13817
13818 --*/
13819
13820 {
13821 BOOLEAN allDataSetRangeFullyConverted;
13822 UINT32 allocationSize;
13823 ULONG blockDescrIndex;
13824 PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescrPointer;
13825 PVOID buffer;
13826 ULONG bufferLength;
13827 ULONG dataSetRangeIndex;
13828 PDEVICE_DATA_SET_RANGE dataSetRanges;
13829 ULONG dataSetRangesCount;
13830 PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
13831 ULONGLONG entireXferLen;
13832 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
13833 ULONG i;
13834 ULONG lbaCount;
13835 ULONG listIdentifier;
13836 ULONG maxBlockDescrCount;
13837 ULONGLONG maxLbaCount;
13838 POFFLOAD_READ_CONTEXT offloadReadContext;
13839 PDEVICE_DSM_OFFLOAD_READ_PARAMETERS offloadReadParameters;
13840 PTRANSFER_PACKET pkt;
13841 USHORT populateTokenDataLength;
13842 USHORT populateTokenDescriptorsLength;
13843 PMDL populateTokenMdl;
13844 PIRP pseudoIrp;
13845 ULONG receiveTokenInformationBufferLength;
13846 NTSTATUS status;
13847 DEVICE_DATA_SET_RANGE tempDataSetRange;
13848 BOOLEAN tempDataSetRangeFullyConverted;
13849 PUCHAR token;
13850 ULONG tokenLength;
13851 ULONG tokenOperationBufferLength;
13852 ULONGLONG totalSectorCount;
13853 ULONGLONG totalSectorsToProcess;
13854 ULONG transferSize;
13855
13856 PAGED_CODE();
13857
13858 TracePrint((TRACE_LEVEL_VERBOSE,
13859 TRACE_FLAG_IOCTL,
13860 "ClasspServicePopulateTokenTransferRequest (%p): Entering function. Irp %p\n",
13861 Fdo,
13862 Irp));
13863
13864 fdoExt = Fdo->DeviceExtension;
13865 status = STATUS_SUCCESS;
13866 dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
13867 buffer = NULL;
13868 pkt = NULL;
13869 populateTokenMdl = NULL;
13870 offloadReadParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
13871 dataSetRanges = Add2Ptr(dsmAttributes, dsmAttributes->DataSetRangesOffset);
13872 dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
13873 totalSectorsToProcess = 0;
13874 token = NULL;
13875 tokenLength = 0;
13876 bufferLength = 0;
13877 offloadReadContext = NULL;
13878
13879 NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
13880 NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
13881
13882
13883 for (i = 0, entireXferLen = 0; i < dataSetRangesCount; i++) {
13884 entireXferLen += dataSetRanges[i].LengthInBytes;
13885 }
13886
13887 //
13888 // We need to truncate the read request based on the following hardware limitations:
13889 // 1. The size of the data buffer containing the TokenOperation command's parameters must
13890 // not exceed the MaximumTransferLength (and max physical pages) of the underlying
13891 // adapter.
13892 // 2. The number of descriptors specified in the TokenOperation command must not exceed
13893 // the MaximumRangeDescriptors.
13894 // 3. The cumulative total of the number of transfer blocks in all the descriptors in
13895 // the TokenOperation command must not exceed the MaximumTokenTransferSize.
13896 // 4. If the TTL has been set to the 0 (i.e. indicates the use of the default TLT),
13897 // limit the cumulative total of the number of transfer blocks in all the descriptors
13898 // in the TokenOperation command to the OptimalTransferCount.
13899 // NOTE: only a TTL of 0 has an implicit indication that the initiator of the
13900 // request would like the storage stack to be smart about the amount of data
13901 // transfer.
13902 //
13903 // In addition to the above, we need to ensure that for each of the descriptors in the
13904 // TokenOperation command:
13905 // 1. The number of blocks specified is an exact multiple of the OptimalTransferLengthGranularity.
13906 // 2. The number of blocks specified is limited to the MaximumTransferLength. (We shall
13907 // however, limit the number of blocks specified in each descriptor to be a maximum of
13908 // OptimalTransferLength or MaximumTransferLength, whichever is lesser).
13909 //
13910 // Finally, we shall always send down the PopulateToken command using IMMED = 0 for this
13911 // release. This makes it simpler to handle multi-initiator scenarios since we won't need
13912 // to deal with the PopulateToken (with IMMED = 1) succeeding but ReceiveRODTokenInformation
13913 // failing due to the path/node failing, thus making it impossible to retrieve the token.
13914 //
13915 // The LBA ranges is in DEVICE_DATA_SET_RANGE format, it needs to be converted into
13916 // WINDOWS_RANGE_DESCRIPTOR Block Descriptors.
13917 //
13918
13919 ClasspGetTokenOperationCommandBufferLength(Fdo,
13920 SERVICE_ACTION_POPULATE_TOKEN,
13921 &bufferLength,
13922 &tokenOperationBufferLength,
13923 &receiveTokenInformationBufferLength);
13924
13925 allocationSize = sizeof(OFFLOAD_READ_CONTEXT) + bufferLength;
13926
13927 offloadReadContext = ExAllocatePoolWithTag(
13928 NonPagedPoolNx,
13929 allocationSize,
13930 CLASSPNP_POOL_TAG_TOKEN_OPERATION);
13931
13932 if (!offloadReadContext) {
13933
13934 TracePrint((TRACE_LEVEL_ERROR,
13935 TRACE_FLAG_IOCTL,
13936 "ClasspServicePopulateTokenTransferRequest (%p): Failed to allocate buffer for PopulateToken operations.\n",
13937 Fdo));
13938
13939 status = STATUS_INSUFFICIENT_RESOURCES;
13940 goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
13941 }
13942
13943 RtlZeroMemory(offloadReadContext, allocationSize);
13944
13945 offloadReadContext->Fdo = Fdo;
13946 offloadReadContext->OffloadReadDsmIrp = Irp;
13947
13948 //
13949 // The buffer for the commands is after the offloadReadContext.
13950 //
13951 buffer = (offloadReadContext + 1);
13952
13953 //
13954 // No matter how large the offload read request, we'll be sending it down in one shot.
13955 // So we need only one transfer packet.
13956 //
13957 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
13958 if (!pkt){
13959
13960 TracePrint((TRACE_LEVEL_ERROR,
13961 TRACE_FLAG_IOCTL,
13962 "ClasspServicePopulateTokenTransferRequest (%p): Failed to retrieve transfer packet for TokenOperation (PopulateToken) operation.\n",
13963 Fdo));
13964
13965 status = STATUS_INSUFFICIENT_RESOURCES;
13966 goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
13967 }
13968
13969 offloadReadContext->Pkt = pkt;
13970
13971 ClasspGetTokenOperationDescriptorLimits(Fdo,
13972 SERVICE_ACTION_POPULATE_TOKEN,
13973 tokenOperationBufferLength,
13974 &maxBlockDescrCount,
13975 &maxLbaCount);
13976
13977 //
13978 // We will limit the maximum data transfer in an offload read operation to:
13979 // - OTC if TTL was specified as 0
13980 // - MaximumTransferTransferSize if lesser than above, or if TTL was specified was non-zero
13981 //
13982 if ((offloadReadParameters->TimeToLive == 0) && (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount > 0)) {
13983
13984 maxLbaCount = MIN(maxLbaCount, fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount);
13985 }
13986
13987 //
13988 // Since we do not want very fragmented files to end up causing the PopulateToken command to take
13989 // too long (and potentially timeout), we will limit the max number of descriptors sent down in a
13990 // command.
13991 //
13992 maxBlockDescrCount = MIN(maxBlockDescrCount, MAX_NUMBER_BLOCK_DEVICE_DESCRIPTORS);
13993
13994 TracePrint((TRACE_LEVEL_INFORMATION,
13995 TRACE_FLAG_IOCTL,
13996 "ClasspServicePopulateTokenTransferRequest (%p): Using MaxBlockDescrCount %u and MaxLbaCount %I64u.\n",
13997 Fdo,
13998 maxBlockDescrCount,
13999 maxLbaCount));
14000
14001 allDataSetRangeFullyConverted = FALSE;
14002 tempDataSetRangeFullyConverted = TRUE;
14003 dataSetRangeIndex = (ULONG)-1;
14004
14005 blockDescrPointer = (PBLOCK_DEVICE_RANGE_DESCRIPTOR)
14006 &((PPOPULATE_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptor[0];
14007
14008 RtlZeroMemory(&tempDataSetRange, sizeof(tempDataSetRange));
14009
14010 blockDescrIndex = 0;
14011 lbaCount = 0;
14012
14013 //
14014 // Send PopulateToken command when the buffer is full or all input entries are converted.
14015 //
14016 while (!((blockDescrIndex == maxBlockDescrCount) || // buffer full or block descriptor count reached
14017 (lbaCount == maxLbaCount) || // block LBA count reached
14018 (allDataSetRangeFullyConverted))) { // all DataSetRanges have been converted
14019
14020 //
14021 // If the previous entry conversion completed, continue the next one;
14022 // Otherwise, still process the left part of the un-completed entry.
14023 //
14024 if (tempDataSetRangeFullyConverted) {
14025 dataSetRangeIndex++;
14026 tempDataSetRange.StartingOffset = dataSetRanges[dataSetRangeIndex].StartingOffset;
14027 tempDataSetRange.LengthInBytes = dataSetRanges[dataSetRangeIndex].LengthInBytes;
14028 }
14029
14030 totalSectorCount = 0;
14031
14032 ClasspConvertDataSetRangeToBlockDescr(Fdo,
14033 blockDescrPointer,
14034 &blockDescrIndex,
14035 maxBlockDescrCount,
14036 &lbaCount,
14037 maxLbaCount,
14038 &tempDataSetRange,
14039 &totalSectorCount);
14040
14041 tempDataSetRangeFullyConverted = (tempDataSetRange.LengthInBytes == 0) ? TRUE : FALSE;
14042
14043 allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted && ((dataSetRangeIndex + 1) == dataSetRangesCount);
14044
14045 totalSectorsToProcess += totalSectorCount;
14046 }
14047
14048 //
14049 // Calculate transfer size, including the header
14050 //
14051 transferSize = (blockDescrIndex * sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR)) + FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor);
14052
14053 NT_ASSERT(transferSize <= MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH);
14054
14055 populateTokenDataLength = (USHORT)transferSize - RTL_SIZEOF_THROUGH_FIELD(POPULATE_TOKEN_HEADER, PopulateTokenDataLength);
14056 REVERSE_BYTES_SHORT(((PPOPULATE_TOKEN_HEADER)buffer)->PopulateTokenDataLength, &populateTokenDataLength);
14057
14058 ((PPOPULATE_TOKEN_HEADER)buffer)->Immediate = 0;
14059
14060 REVERSE_BYTES(((PPOPULATE_TOKEN_HEADER)buffer)->InactivityTimeout, &offloadReadParameters->TimeToLive);
14061
14062 populateTokenDescriptorsLength = (USHORT)transferSize - FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor);
14063 REVERSE_BYTES_SHORT(((PPOPULATE_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptorListLength, &populateTokenDescriptorsLength);
14064
14065 //
14066 // Reuse a single buffer for both TokenOperation and ReceiveTokenInformation. This has the one disadvantage
14067 // that we'll be marking the page(s) as IoWriteAccess even though we only need read access for token
14068 // operation command. However, the advantage is that we eliminate the possibility of any potential failures
14069 // when trying to allocate an MDL for the ReceiveTokenInformation later on.
14070 //
14071 bufferLength = max(transferSize, receiveTokenInformationBufferLength);
14072
14073 populateTokenMdl = ClasspBuildDeviceMdl(buffer, bufferLength, FALSE);
14074 if (!populateTokenMdl) {
14075
14076 TracePrint((TRACE_LEVEL_ERROR,
14077 TRACE_FLAG_IOCTL,
14078 "ClasspServicePopulateTokenTransferRequest (%p): Failed to allocate MDL for PopulateToken operations.\n",
14079 Fdo));
14080
14081 status = STATUS_INSUFFICIENT_RESOURCES;
14082 goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
14083 }
14084
14085 offloadReadContext->PopulateTokenMdl = populateTokenMdl;
14086
14087 pseudoIrp = &offloadReadContext->PseudoIrp;
14088
14089
14090 pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
14091 pseudoIrp->IoStatus.Information = 0;
14092 pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
14093 pseudoIrp->MdlAddress = populateTokenMdl;
14094
14095 InterlockedCompareExchange((volatile LONG *)&TokenOperationListIdentifier, -1, MaxTokenOperationListIdentifier);
14096 listIdentifier = InterlockedIncrement((volatile LONG *)&TokenOperationListIdentifier);
14097
14098 ClasspSetupPopulateTokenTransferPacket(
14099 offloadReadContext,
14100 pkt,
14101 transferSize,
14102 (PUCHAR)buffer,
14103 pseudoIrp,
14104 listIdentifier);
14105
14106 TracePrint((TRACE_LEVEL_INFORMATION,
14107 TRACE_FLAG_IOCTL,
14108 "ClasspServicePopulateTokenTransferRequest (%p): Generate token for %I64u bytes (versus %I64u) [via %u descriptors]. \
14109 \n\t\t\tDataLength: %u, DescriptorsLength: %u. Pkt %p (list id %x). Requested TTL: %u secs.\n",
14110 Fdo,
14111 totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
14112 entireXferLen,
14113 blockDescrIndex,
14114 populateTokenDataLength,
14115 populateTokenDescriptorsLength,
14116 pkt,
14117 listIdentifier,
14118 offloadReadParameters->TimeToLive));
14119
14120 //
14121 // Save (into the offloadReadContext) any remaining things that
14122 // ClasspPopulateTokenTransferPacketDone() will need.
14123 //
14124
14125 offloadReadContext->ListIdentifier = listIdentifier;
14126 offloadReadContext->BufferLength = bufferLength;
14127 offloadReadContext->ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength;
14128 offloadReadContext->TotalSectorsToProcess = totalSectorsToProcess; // so far
14129 offloadReadContext->EntireXferLen = entireXferLen;
14130
14131 NT_ASSERT(status == STATUS_SUCCESS); // so far.
14132
14133 IoMarkIrpPending(Irp);
14134 SubmitTransferPacket(pkt);
14135
14136 status = STATUS_PENDING;
14137 goto __ClasspServicePopulateTokenTransferRequest_Exit;
14138
14139 //
14140 // Error cleanup label only - not used in success case:
14141 //
14142
14143 __ClasspServicePopulateTokenTransferRequest_ErrorExit:
14144
14145 NT_ASSERT(status != STATUS_PENDING);
14146
14147 if (offloadReadContext != NULL) {
14148 ClasspCleanupOffloadReadContext(offloadReadContext);
14149 offloadReadContext = NULL;
14150 }
14151
14152 __ClasspServicePopulateTokenTransferRequest_Exit:
14153
14154 TracePrint((TRACE_LEVEL_VERBOSE,
14155 TRACE_FLAG_IOCTL,
14156 "ClasspServicePopulateTokenTransferRequest (%p): Exiting function (Irp %p) with status %x.\n",
14157 Fdo,
14158 Irp,
14159 status));
14160
14161 return status;
14162 }
14163
14164
14165 VOID
14166 ClasspPopulateTokenTransferPacketDone(
14167 _In_ PVOID Context
14168 )
14169
14170 /*++
14171
14172 Routine description:
14173
14174 This routine continues an offload read operation on completion of the
14175 populate token transfer packet.
14176
14177 This function is responsible for continuing or completing the offload read
14178 operation.
14179
14180 Arguments:
14181
14182 Context - Pointer to the OFFLOAD_READ_CONTEXT for the offload read
14183 operation.
14184
14185 Return Value:
14186
14187 None.
14188
14189 --*/
14190
14191 {
14192 PDEVICE_OBJECT fdo;
14193 ULONG listIdentifier;
14194 POFFLOAD_READ_CONTEXT offloadReadContext;
14195 PTRANSFER_PACKET pkt;
14196 PIRP pseudoIrp;
14197 NTSTATUS status;
14198
14199 offloadReadContext = Context;
14200 pseudoIrp = &offloadReadContext->PseudoIrp;
14201 pkt = offloadReadContext->Pkt;
14202 fdo = offloadReadContext->Fdo;
14203 listIdentifier = offloadReadContext->ListIdentifier;
14204
14205 offloadReadContext->Pkt = NULL;
14206
14207
14208 status = pseudoIrp->IoStatus.Status;
14209 NT_ASSERT(status != STATUS_PENDING);
14210
14211 if (!NT_SUCCESS(status)) {
14212 TracePrint((TRACE_LEVEL_ERROR,
14213 TRACE_FLAG_IOCTL,
14214 "ClasspPopulateTokenTransferPacketDone (%p): Generate token for list Id %x failed with %x (Pkt %p).\n",
14215 fdo,
14216 listIdentifier,
14217 status,
14218 pkt));
14219 goto __ClasspPopulateTokenTransferPacketDone_ErrorExit;
14220 }
14221
14222 //
14223 // If a token was successfully generated, it is now time to retrieve the token.
14224 // The called function is responsible for completing the offload read DSM IRP.
14225 //
14226 ClasspReceivePopulateTokenInformation(offloadReadContext);
14227
14228 //
14229 // ClasspReceivePopulateTokenInformation() takes care of completing the IRP,
14230 // so this function is done regardless of success or failure in
14231 // ClasspReceivePopulateTokenInformation().
14232 //
14233
14234 return;
14235
14236 //
14237 // Error cleanup label only - not used in success case:
14238 //
14239
14240 __ClasspPopulateTokenTransferPacketDone_ErrorExit:
14241
14242 NT_ASSERT(!NT_SUCCESS(status));
14243
14244 //
14245 // ClasspCompleteOffloadRead also cleans up offloadReadContext.
14246 //
14247
14248 ClasspCompleteOffloadRead(offloadReadContext, status);
14249
14250 return;
14251 }
14252
14253
14254 VOID
14255 ClasspCompleteOffloadRead(
14256 _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
14257 _In_ NTSTATUS CompletionStatus
14258 )
14259
14260 /*++
14261
14262 Routine description:
14263
14264 This routine completes an offload read operation with given status, and
14265 cleans up the OFFLOAD_READ_CONTEXT.
14266
14267 Arguments:
14268
14269 OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
14270 read operation.
14271
14272 CompletionStatus - The completion status for the offload read operation.
14273
14274 Return Value:
14275
14276 None.
14277
14278 --*/
14279
14280 {
14281 PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
14282 ULONGLONG entireXferLen;
14283 PDEVICE_OBJECT fdo;
14284 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
14285 PIRP irp;
14286 NTSTATUS status;
14287 PUCHAR token;
14288 ULONGLONG totalSectorsProcessed;
14289
14290 status = CompletionStatus;
14291 dsmAttributes = OffloadReadContext->OffloadReadDsmIrp->AssociatedIrp.SystemBuffer;
14292 totalSectorsProcessed = OffloadReadContext->TotalSectorsProcessed;
14293 fdoExt = OffloadReadContext->Fdo->DeviceExtension;
14294 entireXferLen = OffloadReadContext->EntireXferLen;
14295 token = OffloadReadContext->Token;
14296 irp = OffloadReadContext->OffloadReadDsmIrp;
14297 fdo = OffloadReadContext->Fdo;
14298
14299 ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->OffloadReadFlags = 0;
14300 ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->Reserved = 0;
14301
14302 if (NT_SUCCESS(status)) {
14303 ULONGLONG totalBytesProcessed = totalSectorsProcessed * fdoExt->DiskGeometry.BytesPerSector;
14304
14305 TracePrint((totalBytesProcessed == entireXferLen ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
14306 TRACE_FLAG_IOCTL,
14307 "ClasspCompleteOffloadRead (%p): Successfully populated token with %I64u (out of %I64u) bytes (list Id %x).\n",
14308 fdo,
14309 totalBytesProcessed,
14310 entireXferLen,
14311 OffloadReadContext->ListIdentifier));
14312
14313 if (totalBytesProcessed < entireXferLen) {
14314 SET_FLAG(((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->OffloadReadFlags, STORAGE_OFFLOAD_READ_RANGE_TRUNCATED);
14315 }
14316 ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->LengthProtected = totalBytesProcessed;
14317 ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->TokenLength = STORAGE_OFFLOAD_MAX_TOKEN_LENGTH;
14318 RtlCopyMemory(&((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->Token, token, STORAGE_OFFLOAD_MAX_TOKEN_LENGTH);
14319 } else {
14320 ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->LengthProtected = 0;
14321 ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->TokenLength = 0;
14322 }
14323
14324 irp->IoStatus.Information = sizeof(STORAGE_OFFLOAD_READ_OUTPUT);
14325
14326 ClasspCompleteOffloadRequest(fdo, irp, status);
14327 ClasspCleanupOffloadReadContext(OffloadReadContext);
14328 OffloadReadContext = NULL;
14329
14330 return;
14331 }
14332
14333
14334 VOID
14335 ClasspCleanupOffloadReadContext(
14336 _In_ __drv_freesMem(mem) POFFLOAD_READ_CONTEXT OffloadReadContext
14337 )
14338
14339 /*++
14340
14341 Routine description:
14342
14343 This routine cleans up an OFFLOAD_READ_CONTEXT.
14344
14345 Arguments:
14346
14347 OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
14348 read operation.
14349
14350 Return Value:
14351
14352 None.
14353
14354 --*/
14355
14356 {
14357 PMDL populateTokenMdl;
14358
14359 populateTokenMdl = OffloadReadContext->PopulateTokenMdl;
14360
14361 NT_ASSERT(OffloadReadContext != NULL);
14362
14363 if (populateTokenMdl) {
14364 ClasspFreeDeviceMdl(populateTokenMdl);
14365 }
14366 FREE_POOL(OffloadReadContext);
14367
14368 return;
14369 }
14370
14371
14372 _IRQL_requires_same_
14373 VOID
14374 ClasspReceivePopulateTokenInformation(
14375 _In_ POFFLOAD_READ_CONTEXT OffloadReadContext
14376 )
14377
14378 /*++
14379
14380 Routine description:
14381
14382 This routine retrieves the token after a PopulateToken command
14383 has been sent down.
14384
14385 Can also be called repeatedly for a single offload op when a previous
14386 RECEIVE ROD TOKEN INFORMATION for that op indicated that the operation was
14387 not yet complete.
14388
14389 This function is responsible for continuing or completing the offload read
14390 operation.
14391
14392 Arguments:
14393
14394 OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
14395 read operation.
14396
14397 Return Value:
14398
14399 None.
14400
14401 --*/
14402
14403 {
14404 PVOID buffer;
14405 ULONG bufferLength;
14406 ULONG cdbLength;
14407 PDEVICE_OBJECT fdo;
14408 PIRP irp;
14409 ULONG listIdentifier;
14410 PTRANSFER_PACKET pkt;
14411 PIRP pseudoIrp;
14412 ULONG receiveTokenInformationBufferLength;
14413 PSCSI_REQUEST_BLOCK srb;
14414 NTSTATUS status;
14415 ULONG tempSizeUlong;
14416 PULONGLONG totalSectorsProcessed;
14417
14418 totalSectorsProcessed = &OffloadReadContext->TotalSectorsProcessed;
14419 buffer = OffloadReadContext + 1;
14420 bufferLength = OffloadReadContext->BufferLength;
14421 fdo = OffloadReadContext->Fdo;
14422 irp = OffloadReadContext->OffloadReadDsmIrp;
14423 receiveTokenInformationBufferLength = OffloadReadContext->ReceiveTokenInformationBufferLength;
14424 listIdentifier = OffloadReadContext->ListIdentifier;
14425
14426 TracePrint((TRACE_LEVEL_VERBOSE,
14427 TRACE_FLAG_IOCTL,
14428 "ClasspReceivePopulateTokenInformation (%p): Entering function. Irp %p\n",
14429 fdo,
14430 irp));
14431
14432 srb = &OffloadReadContext->Srb;
14433 *totalSectorsProcessed = 0;
14434
14435 pkt = DequeueFreeTransferPacket(fdo, TRUE);
14436 if (!pkt){
14437
14438 TracePrint((TRACE_LEVEL_ERROR,
14439 TRACE_FLAG_IOCTL,
14440 "ClasspReceivePopulateTokenInformation (%p): Failed to retrieve transfer packet for ReceiveTokenInformation (PopulateToken) operation.\n",
14441 fdo));
14442
14443 status = STATUS_INSUFFICIENT_RESOURCES;
14444 goto __ClasspReceivePopulateTokenInformation_ErrorExit;
14445 }
14446
14447 OffloadReadContext->Pkt = pkt;
14448
14449 RtlZeroMemory(buffer, bufferLength);
14450
14451 tempSizeUlong = receiveTokenInformationBufferLength - 4;
14452 REVERSE_BYTES(((PRECEIVE_TOKEN_INFORMATION_HEADER)buffer)->AvailableData, &tempSizeUlong);
14453
14454 pseudoIrp = &OffloadReadContext->PseudoIrp;
14455 RtlZeroMemory(pseudoIrp, sizeof(IRP));
14456
14457
14458 pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
14459 pseudoIrp->IoStatus.Information = 0;
14460 pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
14461 pseudoIrp->MdlAddress = OffloadReadContext->PopulateTokenMdl;
14462
14463 ClasspSetupReceivePopulateTokenInformationTransferPacket(
14464 OffloadReadContext,
14465 pkt,
14466 receiveTokenInformationBufferLength,
14467 (PUCHAR)buffer,
14468 pseudoIrp,
14469 listIdentifier);
14470
14471 //
14472 // Cache away the CDB as it may be required for forwarded sense data
14473 // after this command completes.
14474 //
14475 RtlZeroMemory(srb, sizeof(*srb));
14476 cdbLength = SrbGetCdbLength(pkt->Srb);
14477 if (cdbLength <= 16) {
14478 RtlCopyMemory(&srb->Cdb, SrbGetCdb(pkt->Srb), cdbLength);
14479 }
14480
14481 SubmitTransferPacket(pkt);
14482
14483 return;
14484
14485 //
14486 // Error cleanup label only - not used in success case:
14487 //
14488
14489 __ClasspReceivePopulateTokenInformation_ErrorExit:
14490
14491 NT_ASSERT(!NT_SUCCESS(status));
14492
14493 //
14494 // ClasspCompleteOffloadRead also cleans up offloadReadContext.
14495 //
14496
14497 ClasspCompleteOffloadRead(OffloadReadContext, status);
14498
14499 return;
14500 }
14501
14502
14503 VOID
14504 ClasspReceivePopulateTokenInformationTransferPacketDone(
14505 _In_ PVOID Context
14506 )
14507
14508 /*++
14509
14510 Routine description:
14511
14512 This routine continues an offload read operation on completion of the
14513 RECEIVE ROD TOKEN INFORMATION transfer packet.
14514
14515 This routine is responsible for continuing or completing the offload read
14516 operation.
14517
14518 Arguments:
14519
14520 Context - Pointer to the OFFLOAD_READ_CONTEXT for the offload read
14521 operation.
14522
14523 Return Value:
14524
14525 None.
14526
14527 --*/
14528
14529 {
14530 ULONG availableData;
14531 PVOID buffer;
14532 UCHAR completionStatus;
14533 ULONG estimatedRetryInterval;
14534 PDEVICE_OBJECT fdo;
14535 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
14536 PIRP irp;
14537 ULONG listIdentifier;
14538 POFFLOAD_READ_CONTEXT offloadReadContext;
14539 BOOLEAN operationCompleted;
14540 UCHAR operationStatus;
14541 PIRP pseudoIrp;
14542 USHORT segmentsProcessed;
14543 PSENSE_DATA senseData;
14544 ULONG senseDataFieldLength;
14545 UCHAR senseDataLength;
14546 PSCSI_REQUEST_BLOCK srb;
14547 NTSTATUS status;
14548 PUCHAR token;
14549 PVOID tokenAscii;
14550 PBLOCK_DEVICE_TOKEN_DESCRIPTOR tokenDescriptor;
14551 ULONG tokenDescriptorLength;
14552 PRECEIVE_TOKEN_INFORMATION_HEADER tokenInformationResults;
14553 PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER tokenInformationResultsResponse;
14554 ULONG tokenLength;
14555 ULONG tokenSize;
14556 PULONGLONG totalSectorsProcessed;
14557 ULONGLONG totalSectorsToProcess;
14558 ULONGLONG transferBlockCount;
14559
14560 offloadReadContext = Context;
14561 fdo = offloadReadContext->Fdo;
14562 fdoExt = fdo->DeviceExtension;
14563 buffer = offloadReadContext + 1;
14564 listIdentifier = offloadReadContext->ListIdentifier;
14565 irp = offloadReadContext->OffloadReadDsmIrp;
14566 totalSectorsToProcess = offloadReadContext->TotalSectorsToProcess;
14567 totalSectorsProcessed = &offloadReadContext->TotalSectorsProcessed;
14568 srb = &offloadReadContext->Srb;
14569 tokenAscii = NULL;
14570 tokenSize = BLOCK_DEVICE_TOKEN_SIZE;
14571 tokenInformationResults = (PRECEIVE_TOKEN_INFORMATION_HEADER)buffer;
14572 senseData = (PSENSE_DATA)((PUCHAR)tokenInformationResults + FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData));
14573 transferBlockCount = 0;
14574 tokenInformationResultsResponse = NULL;
14575 tokenDescriptor = NULL;
14576 operationCompleted = FALSE;
14577 tokenDescriptorLength = 0;
14578 tokenLength = 0;
14579 token = NULL;
14580 pseudoIrp = &offloadReadContext->PseudoIrp;
14581
14582 status = pseudoIrp->IoStatus.Status;
14583 NT_ASSERT(status != STATUS_PENDING);
14584
14585 //
14586 // The buffer we hand allows for the max sizes for all the fields whereas the returned
14587 // data may be lesser (e.g. sense data info will almost never be MAX_SENSE_BUFFER_SIZE, etc.
14588 // so handle underrun "error"
14589 //
14590 if (status == STATUS_DATA_OVERRUN) {
14591
14592 status = STATUS_SUCCESS;
14593 }
14594
14595 if (!NT_SUCCESS(status)) {
14596 TracePrint((TRACE_LEVEL_ERROR,
14597 TRACE_FLAG_IOCTL,
14598 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Token retrieval failed for list Id %x with %x.\n",
14599 fdo,
14600 listIdentifier,
14601 status));
14602 goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
14603 }
14604
14605 REVERSE_BYTES(&availableData, &tokenInformationResults->AvailableData);
14606
14607 NT_ASSERT(availableData <= FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) + MAX_SENSE_BUFFER_SIZE +
14608 FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER, TokenDescriptor) + BLOCK_DEVICE_TOKEN_SIZE);
14609
14610 NT_ASSERT(tokenInformationResults->ResponseToServiceAction == SERVICE_ACTION_POPULATE_TOKEN);
14611
14612 operationStatus = tokenInformationResults->OperationStatus;
14613 operationCompleted = ClasspIsTokenOperationComplete(operationStatus);
14614 NT_ASSERT(operationCompleted);
14615
14616 REVERSE_BYTES(&estimatedRetryInterval, &tokenInformationResults->EstimatedStatusUpdateDelay);
14617
14618 completionStatus = tokenInformationResults->CompletionStatus;
14619
14620 NT_ASSERT(tokenInformationResults->TransferCountUnits == TRANSFER_COUNT_UNITS_NUMBER_BLOCKS);
14621 REVERSE_BYTES_QUAD(&transferBlockCount, &tokenInformationResults->TransferCount);
14622
14623 REVERSE_BYTES_SHORT(&segmentsProcessed, &tokenInformationResults->SegmentsProcessed);
14624 NT_ASSERT(segmentsProcessed == 0);
14625
14626 if (operationCompleted) {
14627
14628 if (transferBlockCount > totalSectorsToProcess) {
14629
14630 //
14631 // Buggy or hostile target. Don't let it claim more was procesed
14632 // than was requested. Since this is likely a bug and it's unknown
14633 // how much was actually transferred, assume no data was
14634 // transferred.
14635 //
14636
14637 NT_ASSERT(transferBlockCount <= totalSectorsToProcess);
14638 transferBlockCount = 0;
14639 }
14640
14641 if (operationStatus != OPERATION_COMPLETED_WITH_SUCCESS &&
14642 operationStatus != OPERATION_COMPLETED_WITH_RESIDUAL_DATA) {
14643
14644 //
14645 // Assert on buggy response from target, but in any case, make sure not
14646 // to claim that any data was written.
14647 //
14648
14649 NT_ASSERT(transferBlockCount == 0);
14650 transferBlockCount = 0;
14651 }
14652
14653 //
14654 // Since the TokenOperation was sent down synchronously, the operation is complete as soon as the command returns.
14655 //
14656
14657 senseDataFieldLength = tokenInformationResults->SenseDataFieldLength;
14658 senseDataLength = tokenInformationResults->SenseDataLength;
14659 NT_ASSERT(senseDataFieldLength >= senseDataLength);
14660
14661 tokenInformationResultsResponse = (PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER)((PUCHAR)tokenInformationResults +
14662 FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) +
14663 tokenInformationResults->SenseDataFieldLength);
14664
14665 REVERSE_BYTES(&tokenDescriptorLength, &tokenInformationResultsResponse->TokenDescriptorsLength);
14666
14667 if (tokenDescriptorLength > 0) {
14668
14669 NT_ASSERT(tokenDescriptorLength == sizeof(BLOCK_DEVICE_TOKEN_DESCRIPTOR));
14670
14671 if (tokenDescriptorLength != sizeof(BLOCK_DEVICE_TOKEN_DESCRIPTOR)) {
14672
14673 TracePrint((TRACE_LEVEL_ERROR,
14674 TRACE_FLAG_IOCTL,
14675 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Bad firmware, token descriptor length %u.\n",
14676 fdo,
14677 tokenDescriptorLength));
14678
14679 NT_ASSERT((*totalSectorsProcessed) == 0);
14680 NT_ASSERT(tokenLength == 0);
14681
14682 } else {
14683
14684 USHORT restrictedId;
14685
14686 tokenDescriptor = (PBLOCK_DEVICE_TOKEN_DESCRIPTOR)tokenInformationResultsResponse->TokenDescriptor;
14687
14688 REVERSE_BYTES_SHORT(&restrictedId, &tokenDescriptor->TokenIdentifier);
14689 NT_ASSERT(restrictedId == 0);
14690
14691 tokenLength = BLOCK_DEVICE_TOKEN_SIZE;
14692 token = tokenDescriptor->Token;
14693
14694 *totalSectorsProcessed = transferBlockCount;
14695
14696 if (transferBlockCount < totalSectorsToProcess) {
14697
14698 NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
14699 operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
14700 operationStatus == OPERATION_TERMINATED);
14701
14702 if (transferBlockCount == 0) {
14703 //
14704 // Treat the same as not getting a token.
14705 //
14706
14707 tokenLength = 0;
14708 }
14709
14710 } else {
14711
14712 NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_SUCCESS);
14713 NT_ASSERT(transferBlockCount == totalSectorsToProcess);
14714 }
14715
14716 //
14717 // Need to convert to ascii.
14718 //
14719 tokenAscii = ClasspBinaryToAscii((PUCHAR)token,
14720 tokenSize,
14721 &tokenSize);
14722
14723 TracePrint((transferBlockCount == totalSectorsToProcess ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
14724 TRACE_FLAG_IOCTL,
14725 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): %wsToken %s generated successfully for list Id %x for data size %I64u bytes.\n",
14726 fdo,
14727 transferBlockCount == totalSectorsToProcess ? L"" : L"Target truncated read. ",
14728 (tokenAscii == NULL) ? "" : tokenAscii,
14729 listIdentifier,
14730 (*totalSectorsProcessed) * fdoExt->DiskGeometry.BytesPerSector));
14731
14732 FREE_POOL(tokenAscii);
14733 }
14734 } else {
14735
14736 TracePrint((TRACE_LEVEL_ERROR,
14737 TRACE_FLAG_IOCTL,
14738 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Target failed to generate a token for list Id %x for data size %I64u bytes (requested %I64u bytes).\n",
14739 fdo,
14740 listIdentifier,
14741 transferBlockCount * fdoExt->DiskGeometry.BytesPerSector,
14742 totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector));
14743
14744 *totalSectorsProcessed = 0;
14745
14746 NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_ERROR);
14747 }
14748
14749 //
14750 // Operation that completes with success can have sense data (for target to pass on some extra info)
14751 // but we don't care about such sense info.
14752 // Operation that complete but not with success, may not have sense data associated, but may
14753 // have valid CompletionStatus.
14754 //
14755 // The "status" may be overriden by ClassInterpretSenseInfo(). Final
14756 // status is determined a bit later - this is just the default status
14757 // when ClassInterpretSenseInfo() doesn't get to run here.
14758 //
14759 status = STATUS_SUCCESS;
14760 if (operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
14761 operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
14762 operationStatus == OPERATION_TERMINATED) {
14763
14764 SrbSetScsiStatus((PSTORAGE_REQUEST_BLOCK_HEADER)srb, completionStatus);
14765
14766 if (senseDataLength) {
14767
14768 ULONG retryInterval;
14769
14770 NT_ASSERT(senseDataLength <= sizeof(SENSE_DATA));
14771
14772 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
14773
14774 srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
14775 SrbSetSenseInfoBuffer((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseData);
14776 SrbSetSenseInfoBufferLength((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseDataLength);
14777
14778 ClassInterpretSenseInfo(fdo,
14779 srb,
14780 IRP_MJ_SCSI,
14781 0,
14782 0,
14783 &status,
14784 &retryInterval);
14785
14786 TracePrint((TRACE_LEVEL_WARNING,
14787 TRACE_FLAG_IOCTL,
14788 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Reason for truncation/failure: %x - for list Id %x for data size %I64u bytes.\n",
14789 fdo,
14790 status,
14791 listIdentifier,
14792 transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
14793 } else {
14794
14795 TracePrint((TRACE_LEVEL_WARNING,
14796 TRACE_FLAG_IOCTL,
14797 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): No sense data available but reason for truncation/failure, possibly: %x - for list Id %x for data size %I64u bytes.\n",
14798 fdo,
14799 completionStatus,
14800 listIdentifier,
14801 transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
14802 }
14803 }
14804
14805 if (tokenLength > 0) {
14806
14807 offloadReadContext->Token = token;
14808
14809 //
14810 // Even if target returned an error, from the OS upper layers' perspective,
14811 // it is a success (with truncation) if any data at all was read.
14812 //
14813 status = STATUS_SUCCESS;
14814
14815 } else {
14816
14817 if (NT_SUCCESS(status)) {
14818 //
14819 // Make sure status is a failing status, without throwing away an
14820 // already-failing status obtained from sense data.
14821 //
14822 status = STATUS_UNSUCCESSFUL;
14823 }
14824 }
14825
14826 //
14827 // Done with the operation.
14828 //
14829
14830 NT_ASSERT(status != STATUS_PENDING);
14831 goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
14832
14833 } else {
14834
14835 status = STATUS_UNSUCCESSFUL;
14836
14837 TracePrint((TRACE_LEVEL_ERROR,
14838 TRACE_FLAG_IOCTL,
14839 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Token retrieval failed for list Id %x with %x.\n",
14840 fdo,
14841 listIdentifier,
14842 status));
14843
14844 NT_ASSERT(*totalSectorsProcessed == 0);
14845 goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
14846 }
14847
14848 __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit:
14849
14850 if (status != STATUS_PENDING) {
14851
14852 //
14853 // The "status" value can be success or failure at this point, as
14854 // appropriate.
14855 //
14856
14857 ClasspCompleteOffloadRead(offloadReadContext, status);
14858 }
14859
14860 //
14861 // Due to tracing a potentially freed pointer value "Irp", this trace could
14862 // be delayed beyond another offload op picking up the same pointer value
14863 // for its Irp. This function exits after the operation is complete when
14864 // status != STATUS_PENDING.
14865 //
14866
14867 TracePrint((TRACE_LEVEL_VERBOSE,
14868 TRACE_FLAG_IOCTL,
14869 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Exiting function (Irp %p) with internal status %x.\n",
14870 fdo,
14871 irp,
14872 status));
14873
14874 return;
14875 }
14876
14877
14878 _IRQL_requires_max_(APC_LEVEL)
14879 _IRQL_requires_min_(PASSIVE_LEVEL)
14880 _IRQL_requires_same_
14881 NTSTATUS
14882 ClasspServiceWriteUsingTokenTransferRequest(
14883 _In_ PDEVICE_OBJECT Fdo,
14884 _In_ PIRP Irp
14885 )
14886
14887 /*++
14888
14889 Routine description:
14890
14891 This routine processes offload write requests by building the SRB
14892 for WriteUsingToken.
14893
14894 Arguments:
14895
14896 Fdo - The functional device object processing the request
14897 Irp - The Io request to be processed
14898
14899 Return Value:
14900
14901 STATUS_SUCCESS if successful, an error code otherwise
14902
14903 --*/
14904
14905 {
14906 ULONG allocationSize;
14907 PVOID buffer;
14908 ULONG bufferLength;
14909 PDEVICE_DATA_SET_RANGE dataSetRanges;
14910 ULONG dataSetRangesCount;
14911 PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
14912 ULONGLONG entireXferLen;
14913 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
14914 ULONG i;
14915 ULONGLONG logicalBlockOffset;
14916 ULONG maxBlockDescrCount;
14917 ULONGLONG maxLbaCount;
14918 POFFLOAD_WRITE_CONTEXT offloadWriteContext;
14919 PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS offloadWriteParameters;
14920 ULONG receiveTokenInformationBufferLength;
14921 NTSTATUS status;
14922 NTSTATUS tempStatus;
14923 BOOLEAN tokenInvalidated;
14924 ULONG tokenOperationBufferLength;
14925 PMDL writeUsingTokenMdl;
14926
14927 PAGED_CODE();
14928
14929 TracePrint((TRACE_LEVEL_VERBOSE,
14930 TRACE_FLAG_IOCTL,
14931 "ClasspServiceWriteUsingTokenTransferRequest (%p): Entering function. Irp %p.\n",
14932 Fdo,
14933 Irp));
14934
14935 fdoExt = Fdo->DeviceExtension;
14936 status = STATUS_SUCCESS;
14937 tempStatus = STATUS_SUCCESS;
14938 dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
14939 buffer = NULL;
14940 writeUsingTokenMdl = NULL;
14941 offloadWriteParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
14942 dataSetRanges = Add2Ptr(dsmAttributes, dsmAttributes->DataSetRangesOffset);
14943 dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
14944 logicalBlockOffset = offloadWriteParameters->TokenOffset / fdoExt->DiskGeometry.BytesPerSector;
14945 tokenInvalidated = FALSE;
14946 bufferLength = 0;
14947
14948
14949 NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
14950 NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
14951
14952 for (i = 0, entireXferLen = 0; i < dataSetRangesCount; i++) {
14953 entireXferLen += dataSetRanges[i].LengthInBytes;
14954 }
14955
14956 //
14957 // We need to split the write request based on the following hardware limitations:
14958 // 1. The size of the data buffer containing the TokenOperation command's parameters must
14959 // not exceed the MaximumTransferLength (and max physical pages) of the underlying
14960 // adapter.
14961 // 2. The number of descriptors specified in the TokenOperation command must not exceed
14962 // the MaximumRangeDescriptors.
14963 // 3. The cumulative total of the number of transfer blocks in all the descriptors in
14964 // the TokenOperation command must not exceed the MaximumTokenTransferSize.
14965 //
14966 // In addition to the above, we need to ensure that for each of the descriptors in the
14967 // TokenOperation command:
14968 // 1. The number of blocks specified is an exact multiple of the OptimalTransferLengthGranularity.
14969 // 2. The number of blocks specified is limited to the MaximumTransferLength. (We shall
14970 // however, limit the number of blocks specified in each descriptor to be a maximum of
14971 // OptimalTransferLength or MaximumTransferLength, whichever is lesser).
14972 //
14973 // Finally, we shall always send down the WriteUsingToken command using IMMED = 0 for this
14974 // release. This makes it simpler to handle multi-initiator scenarios since we won't need
14975 // to deal with the WriteUsingToken (with IMMED = 1) succeeding but ReceiveRODTokenInformation
14976 // failing due to the path/node failing, thus making it impossible to retrieve results of
14977 // the data transfer operation, or to cancel the data transfer via CopyOperationAbort.
14978 // Since a write data transfer of a large amount of data may take a long time when sent
14979 // down IMMED = 0, we shall limit the size to a maximum of 256MB even if it means truncating
14980 // the original requested size to this capped size. The application is expected to deal with
14981 // this truncation.
14982 // (NOTE: the cap of 256MB is chosen to match with the Copy Engine's chunk size).
14983 //
14984 // The LBA ranges is in DEVICE_DATA_SET_RANGE format, it needs to be converted into
14985 // WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR Block Descriptors.
14986 //
14987
14988 ClasspGetTokenOperationCommandBufferLength(Fdo,
14989 SERVICE_ACTION_WRITE_USING_TOKEN,
14990 &bufferLength,
14991 &tokenOperationBufferLength,
14992 &receiveTokenInformationBufferLength);
14993
14994 allocationSize = sizeof(OFFLOAD_WRITE_CONTEXT) + bufferLength;
14995
14996 offloadWriteContext = ExAllocatePoolWithTag(
14997 NonPagedPoolNx,
14998 allocationSize,
14999 CLASSPNP_POOL_TAG_TOKEN_OPERATION);
15000
15001 if (!offloadWriteContext) {
15002
15003 TracePrint((TRACE_LEVEL_ERROR,
15004 TRACE_FLAG_IOCTL,
15005 "ClasspServiceWriteUsingTokenTransferRequest (%p): Failed to allocate buffer for WriteUsingToken operations.\n",
15006 Fdo));
15007
15008 status = STATUS_INSUFFICIENT_RESOURCES;
15009 goto __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit;
15010 }
15011
15012 //
15013 // Only zero the context portion here. The buffer portion is zeroed for
15014 // each sub-request.
15015 //
15016 RtlZeroMemory(offloadWriteContext, sizeof(OFFLOAD_WRITE_CONTEXT));
15017
15018 offloadWriteContext->Fdo = Fdo;
15019 offloadWriteContext->OffloadWriteDsmIrp = Irp;
15020 offloadWriteContext->OperationStartTime = KeQueryInterruptTime();
15021
15022 //
15023 // The buffer for the commands is after the offloadWriteContext.
15024 //
15025 buffer = (offloadWriteContext + 1);
15026
15027 //
15028 // Set up fields that allow iterating through whole request, by issuing sub-
15029 // requests which each do some of the writing. Because of truncation by the
15030 // target, it's not known exactly how many bytes will be written by the
15031 // target in each sub-request (can be less than requested), so the
15032 // progress through the outer request must only commit the move through the
15033 // upper DSM ranges when a lower request is done and the number of written
15034 // sectors is known.
15035 //
15036
15037 NT_ASSERT(offloadWriteContext->TotalSectorsProcessedSuccessfully == 0);
15038 offloadWriteContext->TotalRequestSizeSectors = entireXferLen / fdoExt->DiskGeometry.BytesPerSector;
15039 NT_ASSERT(offloadWriteContext->DataSetRangeIndex == 0);
15040 NT_ASSERT(offloadWriteContext->DataSetRangeByteOffset == 0);
15041 offloadWriteContext->DataSetRangesCount = dataSetRangesCount;
15042
15043 offloadWriteContext->DsmAttributes = dsmAttributes;
15044 offloadWriteContext->OffloadWriteParameters = offloadWriteParameters;
15045 offloadWriteContext->DataSetRanges = dataSetRanges;
15046 offloadWriteContext->LogicalBlockOffset = logicalBlockOffset;
15047
15048 ClasspGetTokenOperationDescriptorLimits(Fdo,
15049 SERVICE_ACTION_WRITE_USING_TOKEN,
15050 tokenOperationBufferLength,
15051 &maxBlockDescrCount,
15052 &maxLbaCount);
15053
15054 if (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount && fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize) {
15055
15056 NT_ASSERT(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount <= fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize);
15057 }
15058
15059 //
15060 // We will limit the maximum data transfer in an offload write operation to:
15061 // - 64MB if OptimalTransferCount = 0
15062 // - OptimalTransferCount if < 256MB
15063 // - 256MB if OptimalTransferCount >= 256MB
15064 // - MaximumTokenTransferSize if lesser than above chosen size
15065 //
15066 if (0 == fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount) {
15067
15068 maxLbaCount = MIN(maxLbaCount, (DEFAULT_MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector));
15069
15070 } else if (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount < (MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector)) {
15071
15072 maxLbaCount = MIN(maxLbaCount, fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount);
15073
15074 } else {
15075
15076 maxLbaCount = MIN(maxLbaCount, (MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector));
15077 }
15078
15079 //
15080 // Since we do not want very fragmented files to end up causing the WriteUsingToken command to take
15081 // too long (and potentially timeout), we will limit the max number of descriptors sent down in a
15082 // command.
15083 //
15084 maxBlockDescrCount = MIN(maxBlockDescrCount, MAX_NUMBER_BLOCK_DEVICE_DESCRIPTORS);
15085
15086 TracePrint((TRACE_LEVEL_INFORMATION,
15087 TRACE_FLAG_IOCTL,
15088 "ClasspServiceWriteUsingTokenTransferRequest (%p): Using MaxBlockDescrCount %u and MaxLbaCount %I64u.\n",
15089 Fdo,
15090 maxBlockDescrCount,
15091 maxLbaCount));
15092
15093 offloadWriteContext->MaxBlockDescrCount = maxBlockDescrCount;
15094 offloadWriteContext->MaxLbaCount = maxLbaCount;
15095
15096 //
15097 // Reuse a single buffer for both TokenOperation and ReceiveTokenInformation. This has the one disadvantage
15098 // that we'll be marking the page(s) as IoWriteAccess even though we only need read access for token
15099 // operation command and we may not even need to send down a ReceiveTokenInformation (in case of a successful
15100 // synchronous transfer). However, the advantage is that we eliminate the possibility of any potential
15101 // failures when trying to allocate an MDL for the ReceiveTokenInformation later on if we need to send it.
15102 //
15103 writeUsingTokenMdl = ClasspBuildDeviceMdl(buffer, bufferLength, FALSE);
15104 if (!writeUsingTokenMdl) {
15105
15106 TracePrint((TRACE_LEVEL_ERROR,
15107 TRACE_FLAG_IOCTL,
15108 "ClasspServiceWriteUsingTokenTransferRequest (%p): Failed to allocate MDL for WriteUsingToken operations.\n",
15109 Fdo));
15110
15111 status = STATUS_INSUFFICIENT_RESOURCES;
15112 goto __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit;
15113 }
15114
15115 offloadWriteContext->WriteUsingTokenMdl = writeUsingTokenMdl;
15116
15117 //
15118 // There are potentially two approaches that we can take:
15119 // 1. Determine how many transfer packets we need (in case we need to split the request), get
15120 // them all up-front and then send down all the split WriteUsingToken commands in parallel.
15121 // The benefit of this approach is that the performance will be improved in the success case.
15122 // But error case handling becomes very complex to handle, since if one of the intermediate
15123 // write fails, there is no way to cancel the remaining writes that were sent. Waiting for
15124 // such requests to complete in geo-distributed source and target cases can be very time
15125 // consuming. The complexity gets worse in the case that the target succeeds only a partial
15126 // amount of data for one the of intermediate split commands.
15127 // [OR]
15128 // 2. Until the entire data set range is processed, build the command for as much of the range as
15129 // possible, send down a packet, and once it completes, repeat sequentially in a loop.
15130 // The advantage of this approach is its simplistic nature. In the success case, it will
15131 // be less performant as compared to the previous approach, but since the gain of offload
15132 // copy is so significant compared to native buffered-copy, the tradeoff is acceptable.
15133 // In the failure case the simplicity offers the following benefit - if any command fails,
15134 // there is no further processing that is needed. And the cumulative total bytes that succeeded
15135 // (until the failing split WriteUsingToken command) is easily tracked.
15136 //
15137 // Given the above, we're going with the second approach.
15138 //
15139
15140 NT_ASSERT(status == STATUS_SUCCESS); // so far
15141
15142 //
15143 // Save (into the offloadReadContext) any remaining things that
15144 // ClasspPopulateTokenTransferPacketDone() will need.
15145 //
15146
15147 offloadWriteContext->BufferLength = bufferLength;
15148 offloadWriteContext->ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength;
15149 offloadWriteContext->EntireXferLen = entireXferLen;
15150
15151 IoMarkIrpPending(Irp);
15152 ClasspContinueOffloadWrite(offloadWriteContext);
15153
15154 status = STATUS_PENDING;
15155 goto __ClasspServiceWriteUsingTokenTransferRequest_Exit;
15156
15157 //
15158 // Error label only - not used in success case:
15159 //
15160
15161 __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit:
15162
15163 NT_ASSERT(status != STATUS_PENDING);
15164
15165 if (offloadWriteContext != NULL) {
15166 ClasspCleanupOffloadWriteContext(offloadWriteContext);
15167 offloadWriteContext = NULL;
15168 }
15169
15170 __ClasspServiceWriteUsingTokenTransferRequest_Exit:
15171
15172 TracePrint((TRACE_LEVEL_VERBOSE,
15173 TRACE_FLAG_IOCTL,
15174 "ClasspServiceWriteUsingTokenTransferRequest (%p): Exiting function (Irp %p) with status %x.\n",
15175 Fdo,
15176 Irp,
15177 status));
15178
15179 return status;
15180 }
15181
15182
15183 VOID
15184 #ifdef _MSC_VER
15185 #pragma warning(suppress: 28194) // This function will either alias or free OffloadWriteContext
15186 #endif
15187 ClasspContinueOffloadWrite(
15188 _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext
15189 )
15190
15191 /*++
15192
15193 Routine description:
15194
15195 This routine continues an offload write operation. This routine expects the
15196 offload write operation to be set up and ready to start a WRITE USING TOKEN,
15197 but with no WRITE USING TOKEN currently in flight.
15198
15199 This routine is responsible for continuing or completing the offload write
15200 operation.
15201
15202 Arguments:
15203
15204 OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15205 write operation.
15206
15207 Return Value:
15208
15209 None.
15210
15211 --*/
15212
15213 {
15214 BOOLEAN allDataSetRangeFullyConverted;
15215 ULONG blockDescrIndex;
15216 PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescrPointer;
15217 PVOID buffer;
15218 ULONG bufferLength;
15219 ULONGLONG dataSetRangeByteOffset;
15220 ULONG dataSetRangeIndex;
15221 PDEVICE_DATA_SET_RANGE dataSetRanges;
15222 ULONG dataSetRangesCount;
15223 ULONGLONG entireXferLen;
15224 PDEVICE_OBJECT fdo;
15225 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
15226 PIRP irp;
15227 ULONG lbaCount;
15228 ULONG listIdentifier;
15229 ULONGLONG logicalBlockOffset;
15230 ULONG maxBlockDescrCount;
15231 ULONGLONG maxLbaCount;
15232 PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS offloadWriteParameters;
15233 PTRANSFER_PACKET pkt;
15234 PIRP pseudoIrp;
15235 NTSTATUS status;
15236 DEVICE_DATA_SET_RANGE tempDataSetRange;
15237 BOOLEAN tempDataSetRangeFullyConverted;
15238 PVOID tokenAscii;
15239 ULONG tokenSize;
15240 ULONGLONG totalSectorCount;
15241 ULONGLONG totalSectorsProcessedSuccessfully;
15242 ULONGLONG totalSectorsToProcess;
15243 ULONG transferSize;
15244 USHORT writeUsingTokenDataLength;
15245 USHORT writeUsingTokenDescriptorsLength;
15246 PMDL writeUsingTokenMdl;
15247
15248 tokenAscii = NULL;
15249 tokenSize = BLOCK_DEVICE_TOKEN_SIZE;
15250 tempDataSetRangeFullyConverted = FALSE;
15251 allDataSetRangeFullyConverted = FALSE;
15252 fdo = OffloadWriteContext->Fdo;
15253 fdoExt = fdo->DeviceExtension;
15254 irp = OffloadWriteContext->OffloadWriteDsmIrp;
15255 buffer = OffloadWriteContext + 1;
15256 bufferLength = OffloadWriteContext->BufferLength;
15257 dataSetRanges = OffloadWriteContext->DataSetRanges;
15258 offloadWriteParameters = OffloadWriteContext->OffloadWriteParameters;
15259 pseudoIrp = &OffloadWriteContext->PseudoIrp;
15260 writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
15261 entireXferLen = OffloadWriteContext->EntireXferLen;
15262
15263 RtlZeroMemory(buffer, bufferLength);
15264
15265 pkt = DequeueFreeTransferPacket(fdo, TRUE);
15266 if (!pkt){
15267
15268 TracePrint((TRACE_LEVEL_ERROR,
15269 TRACE_FLAG_IOCTL,
15270 "ClasspContinueOffloadWrite (%p): Failed to retrieve transfer packet for TokenOperation (WriteUsingToken) operation.\n",
15271 fdo));
15272
15273 status = STATUS_INSUFFICIENT_RESOURCES;
15274 goto __ClasspContinueOffloadWrite_ErrorExit;
15275 }
15276
15277 OffloadWriteContext->Pkt = pkt;
15278
15279 blockDescrPointer = (PBLOCK_DEVICE_RANGE_DESCRIPTOR)
15280 &((PWRITE_USING_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptor[0];
15281
15282 blockDescrIndex = 0;
15283 lbaCount = 0;
15284
15285 totalSectorsToProcess = 0;
15286
15287 maxBlockDescrCount = OffloadWriteContext->MaxBlockDescrCount;
15288 maxLbaCount = OffloadWriteContext->MaxLbaCount;
15289
15290 //
15291 // The OffloadWriteContext->DataSetRangeIndex, DataSetRangeByteOffset, and
15292 // TotalSectorsProcessedSuccessfully don't move forward until RRTI has
15293 // reported the actual amount written.
15294 //
15295 // For that reason, this function only updates
15296 // OffloadWriteContext->TotalSectorsProcessed, which tracks the number of
15297 // sectors requested to be written by the current WRITE USING TOKEN command
15298 // (not all will necessarily be written).
15299 //
15300
15301 dataSetRangeIndex = OffloadWriteContext->DataSetRangeIndex;
15302 dataSetRangesCount = OffloadWriteContext->DataSetRangesCount;
15303 dataSetRangeByteOffset = OffloadWriteContext->DataSetRangeByteOffset;
15304 totalSectorsProcessedSuccessfully = OffloadWriteContext->TotalSectorsProcessedSuccessfully;
15305
15306 //
15307 // Send WriteUsingToken commands when the buffer is full or all input entries are converted.
15308 //
15309 while (!((blockDescrIndex == maxBlockDescrCount) || // buffer full or block descriptor count reached
15310 (lbaCount == maxLbaCount) || // block LBA count reached
15311 (allDataSetRangeFullyConverted))) { // all DataSetRanges have been converted
15312
15313 NT_ASSERT(dataSetRangeIndex < dataSetRangesCount);
15314 NT_ASSERT(dataSetRangeByteOffset < dataSetRanges[dataSetRangeIndex].LengthInBytes);
15315
15316 tempDataSetRange.StartingOffset = dataSetRanges[dataSetRangeIndex].StartingOffset + dataSetRangeByteOffset;
15317 tempDataSetRange.LengthInBytes = dataSetRanges[dataSetRangeIndex].LengthInBytes - dataSetRangeByteOffset;
15318
15319 totalSectorCount = 0;
15320
15321 ClasspConvertDataSetRangeToBlockDescr(fdo,
15322 blockDescrPointer,
15323 &blockDescrIndex,
15324 maxBlockDescrCount,
15325 &lbaCount,
15326 maxLbaCount,
15327 &tempDataSetRange,
15328 &totalSectorCount);
15329
15330 tempDataSetRangeFullyConverted = (tempDataSetRange.LengthInBytes == 0) ? TRUE : FALSE;
15331
15332 allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted && ((dataSetRangeIndex + 1) == dataSetRangesCount);
15333
15334 if (tempDataSetRangeFullyConverted) {
15335 dataSetRangeIndex += 1;
15336 dataSetRangeByteOffset = 0;
15337 NT_ASSERT(dataSetRangeIndex <= dataSetRangesCount);
15338 } else {
15339 dataSetRangeByteOffset += totalSectorCount * fdoExt->DiskGeometry.BytesPerSector;
15340 NT_ASSERT(dataSetRangeByteOffset < dataSetRanges[dataSetRangeIndex].LengthInBytes);
15341 }
15342
15343 totalSectorsToProcess += totalSectorCount;
15344 }
15345
15346 //
15347 // Save the number of sectors being attempted in this WRITE USING TOKEN
15348 // command, so that a success return from the command will know how much
15349 // was written, without needing to issue a RECEIVE ROD TOKEN INFORMATION
15350 // command.
15351 //
15352 OffloadWriteContext->TotalSectorsToProcess = totalSectorsToProcess;
15353 OffloadWriteContext->TotalSectorsProcessed = 0;
15354
15355 //
15356 // Calculate transfer size, including the header
15357 //
15358 transferSize = (blockDescrIndex * sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR)) + FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
15359
15360 NT_ASSERT(transferSize <= MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH);
15361
15362 writeUsingTokenDataLength = (USHORT)transferSize - RTL_SIZEOF_THROUGH_FIELD(WRITE_USING_TOKEN_HEADER, WriteUsingTokenDataLength);
15363 REVERSE_BYTES_SHORT(((PWRITE_USING_TOKEN_HEADER)buffer)->WriteUsingTokenDataLength, &writeUsingTokenDataLength);
15364
15365 ((PWRITE_USING_TOKEN_HEADER)buffer)->Immediate = 0;
15366
15367 logicalBlockOffset = OffloadWriteContext->LogicalBlockOffset + totalSectorsProcessedSuccessfully;
15368 REVERSE_BYTES_QUAD(((PWRITE_USING_TOKEN_HEADER)buffer)->BlockOffsetIntoToken, &logicalBlockOffset);
15369
15370 RtlCopyMemory(((PWRITE_USING_TOKEN_HEADER)buffer)->Token,
15371 &offloadWriteParameters->Token,
15372 BLOCK_DEVICE_TOKEN_SIZE);
15373
15374 writeUsingTokenDescriptorsLength = (USHORT)transferSize - FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
15375 REVERSE_BYTES_SHORT(((PWRITE_USING_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptorListLength, &writeUsingTokenDescriptorsLength);
15376
15377 RtlZeroMemory(pseudoIrp, sizeof(IRP));
15378
15379 pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
15380 pseudoIrp->IoStatus.Information = 0;
15381 pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
15382 pseudoIrp->MdlAddress = writeUsingTokenMdl;
15383
15384 InterlockedCompareExchange((volatile LONG *)&TokenOperationListIdentifier, -1, MaxTokenOperationListIdentifier);
15385 listIdentifier = InterlockedIncrement((volatile LONG *)&TokenOperationListIdentifier);
15386
15387 ClasspSetupWriteUsingTokenTransferPacket(
15388 OffloadWriteContext,
15389 pkt,
15390 transferSize,
15391 (PUCHAR)buffer,
15392 pseudoIrp,
15393 listIdentifier);
15394
15395 tokenAscii = ClasspBinaryToAscii((PUCHAR)(((PWRITE_USING_TOKEN_HEADER)buffer)->Token),
15396 tokenSize,
15397 &tokenSize);
15398
15399 TracePrint((TRACE_LEVEL_INFORMATION,
15400 TRACE_FLAG_IOCTL,
15401 "ClasspContinueOffloadWrite (%p): Offloading write for %I64u bytes (versus %I64u) [via %u descriptors]. \
15402 \n\t\t\tDataLength: %u, DescriptorsLength: %u. Pkt %p (list id %x) [Token: %s]\n",
15403 fdo,
15404 totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
15405 entireXferLen,
15406 blockDescrIndex,
15407 writeUsingTokenDataLength,
15408 writeUsingTokenDescriptorsLength,
15409 pkt,
15410 listIdentifier,
15411 (tokenAscii == NULL) ? "" : tokenAscii));
15412
15413 FREE_POOL(tokenAscii);
15414
15415 OffloadWriteContext->ListIdentifier = listIdentifier;
15416
15417 SubmitTransferPacket(pkt);
15418
15419 //
15420 // ClasspWriteUsingTokenTransferPacketDone() takes care of completing the
15421 // IRP, so this function is done.
15422 //
15423
15424 return;
15425
15426 //
15427 // Error cleaup label only - not used in success case:
15428 //
15429
15430 __ClasspContinueOffloadWrite_ErrorExit:
15431
15432 NT_ASSERT(!NT_SUCCESS(status));
15433
15434 //
15435 // ClasspCompleteOffloadWrite also cleans up offloadWriteContext.
15436 //
15437
15438 ClasspCompleteOffloadWrite(OffloadWriteContext, status);
15439
15440 return;
15441 }
15442
15443
15444 VOID
15445 ClasspAdvanceOffloadWritePosition(
15446 _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
15447 _In_ ULONGLONG SectorsToAdvance
15448 )
15449
15450 /*++
15451
15452 Routine description:
15453
15454 After the target has responded to WRITE USING TOKEN with success, or RRTI
15455 with a specific TRANSFER COUNT, this routine is used to update the relative
15456 position within the overall offload write request. This position includes
15457 the TotalSectorsProcessedSuccessfully, the DataSetRangeIndex, and the
15458 DataSetRangeByteOffset.
15459
15460 The caller is responsible for continuing or completing the offload write
15461 operation (this routine doesn't do that).
15462
15463 Arguments:
15464
15465 OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15466 write operation.
15467
15468 SectorsToAdvance - The number of sectors which were just written
15469 successfully (not the total for the offload write operation overall,
15470 just the number done by the most recent WRITE USING TOKEN).
15471
15472 Return Value:
15473
15474 None.
15475
15476 --*/
15477
15478 {
15479 ULONGLONG bytesToAdvance;
15480 ULONGLONG bytesToDo;
15481 PULONGLONG dataSetRangeByteOffset;
15482 PULONG dataSetRangeIndex;
15483 PDEVICE_DATA_SET_RANGE dataSetRanges;
15484 PDEVICE_OBJECT fdo;
15485 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
15486 PULONGLONG totalSectorsProcessedSuccessfully;
15487
15488 fdo = OffloadWriteContext->Fdo;
15489 fdoExt = fdo->DeviceExtension;
15490 dataSetRanges = OffloadWriteContext->DataSetRanges;
15491 dataSetRangeByteOffset = &OffloadWriteContext->DataSetRangeByteOffset;
15492 dataSetRangeIndex = &OffloadWriteContext->DataSetRangeIndex;
15493 totalSectorsProcessedSuccessfully = &OffloadWriteContext->TotalSectorsProcessedSuccessfully;
15494 bytesToAdvance = SectorsToAdvance * fdoExt->DiskGeometry.BytesPerSector;
15495
15496 (*totalSectorsProcessedSuccessfully) += SectorsToAdvance;
15497 NT_ASSERT((*totalSectorsProcessedSuccessfully) <= OffloadWriteContext->TotalRequestSizeSectors);
15498
15499 while (bytesToAdvance != 0) {
15500 bytesToDo = dataSetRanges[*dataSetRangeIndex].LengthInBytes - *dataSetRangeByteOffset;
15501 if (bytesToDo > bytesToAdvance) {
15502 bytesToDo = bytesToAdvance;
15503 }
15504 (*dataSetRangeByteOffset) += bytesToDo;
15505 bytesToAdvance -= bytesToDo;
15506 if ((*dataSetRangeByteOffset) == dataSetRanges[*dataSetRangeIndex].LengthInBytes) {
15507 (*dataSetRangeIndex) += 1;
15508 (*dataSetRangeByteOffset) = 0;
15509 }
15510 }
15511
15512 NT_ASSERT((*dataSetRangeIndex) <= OffloadWriteContext->DataSetRangesCount);
15513
15514 return;
15515 }
15516
15517
15518 VOID
15519 ClasspWriteUsingTokenTransferPacketDone(
15520 _In_ PVOID Context
15521 )
15522
15523 /*++
15524
15525 Routine description:
15526
15527 This routine continues an offload write operation when the WRITE USING
15528 TOKEN transfer packet completes.
15529
15530 This routine may be able to determine that all requested sectors were
15531 written if the WRITE USING TOKEN completed with success, or may need to
15532 issue a RECEIVE ROD TOKEN INFORMATION if the WRITE USING TOKEN indicated
15533 check condition.
15534
15535 This routine is responsible for continuing or completing the offload write
15536 operation.
15537
15538 Arguments:
15539
15540 Context - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload write
15541 operation.
15542
15543 Return Value:
15544
15545 None.
15546
15547 --*/
15548
15549 {
15550 ULONGLONG entireXferLen;
15551 PDEVICE_OBJECT fdo;
15552 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
15553 ULONG listIdentifier;
15554 POFFLOAD_WRITE_CONTEXT offloadWriteContext;
15555 PTRANSFER_PACKET pkt;
15556 PIRP pseudoIrp;
15557 NTSTATUS status;
15558 PBOOLEAN tokenInvalidated;
15559 ULONGLONG totalSectorsToProcess;
15560
15561 offloadWriteContext = Context;
15562 pseudoIrp = &offloadWriteContext->PseudoIrp;
15563 fdo = offloadWriteContext->Fdo;
15564 fdoExt = fdo->DeviceExtension;
15565 listIdentifier = offloadWriteContext->ListIdentifier;
15566 totalSectorsToProcess = offloadWriteContext->TotalSectorsToProcess;
15567 entireXferLen = offloadWriteContext->EntireXferLen;
15568 tokenInvalidated = &offloadWriteContext->TokenInvalidated;
15569 pkt = offloadWriteContext->Pkt;
15570
15571 offloadWriteContext->Pkt = NULL;
15572
15573
15574 status = pseudoIrp->IoStatus.Status;
15575 NT_ASSERT(status != STATUS_PENDING);
15576
15577 //
15578 // If the request failed with any of the following errors, then it is meaningless to send
15579 // down a ReceiveTokenInformation (regardless of whether the transfer was requested as sync
15580 // or async), since the target has no saved information about the command:
15581 // - STATUS_INVALID_TOKEN
15582 // - STATUS_INVALID_PARAMETER
15583 //
15584 if (status == STATUS_INVALID_PARAMETER ||
15585 status == STATUS_INVALID_TOKEN) {
15586
15587 TracePrint((TRACE_LEVEL_ERROR,
15588 TRACE_FLAG_IOCTL,
15589 "ClasspWriteUsingTokenTransferPacketDone (%p): Write failed with %x (list id %x).\n",
15590 fdo,
15591 status,
15592 listIdentifier));
15593
15594 //
15595 // If the token isn't valid any longer, we should let the upper layers know so that
15596 // they don't waste time retrying the write with the same token.
15597 //
15598 if (status == STATUS_INVALID_TOKEN) {
15599
15600 *tokenInvalidated = TRUE;
15601 }
15602
15603 NT_ASSERT(status != STATUS_PENDING && !NT_SUCCESS(status));
15604 goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
15605
15606 } else if ((NT_SUCCESS(status)) &&
15607 (pkt->Srb->SrbStatus == SRB_STATUS_SUCCESS || pkt->TransferCount != 0)) {
15608
15609 //
15610 // If the TokenOperation command was sent to the target requesting synchronous data
15611 // transfer, a success indicates that the command is complete.
15612 // This could either be because of a successful completion of the entire transfer
15613 // or because of a partial transfer due to target truncation. If it is the latter,
15614 // and the information field of the sense data has returned the TransferCount, we
15615 // can avoid sending down an RRTI.
15616 //
15617 if (pkt->Srb->SrbStatus == SRB_STATUS_SUCCESS) {
15618
15619 //
15620 // The entire transfer has completed successfully.
15621 //
15622 offloadWriteContext->TotalSectorsProcessed = totalSectorsToProcess;
15623 TracePrint((TRACE_LEVEL_INFORMATION,
15624 TRACE_FLAG_IOCTL,
15625 "ClasspWriteUsingTokenTransferPacketDone (%p): Successfully wrote using token %I64u (out of %I64u) bytes (list Id %x).\n",
15626 fdo,
15627 totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
15628 entireXferLen,
15629 listIdentifier));
15630 } else {
15631
15632 //
15633 // The target has returned how much data it transferred in the response to the
15634 // WUT command itself, allowing us to optimize by removing the necessaity for
15635 // sending down an RRTI to query the TransferCount.
15636 //
15637 NT_ASSERT(pkt->TransferCount);
15638
15639 offloadWriteContext->TotalSectorsProcessed = totalSectorsToProcess = pkt->TransferCount;
15640 TracePrint((TRACE_LEVEL_INFORMATION,
15641 TRACE_FLAG_IOCTL,
15642 "ClasspWriteUsingTokenTransferPacketDone (%p): Target truncated write using token %I64u (out of %I64u) bytes (list Id %x).\n",
15643 fdo,
15644 totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
15645 entireXferLen,
15646 listIdentifier));
15647 }
15648
15649 ClasspAdvanceOffloadWritePosition(offloadWriteContext, totalSectorsToProcess);
15650
15651 NT_ASSERT(status != STATUS_PENDING);
15652
15653 //
15654 // ClasspReceiveWriteUsingTokenInformationDone() takes care of
15655 // completing the operation (eventually), so pending from point of view
15656 // of this function.
15657 //
15658
15659 ClasspReceiveWriteUsingTokenInformationDone(offloadWriteContext, status);
15660 status = STATUS_PENDING;
15661
15662 goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
15663
15664 } else {
15665
15666 //
15667 // Since the TokenOperation was failed (or the target truncated the transfer but
15668 // didn't indicate the amount), we need to send down ReceiveTokenInformation.
15669 //
15670 TracePrint((TRACE_LEVEL_ERROR,
15671 TRACE_FLAG_IOCTL,
15672 "ClasspWriteUsingTokenTransferPacketDone (%p): Write failed with status %x, %x (list id %x).\n",
15673 fdo,
15674 status,
15675 pkt->Srb->SrbStatus,
15676 listIdentifier));
15677
15678 ClasspReceiveWriteUsingTokenInformation(offloadWriteContext);
15679
15680 status = STATUS_PENDING;
15681 goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
15682 }
15683
15684 __ClasspWriteUsingTokenTransferPacketDone_Exit:
15685
15686 if (status != STATUS_PENDING) {
15687 ClasspCompleteOffloadWrite(offloadWriteContext, status);
15688 }
15689
15690 return;
15691 }
15692
15693
15694 VOID
15695 ClasspReceiveWriteUsingTokenInformationDone(
15696 _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
15697 _In_ NTSTATUS CompletionCausingStatus
15698 )
15699
15700 /*++
15701
15702 Routine description:
15703
15704 This routine continues an offload write operation when a WRITE USING TOKEN
15705 and possible associated RECEIVE ROD TOKEN INFORMATION have both fully
15706 completed and the RRTI has indicated completion of the WUT.
15707
15708 This routine checks to see if the total sectors written is already equal to
15709 the overall total requested sector count, and if so, completes the offload
15710 write operation. If not, this routine continues the operation by issuing
15711 another WUT.
15712
15713 This routine is responsible for continuing or completing the offload write
15714 operation.
15715
15716 Arguments:
15717
15718 OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15719 write operation.
15720
15721 Return Value:
15722
15723 None.
15724
15725 --*/
15726
15727 {
15728 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = OffloadWriteContext->Fdo->DeviceExtension;
15729 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
15730
15731 //
15732 // Time taken in 100ns units.
15733 //
15734 ULONGLONG durationIn100ns = (KeQueryInterruptTime() - OffloadWriteContext->OperationStartTime);
15735 ULONGLONG maxTargetDuration = fdoData->CopyOffloadMaxTargetDuration * 10ULL * 1000 * 1000;
15736
15737 NT_ASSERT(
15738 OffloadWriteContext->TotalSectorsProcessedSuccessfully <=
15739 OffloadWriteContext->TotalRequestSizeSectors);
15740
15741
15742 if (OffloadWriteContext->TotalSectorsProcessedSuccessfully == OffloadWriteContext->TotalRequestSizeSectors) {
15743
15744 ClasspCompleteOffloadWrite(OffloadWriteContext, CompletionCausingStatus);
15745
15746 goto __ClasspReceiveWriteUsingTokenInformationDone_Exit;
15747 }
15748
15749 //
15750 // Since we don't want a layered timeout mechanism (e.g. guest and parent OS in Hyper-V scenarios)
15751 // to cause a SCSI timeout for the higher layer token operations.
15752 //
15753 if (maxTargetDuration <= durationIn100ns) {
15754
15755 TracePrint((TRACE_LEVEL_WARNING,
15756 TRACE_FLAG_IOCTL,
15757 "ClasspReceiveWriteUsingTokenInformationDone (%p): Truncating write (list id %x) because of max-duration-rule.\n",
15758 OffloadWriteContext->Fdo,
15759 OffloadWriteContext->ListIdentifier));
15760
15761 //
15762 // We could technically pass in STATUS_IO_OPERATION_TIMEOUT, but ClasspCompleteOffloadWrite
15763 // won't end up doing anything (useful) with this status, since some bytes would already
15764 // have been transferred.
15765 //
15766 ClasspCompleteOffloadWrite(OffloadWriteContext, STATUS_UNSUCCESSFUL);
15767
15768 goto __ClasspReceiveWriteUsingTokenInformationDone_Exit;
15769 }
15770
15771 NT_ASSERT(
15772 OffloadWriteContext->TotalSectorsProcessedSuccessfully <
15773 OffloadWriteContext->TotalRequestSizeSectors);
15774
15775 //
15776 // Keep going with the next sub-request.
15777 //
15778
15779 ClasspContinueOffloadWrite(OffloadWriteContext);
15780
15781 __ClasspReceiveWriteUsingTokenInformationDone_Exit:
15782
15783 return;
15784 }
15785
15786
15787 VOID
15788 ClasspCompleteOffloadWrite(
15789 _In_ __drv_freesMem(Mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
15790 _In_ NTSTATUS CompletionCausingStatus
15791 )
15792
15793 /*++
15794
15795 Routine description:
15796
15797 This routine is used to complete an offload write operation.
15798
15799 The input CompletionCausingStatus doesn't necessarily drive the completion
15800 status of the offload write operation overall, if the offload write
15801 operation overall has previously written some sectors successfully.
15802
15803 This routine completes the offload write operation.
15804
15805 Arguments:
15806
15807 OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15808 write operation.
15809
15810 CompletionCausingStatus - Status code indicating a reason that the offload
15811 write operation is completing. For success this will be STATUS_SUCCESS,
15812 but for a failure, this status will indicate what failure occurred.
15813 This status doesn't directly propagate to the completion status of the
15814 overall offload write operation if this status is failure and the
15815 overall offload write operation has already previously written some
15816 sectors successfully.
15817
15818 Return Value:
15819
15820 None.
15821
15822 --*/
15823
15824 {
15825 PDEVICE_OBJECT fdo;
15826 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
15827 PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
15828 PULONGLONG totalSectorsProcessedSuccessfully;
15829 ULONGLONG entireXferLen;
15830 PIRP irp;
15831 PBOOLEAN tokenInvalidated;
15832 ULONG listIdentifier;
15833 ULONGLONG totalSectorsProcessed;
15834 NTSTATUS status;
15835 ULONGLONG totalBytesProcessed;
15836
15837 fdo = OffloadWriteContext->Fdo;
15838 fdoExt = fdo->DeviceExtension;
15839 dsmAttributes = OffloadWriteContext->DsmAttributes;
15840 totalSectorsProcessedSuccessfully = &OffloadWriteContext->TotalSectorsProcessedSuccessfully;
15841 entireXferLen = OffloadWriteContext->EntireXferLen;
15842 irp = OffloadWriteContext->OffloadWriteDsmIrp;
15843 tokenInvalidated = &OffloadWriteContext->TokenInvalidated;
15844 listIdentifier = OffloadWriteContext->ListIdentifier;
15845 totalSectorsProcessed = OffloadWriteContext->TotalSectorsProcessed;
15846 status = CompletionCausingStatus;
15847
15848 ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags = 0;
15849 ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->Reserved = 0;
15850
15851 totalBytesProcessed = (*totalSectorsProcessedSuccessfully) * fdoExt->DiskGeometry.BytesPerSector;
15852
15853 TracePrint((totalBytesProcessed == entireXferLen ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
15854 TRACE_FLAG_IOCTL,
15855 "ClasspCompleteOffloadWrite (%p): %ws wrote using token %I64u (out of %I64u) bytes (Irp %p).\n",
15856 fdo,
15857 NT_SUCCESS(status) ? L"Successful" : L"Failed",
15858 totalBytesProcessed,
15859 entireXferLen,
15860 irp));
15861
15862 if (totalBytesProcessed > 0 && totalBytesProcessed < entireXferLen) {
15863 SET_FLAG(((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags, STORAGE_OFFLOAD_WRITE_RANGE_TRUNCATED);
15864 }
15865 if (*tokenInvalidated) {
15866 SET_FLAG(((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags, STORAGE_OFFLOAD_TOKEN_INVALID);
15867 }
15868 ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->LengthCopied = totalBytesProcessed;
15869
15870
15871 if (!NT_SUCCESS(status)) {
15872
15873 TracePrint((TRACE_LEVEL_WARNING,
15874 TRACE_FLAG_IOCTL,
15875 "ClasspCompleteOffloadWrite (%p): TokenOperation for WriteUsingToken (list Id %u) completed with %x writing %I64u blocks (currentTotal %I64u blocks).\n",
15876 fdo,
15877 listIdentifier,
15878 status,
15879 totalSectorsProcessed,
15880 *totalSectorsProcessedSuccessfully));
15881
15882 //
15883 // Even if target returned an error, from the OS upper layers' perspective,
15884 // it is a success (with truncation) if any data at all was written.
15885 //
15886 if (*totalSectorsProcessedSuccessfully) {
15887 status = STATUS_SUCCESS;
15888 }
15889 }
15890
15891 irp->IoStatus.Information = sizeof(STORAGE_OFFLOAD_WRITE_OUTPUT);
15892
15893 ClasspCompleteOffloadRequest(fdo, irp, status);
15894 ClasspCleanupOffloadWriteContext(OffloadWriteContext);
15895 OffloadWriteContext = NULL;
15896
15897 return;
15898 }
15899
15900
15901 VOID
15902 ClasspCleanupOffloadWriteContext(
15903 _In_ __drv_freesMem(mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext
15904 )
15905
15906 /*++
15907
15908 Routine description:
15909
15910 This routine cleans up an offload write context.
15911
15912 Arguments:
15913
15914 OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15915 write operation.
15916
15917 Return Value:
15918
15919 None.
15920
15921 --*/
15922
15923 {
15924 PMDL writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
15925
15926 if (writeUsingTokenMdl) {
15927 ClasspFreeDeviceMdl(writeUsingTokenMdl);
15928 }
15929 FREE_POOL(OffloadWriteContext);
15930
15931 return;
15932 }
15933
15934
15935 _IRQL_requires_same_
15936 VOID
15937 ClasspReceiveWriteUsingTokenInformation(
15938 _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
15939 )
15940
15941 /*++
15942
15943 Routine description:
15944
15945 This routine retrieves the token after a WriteUsingToken command
15946 has been sent down in case of an error or if there is a need to
15947 poll for the result.
15948
15949 This routine is responsible for continuing or completing the offload write
15950 operation.
15951
15952 Arguments:
15953
15954 OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15955 write operation.
15956
15957 Return Value:
15958
15959 None.
15960
15961 --*/
15962
15963 {
15964 PVOID buffer;
15965 ULONG bufferLength;
15966 ULONG cdbLength;
15967 PDEVICE_OBJECT fdo;
15968 PIRP irp;
15969 ULONG listIdentifier;
15970 PTRANSFER_PACKET pkt;
15971 PIRP pseudoIrp;
15972 ULONG receiveTokenInformationBufferLength;
15973 PSCSI_REQUEST_BLOCK srb;
15974 NTSTATUS status;
15975 ULONG tempSizeUlong;
15976 PMDL writeUsingTokenMdl;
15977
15978 fdo = OffloadWriteContext->Fdo;
15979 irp = OffloadWriteContext->OffloadWriteDsmIrp;
15980 pseudoIrp = &OffloadWriteContext->PseudoIrp;
15981 buffer = OffloadWriteContext + 1;
15982 bufferLength = OffloadWriteContext->BufferLength;
15983 receiveTokenInformationBufferLength = OffloadWriteContext->ReceiveTokenInformationBufferLength;
15984 writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
15985 listIdentifier = OffloadWriteContext->ListIdentifier;
15986 srb = &OffloadWriteContext->Srb;
15987 status = STATUS_SUCCESS;
15988
15989 TracePrint((TRACE_LEVEL_VERBOSE,
15990 TRACE_FLAG_IOCTL,
15991 "ClasspReceiveWriteUsingTokenInformation (%p): Entering function. Irp %p\n",
15992 fdo,
15993 irp));
15994
15995 //
15996 // The WRITE USING TOKEN wasn't immediately fully successful, so that means
15997 // the only way to find out how many sectors were processed by the WRITE
15998 // USING TOKEN is to get a successful RECEIVE ROD TOKEN INFORMATION that
15999 // indicates the operation is complete.
16000 //
16001
16002 NT_ASSERT(OffloadWriteContext->TotalSectorsProcessed == 0);
16003
16004 pkt = DequeueFreeTransferPacket(fdo, TRUE);
16005
16006 if (!pkt) {
16007
16008 TracePrint((TRACE_LEVEL_ERROR,
16009 TRACE_FLAG_IOCTL,
16010 "ClasspReceiveWriteUsingTokenInformation (%p): Failed to retrieve transfer packet for ReceiveTokenInformation (WriteUsingToken) operation.\n",
16011 fdo));
16012
16013 status = STATUS_INSUFFICIENT_RESOURCES;
16014
16015 goto __ClasspReceiveWriteUsingTokenInformation_ErrorExit;
16016 }
16017
16018 RtlZeroMemory(buffer, bufferLength);
16019
16020 tempSizeUlong = receiveTokenInformationBufferLength - 4;
16021 REVERSE_BYTES(((PRECEIVE_TOKEN_INFORMATION_HEADER)buffer)->AvailableData, &tempSizeUlong);
16022
16023 RtlZeroMemory(pseudoIrp, sizeof(IRP));
16024
16025 pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
16026 pseudoIrp->IoStatus.Information = 0;
16027 pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
16028 pseudoIrp->MdlAddress = writeUsingTokenMdl;
16029
16030 ClasspSetupReceiveWriteUsingTokenInformationTransferPacket(
16031 OffloadWriteContext,
16032 pkt,
16033 bufferLength,
16034 (PUCHAR)buffer,
16035 pseudoIrp,
16036 listIdentifier);
16037
16038 //
16039 // Cache away the CDB as it may be required for forwarded sense data
16040 // after this command completes.
16041 //
16042 RtlZeroMemory(srb, sizeof(*srb));
16043 cdbLength = SrbGetCdbLength(pkt->Srb);
16044 if (cdbLength <= 16) {
16045 RtlCopyMemory(&srb->Cdb, SrbGetCdb(pkt->Srb), cdbLength);
16046 }
16047
16048 SubmitTransferPacket(pkt);
16049
16050 return;
16051
16052 //
16053 // Error label only - not used by success cases:
16054 //
16055
16056 __ClasspReceiveWriteUsingTokenInformation_ErrorExit:
16057
16058 NT_ASSERT(!NT_SUCCESS(status));
16059
16060 //
16061 // ClasspCompleteOffloadWrite also cleans up OffloadWriteContext.
16062 //
16063
16064 ClasspCompleteOffloadWrite(OffloadWriteContext, status);
16065
16066 return;
16067 }
16068
16069
16070 VOID
16071 ClasspReceiveWriteUsingTokenInformationTransferPacketDone(
16072 _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
16073 )
16074
16075 /*++
16076
16077 Routine description:
16078
16079 This routine continues an offload write operation when a RECEIVE ROD TOKEN
16080 INFORMATION transfer packet is done.
16081
16082 This routine may need to send another RRTI, or it may be able to indicate
16083 that this WUT is done via call to
16084 ClasspReceiveWriteUsingTokenInformationDone().
16085
16086 This routine is responsible for continuing or completing the offload write
16087 operation.
16088
16089 Arguments:
16090
16091 OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
16092 write operation.
16093
16094 Return Value:
16095
16096 None.
16097
16098 --*/
16099
16100 {
16101 ULONG availableData;
16102 PVOID buffer;
16103 UCHAR completionStatus;
16104 ULONG estimatedRetryInterval;
16105 PDEVICE_OBJECT fdo;
16106 PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
16107 PIRP irp;
16108 ULONG listIdentifier;
16109 BOOLEAN operationCompleted;
16110 UCHAR operationStatus;
16111 PIRP pseudoIrp;
16112 USHORT segmentsProcessed;
16113 PSENSE_DATA senseData;
16114 ULONG senseDataFieldLength;
16115 UCHAR senseDataLength;
16116 PSCSI_REQUEST_BLOCK srb;
16117 NTSTATUS status;
16118 ULONG tokenDescriptorLength;
16119 PRECEIVE_TOKEN_INFORMATION_HEADER tokenInformationResults;
16120 PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER tokenInformationResponsePadding;
16121 PBOOLEAN tokenInvalidated;
16122 PULONGLONG totalSectorsProcessed;
16123 ULONGLONG totalSectorsToProcess;
16124 ULONGLONG transferBlockCount;
16125
16126 fdo = OffloadWriteContext->Fdo;
16127 fdoExt = fdo->DeviceExtension;
16128 listIdentifier = OffloadWriteContext->ListIdentifier;
16129 totalSectorsProcessed = &OffloadWriteContext->TotalSectorsProcessed;
16130 totalSectorsToProcess = OffloadWriteContext->TotalSectorsToProcess;
16131 irp = OffloadWriteContext->OffloadWriteDsmIrp;
16132 pseudoIrp = &OffloadWriteContext->PseudoIrp;
16133 tokenInvalidated = &OffloadWriteContext->TokenInvalidated;
16134 srb = &OffloadWriteContext->Srb;
16135 operationCompleted = FALSE;
16136 buffer = OffloadWriteContext + 1;
16137 tokenInformationResults = (PRECEIVE_TOKEN_INFORMATION_HEADER)buffer;
16138 senseData = (PSENSE_DATA)((PUCHAR)tokenInformationResults + FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData));
16139 transferBlockCount = 0;
16140 tokenInformationResponsePadding = NULL;
16141 tokenDescriptorLength = 0;
16142
16143 NT_ASSERT((*totalSectorsProcessed) == 0);
16144
16145 OffloadWriteContext->Pkt = NULL;
16146
16147
16148 status = pseudoIrp->IoStatus.Status;
16149 NT_ASSERT(status != STATUS_PENDING);
16150
16151 //
16152 // The buffer we hand allows for the max sizes for all the fields whereas the returned
16153 // data may be lesser (e.g. sense data info will almost never be MAX_SENSE_BUFFER_SIZE, etc.
16154 // so handle underrun "error"
16155 //
16156 if (status == STATUS_DATA_OVERRUN) {
16157
16158 status = STATUS_SUCCESS;
16159 }
16160
16161 if (!NT_SUCCESS(status)) {
16162 TracePrint((TRACE_LEVEL_ERROR,
16163 TRACE_FLAG_IOCTL,
16164 "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Failed with %x to retrieve write results for list Id %x for data size %I64u bytes.\n",
16165 fdo,
16166 status,
16167 listIdentifier,
16168 totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector));
16169
16170 NT_ASSERT((*totalSectorsProcessed) == 0);
16171
16172 goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
16173 }
16174
16175 REVERSE_BYTES(&availableData, &tokenInformationResults->AvailableData);
16176
16177 NT_ASSERT(availableData <= FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) + MAX_SENSE_BUFFER_SIZE);
16178
16179 NT_ASSERT(tokenInformationResults->ResponseToServiceAction == SERVICE_ACTION_WRITE_USING_TOKEN);
16180
16181 operationStatus = tokenInformationResults->OperationStatus;
16182 operationCompleted = ClasspIsTokenOperationComplete(operationStatus);
16183 NT_ASSERT(operationCompleted);
16184
16185 REVERSE_BYTES(&estimatedRetryInterval, &tokenInformationResults->EstimatedStatusUpdateDelay);
16186
16187 completionStatus = tokenInformationResults->CompletionStatus;
16188
16189 senseDataFieldLength = tokenInformationResults->SenseDataFieldLength;
16190 senseDataLength = tokenInformationResults->SenseDataLength;
16191 NT_ASSERT(senseDataFieldLength >= senseDataLength);
16192
16193 tokenInformationResponsePadding = (PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER)((PUCHAR)tokenInformationResults +
16194 FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) +
16195 tokenInformationResults->SenseDataFieldLength);
16196
16197 REVERSE_BYTES(&tokenDescriptorLength, &tokenInformationResponsePadding->TokenDescriptorsLength);
16198 NT_ASSERT(tokenDescriptorLength == 0);
16199
16200 NT_ASSERT(tokenInformationResults->TransferCountUnits == TRANSFER_COUNT_UNITS_NUMBER_BLOCKS);
16201 REVERSE_BYTES_QUAD(&transferBlockCount, &tokenInformationResults->TransferCount);
16202
16203 REVERSE_BYTES_SHORT(&segmentsProcessed, &tokenInformationResults->SegmentsProcessed);
16204 NT_ASSERT(segmentsProcessed == 0);
16205
16206 if (operationCompleted) {
16207
16208 if (transferBlockCount > totalSectorsToProcess) {
16209
16210 //
16211 // Buggy or hostile target. Don't let it claim more was procesed
16212 // than was requested. Since this is likely a bug and it's unknown
16213 // how much was actually transferred, assume no data was
16214 // transferred.
16215 //
16216
16217 NT_ASSERT(transferBlockCount <= totalSectorsToProcess);
16218 transferBlockCount = 0;
16219 }
16220
16221 if (operationStatus != OPERATION_COMPLETED_WITH_SUCCESS &&
16222 operationStatus != OPERATION_COMPLETED_WITH_RESIDUAL_DATA) {
16223
16224 //
16225 // Assert on buggy response from target, but in any case, make sure not
16226 // to claim that any data was written.
16227 //
16228
16229 NT_ASSERT(transferBlockCount == 0);
16230 transferBlockCount = 0;
16231 }
16232
16233 //
16234 // Since the TokenOperation was sent down synchronously but failed, the operation is complete as soon as the
16235 // ReceiveTokenInformation command returns.
16236 //
16237
16238 NT_ASSERT((*totalSectorsProcessed) == 0);
16239 *totalSectorsProcessed = transferBlockCount;
16240 ClasspAdvanceOffloadWritePosition(OffloadWriteContext, transferBlockCount);
16241
16242 if (transferBlockCount < totalSectorsToProcess) {
16243
16244 NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
16245 operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
16246 operationStatus == OPERATION_TERMINATED);
16247
16248 } else {
16249
16250 NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_SUCCESS);
16251 }
16252
16253 TracePrint((transferBlockCount == totalSectorsToProcess ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
16254 TRACE_FLAG_IOCTL,
16255 "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): %wsSuccessfully wrote (for list Id %x) for data size %I64u bytes\n",
16256 fdo,
16257 transferBlockCount == totalSectorsToProcess ? L"" : L"Target truncated write. ",
16258 listIdentifier,
16259 (*totalSectorsProcessed) * fdoExt->DiskGeometry.BytesPerSector));
16260
16261 //
16262 // Operation that completes with success can have sense data (for target to pass on some extra info)
16263 // but we don't care about such sense info.
16264 // Operation that complete but not with success, may not have sense data associated, but may
16265 // have valid CompletionStatus.
16266 //
16267 // The "status" may be overriden by ClassInterpretSenseInfo(). Final
16268 // status is determined a bit later - this is just the default status
16269 // when ClassInterpretSenseInfo() doesn't get to run here.
16270 //
16271 status = STATUS_SUCCESS;
16272 if (operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
16273 operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
16274 operationStatus == OPERATION_TERMINATED) {
16275
16276 SrbSetScsiStatus((PSTORAGE_REQUEST_BLOCK_HEADER)srb, completionStatus);
16277
16278 if (senseDataLength) {
16279
16280 ULONG retryInterval;
16281
16282 NT_ASSERT(senseDataLength <= sizeof(SENSE_DATA));
16283
16284 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
16285
16286 srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
16287 SrbSetSenseInfoBuffer((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseData);
16288 SrbSetSenseInfoBufferLength((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseDataLength);
16289
16290 ClassInterpretSenseInfo(fdo,
16291 srb,
16292 IRP_MJ_SCSI,
16293 0,
16294 0,
16295 &status,
16296 &retryInterval);
16297
16298 TracePrint((TRACE_LEVEL_WARNING,
16299 TRACE_FLAG_IOCTL,
16300 "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Reason for truncation/failure: %x - for list Id %x for data size %I64u bytes.\n",
16301 fdo,
16302 status,
16303 listIdentifier,
16304 transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
16305
16306 //
16307 // If the token isn't valid any longer, we should let the upper layers know so that
16308 // they don't waste time retrying the write with the same token.
16309 //
16310 if (status == STATUS_INVALID_TOKEN) {
16311
16312 *tokenInvalidated = TRUE;
16313 }
16314 } else {
16315
16316 TracePrint((TRACE_LEVEL_WARNING,
16317 TRACE_FLAG_IOCTL,
16318 "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): No sense data available but reason for truncation/failure, possibly: %x - for list Id %x for data size %I64u bytes.\n",
16319 fdo,
16320 completionStatus,
16321 listIdentifier,
16322 transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
16323 }
16324 }
16325
16326 //
16327 // Initialize status. Upper layer needs to know if command failed because it
16328 // timed out without doing any writing. ClasspCompleteOffloadWrite() will
16329 // force status to success if any data was written, so it's this function's
16330 // job to set the status appropriately based on the outcome of this
16331 // WRITE USING TOKEN command, and then ClasspCompleteOffloadWrite()
16332 // can override with success if previos WRITE USING TOKEN commands
16333 // issued for the same upper request were able to write some data.
16334 //
16335 if (transferBlockCount != 0) {
16336 status = STATUS_SUCCESS;
16337 } else {
16338
16339 if (NT_SUCCESS(status)) {
16340 //
16341 // Make sure status is a failing status, without throwing away an
16342 // already-failing status obtained from sense data.
16343 //
16344 status = STATUS_UNSUCCESSFUL;
16345 }
16346 }
16347
16348 NT_ASSERT(status != STATUS_PENDING);
16349
16350 if (!NT_SUCCESS(status)) {
16351 goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
16352 }
16353
16354 ClasspReceiveWriteUsingTokenInformationDone(OffloadWriteContext, status);
16355 status = STATUS_PENDING;
16356 goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_Exit;
16357
16358 } else {
16359
16360 status = STATUS_UNSUCCESSFUL;
16361
16362 goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
16363 }
16364
16365 //
16366 // Error label only - not used in success case:
16367 //
16368
16369 __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit:
16370
16371 NT_ASSERT(!NT_SUCCESS(status));
16372
16373 ClasspCompleteOffloadWrite(OffloadWriteContext, status);
16374
16375 __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_Exit:
16376
16377 //
16378 // Due to tracing a potentially freed pointer value "Irp", this trace could
16379 // be delayed beyond another offload op picking up the same pointer value
16380 // for its Irp.
16381 //
16382
16383 TracePrint((TRACE_LEVEL_VERBOSE,
16384 TRACE_FLAG_IOCTL,
16385 "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Exiting function (Irp %p) with status %x.\n",
16386 fdo,
16387 irp,
16388 status));
16389
16390 return;
16391 }
16392
16393
16394 NTSTATUS
16395 ClasspRefreshFunctionSupportInfo(
16396 _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
16397 _In_ BOOLEAN ForceQuery
16398 )
16399 /*
16400 Routine Description:
16401
16402 This function is to update various properties described in FDO extension's
16403 CLASS_FUNCTION_SUPPORT_INFO structure by requerying the device's VPD pages.
16404 Although this function is capable of updating any properties in the
16405 CLASS_FUNCTION_SUPPORT_INFO structure, it will initially support only
16406 a small number of proporties in the block limit data
16407
16408 Arguments:
16409
16410 FdoExtension : FDO extension
16411
16412 ForceQuery : TRUE if the caller wants to force a query of the device's
16413 VPD pages. Otherwise, the function may use cached data.
16414
16415 Return Value:
16416
16417 STATUS_SUCCESS or an error status
16418
16419 --*/
16420 {
16421 NTSTATUS status;
16422 PSCSI_REQUEST_BLOCK srb = NULL;
16423 ULONG srbSize;
16424 CLASS_VPD_B0_DATA blockLimitsDataNew;
16425 PCLASS_VPD_B0_DATA blockLimitsDataOriginal;
16426 KLOCK_QUEUE_HANDLE lockHandle;
16427 ULONG generationCount;
16428 ULONG changeRequestCount;
16429
16430 //
16431 // ChangeRequestCount is incremented every time we get an unit attention with
16432 // SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED. GenerationCount will be set to
16433 // ChangeRequestCount after CLASS_FUNCTION_SUPPORT_INFO is refreshed with the latest
16434 // VPD data. i.e. if both values are the same, data in
16435 // CLASS_FUNCTION_SUPPORT_INFO is current
16436 //
16437
16438 generationCount = FdoExtension->FunctionSupportInfo->GenerationCount;
16439 changeRequestCount = FdoExtension->FunctionSupportInfo->ChangeRequestCount;
16440 if (!ForceQuery && generationCount == changeRequestCount) {
16441 return STATUS_SUCCESS;
16442 }
16443
16444 //
16445 // Allocate an SRB for querying the device for LBP-related info if either
16446 // the Logical Block Provisioning (0xB2) or Block Limits (0xB0) VPD page
16447 // exists.
16448 //
16449 if ((FdoExtension->AdapterDescriptor != NULL) &&
16450 (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
16451 srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
16452 } else {
16453 srbSize = sizeof(SCSI_REQUEST_BLOCK);
16454 }
16455
16456 srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, '1DcS');
16457 if (srb == NULL) {
16458 return STATUS_INSUFFICIENT_RESOURCES;
16459 }
16460
16461 status = ClasspDeviceGetBlockLimitsVPDPage(FdoExtension,
16462 srb,
16463 srbSize,
16464 &blockLimitsDataNew);
16465
16466 if (NT_SUCCESS(status)) {
16467
16468 KeAcquireInStackQueuedSpinLock(&FdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
16469
16470 //
16471 // If the generationCount didn't change since we looked at it last time, it means
16472 // no one has tried to update the CLASS_FUNCTION_SUPPORT_INFO data; otherwise, someone
16473 // else has beat us to it.
16474 //
16475 if (generationCount == FdoExtension->FunctionSupportInfo->GenerationCount) {
16476
16477 blockLimitsDataOriginal = &FdoExtension->FunctionSupportInfo->BlockLimitsData;
16478 if (blockLimitsDataOriginal->CommandStatus == -1) {
16479 //
16480 // CommandStatus == -1 means this is the first time we have
16481 // gotten the block limits data.
16482 //
16483 *blockLimitsDataOriginal = blockLimitsDataNew;
16484 } else {
16485 //
16486 // We only expect the Optimal Unmap Granularity (and alignment)
16487 // to change, so those are the only parameters we update.
16488 //
16489 blockLimitsDataOriginal->UGAVALID = blockLimitsDataNew.UGAVALID;
16490 blockLimitsDataOriginal->UnmapGranularityAlignment = blockLimitsDataNew.UnmapGranularityAlignment;
16491 blockLimitsDataOriginal->OptimalUnmapGranularity = blockLimitsDataNew.OptimalUnmapGranularity;
16492 }
16493 FdoExtension->FunctionSupportInfo->GenerationCount = changeRequestCount;
16494 }
16495
16496 KeReleaseInStackQueuedSpinLock(&lockHandle);
16497 }
16498
16499 FREE_POOL(srb);
16500 return status;
16501 }
16502
16503 NTSTATUS
16504 ClasspBlockLimitsDataSnapshot(
16505 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
16506 _In_ BOOLEAN ForceQuery,
16507 _Out_ PCLASS_VPD_B0_DATA BlockLimitsData,
16508 _Out_ PULONG GenerationCount
16509 )
16510 /*
16511 Routine Description:
16512
16513 This function is to get a copy of the latest block limits data.
16514
16515 When this function is called multiple times, GenerationCount can change (value always goes up)
16516 while BlockLimitsData stays the same. In this case, the caller should assume BlockLimitsData
16517 has changed to different values and eventually changed back to the same state when the first
16518 call to this function was made.
16519
16520 Arguments:
16521
16522 FdoExtension : FDO extension
16523
16524 ForceQuery : TRUE if the caller wants to force a query of the device's
16525 VPD pages. Otherwise, the function may use cached data.
16526
16527 BlockLimitsData : pointer to memory that will receive the block limits data
16528
16529 GenerationCount : generation count of the block limit data.
16530
16531 DataIsOutdated: set to TRUE if the BlockLimitsData is old but this function fails to
16532 query the latest data from the device due to insufficient resources
16533
16534 Return Value:
16535
16536 STATUS_SUCCESS or an error status
16537
16538 --*/
16539 {
16540 NTSTATUS status;
16541 KLOCK_QUEUE_HANDLE lockHandle;
16542
16543 status = ClasspRefreshFunctionSupportInfo(FdoExtension, ForceQuery);
16544
16545 KeAcquireInStackQueuedSpinLock(&FdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
16546 *BlockLimitsData = FdoExtension->FunctionSupportInfo->BlockLimitsData;
16547 *GenerationCount = FdoExtension->FunctionSupportInfo->GenerationCount;
16548 KeReleaseInStackQueuedSpinLock(&lockHandle);
16549
16550 return status;
16551 }
16552
16553