[CMAKE]
[reactos.git] / drivers / storage / classpnp / class.c
1 /*++
2
3 Copyright (C) Microsoft Corporation, 1991 - 1999
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 #include "classp.h"
26 #include "debug.h"
27
28 #ifdef ALLOC_PRAGMA
29 #pragma alloc_text(INIT, DriverEntry)
30 #pragma alloc_text(PAGE, ClassAddDevice)
31 #pragma alloc_text(PAGE, ClassClaimDevice)
32 #pragma alloc_text(PAGE, ClassCreateDeviceObject)
33 #pragma alloc_text(PAGE, ClassDispatchPnp)
34 #pragma alloc_text(PAGE, ClassGetDescriptor)
35 #pragma alloc_text(PAGE, ClassGetPdoId)
36 #pragma alloc_text(PAGE, ClassInitialize)
37 #pragma alloc_text(PAGE, ClassInitializeEx)
38 #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
39 #pragma alloc_text(PAGE, ClassMarkChildMissing)
40 #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
41 #pragma alloc_text(PAGE, ClassModeSense)
42 #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
43 #pragma alloc_text(PAGE, ClassPnpStartDevice)
44 #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
45 #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
46 #pragma alloc_text(PAGE, ClassRemoveDevice)
47 #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
48 #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
49 #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
50 #pragma alloc_text(PAGE, ClassUnload)
51 #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
52 #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
53 #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
54 #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
55 #pragma alloc_text(PAGE, ClasspScanForClassHacks)
56 #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
57 #endif
58
59 ULONG ClassPnpAllowUnload = TRUE;
60
61
62 #define FirstDriveLetter 'C'
63 #define LastDriveLetter 'Z'
64
65
66
67 /*++////////////////////////////////////////////////////////////////////////////
68
69 DriverEntry()
70
71 Routine Description:
72
73 Temporary entry point needed to initialize the class system dll.
74 It doesn't do anything.
75
76 Arguments:
77
78 DriverObject - Pointer to the driver object created by the system.
79
80 Return Value:
81
82 STATUS_SUCCESS
83
84 --*/
85 NTSTATUS
86 NTAPI
87 DriverEntry(
88 IN PDRIVER_OBJECT DriverObject,
89 IN PUNICODE_STRING RegistryPath
90 )
91 {
92 return STATUS_SUCCESS;
93 }
94
95 \f
96
97
98 /*++////////////////////////////////////////////////////////////////////////////
99
100 ClassInitialize()
101
102 Routine Description:
103
104 This routine is called by a class driver during its
105 DriverEntry routine to initialize the driver.
106
107 Arguments:
108
109 Argument1 - Driver Object.
110 Argument2 - Registry Path.
111 InitializationData - Device-specific driver's initialization data.
112
113 Return Value:
114
115 A valid return code for a DriverEntry routine.
116
117 --*/
118 ULONG
119 ClassInitialize(
120 IN PVOID Argument1,
121 IN PVOID Argument2,
122 IN PCLASS_INIT_DATA InitializationData
123 )
124 {
125 PDRIVER_OBJECT DriverObject = Argument1;
126 PUNICODE_STRING RegistryPath = Argument2;
127
128 PCLASS_DRIVER_EXTENSION driverExtension;
129
130 NTSTATUS status;
131
132 PAGED_CODE();
133
134 DebugPrint((3,"\n\nSCSI Class Driver\n"));
135
136 ClasspInitializeDebugGlobals();
137
138 //
139 // Validate the length of this structure. This is effectively a
140 // version check.
141 //
142
143 if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
144
145 //
146 // This DebugPrint is to help third-party driver writers
147 //
148
149 DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
150 return (ULONG) STATUS_REVISION_MISMATCH;
151 }
152
153 //
154 // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
155 // are not required entry points.
156 //
157
158 if ((!InitializationData->FdoData.ClassDeviceControl) ||
159 (!((InitializationData->FdoData.ClassReadWriteVerification) ||
160 (InitializationData->ClassStartIo))) ||
161 (!InitializationData->ClassAddDevice) ||
162 (!InitializationData->FdoData.ClassStartDevice)) {
163
164 //
165 // This DebugPrint is to help third-party driver writers
166 //
167
168 DebugPrint((0,
169 "ClassInitialize: Class device-specific driver missing required "
170 "FDO entry\n"));
171
172 return (ULONG) STATUS_REVISION_MISMATCH;
173 }
174
175 if ((InitializationData->ClassEnumerateDevice) &&
176 ((!InitializationData->PdoData.ClassDeviceControl) ||
177 (!InitializationData->PdoData.ClassStartDevice) ||
178 (!((InitializationData->PdoData.ClassReadWriteVerification) ||
179 (InitializationData->ClassStartIo))))) {
180
181 //
182 // This DebugPrint is to help third-party driver writers
183 //
184
185 DebugPrint((0, "ClassInitialize: Class device-specific missing "
186 "required PDO entry\n"));
187
188 return (ULONG) STATUS_REVISION_MISMATCH;
189 }
190
191 if((InitializationData->FdoData.ClassStopDevice == NULL) ||
192 ((InitializationData->ClassEnumerateDevice != NULL) &&
193 (InitializationData->PdoData.ClassStopDevice == NULL))) {
194
195 //
196 // This DebugPrint is to help third-party driver writers
197 //
198
199 DebugPrint((0, "ClassInitialize: Class device-specific missing "
200 "required PDO entry\n"));
201 ASSERT(FALSE);
202 return (ULONG) STATUS_REVISION_MISMATCH;
203 }
204
205 //
206 // Setup the default power handlers if the class driver didn't provide
207 // any.
208 //
209
210 if(InitializationData->FdoData.ClassPowerDevice == NULL) {
211 InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
212 }
213
214 if((InitializationData->ClassEnumerateDevice != NULL) &&
215 (InitializationData->PdoData.ClassPowerDevice == NULL)) {
216 InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
217 }
218
219 //
220 // warn that unload is not supported
221 //
222 // ISSUE-2000/02/03-peterwie
223 // We should think about making this a fatal error.
224 //
225
226 if(InitializationData->ClassUnload == NULL) {
227
228 //
229 // This DebugPrint is to help third-party driver writers
230 //
231
232 DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
233 RegistryPath));
234 }
235
236 //
237 // Create an extension for the driver object
238 //
239
240 status = IoAllocateDriverObjectExtension(DriverObject,
241 CLASS_DRIVER_EXTENSION_KEY,
242 sizeof(CLASS_DRIVER_EXTENSION),
243 &driverExtension);
244
245 if(NT_SUCCESS(status)) {
246
247 //
248 // Copy the registry path into the driver extension so we can use it later
249 //
250
251 driverExtension->RegistryPath.Length = RegistryPath->Length;
252 driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
253
254 driverExtension->RegistryPath.Buffer =
255 ExAllocatePoolWithTag(PagedPool,
256 RegistryPath->MaximumLength,
257 '1CcS');
258
259 if(driverExtension->RegistryPath.Buffer == NULL) {
260
261 status = STATUS_INSUFFICIENT_RESOURCES;
262 return status;
263 }
264
265 RtlCopyUnicodeString(
266 &(driverExtension->RegistryPath),
267 RegistryPath);
268
269 //
270 // Copy the initialization data into the driver extension so we can reuse
271 // it during our add device routine
272 //
273
274 RtlCopyMemory(
275 &(driverExtension->InitData),
276 InitializationData,
277 sizeof(CLASS_INIT_DATA));
278
279 driverExtension->DeviceCount = 0;
280
281 } else if (status == STATUS_OBJECT_NAME_COLLISION) {
282
283 //
284 // The extension already exists - get a pointer to it
285 //
286
287 driverExtension = IoGetDriverObjectExtension(DriverObject,
288 CLASS_DRIVER_EXTENSION_KEY);
289
290 ASSERT(driverExtension != NULL);
291
292 } else {
293
294 DebugPrint((1, "ClassInitialize: Class driver extension could not be "
295 "allocated %lx\n", status));
296 return status;
297 }
298
299 //
300 // Update driver object with entry points.
301 //
302
303 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreateClose;
304 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassCreateClose;
305 DriverObject->MajorFunction[IRP_MJ_READ] = ClassReadWrite;
306 DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassReadWrite;
307 DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassInternalIoControl;
308 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch;
309 DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassShutdownFlush;
310 DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush;
311 DriverObject->MajorFunction[IRP_MJ_PNP] = ClassDispatchPnp;
312 DriverObject->MajorFunction[IRP_MJ_POWER] = ClassDispatchPower;
313 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl;
314
315 if (InitializationData->ClassStartIo) {
316 DriverObject->DriverStartIo = ClasspStartIo;
317 }
318
319 if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) {
320 DriverObject->DriverUnload = ClassUnload;
321 } else {
322 DriverObject->DriverUnload = NULL;
323 }
324
325 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
326
327 DbgPrint("Driver is ready to go\n");
328 status = STATUS_SUCCESS;
329 return status;
330 } // end ClassInitialize()
331 \f
332 /*++////////////////////////////////////////////////////////////////////////////
333
334 ClassInitializeEx()
335
336 Routine Description:
337
338 This routine is allows the caller to do any extra initialization or
339 setup that is not done in ClassInitialize. The operation is
340 controlled by the GUID that is passed and the contents of the Data
341 parameter is dependent upon the GUID.
342
343 This is the list of supported operations:
344
345 Guid - GUID_CLASSPNP_QUERY_REGINFOEX
346 Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
347
348 Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
349 callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
350 former callback allows the driver to specify the name of the
351 mof resource.
352
353 Arguments:
354
355 DriverObject
356 Guid
357 Data
358
359 Return Value:
360
361 Status Code
362
363 --*/
364 ULONG
365 ClassInitializeEx(
366 IN PDRIVER_OBJECT DriverObject,
367 IN LPGUID Guid,
368 IN PVOID Data
369 )
370 {
371 PCLASS_DRIVER_EXTENSION driverExtension;
372
373 NTSTATUS status;
374
375 PAGED_CODE();
376
377 driverExtension = IoGetDriverObjectExtension( DriverObject,
378 CLASS_DRIVER_EXTENSION_KEY
379 );
380 if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
381 {
382 PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
383
384 //
385 // Indicate the device supports PCLASS_QUERY_REGINFO_EX
386 // callback instead of PCLASS_QUERY_REGINFO callback.
387 //
388 List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
389
390 if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
391 {
392 driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
393 driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
394 status = STATUS_SUCCESS;
395 } else {
396 status = STATUS_INVALID_PARAMETER;
397 }
398 } else {
399 status = STATUS_NOT_SUPPORTED;
400 }
401
402 return(status);
403
404 } // end ClassInitializeEx()
405 \f
406 /*++////////////////////////////////////////////////////////////////////////////
407
408 ClassUnload()
409
410 Routine Description:
411
412 called when there are no more references to the driver. this allows
413 drivers to be updated without rebooting.
414
415 Arguments:
416
417 DriverObject - a pointer to the driver object that is being unloaded
418
419 Status:
420
421 --*/
422 VOID
423 ClassUnload(
424 IN PDRIVER_OBJECT DriverObject
425 )
426 {
427 PCLASS_DRIVER_EXTENSION driverExtension;
428 NTSTATUS status;
429
430 PAGED_CODE();
431
432 ASSERT( DriverObject->DeviceObject == NULL );
433
434 driverExtension = IoGetDriverObjectExtension( DriverObject,
435 CLASS_DRIVER_EXTENSION_KEY
436 );
437
438 ASSERT(driverExtension != NULL);
439 ASSERT(driverExtension->RegistryPath.Buffer != NULL);
440 ASSERT(driverExtension->InitData.ClassUnload != NULL);
441
442 DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
443 &driverExtension->RegistryPath));
444
445 //
446 // attempt to process the driver's unload routine first.
447 //
448
449 driverExtension->InitData.ClassUnload(DriverObject);
450
451 //
452 // free own allocated resources and return
453 //
454
455 ExFreePool( driverExtension->RegistryPath.Buffer );
456 driverExtension->RegistryPath.Buffer = NULL;
457 driverExtension->RegistryPath.Length = 0;
458 driverExtension->RegistryPath.MaximumLength = 0;
459
460 return;
461 } // end ClassUnload()
462 \f
463 /*++////////////////////////////////////////////////////////////////////////////
464
465 ClassAddDevice()
466
467 Routine Description:
468
469 SCSI class driver add device routine. This is called by pnp when a new
470 physical device come into being.
471
472 This routine will call out to the class driver to verify that it should
473 own this device then will create and attach a device object and then hand
474 it to the driver to initialize and create symbolic links
475
476 Arguments:
477
478 DriverObject - a pointer to the driver object that this is being created for
479 PhysicalDeviceObject - a pointer to the physical device object
480
481 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
482 STATUS_SUCCESS if the creation and attachment was successful
483 status of device creation and initialization
484
485 --*/
486 NTSTATUS
487 ClassAddDevice(
488 IN PDRIVER_OBJECT DriverObject,
489 IN PDEVICE_OBJECT PhysicalDeviceObject
490 )
491 {
492 PCLASS_DRIVER_EXTENSION driverExtension =
493 IoGetDriverObjectExtension(DriverObject,
494 CLASS_DRIVER_EXTENSION_KEY);
495
496 NTSTATUS status;
497
498 PAGED_CODE();
499
500 DbgPrint("got a device\n");
501 status = driverExtension->InitData.ClassAddDevice(DriverObject,
502 PhysicalDeviceObject);
503 return status;
504 } // end ClassAddDevice()
505 \f
506 /*++////////////////////////////////////////////////////////////////////////////
507
508 ClassDispatchPnp()
509
510 Routine Description:
511
512 Storage class driver pnp routine. This is called by the io system when
513 a PNP request is sent to the device.
514
515 Arguments:
516
517 DeviceObject - pointer to the device object
518
519 Irp - pointer to the io request packet
520
521 Return Value:
522
523 status
524
525 --*/
526 NTSTATUS
527 ClassDispatchPnp(
528 IN PDEVICE_OBJECT DeviceObject,
529 IN PIRP Irp
530 )
531 {
532 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
533 BOOLEAN isFdo = commonExtension->IsFdo;
534
535 PCLASS_DRIVER_EXTENSION driverExtension;
536 PCLASS_INIT_DATA initData;
537 PCLASS_DEV_INFO devInfo;
538
539 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
540 PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
541
542 NTSTATUS status = Irp->IoStatus.Status;
543 BOOLEAN completeRequest = TRUE;
544 BOOLEAN lockReleased = FALSE;
545
546 ULONG isRemoved;
547
548 PAGED_CODE();
549
550 //
551 // Extract all the useful information out of the driver object
552 // extension
553 //
554
555 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
556 CLASS_DRIVER_EXTENSION_KEY);
557 if (driverExtension){
558
559 initData = &(driverExtension->InitData);
560
561 if(isFdo) {
562 devInfo = &(initData->FdoData);
563 } else {
564 devInfo = &(initData->PdoData);
565 }
566
567 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
568
569 DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
570 DeviceObject, Irp,
571 irpStack->MinorFunction,
572 isFdo ? "fdo" : "pdo",
573 DeviceObject));
574 DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
575 DeviceObject, Irp,
576 commonExtension->PreviousState,
577 commonExtension->CurrentState));
578
579 switch(irpStack->MinorFunction) {
580
581 case IRP_MN_START_DEVICE: {
582
583 //
584 // if this is sent to the FDO we should forward it down the
585 // attachment chain before we start the FDO.
586 //
587
588 if (isFdo) {
589 status = ClassForwardIrpSynchronous(commonExtension, Irp);
590 }
591 else {
592 status = STATUS_SUCCESS;
593 }
594
595 if (NT_SUCCESS(status)){
596 status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
597 }
598
599 break;
600 }
601
602
603 case IRP_MN_QUERY_DEVICE_RELATIONS: {
604
605 DEVICE_RELATION_TYPE type =
606 irpStack->Parameters.QueryDeviceRelations.Type;
607
608 PDEVICE_RELATIONS deviceRelations = NULL;
609
610 if(!isFdo) {
611
612 if(type == TargetDeviceRelation) {
613
614 //
615 // Device relations has one entry built in to it's size.
616 //
617
618 status = STATUS_INSUFFICIENT_RESOURCES;
619
620 deviceRelations = ExAllocatePoolWithTag(PagedPool,
621 sizeof(DEVICE_RELATIONS),
622 '2CcS');
623
624 if(deviceRelations != NULL) {
625
626 RtlZeroMemory(deviceRelations,
627 sizeof(DEVICE_RELATIONS));
628
629 Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
630
631 deviceRelations->Count = 1;
632 deviceRelations->Objects[0] = DeviceObject;
633 ObReferenceObject(deviceRelations->Objects[0]);
634
635 status = STATUS_SUCCESS;
636 }
637
638 } else {
639 //
640 // PDO's just complete enumeration requests without altering
641 // the status.
642 //
643
644 status = Irp->IoStatus.Status;
645 }
646
647 break;
648
649 } else if (type == BusRelations) {
650
651 ASSERT(commonExtension->IsInitialized);
652
653 //
654 // Make sure we support enumeration
655 //
656
657 if(initData->ClassEnumerateDevice == NULL) {
658
659 //
660 // Just send the request down to the lower driver. Perhaps
661 // It can enumerate children.
662 //
663
664 } else {
665
666 //
667 // Re-enumerate the device
668 //
669
670 status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
671
672 if(!NT_SUCCESS(status)) {
673 completeRequest = TRUE;
674 break;
675 }
676 }
677 }
678
679 IoCopyCurrentIrpStackLocationToNext(Irp);
680 ClassReleaseRemoveLock(DeviceObject, Irp);
681 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
682 completeRequest = FALSE;
683
684 break;
685 }
686
687 case IRP_MN_QUERY_ID: {
688
689 BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
690 UNICODE_STRING unicodeString;
691
692 if(isFdo) {
693
694 //
695 // FDO's should just forward the query down to the lower
696 // device objects
697 //
698
699 IoCopyCurrentIrpStackLocationToNext(Irp);
700 ClassReleaseRemoveLock(DeviceObject, Irp);
701
702 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
703 completeRequest = FALSE;
704 break;
705 }
706
707 //
708 // PDO's need to give an answer - this is easy for now
709 //
710
711 RtlInitUnicodeString(&unicodeString, NULL);
712
713 status = ClassGetPdoId(DeviceObject,
714 idType,
715 &unicodeString);
716
717 if(status == STATUS_NOT_IMPLEMENTED) {
718 //
719 // The driver doesn't implement this ID (whatever it is).
720 // Use the status out of the IRP so that we don't mangle a
721 // response from someone else.
722 //
723
724 status = Irp->IoStatus.Status;
725 } else if(NT_SUCCESS(status)) {
726 Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
727 } else {
728 Irp->IoStatus.Information = (ULONG_PTR) NULL;
729 }
730
731 break;
732 }
733
734 case IRP_MN_QUERY_STOP_DEVICE:
735 case IRP_MN_QUERY_REMOVE_DEVICE: {
736
737 DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
738 DeviceObject, Irp,
739 ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
740 "STOP" : "REMOVE")));
741
742 //
743 // If this device is in use for some reason (paging, etc...)
744 // then we need to fail the request.
745 //
746
747 if(commonExtension->PagingPathCount != 0) {
748
749 DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
750 "path and cannot be removed\n",
751 DeviceObject, Irp));
752 status = STATUS_DEVICE_BUSY;
753 break;
754 }
755
756 //
757 // Check with the class driver to see if the query operation
758 // can succeed.
759 //
760
761 if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
762 status = devInfo->ClassStopDevice(DeviceObject,
763 irpStack->MinorFunction);
764 } else {
765 status = devInfo->ClassRemoveDevice(DeviceObject,
766 irpStack->MinorFunction);
767 }
768
769 if(NT_SUCCESS(status)) {
770
771 //
772 // ASSERT that we never get two queries in a row, as
773 // this will severly mess up the state machine
774 //
775 ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
776 commonExtension->PreviousState = commonExtension->CurrentState;
777 commonExtension->CurrentState = irpStack->MinorFunction;
778
779 if(isFdo) {
780 DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
781 "%s irp\n", DeviceObject, Irp,
782 ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
783 "STOP" : "REMOVE")));
784 status = ClassForwardIrpSynchronous(commonExtension, Irp);
785 }
786 }
787 DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
788 DeviceObject, Irp, status));
789
790 break;
791 }
792
793 case IRP_MN_CANCEL_STOP_DEVICE:
794 case IRP_MN_CANCEL_REMOVE_DEVICE: {
795
796 //
797 // Check with the class driver to see if the query or cancel
798 // operation can succeed.
799 //
800
801 if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
802 status = devInfo->ClassStopDevice(DeviceObject,
803 irpStack->MinorFunction);
804 ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
805 "never be failed\n", NT_SUCCESS(status));
806 } else {
807 status = devInfo->ClassRemoveDevice(DeviceObject,
808 irpStack->MinorFunction);
809 ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
810 "never be failed\n", NT_SUCCESS(status));
811 }
812
813 Irp->IoStatus.Status = status;
814
815 //
816 // We got a CANCEL - roll back to the previous state only
817 // if the current state is the respective QUERY state.
818 //
819
820 if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
821 (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
822 ) ||
823 ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
824 (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
825 )
826 ) {
827
828 commonExtension->CurrentState =
829 commonExtension->PreviousState;
830 commonExtension->PreviousState = 0xff;
831
832 }
833
834 if(isFdo) {
835 IoCopyCurrentIrpStackLocationToNext(Irp);
836 ClassReleaseRemoveLock(DeviceObject, Irp);
837 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
838 completeRequest = FALSE;
839 } else {
840 status = STATUS_SUCCESS;
841 }
842
843 break;
844 }
845
846 case IRP_MN_STOP_DEVICE: {
847
848 //
849 // These all mean nothing to the class driver currently. The
850 // port driver will handle all queueing when necessary.
851 //
852
853 DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
854 DeviceObject, Irp,
855 (isFdo ? "fdo" : "pdo")
856 ));
857
858 ASSERT(commonExtension->PagingPathCount == 0);
859
860 //
861 // ISSUE-2000/02/03-peterwie
862 // if we stop the timer here then it means no class driver can
863 // do i/o in its ClassStopDevice routine. This is because the
864 // retry (among other things) is tied into the tick handler
865 // and disabling retries could cause the class driver to deadlock.
866 // Currently no class driver we're aware of issues i/o in its
867 // Stop routine but this is a case we may want to defend ourself
868 // against.
869 //
870
871 if (DeviceObject->Timer) {
872 IoStopTimer(DeviceObject);
873 }
874
875 status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
876
877 ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
878 "never be failed\n", NT_SUCCESS(status));
879
880 if(isFdo) {
881 status = ClassForwardIrpSynchronous(commonExtension, Irp);
882 }
883
884 if(NT_SUCCESS(status)) {
885 commonExtension->CurrentState = irpStack->MinorFunction;
886 commonExtension->PreviousState = 0xff;
887 }
888
889 break;
890 }
891
892 case IRP_MN_REMOVE_DEVICE:
893 case IRP_MN_SURPRISE_REMOVAL: {
894
895 PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
896 UCHAR removeType = irpStack->MinorFunction;
897
898 if (commonExtension->PagingPathCount != 0) {
899 DBGTRACE(ClassDebugWarning, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
900 }
901
902 //
903 // Release the lock for this IRP before calling in.
904 //
905 ClassReleaseRemoveLock(DeviceObject, Irp);
906 lockReleased = TRUE;
907
908 /*
909 * If a timer was started on the device, stop it.
910 */
911 if (DeviceObject->Timer) {
912 IoStopTimer(DeviceObject);
913 }
914
915 /*
916 * "Fire-and-forget" the remove irp to the lower stack.
917 * Don't touch the irp (or the irp stack!) after this.
918 */
919 if (isFdo) {
920 IoCopyCurrentIrpStackLocationToNext(Irp);
921 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
922 ASSERT(NT_SUCCESS(status));
923 completeRequest = FALSE;
924 }
925 else {
926 status = STATUS_SUCCESS;
927 }
928
929 /*
930 * Do our own cleanup and call the class driver's remove
931 * cleanup routine.
932 * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
933 * so don't touch the extension after this.
934 */
935 commonExtension->PreviousState = commonExtension->CurrentState;
936 commonExtension->CurrentState = removeType;
937 ClassRemoveDevice(DeviceObject, removeType);
938
939 break;
940 }
941
942 case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
943
944 switch(irpStack->Parameters.UsageNotification.Type) {
945
946 case DeviceUsageTypePaging: {
947
948 BOOLEAN setPagable;
949
950 if((irpStack->Parameters.UsageNotification.InPath) &&
951 (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
952
953 //
954 // Device isn't started. Don't allow adding a
955 // paging file, but allow a removal of one.
956 //
957
958 status = STATUS_DEVICE_NOT_READY;
959 break;
960 }
961
962 ASSERT(commonExtension->IsInitialized);
963
964 //
965 // need to synchronize this now...
966 //
967
968 KeEnterCriticalRegion();
969 status = KeWaitForSingleObject(&commonExtension->PathCountEvent,
970 Executive, KernelMode,
971 FALSE, NULL);
972 ASSERT(NT_SUCCESS(status));
973 status = STATUS_SUCCESS;
974
975 //
976 // If the volume is removable we should try to lock it in
977 // place or unlock it once per paging path count
978 //
979
980 if (commonExtension->IsFdo){
981 status = ClasspEjectionControl(
982 DeviceObject,
983 Irp,
984 InternalMediaLock,
985 (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
986 }
987
988 if (!NT_SUCCESS(status)){
989 KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
990 KeLeaveCriticalRegion();
991 break;
992 }
993
994 //
995 // if removing last paging device, need to set DO_POWER_PAGABLE
996 // bit here, and possible re-set it below on failure.
997 //
998
999 setPagable = FALSE;
1000
1001 if (!irpStack->Parameters.UsageNotification.InPath &&
1002 commonExtension->PagingPathCount == 1
1003 ) {
1004
1005 //
1006 // removing last paging file
1007 // must have DO_POWER_PAGABLE bits set, but only
1008 // if noone set the DO_POWER_INRUSH bit
1009 //
1010
1011
1012 if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1013 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1014 "paging file removed, but "
1015 "DO_POWER_INRUSH was set, so NOT "
1016 "setting DO_POWER_PAGABLE\n",
1017 DeviceObject, Irp));
1018 } else {
1019 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1020 "paging file removed, "
1021 "setting DO_POWER_PAGABLE\n",
1022 DeviceObject, Irp));
1023 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1024 setPagable = TRUE;
1025 }
1026
1027 }
1028
1029 //
1030 // forward the irp before finishing handling the
1031 // special cases
1032 //
1033
1034 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1035
1036 //
1037 // now deal with the failure and success cases.
1038 // note that we are not allowed to fail the irp
1039 // once it is sent to the lower drivers.
1040 //
1041
1042 if (NT_SUCCESS(status)) {
1043
1044 IoAdjustPagingPathCount(
1045 &commonExtension->PagingPathCount,
1046 irpStack->Parameters.UsageNotification.InPath);
1047
1048 if (irpStack->Parameters.UsageNotification.InPath) {
1049 if (commonExtension->PagingPathCount == 1) {
1050 DebugPrint((2, "ClassDispatchPnp (%p,%p): "
1051 "Clearing PAGABLE bit\n",
1052 DeviceObject, Irp));
1053 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1054 }
1055 }
1056
1057 } else {
1058
1059 //
1060 // cleanup the changes done above
1061 //
1062
1063 if (setPagable == TRUE) {
1064 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
1065 "PAGABLE bit due to irp failure\n",
1066 DeviceObject, Irp));
1067 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1068 setPagable = FALSE;
1069 }
1070
1071 //
1072 // relock or unlock the media if needed.
1073 //
1074
1075 if (commonExtension->IsFdo) {
1076
1077 ClasspEjectionControl(
1078 DeviceObject,
1079 Irp,
1080 InternalMediaLock,
1081 (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
1082 }
1083 }
1084
1085 //
1086 // set the event so the next one can occur.
1087 //
1088
1089 KeSetEvent(&commonExtension->PathCountEvent,
1090 IO_NO_INCREMENT, FALSE);
1091 KeLeaveCriticalRegion();
1092 break;
1093 }
1094
1095 case DeviceUsageTypeHibernation: {
1096
1097 IoAdjustPagingPathCount(
1098 &commonExtension->HibernationPathCount,
1099 irpStack->Parameters.UsageNotification.InPath
1100 );
1101 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1102 if (!NT_SUCCESS(status)) {
1103 IoAdjustPagingPathCount(
1104 &commonExtension->HibernationPathCount,
1105 !irpStack->Parameters.UsageNotification.InPath
1106 );
1107 }
1108
1109 break;
1110 }
1111
1112 case DeviceUsageTypeDumpFile: {
1113 IoAdjustPagingPathCount(
1114 &commonExtension->DumpPathCount,
1115 irpStack->Parameters.UsageNotification.InPath
1116 );
1117 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1118 if (!NT_SUCCESS(status)) {
1119 IoAdjustPagingPathCount(
1120 &commonExtension->DumpPathCount,
1121 !irpStack->Parameters.UsageNotification.InPath
1122 );
1123 }
1124
1125 break;
1126 }
1127
1128 default: {
1129 status = STATUS_INVALID_PARAMETER;
1130 break;
1131 }
1132 }
1133 break;
1134 }
1135
1136 case IRP_MN_QUERY_CAPABILITIES: {
1137
1138 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1139 DeviceObject, Irp));
1140
1141 if(!isFdo) {
1142
1143 status = ClassQueryPnpCapabilities(
1144 DeviceObject,
1145 irpStack->Parameters.DeviceCapabilities.Capabilities
1146 );
1147
1148 break;
1149
1150 } else {
1151
1152 PDEVICE_CAPABILITIES deviceCapabilities;
1153 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1154 PCLASS_PRIVATE_FDO_DATA fdoData;
1155
1156 fdoExtension = DeviceObject->DeviceExtension;
1157 fdoData = fdoExtension->PrivateFdoData;
1158 deviceCapabilities =
1159 irpStack->Parameters.DeviceCapabilities.Capabilities;
1160
1161 //
1162 // forward the irp before handling the special cases
1163 //
1164
1165 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1166 if (!NT_SUCCESS(status)) {
1167 break;
1168 }
1169
1170 //
1171 // we generally want to remove the device from the hotplug
1172 // applet, which requires the SR-OK bit to be set.
1173 // only when the user specifies that they are capable of
1174 // safely removing things do we want to clear this bit
1175 // (saved in WriteCacheEnableOverride)
1176 //
1177 // setting of this bit is done either above, or by the
1178 // lower driver.
1179 //
1180 // note: may not be started, so check we have FDO data first.
1181 //
1182
1183 if (fdoData &&
1184 fdoData->HotplugInfo.WriteCacheEnableOverride) {
1185 if (deviceCapabilities->SurpriseRemovalOK) {
1186 DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
1187 "device capabilities due to hotplug "
1188 "device or media\n"));
1189 }
1190 deviceCapabilities->SurpriseRemovalOK = FALSE;
1191 }
1192 break;
1193
1194 } // end QUERY_CAPABILITIES for FDOs
1195
1196 ASSERT(FALSE);
1197 break;
1198
1199
1200 } // end QUERY_CAPABILITIES
1201
1202 default: {
1203
1204 if (isFdo){
1205 IoCopyCurrentIrpStackLocationToNext(Irp);
1206
1207 ClassReleaseRemoveLock(DeviceObject, Irp);
1208 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1209
1210 completeRequest = FALSE;
1211 }
1212
1213 break;
1214 }
1215 }
1216 }
1217 else {
1218 ASSERT(driverExtension);
1219 status = STATUS_INTERNAL_ERROR;
1220 }
1221
1222 if (completeRequest){
1223 Irp->IoStatus.Status = status;
1224
1225 if (!lockReleased){
1226 ClassReleaseRemoveLock(DeviceObject, Irp);
1227 }
1228
1229 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1230
1231 DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
1232 }
1233 else {
1234 /*
1235 * The irp is already completed so don't touch it.
1236 * This may be a remove so don't touch the device extension.
1237 */
1238 DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
1239 }
1240
1241 return status;
1242 } // end ClassDispatchPnp()
1243 \f
1244 /*++////////////////////////////////////////////////////////////////////////////
1245
1246 ClassPnpStartDevice()
1247
1248 Routine Description:
1249
1250 Storage class driver routine for IRP_MN_START_DEVICE requests.
1251 This routine kicks off any device specific initialization
1252
1253 Arguments:
1254
1255 DeviceObject - a pointer to the device object
1256
1257 Irp - a pointer to the io request packet
1258
1259 Return Value:
1260
1261 none
1262
1263 --*/
1264 NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
1265 {
1266 PCLASS_DRIVER_EXTENSION driverExtension;
1267 PCLASS_INIT_DATA initData;
1268
1269 PCLASS_DEV_INFO devInfo;
1270
1271 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1272 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1273 BOOLEAN isFdo = commonExtension->IsFdo;
1274
1275 BOOLEAN isMountedDevice = TRUE;
1276 UNICODE_STRING interfaceName;
1277
1278 BOOLEAN timerStarted;
1279
1280 NTSTATUS status = STATUS_SUCCESS;
1281
1282 PAGED_CODE();
1283
1284 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
1285 CLASS_DRIVER_EXTENSION_KEY);
1286
1287 initData = &(driverExtension->InitData);
1288 if(isFdo) {
1289 devInfo = &(initData->FdoData);
1290 } else {
1291 devInfo = &(initData->PdoData);
1292 }
1293
1294 ASSERT(devInfo->ClassInitDevice != NULL);
1295 ASSERT(devInfo->ClassStartDevice != NULL);
1296
1297 if (!commonExtension->IsInitialized){
1298
1299 //
1300 // perform FDO/PDO specific initialization
1301 //
1302
1303 if (isFdo){
1304 STORAGE_PROPERTY_ID propertyId;
1305
1306 //
1307 // allocate a private extension for class data
1308 //
1309
1310 if (fdoExtension->PrivateFdoData == NULL) {
1311 fdoExtension->PrivateFdoData =
1312 ExAllocatePoolWithTag(NonPagedPool,
1313 sizeof(CLASS_PRIVATE_FDO_DATA),
1314 CLASS_TAG_PRIVATE_DATA
1315 );
1316 }
1317
1318 if (fdoExtension->PrivateFdoData == NULL) {
1319 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
1320 "private fdo data\n"));
1321 return STATUS_INSUFFICIENT_RESOURCES;
1322 }
1323
1324 //
1325 // initialize the struct's various fields.
1326 //
1327
1328 RtlZeroMemory(fdoExtension->PrivateFdoData,
1329 sizeof(CLASS_PRIVATE_FDO_DATA)
1330 );
1331 KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
1332 KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
1333 ClasspRetryRequestDpc,
1334 DeviceObject);
1335 KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
1336 fdoExtension->PrivateFdoData->Retry.Granularity =
1337 KeQueryTimeIncrement();
1338 commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
1339
1340 //
1341 // NOTE: the old interface allowed the class driver to allocate
1342 // this. this was unsafe for low-memory conditions. allocate one
1343 // unconditionally now, and modify our internal functions to use
1344 // our own exclusively as it is the only safe way to do this.
1345 //
1346
1347 status = ClasspAllocateReleaseQueueIrp(fdoExtension);
1348 if (!NT_SUCCESS(status)) {
1349 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
1350 "private release queue irp\n"));
1351 return status;
1352 }
1353
1354 //
1355 // Call port driver to get adapter capabilities.
1356 //
1357
1358 propertyId = StorageAdapterProperty;
1359
1360 status = ClassGetDescriptor(
1361 commonExtension->LowerDeviceObject,
1362 &propertyId,
1363 &fdoExtension->AdapterDescriptor);
1364
1365 if(!NT_SUCCESS(status)) {
1366
1367 //
1368 // This DebugPrint is to help third-party driver writers
1369 //
1370
1371 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1372 "[ADAPTER] failed %lx\n", status));
1373 return status;
1374 }
1375
1376 //
1377 // Call port driver to get device descriptor.
1378 //
1379
1380 propertyId = StorageDeviceProperty;
1381
1382 status = ClassGetDescriptor(
1383 commonExtension->LowerDeviceObject,
1384 &propertyId,
1385 &fdoExtension->DeviceDescriptor);
1386
1387 if(!NT_SUCCESS(status)) {
1388
1389 //
1390 // This DebugPrint is to help third-party driver writers
1391 //
1392
1393 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1394 "[DEVICE] failed %lx\n", status));
1395 return status;
1396 }
1397
1398 ClasspScanForSpecialInRegistry(fdoExtension);
1399 ClassScanForSpecial(fdoExtension,
1400 ClassBadItems,
1401 ClasspScanForClassHacks);
1402
1403 //
1404 // allow perf to be re-enabled after a given number of failed IOs
1405 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
1406 //
1407
1408 {
1409 ULONG t = 0;
1410 ClassGetDeviceParameter(fdoExtension,
1411 CLASSP_REG_SUBKEY_NAME,
1412 CLASSP_REG_PERF_RESTORE_VALUE_NAME,
1413 &t);
1414 if (t >= CLASS_PERF_RESTORE_MINIMUM) {
1415 fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
1416 }
1417 }
1418
1419
1420 //
1421 // compatibility comes first. writable cd media will not
1422 // get a SYNCH_CACHE on power down.
1423 //
1424
1425 if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
1426 SET_FLAG(fdoExtension->PrivateFdoData->HackFlags,
1427 FDO_HACK_NO_SYNC_CACHE);
1428 }
1429
1430 //
1431 // initialize the hotplug information only after the ScanForSpecial
1432 // routines, as it relies upon the hack flags.
1433 //
1434
1435 status = ClasspInitializeHotplugInfo(fdoExtension);
1436
1437 if (!NT_SUCCESS(status)) {
1438 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
1439 "hotplug information %lx\n", status));
1440 return status;
1441 }
1442
1443 /*
1444 * Allocate/initialize TRANSFER_PACKETs and related resources.
1445 */
1446 status = InitializeTransferPackets(DeviceObject);
1447 }
1448
1449 //
1450 // ISSUE - drivers need to disable write caching on the media
1451 // if hotplug and !useroverride. perhaps we should
1452 // allow registration of a callback to enable/disable
1453 // write cache instead.
1454 //
1455
1456 if (NT_SUCCESS(status)){
1457 status = devInfo->ClassInitDevice(DeviceObject);
1458 }
1459
1460 }
1461
1462 if (!NT_SUCCESS(status)){
1463
1464 //
1465 // Just bail out - the remove that comes down will clean up the
1466 // initialized scraps.
1467 //
1468
1469 return status;
1470 } else {
1471 commonExtension->IsInitialized = TRUE;
1472
1473 if (commonExtension->IsFdo) {
1474 fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
1475 }
1476
1477 }
1478
1479 //
1480 // If device requests autorun functionality or a once a second callback
1481 // then enable the once per second timer.
1482 //
1483 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
1484 // called in the context of the ClassInitDevice callback. If called
1485 // after then this check will have already been made and the
1486 // once a second timer will not have been enabled.
1487 //
1488 if ((isFdo) &&
1489 ((initData->ClassTick != NULL) ||
1490 (fdoExtension->MediaChangeDetectionInfo != NULL) ||
1491 ((fdoExtension->FailurePredictionInfo != NULL) &&
1492 (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
1493 {
1494 ClasspEnableTimer(DeviceObject);
1495 timerStarted = TRUE;
1496 } else {
1497 timerStarted = FALSE;
1498 }
1499
1500 //
1501 // NOTE: the timer looks at commonExtension->CurrentState now
1502 // to prevent Media Change Notification code from running
1503 // until the device is started, but allows the device
1504 // specific tick handler to run. therefore it is imperative
1505 // that commonExtension->CurrentState not be updated until
1506 // the device specific startdevice handler has finished.
1507 //
1508
1509 status = devInfo->ClassStartDevice(DeviceObject);
1510
1511 if(NT_SUCCESS(status)) {
1512 commonExtension->CurrentState = IRP_MN_START_DEVICE;
1513
1514 if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
1515 isMountedDevice = FALSE;
1516 }
1517
1518 if((DeviceObject->DeviceType != FILE_DEVICE_DISK) &&
1519 (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM)) {
1520
1521 isMountedDevice = FALSE;
1522 }
1523
1524
1525 if(isMountedDevice) {
1526 ClasspRegisterMountedDeviceInterface(DeviceObject);
1527 }
1528
1529 if((commonExtension->IsFdo) &&
1530 (devInfo->ClassWmiInfo.GuidRegInfo != NULL)) {
1531
1532 IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
1533 }
1534 } else {
1535
1536 if (timerStarted) {
1537 ClasspDisableTimer(DeviceObject);
1538 }
1539 }
1540
1541 return status;
1542 }
1543
1544
1545 /*++////////////////////////////////////////////////////////////////////////////
1546
1547 ClassReadWrite()
1548
1549 Routine Description:
1550
1551 This is the system entry point for read and write requests. The
1552 device-specific handler is invoked to perform any validation necessary.
1553
1554 If the device object is a PDO (partition object) then the request will
1555 simply be adjusted for Partition0 and issued to the lower device driver.
1556
1557 IF the device object is an FDO (paritition 0 object), the number of bytes
1558 in the request are checked against the maximum byte counts that the adapter
1559 supports and requests are broken up into
1560 smaller sizes if necessary.
1561
1562 Arguments:
1563
1564 DeviceObject - a pointer to the device object for this request
1565
1566 Irp - IO request
1567
1568 Return Value:
1569
1570 NT Status
1571
1572 --*/
1573 NTSTATUS ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
1574 {
1575 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1576 PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
1577 PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
1578 LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
1579 ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
1580 ULONG isRemoved;
1581 NTSTATUS status;
1582
1583 /*
1584 * Grab the remove lock. If we can't acquire it, bail out.
1585 */
1586 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
1587 if (isRemoved) {
1588 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
1589 ClassReleaseRemoveLock(DeviceObject, Irp);
1590 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1591 status = STATUS_DEVICE_DOES_NOT_EXIST;
1592 }
1593 else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
1594 (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
1595 !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
1596
1597 /*
1598 * DO_VERIFY_VOLUME is set for the device object,
1599 * but this request is not itself a verify request.
1600 * So fail this request.
1601 */
1602 IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
1603 Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
1604 Irp->IoStatus.Information = 0;
1605 ClassReleaseRemoveLock(DeviceObject, Irp);
1606 ClassCompleteRequest(DeviceObject, Irp, 0);
1607 status = STATUS_VERIFY_REQUIRED;
1608 }
1609 else {
1610
1611 /*
1612 * Since we've bypassed the verify-required tests we don't need to repeat
1613 * them with this IRP - in particular we don't want to worry about
1614 * hitting them at the partition 0 level if the request has come through
1615 * a non-zero partition.
1616 */
1617 currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
1618
1619 /*
1620 * Call the miniport driver's pre-pass filter to check if we
1621 * should continue with this transfer.
1622 */
1623 ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
1624 status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
1625 if (!NT_SUCCESS(status)){
1626 ASSERT(Irp->IoStatus.Status == status);
1627 ClassReleaseRemoveLock(DeviceObject, Irp);
1628 ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
1629 }
1630 else if (status == STATUS_PENDING){
1631 /*
1632 * ClassReadWriteVerification queued this request.
1633 * So don't touch the irp anymore.
1634 */
1635 }
1636 else {
1637
1638 if (transferByteCount == 0) {
1639 /*
1640 * Several parts of the code turn 0 into 0xffffffff,
1641 * so don't process a zero-length request any further.
1642 */
1643 Irp->IoStatus.Status = STATUS_SUCCESS;
1644 Irp->IoStatus.Information = 0;
1645 ClassReleaseRemoveLock(DeviceObject, Irp);
1646 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1647 status = STATUS_SUCCESS;
1648 }
1649 else {
1650 /*
1651 * If the driver has its own StartIo routine, call it.
1652 */
1653 if (commonExtension->DriverExtension->InitData.ClassStartIo) {
1654 IoMarkIrpPending(Irp);
1655 IoStartPacket(DeviceObject, Irp, NULL, NULL);
1656 status = STATUS_PENDING;
1657 }
1658 else {
1659 /*
1660 * The driver does not have its own StartIo routine.
1661 * So process this request ourselves.
1662 */
1663
1664 /*
1665 * Add partition byte offset to make starting byte relative to
1666 * beginning of disk.
1667 */
1668 currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
1669 commonExtension->StartingOffset.QuadPart;
1670
1671 if (commonExtension->IsFdo){
1672
1673 /*
1674 * Add in any skew for the disk manager software.
1675 */
1676 currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
1677 commonExtension->PartitionZeroExtension->DMByteSkew;
1678
1679 /*
1680 * Perform the actual transfer(s) on the hardware
1681 * to service this request.
1682 */
1683 ServiceTransferRequest(DeviceObject, Irp);
1684 status = STATUS_PENDING;
1685 }
1686 else {
1687 /*
1688 * This is a child PDO enumerated for our FDO by e.g. disk.sys
1689 * and owned by e.g. partmgr. Send it down to the next device
1690 * and the same irp will come back to us for the FDO.
1691 */
1692 IoCopyCurrentIrpStackLocationToNext(Irp);
1693 ClassReleaseRemoveLock(DeviceObject, Irp);
1694 status = IoCallDriver(lowerDeviceObject, Irp);
1695 }
1696 }
1697 }
1698 }
1699 }
1700
1701 return status;
1702 }
1703
1704
1705 /*++////////////////////////////////////////////////////////////////////////////
1706
1707 ClassReadDriveCapacity()
1708
1709 Routine Description:
1710
1711 This routine sends a READ CAPACITY to the requested device, updates
1712 the geometry information in the device object and returns
1713 when it is complete. This routine is synchronous.
1714
1715 This routine must be called with the remove lock held or some other
1716 assurance that the Fdo will not be removed while processing.
1717
1718 Arguments:
1719
1720 DeviceObject - Supplies a pointer to the device object that represents
1721 the device whose capacity is to be read.
1722
1723 Return Value:
1724
1725 Status is returned.
1726
1727 --*/
1728 NTSTATUS ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo)
1729 {
1730 READ_CAPACITY_DATA readCapacityBuffer = {0};
1731 NTSTATUS status;
1732 PMDL driveCapMdl;
1733
1734 driveCapMdl = BuildDeviceInputMdl(&readCapacityBuffer, sizeof(READ_CAPACITY_DATA));
1735 if (driveCapMdl){
1736
1737 TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
1738 if (pkt){
1739 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
1740 KEVENT event;
1741 NTSTATUS pktStatus;
1742 IRP pseudoIrp = {0};
1743
1744 /*
1745 * Our engine needs an "original irp" to write the status back to
1746 * and to count down packets (one in this case).
1747 * Just use a pretend irp for this.
1748 */
1749 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
1750 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
1751 pseudoIrp.IoStatus.Information = 0;
1752 pseudoIrp.MdlAddress = driveCapMdl;
1753
1754 /*
1755 * Set this up as a SYNCHRONOUS transfer, submit it,
1756 * and wait for the packet to complete. The result
1757 * status will be written to the original irp.
1758 */
1759 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1760 SetupDriveCapacityTransferPacket( pkt,
1761 &readCapacityBuffer,
1762 sizeof(READ_CAPACITY_DATA),
1763 &event,
1764 &pseudoIrp);
1765 SubmitTransferPacket(pkt);
1766 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1767
1768 status = pseudoIrp.IoStatus.Status;
1769
1770 /*
1771 * If we got an UNDERRUN, retry exactly once.
1772 * (The transfer_packet engine didn't retry because the result
1773 * status was success).
1774 */
1775 if (NT_SUCCESS(status) &&
1776 (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA))){
1777 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG)pseudoIrp.IoStatus.Information, sizeof(READ_CAPACITY_DATA)));
1778
1779 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
1780 if (pkt){
1781 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
1782 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
1783 pseudoIrp.IoStatus.Information = 0;
1784 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1785 SetupDriveCapacityTransferPacket( pkt,
1786 &readCapacityBuffer,
1787 sizeof(READ_CAPACITY_DATA),
1788 &event,
1789 &pseudoIrp);
1790 SubmitTransferPacket(pkt);
1791 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1792 status = pseudoIrp.IoStatus.Status;
1793 if (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA)){
1794 status = STATUS_DEVICE_BUSY;
1795 }
1796 }
1797 else {
1798 status = STATUS_INSUFFICIENT_RESOURCES;
1799 }
1800 }
1801
1802
1803 if (NT_SUCCESS(status)){
1804 /*
1805 * The request succeeded.
1806 * Read out and store the drive information.
1807 */
1808 ULONG cylinderSize;
1809 ULONG bytesPerSector;
1810 ULONG tmp;
1811 ULONG lastSector;
1812
1813 /*
1814 * Read the bytesPerSector value,
1815 * which is big-endian in the returned buffer.
1816 * Default to the standard 512 bytes.
1817 */
1818 tmp = readCapacityBuffer.BytesPerBlock;
1819 ((PFOUR_BYTE)&bytesPerSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
1820 ((PFOUR_BYTE)&bytesPerSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
1821 ((PFOUR_BYTE)&bytesPerSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
1822 ((PFOUR_BYTE)&bytesPerSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
1823 if (bytesPerSector == 0) {
1824 bytesPerSector = 512;
1825 }
1826 else {
1827 /*
1828 * Clear all but the highest set bit.
1829 * That will give us a bytesPerSector value that is a power of 2.
1830 */
1831 while (bytesPerSector & (bytesPerSector-1)) {
1832 bytesPerSector &= bytesPerSector-1;
1833 }
1834 }
1835 fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
1836
1837 //
1838 // Copy last sector in reverse byte order.
1839 //
1840
1841 tmp = readCapacityBuffer.LogicalBlockAddress;
1842 ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
1843 ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
1844 ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
1845 ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
1846
1847 //
1848 // Calculate sector to byte shift.
1849 //
1850
1851 WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
1852
1853 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
1854 fdoExt->DiskGeometry.BytesPerSector));
1855
1856 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
1857 lastSector + 1));
1858
1859 if (fdoExt->DMActive){
1860 DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
1861 fdoExt->DMSkew));
1862 lastSector -= fdoExt->DMSkew;
1863 }
1864
1865 /*
1866 * Check to see if we have a geometry we should be using already.
1867 */
1868 cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
1869 fdoExt->DiskGeometry.SectorsPerTrack);
1870 if (cylinderSize == 0){
1871 DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
1872 "values from %#x/%#x to %#x/%#x\n",
1873 fdoExt->DiskGeometry.TracksPerCylinder,
1874 fdoExt->DiskGeometry.SectorsPerTrack,
1875 0xff,
1876 0x3f));
1877
1878 fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
1879 fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
1880
1881
1882 cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
1883 fdoExt->DiskGeometry.SectorsPerTrack);
1884 }
1885
1886 //
1887 // Calculate number of cylinders.
1888 //
1889
1890 fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/cylinderSize);
1891
1892 //
1893 // if there are zero cylinders, then the device lied AND it's
1894 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
1895 // this can fit into a single LONGLONG, so create another usable
1896 // geometry, even if it's unusual looking. This allows small,
1897 // non-standard devices, such as Sony's Memory Stick, to show
1898 // up as having a partition.
1899 //
1900
1901 if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
1902 fdoExt->DiskGeometry.SectorsPerTrack = 1;
1903 fdoExt->DiskGeometry.TracksPerCylinder = 1;
1904 fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector;
1905 }
1906
1907
1908 //
1909 // Calculate media capacity in bytes.
1910 //
1911
1912 fdoExt->CommonExtension.PartitionLength.QuadPart =
1913 ((LONGLONG)(lastSector + 1)) << fdoExt->SectorShift;
1914
1915 /*
1916 * Is this removable or fixed media
1917 */
1918 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
1919 fdoExt->DiskGeometry.MediaType = RemovableMedia;
1920 }
1921 else {
1922 fdoExt->DiskGeometry.MediaType = FixedMedia;
1923 }
1924 }
1925 else {
1926 /*
1927 * The request failed.
1928 */
1929
1930 //
1931 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
1932 // what happens when the disk's sector size is bigger than
1933 // 512 bytes and we hit this code path? this is untested.
1934 //
1935 // If the read capacity fails, set the geometry to reasonable parameter
1936 // so things don't fail at unexpected places. Zero the geometry
1937 // except for the bytes per sector and sector shift.
1938 //
1939
1940 /*
1941 * This request can sometimes fail legitimately
1942 * (e.g. when a SCSI device is attached but turned off)
1943 * so this is not necessarily a device/driver bug.
1944 */
1945 DBGTRACE(ClassDebugWarning, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo, status));
1946
1947 /*
1948 * Write in a default disk geometry which we HOPE is right (??).
1949 * BUGBUG !!
1950 */
1951 RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
1952 fdoExt->DiskGeometry.BytesPerSector = 512;
1953 fdoExt->SectorShift = 9;
1954 fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
1955
1956 /*
1957 * Is this removable or fixed media
1958 */
1959 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
1960 fdoExt->DiskGeometry.MediaType = RemovableMedia;
1961 }
1962 else {
1963 fdoExt->DiskGeometry.MediaType = FixedMedia;
1964 }
1965 }
1966
1967 }
1968 else {
1969 status = STATUS_INSUFFICIENT_RESOURCES;
1970 }
1971
1972 FreeDeviceInputMdl(driveCapMdl);
1973 }
1974 else {
1975 status = STATUS_INSUFFICIENT_RESOURCES;
1976 }
1977
1978 return status;
1979 }
1980
1981
1982 /*++////////////////////////////////////////////////////////////////////////////
1983
1984 ClassSendStartUnit()
1985
1986 Routine Description:
1987
1988 Send command to SCSI unit to start or power up.
1989 Because this command is issued asynchronounsly, that is, without
1990 waiting on it to complete, the IMMEDIATE flag is not set. This
1991 means that the CDB will not return until the drive has powered up.
1992 This should keep subsequent requests from being submitted to the
1993 device before it has completely spun up.
1994
1995 This routine is called from the InterpretSense routine, when a
1996 request sense returns data indicating that a drive must be
1997 powered up.
1998
1999 This routine may also be called from a class driver's error handler,
2000 or anytime a non-critical start device should be sent to the device.
2001
2002 Arguments:
2003
2004 Fdo - The functional device object for the stopped device.
2005
2006 Return Value:
2007
2008 None.
2009
2010 --*/
2011 VOID
2012 ClassSendStartUnit(
2013 IN PDEVICE_OBJECT Fdo
2014 )
2015 {
2016 PIO_STACK_LOCATION irpStack;
2017 PIRP irp;
2018 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2019 PSCSI_REQUEST_BLOCK srb;
2020 PCOMPLETION_CONTEXT context;
2021 PCDB cdb;
2022
2023 //
2024 // Allocate Srb from nonpaged pool.
2025 //
2026
2027 context = ExAllocatePoolWithTag(NonPagedPool,
2028 sizeof(COMPLETION_CONTEXT),
2029 '6CcS');
2030
2031 if(context == NULL) {
2032
2033 //
2034 // ISSUE-2000/02/03-peterwie
2035 // This code path was inheritted from the NT 4.0 class2.sys driver.
2036 // It needs to be changed to survive low-memory conditions.
2037 //
2038
2039 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
2040 }
2041
2042 //
2043 // Save the device object in the context for use by the completion
2044 // routine.
2045 //
2046
2047 context->DeviceObject = Fdo;
2048 srb = &context->Srb;
2049
2050 //
2051 // Zero out srb.
2052 //
2053
2054 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
2055
2056 //
2057 // Write length to SRB.
2058 //
2059
2060 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2061
2062 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2063
2064 //
2065 // Set timeout value large enough for drive to spin up.
2066 //
2067
2068 srb->TimeOutValue = START_UNIT_TIMEOUT;
2069
2070 //
2071 // Set the transfer length.
2072 //
2073
2074 srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
2075 SRB_FLAGS_DISABLE_AUTOSENSE |
2076 SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
2077
2078 //
2079 // Build the start unit CDB.
2080 //
2081
2082 srb->CdbLength = 6;
2083 cdb = (PCDB)srb->Cdb;
2084
2085 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
2086 cdb->START_STOP.Start = 1;
2087 cdb->START_STOP.Immediate = 0;
2088 cdb->START_STOP.LogicalUnitNumber = srb->Lun;
2089
2090 //
2091 // Build the asynchronous request to be sent to the port driver.
2092 // Since this routine is called from a DPC the IRP should always be
2093 // available.
2094 //
2095
2096 irp = IoAllocateIrp(Fdo->StackSize, FALSE);
2097
2098 if(irp == NULL) {
2099
2100 //
2101 // ISSUE-2000/02/03-peterwie
2102 // This code path was inheritted from the NT 4.0 class2.sys driver.
2103 // It needs to be changed to survive low-memory conditions.
2104 //
2105
2106 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
2107
2108 }
2109
2110 ClassAcquireRemoveLock(Fdo, irp);
2111
2112 IoSetCompletionRoutine(irp,
2113 (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
2114 context,
2115 TRUE,
2116 TRUE,
2117 TRUE);
2118
2119 irpStack = IoGetNextIrpStackLocation(irp);
2120 irpStack->MajorFunction = IRP_MJ_SCSI;
2121 srb->OriginalRequest = irp;
2122
2123 //
2124 // Store the SRB address in next stack for port driver.
2125 //
2126
2127 irpStack->Parameters.Scsi.Srb = srb;
2128
2129 //
2130 // Call the port driver with the IRP.
2131 //
2132
2133 IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
2134
2135 return;
2136
2137 } // end StartUnit()
2138 \f
2139 /*++////////////////////////////////////////////////////////////////////////////
2140
2141 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
2142
2143 Routine Description:
2144
2145 This routine is called when an asynchronous I/O request
2146 which was issused by the class driver completes. Examples of such requests
2147 are release queue or START UNIT. This routine releases the queue if
2148 necessary. It then frees the context and the IRP.
2149
2150 Arguments:
2151
2152 DeviceObject - The device object for the logical unit; however since this
2153 is the top stack location the value is NULL.
2154
2155 Irp - Supplies a pointer to the Irp to be processed.
2156
2157 Context - Supplies the context to be used to process this request.
2158
2159 Return Value:
2160
2161 None.
2162
2163 --*/
2164 NTSTATUS
2165 ClassAsynchronousCompletion(
2166 PDEVICE_OBJECT DeviceObject,
2167 PIRP Irp,
2168 PVOID Context
2169 )
2170 {
2171 PCOMPLETION_CONTEXT context = Context;
2172 PSCSI_REQUEST_BLOCK srb;
2173
2174 if(DeviceObject == NULL) {
2175
2176 DeviceObject = context->DeviceObject;
2177 }
2178
2179 srb = &context->Srb;
2180
2181 //
2182 // If this is an execute srb, then check the return status and make sure.
2183 // the queue is not frozen.
2184 //
2185
2186 if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
2187
2188 //
2189 // Check for a frozen queue.
2190 //
2191
2192 if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
2193
2194 //
2195 // Unfreeze the queue getting the device object from the context.
2196 //
2197
2198 ClassReleaseQueue(context->DeviceObject);
2199 }
2200 }
2201
2202 { // free port-allocated sense buffer if we can detect
2203
2204 if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
2205
2206 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
2207 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2208 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2209 }
2210
2211 } else {
2212
2213 ASSERT(!TEST_FLAG(srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
2214
2215 }
2216 }
2217
2218
2219 //
2220 // Free the context and the Irp.
2221 //
2222
2223 if (Irp->MdlAddress != NULL) {
2224 MmUnlockPages(Irp->MdlAddress);
2225 IoFreeMdl(Irp->MdlAddress);
2226
2227 Irp->MdlAddress = NULL;
2228 }
2229
2230 ClassReleaseRemoveLock(DeviceObject, Irp);
2231
2232 ExFreePool(context);
2233 IoFreeIrp(Irp);
2234
2235 //
2236 // Indicate the I/O system should stop processing the Irp completion.
2237 //
2238
2239 return STATUS_MORE_PROCESSING_REQUIRED;
2240
2241 } // end ClassAsynchronousCompletion()
2242 \f
2243
2244
2245 VOID ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp)
2246 {
2247 PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
2248 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
2249 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
2250 PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
2251 PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
2252 ULONG entireXferLen = currentSp->Parameters.Read.Length;
2253 PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
2254 LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
2255 PTRANSFER_PACKET pkt;
2256 SINGLE_LIST_ENTRY pktList;
2257 PSINGLE_LIST_ENTRY slistEntry;
2258 ULONG numPackets;
2259 KIRQL oldIrql;
2260 ULONG i;
2261
2262 /*
2263 * Compute the number of hw xfers we'll have to do.
2264 * Calculate this without allowing for an overflow condition.
2265 */
2266 ASSERT(fdoData->HwMaxXferLen >= PAGE_SIZE);
2267 numPackets = entireXferLen/fdoData->HwMaxXferLen;
2268 if (entireXferLen % fdoData->HwMaxXferLen){
2269 numPackets++;
2270 }
2271
2272 /*
2273 * First get all the TRANSFER_PACKETs that we'll need at once.
2274 * Use our 'simple' slist functions since we don't need interlocked.
2275 */
2276 SimpleInitSlistHdr(&pktList);
2277 for (i = 0; i < numPackets; i++){
2278 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
2279 if (pkt){
2280 SimplePushSlist(&pktList, &pkt->SlistEntry);
2281 }
2282 else {
2283 break;
2284 }
2285 }
2286
2287 if (i == numPackets){
2288 /*
2289 * Initialize the original IRP's status to success.
2290 * If any of the packets fail, they will set it to an error status.
2291 * The IoStatus.Information field will be incremented to the
2292 * transfer length as the pieces complete.
2293 */
2294 Irp->IoStatus.Status = STATUS_SUCCESS;
2295 Irp->IoStatus.Information = 0;
2296
2297 /*
2298 * Store the number of transfer pieces inside the original IRP.
2299 * It will be used to count down the pieces as they complete.
2300 */
2301 Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
2302
2303 /*
2304 * We are proceeding with the transfer.
2305 * Mark the client IRP pending since it may complete on a different thread.
2306 */
2307 IoMarkIrpPending(Irp);
2308
2309 /*
2310 * Transmit the pieces of the transfer.
2311 */
2312 while (entireXferLen > 0){
2313 ULONG thisPieceLen = MIN(fdoData->HwMaxXferLen, entireXferLen);
2314
2315 /*
2316 * Set up a TRANSFER_PACKET for this piece and send it.
2317 */
2318 slistEntry = SimplePopSlist(&pktList);
2319 ASSERT(slistEntry);
2320 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2321 SetupReadWriteTransferPacket( pkt,
2322 bufPtr,
2323 thisPieceLen,
2324 targetLocation,
2325 Irp);
2326 SubmitTransferPacket(pkt);
2327
2328 entireXferLen -= thisPieceLen;
2329 bufPtr += thisPieceLen;
2330 targetLocation.QuadPart += thisPieceLen;
2331 }
2332 ASSERT(SimpleIsSlistEmpty(&pktList));
2333 }
2334 else if (i >= 1){
2335 /*
2336 * We were unable to get all the TRANSFER_PACKETs we need,
2337 * but we did get at least one.
2338 * That means that we are in extreme low-memory stress.
2339 * We'll try doing this transfer using a single packet.
2340 * The port driver is certainly also in stress, so use one-page
2341 * transfers.
2342 */
2343
2344 /*
2345 * Free all but one of the TRANSFER_PACKETs.
2346 */
2347 while (i-- > 1){
2348 slistEntry = SimplePopSlist(&pktList);
2349 ASSERT(slistEntry);
2350 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2351 EnqueueFreeTransferPacket(Fdo, pkt);
2352 }
2353
2354 /*
2355 * Get the single TRANSFER_PACKET that we'll be using.
2356 */
2357 slistEntry = SimplePopSlist(&pktList);
2358 ASSERT(slistEntry);
2359 ASSERT(SimpleIsSlistEmpty(&pktList));
2360 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2361 DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt));
2362
2363 /*
2364 * Set default status and the number of transfer packets (one)
2365 * inside the original irp.
2366 */
2367 Irp->IoStatus.Status = STATUS_SUCCESS;
2368 Irp->IoStatus.Information = 0;
2369 Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
2370
2371 /*
2372 * Mark the client irp pending since it may complete on
2373 * another thread.
2374 */
2375 IoMarkIrpPending(Irp);
2376
2377 /*
2378 * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
2379 */
2380 SetupReadWriteTransferPacket( pkt,
2381 bufPtr,
2382 entireXferLen,
2383 targetLocation,
2384 Irp);
2385 InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
2386 StepLowMemRetry(pkt);
2387 }
2388 else {
2389 /*
2390 * We were unable to get ANY TRANSFER_PACKETs.
2391 * Defer this client irp until some TRANSFER_PACKETs free up.
2392 */
2393 DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp));
2394 IoMarkIrpPending(Irp);
2395 EnqueueDeferredClientIrp(fdoData, Irp);
2396 }
2397
2398 }
2399
2400
2401 /*++////////////////////////////////////////////////////////////////////////////
2402
2403 ClassIoComplete()
2404
2405 Routine Description:
2406
2407 This routine executes when the port driver has completed a request.
2408 It looks at the SRB status in the completing SRB and if not success
2409 it checks for valid request sense buffer information. If valid, the
2410 info is used to update status with more precise message of type of
2411 error. This routine deallocates the SRB.
2412
2413 This routine should only be placed on the stack location for a class
2414 driver FDO.
2415
2416 Arguments:
2417
2418 Fdo - Supplies the device object which represents the logical
2419 unit.
2420
2421 Irp - Supplies the Irp which has completed.
2422
2423 Context - Supplies a pointer to the SRB.
2424
2425 Return Value:
2426
2427 NT status
2428
2429 --*/
2430 NTSTATUS
2431 ClassIoComplete(
2432 IN PDEVICE_OBJECT Fdo,
2433 IN PIRP Irp,
2434 IN PVOID Context
2435 )
2436 {
2437 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
2438 PSCSI_REQUEST_BLOCK srb = Context;
2439 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2440 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
2441 NTSTATUS status;
2442 BOOLEAN retry;
2443 BOOLEAN callStartNextPacket;
2444
2445 ASSERT(fdoExtension->CommonExtension.IsFdo);
2446
2447 //
2448 // Check SRB status for success of completing request.
2449 //
2450
2451 if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
2452 ULONG retryInterval;
2453
2454 DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
2455
2456 //
2457 // Release the queue if it is frozen.
2458 //
2459
2460 if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
2461 ClassReleaseQueue(Fdo);
2462 }
2463
2464 retry = ClassInterpretSenseInfo(
2465 Fdo,
2466 srb,
2467 irpStack->MajorFunction,
2468 irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
2469 irpStack->Parameters.DeviceIoControl.IoControlCode :
2470 0,
2471 MAXIMUM_RETRIES -
2472 ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
2473 &status,
2474 &retryInterval);
2475
2476 //
2477 // If the status is verified required and the this request
2478 // should bypass verify required then retry the request.
2479 //
2480
2481 if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
2482 status == STATUS_VERIFY_REQUIRED) {
2483
2484 status = STATUS_IO_DEVICE_ERROR;
2485 retry = TRUE;
2486 }
2487
2488 if (retry && (irpStack->Parameters.Others.Argument4--)) {
2489
2490 //
2491 // Retry request.
2492 //
2493
2494 DebugPrint((1, "Retry request %p\n", Irp));
2495
2496 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2497 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2498 }
2499
2500 RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
2501 return STATUS_MORE_PROCESSING_REQUIRED;
2502 }
2503
2504 } else {
2505
2506 //
2507 // Set status for successful request
2508 //
2509 fdoData->LoggedTURFailureSinceLastIO = FALSE;
2510 ClasspPerfIncrementSuccessfulIo(fdoExtension);
2511 status = STATUS_SUCCESS;
2512 } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
2513
2514
2515 //
2516 // ensure we have returned some info, and it matches what the
2517 // original request wanted for PAGING operations only
2518 //
2519
2520 if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
2521 ASSERT(Irp->IoStatus.Information != 0);
2522 ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
2523 }
2524
2525 //
2526 // remember if the caller wanted to skip calling IoStartNextPacket.
2527 // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
2528 // calls. this setting only affects device objects with StartIo routines.
2529 //
2530
2531 callStartNextPacket = !TEST_FLAG(srb->SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
2532 if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
2533 callStartNextPacket = FALSE;
2534 }
2535
2536 //
2537 // Free the srb
2538 //
2539
2540 if(!TEST_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
2541
2542 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2543 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2544 }
2545
2546 if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
2547 ClassFreeOrReuseSrb(fdoExtension, srb);
2548 }
2549 else {
2550 DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
2551 ExFreePool(srb);
2552 }
2553
2554 } else {
2555
2556 DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
2557 "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
2558 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2559 DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
2560 " because SRB_CLASS_FLAGS_PERSISTANT set\n",
2561 srb->SenseInfoBuffer));
2562 }
2563
2564 }
2565
2566 //
2567 // Set status in completing IRP.
2568 //
2569
2570 Irp->IoStatus.Status = status;
2571
2572 //
2573 // Set the hard error if necessary.
2574 //
2575
2576 if (!NT_SUCCESS(status) &&
2577 IoIsErrorUserInduced(status) &&
2578 (Irp->Tail.Overlay.Thread != NULL)
2579 ) {
2580
2581 //
2582 // Store DeviceObject for filesystem, and clear
2583 // in IoStatus.Information field.
2584 //
2585
2586 IoSetHardErrorOrVerifyDevice(Irp, Fdo);
2587 Irp->IoStatus.Information = 0;
2588 }
2589
2590 //
2591 // If pending has be returned for this irp then mark the current stack as
2592 // pending.
2593 //
2594
2595 if (Irp->PendingReturned) {
2596 IoMarkIrpPending(Irp);
2597 }
2598
2599 if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
2600 if (callStartNextPacket) {
2601 KIRQL oldIrql;
2602 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
2603 IoStartNextPacket(Fdo, FALSE);
2604 KeLowerIrql(oldIrql);
2605 }
2606 }
2607
2608 ClassReleaseRemoveLock(Fdo, Irp);
2609
2610 return status;
2611
2612 } // end ClassIoComplete()
2613
2614
2615 /*++////////////////////////////////////////////////////////////////////////////
2616
2617 ClassSendSrbSynchronous()
2618
2619 Routine Description:
2620
2621 This routine is called by SCSI device controls to complete an
2622 SRB and send it to the port driver synchronously (ie wait for
2623 completion). The CDB is already completed along with the SRB CDB
2624 size and request timeout value.
2625
2626 Arguments:
2627
2628 Fdo - Supplies the functional device object which represents the target.
2629
2630 Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
2631
2632 BufferAddress - Supplies the address of the buffer.
2633
2634 BufferLength - Supplies the length in bytes of the buffer.
2635
2636 WriteToDevice - Indicates the data should be transfer to the device.
2637
2638 Return Value:
2639
2640 NTSTATUS indicating the final results of the operation.
2641
2642 If NT_SUCCESS(), then the amount of usable data is contained in the field
2643 Srb->DataTransferLength
2644
2645 --*/
2646 NTSTATUS
2647 ClassSendSrbSynchronous(
2648 PDEVICE_OBJECT Fdo,
2649 PSCSI_REQUEST_BLOCK Srb,
2650 PVOID BufferAddress,
2651 ULONG BufferLength,
2652 BOOLEAN WriteToDevice
2653 )
2654 {
2655
2656 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2657 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
2658 IO_STATUS_BLOCK ioStatus;
2659 ULONG controlType;
2660 PIRP irp;
2661 PIO_STACK_LOCATION irpStack;
2662 KEVENT event;
2663 PUCHAR senseInfoBuffer;
2664 ULONG retryCount = MAXIMUM_RETRIES;
2665 NTSTATUS status;
2666 BOOLEAN retry;
2667
2668 //
2669 // NOTE: This code is only pagable because we are not freezing
2670 // the queue. Allowing the queue to be frozen from a pagable
2671 // routine could leave the queue frozen as we try to page in
2672 // the code to unfreeze the queue. The result would be a nice
2673 // case of deadlock. Therefore, since we are unfreezing the
2674 // queue regardless of the result, just set the NO_FREEZE_QUEUE
2675 // flag in the SRB.
2676 //
2677
2678 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2679 ASSERT(fdoExtension->CommonExtension.IsFdo);
2680
2681 //
2682 // Write length to SRB.
2683 //
2684
2685 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2686
2687 //
2688 // Set SCSI bus address.
2689 //
2690
2691 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2692
2693 //
2694 // Enable auto request sense.
2695 //
2696
2697 Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
2698
2699 //
2700 // Sense buffer is in aligned nonpaged pool.
2701 //
2702 //
2703 senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
2704 SENSE_BUFFER_SIZE,
2705 '7CcS');
2706
2707 if (senseInfoBuffer == NULL) {
2708
2709 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
2710 "buffer\n"));
2711 return(STATUS_INSUFFICIENT_RESOURCES);
2712 }
2713
2714 Srb->SenseInfoBuffer = senseInfoBuffer;
2715 Srb->DataBuffer = BufferAddress;
2716
2717 //
2718 // Start retries here.
2719 //
2720
2721 retry:
2722
2723 //
2724 // use fdoextension's flags by default.
2725 // do not move out of loop, as the flag may change due to errors
2726 // sending this command.
2727 //
2728
2729 Srb->SrbFlags = fdoExtension->SrbFlags;
2730
2731 if(BufferAddress != NULL) {
2732 if(WriteToDevice) {
2733 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
2734 } else {
2735 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
2736 }
2737 }
2738
2739 //
2740 // Initialize the QueueAction field.
2741 //
2742
2743 Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
2744
2745 //
2746 // Disable synchronous transfer for these requests.
2747 // Disable freezing the queue, since all we do is unfreeze it anyways.
2748 //
2749
2750 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
2751 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
2752
2753 //
2754 // Set the event object to the unsignaled state.
2755 // It will be used to signal request completion.
2756 //
2757
2758 KeInitializeEvent(&event, NotificationEvent, FALSE);
2759
2760 //
2761 // Build device I/O control request with METHOD_NEITHER data transfer.
2762 // We'll queue a completion routine to cleanup the MDL's and such ourself.
2763 //
2764
2765 irp = IoAllocateIrp(
2766 (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
2767 FALSE);
2768
2769 if(irp == NULL) {
2770 ExFreePool(senseInfoBuffer);
2771 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
2772 return(STATUS_INSUFFICIENT_RESOURCES);
2773 }
2774
2775 //
2776 // Get next stack location.
2777 //
2778
2779 irpStack = IoGetNextIrpStackLocation(irp);
2780
2781 //
2782 // Set up SRB for execute scsi request. Save SRB address in next stack
2783 // for the port driver.
2784 //
2785
2786 irpStack->MajorFunction = IRP_MJ_SCSI;
2787 irpStack->Parameters.Scsi.Srb = Srb;
2788
2789 IoSetCompletionRoutine(irp,
2790 ClasspSendSynchronousCompletion,
2791 Srb,
2792 TRUE,
2793 TRUE,
2794 TRUE);
2795
2796 irp->UserIosb = &ioStatus;
2797 irp->UserEvent = &event;
2798
2799 if(BufferAddress) {
2800 //
2801 // Build an MDL for the data buffer and stick it into the irp. The
2802 // completion routine will unlock the pages and free the MDL.
2803 //
2804
2805 irp->MdlAddress = IoAllocateMdl( BufferAddress,
2806 BufferLength,
2807 FALSE,
2808 FALSE,
2809 irp );
2810 if (irp->MdlAddress == NULL) {
2811 ExFreePool(senseInfoBuffer);
2812 Srb->SenseInfoBuffer = NULL;
2813 IoFreeIrp( irp );
2814 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
2815 return STATUS_INSUFFICIENT_RESOURCES;
2816 }
2817
2818 _SEH2_TRY {
2819
2820 //
2821 // the io manager unlocks these pages upon completion
2822 //
2823
2824 MmProbeAndLockPages( irp->MdlAddress,
2825 KernelMode,
2826 (WriteToDevice ? IoReadAccess :
2827 IoWriteAccess));
2828
2829 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2830 status = _SEH2_GetExceptionCode();
2831
2832 ExFreePool(senseInfoBuffer);
2833 Srb->SenseInfoBuffer = NULL;
2834 IoFreeMdl(irp->MdlAddress);
2835 IoFreeIrp(irp);
2836
2837 DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
2838 "locking buffer\n", status));
2839 return status;
2840 } _SEH2_END;
2841 }
2842
2843 //
2844 // Set the transfer length.
2845 //
2846
2847 Srb->DataTransferLength = BufferLength;
2848
2849 //
2850 // Zero out status.
2851 //
2852
2853 Srb->ScsiStatus = Srb->SrbStatus = 0;
2854 Srb->NextSrb = 0;
2855
2856 //
2857 // Set up IRP Address.
2858 //
2859
2860 Srb->OriginalRequest = irp;
2861
2862 //
2863 // Call the port driver with the request and wait for it to complete.
2864 //
2865
2866 status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
2867
2868 if (status == STATUS_PENDING) {
2869 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
2870 status = ioStatus.Status;
2871 }
2872
2873 //
2874 // Check that request completed without error.
2875 //
2876
2877 if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
2878
2879 ULONG retryInterval;
2880
2881 DBGTRACE(ClassDebugWarning, ("ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb, DBGGETSCSIOPSTR(Srb), DBGGETSRBSTATUSSTR(Srb), (ULONG)Srb->SrbStatus, status, DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));
2882
2883 //
2884 // assert that the queue is not frozen
2885 //
2886
2887 ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
2888
2889 //
2890 // Update status and determine if request should be retried.
2891 //
2892
2893 retry = ClassInterpretSenseInfo(Fdo,
2894 Srb,
2895 IRP_MJ_SCSI,
2896 0,
2897 MAXIMUM_RETRIES - retryCount,
2898 &status,
2899 &retryInterval);
2900
2901
2902 if (retry) {
2903
2904 if ((status == STATUS_DEVICE_NOT_READY &&
2905 ((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode ==
2906 SCSI_ADSENSE_LUN_NOT_READY) ||
2907 (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
2908
2909 LARGE_INTEGER delay;
2910
2911 //
2912 // Delay for at least 2 seconds.
2913 //
2914
2915 if(retryInterval < 2) {
2916 retryInterval = 2;
2917 }
2918
2919 delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval);
2920
2921 //
2922 // Stall for a while to let the device become ready
2923 //
2924
2925 KeDelayExecutionThread(KernelMode, FALSE, &delay);
2926
2927 }
2928
2929 //
2930 // If retries are not exhausted then retry this operation.
2931 //
2932
2933 if (retryCount--) {
2934
2935 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
2936 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
2937 }
2938
2939 goto retry;
2940 }
2941 }
2942
2943 } else {
2944 fdoData->LoggedTURFailureSinceLastIO = FALSE;
2945 status = STATUS_SUCCESS;
2946 }
2947
2948 //
2949 // required even though we allocated our own, since the port driver may
2950 // have allocated one also
2951 //
2952
2953 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
2954 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
2955 }
2956
2957 Srb->SenseInfoBuffer = NULL;
2958 ExFreePool(senseInfoBuffer);
2959
2960 return status;
2961 }
2962
2963 \f
2964 /*++////////////////////////////////////////////////////////////////////////////
2965
2966 ClassInterpretSenseInfo()
2967
2968 Routine Description:
2969
2970 This routine interprets the data returned from the SCSI
2971 request sense. It determines the status to return in the
2972 IRP and whether this request can be retried.
2973
2974 Arguments:
2975
2976 DeviceObject - Supplies the device object associated with this request.
2977
2978 Srb - Supplies the scsi request block which failed.
2979
2980 MajorFunctionCode - Supplies the function code to be used for logging.
2981
2982 IoDeviceCode - Supplies the device code to be used for logging.
2983
2984 Status - Returns the status for the request.
2985
2986 Return Value:
2987
2988 BOOLEAN TRUE: Drivers should retry this request.
2989 FALSE: Drivers should not retry this request.
2990
2991 --*/
2992 BOOLEAN
2993 ClassInterpretSenseInfo(
2994 IN PDEVICE_OBJECT Fdo,
2995 IN PSCSI_REQUEST_BLOCK Srb,
2996 IN UCHAR MajorFunctionCode,
2997 IN ULONG IoDeviceCode,
2998 IN ULONG RetryCount,
2999 OUT NTSTATUS *Status,
3000 OUT OPTIONAL ULONG *RetryInterval
3001 )
3002 {
3003 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
3004 PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
3005 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
3006
3007 PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
3008
3009 BOOLEAN retry = TRUE;
3010 BOOLEAN logError = FALSE;
3011 BOOLEAN unhandledError = FALSE;
3012 BOOLEAN incrementErrorCount = FALSE;
3013
3014 ULONG badSector = 0;
3015 ULONG uniqueId = 0;
3016
3017 NTSTATUS logStatus;
3018
3019 ULONG readSector;
3020 ULONG index;
3021
3022 ULONG retryInterval = 0;
3023 KIRQL oldIrql;
3024
3025
3026 logStatus = -1;
3027
3028 if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
3029
3030 //
3031 // Log anything remotely incorrect about paging i/o
3032 //
3033
3034 logError = TRUE;
3035 uniqueId = 301;
3036 logStatus = IO_WARNING_PAGING_FAILURE;
3037 }
3038
3039 //
3040 // Check that request sense buffer is valid.
3041 //
3042
3043 ASSERT(fdoExtension->CommonExtension.IsFdo);
3044
3045
3046 //
3047 // must handle the SRB_STATUS_INTERNAL_ERROR case first,
3048 // as it has all the flags set.
3049 //
3050
3051 if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
3052
3053 DebugPrint((ClassDebugSenseInfo,
3054 "ClassInterpretSenseInfo: Internal Error code is %x\n",
3055 Srb->InternalStatus));
3056
3057 retry = FALSE;
3058 *Status = Srb->InternalStatus;
3059
3060 } else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
3061 (Srb->SenseInfoBufferLength >=
3062 offsetof(SENSE_DATA, CommandSpecificInformation))) {
3063
3064 //
3065 // Zero the additional sense code and additional sense code qualifier
3066 // if they were not returned by the device.
3067 //
3068
3069 readSector = senseBuffer->AdditionalSenseLength +
3070 offsetof(SENSE_DATA, AdditionalSenseLength);
3071
3072 if (readSector > Srb->SenseInfoBufferLength) {
3073 readSector = Srb->SenseInfoBufferLength;
3074 }
3075
3076 if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCode)) {
3077 senseBuffer->AdditionalSenseCode = 0;
3078 }
3079
3080 if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCodeQualifier)) {
3081 senseBuffer->AdditionalSenseCodeQualifier = 0;
3082 }
3083
3084 DebugPrint((ClassDebugSenseInfo,
3085 "ClassInterpretSenseInfo: Error code is %x\n",
3086 senseBuffer->ErrorCode));
3087 DebugPrint((ClassDebugSenseInfo,
3088 "ClassInterpretSenseInfo: Sense key is %x\n",
3089 senseBuffer->SenseKey));
3090 DebugPrint((ClassDebugSenseInfo,
3091 "ClassInterpretSenseInfo: Additional sense code is %x\n",
3092 senseBuffer->AdditionalSenseCode));
3093 DebugPrint((ClassDebugSenseInfo,
3094 "ClassInterpretSenseInfo: Additional sense code qualifier "
3095 "is %x\n",
3096 senseBuffer->AdditionalSenseCodeQualifier));
3097
3098
3099 switch (senseBuffer->SenseKey & 0xf) {
3100
3101 case SCSI_SENSE_NOT_READY: {
3102
3103 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3104 "Device not ready\n"));
3105 *Status = STATUS_DEVICE_NOT_READY;
3106
3107 switch (senseBuffer->AdditionalSenseCode) {
3108
3109 case SCSI_ADSENSE_LUN_NOT_READY: {
3110
3111 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3112 "Lun not ready\n"));
3113
3114 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3115
3116 case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
3117 DEVICE_EVENT_BECOMING_READY notReady;
3118
3119 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3120 "Operation In Progress\n"));
3121 retryInterval = NOT_READY_RETRY_INTERVAL;
3122
3123 RtlZeroMemory(&notReady, sizeof(DEVICE_EVENT_BECOMING_READY));
3124 notReady.Version = 1;
3125 notReady.Reason = 2;
3126 notReady.Estimated100msToReady = retryInterval * 10;
3127 ClasspSendNotification(fdoExtension,
3128 &GUID_IO_DEVICE_BECOMING_READY,
3129 sizeof(DEVICE_EVENT_BECOMING_READY),
3130 &notReady);
3131
3132 break;
3133 }
3134
3135 case SCSI_SENSEQ_BECOMING_READY: {
3136 DEVICE_EVENT_BECOMING_READY notReady;
3137
3138 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3139 "In process of becoming ready\n"));
3140 retryInterval = NOT_READY_RETRY_INTERVAL;
3141
3142 RtlZeroMemory(&notReady, sizeof(DEVICE_EVENT_BECOMING_READY));
3143 notReady.Version = 1;
3144 notReady.Reason = 1;
3145 notReady.Estimated100msToReady = retryInterval * 10;
3146 ClasspSendNotification(fdoExtension,
3147 &GUID_IO_DEVICE_BECOMING_READY,
3148 sizeof(DEVICE_EVENT_BECOMING_READY),
3149 &notReady);
3150 break;
3151 }
3152
3153 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
3154 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3155 "Long write in progress\n"));
3156 retry = FALSE;
3157 break;
3158 }
3159
3160 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
3161 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3162 "Manual intervention required\n"));
3163 *Status = STATUS_NO_MEDIA_IN_DEVICE;
3164 retry = FALSE;
3165 break;
3166 }
3167
3168 case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
3169 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3170 "Format in progress\n"));
3171 retry = FALSE;
3172 break;
3173 }
3174
3175 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
3176
3177 if(!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
3178 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
3179
3180 DebugPrint((ClassDebugSenseInfo,
3181 "ClassInterpretSenseInfo: "
3182 "not ready, cause unknown\n"));
3183 /*
3184 Many non-WHQL certified drives (mostly CD-RW) return
3185 this when they have no media instead of the obvious
3186 choice of:
3187
3188 SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
3189
3190 These drives should not pass WHQL certification due
3191 to this discrepency.
3192
3193 */
3194 retry = FALSE;
3195 break;
3196
3197 } else {
3198
3199 //
3200 // Treat this as init command required and fall through.
3201 //
3202 }
3203 }
3204
3205 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
3206 default: {
3207 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3208 "Initializing command required\n"));
3209
3210 //
3211 // This sense code/additional sense code
3212 // combination may indicate that the device
3213 // needs to be started. Send an start unit if this
3214 // is a disk device.
3215 //
3216
3217 if(TEST_FLAG(fdoExtension->DeviceFlags,
3218 DEV_SAFE_START_UNIT) &&
3219 !TEST_FLAG(Srb->SrbFlags,
3220 SRB_CLASS_FLAGS_LOW_PRIORITY)) {
3221 ClassSendStartUnit(Fdo);
3222 }
3223 break;
3224 }
3225
3226
3227 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
3228 break;
3229 }
3230
3231 case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
3232 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3233 "No Media in device.\n"));
3234 *Status = STATUS_NO_MEDIA_IN_DEVICE;
3235 retry = FALSE;
3236
3237 //
3238 // signal MCN that there isn't any media in the device
3239 //
3240 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
3241 DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
3242 "No Media in a non-removable device %p\n",
3243 Fdo));
3244 }
3245 ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
3246
3247 break;
3248 }
3249 } // end switch (senseBuffer->AdditionalSenseCode)
3250
3251 break;
3252 } // end SCSI_SENSE_NOT_READY
3253
3254 case SCSI_SENSE_DATA_PROTECT: {
3255 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3256 "Media write protected\n"));
3257 *Status = STATUS_MEDIA_WRITE_PROTECTED;
3258 retry = FALSE;
3259 break;
3260 } // end SCSI_SENSE_DATA_PROTECT
3261
3262 case SCSI_SENSE_MEDIUM_ERROR: {
3263 DebugPrint((ClassDebugSenseInfo,"ClassInterpretSenseInfo: "
3264 "Medium Error (bad block)\n"));
3265 *Status = STATUS_DEVICE_DATA_ERROR;
3266
3267 retry = FALSE;
3268 logError = TRUE;
3269 uniqueId = 256;
3270 logStatus = IO_ERR_BAD_BLOCK;
3271
3272 //
3273 // Check if this error is due to unknown format
3274 //
3275 if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA){
3276
3277 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3278
3279 case SCSI_SENSEQ_UNKNOWN_FORMAT: {
3280
3281 *Status = STATUS_UNRECOGNIZED_MEDIA;
3282
3283 //
3284 // Log error only if this is a paging request
3285 //
3286 if(!TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
3287 logError = FALSE;
3288 }
3289 break;
3290 }
3291
3292 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
3293
3294 *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
3295 logError = FALSE;
3296 break;
3297
3298 }
3299 default: {
3300 break;
3301 }
3302 } // end switch AdditionalSenseCodeQualifier
3303
3304 } // end SCSI_ADSENSE_INVALID_MEDIA
3305
3306 break;
3307
3308 } // end SCSI_SENSE_MEDIUM_ERROR
3309
3310 case SCSI_SENSE_HARDWARE_ERROR: {
3311 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3312 "Hardware error\n"));
3313 *Status = STATUS_IO_DEVICE_ERROR;
3314 logError = TRUE;
3315 uniqueId = 257;
3316 logStatus = IO_ERR_CONTROLLER_ERROR;
3317 break;
3318 } // end SCSI_SENSE_HARDWARE_ERROR
3319
3320 case SCSI_SENSE_ILLEGAL_REQUEST: {
3321
3322 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3323 "Illegal SCSI request\n"));
3324 *Status = STATUS_INVALID_DEVICE_REQUEST;
3325 retry = FALSE;
3326
3327 switch (senseBuffer->AdditionalSenseCode) {
3328
3329 case SCSI_ADSENSE_ILLEGAL_COMMAND: {
3330 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3331 "Illegal command\n"));
3332 break;
3333 }
3334
3335 case SCSI_ADSENSE_ILLEGAL_BLOCK: {
3336 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3337 "Illegal block address\n"));
3338 *Status = STATUS_NONEXISTENT_SECTOR;
3339 break;
3340 }
3341
3342 case SCSI_ADSENSE_INVALID_LUN: {
3343 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3344 "Invalid LUN\n"));
3345 *Status = STATUS_NO_SUCH_DEVICE;
3346 break;
3347 }
3348
3349 case SCSI_ADSENSE_MUSIC_AREA: {
3350 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3351 "Music area\n"));
3352 break;
3353 }
3354
3355 case SCSI_ADSENSE_DATA_AREA: {
3356 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3357 "Data area\n"));
3358 break;
3359 }
3360
3361 case SCSI_ADSENSE_VOLUME_OVERFLOW: {
3362 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3363 "Volume overflow\n"));
3364 break;
3365 }
3366
3367 case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
3368 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3369 "Copy protection failure\n"));
3370
3371 *Status = STATUS_COPY_PROTECTION_FAILURE;
3372
3373 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3374 case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
3375 DebugPrint((ClassDebugSenseInfo,
3376 "ClassInterpretSenseInfo: "
3377 "Authentication failure\n"));
3378 *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
3379 break;
3380 case SCSI_SENSEQ_KEY_NOT_PRESENT:
3381 DebugPrint((ClassDebugSenseInfo,
3382 "ClassInterpretSenseInfo: "
3383 "Key not present\n"));
3384 *Status = STATUS_CSS_KEY_NOT_PRESENT;
3385 break;
3386 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
3387 DebugPrint((ClassDebugSenseInfo,
3388 "ClassInterpretSenseInfo: "
3389 "Key not established\n"));
3390 *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
3391 break;
3392 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
3393 DebugPrint((ClassDebugSenseInfo,
3394 "ClassInterpretSenseInfo: "
3395 "Read of scrambled sector w/o "
3396 "authentication\n"));
3397 *Status = STATUS_CSS_SCRAMBLED_SECTOR;
3398 break;
3399 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
3400 DebugPrint((ClassDebugSenseInfo,
3401 "ClassInterpretSenseInfo: "
3402 "Media region does not logical unit "
3403 "region\n"));
3404 *Status = STATUS_CSS_REGION_MISMATCH;
3405 break;
3406 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
3407 DebugPrint((ClassDebugSenseInfo,
3408 "ClassInterpretSenseInfo: "
3409 "Region set error -- region may "
3410 "be permanent\n"));
3411 *Status = STATUS_CSS_RESETS_EXHAUSTED;
3412 break;
3413 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
3414
3415 break;
3416 }
3417
3418
3419 case SCSI_ADSENSE_INVALID_CDB: {
3420 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3421 "Invalid CDB\n"));
3422
3423 //
3424 // Note: the retry interval is not typically used.
3425 // it is set here only because a ClassErrorHandler
3426 // cannot set the retryInterval, and the error may
3427 // require a few commands to be sent to clear whatever
3428 // caused this condition (i.e. disk clears the write
3429 // cache, requiring at least two commands)
3430 //
3431 // hopefully, this shortcoming can be changed for
3432 // blackcomb.
3433 //
3434
3435 retryInterval = 3;
3436 break;
3437 }
3438
3439 } // end switch (senseBuffer->AdditionalSenseCode)
3440
3441 break;
3442 } // end SCSI_SENSE_ILLEGAL_REQUEST
3443
3444 case SCSI_SENSE_UNIT_ATTENTION: {
3445
3446 PVPB vpb;
3447 ULONG count;
3448
3449 //
3450 // A media change may have occured so increment the change
3451 // count for the physical device
3452 //
3453
3454 count = InterlockedIncrement(&fdoExtension->MediaChangeCount);
3455 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3456 "Media change count for device %d incremented to %#lx\n",
3457 fdoExtension->DeviceNumber, count));
3458
3459
3460 switch (senseBuffer->AdditionalSenseCode) {
3461 case SCSI_ADSENSE_MEDIUM_CHANGED: {
3462 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3463 "Media changed\n"));
3464
3465 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
3466 DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
3467 "Media Changed on non-removable device %p\n",
3468 Fdo));
3469 }
3470 ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
3471 break;
3472 }
3473
3474 case SCSI_ADSENSE_BUS_RESET: {
3475 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3476 "Bus reset\n"));
3477 break;
3478 }
3479
3480 case SCSI_ADSENSE_OPERATOR_REQUEST: {
3481 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3482
3483 case SCSI_SENSEQ_MEDIUM_REMOVAL: {
3484 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3485 "Ejection request received!\n"));
3486 ClassSendEjectionNotification(fdoExtension);
3487 break;
3488 }
3489
3490 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
3491 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3492 "Operator selected write permit?! "
3493 "(unsupported!)\n"));
3494 break;
3495 }
3496
3497 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
3498 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3499 "Operator selected write protect?! "
3500 "(unsupported!)\n"));
3501 break;
3502 }
3503
3504 }
3505 break;
3506 }
3507
3508 default: {
3509 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3510 "Unit attention\n"));
3511 break;
3512 }
3513
3514 } // end switch (senseBuffer->AdditionalSenseCode)
3515
3516 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
3517 {
3518 //
3519 // TODO : Is the media lockable?
3520 //
3521
3522 if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
3523 {
3524 //
3525 // Set bit to indicate that media may have changed
3526 // and volume needs verification.
3527 //
3528
3529 SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
3530
3531 *Status = STATUS_VERIFY_REQUIRED;
3532 retry = FALSE;
3533 }
3534 }
3535 else
3536 {
3537 *Status = STATUS_IO_DEVICE_ERROR;
3538 }
3539
3540 break;
3541
3542 } // end SCSI_SENSE_UNIT_ATTENTION
3543
3544 case SCSI_SENSE_ABORTED_COMMAND: {
3545 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3546 "Command aborted\n"));
3547 *Status = STATUS_IO_DEVICE_ERROR;
3548 retryInterval = 1;
3549 break;
3550 } // end SCSI_SENSE_ABORTED_COMMAND
3551
3552 case SCSI_SENSE_BLANK_CHECK: {
3553 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3554 "Media blank check\n"));
3555 retry = FALSE;
3556 *Status = STATUS_NO_DATA_DETECTED;
3557 break;
3558 } // end SCSI_SENSE_BLANK_CHECK
3559
3560 case SCSI_SENSE_RECOVERED_ERROR: {
3561
3562 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3563 "Recovered error\n"));
3564 *Status = STATUS_SUCCESS;
3565 retry = FALSE;
3566 logError = TRUE;
3567 uniqueId = 258;
3568
3569 switch(senseBuffer->AdditionalSenseCode) {
3570 case SCSI_ADSENSE_SEEK_ERROR:
3571 case SCSI_ADSENSE_TRACK_ERROR: {
3572 logStatus = IO_ERR_SEEK_ERROR;
3573 break;
3574 }
3575
3576 case SCSI_ADSENSE_REC_DATA_NOECC:
3577 case SCSI_ADSENSE_REC_DATA_ECC: {
3578 logStatus = IO_RECOVERED_VIA_ECC;
3579 break;
3580 }
3581
3582 case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
3583 UCHAR wmiEventData[5];
3584
3585 *((PULONG)wmiEventData) = sizeof(UCHAR);
3586 wmiEventData[sizeof(ULONG)] = senseBuffer->AdditionalSenseCodeQualifier;
3587
3588 //
3589 // Don't log another eventlog if we have already logged once
3590 // NOTE: this should have been interlocked, but the structure
3591 // was publicly defined to use a BOOLEAN (char). Since
3592 // media only reports these errors once per X minutes,
3593 // the potential race condition is nearly non-existant.
3594 // the worst case is duplicate log entries, so ignore.
3595 //
3596
3597 if (fdoExtension->FailurePredicted == 0) {
3598 logError = TRUE;
3599 }
3600 fdoExtension->FailurePredicted = TRUE;
3601 fdoExtension->FailureReason = senseBuffer->AdditionalSenseCodeQualifier;
3602 logStatus = IO_WRN_FAILURE_PREDICTED;
3603
3604 ClassNotifyFailurePredicted(fdoExtension,
3605 (PUCHAR)&wmiEventData,
3606 sizeof(wmiEventData),
3607 0,
3608 4,
3609 Srb->PathId,
3610 Srb->TargetId,
3611 Srb->Lun);
3612 break;
3613 }
3614
3615 default: {
3616 logStatus = IO_ERR_CONTROLLER_ERROR;
3617 break;
3618 }
3619
3620 } // end switch(senseBuffer->AdditionalSenseCode)
3621
3622 if (senseBuffer->IncorrectLength) {
3623
3624 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3625 "Incorrect length detected.\n"));
3626 *Status = STATUS_INVALID_BLOCK_LENGTH ;
3627 }
3628
3629 break;
3630 } // end SCSI_SENSE_RECOVERED_ERROR
3631
3632 case SCSI_SENSE_NO_SENSE: {
3633
3634 //
3635 // Check other indicators.
3636 //
3637
3638 if (senseBuffer->IncorrectLength) {
3639
3640 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3641 "Incorrect length detected.\n"));
3642 *Status = STATUS_INVALID_BLOCK_LENGTH ;
3643 retry = FALSE;
3644
3645 } else {
3646
3647 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3648 "No specific sense key\n"));
3649 *Status = STATUS_IO_DEVICE_ERROR;
3650 retry = TRUE;
3651 }
3652
3653 break;
3654 } // end SCSI_SENSE_NO_SENSE
3655
3656 default: {
3657 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3658 "Unrecognized sense code\n"));
3659 *Status = STATUS_IO_DEVICE_ERROR;
3660 break;
3661 }
3662
3663 } // end switch (senseBuffer->SenseKey & 0xf)
3664
3665 //
3666 // Try to determine the bad sector from the inquiry data.
3667 //
3668
3669 if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_READ ||
3670 ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY ||
3671 ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_WRITE)) {
3672
3673 for (index = 0; index < 4; index++) {
3674 badSector = (badSector << 8) | senseBuffer->Information[index];
3675 }
3676
3677 readSector = 0;
3678 for (index = 0; index < 4; index++) {
3679 readSector = (readSector << 8) | Srb->Cdb[index+2];
3680 }
3681
3682 index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
3683 ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
3684
3685 //
3686 // Make sure the bad sector is within the read sectors.
3687 //
3688
3689 if (!(badSector >= readSector && badSector < readSector + index)) {
3690 badSector = readSector;
3691 }
3692 }
3693
3694 } else {
3695
3696 //
3697 // Request sense buffer not valid. No sense information
3698 // to pinpoint the error. Return general request fail.
3699 //
3700
3701 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3702 "Request sense info not valid. SrbStatus %2x\n",
3703 SRB_STATUS(Srb->SrbStatus)));
3704 retry = TRUE;
3705
3706 switch (SRB_STATUS(Srb->SrbStatus)) {
3707 case SRB_STATUS_INVALID_LUN:
3708 case SRB_STATUS_INVALID_TARGET_ID:
3709 case SRB_STATUS_NO_DEVICE:
3710 case SRB_STATUS_NO_HBA:
3711 case SRB_STATUS_INVALID_PATH_ID: {
3712 *Status = STATUS_NO_SUCH_DEVICE;
3713 retry = FALSE;
3714 break;
3715 }
3716
3717 case SRB_STATUS_COMMAND_TIMEOUT:
3718 case SRB_STATUS_TIMEOUT: {
3719
3720 //
3721 // Update the error count for the device.
3722 //
3723
3724 incrementErrorCount = TRUE;
3725 *Status = STATUS_IO_TIMEOUT;
3726 break;
3727 }
3728
3729 case SRB_STATUS_ABORTED: {
3730
3731 //
3732 // Update the error count for the device.
3733 //
3734
3735 incrementErrorCount = TRUE;
3736 *Status = STATUS_IO_TIMEOUT;
3737 retryInterval = 1;
3738 break;
3739 }
3740
3741
3742 case SRB_STATUS_SELECTION_TIMEOUT: {
3743 logError = TRUE;
3744 logStatus = IO_ERR_NOT_READY;
3745 uniqueId = 260;
3746 *Status = STATUS_DEVICE_NOT_CONNECTED;
3747 retry = FALSE;
3748 break;
3749 }
3750
3751 case SRB_STATUS_DATA_OVERRUN: {
3752 *Status = STATUS_DATA_OVERRUN;
3753 retry = FALSE;
3754 break;
3755 }
3756
3757 case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
3758
3759 //
3760 // Update the error count for the device.
3761 //
3762
3763 incrementErrorCount = TRUE;
3764 *Status = STATUS_IO_DEVICE_ERROR;
3765
3766 //
3767 // If there was phase sequence error then limit the number of
3768 // retries.
3769 //
3770
3771 if (RetryCount > 1 ) {
3772 retry = FALSE;
3773 }
3774
3775 break;
3776 }
3777
3778 case SRB_STATUS_REQUEST_FLUSHED: {
3779
3780 //
3781 // If the status needs verification bit is set. Then set
3782 // the status to need verification and no retry; otherwise,
3783 // just retry the request.
3784 //
3785
3786 if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
3787
3788 *Status = STATUS_VERIFY_REQUIRED;
3789 retry = FALSE;
3790
3791 } else {
3792 *Status = STATUS_IO_DEVICE_ERROR;
3793 }
3794
3795 break;
3796 }
3797
3798 case SRB_STATUS_INVALID_REQUEST: {
3799 *Status = STATUS_INVALID_DEVICE_REQUEST;
3800 retry = FALSE;
3801 break;
3802 }
3803
3804 case SRB_STATUS_UNEXPECTED_BUS_FREE:
3805 case SRB_STATUS_PARITY_ERROR:
3806
3807 //
3808 // Update the error count for the device
3809 // and fall through to below
3810 //
3811
3812 incrementErrorCount = TRUE;
3813
3814 case SRB_STATUS_BUS_RESET: {
3815 *Status = STATUS_IO_DEVICE_ERROR;
3816 break;
3817 }
3818
3819 case SRB_STATUS_ERROR: {
3820
3821 *Status = STATUS_IO_DEVICE_ERROR;
3822 if (Srb->ScsiStatus == 0) {
3823
3824 //
3825 // This is some strange return code. Update the error
3826 // count for the device.
3827 //
3828
3829 incrementErrorCount = TRUE;
3830
3831 } if (Srb->ScsiStatus == SCSISTAT_BUSY) {
3832
3833 *Status = STATUS_DEVICE_NOT_READY;
3834
3835 } if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) {
3836
3837 *Status = STATUS_DEVICE_BUSY;
3838 retry = FALSE;
3839 logError = FALSE;
3840
3841 }
3842
3843 break;
3844 }
3845
3846 default: {
3847 logError = TRUE;
3848 logStatus = IO_ERR_CONTROLLER_ERROR;
3849 uniqueId = 259;
3850 *Status = STATUS_IO_DEVICE_ERROR;
3851 unhandledError = TRUE;
3852 break;
3853 }
3854
3855 }
3856
3857 //
3858 // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
3859 // we know from a previous poll when the device will be ready (ETA)
3860 // we should delay the retry more appropriately than just guessing.
3861 //
3862 /*
3863 if (fdoExtension->MediaChangeDetectionInfo &&
3864 fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
3865 TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
3866 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
3867 ) {
3868 // check if Gesn.ReadyTime if greater than current tick count
3869 // if so, delay that long (from 1 to 30 seconds max?)
3870 // else, leave the guess of time alone.
3871 }
3872 */
3873
3874 }
3875
3876 if (incrementErrorCount) {
3877
3878 //
3879 // if any error count occurred, delay the retry of this io by
3880 // at least one second, if caller supports it.
3881 //
3882
3883 if (retryInterval == 0) {
3884 retryInterval = 1;
3885 }
3886 ClasspPerfIncrementErrorCount(fdoExtension);
3887 }
3888
3889 //
3890 // If there is a class specific error handler call it.
3891 //
3892
3893 if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
3894
3895 fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
3896 Srb,
3897 Status,
3898 &retry);
3899 }
3900
3901 //
3902 // If the caller wants to know the suggested retry interval tell them.
3903 //
3904
3905 if(ARGUMENT_PRESENT(RetryInterval)) {
3906 *RetryInterval = retryInterval;
3907 }
3908
3909
3910 /*
3911 * LOG the error:
3912 * Always log the error in our internal log.
3913 * If logError is set, also log the error in the system log.
3914 */
3915 {
3916 ULONG totalSize;
3917 ULONG senseBufferSize = 0;
3918 IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
3919 CLASS_ERROR_LOG_DATA staticErrLogData = {0};
3920
3921 //
3922 // Calculate the total size of the error log entry.
3923 // add to totalSize in the order that they are used.
3924 // the advantage to calculating all the sizes here is
3925 // that we don't have to do a bunch of extraneous checks
3926 // later on in this code path.
3927 //
3928 totalSize = sizeof(IO_ERROR_LOG_PACKET) // required
3929 - sizeof(ULONG) // struct includes one ULONG
3930 + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
3931
3932 //
3933 // also save any available extra sense data, up to the maximum errlog
3934 // packet size . WMI should be used for real-time analysis.
3935 // the event log should only be used for post-mortem debugging.
3936 //
3937 if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
3938 ULONG validSenseBytes;
3939 BOOLEAN validSense;
3940
3941 //
3942 // make sure we can at least access the AdditionalSenseLength field
3943 //
3944 validSense = RTL_CONTAINS_FIELD(senseBuffer,
3945 Srb->SenseInfoBufferLength,
3946 AdditionalSenseLength);
3947 if (validSense) {
3948
3949 //
3950 // if extra info exists, copy the maximum amount of available
3951 // sense data that is safe into the the errlog.
3952 //
3953 validSenseBytes = senseBuffer->AdditionalSenseLength
3954 + offsetof(SENSE_DATA, AdditionalSenseLength);
3955
3956 //
3957 // this is invalid because it causes overflow!
3958 // whoever sent this type of request would cause
3959 // a system crash.
3960 //
3961 ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
3962
3963 //
3964 // set to save the most sense buffer possible
3965 //
3966 senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
3967 senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
3968 } else {
3969 //
3970 // it's smaller than required to read the total number of
3971 // valid bytes, so just use the SenseInfoBufferLength field.
3972 //
3973 senseBufferSize = Srb->SenseInfoBufferLength;
3974 }
3975
3976 /*
3977 * Bump totalSize by the number of extra senseBuffer bytes
3978 * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
3979 * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
3980 */
3981 if (senseBufferSize > sizeof(SENSE_DATA)){
3982 totalSize += senseBufferSize-sizeof(SENSE_DATA);
3983 if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
3984 senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
3985 totalSize = ERROR_LOG_MAXIMUM_SIZE;
3986 }
3987 }
3988 }
3989
3990 //
3991 // If we've used up all of our retry attempts, set the final status to
3992 // reflect the appropriate result.
3993 //
3994 if (retry && RetryCount < MAXIMUM_RETRIES) {
3995 staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
3996 staticErrLogData.ErrorRetried = TRUE;
3997 } else {
3998 staticErrLogEntry.FinalStatus = *Status;
3999 }
4000 if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
4001 staticErrLogData.ErrorPaging = TRUE;
4002 }
4003 if (unhandledError) {
4004 staticErrLogData.ErrorUnhandled = TRUE;
4005 }
4006
4007 //
4008 // Calculate the device offset if there is a geometry.
4009 //
4010 staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
4011 staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
4012 if (logStatus == -1){
4013 staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
4014 } else {
4015 staticErrLogEntry.ErrorCode = logStatus;
4016 }
4017
4018 /*
4019 * The dump data follows the IO_ERROR_LOG_PACKET,
4020 * with the first ULONG of dump data inside the packet.
4021 */
4022 staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG);
4023
4024 staticErrLogEntry.SequenceNumber = 0;
4025 staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
4026 staticErrLogEntry.IoControlCode = IoDeviceCode;
4027 staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
4028 staticErrLogEntry.UniqueErrorValue = uniqueId;
4029
4030 KeQueryTickCount(&staticErrLogData.TickCount);
4031 staticErrLogData.PortNumber = (ULONG)-1;
4032
4033 /*
4034 * Save the entire contents of the SRB.
4035 */
4036 staticErrLogData.Srb = *Srb;
4037
4038 /*
4039 * For our private log, save just the default length of the SENSE_DATA.
4040 */
4041 if (senseBufferSize != 0){
4042 RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
4043 }
4044
4045 /*
4046 * Save the error log in our context.
4047 * We only save the default sense buffer length.
4048 */
4049 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
4050 fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
4051 fdoData->ErrorLogNextIndex++;
4052 fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
4053 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
4054
4055 /*
4056 * If logError is set, also save this log in the system's error log.
4057 * But make sure we don't log TUR failures over and over
4058 * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
4059 */
4060 if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_TEST_UNIT_READY) && logError){
4061 if (fdoData->LoggedTURFailureSinceLastIO){
4062 logError = FALSE;
4063 }
4064 else {
4065 fdoData->LoggedTURFailureSinceLastIO = TRUE;
4066 }
4067 }
4068 if (logError){
4069 PIO_ERROR_LOG_PACKET errorLogEntry;
4070 PCLASS_ERROR_LOG_DATA errlogData;
4071
4072 errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
4073 if (errorLogEntry){
4074 errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
4075
4076 *errorLogEntry = staticErrLogEntry;
4077 *errlogData = staticErrLogData;
4078
4079 /*
4080 * For the system log, copy as much of the sense buffer as possible.
4081 */
4082 if (senseBufferSize != 0) {
4083 RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
4084 }
4085
4086 /*
4087 * Write the error log packet to the system error logging thread.
4088 */
4089 IoWriteErrorLogEntry(errorLogEntry);
4090 }
4091 }
4092 }
4093
4094 return retry;
4095
4096 } // end ClassInterpretSenseInfo()
4097
4098
4099 \f
4100 /*++////////////////////////////////////////////////////////////////////////////
4101
4102 ClassModeSense()
4103
4104 Routine Description:
4105
4106 This routine sends a mode sense command to a target ID and returns
4107 when it is complete.
4108
4109 Arguments:
4110
4111 Fdo - Supplies the functional device object associated with this request.
4112
4113 ModeSenseBuffer - Supplies a buffer to store the sense data.
4114
4115 Length - Supplies the length in bytes of the mode sense buffer.
4116
4117 PageMode - Supplies the page or pages of mode sense data to be retrived.
4118
4119 Return Value:
4120
4121 Length of the transferred data is returned.
4122
4123 --*/
4124 ULONG ClassModeSense( IN PDEVICE_OBJECT Fdo,
4125 IN PCHAR ModeSenseBuffer,
4126 IN ULONG Length,
4127 IN UCHAR PageMode)
4128 {
4129 ULONG lengthTransferred = 0;
4130 PMDL senseBufferMdl;
4131
4132 PAGED_CODE();
4133
4134 senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
4135 if (senseBufferMdl){
4136
4137 TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
4138 if (pkt){
4139 KEVENT event;
4140 NTSTATUS pktStatus;
4141 IRP pseudoIrp = {0};
4142
4143 /*
4144 * Store the number of packets servicing the irp (one)
4145 * inside the original IRP. It will be used to counted down
4146 * to zero when the packet completes.
4147 * Initialize the original IRP's status to success.
4148 * If the packet fails, we will set it to the error status.
4149 */
4150 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
4151 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
4152 pseudoIrp.IoStatus.Information = 0;
4153 pseudoIrp.MdlAddress = senseBufferMdl;
4154
4155 /*
4156 * Set this up as a SYNCHRONOUS transfer, submit it,
4157 * and wait for the packet to complete. The result
4158 * status will be written to the original irp.
4159 */
4160 ASSERT(Length <= 0x0ff);
4161 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
4162 SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, &pseudoIrp);
4163 SubmitTransferPacket(pkt);
4164 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
4165
4166 if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
4167 lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
4168 }
4169 else {
4170 /*
4171 * This request can sometimes fail legitimately
4172 * (e.g. when a SCSI device is attached but turned off)
4173 * so this is not necessarily a device/driver bug.
4174 */
4175 DBGTRACE(ClassDebugWarning, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
4176 }
4177 }
4178
4179 FreeDeviceInputMdl(senseBufferMdl);
4180 }
4181
4182 return lengthTransferred;
4183 }
4184
4185
4186 /*++////////////////////////////////////////////////////////////////////////////
4187
4188 ClassFindModePage()
4189
4190 Routine Description:
4191
4192 This routine scans through the mode sense data and finds the requested
4193 mode sense page code.
4194
4195 Arguments:
4196 ModeSenseBuffer - Supplies a pointer to the mode sense data.
4197
4198 Length - Indicates the length of valid data.
4199
4200 PageMode - Supplies the page mode to be searched for.
4201
4202 Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
4203
4204 Return Value:
4205
4206 A pointer to the the requested mode page. If the mode page was not found
4207 then NULL is return.
4208
4209 --*/
4210 PVOID
4211 ClassFindModePage(
4212 IN PCHAR ModeSenseBuffer,
4213 IN ULONG Length,
4214 IN UCHAR PageMode,
4215 IN BOOLEAN Use6Byte
4216 )
4217 {
4218 PUCHAR limit;
4219 ULONG parameterHeaderLength;
4220 PVOID result = NULL;
4221
4222 limit = ModeSenseBuffer + Length;
4223 parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
4224
4225 if (Length >= parameterHeaderLength) {
4226
4227 PMODE_PARAMETER_HEADER10 modeParam10;
4228 ULONG blockDescriptorLength;
4229
4230 /*
4231 * Skip the mode select header and block descriptors.
4232 */
4233 if (Use6Byte){
4234 blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
4235 }
4236 else {
4237 modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
4238 blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
4239 }
4240
4241 ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
4242
4243 //
4244 // ModeSenseBuffer now points at pages. Walk the pages looking for the
4245 // requested page until the limit is reached.
4246 //
4247
4248 while (ModeSenseBuffer +
4249 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit) {
4250
4251 if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
4252
4253 /*
4254 * found the mode page. make sure it's safe to touch it all
4255 * before returning the pointer to caller
4256 */
4257
4258 if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit) {
4259 /*
4260 * Return NULL since the page is not safe to access in full
4261 */
4262 result = NULL;
4263 }
4264 else {
4265 result = ModeSenseBuffer;
4266 }
4267 break;
4268 }
4269
4270 //
4271 // Advance to the next page which is 4-byte-aligned offset after this page.
4272 //
4273 ModeSenseBuffer +=
4274 ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
4275 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
4276
4277 }
4278 }
4279
4280 return result;
4281 } // end ClassFindModePage()
4282 \f
4283 /*++////////////////////////////////////////////////////////////////////////////
4284
4285 ClassSendSrbAsynchronous()
4286
4287 Routine Description:
4288
4289 This routine takes a partially built Srb and an Irp and sends it down to
4290 the port driver.
4291
4292 This routine must be called with the remove lock held for the specified
4293 Irp.
4294
4295 Arguments:
4296
4297 Fdo - Supplies the functional device object for the orginal request.
4298
4299 Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
4300 CDB and the SRB timeout value must be filled in. The SRB must not be
4301 allocated from zone.
4302
4303 Irp - Supplies the requesting Irp.
4304
4305 BufferAddress - Supplies a pointer to the buffer to be transfered.
4306
4307 BufferLength - Supplies the length of data transfer.
4308
4309 WriteToDevice - Indicates the data transfer will be from system memory to
4310 device.
4311
4312 Return Value:
4313
4314 Returns STATUS_PENDING if the request is dispatched (since the
4315 completion routine may change the irp's status value we cannot simply
4316 return the value of the dispatch)
4317
4318 or returns a status value to indicate why it failed.
4319
4320 --*/
4321 NTSTATUS
4322 ClassSendSrbAsynchronous(
4323 PDEVICE_OBJECT Fdo,
4324 PSCSI_REQUEST_BLOCK Srb,
4325 PIRP Irp,
4326 PVOID BufferAddress,
4327 ULONG BufferLength,
4328 BOOLEAN WriteToDevice
4329 )
4330 {
4331
4332 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
4333 PIO_STACK_LOCATION irpStack;
4334
4335 ULONG savedFlags;
4336
4337 //
4338 // Write length to SRB.
4339 //
4340
4341 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
4342
4343 //
4344 // Set SCSI bus address.
4345 //
4346
4347 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
4348
4349 //
4350 // This is a violation of the SCSI spec but it is required for
4351 // some targets.
4352 //
4353
4354 // Srb->Cdb[1] |= deviceExtension->Lun << 5;
4355
4356 //
4357 // Indicate auto request sense by specifying buffer and size.
4358 //
4359
4360 Srb->SenseInfoBuffer = fdoExtension->SenseData;
4361 Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
4362 Srb->DataBuffer = BufferAddress;
4363
4364 //
4365 // Save the class driver specific flags away.
4366 //
4367
4368 savedFlags = Srb->SrbFlags & SRB_FLAGS_CLASS_DRIVER_RESERVED;
4369
4370 //
4371 // Allow the caller to specify that they do not wish
4372 // IoStartNextPacket() to be called in the completion routine.
4373 //
4374
4375 SET_FLAG(savedFlags, (Srb->SrbFlags & SRB_FLAGS_DONT_START_NEXT_PACKET));
4376
4377 if (BufferAddress != NULL) {
4378
4379 //
4380 // Build Mdl if necessary.
4381 //
4382
4383 if (Irp->MdlAddress == NULL) {
4384
4385 if (IoAllocateMdl(BufferAddress,
4386 BufferLength,
4387 FALSE,
4388 FALSE,
4389 Irp) == NULL) {
4390
4391 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
4392
4393 //
4394 // ClassIoComplete() would have free'd the srb
4395 //
4396
4397 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
4398 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
4399 }
4400 ClassFreeOrReuseSrb(fdoExtension, Srb);
4401 ClassReleaseRemoveLock(Fdo, Irp);
4402 ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
4403
4404 return STATUS_INSUFFICIENT_RESOURCES;
4405 }
4406
4407 MmBuildMdlForNonPagedPool(Irp->MdlAddress);
4408
4409 } else {
4410
4411 //
4412 // Make sure the buffer requested matches the MDL.
4413 //
4414
4415 ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
4416 }
4417
4418 //
4419 // Set read flag.
4420 //
4421
4422 Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN;
4423
4424 } else {
4425
4426 //
4427 // Clear flags.
4428 //
4429
4430 Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
4431 }
4432
4433 //
4434 // Restore saved flags.
4435 //
4436
4437 SET_FLAG(Srb->SrbFlags, savedFlags);
4438
4439 //
4440 // Disable synchronous transfer for these requests.
4441 //
4442
4443 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
4444
4445 //
4446 // Set the transfer length.
4447 //
4448
4449 Srb->DataTransferLength = BufferLength;
4450
4451 //
4452 // Zero out status.
4453 //
4454
4455 Srb->ScsiStatus = Srb->SrbStatus = 0;
4456
4457 Srb->NextSrb = 0;
4458
4459 //
4460 // Save a few parameters in the current stack location.
4461 //
4462
4463 irpStack = IoGetCurrentIrpStackLocation(Irp);
4464
4465 //
4466 // Save retry count in current Irp stack.
4467 //
4468
4469 irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
4470
4471 //
4472 // Set up IoCompletion routine address.
4473 //
4474
4475 IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
4476
4477 //
4478 // Get next stack location and
4479 // set major function code.
4480 //
4481
4482 irpStack = IoGetNextIrpStackLocation(Irp);
4483
4484 irpStack->MajorFunction = IRP_MJ_SCSI;
4485
4486 //
4487 // Save SRB address in next stack for port driver.
4488 //
4489
4490 irpStack->Parameters.Scsi.Srb = Srb;
4491
4492 //
4493 // Set up Irp Address.
4494 //
4495
4496 Srb->OriginalRequest = Irp;
4497
4498 //
4499 // Call the port driver to process the request.
4500 //
4501
4502 IoMarkIrpPending(Irp);
4503
4504 IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
4505
4506 return STATUS_PENDING;
4507
4508 } // end ClassSendSrbAsynchronous()
4509 \f
4510 /*++////////////////////////////////////////////////////////////////////////////
4511
4512 ClassDeviceControlDispatch()
4513
4514 Routine Description:
4515
4516 The routine is the common class driver device control dispatch entry point.
4517 This routine is invokes the device-specific drivers DeviceControl routine,
4518 (which may call the Class driver's common DeviceControl routine).
4519
4520 Arguments:
4521
4522 DeviceObject - Supplies a pointer to the device object for this request.
4523
4524 Irp - Supplies the Irp making the request.
4525
4526 Return Value:
4527
4528 Returns the status returned from the device-specific driver.
4529
4530 --*/
4531 NTSTATUS
4532 ClassDeviceControlDispatch(
4533 PDEVICE_OBJECT DeviceObject,
4534 PIRP Irp
4535 )
4536 {
4537
4538 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
4539 ULONG isRemoved;
4540
4541 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
4542
4543 if(isRemoved) {
4544
4545 ClassReleaseRemoveLock(DeviceObject, Irp);
4546
4547 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
4548 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4549 return STATUS_DEVICE_DOES_NOT_EXIST;
4550 }
4551
4552 //
4553 // Call the class specific driver DeviceControl routine.
4554 // If it doesn't handle it, it will call back into ClassDeviceControl.
4555 //
4556
4557 ASSERT(commonExtension->DevInfo->ClassDeviceControl);
4558
4559 return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
4560 } // end ClassDeviceControlDispatch()
4561
4562 \f
4563 /*++////////////////////////////////////////////////////////////////////////////
4564
4565 ClassDeviceControl()
4566
4567 Routine Description:
4568
4569 The routine is the common class driver device control dispatch function.
4570 This routine is called by a class driver when it get an unrecognized
4571 device control request. This routine will perform the correct action for
4572 common requests such as lock media. If the device request is unknown it
4573 passed down to the next level.
4574
4575 This routine must be called with the remove lock held for the specified
4576 irp.
4577
4578 Arguments:
4579
4580 DeviceObject - Supplies a pointer to the device object for this request.
4581
4582 Irp - Supplies the Irp making the request.
4583
4584 Return Value:
4585
4586 Returns back a STATUS_PENDING or a completion status.
4587
4588 --*/
4589 NTSTATUS
4590 ClassDeviceControl(
4591 PDEVICE_OBJECT DeviceObject,
4592 PIRP Irp
4593 )
4594 {
4595 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
4596
4597 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
4598 PIO_STACK_LOCATION nextStack = NULL;
4599
4600 ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
4601
4602 PSCSI_REQUEST_BLOCK srb = NULL;
4603 PCDB cdb = NULL;
4604
4605 NTSTATUS status;
4606 ULONG modifiedIoControlCode;
4607
4608 //
4609 // If this is a pass through I/O control, set the minor function code
4610 // and device address and pass it to the port driver.
4611 //
4612
4613 if ((controlCode == IOCTL_SCSI_PASS_THROUGH) ||
4614 (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT)) {
4615
4616 PSCSI_PASS_THROUGH scsiPass;
4617
4618 //
4619 // Validiate the user buffer.
4620 //
4621 #if defined (_WIN64)
4622
4623 if (IoIs32bitProcess(Irp)) {
4624
4625 if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
4626
4627 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
4628
4629 ClassReleaseRemoveLock(DeviceObject, Irp);
4630 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4631
4632 status = STATUS_INVALID_PARAMETER;
4633 goto SetStatusAndReturn;
4634 }
4635 }
4636 else
4637 #endif
4638 {
4639 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
4640 sizeof(SCSI_PASS_THROUGH)) {
4641
4642 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
4643
4644 ClassReleaseRemoveLock(DeviceObject, Irp);
4645 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4646
4647 status = STATUS_INVALID_PARAMETER;
4648 goto SetStatusAndReturn;
4649 }
4650 }
4651
4652 IoCopyCurrentIrpStackLocationToNext(Irp);
4653
4654 nextStack = IoGetNextIrpStackLocation(Irp);
4655 nextStack->MinorFunction = 1;
4656
4657 ClassReleaseRemoveLock(DeviceObject, Irp);
4658
4659 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4660 goto SetStatusAndReturn;
4661 }
4662
4663 Irp->IoStatus.Information = 0;
4664
4665 switch (controlCode) {
4666
4667 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
4668
4669 PMOUNTDEV_UNIQUE_ID uniqueId;
4670
4671 if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
4672 status = STATUS_INVALID_PARAMETER;
4673 break;
4674 }
4675
4676 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4677 sizeof(MOUNTDEV_UNIQUE_ID)) {
4678
4679 status = STATUS_BUFFER_TOO_SMALL;
4680 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
4681 break;
4682 }
4683
4684 uniqueId = Irp->AssociatedIrp.SystemBuffer;
4685 uniqueId->UniqueIdLength =
4686 commonExtension->MountedDeviceInterfaceName.Length;
4687
4688 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4689 sizeof(USHORT) + uniqueId->UniqueIdLength) {
4690
4691 status = STATUS_BUFFER_OVERFLOW;
4692 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
4693 break;
4694 }
4695
4696 RtlCopyMemory(uniqueId->UniqueId,
4697 commonExtension->MountedDeviceInterfaceName.Buffer,
4698 uniqueId->UniqueIdLength);
4699
4700 status = STATUS_SUCCESS;
4701 Irp->IoStatus.Information = sizeof(USHORT) +
4702 uniqueId->UniqueIdLength;
4703 break;
4704 }
4705
4706 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
4707
4708 PMOUNTDEV_NAME name;
4709
4710 ASSERT(commonExtension->DeviceName.Buffer);
4711
4712 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4713 sizeof(MOUNTDEV_NAME)) {
4714
4715 status = STATUS_BUFFER_TOO_SMALL;
4716 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4717 break;
4718 }
4719
4720 name = Irp->AssociatedIrp.SystemBuffer;
4721 name->NameLength = commonExtension->DeviceName.Length;
4722
4723 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4724 sizeof(USHORT) + name->NameLength) {
4725
4726 status = STATUS_BUFFER_OVERFLOW;
4727 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4728 break;
4729 }
4730
4731 RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
4732 name->NameLength);
4733
4734 status = STATUS_SUCCESS;
4735 Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
4736 break;
4737 }
4738
4739 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
4740
4741 PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
4742 WCHAR driveLetterNameBuffer[10];
4743 RTL_QUERY_REGISTRY_TABLE queryTable[2];
4744 PWSTR valueName;
4745 UNICODE_STRING driveLetterName;
4746
4747 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4748 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
4749
4750 status = STATUS_BUFFER_TOO_SMALL;
4751 Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
4752 break;
4753 }
4754
4755 valueName = ExAllocatePoolWithTag(
4756 PagedPool,
4757 commonExtension->DeviceName.Length + sizeof(WCHAR),
4758 '8CcS');
4759
4760 if (!valueName) {
4761 status = STATUS_INSUFFICIENT_RESOURCES;
4762 break;
4763 }
4764
4765 RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
4766 commonExtension->DeviceName.Length);
4767 valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
4768
4769 driveLetterName.Buffer = driveLetterNameBuffer;
4770 driveLetterName.MaximumLength = 20;
4771 driveLetterName.Length = 0;
4772
4773 RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
4774 queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
4775 RTL_QUERY_REGISTRY_DIRECT;
4776 queryTable[0].Name = valueName;
4777 queryTable[0].EntryContext = &driveLetterName;
4778
4779 status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
4780 L"\\Registry\\Machine\\System\\DISK",
4781 queryTable, NULL, NULL);
4782
4783 if (!NT_SUCCESS(status)) {
4784 ExFreePool(valueName);
4785 break;
4786 }
4787
4788 if (driveLetterName.Length == 4 &&
4789 driveLetterName.Buffer[0] == '%' &&
4790 driveLetterName.Buffer[1] == ':') {
4791
4792 driveLetterName.Buffer[0] = 0xFF;
4793
4794 } else if (driveLetterName.Length != 4 ||
4795 driveLetterName.Buffer[0] < FirstDriveLetter ||
4796 driveLetterName.Buffer[0] > LastDriveLetter ||
4797 driveLetterName.Buffer[1] != ':') {
4798
4799 status = STATUS_NOT_FOUND;
4800 ExFreePool(valueName);
4801 break;
4802 }
4803
4804 suggestedName = Irp->AssociatedIrp.SystemBuffer;
4805 suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
4806 suggestedName->NameLength = 28;
4807
4808 Irp->IoStatus.Information =
4809 FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
4810
4811 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4812 Irp->IoStatus.Information) {
4813
4814 Irp->IoStatus.Information =
4815 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
4816 status = STATUS_BUFFER_OVERFLOW;
4817 ExFreePool(valueName);
4818 break;
4819 }
4820
4821 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
4822 L"\\Registry\\Machine\\System\\DISK",
4823 valueName);
4824
4825 ExFreePool(valueName);
4826
4827 RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
4828 suggestedName->Name[12] = driveLetterName.Buffer[0];
4829 suggestedName->Name[13] = ':';
4830
4831 //
4832 // NT_SUCCESS(status) based on RtlQueryRegistryValues
4833 //
4834 status = STATUS_SUCCESS;
4835
4836 break;
4837 }
4838
4839 default:
4840 status = STATUS_PENDING;
4841 break;
4842 }
4843
4844 if (status != STATUS_PENDING) {
4845 ClassReleaseRemoveLock(DeviceObject, Irp);
4846 Irp->IoStatus.Status = status;
4847 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4848 return status;
4849 }
4850
4851 if (commonExtension->IsFdo){
4852
4853 PULONG_PTR function;
4854
4855 srb = ExAllocatePoolWithTag(NonPagedPool,
4856 sizeof(SCSI_REQUEST_BLOCK) +
4857 (sizeof(ULONG_PTR) * 2),
4858 '9CcS');
4859
4860 if (srb == NULL) {
4861
4862 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
4863 ClassReleaseRemoveLock(DeviceObject, Irp);
4864 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4865 status = STATUS_INSUFFICIENT_RESOURCES;
4866 goto SetStatusAndReturn;
4867 }
4868
4869 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
4870
4871 cdb = (PCDB)srb->Cdb;
4872
4873 //
4874 // Save the function code and the device object in the memory after
4875 // the SRB.
4876 //
4877
4878 function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
4879 *function = (ULONG_PTR) DeviceObject;
4880 function++;
4881 *function = (ULONG_PTR) controlCode;
4882
4883 } else {
4884 srb = NULL;
4885 }
4886
4887 //
4888 // Change the device type to storage for the switch statement, but only
4889 // if from a legacy device type
4890 //
4891
4892 if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE << 16)) ||
4893 ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE << 16)) ||
4894 ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
4895 ) {
4896
4897 modifiedIoControlCode = (controlCode & ~0xffff0000);
4898 modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
4899
4900 } else {
4901
4902 modifiedIoControlCode = controlCode;
4903
4904 }
4905
4906 DBGTRACE(ClassDebugTrace, ("> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
4907
4908 switch (modifiedIoControlCode) {
4909
4910 case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
4911
4912 if (srb) {
4913 ExFreePool(srb);
4914 srb = NULL;
4915 }
4916
4917 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4918 sizeof(STORAGE_HOTPLUG_INFO)) {
4919
4920 //
4921 // Indicate unsuccessful status and no data transferred.
4922 //
4923
4924 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
4925 Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
4926
4927 ClassReleaseRemoveLock(DeviceObject, Irp);
4928 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4929 status = STATUS_BUFFER_TOO_SMALL;
4930
4931 } else if(!commonExtension->IsFdo) {
4932
4933 //
4934 // Just forward this down and return
4935 //
4936
4937 IoCopyCurrentIrpStackLocationToNext(Irp);
4938
4939 ClassReleaseRemoveLock(DeviceObject, Irp);
4940 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4941
4942 } else {
4943
4944 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
4945 PSTORAGE_HOTPLUG_INFO info;
4946
4947 fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
4948 info = Irp->AssociatedIrp.SystemBuffer;
4949
4950 *info = fdoExtension->PrivateFdoData->HotplugInfo;
4951 Irp->IoStatus.Status = STATUS_SUCCESS;
4952 Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
4953 ClassReleaseRemoveLock(DeviceObject, Irp);
4954 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4955 status = STATUS_SUCCESS;
4956
4957 }
4958 break;
4959 }
4960
4961 case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
4962
4963 if (srb)
4964 {
4965 ExFreePool(srb);
4966 srb = NULL;
4967 }
4968
4969 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
4970 sizeof(STORAGE_HOTPLUG_INFO)) {
4971
4972 //
4973 // Indicate unsuccessful status and no data transferred.
4974 //
4975
4976 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
4977
4978 ClassReleaseRemoveLock(DeviceObject, Irp);
4979 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4980 status = STATUS_INFO_LENGTH_MISMATCH;
4981 goto SetStatusAndReturn;
4982
4983 }
4984
4985 if(!commonExtension->IsFdo) {
4986
4987 //
4988 // Just forward this down and return
4989 //
4990
4991 IoCopyCurrentIrpStackLocationToNext(Irp);
4992
4993 ClassReleaseRemoveLock(DeviceObject, Irp);
4994 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4995
4996 } else {
4997
4998 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
4999 PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
5000
5001 status = STATUS_SUCCESS;
5002
5003 if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
5004 {
5005 status = STATUS_INVALID_PARAMETER_1;
5006 }
5007
5008 if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
5009 {
5010 status = STATUS_INVALID_PARAMETER_2;
5011 }
5012
5013 if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
5014 {
5015 status = STATUS_INVALID_PARAMETER_3;
5016 }
5017
5018 if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
5019 {
5020 status = STATUS_INVALID_PARAMETER_5;
5021 }
5022
5023 if (NT_SUCCESS(status))
5024 {
5025 fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
5026
5027 //
5028 // Store the user-defined override in the registry
5029 //
5030
5031 ClassSetDeviceParameter(fdoExtension,
5032 CLASSP_REG_SUBKEY_NAME,
5033 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
5034 (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
5035 }
5036
5037 Irp->IoStatus.Status = status;
5038
5039 ClassReleaseRemoveLock(DeviceObject, Irp);
5040 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5041 }
5042
5043 break;
5044 }
5045
5046 case IOCTL_STORAGE_CHECK_VERIFY:
5047 case IOCTL_STORAGE_CHECK_VERIFY2: {
5048
5049 PIRP irp2 = NULL;
5050 PIO_STACK_LOCATION newStack;
5051
5052 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5053
5054 DebugPrint((1,"DeviceIoControl: Check verify\n"));
5055
5056 //
5057 // If a buffer for a media change count was provided, make sure it's
5058 // big enough to hold the result
5059 //
5060
5061 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
5062
5063 //
5064 // If the buffer is too small to hold the media change count
5065 // then return an error to the caller
5066 //
5067
5068 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
5069 sizeof(ULONG)) {
5070
5071 DebugPrint((3,"DeviceIoControl: media count "
5072 "buffer too small\n"));
5073
5074 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
5075 Irp->IoStatus.Information = sizeof(ULONG);
5076
5077 if(srb != NULL) {
5078 ExFreePool(srb);
5079 }
5080
5081 ClassReleaseRemoveLock(DeviceObject, Irp);
5082 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5083
5084 status = STATUS_BUFFER_TOO_SMALL;
5085 goto SetStatusAndReturn;
5086
5087 }
5088 }
5089
5090 if(!commonExtension->IsFdo) {
5091
5092 //
5093 // If this is a PDO then we should just forward the request down
5094 //
5095 ASSERT(!srb);
5096
5097 IoCopyCurrentIrpStackLocationToNext(Irp);
5098
5099 ClassReleaseRemoveLock(DeviceObject, Irp);
5100
5101 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5102
5103 goto SetStatusAndReturn;
5104
5105 } else {
5106
5107 fdoExtension = DeviceObject->DeviceExtension;
5108
5109 }
5110
5111 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
5112
5113 //
5114 // The caller has provided a valid buffer. Allocate an additional
5115 // irp and stick the CheckVerify completion routine on it. We will
5116 // then send this down to the port driver instead of the irp the
5117 // caller sent in
5118 //
5119
5120 DebugPrint((2,"DeviceIoControl: Check verify wants "
5121 "media count\n"));
5122
5123 //
5124 // Allocate a new irp to send the TestUnitReady to the port driver
5125 //
5126
5127 irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
5128
5129 if(irp2 == NULL) {
5130 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
5131 Irp->IoStatus.Information = 0;
5132 ASSERT(srb);
5133 ExFreePool(srb);
5134 ClassReleaseRemoveLock(DeviceObject, Irp);
5135 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5136 status = STATUS_INSUFFICIENT_RESOURCES;
5137 goto SetStatusAndReturn;
5138
5139 break;
5140 }
5141
5142 //
5143 // Make sure to acquire the lock for the new irp.
5144 //
5145
5146 ClassAcquireRemoveLock(DeviceObject, irp2);
5147
5148 irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
5149 IoSetNextIrpStackLocation(irp2);
5150
5151 //
5152 // Set the top stack location and shove the master Irp into the
5153 // top location
5154 //
5155
5156 newStack = IoGetCurrentIrpStackLocation(irp2);
5157 newStack->Parameters.Others.Argument1 = Irp;
5158 newStack->DeviceObject = DeviceObject;
5159
5160 //
5161 // Stick the check verify completion routine onto the stack
5162 // and prepare the irp for the port driver
5163 //
5164
5165 IoSetCompletionRoutine(irp2,
5166 ClassCheckVerifyComplete,
5167 NULL,
5168 TRUE,
5169 TRUE,
5170 TRUE);
5171
5172 IoSetNextIrpStackLocation(irp2);
5173 newStack = IoGetCurrentIrpStackLocation(irp2);
5174 newStack->DeviceObject = DeviceObject;
5175 newStack->MajorFunction = irpStack->MajorFunction;
5176 newStack->MinorFunction = irpStack->MinorFunction;
5177
5178 //
5179 // Mark the master irp as pending - whether the lower level
5180 // driver completes it immediately or not this should allow it
5181 // to go all the way back up.
5182 //
5183
5184 IoMarkIrpPending(Irp);
5185
5186 Irp = irp2;
5187
5188 }
5189
5190 //
5191 // Test Unit Ready
5192 //
5193
5194 srb->CdbLength = 6;
5195 cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
5196
5197 //
5198 // Set timeout value.
5199 //
5200
5201 srb->TimeOutValue = fdoExtension->TimeOutValue;
5202
5203 //
5204 // If this was a CV2 then mark the request as low-priority so we don't
5205 // spin up the drive just to satisfy it.
5206 //
5207
5208 if(controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
5209 SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
5210 }
5211
5212 //
5213 // Since this routine will always hand the request to the
5214 // port driver if there isn't a data transfer to be done
5215 // we don't have to worry about completing the request here
5216 // on an error
5217 //
5218
5219 //
5220 // This routine uses a completion routine so we don't want to release
5221 // the remove lock until then.
5222 //
5223
5224 status = ClassSendSrbAsynchronous(DeviceObject,
5225 srb,
5226 Irp,
5227 NULL,
5228 0,
5229 FALSE);
5230
5231 break;
5232 }
5233
5234 case IOCTL_STORAGE_MEDIA_REMOVAL:
5235 case IOCTL_STORAGE_EJECTION_CONTROL: {
5236
5237 PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
5238
5239 DebugPrint((3, "DiskIoControl: ejection control\n"));
5240
5241 if(srb) {
5242 ExFreePool(srb);
5243 }
5244
5245 if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
5246 sizeof(PREVENT_MEDIA_REMOVAL)) {
5247
5248 //
5249 // Indicate unsuccessful status and no data transferred.
5250 //
5251
5252 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
5253
5254 ClassReleaseRemoveLock(DeviceObject, Irp);
5255 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5256 status = STATUS_INFO_LENGTH_MISMATCH;
5257 goto SetStatusAndReturn;
5258 }
5259
5260 if(!commonExtension->IsFdo) {
5261
5262 //
5263 // Just forward this down and return
5264 //
5265
5266 IoCopyCurrentIrpStackLocationToNext(Irp);
5267
5268 ClassReleaseRemoveLock(DeviceObject, Irp);
5269 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5270 }
5271 else {
5272
5273 // i don't believe this assertion is valid. this is a request
5274 // from user-mode, so they could request this for any device
5275 // they want? also, we handle it properly.
5276 // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
5277 status = ClasspEjectionControl(
5278 DeviceObject,
5279 Irp,
5280 ((modifiedIoControlCode ==
5281 IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
5282 SimpleMediaLock),
5283 mediaRemoval->PreventMediaRemoval);
5284
5285 Irp->IoStatus.Status = status;
5286 ClassReleaseRemoveLock(DeviceObject, Irp);
5287 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5288 }
5289
5290 break;
5291 }
5292
5293 case IOCTL_STORAGE_MCN_CONTROL: {
5294
5295 DebugPrint((3, "DiskIoControl: MCN control\n"));
5296
5297 if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
5298 sizeof(PREVENT_MEDIA_REMOVAL)) {
5299
5300 //
5301 // Indicate unsuccessful status and no data transferred.
5302 //
5303
5304 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
5305 Irp->IoStatus.Information = 0;
5306
5307 if(srb) {
5308 ExFreePool(srb);
5309 }
5310
5311 ClassReleaseRemoveLock(DeviceObject, Irp);
5312 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5313 status = STATUS_INFO_LENGTH_MISMATCH;
5314 goto SetStatusAndReturn;
5315 }
5316
5317 if(!commonExtension->IsFdo) {
5318
5319 //
5320 // Just forward this down and return
5321 //
5322
5323 if(srb) {
5324 ExFreePool(srb);
5325 }
5326
5327 IoCopyCurrentIrpStackLocationToNext(Irp);
5328
5329 ClassReleaseRemoveLock(DeviceObject, Irp);
5330 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5331
5332 } else {
5333
5334 //
5335 // Call to the FDO - handle the ejection control.
5336 //
5337
5338 status = ClasspMcnControl(DeviceObject->DeviceExtension,
5339 Irp,
5340 srb);
5341 }
5342 goto SetStatusAndReturn;
5343 }
5344
5345 case IOCTL_STORAGE_RESERVE:
5346 case IOCTL_STORAGE_RELEASE: {
5347
5348 //
5349 // Reserve logical unit.
5350 //
5351
5352 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5353
5354 if(!commonExtension->IsFdo) {
5355
5356 IoCopyCurrentIrpStackLocationToNext(Irp);
5357
5358 ClassReleaseRemoveLock(DeviceObject, Irp);
5359 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5360 goto SetStatusAndReturn;
5361 } else {
5362 fdoExtension = DeviceObject->DeviceExtension;
5363 }
5364
5365 srb->CdbLength = 6;
5366
5367 if(modifiedIoControlCode == IOCTL_STORAGE_RESERVE) {
5368 cdb->CDB6GENERIC.OperationCode = SCSIOP_RESERVE_UNIT;
5369 } else {
5370 cdb->CDB6GENERIC.OperationCode = SCSIOP_RELEASE_UNIT;
5371 }
5372
5373 //
5374 // Set timeout value.
5375 //
5376
5377 srb->TimeOutValue = fdoExtension->TimeOutValue;
5378
5379 status = ClassSendSrbAsynchronous(DeviceObject,
5380 srb,
5381 Irp,
5382 NULL,
5383 0,
5384 FALSE);
5385
5386 break;
5387 }
5388
5389 case IOCTL_STORAGE_EJECT_MEDIA:
5390 case IOCTL_STORAGE_LOAD_MEDIA:
5391 case IOCTL_STORAGE_LOAD_MEDIA2:{
5392
5393 //
5394 // Eject media.
5395 //
5396
5397 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5398
5399 if(!commonExtension->IsFdo) {
5400
5401 IoCopyCurrentIrpStackLocationToNext(Irp);
5402
5403 ClassReleaseRemoveLock(DeviceObject, Irp);
5404
5405 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5406 goto SetStatusAndReturn;
5407 } else {
5408 fdoExtension = DeviceObject->DeviceExtension;
5409 }
5410
5411 if(commonExtension->PagingPathCount != 0) {
5412
5413 DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
5414 "failure\n"));
5415
5416 status = STATUS_FILES_OPEN;
5417 Irp->IoStatus.Status = status;
5418
5419 Irp->IoStatus.Information = 0;
5420
5421 if(srb) {
5422 ExFreePool(srb);
5423 }
5424
5425 ClassReleaseRemoveLock(DeviceObject, Irp);
5426 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5427 goto SetStatusAndReturn;
5428 }
5429
5430 //
5431 // Synchronize with ejection control and ejection cleanup code as
5432 // well as other eject/load requests.
5433 //
5434
5435 KeEnterCriticalRegion();
5436 KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
5437 UserRequest,
5438 UserMode,
5439 FALSE,
5440 NULL);
5441
5442 if(fdoExtension->ProtectedLockCount != 0) {
5443
5444 DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
5445 "device - failure\n"));
5446
5447 status = STATUS_DEVICE_BUSY;
5448 Irp->IoStatus.Status = status;
5449 Irp->IoStatus.Information = 0;
5450
5451 if(srb) {
5452 ExFreePool(srb);
5453 }
5454
5455 ClassReleaseRemoveLock(DeviceObject, Irp);
5456 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5457
5458 KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
5459 IO_NO_INCREMENT,
5460 FALSE);
5461 KeLeaveCriticalRegion();
5462
5463 goto SetStatusAndReturn;
5464 }
5465
5466 srb->CdbLength = 6;
5467
5468 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
5469 cdb->START_STOP.LoadEject = 1;
5470
5471 if(modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
5472 cdb->START_STOP.Start = 0;
5473 } else {
5474 cdb->START_STOP.Start = 1;
5475 }
5476
5477 //
5478 // Set timeout value.
5479 //
5480
5481 srb->TimeOutValue = fdoExtension->TimeOutValue;
5482 status = ClassSendSrbAsynchronous(DeviceObject,
5483 srb,
5484 Irp,
5485 NULL,
5486 0,
5487 FALSE);
5488
5489 KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
5490 KeLeaveCriticalRegion();
5491
5492 break;
5493 }
5494
5495 case IOCTL_STORAGE_FIND_NEW_DEVICES: {
5496
5497 if(srb) {
5498 ExFreePool(srb);
5499 }
5500
5501 if(commonExtension->IsFdo) {
5502
5503 IoInvalidateDeviceRelations(
5504 ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
5505 BusRelations);
5506
5507 status = STATUS_SUCCESS;
5508 Irp->IoStatus.Status = status;
5509
5510 ClassReleaseRemoveLock(DeviceObject, Irp);
5511 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5512 }
5513 else {
5514
5515 IoCopyCurrentIrpStackLocationToNext(Irp);
5516
5517 ClassReleaseRemoveLock(DeviceObject, Irp);
5518 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5519 }
5520 break;
5521 }
5522
5523 case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
5524
5525 if(srb) {
5526 ExFreePool(srb);
5527 }
5528
5529 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
5530 sizeof(STORAGE_DEVICE_NUMBER)) {
5531
5532 PSTORAGE_DEVICE_NUMBER deviceNumber =
5533 Irp->AssociatedIrp.SystemBuffer;
5534 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
5535 commonExtension->PartitionZeroExtension;
5536
5537 deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
5538 deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
5539 deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
5540
5541 status = STATUS_SUCCESS;
5542 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
5543
5544 } else {
5545 status = STATUS_BUFFER_TOO_SMALL;
5546 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
5547 }
5548
5549 Irp->IoStatus.Status = status;
5550 ClassReleaseRemoveLock(DeviceObject, Irp);
5551 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5552
5553 break;
5554 }
5555
5556 default: {
5557
5558 DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
5559 controlCode, DeviceObject));
5560
5561 //
5562 // Pass the device control to the next driver.
5563 //
5564
5565 if(srb) {
5566 ExFreePool(srb);
5567 }
5568
5569 //
5570 // Copy the Irp stack parameters to the next stack location.
5571 //
5572
5573 IoCopyCurrentIrpStackLocationToNext(Irp);
5574
5575 ClassReleaseRemoveLock(DeviceObject, Irp);
5576 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5577 break;
5578 }
5579
5580 } // end switch( ...
5581
5582 SetStatusAndReturn:
5583
5584 DBGTRACE(ClassDebugTrace, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
5585
5586 return status;
5587 } // end ClassDeviceControl()
5588 \f
5589 /*++////////////////////////////////////////////////////////////////////////////
5590
5591 ClassShutdownFlush()
5592
5593 Routine Description:
5594
5595 This routine is called for a shutdown and flush IRPs. These are sent by the
5596 system before it actually shuts down or when the file system does a flush.
5597 If it exists, the device-specific driver's routine will be invoked. If there
5598 wasn't one specified, the Irp will be completed with an Invalid device request.
5599
5600 Arguments:
5601
5602 DriverObject - Pointer to device object to being shutdown by system.
5603
5604 Irp - IRP involved.
5605
5606 Return Value:
5607
5608 NT Status
5609
5610 --*/
5611 NTSTATUS
5612 ClassShutdownFlush(
5613 IN PDEVICE_OBJECT DeviceObject,
5614 IN PIRP Irp
5615 )
5616 {
5617 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
5618
5619 ULONG isRemoved;
5620
5621 NTSTATUS status;
5622
5623 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
5624
5625 if(isRemoved) {
5626
5627 ClassReleaseRemoveLock(DeviceObject, Irp);
5628
5629 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
5630
5631 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5632
5633 return STATUS_DEVICE_DOES_NOT_EXIST;
5634 }
5635
5636 if (commonExtension->DevInfo->ClassShutdownFlush) {
5637
5638 //
5639 // Call the device-specific driver's routine.
5640 //
5641
5642 return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
5643 }
5644
5645 //
5646 // Device-specific driver doesn't support this.
5647 //
5648
5649 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
5650
5651 ClassReleaseRemoveLock(DeviceObject, Irp);
5652 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5653
5654 return STATUS_INVALID_DEVICE_REQUEST;
5655 } // end ClassShutdownFlush()
5656 \f
5657 /*++////////////////////////////////////////////////////////////////////////////
5658
5659 ClassCreateDeviceObject()
5660
5661 Routine Description:
5662
5663 This routine creates an object for the physical device specified and
5664 sets up the deviceExtension's function pointers for each entry point
5665 in the device-specific driver.
5666
5667 Arguments:
5668
5669 DriverObject - Pointer to driver object created by system.
5670
5671 ObjectNameBuffer - Dir. name of the object to create.
5672
5673 LowerDeviceObject - Pointer to the lower device object
5674
5675 IsFdo - should this be an fdo or a pdo
5676
5677 DeviceObject - Pointer to the device object pointer we will return.
5678
5679 Return Value:
5680
5681 NTSTATUS
5682
5683 --*/
5684 NTSTATUS
5685 ClassCreateDeviceObject(
5686 IN PDRIVER_OBJECT DriverObject,
5687 IN PCCHAR ObjectNameBuffer,
5688 IN PDEVICE_OBJECT LowerDevice,
5689 IN BOOLEAN IsFdo,
5690 IN OUT PDEVICE_OBJECT *DeviceObject
5691 )
5692 {
5693 BOOLEAN isPartitionable;
5694 STRING ntNameString;
5695 UNICODE_STRING ntUnicodeString;
5696 NTSTATUS status, status2;
5697 PDEVICE_OBJECT deviceObject = NULL;
5698
5699 ULONG characteristics;
5700
5701 PCLASS_DRIVER_EXTENSION
5702 driverExtension = IoGetDriverObjectExtension(DriverObject,
5703 CLASS_DRIVER_EXTENSION_KEY);
5704
5705 PCLASS_DEV_INFO devInfo;
5706
5707 PAGED_CODE();
5708
5709 *DeviceObject = NULL;
5710 RtlInitUnicodeString(&ntUnicodeString, NULL);
5711
5712 DebugPrint((2, "ClassCreateFdo: Create device object\n"));
5713
5714 ASSERT(LowerDevice);
5715
5716 //
5717 // Make sure that if we're making PDO's we have an enumeration routine
5718 //
5719
5720 isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
5721
5722 ASSERT(IsFdo || isPartitionable);
5723
5724 //
5725 // Grab the correct dev-info structure out of the init data
5726 //
5727
5728 if(IsFdo) {
5729 devInfo = &(driverExtension->InitData.FdoData);
5730 } else {
5731 devInfo = &(driverExtension->InitData.PdoData);
5732 }
5733
5734 characteristics = devInfo->DeviceCharacteristics;
5735
5736 if(ARGUMENT_PRESENT(ObjectNameBuffer)) {
5737 DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
5738
5739 RtlInitString(&ntNameString, ObjectNameBuffer);
5740
5741 status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
5742
5743 if (!NT_SUCCESS(status)) {
5744
5745 DebugPrint((1,
5746 "ClassCreateFdo: Cannot convert string %s\n",
5747 ObjectNameBuffer));
5748
5749 ntUnicodeString.Buffer = NULL;
5750 return status;
5751 }
5752 } else {
5753 DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
5754
5755 if(IsFdo == FALSE) {
5756
5757 //
5758 // PDO's have to have some sort of name.
5759 //
5760
5761 SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
5762 }
5763
5764 RtlInitUnicodeString(&ntUnicodeString, NULL);
5765 }
5766
5767 status = IoCreateDevice(DriverObject,
5768 devInfo->DeviceExtensionSize,
5769 &ntUnicodeString,
5770 devInfo->DeviceType,
5771 devInfo->DeviceCharacteristics,
5772 FALSE,
5773 &deviceObject);
5774
5775 if (!NT_SUCCESS(status)) {
5776
5777 DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
5778 status));
5779 ASSERT(deviceObject == NULL);
5780
5781 //
5782 // buffer is not used any longer here.
5783 //
5784
5785 if (ntUnicodeString.Buffer != NULL) {
5786 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5787 ExFreePool(ntUnicodeString.Buffer);
5788 RtlInitUnicodeString(&ntUnicodeString, NULL);
5789 }
5790
5791 } else {
5792
5793 PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
5794
5795 RtlZeroMemory(
5796 deviceObject->DeviceExtension,
5797 devInfo->DeviceExtensionSize);
5798
5799 //
5800 // Setup version code
5801 //
5802
5803 commonExtension->Version = 0x03;
5804
5805 //
5806 // Setup the remove lock and event
5807 //
5808
5809 commonExtension->IsRemoved = NO_REMOVE;
5810 commonExtension->RemoveLock = 0;
5811 KeInitializeEvent(&commonExtension->RemoveEvent,
5812 SynchronizationEvent,
5813 FALSE);
5814
5815 #if DBG
5816 KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
5817 commonExtension->RemoveTrackingList = NULL;
5818 #else
5819 commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
5820 commonExtension->RemoveTrackingList = (PVOID) -1;
5821 #endif
5822
5823 //
5824 // Acquire the lock once. This reference will be released when the
5825 // remove IRP has been received.
5826 //
5827
5828 ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
5829
5830 //
5831 // Store a pointer to the driver extension so we don't have to do
5832 // lookups to get it.
5833 //
5834
5835 commonExtension->DriverExtension = driverExtension;
5836
5837 //
5838 // Fill in entry points
5839 //
5840
5841 commonExtension->DevInfo = devInfo;
5842
5843 //
5844 // Initialize some of the common values in the structure
5845 //
5846
5847 commonExtension->DeviceObject = deviceObject;
5848
5849 commonExtension->LowerDeviceObject = NULL;
5850
5851 if(IsFdo) {
5852
5853 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
5854
5855 commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
5856
5857 //
5858 // Set the initial device object flags.
5859 //
5860
5861 SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
5862
5863 //
5864 // Clear the PDO list
5865 //
5866
5867 commonExtension->ChildList = NULL;
5868
5869 commonExtension->DriverData =
5870 ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
5871
5872 if(isPartitionable) {
5873
5874 commonExtension->PartitionNumber = 0;
5875 } else {
5876 commonExtension->PartitionNumber = (ULONG) (-1L);
5877 }
5878
5879 fdoExtension->DevicePowerState = PowerDeviceD0;
5880
5881 KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
5882 SynchronizationEvent,
5883 TRUE);
5884
5885 KeInitializeEvent(&fdoExtension->ChildLock,
5886 SynchronizationEvent,
5887 TRUE);
5888
5889 status = ClasspAllocateReleaseRequest(deviceObject);
5890
5891 if(!NT_SUCCESS(status)) {
5892 IoDeleteDevice(deviceObject);
5893 *DeviceObject = NULL;
5894
5895 if (ntUnicodeString.Buffer != NULL) {
5896 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5897 ExFreePool(ntUnicodeString.Buffer);
5898 RtlInitUnicodeString(&ntUnicodeString, NULL);
5899 }
5900
5901 return status;
5902 }
5903
5904 } else {
5905
5906 PPHYSICAL_DEVICE_EXTENSION pdoExtension =
5907 deviceObject->DeviceExtension;
5908
5909 PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
5910 LowerDevice->DeviceExtension;
5911
5912 SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
5913
5914 commonExtension->PartitionZeroExtension = p0Extension;
5915
5916 //
5917 // Stick this onto the PDO list
5918 //
5919
5920 ClassAddChild(p0Extension, pdoExtension, TRUE);
5921
5922 commonExtension->DriverData = (PVOID) (pdoExtension + 1);
5923
5924 //
5925 // Get the top of stack for the lower device - this allows
5926 // filters to get stuck in between the partitions and the
5927 // physical disk.
5928 //
5929
5930 commonExtension->LowerDeviceObject =
5931 IoGetAttachedDeviceReference(LowerDevice);
5932
5933 //
5934 // Pnp will keep a reference to the lower device object long
5935 // after this partition has been deleted. Dereference now so
5936 // we don't have to deal with it later.
5937 //
5938
5939 ObDereferenceObject(commonExtension->LowerDeviceObject);
5940 }
5941
5942 KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
5943
5944 commonExtension->IsFdo = IsFdo;
5945
5946 commonExtension->DeviceName = ntUnicodeString;
5947
5948 commonExtension->PreviousState = 0xff;
5949
5950 InitializeDictionary(&(commonExtension->FileObjectDictionary));
5951
5952 commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
5953 }
5954
5955 *DeviceObject = deviceObject;
5956
5957 return status;
5958 } // end ClassCreateDeviceObject()
5959 \f
5960 /*++////////////////////////////////////////////////////////////////////////////
5961
5962 ClassClaimDevice()
5963
5964 Routine Description:
5965
5966 This function claims a device in the port driver. The port driver object
5967 is updated with the correct driver object if the device is successfully
5968 claimed.
5969
5970 Arguments:
5971
5972 LowerDeviceObject - Supplies the base port device object.
5973
5974 Release - Indicates the logical unit should be released rather than claimed.
5975
5976 Return Value:
5977
5978 Returns a status indicating success or failure of the operation.
5979
5980 --*/
5981 NTSTATUS
5982 ClassClaimDevice(
5983 IN PDEVICE_OBJECT LowerDeviceObject,
5984 IN BOOLEAN Release
5985 )
5986 {
5987 IO_STATUS_BLOCK ioStatus;
5988 PIRP irp;
5989 PIO_STACK_LOCATION irpStack;
5990 KEVENT event;
5991 NTSTATUS status;
5992 SCSI_REQUEST_BLOCK srb;
5993
5994 PAGED_CODE();
5995
5996 //
5997 // Clear the SRB fields.
5998 //
5999
6000 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
6001
6002 //
6003 // Write length to SRB.
6004 //
6005
6006 srb.Length = sizeof(SCSI_REQUEST_BLOCK);
6007
6008 srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
6009 SRB_FUNCTION_CLAIM_DEVICE;
6010
6011 //
6012 // Set the event object to the unsignaled state.
6013 // It will be used to signal request completion
6014 //
6015
6016 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
6017
6018 //
6019 // Build synchronous request with no transfer.
6020 //
6021
6022 irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
6023 LowerDeviceObject,
6024 NULL,
6025 0,
6026 NULL,
6027 0,
6028 TRUE,
6029 &event,
6030 &ioStatus);
6031
6032 if (irp == NULL) {
6033 DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
6034 return STATUS_INSUFFICIENT_RESOURCES;
6035 }
6036
6037 irpStack = IoGetNextIrpStackLocation(irp);
6038
6039 //
6040 // Save SRB address in next stack for port driver.
6041 //
6042
6043 irpStack->Parameters.Scsi.Srb = &srb;
6044
6045 //
6046 // Set up IRP Address.
6047 //
6048
6049 srb.OriginalRequest = irp;
6050
6051 //
6052 // Call the port driver with the request and wait for it to complete.
6053 //
6054
6055 status = IoCallDriver(LowerDeviceObject, irp);
6056 if (status == STATUS_PENDING) {
6057
6058 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
6059 status = ioStatus.Status;
6060 }
6061
6062 //
6063 // If this is a release request, then just decrement the reference count
6064 // and return. The status does not matter.
6065 //
6066
6067 if (Release) {
6068
6069 // ObDereferenceObject(LowerDeviceObject);
6070 return STATUS_SUCCESS;
6071 }
6072
6073 if (!NT_SUCCESS(status)) {
6074 return status;
6075 }
6076
6077 ASSERT(srb.DataBuffer != NULL);
6078 ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
6079
6080 return status;
6081 } // end ClassClaimDevice()
6082 \f
6083 /*++////////////////////////////////////////////////////////////////////////////
6084
6085 ClassInternalIoControl()
6086
6087 Routine Description:
6088
6089 This routine passes internal device controls to the port driver.
6090 Internal device controls are used by higher level drivers both for ioctls
6091 and to pass through scsi requests.
6092
6093 If the IoControlCode does not match any of the handled ioctls and is
6094 a valid system address then the request will be treated as an SRB and
6095 passed down to the lower driver. If the IoControlCode is not a valid
6096 system address the ioctl will be failed.
6097
6098 Callers must therefore be extremely cautious to pass correct, initialized
6099 values to this function.
6100
6101 Arguments:
6102
6103 DeviceObject - Supplies a pointer to the device object for this request.
6104
6105 Irp - Supplies the Irp making the request.
6106
6107 Return Value:
6108
6109 Returns back a STATUS_PENDING or a completion status.
6110
6111 --*/
6112 NTSTATUS
6113 ClassInternalIoControl(
6114 IN PDEVICE_OBJECT DeviceObject,
6115 IN PIRP Irp
6116 )
6117 {
6118 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6119
6120 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
6121 PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
6122
6123 ULONG isRemoved;
6124
6125 PSCSI_REQUEST_BLOCK srb;
6126
6127 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
6128
6129 if(isRemoved) {
6130
6131 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
6132
6133 ClassReleaseRemoveLock(DeviceObject, Irp);
6134
6135 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
6136
6137 return STATUS_DEVICE_DOES_NOT_EXIST;
6138 }
6139
6140 //
6141 // Get a pointer to the SRB.
6142 //
6143
6144 srb = irpStack->Parameters.Scsi.Srb;
6145
6146 //
6147 // Set the parameters in the next stack location.
6148 //
6149
6150 if(commonExtension->IsFdo) {
6151 nextStack->Parameters.Scsi.Srb = srb;
6152 nextStack->MajorFunction = IRP_MJ_SCSI;
6153 nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
6154
6155 } else {
6156
6157 IoCopyCurrentIrpStackLocationToNext(Irp);
6158 }
6159
6160 ClassReleaseRemoveLock(DeviceObject, Irp);
6161
6162 return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
6163 } // end ClassInternalIoControl()
6164 \f
6165 /*++////////////////////////////////////////////////////////////////////////////
6166
6167 ClassQueryTimeOutRegistryValue()
6168
6169 Routine Description:
6170
6171 This routine determines whether a reg key for a user-specified timeout
6172 value exists. This should be called at initialization time.
6173
6174 Arguments:
6175
6176 DeviceObject - Pointer to the device object we are retrieving the timeout
6177 value for
6178
6179 Return Value:
6180
6181 None, but it sets a new default timeout for a class of devices.
6182
6183 --*/
6184 ULONG
6185 ClassQueryTimeOutRegistryValue(
6186 IN PDEVICE_OBJECT DeviceObject
6187 )
6188 {
6189 //
6190 // Find the appropriate reg. key
6191 //
6192
6193 PCLASS_DRIVER_EXTENSION
6194 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
6195 CLASS_DRIVER_EXTENSION_KEY);
6196
6197 PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
6198
6199 PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
6200 PWSTR path;
6201 NTSTATUS status;
6202 LONG timeOut = 0;
6203 ULONG zero = 0;
6204 ULONG size;
6205
6206 PAGED_CODE();
6207
6208 if (!registryPath) {
6209 return 0;
6210 }
6211
6212 parameters = ExAllocatePoolWithTag(NonPagedPool,
6213 sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
6214 '1BcS');
6215
6216 if (!parameters) {
6217 return 0;
6218 }
6219
6220 size = registryPath->MaximumLength + sizeof(WCHAR);
6221 path = ExAllocatePoolWithTag(NonPagedPool, size, '2BcS');
6222
6223 if (!path) {
6224 ExFreePool(parameters);
6225 return 0;
6226 }
6227
6228 RtlZeroMemory(path,size);
6229 RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
6230
6231
6232 //
6233 // Check for the Timeout value.
6234 //
6235
6236 RtlZeroMemory(parameters,
6237 (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
6238
6239 parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
6240 parameters[0].Name = L"TimeOutValue";
6241 parameters[0].EntryContext = &timeOut;
6242 parameters[0].DefaultType = REG_DWORD;
6243 parameters[0].DefaultData = &zero;
6244 parameters[0].DefaultLength = sizeof(ULONG);
6245
6246 status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
6247 path,
6248 parameters,
6249 NULL,
6250 NULL);
6251
6252 if (!(NT_SUCCESS(status))) {
6253 timeOut = 0;
6254 }
6255
6256 ExFreePool(parameters);
6257 ExFreePool(path);
6258
6259 DebugPrint((2,
6260 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
6261 timeOut));
6262
6263
6264 return timeOut;
6265
6266 } // end ClassQueryTimeOutRegistryValue()
6267 \f
6268 /*++////////////////////////////////////////////////////////////////////////////
6269
6270 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
6271
6272 Routine Description:
6273
6274 This routine executes when the port driver has completed a check verify
6275 ioctl. It will set the status of the master Irp, copy the media change
6276 count and complete the request.
6277
6278 Arguments:
6279
6280 Fdo - Supplies the functional device object which represents the logical unit.
6281
6282 Irp - Supplies the Irp which has completed.
6283
6284 Context - NULL
6285
6286 Return Value:
6287
6288 NT status
6289
6290 --*/
6291 NTSTATUS
6292 ClassCheckVerifyComplete(
6293 IN PDEVICE_OBJECT Fdo,
6294 IN PIRP Irp,
6295 IN PVOID Context
6296 )
6297 {
6298 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
6299 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6300
6301 PIRP originalIrp;
6302
6303 ASSERT_FDO(Fdo);
6304
6305 originalIrp = irpStack->Parameters.Others.Argument1;
6306
6307 //
6308 // Copy the media change count and status
6309 //
6310
6311 *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
6312 fdoExtension->MediaChangeCount;
6313
6314 DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
6315 "device %d is %lx - saved as %lx\n",
6316 fdoExtension->DeviceNumber,
6317 fdoExtension->MediaChangeCount,
6318 *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
6319
6320 originalIrp->IoStatus.Status = Irp->IoStatus.Status;
6321 originalIrp->IoStatus.Information = sizeof(ULONG);
6322
6323 ClassReleaseRemoveLock(Fdo, originalIrp);
6324 ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
6325
6326 IoFreeIrp(Irp);
6327
6328 return STATUS_MORE_PROCESSING_REQUIRED;
6329
6330 } // end ClassCheckVerifyComplete()
6331 \f
6332 /*++////////////////////////////////////////////////////////////////////////////
6333
6334 ClassGetDescriptor()
6335
6336 Routine Description:
6337
6338 This routine will perform a query for the specified property id and will
6339 allocate a non-paged buffer to store the data in. It is the responsibility
6340 of the caller to ensure that this buffer is freed.
6341
6342 This routine must be run at IRQL_PASSIVE_LEVEL
6343
6344 Arguments:
6345
6346 DeviceObject - the device to query
6347 DeviceInfo - a location to store a pointer to the buffer we allocate
6348
6349 Return Value:
6350
6351 status
6352 if status is unsuccessful *DeviceInfo will be set to NULL, else the
6353 buffer allocated on behalf of the caller.
6354
6355 --*/
6356 NTSTATUS
6357 ClassGetDescriptor(
6358 IN PDEVICE_OBJECT DeviceObject,
6359 IN PSTORAGE_PROPERTY_ID PropertyId,
6360 OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
6361 )
6362 {
6363 STORAGE_PROPERTY_QUERY query;
6364 IO_STATUS_BLOCK ioStatus;
6365
6366 PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
6367 ULONG length;
6368
6369 UCHAR pass = 0;
6370
6371 PAGED_CODE();
6372
6373 //
6374 // Set the passed-in descriptor pointer to NULL as default
6375 //
6376
6377 *Descriptor = NULL;
6378
6379
6380 RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
6381 query.PropertyId = *PropertyId;
6382 query.QueryType = PropertyStandardQuery;
6383
6384 //
6385 // On the first pass we just want to get the first few
6386 // bytes of the descriptor so we can read it's size
6387 //
6388
6389 descriptor = (PVOID)&query;
6390
6391 ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
6392
6393 ClassSendDeviceIoControlSynchronous(
6394 IOCTL_STORAGE_QUERY_PROPERTY,
6395 DeviceObject,
6396 &query,
6397 sizeof(STORAGE_PROPERTY_QUERY),
6398 sizeof(ULONG) * 2,
6399 FALSE,
6400 &ioStatus
6401 );
6402
6403 if(!NT_SUCCESS(ioStatus.Status)) {
6404
6405 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6406 "query properties #1\n", ioStatus.Status));
6407 return ioStatus.Status;
6408 }
6409
6410 if (descriptor->Size == 0) {
6411
6412 //
6413 // This DebugPrint is to help third-party driver writers
6414 //
6415
6416 DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
6417 "%x\n", ioStatus.Status));
6418 return STATUS_UNSUCCESSFUL;
6419
6420 }
6421
6422 //
6423 // This time we know how much data there is so we can
6424 // allocate a buffer of the correct size
6425 //
6426
6427 length = descriptor->Size;
6428
6429 descriptor = ExAllocatePoolWithTag(NonPagedPool, length, '4BcS');
6430
6431 if(descriptor == NULL) {
6432
6433 DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
6434 "(%d bytes)\n", length));
6435 return STATUS_INSUFFICIENT_RESOURCES;
6436 }
6437
6438 //
6439 // setup the query again, as it was overwritten above
6440 //
6441
6442 RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
6443 query.PropertyId = *PropertyId;
6444 query.QueryType = PropertyStandardQuery;
6445
6446 //
6447 // copy the input to the new outputbuffer
6448 //
6449
6450 RtlCopyMemory(descriptor,
6451 &query,
6452 sizeof(STORAGE_PROPERTY_QUERY)
6453 );
6454
6455 ClassSendDeviceIoControlSynchronous(
6456 IOCTL_STORAGE_QUERY_PROPERTY,
6457 DeviceObject,
6458 descriptor,
6459 sizeof(STORAGE_PROPERTY_QUERY),
6460 length,
6461 FALSE,
6462 &ioStatus
6463 );
6464
6465 if(!NT_SUCCESS(ioStatus.Status)) {
6466
6467 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6468 "query properties #1\n", ioStatus.Status));
6469 ExFreePool(descriptor);
6470 return ioStatus.Status;
6471 }
6472
6473 //
6474 // return the memory we've allocated to the caller
6475 //
6476
6477 *Descriptor = descriptor;
6478 return ioStatus.Status;
6479 } // end ClassGetDescriptor()
6480 \f
6481 /*++////////////////////////////////////////////////////////////////////////////
6482
6483 ClassSignalCompletion()
6484
6485 Routine Description:
6486
6487 This completion routine will signal the event given as context and then
6488 return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
6489 the responsibility of the routine waiting on the event to complete the
6490 request and free the event.
6491
6492 Arguments:
6493
6494 DeviceObject - a pointer to the device object
6495
6496 Irp - a pointer to the irp
6497
6498 Event - a pointer to the event to signal
6499
6500 Return Value:
6501
6502 STATUS_MORE_PROCESSING_REQUIRED
6503
6504 --*/
6505 NTSTATUS
6506 ClassSignalCompletion(
6507 IN PDEVICE_OBJECT DeviceObject,
6508 IN PIRP Irp,
6509 IN PKEVENT Event
6510 )
6511 {
6512 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
6513
6514 return STATUS_MORE_PROCESSING_REQUIRED;
6515 } // end ClassSignalCompletion()
6516 \f
6517 /*++////////////////////////////////////////////////////////////////////////////
6518
6519 ClassPnpQueryFdoRelations()
6520
6521 Routine Description:
6522
6523 This routine will call the driver's enumeration routine to update the
6524 list of PDO's. It will then build a response to the
6525 IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
6526 the irp.
6527
6528 Arguments:
6529
6530 Fdo - a pointer to the functional device object we are enumerating
6531
6532 Irp - a pointer to the enumeration request
6533
6534 Return Value:
6535
6536 status
6537
6538 --*/
6539 NTSTATUS
6540 ClassPnpQueryFdoRelations(
6541 IN PDEVICE_OBJECT Fdo,
6542 IN PIRP Irp
6543 )
6544 {
6545 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6546 PCLASS_DRIVER_EXTENSION
6547 driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
6548 CLASS_DRIVER_EXTENSION_KEY);
6549 NTSTATUS status;
6550
6551 PAGED_CODE();
6552
6553 //
6554 // If there's already an enumeration in progress then don't start another
6555 // one.
6556 //
6557
6558 if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
6559 status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
6560 }
6561
6562 Irp->IoStatus.Information = (ULONG_PTR) NULL;
6563
6564 Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
6565 Fdo,
6566 BusRelations,
6567 (PDEVICE_RELATIONS)&Irp->IoStatus.Information);
6568 InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
6569
6570 return Irp->IoStatus.Status;
6571 } // end ClassPnpQueryFdoRelations()
6572 \f
6573 /*++////////////////////////////////////////////////////////////////////////////
6574
6575 ClassMarkChildrenMissing()
6576
6577 Routine Description:
6578
6579 This routine will call ClassMarkChildMissing() for all children.
6580 It acquires the ChildLock before calling ClassMarkChildMissing().
6581
6582 Arguments:
6583
6584 Fdo - the "bus's" device object, such as the disk FDO for non-removable
6585 disks with multiple partitions.
6586
6587 Return Value:
6588
6589 None
6590
6591 --*/
6592 VOID
6593 ClassMarkChildrenMissing(
6594 IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
6595 )
6596 {
6597 PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
6598 PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
6599
6600 PAGED_CODE();
6601
6602 ClassAcquireChildLock(Fdo);
6603
6604 while (nextChild){
6605 PPHYSICAL_DEVICE_EXTENSION tmpChild;
6606
6607 /*
6608 * ClassMarkChildMissing will also dequeue the child extension.
6609 * So get the next pointer before calling ClassMarkChildMissing.
6610 */
6611 tmpChild = nextChild;
6612 nextChild = tmpChild->CommonExtension.ChildList;
6613 ClassMarkChildMissing(tmpChild, FALSE);
6614 }
6615 ClassReleaseChildLock(Fdo);
6616 return;
6617 } // end ClassMarkChildrenMissing()
6618 \f
6619 /*++////////////////////////////////////////////////////////////////////////////
6620
6621 ClassMarkChildMissing()
6622
6623 Routine Description:
6624
6625 This routine will make an active child "missing." If the device has never
6626 been enumerated then it will be deleted on the spot. If the device has
6627 not been enumerated then it will be marked as missing so that we can
6628 not report it in the next device enumeration.
6629
6630 Arguments:
6631
6632 Child - the child device to be marked as missing.
6633
6634 AcquireChildLock - TRUE if the child lock should be acquired before removing
6635 the missing child. FALSE if the child lock is already
6636 acquired by this thread.
6637
6638 Return Value:
6639
6640 returns whether or not the child device object has previously been reported
6641 to PNP.
6642
6643 --*/
6644 BOOLEAN
6645 ClassMarkChildMissing(
6646 IN PPHYSICAL_DEVICE_EXTENSION Child,
6647 IN BOOLEAN AcquireChildLock
6648 )
6649 {
6650 BOOLEAN returnValue = Child->IsEnumerated;
6651
6652 PAGED_CODE();
6653 ASSERT_PDO(Child->DeviceObject);
6654
6655 Child->IsMissing = TRUE;
6656
6657 //
6658 // Make sure this child is not in the active list.
6659 //
6660
6661 ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
6662 Child,
6663 AcquireChildLock);
6664
6665 if(Child->IsEnumerated == FALSE) {
6666 ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
6667 }
6668
6669 return returnValue;
6670 } // end ClassMarkChildMissing()
6671 \f
6672 /*++////////////////////////////////////////////////////////////////////////////
6673
6674 ClassRetrieveDeviceRelations()
6675
6676 Routine Description:
6677
6678 This routine will allocate a buffer to hold the specified list of
6679 relations. It will then fill in the list with referenced device pointers
6680 and will return the request.
6681
6682 Arguments:
6683
6684 Fdo - pointer to the FDO being queried
6685
6686 RelationType - what type of relations are being queried
6687
6688 DeviceRelations - a location to store a pointer to the response
6689
6690 Return Value:
6691
6692 status
6693
6694 --*/
6695 NTSTATUS
6696 ClassRetrieveDeviceRelations(
6697 IN PDEVICE_OBJECT Fdo,
6698 IN DEVICE_RELATION_TYPE RelationType,
6699 OUT PDEVICE_RELATIONS *DeviceRelations
6700 )
6701 {
6702 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6703
6704 ULONG count = 0;
6705 ULONG i;
6706
6707 PPHYSICAL_DEVICE_EXTENSION nextChild;
6708
6709 ULONG relationsSize;
6710 PDEVICE_RELATIONS deviceRelations = NULL;
6711
6712 NTSTATUS status;
6713
6714 PAGED_CODE();
6715
6716 ClassAcquireChildLock(fdoExtension);
6717
6718 nextChild = fdoExtension->CommonExtension.ChildList;
6719
6720 //
6721 // Count the number of PDO's attached to this disk
6722 //
6723
6724 while(nextChild != NULL) {
6725 PCOMMON_DEVICE_EXTENSION commonExtension;
6726
6727 commonExtension = &(nextChild->CommonExtension);
6728
6729 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6730 (nextChild->IsMissing == FALSE));
6731
6732 nextChild = commonExtension->ChildList;
6733
6734 count++;
6735 };
6736
6737 relationsSize = (sizeof(DEVICE_RELATIONS) +
6738 (count * sizeof(PDEVICE_OBJECT)));
6739
6740 deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
6741
6742 if(deviceRelations == NULL) {
6743
6744 DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
6745 "%d bytes for device relations\n", relationsSize));
6746
6747 ClassReleaseChildLock(fdoExtension);
6748
6749 return STATUS_INSUFFICIENT_RESOURCES;
6750 }
6751
6752 RtlZeroMemory(deviceRelations, relationsSize);
6753
6754 nextChild = fdoExtension->CommonExtension.ChildList;
6755 i = count - 1;
6756
6757 while(nextChild != NULL) {
6758 PCOMMON_DEVICE_EXTENSION commonExtension;
6759
6760 commonExtension = &(nextChild->CommonExtension);
6761
6762 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6763 (nextChild->IsMissing == FALSE));
6764
6765 deviceRelations->Objects[i--] = nextChild->DeviceObject;
6766
6767 status = ObReferenceObjectByPointer(
6768 nextChild->DeviceObject,
6769 0,
6770 NULL,
6771 KernelMode);
6772 ASSERT(NT_SUCCESS(status));
6773
6774 nextChild->IsEnumerated = TRUE;
6775 nextChild = commonExtension->ChildList;
6776 }
6777
6778 ASSERTMSG("Child list has changed: ", i == -1);
6779
6780 deviceRelations->Count = count;
6781 *DeviceRelations = deviceRelations;
6782 ClassReleaseChildLock(fdoExtension);
6783 return STATUS_SUCCESS;
6784 } // end ClassRetrieveDeviceRelations()
6785 \f
6786 /*++////////////////////////////////////////////////////////////////////////////
6787
6788 ClassGetPdoId()
6789
6790 Routine Description:
6791
6792 This routine will call into the driver to retrieve a copy of one of it's
6793 id strings.
6794
6795 Arguments:
6796
6797 Pdo - a pointer to the pdo being queried
6798
6799 IdType - which type of id string is being queried
6800
6801 IdString - an allocated unicode string structure which the driver
6802 can fill in.
6803
6804 Return Value:
6805
6806 status
6807
6808 --*/
6809 NTSTATUS
6810 ClassGetPdoId(
6811 IN PDEVICE_OBJECT Pdo,
6812 IN BUS_QUERY_ID_TYPE IdType,
6813 IN PUNICODE_STRING IdString
6814 )
6815 {
6816 PCLASS_DRIVER_EXTENSION
6817 driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject,
6818 CLASS_DRIVER_EXTENSION_KEY);
6819
6820 ASSERT_PDO(Pdo);
6821 ASSERT(driverExtension->InitData.ClassQueryId);
6822
6823 PAGED_CODE();
6824
6825 return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
6826 } // end ClassGetPdoId()
6827 \f
6828 /*++////////////////////////////////////////////////////////////////////////////
6829
6830 ClassQueryPnpCapabilities()
6831
6832 Routine Description:
6833
6834 This routine will call into the class driver to retrieve it's pnp
6835 capabilities.
6836
6837 Arguments:
6838
6839 PhysicalDeviceObject - The physical device object to retrieve properties
6840 for.
6841
6842 Return Value:
6843
6844 status
6845
6846 --*/
6847 NTSTATUS
6848 ClassQueryPnpCapabilities(
6849 IN PDEVICE_OBJECT DeviceObject,
6850 IN PDEVICE_CAPABILITIES Capabilities
6851 )
6852 {
6853 PCLASS_DRIVER_EXTENSION driverExtension =
6854 ClassGetDriverExtension(DeviceObject->DriverObject);
6855 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6856
6857 PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
6858
6859 PAGED_CODE();
6860
6861 ASSERT(DeviceObject);
6862 ASSERT(Capabilities);
6863
6864 if(commonExtension->IsFdo) {
6865 queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
6866 } else {
6867 queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
6868 }
6869
6870 if(queryRoutine) {
6871 return queryRoutine(DeviceObject,
6872 Capabilities);
6873 } else {
6874 return STATUS_NOT_IMPLEMENTED;
6875 }
6876 } // end ClassQueryPnpCapabilities()
6877 \f
6878 /*++////////////////////////////////////////////////////////////////////////////
6879
6880 ClassInvalidateBusRelations()
6881
6882 Routine Description:
6883
6884 This routine re-enumerates the devices on the "bus". It will call into
6885 the driver's ClassEnumerate routine to update the device objects
6886 immediately. It will then schedule a bus re-enumeration for pnp by calling
6887 IoInvalidateDeviceRelations.
6888
6889 Arguments:
6890
6891 Fdo - a pointer to the functional device object for this bus
6892
6893 Return Value:
6894
6895 none
6896
6897 --*/
6898 VOID
6899 ClassInvalidateBusRelations(
6900 IN PDEVICE_OBJECT Fdo
6901 )
6902 {
6903 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6904 PCLASS_DRIVER_EXTENSION
6905 driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
6906 CLASS_DRIVER_EXTENSION_KEY);
6907
6908 NTSTATUS status = STATUS_SUCCESS;
6909
6910 PAGED_CODE();
6911
6912 ASSERT_FDO(Fdo);
6913 ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
6914
6915 if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
6916 status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
6917 }
6918 InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
6919
6920 if(!NT_SUCCESS(status)) {
6921
6922 DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
6923 "returned %lx\n", status));
6924 }
6925
6926 IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
6927
6928 return;
6929 } // end ClassInvalidateBusRelations()
6930 \f
6931 /*++////////////////////////////////////////////////////////////////////////////
6932
6933 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
6934
6935 Routine Description:
6936
6937 This routine is called to handle the "removal" of a device. It will
6938 forward the request downwards if necesssary, call into the driver
6939 to release any necessary resources (memory, events, etc) and then
6940 will delete the device object.
6941
6942 Arguments:
6943
6944 DeviceObject - a pointer to the device object being removed
6945
6946 RemoveType - indicates what type of remove this is (regular or surprise).
6947
6948 Return Value:
6949
6950 status
6951
6952 --*/
6953 NTSTATUS
6954 ClassRemoveDevice(
6955 IN PDEVICE_OBJECT DeviceObject,
6956 IN UCHAR RemoveType
6957 )
6958 {
6959 PCLASS_DRIVER_EXTENSION
6960 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
6961 CLASS_DRIVER_EXTENSION_KEY);
6962 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6963 PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
6964 PCLASS_WMI_INFO classWmiInfo;
6965 BOOLEAN proceedWithRemove = TRUE;
6966 NTSTATUS status;
6967
6968 PAGED_CODE();
6969
6970 commonExtension->IsRemoved = REMOVE_PENDING;
6971
6972 /*
6973 * Deregister from WMI.
6974 */
6975 classWmiInfo = commonExtension->IsFdo ?
6976 &driverExtension->InitData.FdoData.ClassWmiInfo :
6977 &driverExtension->InitData.PdoData.ClassWmiInfo;
6978 if (classWmiInfo->GuidRegInfo){
6979 status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
6980 DBGTRACE(ClassDebugInfo, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
6981 }
6982
6983 /*
6984 * If we exposed a "shingle" (a named device interface openable by CreateFile)
6985 * then delete it now.
6986 */
6987 if (commonExtension->MountedDeviceInterfaceName.Buffer){
6988 IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
6989 RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
6990 RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
6991 }
6992
6993 //
6994 // If this is a surprise removal we leave the device around - which means
6995 // we don't have to (or want to) drop the remove lock and wait for pending
6996 // requests to complete.
6997 //
6998
6999 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7000
7001 //
7002 // Release the lock we acquired when the device object was created.
7003 //
7004
7005 ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
7006
7007 DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
7008 commonExtension->RemoveLock));
7009
7010 KeWaitForSingleObject(&commonExtension->RemoveEvent,
7011 Executive,
7012 KernelMode,
7013 FALSE,
7014 NULL);
7015
7016 DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject));
7017
7018 if(commonExtension->IsFdo) {
7019
7020 DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
7021 "remove request.\n", DeviceObject));
7022
7023 }
7024 else {
7025 PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
7026
7027 if (pdoExtension->IsMissing){
7028 /*
7029 * The child partition PDO is missing, so we are going to go ahead
7030 * and delete it for the remove.
7031 */
7032 DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
7033 }
7034 else {
7035 /*
7036 * We got a remove for a child partition PDO which is not actually missing.
7037 * So we will NOT actually delete it.
7038 */
7039 DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
7040
7041 //
7042 // Reacquire the remove lock for the next time this comes around.
7043 //
7044
7045 ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
7046
7047 //
7048 // the device wasn't missing so it's not really been removed.
7049 //
7050
7051 commonExtension->IsRemoved = NO_REMOVE;
7052
7053 IoInvalidateDeviceRelations(
7054 commonExtension->PartitionZeroExtension->LowerPdo,
7055 BusRelations);
7056
7057 proceedWithRemove = FALSE;
7058 }
7059 }
7060 }
7061
7062
7063 if (proceedWithRemove){
7064
7065 /*
7066 * Call the class driver's remove handler.
7067 * All this is supposed to do is clean up its data and device interfaces.
7068 */
7069 ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
7070 status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
7071 ASSERT(NT_SUCCESS(status));
7072 status = STATUS_SUCCESS;
7073
7074 if (commonExtension->IsFdo){
7075 PDEVICE_OBJECT pdo;
7076 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
7077
7078 ClasspDisableTimer(fdoExtension->DeviceObject);
7079
7080 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7081
7082 PPHYSICAL_DEVICE_EXTENSION child;
7083
7084 //
7085 // Cleanup the media detection resources now that the class driver
7086 // has stopped it's timer (if any) and we can be sure they won't
7087 // call us to do detection again.
7088 //
7089
7090 ClassCleanupMediaChangeDetection(fdoExtension);
7091
7092 //
7093 // Cleanup any Failure Prediction stuff
7094 //
7095 if (fdoExtension->FailurePredictionInfo) {
7096 ExFreePool(fdoExtension->FailurePredictionInfo);
7097 fdoExtension->FailurePredictionInfo = NULL;
7098 }
7099
7100 /*
7101 * Ordinarily all child PDOs will be removed by the time
7102 * that the parent gets the REMOVE_DEVICE.
7103 * However, if a child PDO has been created but has not
7104 * been announced in a QueryDeviceRelations, then it is
7105 * just a private data structure unknown to pnp, and we have
7106 * to delete it ourselves.
7107 */
7108 ClassAcquireChildLock(fdoExtension);
7109 while (child = ClassRemoveChild(fdoExtension, NULL, FALSE)){
7110
7111 //
7112 // Yank the pdo. This routine will unlink the device from the
7113 // pdo list so NextPdo will point to the next one when it's
7114 // complete.
7115 //
7116 child->IsMissing = TRUE;
7117 ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
7118 }
7119 ClassReleaseChildLock(fdoExtension);
7120 }
7121 else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
7122 /*
7123 * This is a surprise-remove on the parent FDO.
7124 * We will mark the child PDOs as missing so that they
7125 * will actually get deleted when they get a REMOVE_DEVICE.
7126 */
7127 ClassMarkChildrenMissing(fdoExtension);
7128 }
7129
7130 ClasspFreeReleaseRequest(DeviceObject);
7131
7132 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7133
7134 //
7135 // Free FDO-specific data structs
7136 //
7137 if (fdoExtension->PrivateFdoData){
7138
7139 DestroyAllTransferPackets(DeviceObject);
7140
7141 ExFreePool(fdoExtension->PrivateFdoData);
7142 fdoExtension->PrivateFdoData = NULL;
7143 }
7144
7145 if (commonExtension->DeviceName.Buffer) {
7146 ExFreePool(commonExtension->DeviceName.Buffer);
7147 RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
7148 }
7149
7150 if (fdoExtension->AdapterDescriptor) {
7151 ExFreePool(fdoExtension->AdapterDescriptor);
7152 fdoExtension->AdapterDescriptor = NULL;
7153 }
7154
7155 if (fdoExtension->DeviceDescriptor) {
7156 ExFreePool(fdoExtension->DeviceDescriptor);
7157 fdoExtension->DeviceDescriptor = NULL;
7158 }
7159
7160 //
7161 // Detach our device object from the stack - there's no reason
7162 // to hold off our cleanup any longer.
7163 //
7164
7165 IoDetachDevice(lowerDeviceObject);
7166 }
7167 }
7168 else {
7169 /*
7170 * This is a child partition PDO.
7171 * We have already determined that it was previously marked
7172 * as missing. So if this is a REMOVE_DEVICE, we will actually
7173 * delete it.
7174 */
7175 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7176 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
7177 commonExtension->PartitionZeroExtension;
7178 PPHYSICAL_DEVICE_EXTENSION pdoExtension =
7179 (PPHYSICAL_DEVICE_EXTENSION) commonExtension;
7180
7181 //
7182 // See if this device is in the child list (if this was a suprise
7183 // removal it might be) and remove it.
7184 //
7185
7186 ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
7187 }
7188 }
7189
7190 commonExtension->PartitionLength.QuadPart = 0;
7191
7192 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7193 IoDeleteDevice(DeviceObject);
7194 }
7195 }
7196
7197 return STATUS_SUCCESS;
7198 } // end ClassRemoveDevice()
7199 \f
7200 /*++////////////////////////////////////////////////////////////////////////////
7201
7202 ClassGetDriverExtension()
7203
7204 Routine Description:
7205
7206 This routine will return the classpnp's driver extension.
7207
7208 Arguments:
7209
7210 DriverObject - the driver object for which to get classpnp's extension
7211
7212 Return Value:
7213
7214 Either NULL if none, or a pointer to the driver extension
7215
7216 --*/
7217 PCLASS_DRIVER_EXTENSION
7218 ClassGetDriverExtension(
7219 IN PDRIVER_OBJECT DriverObject
7220 )
7221 {
7222 return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
7223 } // end ClassGetDriverExtension()
7224 \f
7225 /*++////////////////////////////////////////////////////////////////////////////
7226
7227 ClasspStartIo()
7228
7229 Routine Description:
7230
7231 This routine wraps the class driver's start io routine. If the device
7232 is being removed it will complete any requests with
7233 STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
7234
7235 Arguments:
7236
7237 Return Value:
7238
7239 none
7240
7241 --*/
7242 VOID
7243 ClasspStartIo(
7244 IN PDEVICE_OBJECT DeviceObject,
7245 IN PIRP Irp
7246 )
7247 {
7248 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7249
7250 //
7251 // We're already holding the remove lock so just check the variable and
7252 // see what's going on.
7253 //
7254
7255 if(commonExtension->IsRemoved) {
7256
7257 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
7258
7259 ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
7260 ClassReleaseRemoveLock(DeviceObject, Irp);
7261 ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
7262 IoStartNextPacket(DeviceObject, FALSE);
7263 ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
7264 return;
7265 }
7266
7267 commonExtension->DriverExtension->InitData.ClassStartIo(
7268 DeviceObject,
7269 Irp);
7270
7271 return;
7272 } // ClasspStartIo()
7273 \f
7274 /*++////////////////////////////////////////////////////////////////////////////
7275
7276 ClassUpdateInformationInRegistry()
7277
7278 Routine Description:
7279
7280 This routine has knowledge about the layout of the device map information
7281 in the registry. It will update this information to include a value
7282 entry specifying the dos device name that is assumed to get assigned
7283 to this NT device name. For more information on this assigning of the
7284 dos device name look in the drive support routine in the hal that assigns
7285 all dos names.
7286
7287 Since some versions of some device's firmware did not work and some
7288 vendors did not bother to follow the specification, the entire inquiry
7289 information must also be stored in the registry so than someone can
7290 figure out the firmware version.
7291
7292 Arguments:
7293
7294 DeviceObject - A pointer to the device object for the tape device.
7295
7296 Return Value:
7297
7298 None
7299
7300 --*/
7301 VOID
7302 ClassUpdateInformationInRegistry(
7303 IN PDEVICE_OBJECT Fdo,
7304 IN PCHAR DeviceName,
7305 IN ULONG DeviceNumber,
7306 IN PINQUIRYDATA InquiryData,
7307 IN ULONG InquiryDataLength
7308 )
7309 {
7310 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
7311 NTSTATUS status;
7312 SCSI_ADDRESS scsiAddress;
7313 OBJECT_ATTRIBUTES objectAttributes;
7314 PUCHAR buffer;
7315 STRING string;
7316 UNICODE_STRING unicodeName;
7317 UNICODE_STRING unicodeRegistryPath;
7318 UNICODE_STRING unicodeData;
7319 HANDLE targetKey;
7320 IO_STATUS_BLOCK ioStatus;
7321
7322
7323 PAGED_CODE();
7324
7325 ASSERT(DeviceName);
7326 fdoExtension = Fdo->DeviceExtension;
7327 buffer = NULL;
7328 targetKey = NULL;
7329 RtlZeroMemory(&unicodeName, sizeof(UNICODE_STRING));
7330 RtlZeroMemory(&unicodeData, sizeof(UNICODE_STRING));
7331 RtlZeroMemory(&unicodeRegistryPath, sizeof(UNICODE_STRING));
7332
7333 TRY {
7334
7335 //
7336 // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
7337 //
7338
7339 ClassSendDeviceIoControlSynchronous(
7340 IOCTL_SCSI_GET_ADDRESS,
7341 Fdo,
7342 &scsiAddress,
7343 0,
7344 sizeof(SCSI_ADDRESS),
7345 FALSE,
7346 &ioStatus
7347 );
7348
7349 if (!NT_SUCCESS(ioStatus.Status)) {
7350
7351 status = ioStatus.Status;
7352 DebugPrint((1,
7353 "UpdateInformationInRegistry: Get Address failed %lx\n",
7354 status));
7355 LEAVE;
7356
7357 } else {
7358
7359 DebugPrint((1,
7360 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
7361 scsiAddress.PortNumber,
7362 scsiAddress.PathId,
7363 scsiAddress.TargetId,
7364 scsiAddress.Lun));
7365
7366 }
7367
7368 //
7369 // Allocate a buffer for the reg. spooge.
7370 //
7371
7372 buffer = ExAllocatePoolWithTag(PagedPool, 1024, '6BcS');
7373
7374 if (buffer == NULL) {
7375
7376 //
7377 // There is not return value for this. Since this is done at
7378 // claim device time (currently only system initialization) getting
7379 // the registry information correct will be the least of the worries.
7380 //
7381
7382 LEAVE;
7383 }
7384
7385 sprintf(buffer,
7386 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
7387 scsiAddress.PortNumber,
7388 scsiAddress.PathId,
7389 scsiAddress.TargetId,
7390 scsiAddress.Lun);
7391
7392 RtlInitString(&string, buffer);
7393
7394 status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
7395 &string,
7396 TRUE);
7397
7398 if (!NT_SUCCESS(status)) {
7399 LEAVE;
7400 }
7401
7402 //
7403 // Open the registry key for the scsi information for this
7404 // scsibus, target, lun.
7405 //
7406
7407 InitializeObjectAttributes(&objectAttributes,
7408 &unicodeRegistryPath,
7409 OBJ_CASE_INSENSITIVE,
7410 NULL,
7411 NULL);
7412
7413 status = ZwOpenKey(&targetKey,
7414 KEY_READ | KEY_WRITE,
7415 &objectAttributes);
7416
7417 if (!NT_SUCCESS(status)) {
7418 LEAVE;
7419 }
7420
7421 //
7422 // Now construct and attempt to create the registry value
7423 // specifying the device name in the appropriate place in the
7424 // device map.
7425 //
7426
7427 RtlInitUnicodeString(&unicodeName, L"DeviceName");
7428
7429 sprintf(buffer, "%s%d", DeviceName, DeviceNumber);
7430 RtlInitString(&string, buffer);
7431 status = RtlAnsiStringToUnicodeString(&unicodeData,
7432 &string,
7433 TRUE);
7434 if (NT_SUCCESS(status)) {
7435 status = ZwSetValueKey(targetKey,
7436 &unicodeName,
7437 0,
7438 REG_SZ,
7439 unicodeData.Buffer,
7440 unicodeData.Length);
7441 }
7442
7443 //
7444 // if they sent in data, update the registry
7445 //
7446
7447 if (InquiryDataLength) {
7448
7449 ASSERT(InquiryData);
7450
7451 RtlInitUnicodeString(&unicodeName, L"InquiryData");
7452 status = ZwSetValueKey(targetKey,
7453 &unicodeName,
7454 0,
7455 REG_BINARY,
7456 InquiryData,
7457 InquiryDataLength);
7458 }
7459
7460 // that's all, except to clean up.
7461
7462 } FINALLY {
7463
7464 if (unicodeData.Buffer) {
7465 RtlFreeUnicodeString(&unicodeData);
7466 }
7467 if (unicodeRegistryPath.Buffer) {
7468 RtlFreeUnicodeString(&unicodeRegistryPath);
7469 }
7470 if (targetKey) {
7471 ZwClose(targetKey);
7472 }
7473 if (buffer) {
7474 ExFreePool(buffer);
7475 }
7476
7477 }
7478
7479 } // end ClassUpdateInformationInRegistry()
7480 \f
7481 /*++////////////////////////////////////////////////////////////////////////////
7482
7483 ClasspSendSynchronousCompletion()
7484
7485 Routine Description:
7486
7487 This completion routine will set the user event in the irp after
7488 freeing the irp and the associated MDL (if any).
7489
7490 Arguments:
7491
7492 DeviceObject - the device object which requested the completion routine
7493
7494 Irp - the irp being completed
7495
7496 Context - unused
7497
7498 Return Value:
7499
7500 STATUS_MORE_PROCESSING_REQUIRED
7501
7502 --*/
7503 NTSTATUS
7504 ClasspSendSynchronousCompletion(
7505 IN PDEVICE_OBJECT DeviceObject,
7506 IN PIRP Irp,
7507 IN PVOID Context
7508 )
7509 {
7510 DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
7511 DeviceObject, Irp, Context));
7512 //
7513 // First set the status and information fields in the io status block
7514 // provided by the caller.
7515 //
7516
7517 *(Irp->UserIosb) = Irp->IoStatus;
7518
7519 //
7520 // Unlock the pages for the data buffer.
7521 //
7522
7523 if(Irp->MdlAddress) {
7524 MmUnlockPages(Irp->MdlAddress);
7525 IoFreeMdl(Irp->MdlAddress);
7526 }
7527
7528 //
7529 // Signal the caller's event.
7530 //
7531
7532 KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
7533
7534 //
7535 // Free the MDL and the IRP.
7536 //
7537
7538 IoFreeIrp(Irp);
7539
7540 return STATUS_MORE_PROCESSING_REQUIRED;
7541 } // end ClasspSendSynchronousCompletion()
7542 \f
7543 /*++
7544
7545 ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
7546
7547 --*/
7548 VOID
7549 ClasspRegisterMountedDeviceInterface(
7550 IN PDEVICE_OBJECT DeviceObject
7551 )
7552 {
7553
7554 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7555 BOOLEAN isFdo = commonExtension->IsFdo;
7556
7557 PDEVICE_OBJECT pdo;
7558 UNICODE_STRING interfaceName;
7559
7560 NTSTATUS status;
7561
7562 if(isFdo) {
7563
7564 PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
7565
7566 functionalExtension =
7567 (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
7568 pdo = functionalExtension->LowerPdo;
7569 } else {
7570 pdo = DeviceObject;
7571 }
7572
7573 status = IoRegisterDeviceInterface(
7574 pdo,
7575 &MOUNTDEV_MOUNTED_DEVICE_GUID,
7576 NULL,
7577 &interfaceName
7578 );
7579
7580 if(NT_SUCCESS(status)) {
7581
7582 //
7583 // Copy the interface name before setting the interface state - the
7584 // name is needed by the components we notify.
7585 //
7586
7587 commonExtension->MountedDeviceInterfaceName = interfaceName;
7588 status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
7589
7590 if(!NT_SUCCESS(status)) {
7591 RtlFreeUnicodeString(&interfaceName);
7592 }
7593 }
7594
7595 if(!NT_SUCCESS(status)) {
7596 RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
7597 NULL);
7598 }
7599 return;
7600 } // end ClasspRegisterMountedDeviceInterface()
7601 \f
7602 /*++////////////////////////////////////////////////////////////////////////////
7603
7604 ClassSendDeviceIoControlSynchronous()
7605
7606 Routine Description:
7607
7608 This routine is based upon IoBuildDeviceIoControlRequest(). It has been
7609 modified to reduce code and memory by not double-buffering the io, using
7610 the same buffer for both input and output, allocating and deallocating
7611 the mdl on behalf of the caller, and waiting for the io to complete.
7612
7613 This routine also works around the rare cases in which APC's are disabled.
7614 Since IoBuildDeviceIoControl() used APC's to signal completion, this had
7615 led to a number of difficult-to-detect hangs, where the irp was completed,
7616 but the event passed to IoBuild..() was still being waited upon by the
7617 caller.
7618
7619 Arguments:
7620
7621 IoControlCode - the IOCTL to send
7622
7623 TargetDeviceObject - the device object that should handle the ioctl
7624
7625 Buffer - the input and output buffer, or NULL if no input/output
7626
7627 InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
7628
7629 OutputBufferLength - the number of bytes to be filled in upon success
7630
7631 InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
7632
7633 IoStatus - the status block that contains the results of the operation
7634
7635 Return Value:
7636
7637 --*/
7638 VOID
7639 ClassSendDeviceIoControlSynchronous(
7640 IN ULONG IoControlCode,
7641 IN PDEVICE_OBJECT TargetDeviceObject,
7642 IN OUT PVOID Buffer OPTIONAL,
7643 IN ULONG InputBufferLength,
7644 IN ULONG OutputBufferLength,
7645 IN BOOLEAN InternalDeviceIoControl,
7646 OUT PIO_STATUS_BLOCK IoStatus
7647 )
7648 {
7649 PIRP irp;
7650 PIO_STACK_LOCATION irpSp;
7651 ULONG method;
7652
7653 PAGED_CODE();
7654
7655 irp = NULL;
7656 method = IoControlCode & 3;
7657
7658
7659 #if DBG // Begin Argument Checking (nop in fre version)
7660
7661 ASSERT(ARGUMENT_PRESENT(IoStatus));
7662
7663 if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
7664 ASSERT(ARGUMENT_PRESENT(Buffer));
7665 }
7666 else {
7667 ASSERT(!ARGUMENT_PRESENT(Buffer));
7668 }
7669 #endif
7670
7671 //
7672 // Begin by allocating the IRP for this request. Do not charge quota to
7673 // the current process for this IRP.
7674 //
7675
7676 irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
7677 if (!irp) {
7678 (*IoStatus).Information = 0;
7679 (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7680 return;
7681 }
7682
7683 //
7684 // Get a pointer to the stack location of the first driver which will be
7685 // invoked. This is where the function codes and the parameters are set.
7686 //
7687
7688 irpSp = IoGetNextIrpStackLocation(irp);
7689
7690 //
7691 // Set the major function code based on the type of device I/O control
7692 // function the caller has specified.
7693 //
7694
7695 if (InternalDeviceIoControl) {
7696 irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
7697 } else {
7698 irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
7699 }
7700
7701 //
7702 // Copy the caller's parameters to the service-specific portion of the
7703 // IRP for those parameters that are the same for all four methods.
7704 //
7705
7706 irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
7707 irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
7708 irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
7709
7710 //
7711 // Get the method bits from the I/O control code to determine how the
7712 // buffers are to be passed to the driver.
7713 //
7714
7715 switch (method) {
7716 // case 0
7717 case METHOD_BUFFERED: {
7718 if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
7719
7720 irp->AssociatedIrp.SystemBuffer =
7721 ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
7722 max(InputBufferLength, OutputBufferLength),
7723 CLASS_TAG_DEVICE_CONTROL
7724 );
7725
7726 if (irp->AssociatedIrp.SystemBuffer == NULL) {
7727 IoFreeIrp(irp);
7728 (*IoStatus).Information = 0;
7729 (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7730 return;
7731 }
7732
7733 if (InputBufferLength != 0) {
7734 RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,
7735 Buffer,
7736 InputBufferLength);
7737 }
7738 } // end of buffering
7739
7740 irp->UserBuffer = Buffer;
7741 break;
7742 }
7743
7744 // case 1, case 2
7745 case METHOD_IN_DIRECT:
7746 case METHOD_OUT_DIRECT: {
7747
7748
7749 if (InputBufferLength != 0) {
7750 irp->AssociatedIrp.SystemBuffer = Buffer;
7751 }
7752
7753 if (OutputBufferLength != 0) {
7754
7755 irp->MdlAddress = IoAllocateMdl(Buffer,
7756 OutputBufferLength,
7757 FALSE, FALSE,
7758 (PIRP) NULL);
7759
7760 if (irp->MdlAddress == NULL) {
7761 IoFreeIrp(irp);
7762 (*IoStatus).Information = 0;
7763 (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7764 return;
7765 }
7766
7767 if (method == METHOD_IN_DIRECT) {
7768 MmProbeAndLockPages(irp->MdlAddress,
7769 KernelMode,
7770 IoReadAccess);
7771 } else if (method == METHOD_OUT_DIRECT) {
7772 MmProbeAndLockPages(irp->MdlAddress,
7773 KernelMode,
7774 IoWriteAccess);
7775 } else {
7776 ASSERT(!"If other methods reach here, code is out of date");
7777 }
7778 }
7779 break;
7780 }
7781
7782 // case 3
7783 case METHOD_NEITHER: {
7784
7785 ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
7786 IoStatus->Information = 0;
7787 IoStatus->Status = STATUS_NOT_SUPPORTED;
7788 return;
7789 break;
7790 }
7791 } // end of switch(method)
7792
7793 irp->Tail.Overlay.Thread = PsGetCurrentThread();
7794
7795 //
7796 // send the irp synchronously
7797 //
7798
7799 ClassSendIrpSynchronous(TargetDeviceObject, irp);
7800
7801 //
7802 // copy the iostatus block for the caller
7803 //
7804
7805 *IoStatus = irp->IoStatus;
7806
7807 //
7808 // free any allocated resources
7809 //
7810
7811 switch (method) {
7812 case METHOD_BUFFERED: {
7813
7814 ASSERT(irp->UserBuffer == Buffer);
7815
7816 //
7817 // first copy the buffered result, if any
7818 // Note that there are no security implications in
7819 // not checking for success since only drivers can
7820 // call into this routine anyways...
7821 //
7822
7823 if (OutputBufferLength != 0) {
7824 RtlCopyMemory(Buffer, // irp->UserBuffer
7825 irp->AssociatedIrp.SystemBuffer,
7826 OutputBufferLength
7827 );
7828 }
7829
7830 //
7831 // then free the memory allocated to buffer the io
7832 //
7833
7834 if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
7835 ExFreePool(irp->AssociatedIrp.SystemBuffer);
7836 irp->AssociatedIrp.SystemBuffer = NULL;
7837 }
7838 break;
7839 }
7840
7841 case METHOD_IN_DIRECT:
7842 case METHOD_OUT_DIRECT: {
7843
7844 //
7845 // we alloc a mdl if there is an output buffer specified
7846 // free it here after unlocking the pages
7847 //
7848
7849 if (OutputBufferLength != 0) {
7850 ASSERT(irp->MdlAddress != NULL);
7851 MmUnlockPages(irp->MdlAddress);
7852 IoFreeMdl(irp->MdlAddress);
7853 irp->MdlAddress = (PMDL) NULL;
7854 }
7855 break;
7856 }
7857
7858 case METHOD_NEITHER: {
7859 ASSERT(!"Code is out of date");
7860 break;
7861 }
7862 }
7863
7864 //
7865 // we always have allocated an irp. free it here.
7866 //
7867
7868 IoFreeIrp(irp);
7869 irp = (PIRP) NULL;
7870
7871 //
7872 // return the io status block's status to the caller
7873 //
7874
7875 return;
7876 } // end ClassSendDeviceIoControlSynchronous()
7877 \f
7878 /*++////////////////////////////////////////////////////////////////////////////
7879
7880 ClassForwardIrpSynchronous()
7881
7882 Routine Description:
7883
7884 Forwards a given irp to the next lower device object.
7885
7886 Arguments:
7887
7888 CommonExtension - the common class extension
7889
7890 Irp - the request to forward down the stack
7891
7892 Return Value:
7893
7894 --*/
7895 NTSTATUS
7896 ClassForwardIrpSynchronous(
7897 IN PCOMMON_DEVICE_EXTENSION CommonExtension,
7898 IN PIRP Irp
7899 )
7900 {
7901 IoCopyCurrentIrpStackLocationToNext(Irp);
7902 return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
7903 } // end ClassForwardIrpSynchronous()
7904 \f
7905 /*++////////////////////////////////////////////////////////////////////////////
7906
7907 ClassSendIrpSynchronous()
7908
7909 Routine Description:
7910
7911 This routine sends the given irp to the given device object, and waits for
7912 it to complete. On debug versions, will print out a debug message and
7913 optionally assert for "lost" irps based upon classpnp's globals
7914
7915 Arguments:
7916
7917 TargetDeviceObject - the device object to handle this irp
7918
7919 Irp - the request to be sent
7920
7921 Return Value:
7922
7923 --*/
7924 NTSTATUS
7925 ClassSendIrpSynchronous(
7926 IN PDEVICE_OBJECT TargetDeviceObject,
7927 IN PIRP Irp
7928 )
7929 {
7930 KEVENT event;
7931 NTSTATUS status;
7932
7933 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
7934 ASSERT(TargetDeviceObject != NULL);
7935 ASSERT(Irp != NULL);
7936 ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
7937
7938 //
7939 // ISSUE-2000/02/20-henrygab What if APCs are disabled?
7940 // May need to enter critical section before IoCallDriver()
7941 // until the event is hit?
7942 //
7943
7944 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
7945 IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
7946 TRUE, TRUE, TRUE);
7947
7948 status = IoCallDriver(TargetDeviceObject, Irp);
7949
7950 if (status == STATUS_PENDING) {
7951
7952 #if DBG
7953 LARGE_INTEGER timeout;
7954
7955 timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
7956 ClasspnpGlobals.SecondsToWaitForIrps);
7957
7958 do {
7959 status = KeWaitForSingleObject(&event,
7960 Executive,
7961 KernelMode,
7962 FALSE,
7963 &timeout);
7964
7965
7966 if (status == STATUS_TIMEOUT) {
7967
7968 //
7969 // This DebugPrint should almost always be investigated by the
7970 // party who sent the irp and/or the current owner of the irp.
7971 // Synchronous Irps should not take this long (currently 30
7972 // seconds) without good reason. This points to a potentially
7973 // serious problem in the underlying device stack.
7974 //
7975
7976 DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
7977 "complete within %x seconds\n",
7978 TargetDeviceObject, Irp,
7979 ClasspnpGlobals.SecondsToWaitForIrps
7980 ));
7981
7982 if (ClasspnpGlobals.BreakOnLostIrps != 0) {
7983 ASSERT(!" - Irp failed to complete within 30 seconds - ");
7984 }
7985 }
7986
7987
7988 } while (status==STATUS_TIMEOUT);
7989 #else
7990 KeWaitForSingleObject(&event,
7991 Executive,
7992 KernelMode,
7993 FALSE,
7994 NULL);
7995 #endif
7996
7997 status = Irp->IoStatus.Status;
7998 }
7999
8000 return status;
8001 } // end ClassSendIrpSynchronous()
8002 \f
8003 /*++////////////////////////////////////////////////////////////////////////////
8004
8005 ClassGetVpb()
8006
8007 Routine Description:
8008
8009 This routine returns the current VPB (Volume Parameter Block) for the
8010 given device object.
8011 The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
8012 of DEVICE_OBJECT; hence this exported function.
8013
8014 Arguments:
8015
8016 DeviceObject - the device to get the VPB for
8017
8018 Return Value:
8019
8020 the VPB, or NULL if none.
8021
8022 --*/
8023 PVPB
8024 ClassGetVpb(
8025 IN PDEVICE_OBJECT DeviceObject
8026 )
8027 {
8028 return DeviceObject->Vpb;
8029 } // end ClassGetVpb()
8030 \f
8031 /*++
8032
8033 ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
8034
8035 --*/
8036 NTSTATUS
8037 ClasspAllocateReleaseRequest(
8038 IN PDEVICE_OBJECT Fdo
8039 )
8040 {
8041 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8042 PIO_STACK_LOCATION irpStack;
8043
8044 KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
8045
8046 fdoExtension->ReleaseQueueNeeded = FALSE;
8047 fdoExtension->ReleaseQueueInProgress = FALSE;
8048 fdoExtension->ReleaseQueueIrpFromPool = FALSE;
8049
8050 //
8051 // The class driver is responsible for allocating a properly sized irp,
8052 // or ClassReleaseQueue will attempt to do it on the first error.
8053 //
8054
8055 fdoExtension->ReleaseQueueIrp = NULL;
8056
8057 //
8058 // Write length to SRB.
8059 //
8060
8061 fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
8062
8063 return STATUS_SUCCESS;
8064 } // end ClasspAllocateReleaseRequest()
8065 \f
8066 /*++
8067
8068 ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
8069
8070 --*/
8071 VOID
8072 ClasspFreeReleaseRequest(
8073 IN PDEVICE_OBJECT Fdo
8074 )
8075 {
8076 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8077 //KIRQL oldIrql;
8078
8079 ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
8080
8081 //
8082 // free anything the driver allocated
8083 //
8084
8085 if (fdoExtension->ReleaseQueueIrp) {
8086 if (fdoExtension->ReleaseQueueIrpFromPool) {
8087 ExFreePool(fdoExtension->ReleaseQueueIrp);
8088 } else {
8089 IoFreeIrp(fdoExtension->ReleaseQueueIrp);
8090 }
8091 fdoExtension->ReleaseQueueIrp = NULL;
8092 }
8093
8094 //
8095 // free anything that we allocated
8096 //
8097
8098 if ((fdoExtension->PrivateFdoData) &&
8099 (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
8100
8101 ExFreePool(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
8102 fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
8103 fdoExtension->PrivateFdoData->ReleaseQueueIrp = NULL;
8104 }
8105
8106 return;
8107 } // end ClasspFreeReleaseRequest()
8108 \f
8109 /*++////////////////////////////////////////////////////////////////////////////
8110
8111 ClassReleaseQueue()
8112
8113 Routine Description:
8114
8115 This routine issues an internal device control command
8116 to the port driver to release a frozen queue. The call
8117 is issued asynchronously as ClassReleaseQueue will be invoked
8118 from the IO completion DPC (and will have no context to
8119 wait for a synchronous call to complete).
8120
8121 This routine must be called with the remove lock held.
8122
8123 Arguments:
8124
8125 Fdo - The functional device object for the device with the frozen queue.
8126
8127 Return Value:
8128
8129 None.
8130
8131 --*/
8132 VOID
8133 ClassReleaseQueue(
8134 IN PDEVICE_OBJECT Fdo
8135 )
8136 {
8137 ClasspReleaseQueue(Fdo, NULL);
8138 return;
8139 } // end ClassReleaseQueue()
8140 \f
8141 /*++////////////////////////////////////////////////////////////////////////////
8142
8143 ClasspAllocateReleaseQueueIrp()
8144
8145 Routine Description:
8146
8147 This routine allocates the release queue irp held in classpnp's private
8148 extension. This was added to allow no-memory conditions to be more
8149 survivable.
8150
8151 Return Value:
8152
8153 NT_SUCCESS value.
8154
8155 Notes:
8156
8157 Does not grab the spinlock. Should only be called from StartDevice()
8158 routine. May be called elsewhere for poorly-behaved drivers that cause
8159 the queue to lockup before the device is started. This should *never*
8160 occur, since it's illegal to send a request to a non-started PDO. This
8161 condition is checked for in ClasspReleaseQueue().
8162
8163 --*/
8164 NTSTATUS
8165 ClasspAllocateReleaseQueueIrp(
8166 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8167 )
8168 {
8169 KIRQL oldIrql;
8170 UCHAR lowerStackSize;
8171
8172 //
8173 // do an initial check w/o the spinlock
8174 //
8175
8176 if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8177 return STATUS_SUCCESS;
8178 }
8179
8180
8181 lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
8182
8183 //
8184 // don't allocate one if one is in progress! this means whoever called
8185 // this routine didn't check if one was in progress.
8186 //
8187
8188 ASSERT(!(FdoExtension->ReleaseQueueInProgress));
8189
8190 FdoExtension->PrivateFdoData->ReleaseQueueIrp =
8191 ExAllocatePoolWithTag(NonPagedPool,
8192 IoSizeOfIrp(lowerStackSize),
8193 CLASS_TAG_RELEASE_QUEUE
8194 );
8195
8196 if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
8197 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
8198 "release queue irp\n"));
8199 return STATUS_INSUFFICIENT_RESOURCES;
8200 }
8201 IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
8202 IoSizeOfIrp(lowerStackSize),
8203 lowerStackSize);
8204 FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
8205
8206 return STATUS_SUCCESS;
8207 }
8208
8209
8210 /*++////////////////////////////////////////////////////////////////////////////
8211
8212 ClasspReleaseQueue()
8213
8214 Routine Description:
8215
8216 This routine issues an internal device control command
8217 to the port driver to release a frozen queue. The call
8218 is issued asynchronously as ClassReleaseQueue will be invoked
8219 from the IO completion DPC (and will have no context to
8220 wait for a synchronous call to complete).
8221
8222 This routine must be called with the remove lock held.
8223
8224 Arguments:
8225
8226 Fdo - The functional device object for the device with the frozen queue.
8227
8228 ReleaseQueueIrp - If this irp is supplied then the test to determine whether
8229 a release queue request is in progress will be ignored.
8230 The irp provided must be the IRP originally allocated
8231 for release queue requests (so this parameter can only
8232 really be provided by the release queue completion
8233 routine.)
8234
8235 Return Value:
8236
8237 None.
8238
8239 --*/
8240 VOID
8241 ClasspReleaseQueue(
8242 IN PDEVICE_OBJECT Fdo,
8243 IN PIRP ReleaseQueueIrp OPTIONAL
8244 )
8245 {
8246 PIO_STACK_LOCATION irpStack;
8247 PIRP irp;
8248 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8249 PDEVICE_OBJECT lowerDevice;
8250 PSCSI_REQUEST_BLOCK srb;
8251 KIRQL currentIrql;
8252
8253 lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
8254
8255 //
8256 // we raise irql seperately so we're not swapped out or suspended
8257 // while holding the release queue irp in this routine. this lets
8258 // us release the spin lock before lowering irql.
8259 //
8260
8261 KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
8262
8263 KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8264
8265 //
8266 // make sure that if they passed us an irp, it matches our allocated irp.
8267 //
8268
8269 ASSERT((ReleaseQueueIrp == NULL) ||
8270 (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
8271
8272 //
8273 // ASSERT that we've already allocated this. (should not occur)
8274 // try to allocate it anyways, then finally bugcheck if
8275 // there's still no memory...
8276 //
8277
8278 ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
8279 if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8280 ClasspAllocateReleaseQueueIrp(fdoExtension);
8281 }
8282 if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8283 KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
8284 }
8285
8286 if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
8287
8288 //
8289 // Someone is already using the irp - just set the flag to indicate that
8290 // we need to release the queue again.
8291 //
8292
8293 fdoExtension->ReleaseQueueNeeded = TRUE;
8294 KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8295 KeLowerIrql(currentIrql);
8296 return;
8297
8298 }
8299
8300 //
8301 // Mark that there is a release queue in progress and drop the spinlock.
8302 //
8303
8304 fdoExtension->ReleaseQueueInProgress = TRUE;
8305 if (ReleaseQueueIrp) {
8306 irp = ReleaseQueueIrp;
8307 } else {
8308 irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
8309 }
8310 srb = &(fdoExtension->ReleaseQueueSrb);
8311
8312 KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8313
8314 ASSERT(irp != NULL);
8315
8316 irpStack = IoGetNextIrpStackLocation(irp);
8317
8318 irpStack->MajorFunction = IRP_MJ_SCSI;
8319
8320 srb->OriginalRequest = irp;
8321
8322 //
8323 // Store the SRB address in next stack for port driver.
8324 //
8325
8326 irpStack->Parameters.Scsi.Srb = srb;
8327
8328 //
8329 // If this device is removable then flush the queue. This will also
8330 // release it.
8331 //
8332
8333 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
8334 srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
8335 }
8336 else {
8337 srb->Function = SRB_FUNCTION_RELEASE_QUEUE;
8338 }
8339
8340 ClassAcquireRemoveLock(Fdo, irp);
8341
8342 IoSetCompletionRoutine(irp,
8343 ClassReleaseQueueCompletion,
8344 Fdo,
8345 TRUE,
8346 TRUE,
8347 TRUE);
8348
8349 IoCallDriver(lowerDevice, irp);
8350
8351 KeLowerIrql(currentIrql);
8352
8353 return;
8354
8355 } // end ClassReleaseQueue()
8356 \f
8357 /*++////////////////////////////////////////////////////////////////////////////
8358
8359 ClassReleaseQueueCompletion()
8360
8361 Routine Description:
8362
8363 This routine is called when an asynchronous I/O request
8364 which was issused by the class driver completes. Examples of such requests
8365 are release queue or START UNIT. This routine releases the queue if
8366 necessary. It then frees the context and the IRP.
8367
8368 Arguments:
8369
8370 DeviceObject - The device object for the logical unit; however since this
8371 is the top stack location the value is NULL.
8372
8373 Irp - Supplies a pointer to the Irp to be processed.
8374
8375 Context - Supplies the context to be used to process this request.
8376
8377 Return Value:
8378
8379 None.
8380
8381 --*/
8382 NTSTATUS
8383 ClassReleaseQueueCompletion(
8384 PDEVICE_OBJECT DeviceObject,
8385 PIRP Irp,
8386 PVOID Context
8387 )
8388 {
8389 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8390 KIRQL oldIrql;
8391
8392 BOOLEAN releaseQueueNeeded;
8393
8394 DeviceObject = Context;
8395
8396 fdoExtension = DeviceObject->DeviceExtension;
8397
8398 ClassReleaseRemoveLock(DeviceObject, Irp);
8399
8400 //
8401 // Grab the spinlock and clear the release queue in progress flag so others
8402 // can run. Save (and clear) the state of the release queue needed flag
8403 // so that we can issue a new release queue outside the spinlock.
8404 //
8405
8406 KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
8407
8408 releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
8409
8410 fdoExtension->ReleaseQueueNeeded = FALSE;
8411 fdoExtension->ReleaseQueueInProgress = FALSE;
8412
8413 KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
8414
8415 //
8416 // If we need a release queue then issue one now. Another processor may
8417 // have already started one in which case we'll try to issue this one after
8418 // it is done - but we should never recurse more than one deep.
8419 //
8420
8421 if(releaseQueueNeeded) {
8422 ClasspReleaseQueue(DeviceObject, Irp);
8423 }
8424
8425 //
8426 // Indicate the I/O system should stop processing the Irp completion.
8427 //
8428
8429 return STATUS_MORE_PROCESSING_REQUIRED;
8430
8431 } // ClassAsynchronousCompletion()
8432 \f
8433 /*++////////////////////////////////////////////////////////////////////////////
8434
8435 ClassAcquireChildLock()
8436
8437 Routine Description:
8438
8439 This routine acquires the lock protecting children PDOs. It may be
8440 acquired recursively by the same thread, but must be release by the
8441 thread once for each acquisition.
8442
8443 Arguments:
8444
8445 FdoExtension - the device whose child list is protected.
8446
8447 Return Value:
8448
8449 None
8450
8451 --*/
8452 VOID
8453 ClassAcquireChildLock(
8454 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8455 )
8456 {
8457 PAGED_CODE();
8458
8459 if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
8460 KeWaitForSingleObject(&FdoExtension->ChildLock,
8461 Executive, KernelMode,
8462 FALSE, NULL);
8463
8464 ASSERT(FdoExtension->ChildLockOwner == NULL);
8465 ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
8466
8467 FdoExtension->ChildLockOwner = KeGetCurrentThread();
8468 } else {
8469 ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
8470 }
8471
8472 FdoExtension->ChildLockAcquisitionCount++;
8473 return;
8474 }
8475 \f
8476 /*++////////////////////////////////////////////////////////////////////////////
8477
8478 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
8479
8480 Routine Description:
8481
8482 This routine releases the lock protecting children PDOs. It must be
8483 called once for each time ClassAcquireChildLock was called.
8484
8485 Arguments:
8486
8487 FdoExtension - the device whose child list is protected
8488
8489 Return Value:
8490
8491 None.
8492
8493 --*/
8494 VOID
8495 ClassReleaseChildLock(
8496 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8497 )
8498 {
8499 ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
8500 ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
8501
8502 FdoExtension->ChildLockAcquisitionCount -= 1;
8503
8504 if(FdoExtension->ChildLockAcquisitionCount == 0) {
8505 FdoExtension->ChildLockOwner = NULL;
8506 KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
8507 }
8508
8509 return;
8510 } // end ClassReleaseChildLock(
8511 \f
8512 /*++////////////////////////////////////////////////////////////////////////////
8513
8514 ClassAddChild()
8515
8516 Routine Description:
8517
8518 This routine will insert a new child into the head of the child list.
8519
8520 Arguments:
8521
8522 Parent - the child's parent (contains the head of the list)
8523 Child - the child to be inserted.
8524 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8525 it's already been acquired by or on behalf of the caller
8526 (FALSE).
8527
8528 Return Value:
8529
8530 None.
8531
8532 --*/
8533 VOID
8534 ClassAddChild(
8535 IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
8536 IN PPHYSICAL_DEVICE_EXTENSION Child,
8537 IN BOOLEAN AcquireLock
8538 )
8539 {
8540 if(AcquireLock) {
8541 ClassAcquireChildLock(Parent);
8542 }
8543
8544 #if DBG
8545 //
8546 // Make sure this child's not already in the list.
8547 //
8548 {
8549 PPHYSICAL_DEVICE_EXTENSION testChild;
8550
8551 for (testChild = Parent->CommonExtension.ChildList;
8552 testChild != NULL;
8553 testChild = testChild->CommonExtension.ChildList) {
8554
8555 ASSERT(testChild != Child);
8556 }
8557 }
8558 #endif
8559
8560 Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
8561 Parent->CommonExtension.ChildList = Child;
8562
8563 if(AcquireLock) {
8564 ClassReleaseChildLock(Parent);
8565 }
8566 return;
8567 } // end ClassAddChild()
8568 \f
8569 /*++////////////////////////////////////////////////////////////////////////////
8570
8571 ClassRemoveChild()
8572
8573 Routine Description:
8574
8575 This routine will remove a child from the child list.
8576
8577 Arguments:
8578
8579 Parent - the parent to be removed from.
8580
8581 Child - the child to be removed or NULL if the first child should be
8582 removed.
8583
8584 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8585 it's already been acquired by or on behalf of the caller
8586 (FALSE).
8587
8588 Return Value:
8589
8590 A pointer to the child which was removed or NULL if no such child could
8591 be found in the list (or if Child was NULL but the list is empty).
8592
8593 --*/
8594 PPHYSICAL_DEVICE_EXTENSION
8595 ClassRemoveChild(
8596 IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
8597 IN PPHYSICAL_DEVICE_EXTENSION Child,
8598 IN BOOLEAN AcquireLock
8599 )
8600 {
8601 if(AcquireLock) {
8602 ClassAcquireChildLock(Parent);
8603 }
8604
8605 TRY {
8606 PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
8607
8608 //
8609 // If the list is empty then bail out now.
8610 //
8611
8612 if(Parent->CommonExtension.ChildList == NULL) {
8613 Child = NULL;
8614 LEAVE;
8615 }
8616
8617 //
8618 // If the caller specified a child then find the child object before
8619 // it. If none was specified then the FDO is the child object before
8620 // the one we want to remove.
8621 //
8622
8623 if(Child != NULL) {
8624
8625 //
8626 // Scan through the child list to find the entry which points to
8627 // this one.
8628 //
8629
8630 do {
8631 ASSERT(previousChild != &Child->CommonExtension);
8632
8633 if(previousChild->ChildList == Child) {
8634 break;
8635 }
8636
8637 previousChild = &previousChild->ChildList->CommonExtension;
8638 } while(previousChild != NULL);
8639
8640 if(previousChild == NULL) {
8641 Child = NULL;
8642 LEAVE;
8643 }
8644 }
8645
8646 //
8647 // Save the next child away then unlink it from the list.
8648 //
8649
8650 Child = previousChild->ChildList;
8651 previousChild->ChildList = Child->CommonExtension.ChildList;
8652 Child->CommonExtension.ChildList = NULL;
8653
8654 } FINALLY {
8655 if(AcquireLock) {
8656 ClassReleaseChildLock(Parent);
8657 }
8658 }
8659 return Child;
8660 } // end ClassRemoveChild()
8661
8662 \f
8663 /*++
8664
8665 ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
8666
8667 --*/
8668 VOID
8669 ClasspRetryRequestDpc(
8670 IN PKDPC Dpc,
8671 IN PDEVICE_OBJECT DeviceObject,
8672 IN PVOID Arg1,
8673 IN PVOID Arg2
8674 )
8675 {
8676 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8677 PCOMMON_DEVICE_EXTENSION commonExtension;
8678 PCLASS_PRIVATE_FDO_DATA fdoData;
8679 PCLASS_RETRY_INFO retryList;
8680 KIRQL irql;
8681
8682
8683 commonExtension = DeviceObject->DeviceExtension;
8684 ASSERT(commonExtension->IsFdo);
8685 fdoExtension = DeviceObject->DeviceExtension;
8686 fdoData = fdoExtension->PrivateFdoData;
8687
8688
8689 KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
8690 {
8691 LARGE_INTEGER now;
8692 KeQueryTickCount(&now);
8693
8694 //
8695 // if CurrentTick is less than now
8696 // fire another DPC
8697 // else
8698 // retry entire list
8699 // endif
8700 //
8701
8702 if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
8703
8704 ClasspRetryDpcTimer(fdoData);
8705 retryList = NULL;
8706
8707 } else {
8708
8709 retryList = fdoData->Retry.ListHead;
8710 fdoData->Retry.ListHead = NULL;
8711 fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
8712 fdoData->Retry.Tick.QuadPart = (LONGLONG)0;
8713
8714 }
8715 }
8716 KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
8717
8718 while (retryList != NULL) {
8719
8720 PIRP irp;
8721
8722 irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
8723 DebugPrint((ClassDebugDelayedRetry, "ClassRetry: -- %p\n", irp));
8724 retryList = retryList->Next;
8725 #if DBG
8726 irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
8727 irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
8728 irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
8729 irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
8730 #endif
8731
8732 IoCallDriver(commonExtension->LowerDeviceObject, irp);
8733
8734 }
8735 return;
8736
8737 } // end ClasspRetryRequestDpc()
8738 \f
8739 /*++
8740
8741 ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
8742
8743 --*/
8744 VOID
8745 ClassRetryRequest(
8746 IN PDEVICE_OBJECT SelfDeviceObject,
8747 IN PIRP Irp,
8748 IN LARGE_INTEGER TimeDelta100ns // in 100ns units
8749 )
8750 {
8751 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8752 PCLASS_PRIVATE_FDO_DATA fdoData;
8753 PCLASS_RETRY_INFO retryInfo;
8754 PCLASS_RETRY_INFO *previousNext;
8755 LARGE_INTEGER delta;
8756 KIRQL irql;
8757
8758 //
8759 // this checks we aren't destroying irps
8760 //
8761 ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
8762
8763 fdoExtension = SelfDeviceObject->DeviceExtension;
8764 fdoData = fdoExtension->PrivateFdoData;
8765
8766 if (!fdoExtension->CommonExtension.IsFdo) {
8767
8768 //
8769 // this debug print/assertion should ALWAYS be investigated.
8770 // ClassRetryRequest can currently only be used by FDO's
8771 //
8772
8773 DebugPrint((ClassDebugError, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
8774 ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
8775 return;
8776
8777 }
8778
8779 if (TimeDelta100ns.QuadPart < 0) {
8780 ASSERT(!"ClassRetryRequest - must use positive delay");
8781 TimeDelta100ns.QuadPart *= -1;
8782 }
8783
8784 //
8785 // prepare what we can out of the loop
8786 //
8787
8788 retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
8789 RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
8790
8791 delta.QuadPart = (TimeDelta100ns.QuadPart / fdoData->Retry.Granularity);
8792 if (TimeDelta100ns.QuadPart % fdoData->Retry.Granularity) {
8793 delta.QuadPart ++; // round up to next tick
8794 }
8795 if (delta.QuadPart == (LONGLONG)0) {
8796 delta.QuadPart = MINIMUM_RETRY_UNITS;
8797 }
8798
8799 //
8800 // now determine if we should fire another DPC or not
8801 //
8802
8803 KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
8804
8805 //
8806 // always add request to the list
8807 //
8808
8809 retryInfo->Next = fdoData->Retry.ListHead;
8810 fdoData->Retry.ListHead = retryInfo;
8811
8812 if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
8813
8814 DebugPrint((ClassDebugDelayedRetry, "ClassRetry: +++ %p\n", Irp));
8815
8816 //
8817 // must be exactly one item on list
8818 //
8819
8820 ASSERT(fdoData->Retry.ListHead != NULL);
8821 ASSERT(fdoData->Retry.ListHead->Next == NULL);
8822
8823 //
8824 // if currentDelta is zero, always fire a DPC
8825 //
8826
8827 KeQueryTickCount(&fdoData->Retry.Tick);
8828 fdoData->Retry.Tick.QuadPart += delta.QuadPart;
8829 fdoData->Retry.Delta.QuadPart = delta.QuadPart;
8830 ClasspRetryDpcTimer(fdoData);
8831
8832 } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
8833
8834 //
8835 // if delta is greater than the list's current delta,
8836 // increase the DPC handling time by difference
8837 // and update the delta to new larger value
8838 // allow the DPC to re-fire itself if needed
8839 //
8840
8841 DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp));
8842
8843 //
8844 // must be at least two items on list
8845 //
8846
8847 ASSERT(fdoData->Retry.ListHead != NULL);
8848 ASSERT(fdoData->Retry.ListHead->Next != NULL);
8849
8850 fdoData->Retry.Tick.QuadPart -= fdoData->Retry.Delta.QuadPart;
8851 fdoData->Retry.Tick.QuadPart += delta.QuadPart;
8852
8853 fdoData->Retry.Delta.QuadPart = delta.QuadPart;
8854
8855 } else {
8856
8857 //
8858 // just inserting it on the list was enough
8859 //
8860
8861 DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp));
8862
8863 }
8864
8865
8866 KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
8867
8868
8869 } // end ClassRetryRequest()
8870 \f
8871 /*++
8872
8873 ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
8874
8875 --*/
8876 VOID
8877 ClasspRetryDpcTimer(
8878 IN PCLASS_PRIVATE_FDO_DATA FdoData
8879 )
8880 {
8881 LARGE_INTEGER fire;
8882
8883 ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
8884 ASSERT(FdoData->Retry.ListHead != NULL); // never fire an empty list
8885
8886 //
8887 // fire == (CurrentTick - now) * (100ns per tick)
8888 //
8889 // NOTE: Overflow is nearly impossible and is ignored here
8890 //
8891
8892 KeQueryTickCount(&fire);
8893 fire.QuadPart = FdoData->Retry.Tick.QuadPart - fire.QuadPart;
8894 fire.QuadPart *= FdoData->Retry.Granularity;
8895
8896 //
8897 // fire is now multiples of 100ns until should fire the timer.
8898 // if timer should already have expired, or would fire too quickly,
8899 // fire it in some arbitrary number of ticks to prevent infinitely
8900 // recursing.
8901 //
8902
8903 if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
8904 fire.QuadPart = MINIMUM_RETRY_UNITS;
8905 }
8906
8907 DebugPrint((ClassDebugDelayedRetry,
8908 "ClassRetry: ======= %I64x ticks\n",
8909 fire.QuadPart));
8910
8911 //
8912 // must use negative to specify relative time to fire
8913 //
8914
8915 fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
8916
8917 //
8918 // set the timer, since this is the first addition
8919 //
8920
8921 KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
8922
8923 return;
8924 } // end ClasspRetryDpcTimer()
8925 \f
8926 NTSTATUS
8927 ClasspInitializeHotplugInfo(
8928 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8929 )
8930 {
8931 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
8932 DEVICE_REMOVAL_POLICY deviceRemovalPolicy;
8933 NTSTATUS status;
8934 ULONG resultLength = 0;
8935 ULONG writeCacheOverride;
8936
8937 PAGED_CODE();
8938
8939 //
8940 // start with some default settings
8941 //
8942 RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
8943
8944 //
8945 // set the size (aka version)
8946 //
8947
8948 fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
8949
8950 //
8951 // set if the device has removable media
8952 //
8953
8954 if (FdoExtension->DeviceDescriptor->RemovableMedia) {
8955 fdoData->HotplugInfo.MediaRemovable = TRUE;
8956 } else {
8957 fdoData->HotplugInfo.MediaRemovable = FALSE;
8958 }
8959
8960 //
8961 // this refers to devices which, for reasons not yet understood,
8962 // do not fail PREVENT_MEDIA_REMOVAL requests even though they
8963 // have no way to lock the media into the drive. this allows
8964 // the filesystems to turn off delayed-write caching for these
8965 // devices as well.
8966 //
8967
8968 if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
8969 FDO_HACK_CANNOT_LOCK_MEDIA)) {
8970 fdoData->HotplugInfo.MediaHotplug = TRUE;
8971 } else {
8972 fdoData->HotplugInfo.MediaHotplug = FALSE;
8973 }
8974
8975
8976 //
8977 // Look into the registry to see if the user has chosen
8978 // to override the default setting for the removal policy
8979 //
8980
8981 RtlZeroMemory(&deviceRemovalPolicy, sizeof(DEVICE_REMOVAL_POLICY));
8982
8983 ClassGetDeviceParameter(FdoExtension,
8984 CLASSP_REG_SUBKEY_NAME,
8985 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
8986 (PULONG)&deviceRemovalPolicy);
8987
8988 if (deviceRemovalPolicy == 0)
8989 {
8990 //
8991 // Query the default removal policy from the kernel
8992 //
8993
8994 status = IoGetDeviceProperty(FdoExtension->LowerPdo,
8995 DevicePropertyRemovalPolicy,
8996 sizeof(DEVICE_REMOVAL_POLICY),
8997 (PVOID)&deviceRemovalPolicy,
8998 &resultLength);
8999 if (!NT_SUCCESS(status))
9000 {
9001 return status;
9002 }
9003
9004 if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
9005 {
9006 return STATUS_UNSUCCESSFUL;
9007 }
9008 }
9009
9010 //
9011 // use this info to set the DeviceHotplug setting
9012 // don't rely on DeviceCapabilities, since it can't properly
9013 // determine device relations, etc. let the kernel figure this
9014 // stuff out instead.
9015 //
9016
9017 if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
9018 fdoData->HotplugInfo.DeviceHotplug = TRUE;
9019 } else {
9020 fdoData->HotplugInfo.DeviceHotplug = FALSE;
9021 }
9022
9023 //
9024 // this refers to the *filesystem* caching, but has to be included
9025 // here since it's a per-device setting. this may change to be
9026 // stored by the system in the future.
9027 //
9028
9029 writeCacheOverride = FALSE;
9030 ClassGetDeviceParameter(FdoExtension,
9031 CLASSP_REG_SUBKEY_NAME,
9032 CLASSP_REG_WRITE_CACHE_VALUE_NAME,
9033 &writeCacheOverride);
9034
9035 if (writeCacheOverride) {
9036 fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
9037 } else {
9038 fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
9039 }
9040
9041 return STATUS_SUCCESS;
9042 }
9043 \f
9044 VOID
9045 ClasspScanForClassHacks(
9046 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
9047 IN ULONG_PTR Data
9048 )
9049 {
9050 PAGED_CODE();
9051
9052 //
9053 // remove invalid flags and save
9054 //
9055
9056 CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
9057 SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
9058 return;
9059 }
9060 \f
9061 VOID
9062 ClasspScanForSpecialInRegistry(
9063 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
9064 )
9065 {
9066 HANDLE deviceParameterHandle; // device instance key
9067 HANDLE classParameterHandle; // classpnp subkey
9068 OBJECT_ATTRIBUTES objectAttributes;
9069 UNICODE_STRING subkeyName;
9070 NTSTATUS status;
9071
9072 //
9073 // seeded in the ENUM tree by ClassInstaller
9074 //
9075 ULONG deviceHacks;
9076 RTL_QUERY_REGISTRY_TABLE queryTable[2]; // null terminated array
9077
9078 PAGED_CODE();
9079
9080 deviceParameterHandle = NULL;
9081 classParameterHandle = NULL;
9082 deviceHacks = 0;
9083
9084 status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
9085 PLUGPLAY_REGKEY_DEVICE,
9086 KEY_WRITE,
9087 &deviceParameterHandle
9088 );
9089
9090 if (!NT_SUCCESS(status)) {
9091 goto cleanupScanForSpecial;
9092 }
9093
9094 RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
9095 InitializeObjectAttributes(&objectAttributes,
9096 &subkeyName,
9097 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
9098 deviceParameterHandle,
9099 NULL
9100 );
9101
9102 status = ZwOpenKey( &classParameterHandle,
9103 KEY_READ,
9104 &objectAttributes
9105 );
9106
9107 if (!NT_SUCCESS(status)) {
9108 goto cleanupScanForSpecial;
9109 }
9110
9111 //
9112 // Zero out the memory
9113 //
9114
9115 RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
9116
9117 //
9118 // Setup the structure to read
9119 //
9120
9121 queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
9122 queryTable[0].Name = CLASSP_REG_HACK_VALUE_NAME;
9123 queryTable[0].EntryContext = &deviceHacks;
9124 queryTable[0].DefaultType = REG_DWORD;
9125 queryTable[0].DefaultData = &deviceHacks;
9126 queryTable[0].DefaultLength = 0;
9127
9128 //
9129 // read values
9130 //
9131
9132 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
9133 (PWSTR)classParameterHandle,
9134 &queryTable[0],
9135 NULL,
9136 NULL
9137 );
9138 if (!NT_SUCCESS(status)) {
9139 goto cleanupScanForSpecial;
9140 }
9141
9142 //
9143 // remove unknown values and save...
9144 //
9145
9146 KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
9147 "Classpnp => ScanForSpecial: HackFlags %#08x\n",
9148 deviceHacks));
9149
9150 CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
9151 SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
9152
9153
9154 cleanupScanForSpecial:
9155
9156 if (deviceParameterHandle) {
9157 ZwClose(deviceParameterHandle);
9158 }
9159
9160 if (classParameterHandle) {
9161 ZwClose(classParameterHandle);
9162 }
9163
9164 //
9165 // we should modify the system hive to include another key for us to grab
9166 // settings from. in this case: Classpnp\HackFlags
9167 //
9168 // the use of a DWORD value for the HackFlags allows 32 hacks w/o
9169 // significant use of the registry, and also reduces OEM exposure.
9170 //
9171 // definition of bit flags:
9172 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
9173 // cannot actually prevent removal.
9174 // 0x00000002 -- Device hard-hangs or times out for GESN requests.
9175 // 0xfffffffc -- Currently reserved, may be used later.
9176 //
9177
9178 return;
9179 }
9180
9181
9182
9183