0a1c28ea9c54519c4f4ea400d21b46b746330cc1
[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 }
3506
3507 default: {
3508 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3509 "Unit attention\n"));
3510 break;
3511 }
3512
3513 } // end switch (senseBuffer->AdditionalSenseCode)
3514
3515 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
3516 {
3517 //
3518 // TODO : Is the media lockable?
3519 //
3520
3521 if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
3522 {
3523 //
3524 // Set bit to indicate that media may have changed
3525 // and volume needs verification.
3526 //
3527
3528 SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
3529
3530 *Status = STATUS_VERIFY_REQUIRED;
3531 retry = FALSE;
3532 }
3533 }
3534 else
3535 {
3536 *Status = STATUS_IO_DEVICE_ERROR;
3537 }
3538
3539 break;
3540
3541 } // end SCSI_SENSE_UNIT_ATTENTION
3542
3543 case SCSI_SENSE_ABORTED_COMMAND: {
3544 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3545 "Command aborted\n"));
3546 *Status = STATUS_IO_DEVICE_ERROR;
3547 retryInterval = 1;
3548 break;
3549 } // end SCSI_SENSE_ABORTED_COMMAND
3550
3551 case SCSI_SENSE_BLANK_CHECK: {
3552 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3553 "Media blank check\n"));
3554 retry = FALSE;
3555 *Status = STATUS_NO_DATA_DETECTED;
3556 break;
3557 } // end SCSI_SENSE_BLANK_CHECK
3558
3559 case SCSI_SENSE_RECOVERED_ERROR: {
3560
3561 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3562 "Recovered error\n"));
3563 *Status = STATUS_SUCCESS;
3564 retry = FALSE;
3565 logError = TRUE;
3566 uniqueId = 258;
3567
3568 switch(senseBuffer->AdditionalSenseCode) {
3569 case SCSI_ADSENSE_SEEK_ERROR:
3570 case SCSI_ADSENSE_TRACK_ERROR: {
3571 logStatus = IO_ERR_SEEK_ERROR;
3572 break;
3573 }
3574
3575 case SCSI_ADSENSE_REC_DATA_NOECC:
3576 case SCSI_ADSENSE_REC_DATA_ECC: {
3577 logStatus = IO_RECOVERED_VIA_ECC;
3578 break;
3579 }
3580
3581 case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
3582 UCHAR wmiEventData[5];
3583
3584 *((PULONG)wmiEventData) = sizeof(UCHAR);
3585 wmiEventData[sizeof(ULONG)] = senseBuffer->AdditionalSenseCodeQualifier;
3586
3587 //
3588 // Don't log another eventlog if we have already logged once
3589 // NOTE: this should have been interlocked, but the structure
3590 // was publicly defined to use a BOOLEAN (char). Since
3591 // media only reports these errors once per X minutes,
3592 // the potential race condition is nearly non-existant.
3593 // the worst case is duplicate log entries, so ignore.
3594 //
3595
3596 if (fdoExtension->FailurePredicted == 0) {
3597 logError = TRUE;
3598 }
3599 fdoExtension->FailurePredicted = TRUE;
3600 fdoExtension->FailureReason = senseBuffer->AdditionalSenseCodeQualifier;
3601 logStatus = IO_WRN_FAILURE_PREDICTED;
3602
3603 ClassNotifyFailurePredicted(fdoExtension,
3604 (PUCHAR)&wmiEventData,
3605 sizeof(wmiEventData),
3606 0,
3607 4,
3608 Srb->PathId,
3609 Srb->TargetId,
3610 Srb->Lun);
3611 break;
3612 }
3613
3614 default: {
3615 logStatus = IO_ERR_CONTROLLER_ERROR;
3616 break;
3617 }
3618
3619 } // end switch(senseBuffer->AdditionalSenseCode)
3620
3621 if (senseBuffer->IncorrectLength) {
3622
3623 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3624 "Incorrect length detected.\n"));
3625 *Status = STATUS_INVALID_BLOCK_LENGTH ;
3626 }
3627
3628 break;
3629 } // end SCSI_SENSE_RECOVERED_ERROR
3630
3631 case SCSI_SENSE_NO_SENSE: {
3632
3633 //
3634 // Check other indicators.
3635 //
3636
3637 if (senseBuffer->IncorrectLength) {
3638
3639 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3640 "Incorrect length detected.\n"));
3641 *Status = STATUS_INVALID_BLOCK_LENGTH ;
3642 retry = FALSE;
3643
3644 } else {
3645
3646 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3647 "No specific sense key\n"));
3648 *Status = STATUS_IO_DEVICE_ERROR;
3649 retry = TRUE;
3650 }
3651
3652 break;
3653 } // end SCSI_SENSE_NO_SENSE
3654
3655 default: {
3656 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3657 "Unrecognized sense code\n"));
3658 *Status = STATUS_IO_DEVICE_ERROR;
3659 break;
3660 }
3661
3662 } // end switch (senseBuffer->SenseKey & 0xf)
3663
3664 //
3665 // Try to determine the bad sector from the inquiry data.
3666 //
3667
3668 if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_READ ||
3669 ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY ||
3670 ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_WRITE)) {
3671
3672 for (index = 0; index < 4; index++) {
3673 badSector = (badSector << 8) | senseBuffer->Information[index];
3674 }
3675
3676 readSector = 0;
3677 for (index = 0; index < 4; index++) {
3678 readSector = (readSector << 8) | Srb->Cdb[index+2];
3679 }
3680
3681 index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
3682 ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
3683
3684 //
3685 // Make sure the bad sector is within the read sectors.
3686 //
3687
3688 if (!(badSector >= readSector && badSector < readSector + index)) {
3689 badSector = readSector;
3690 }
3691 }
3692
3693 } else {
3694
3695 //
3696 // Request sense buffer not valid. No sense information
3697 // to pinpoint the error. Return general request fail.
3698 //
3699
3700 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3701 "Request sense info not valid. SrbStatus %2x\n",
3702 SRB_STATUS(Srb->SrbStatus)));
3703 retry = TRUE;
3704
3705 switch (SRB_STATUS(Srb->SrbStatus)) {
3706 case SRB_STATUS_INVALID_LUN:
3707 case SRB_STATUS_INVALID_TARGET_ID:
3708 case SRB_STATUS_NO_DEVICE:
3709 case SRB_STATUS_NO_HBA:
3710 case SRB_STATUS_INVALID_PATH_ID: {
3711 *Status = STATUS_NO_SUCH_DEVICE;
3712 retry = FALSE;
3713 break;
3714 }
3715
3716 case SRB_STATUS_COMMAND_TIMEOUT:
3717 case SRB_STATUS_TIMEOUT: {
3718
3719 //
3720 // Update the error count for the device.
3721 //
3722
3723 incrementErrorCount = TRUE;
3724 *Status = STATUS_IO_TIMEOUT;
3725 break;
3726 }
3727
3728 case SRB_STATUS_ABORTED: {
3729
3730 //
3731 // Update the error count for the device.
3732 //
3733
3734 incrementErrorCount = TRUE;
3735 *Status = STATUS_IO_TIMEOUT;
3736 retryInterval = 1;
3737 break;
3738 }
3739
3740
3741 case SRB_STATUS_SELECTION_TIMEOUT: {
3742 logError = TRUE;
3743 logStatus = IO_ERR_NOT_READY;
3744 uniqueId = 260;
3745 *Status = STATUS_DEVICE_NOT_CONNECTED;
3746 retry = FALSE;
3747 break;
3748 }
3749
3750 case SRB_STATUS_DATA_OVERRUN: {
3751 *Status = STATUS_DATA_OVERRUN;
3752 retry = FALSE;
3753 break;
3754 }
3755
3756 case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
3757
3758 //
3759 // Update the error count for the device.
3760 //
3761
3762 incrementErrorCount = TRUE;
3763 *Status = STATUS_IO_DEVICE_ERROR;
3764
3765 //
3766 // If there was phase sequence error then limit the number of
3767 // retries.
3768 //
3769
3770 if (RetryCount > 1 ) {
3771 retry = FALSE;
3772 }
3773
3774 break;
3775 }
3776
3777 case SRB_STATUS_REQUEST_FLUSHED: {
3778
3779 //
3780 // If the status needs verification bit is set. Then set
3781 // the status to need verification and no retry; otherwise,
3782 // just retry the request.
3783 //
3784
3785 if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
3786
3787 *Status = STATUS_VERIFY_REQUIRED;
3788 retry = FALSE;
3789
3790 } else {
3791 *Status = STATUS_IO_DEVICE_ERROR;
3792 }
3793
3794 break;
3795 }
3796
3797 case SRB_STATUS_INVALID_REQUEST: {
3798 *Status = STATUS_INVALID_DEVICE_REQUEST;
3799 retry = FALSE;
3800 break;
3801 }
3802
3803 case SRB_STATUS_UNEXPECTED_BUS_FREE:
3804 case SRB_STATUS_PARITY_ERROR:
3805
3806 //
3807 // Update the error count for the device
3808 // and fall through to below
3809 //
3810
3811 incrementErrorCount = TRUE;
3812
3813 case SRB_STATUS_BUS_RESET: {
3814 *Status = STATUS_IO_DEVICE_ERROR;
3815 break;
3816 }
3817
3818 case SRB_STATUS_ERROR: {
3819
3820 *Status = STATUS_IO_DEVICE_ERROR;
3821 if (Srb->ScsiStatus == 0) {
3822
3823 //
3824 // This is some strange return code. Update the error
3825 // count for the device.
3826 //
3827
3828 incrementErrorCount = TRUE;
3829
3830 } if (Srb->ScsiStatus == SCSISTAT_BUSY) {
3831
3832 *Status = STATUS_DEVICE_NOT_READY;
3833
3834 } if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) {
3835
3836 *Status = STATUS_DEVICE_BUSY;
3837 retry = FALSE;
3838 logError = FALSE;
3839
3840 }
3841
3842 break;
3843 }
3844
3845 default: {
3846 logError = TRUE;
3847 logStatus = IO_ERR_CONTROLLER_ERROR;
3848 uniqueId = 259;
3849 *Status = STATUS_IO_DEVICE_ERROR;
3850 unhandledError = TRUE;
3851 break;
3852 }
3853
3854 }
3855
3856 //
3857 // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
3858 // we know from a previous poll when the device will be ready (ETA)
3859 // we should delay the retry more appropriately than just guessing.
3860 //
3861 /*
3862 if (fdoExtension->MediaChangeDetectionInfo &&
3863 fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
3864 TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
3865 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
3866 ) {
3867 // check if Gesn.ReadyTime if greater than current tick count
3868 // if so, delay that long (from 1 to 30 seconds max?)
3869 // else, leave the guess of time alone.
3870 }
3871 */
3872
3873 }
3874
3875 if (incrementErrorCount) {
3876
3877 //
3878 // if any error count occurred, delay the retry of this io by
3879 // at least one second, if caller supports it.
3880 //
3881
3882 if (retryInterval == 0) {
3883 retryInterval = 1;
3884 }
3885 ClasspPerfIncrementErrorCount(fdoExtension);
3886 }
3887
3888 //
3889 // If there is a class specific error handler call it.
3890 //
3891
3892 if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
3893
3894 fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
3895 Srb,
3896 Status,
3897 &retry);
3898 }
3899
3900 //
3901 // If the caller wants to know the suggested retry interval tell them.
3902 //
3903
3904 if(ARGUMENT_PRESENT(RetryInterval)) {
3905 *RetryInterval = retryInterval;
3906 }
3907
3908
3909 /*
3910 * LOG the error:
3911 * Always log the error in our internal log.
3912 * If logError is set, also log the error in the system log.
3913 */
3914 {
3915 ULONG totalSize;
3916 ULONG senseBufferSize = 0;
3917 IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
3918 CLASS_ERROR_LOG_DATA staticErrLogData = {0};
3919
3920 //
3921 // Calculate the total size of the error log entry.
3922 // add to totalSize in the order that they are used.
3923 // the advantage to calculating all the sizes here is
3924 // that we don't have to do a bunch of extraneous checks
3925 // later on in this code path.
3926 //
3927 totalSize = sizeof(IO_ERROR_LOG_PACKET) // required
3928 - sizeof(ULONG) // struct includes one ULONG
3929 + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
3930
3931 //
3932 // also save any available extra sense data, up to the maximum errlog
3933 // packet size . WMI should be used for real-time analysis.
3934 // the event log should only be used for post-mortem debugging.
3935 //
3936 if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
3937 ULONG validSenseBytes;
3938 BOOLEAN validSense;
3939
3940 //
3941 // make sure we can at least access the AdditionalSenseLength field
3942 //
3943 validSense = RTL_CONTAINS_FIELD(senseBuffer,
3944 Srb->SenseInfoBufferLength,
3945 AdditionalSenseLength);
3946 if (validSense) {
3947
3948 //
3949 // if extra info exists, copy the maximum amount of available
3950 // sense data that is safe into the the errlog.
3951 //
3952 validSenseBytes = senseBuffer->AdditionalSenseLength
3953 + offsetof(SENSE_DATA, AdditionalSenseLength);
3954
3955 //
3956 // this is invalid because it causes overflow!
3957 // whoever sent this type of request would cause
3958 // a system crash.
3959 //
3960 ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
3961
3962 //
3963 // set to save the most sense buffer possible
3964 //
3965 senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
3966 senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
3967 } else {
3968 //
3969 // it's smaller than required to read the total number of
3970 // valid bytes, so just use the SenseInfoBufferLength field.
3971 //
3972 senseBufferSize = Srb->SenseInfoBufferLength;
3973 }
3974
3975 /*
3976 * Bump totalSize by the number of extra senseBuffer bytes
3977 * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
3978 * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
3979 */
3980 if (senseBufferSize > sizeof(SENSE_DATA)){
3981 totalSize += senseBufferSize-sizeof(SENSE_DATA);
3982 if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
3983 senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
3984 totalSize = ERROR_LOG_MAXIMUM_SIZE;
3985 }
3986 }
3987 }
3988
3989 //
3990 // If we've used up all of our retry attempts, set the final status to
3991 // reflect the appropriate result.
3992 //
3993 if (retry && RetryCount < MAXIMUM_RETRIES) {
3994 staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
3995 staticErrLogData.ErrorRetried = TRUE;
3996 } else {
3997 staticErrLogEntry.FinalStatus = *Status;
3998 }
3999 if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
4000 staticErrLogData.ErrorPaging = TRUE;
4001 }
4002 if (unhandledError) {
4003 staticErrLogData.ErrorUnhandled = TRUE;
4004 }
4005
4006 //
4007 // Calculate the device offset if there is a geometry.
4008 //
4009 staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
4010 staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
4011 if (logStatus == -1){
4012 staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
4013 } else {
4014 staticErrLogEntry.ErrorCode = logStatus;
4015 }
4016
4017 /*
4018 * The dump data follows the IO_ERROR_LOG_PACKET,
4019 * with the first ULONG of dump data inside the packet.
4020 */
4021 staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG);
4022
4023 staticErrLogEntry.SequenceNumber = 0;
4024 staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
4025 staticErrLogEntry.IoControlCode = IoDeviceCode;
4026 staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
4027 staticErrLogEntry.UniqueErrorValue = uniqueId;
4028
4029 KeQueryTickCount(&staticErrLogData.TickCount);
4030 staticErrLogData.PortNumber = (ULONG)-1;
4031
4032 /*
4033 * Save the entire contents of the SRB.
4034 */
4035 staticErrLogData.Srb = *Srb;
4036
4037 /*
4038 * For our private log, save just the default length of the SENSE_DATA.
4039 */
4040 if (senseBufferSize != 0){
4041 RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
4042 }
4043
4044 /*
4045 * Save the error log in our context.
4046 * We only save the default sense buffer length.
4047 */
4048 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
4049 fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
4050 fdoData->ErrorLogNextIndex++;
4051 fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
4052 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
4053
4054 /*
4055 * If logError is set, also save this log in the system's error log.
4056 * But make sure we don't log TUR failures over and over
4057 * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
4058 */
4059 if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_TEST_UNIT_READY) && logError){
4060 if (fdoData->LoggedTURFailureSinceLastIO){
4061 logError = FALSE;
4062 }
4063 else {
4064 fdoData->LoggedTURFailureSinceLastIO = TRUE;
4065 }
4066 }
4067 if (logError){
4068 PIO_ERROR_LOG_PACKET errorLogEntry;
4069 PCLASS_ERROR_LOG_DATA errlogData;
4070
4071 errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
4072 if (errorLogEntry){
4073 errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
4074
4075 *errorLogEntry = staticErrLogEntry;
4076 *errlogData = staticErrLogData;
4077
4078 /*
4079 * For the system log, copy as much of the sense buffer as possible.
4080 */
4081 if (senseBufferSize != 0) {
4082 RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
4083 }
4084
4085 /*
4086 * Write the error log packet to the system error logging thread.
4087 */
4088 IoWriteErrorLogEntry(errorLogEntry);
4089 }
4090 }
4091 }
4092
4093 return retry;
4094
4095 } // end ClassInterpretSenseInfo()
4096
4097
4098 \f
4099 /*++////////////////////////////////////////////////////////////////////////////
4100
4101 ClassModeSense()
4102
4103 Routine Description:
4104
4105 This routine sends a mode sense command to a target ID and returns
4106 when it is complete.
4107
4108 Arguments:
4109
4110 Fdo - Supplies the functional device object associated with this request.
4111
4112 ModeSenseBuffer - Supplies a buffer to store the sense data.
4113
4114 Length - Supplies the length in bytes of the mode sense buffer.
4115
4116 PageMode - Supplies the page or pages of mode sense data to be retrived.
4117
4118 Return Value:
4119
4120 Length of the transferred data is returned.
4121
4122 --*/
4123 ULONG ClassModeSense( IN PDEVICE_OBJECT Fdo,
4124 IN PCHAR ModeSenseBuffer,
4125 IN ULONG Length,
4126 IN UCHAR PageMode)
4127 {
4128 ULONG lengthTransferred = 0;
4129 PMDL senseBufferMdl;
4130
4131 PAGED_CODE();
4132
4133 senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
4134 if (senseBufferMdl){
4135
4136 TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
4137 if (pkt){
4138 KEVENT event;
4139 NTSTATUS pktStatus;
4140 IRP pseudoIrp = {0};
4141
4142 /*
4143 * Store the number of packets servicing the irp (one)
4144 * inside the original IRP. It will be used to counted down
4145 * to zero when the packet completes.
4146 * Initialize the original IRP's status to success.
4147 * If the packet fails, we will set it to the error status.
4148 */
4149 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
4150 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
4151 pseudoIrp.IoStatus.Information = 0;
4152 pseudoIrp.MdlAddress = senseBufferMdl;
4153
4154 /*
4155 * Set this up as a SYNCHRONOUS transfer, submit it,
4156 * and wait for the packet to complete. The result
4157 * status will be written to the original irp.
4158 */
4159 ASSERT(Length <= 0x0ff);
4160 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
4161 SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, &pseudoIrp);
4162 SubmitTransferPacket(pkt);
4163 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
4164
4165 if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
4166 lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
4167 }
4168 else {
4169 /*
4170 * This request can sometimes fail legitimately
4171 * (e.g. when a SCSI device is attached but turned off)
4172 * so this is not necessarily a device/driver bug.
4173 */
4174 DBGTRACE(ClassDebugWarning, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
4175 }
4176 }
4177
4178 FreeDeviceInputMdl(senseBufferMdl);
4179 }
4180
4181 return lengthTransferred;
4182 }
4183
4184
4185 /*++////////////////////////////////////////////////////////////////////////////
4186
4187 ClassFindModePage()
4188
4189 Routine Description:
4190
4191 This routine scans through the mode sense data and finds the requested
4192 mode sense page code.
4193
4194 Arguments:
4195 ModeSenseBuffer - Supplies a pointer to the mode sense data.
4196
4197 Length - Indicates the length of valid data.
4198
4199 PageMode - Supplies the page mode to be searched for.
4200
4201 Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
4202
4203 Return Value:
4204
4205 A pointer to the the requested mode page. If the mode page was not found
4206 then NULL is return.
4207
4208 --*/
4209 PVOID
4210 ClassFindModePage(
4211 IN PCHAR ModeSenseBuffer,
4212 IN ULONG Length,
4213 IN UCHAR PageMode,
4214 IN BOOLEAN Use6Byte
4215 )
4216 {
4217 PUCHAR limit;
4218 ULONG parameterHeaderLength;
4219 PVOID result = NULL;
4220
4221 limit = ModeSenseBuffer + Length;
4222 parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
4223
4224 if (Length >= parameterHeaderLength) {
4225
4226 PMODE_PARAMETER_HEADER10 modeParam10;
4227 ULONG blockDescriptorLength;
4228
4229 /*
4230 * Skip the mode select header and block descriptors.
4231 */
4232 if (Use6Byte){
4233 blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
4234 }
4235 else {
4236 modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
4237 blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
4238 }
4239
4240 ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
4241
4242 //
4243 // ModeSenseBuffer now points at pages. Walk the pages looking for the
4244 // requested page until the limit is reached.
4245 //
4246
4247 while (ModeSenseBuffer +
4248 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit) {
4249
4250 if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
4251
4252 /*
4253 * found the mode page. make sure it's safe to touch it all
4254 * before returning the pointer to caller
4255 */
4256
4257 if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit) {
4258 /*
4259 * Return NULL since the page is not safe to access in full
4260 */
4261 result = NULL;
4262 }
4263 else {
4264 result = ModeSenseBuffer;
4265 }
4266 break;
4267 }
4268
4269 //
4270 // Advance to the next page which is 4-byte-aligned offset after this page.
4271 //
4272 ModeSenseBuffer +=
4273 ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
4274 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
4275
4276 }
4277 }
4278
4279 return result;
4280 } // end ClassFindModePage()
4281 \f
4282 /*++////////////////////////////////////////////////////////////////////////////
4283
4284 ClassSendSrbAsynchronous()
4285
4286 Routine Description:
4287
4288 This routine takes a partially built Srb and an Irp and sends it down to
4289 the port driver.
4290
4291 This routine must be called with the remove lock held for the specified
4292 Irp.
4293
4294 Arguments:
4295
4296 Fdo - Supplies the functional device object for the orginal request.
4297
4298 Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
4299 CDB and the SRB timeout value must be filled in. The SRB must not be
4300 allocated from zone.
4301
4302 Irp - Supplies the requesting Irp.
4303
4304 BufferAddress - Supplies a pointer to the buffer to be transfered.
4305
4306 BufferLength - Supplies the length of data transfer.
4307
4308 WriteToDevice - Indicates the data transfer will be from system memory to
4309 device.
4310
4311 Return Value:
4312
4313 Returns STATUS_PENDING if the request is dispatched (since the
4314 completion routine may change the irp's status value we cannot simply
4315 return the value of the dispatch)
4316
4317 or returns a status value to indicate why it failed.
4318
4319 --*/
4320 NTSTATUS
4321 ClassSendSrbAsynchronous(
4322 PDEVICE_OBJECT Fdo,
4323 PSCSI_REQUEST_BLOCK Srb,
4324 PIRP Irp,
4325 PVOID BufferAddress,
4326 ULONG BufferLength,
4327 BOOLEAN WriteToDevice
4328 )
4329 {
4330
4331 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
4332 PIO_STACK_LOCATION irpStack;
4333
4334 ULONG savedFlags;
4335
4336 //
4337 // Write length to SRB.
4338 //
4339
4340 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
4341
4342 //
4343 // Set SCSI bus address.
4344 //
4345
4346 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
4347
4348 //
4349 // This is a violation of the SCSI spec but it is required for
4350 // some targets.
4351 //
4352
4353 // Srb->Cdb[1] |= deviceExtension->Lun << 5;
4354
4355 //
4356 // Indicate auto request sense by specifying buffer and size.
4357 //
4358
4359 Srb->SenseInfoBuffer = fdoExtension->SenseData;
4360 Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
4361 Srb->DataBuffer = BufferAddress;
4362
4363 //
4364 // Save the class driver specific flags away.
4365 //
4366
4367 savedFlags = Srb->SrbFlags & SRB_FLAGS_CLASS_DRIVER_RESERVED;
4368
4369 //
4370 // Allow the caller to specify that they do not wish
4371 // IoStartNextPacket() to be called in the completion routine.
4372 //
4373
4374 SET_FLAG(savedFlags, (Srb->SrbFlags & SRB_FLAGS_DONT_START_NEXT_PACKET));
4375
4376 if (BufferAddress != NULL) {
4377
4378 //
4379 // Build Mdl if necessary.
4380 //
4381
4382 if (Irp->MdlAddress == NULL) {
4383
4384 if (IoAllocateMdl(BufferAddress,
4385 BufferLength,
4386 FALSE,
4387 FALSE,
4388 Irp) == NULL) {
4389
4390 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
4391
4392 //
4393 // ClassIoComplete() would have free'd the srb
4394 //
4395
4396 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
4397 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
4398 }
4399 ClassFreeOrReuseSrb(fdoExtension, Srb);
4400 ClassReleaseRemoveLock(Fdo, Irp);
4401 ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
4402
4403 return STATUS_INSUFFICIENT_RESOURCES;
4404 }
4405
4406 MmBuildMdlForNonPagedPool(Irp->MdlAddress);
4407
4408 } else {
4409
4410 //
4411 // Make sure the buffer requested matches the MDL.
4412 //
4413
4414 ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
4415 }
4416
4417 //
4418 // Set read flag.
4419 //
4420
4421 Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN;
4422
4423 } else {
4424
4425 //
4426 // Clear flags.
4427 //
4428
4429 Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
4430 }
4431
4432 //
4433 // Restore saved flags.
4434 //
4435
4436 SET_FLAG(Srb->SrbFlags, savedFlags);
4437
4438 //
4439 // Disable synchronous transfer for these requests.
4440 //
4441
4442 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
4443
4444 //
4445 // Set the transfer length.
4446 //
4447
4448 Srb->DataTransferLength = BufferLength;
4449
4450 //
4451 // Zero out status.
4452 //
4453
4454 Srb->ScsiStatus = Srb->SrbStatus = 0;
4455
4456 Srb->NextSrb = 0;
4457
4458 //
4459 // Save a few parameters in the current stack location.
4460 //
4461
4462 irpStack = IoGetCurrentIrpStackLocation(Irp);
4463
4464 //
4465 // Save retry count in current Irp stack.
4466 //
4467
4468 irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
4469
4470 //
4471 // Set up IoCompletion routine address.
4472 //
4473
4474 IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
4475
4476 //
4477 // Get next stack location and
4478 // set major function code.
4479 //
4480
4481 irpStack = IoGetNextIrpStackLocation(Irp);
4482
4483 irpStack->MajorFunction = IRP_MJ_SCSI;
4484
4485 //
4486 // Save SRB address in next stack for port driver.
4487 //
4488
4489 irpStack->Parameters.Scsi.Srb = Srb;
4490
4491 //
4492 // Set up Irp Address.
4493 //
4494
4495 Srb->OriginalRequest = Irp;
4496
4497 //
4498 // Call the port driver to process the request.
4499 //
4500
4501 IoMarkIrpPending(Irp);
4502
4503 IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
4504
4505 return STATUS_PENDING;
4506
4507 } // end ClassSendSrbAsynchronous()
4508 \f
4509 /*++////////////////////////////////////////////////////////////////////////////
4510
4511 ClassDeviceControlDispatch()
4512
4513 Routine Description:
4514
4515 The routine is the common class driver device control dispatch entry point.
4516 This routine is invokes the device-specific drivers DeviceControl routine,
4517 (which may call the Class driver's common DeviceControl routine).
4518
4519 Arguments:
4520
4521 DeviceObject - Supplies a pointer to the device object for this request.
4522
4523 Irp - Supplies the Irp making the request.
4524
4525 Return Value:
4526
4527 Returns the status returned from the device-specific driver.
4528
4529 --*/
4530 NTSTATUS
4531 ClassDeviceControlDispatch(
4532 PDEVICE_OBJECT DeviceObject,
4533 PIRP Irp
4534 )
4535 {
4536
4537 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
4538 ULONG isRemoved;
4539
4540 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
4541
4542 if(isRemoved) {
4543
4544 ClassReleaseRemoveLock(DeviceObject, Irp);
4545
4546 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
4547 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4548 return STATUS_DEVICE_DOES_NOT_EXIST;
4549 }
4550
4551 //
4552 // Call the class specific driver DeviceControl routine.
4553 // If it doesn't handle it, it will call back into ClassDeviceControl.
4554 //
4555
4556 ASSERT(commonExtension->DevInfo->ClassDeviceControl);
4557
4558 return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
4559 } // end ClassDeviceControlDispatch()
4560
4561 \f
4562 /*++////////////////////////////////////////////////////////////////////////////
4563
4564 ClassDeviceControl()
4565
4566 Routine Description:
4567
4568 The routine is the common class driver device control dispatch function.
4569 This routine is called by a class driver when it get an unrecognized
4570 device control request. This routine will perform the correct action for
4571 common requests such as lock media. If the device request is unknown it
4572 passed down to the next level.
4573
4574 This routine must be called with the remove lock held for the specified
4575 irp.
4576
4577 Arguments:
4578
4579 DeviceObject - Supplies a pointer to the device object for this request.
4580
4581 Irp - Supplies the Irp making the request.
4582
4583 Return Value:
4584
4585 Returns back a STATUS_PENDING or a completion status.
4586
4587 --*/
4588 NTSTATUS
4589 ClassDeviceControl(
4590 PDEVICE_OBJECT DeviceObject,
4591 PIRP Irp
4592 )
4593 {
4594 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
4595
4596 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
4597 PIO_STACK_LOCATION nextStack = NULL;
4598
4599 ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
4600
4601 PSCSI_REQUEST_BLOCK srb = NULL;
4602 PCDB cdb = NULL;
4603
4604 NTSTATUS status;
4605 ULONG modifiedIoControlCode;
4606
4607 //
4608 // If this is a pass through I/O control, set the minor function code
4609 // and device address and pass it to the port driver.
4610 //
4611
4612 if ((controlCode == IOCTL_SCSI_PASS_THROUGH) ||
4613 (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT)) {
4614
4615 PSCSI_PASS_THROUGH scsiPass;
4616
4617 //
4618 // Validiate the user buffer.
4619 //
4620 #if defined (_WIN64)
4621
4622 if (IoIs32bitProcess(Irp)) {
4623
4624 if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
4625
4626 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
4627
4628 ClassReleaseRemoveLock(DeviceObject, Irp);
4629 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4630
4631 status = STATUS_INVALID_PARAMETER;
4632 goto SetStatusAndReturn;
4633 }
4634 }
4635 else
4636 #endif
4637 {
4638 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
4639 sizeof(SCSI_PASS_THROUGH)) {
4640
4641 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
4642
4643 ClassReleaseRemoveLock(DeviceObject, Irp);
4644 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4645
4646 status = STATUS_INVALID_PARAMETER;
4647 goto SetStatusAndReturn;
4648 }
4649 }
4650
4651 IoCopyCurrentIrpStackLocationToNext(Irp);
4652
4653 nextStack = IoGetNextIrpStackLocation(Irp);
4654 nextStack->MinorFunction = 1;
4655
4656 ClassReleaseRemoveLock(DeviceObject, Irp);
4657
4658 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4659 goto SetStatusAndReturn;
4660 }
4661
4662 Irp->IoStatus.Information = 0;
4663
4664 switch (controlCode) {
4665
4666 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
4667
4668 PMOUNTDEV_UNIQUE_ID uniqueId;
4669
4670 if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
4671 status = STATUS_INVALID_PARAMETER;
4672 break;
4673 }
4674
4675 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4676 sizeof(MOUNTDEV_UNIQUE_ID)) {
4677
4678 status = STATUS_BUFFER_TOO_SMALL;
4679 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
4680 break;
4681 }
4682
4683 uniqueId = Irp->AssociatedIrp.SystemBuffer;
4684 uniqueId->UniqueIdLength =
4685 commonExtension->MountedDeviceInterfaceName.Length;
4686
4687 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4688 sizeof(USHORT) + uniqueId->UniqueIdLength) {
4689
4690 status = STATUS_BUFFER_OVERFLOW;
4691 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
4692 break;
4693 }
4694
4695 RtlCopyMemory(uniqueId->UniqueId,
4696 commonExtension->MountedDeviceInterfaceName.Buffer,
4697 uniqueId->UniqueIdLength);
4698
4699 status = STATUS_SUCCESS;
4700 Irp->IoStatus.Information = sizeof(USHORT) +
4701 uniqueId->UniqueIdLength;
4702 break;
4703 }
4704
4705 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
4706
4707 PMOUNTDEV_NAME name;
4708
4709 ASSERT(commonExtension->DeviceName.Buffer);
4710
4711 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4712 sizeof(MOUNTDEV_NAME)) {
4713
4714 status = STATUS_BUFFER_TOO_SMALL;
4715 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4716 break;
4717 }
4718
4719 name = Irp->AssociatedIrp.SystemBuffer;
4720 name->NameLength = commonExtension->DeviceName.Length;
4721
4722 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4723 sizeof(USHORT) + name->NameLength) {
4724
4725 status = STATUS_BUFFER_OVERFLOW;
4726 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4727 break;
4728 }
4729
4730 RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
4731 name->NameLength);
4732
4733 status = STATUS_SUCCESS;
4734 Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
4735 break;
4736 }
4737
4738 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
4739
4740 PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
4741 WCHAR driveLetterNameBuffer[10];
4742 RTL_QUERY_REGISTRY_TABLE queryTable[2];
4743 PWSTR valueName;
4744 UNICODE_STRING driveLetterName;
4745
4746 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4747 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
4748
4749 status = STATUS_BUFFER_TOO_SMALL;
4750 Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
4751 break;
4752 }
4753
4754 valueName = ExAllocatePoolWithTag(
4755 PagedPool,
4756 commonExtension->DeviceName.Length + sizeof(WCHAR),
4757 '8CcS');
4758
4759 if (!valueName) {
4760 status = STATUS_INSUFFICIENT_RESOURCES;
4761 break;
4762 }
4763
4764 RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
4765 commonExtension->DeviceName.Length);
4766 valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
4767
4768 driveLetterName.Buffer = driveLetterNameBuffer;
4769 driveLetterName.MaximumLength = 20;
4770 driveLetterName.Length = 0;
4771
4772 RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
4773 queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
4774 RTL_QUERY_REGISTRY_DIRECT;
4775 queryTable[0].Name = valueName;
4776 queryTable[0].EntryContext = &driveLetterName;
4777
4778 status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
4779 L"\\Registry\\Machine\\System\\DISK",
4780 queryTable, NULL, NULL);
4781
4782 if (!NT_SUCCESS(status)) {
4783 ExFreePool(valueName);
4784 break;
4785 }
4786
4787 if (driveLetterName.Length == 4 &&
4788 driveLetterName.Buffer[0] == '%' &&
4789 driveLetterName.Buffer[1] == ':') {
4790
4791 driveLetterName.Buffer[0] = 0xFF;
4792
4793 } else if (driveLetterName.Length != 4 ||
4794 driveLetterName.Buffer[0] < FirstDriveLetter ||
4795 driveLetterName.Buffer[0] > LastDriveLetter ||
4796 driveLetterName.Buffer[1] != ':') {
4797
4798 status = STATUS_NOT_FOUND;
4799 ExFreePool(valueName);
4800 break;
4801 }
4802
4803 suggestedName = Irp->AssociatedIrp.SystemBuffer;
4804 suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
4805 suggestedName->NameLength = 28;
4806
4807 Irp->IoStatus.Information =
4808 FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
4809
4810 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4811 Irp->IoStatus.Information) {
4812
4813 Irp->IoStatus.Information =
4814 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
4815 status = STATUS_BUFFER_OVERFLOW;
4816 ExFreePool(valueName);
4817 break;
4818 }
4819
4820 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
4821 L"\\Registry\\Machine\\System\\DISK",
4822 valueName);
4823
4824 ExFreePool(valueName);
4825
4826 RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
4827 suggestedName->Name[12] = driveLetterName.Buffer[0];
4828 suggestedName->Name[13] = ':';
4829
4830 //
4831 // NT_SUCCESS(status) based on RtlQueryRegistryValues
4832 //
4833 status = STATUS_SUCCESS;
4834
4835 break;
4836 }
4837
4838 default:
4839 status = STATUS_PENDING;
4840 break;
4841 }
4842
4843 if (status != STATUS_PENDING) {
4844 ClassReleaseRemoveLock(DeviceObject, Irp);
4845 Irp->IoStatus.Status = status;
4846 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4847 return status;
4848 }
4849
4850 if (commonExtension->IsFdo){
4851
4852 PULONG_PTR function;
4853
4854 srb = ExAllocatePoolWithTag(NonPagedPool,
4855 sizeof(SCSI_REQUEST_BLOCK) +
4856 (sizeof(ULONG_PTR) * 2),
4857 '9CcS');
4858
4859 if (srb == NULL) {
4860
4861 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
4862 ClassReleaseRemoveLock(DeviceObject, Irp);
4863 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4864 status = STATUS_INSUFFICIENT_RESOURCES;
4865 goto SetStatusAndReturn;
4866 }
4867
4868 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
4869
4870 cdb = (PCDB)srb->Cdb;
4871
4872 //
4873 // Save the function code and the device object in the memory after
4874 // the SRB.
4875 //
4876
4877 function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
4878 *function = (ULONG_PTR) DeviceObject;
4879 function++;
4880 *function = (ULONG_PTR) controlCode;
4881
4882 } else {
4883 srb = NULL;
4884 }
4885
4886 //
4887 // Change the device type to storage for the switch statement, but only
4888 // if from a legacy device type
4889 //
4890
4891 if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE << 16)) ||
4892 ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE << 16)) ||
4893 ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
4894 ) {
4895
4896 modifiedIoControlCode = (controlCode & ~0xffff0000);
4897 modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
4898
4899 } else {
4900
4901 modifiedIoControlCode = controlCode;
4902
4903 }
4904
4905 DBGTRACE(ClassDebugTrace, ("> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
4906
4907 switch (modifiedIoControlCode) {
4908
4909 case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
4910
4911 if (srb) {
4912 ExFreePool(srb);
4913 srb = NULL;
4914 }
4915
4916 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4917 sizeof(STORAGE_HOTPLUG_INFO)) {
4918
4919 //
4920 // Indicate unsuccessful status and no data transferred.
4921 //
4922
4923 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
4924 Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
4925
4926 ClassReleaseRemoveLock(DeviceObject, Irp);
4927 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4928 status = STATUS_BUFFER_TOO_SMALL;
4929
4930 } else if(!commonExtension->IsFdo) {
4931
4932 //
4933 // Just forward this down and return
4934 //
4935
4936 IoCopyCurrentIrpStackLocationToNext(Irp);
4937
4938 ClassReleaseRemoveLock(DeviceObject, Irp);
4939 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4940
4941 } else {
4942
4943 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
4944 PSTORAGE_HOTPLUG_INFO info;
4945
4946 fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
4947 info = Irp->AssociatedIrp.SystemBuffer;
4948
4949 *info = fdoExtension->PrivateFdoData->HotplugInfo;
4950 Irp->IoStatus.Status = STATUS_SUCCESS;
4951 Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
4952 ClassReleaseRemoveLock(DeviceObject, Irp);
4953 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4954 status = STATUS_SUCCESS;
4955
4956 }
4957 break;
4958 }
4959
4960 case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
4961
4962 if (srb)
4963 {
4964 ExFreePool(srb);
4965 srb = NULL;
4966 }
4967
4968 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
4969 sizeof(STORAGE_HOTPLUG_INFO)) {
4970
4971 //
4972 // Indicate unsuccessful status and no data transferred.
4973 //
4974
4975 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
4976
4977 ClassReleaseRemoveLock(DeviceObject, Irp);
4978 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4979 status = STATUS_INFO_LENGTH_MISMATCH;
4980 goto SetStatusAndReturn;
4981
4982 }
4983
4984 if(!commonExtension->IsFdo) {
4985
4986 //
4987 // Just forward this down and return
4988 //
4989
4990 IoCopyCurrentIrpStackLocationToNext(Irp);
4991
4992 ClassReleaseRemoveLock(DeviceObject, Irp);
4993 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4994
4995 } else {
4996
4997 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
4998 PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
4999
5000 status = STATUS_SUCCESS;
5001
5002 if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
5003 {
5004 status = STATUS_INVALID_PARAMETER_1;
5005 }
5006
5007 if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
5008 {
5009 status = STATUS_INVALID_PARAMETER_2;
5010 }
5011
5012 if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
5013 {
5014 status = STATUS_INVALID_PARAMETER_3;
5015 }
5016
5017 if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
5018 {
5019 status = STATUS_INVALID_PARAMETER_5;
5020 }
5021
5022 if (NT_SUCCESS(status))
5023 {
5024 fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
5025
5026 //
5027 // Store the user-defined override in the registry
5028 //
5029
5030 ClassSetDeviceParameter(fdoExtension,
5031 CLASSP_REG_SUBKEY_NAME,
5032 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
5033 (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
5034 }
5035
5036 Irp->IoStatus.Status = status;
5037
5038 ClassReleaseRemoveLock(DeviceObject, Irp);
5039 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5040 }
5041
5042 break;
5043 }
5044
5045 case IOCTL_STORAGE_CHECK_VERIFY:
5046 case IOCTL_STORAGE_CHECK_VERIFY2: {
5047
5048 PIRP irp2 = NULL;
5049 PIO_STACK_LOCATION newStack;
5050
5051 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5052
5053 DebugPrint((1,"DeviceIoControl: Check verify\n"));
5054
5055 //
5056 // If a buffer for a media change count was provided, make sure it's
5057 // big enough to hold the result
5058 //
5059
5060 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
5061
5062 //
5063 // If the buffer is too small to hold the media change count
5064 // then return an error to the caller
5065 //
5066
5067 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
5068 sizeof(ULONG)) {
5069
5070 DebugPrint((3,"DeviceIoControl: media count "
5071 "buffer too small\n"));
5072
5073 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
5074 Irp->IoStatus.Information = sizeof(ULONG);
5075
5076 if(srb != NULL) {
5077 ExFreePool(srb);
5078 }
5079
5080 ClassReleaseRemoveLock(DeviceObject, Irp);
5081 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5082
5083 status = STATUS_BUFFER_TOO_SMALL;
5084 goto SetStatusAndReturn;
5085
5086 }
5087 }
5088
5089 if(!commonExtension->IsFdo) {
5090
5091 //
5092 // If this is a PDO then we should just forward the request down
5093 //
5094 ASSERT(!srb);
5095
5096 IoCopyCurrentIrpStackLocationToNext(Irp);
5097
5098 ClassReleaseRemoveLock(DeviceObject, Irp);
5099
5100 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5101
5102 goto SetStatusAndReturn;
5103
5104 } else {
5105
5106 fdoExtension = DeviceObject->DeviceExtension;
5107
5108 }
5109
5110 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
5111
5112 //
5113 // The caller has provided a valid buffer. Allocate an additional
5114 // irp and stick the CheckVerify completion routine on it. We will
5115 // then send this down to the port driver instead of the irp the
5116 // caller sent in
5117 //
5118
5119 DebugPrint((2,"DeviceIoControl: Check verify wants "
5120 "media count\n"));
5121
5122 //
5123 // Allocate a new irp to send the TestUnitReady to the port driver
5124 //
5125
5126 irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
5127
5128 if(irp2 == NULL) {
5129 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
5130 Irp->IoStatus.Information = 0;
5131 ASSERT(srb);
5132 ExFreePool(srb);
5133 ClassReleaseRemoveLock(DeviceObject, Irp);
5134 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5135 status = STATUS_INSUFFICIENT_RESOURCES;
5136 goto SetStatusAndReturn;
5137
5138 break;
5139 }
5140
5141 //
5142 // Make sure to acquire the lock for the new irp.
5143 //
5144
5145 ClassAcquireRemoveLock(DeviceObject, irp2);
5146
5147 irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
5148 IoSetNextIrpStackLocation(irp2);
5149
5150 //
5151 // Set the top stack location and shove the master Irp into the
5152 // top location
5153 //
5154
5155 newStack = IoGetCurrentIrpStackLocation(irp2);
5156 newStack->Parameters.Others.Argument1 = Irp;
5157 newStack->DeviceObject = DeviceObject;
5158
5159 //
5160 // Stick the check verify completion routine onto the stack
5161 // and prepare the irp for the port driver
5162 //
5163
5164 IoSetCompletionRoutine(irp2,
5165 ClassCheckVerifyComplete,
5166 NULL,
5167 TRUE,
5168 TRUE,
5169 TRUE);
5170
5171 IoSetNextIrpStackLocation(irp2);
5172 newStack = IoGetCurrentIrpStackLocation(irp2);
5173 newStack->DeviceObject = DeviceObject;
5174 newStack->MajorFunction = irpStack->MajorFunction;
5175 newStack->MinorFunction = irpStack->MinorFunction;
5176
5177 //
5178 // Mark the master irp as pending - whether the lower level
5179 // driver completes it immediately or not this should allow it
5180 // to go all the way back up.
5181 //
5182
5183 IoMarkIrpPending(Irp);
5184
5185 Irp = irp2;
5186
5187 }
5188
5189 //
5190 // Test Unit Ready
5191 //
5192
5193 srb->CdbLength = 6;
5194 cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
5195
5196 //
5197 // Set timeout value.
5198 //
5199
5200 srb->TimeOutValue = fdoExtension->TimeOutValue;
5201
5202 //
5203 // If this was a CV2 then mark the request as low-priority so we don't
5204 // spin up the drive just to satisfy it.
5205 //
5206
5207 if(controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
5208 SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
5209 }
5210
5211 //
5212 // Since this routine will always hand the request to the
5213 // port driver if there isn't a data transfer to be done
5214 // we don't have to worry about completing the request here
5215 // on an error
5216 //
5217
5218 //
5219 // This routine uses a completion routine so we don't want to release
5220 // the remove lock until then.
5221 //
5222
5223 status = ClassSendSrbAsynchronous(DeviceObject,
5224 srb,
5225 Irp,
5226 NULL,
5227 0,
5228 FALSE);
5229
5230 break;
5231 }
5232
5233 case IOCTL_STORAGE_MEDIA_REMOVAL:
5234 case IOCTL_STORAGE_EJECTION_CONTROL: {
5235
5236 PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
5237
5238 DebugPrint((3, "DiskIoControl: ejection control\n"));
5239
5240 if(srb) {
5241 ExFreePool(srb);
5242 }
5243
5244 if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
5245 sizeof(PREVENT_MEDIA_REMOVAL)) {
5246
5247 //
5248 // Indicate unsuccessful status and no data transferred.
5249 //
5250
5251 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
5252
5253 ClassReleaseRemoveLock(DeviceObject, Irp);
5254 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5255 status = STATUS_INFO_LENGTH_MISMATCH;
5256 goto SetStatusAndReturn;
5257 }
5258
5259 if(!commonExtension->IsFdo) {
5260
5261 //
5262 // Just forward this down and return
5263 //
5264
5265 IoCopyCurrentIrpStackLocationToNext(Irp);
5266
5267 ClassReleaseRemoveLock(DeviceObject, Irp);
5268 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5269 }
5270 else {
5271
5272 // i don't believe this assertion is valid. this is a request
5273 // from user-mode, so they could request this for any device
5274 // they want? also, we handle it properly.
5275 // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
5276 status = ClasspEjectionControl(
5277 DeviceObject,
5278 Irp,
5279 ((modifiedIoControlCode ==
5280 IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
5281 SimpleMediaLock),
5282 mediaRemoval->PreventMediaRemoval);
5283
5284 Irp->IoStatus.Status = status;
5285 ClassReleaseRemoveLock(DeviceObject, Irp);
5286 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5287 }
5288
5289 break;
5290 }
5291
5292 case IOCTL_STORAGE_MCN_CONTROL: {
5293
5294 DebugPrint((3, "DiskIoControl: MCN control\n"));
5295
5296 if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
5297 sizeof(PREVENT_MEDIA_REMOVAL)) {
5298
5299 //
5300 // Indicate unsuccessful status and no data transferred.
5301 //
5302
5303 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
5304 Irp->IoStatus.Information = 0;
5305
5306 if(srb) {
5307 ExFreePool(srb);
5308 }
5309
5310 ClassReleaseRemoveLock(DeviceObject, Irp);
5311 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5312 status = STATUS_INFO_LENGTH_MISMATCH;
5313 goto SetStatusAndReturn;
5314 }
5315
5316 if(!commonExtension->IsFdo) {
5317
5318 //
5319 // Just forward this down and return
5320 //
5321
5322 if(srb) {
5323 ExFreePool(srb);
5324 }
5325
5326 IoCopyCurrentIrpStackLocationToNext(Irp);
5327
5328 ClassReleaseRemoveLock(DeviceObject, Irp);
5329 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5330
5331 } else {
5332
5333 //
5334 // Call to the FDO - handle the ejection control.
5335 //
5336
5337 status = ClasspMcnControl(DeviceObject->DeviceExtension,
5338 Irp,
5339 srb);
5340 }
5341 goto SetStatusAndReturn;
5342 }
5343
5344 case IOCTL_STORAGE_RESERVE:
5345 case IOCTL_STORAGE_RELEASE: {
5346
5347 //
5348 // Reserve logical unit.
5349 //
5350
5351 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5352
5353 if(!commonExtension->IsFdo) {
5354
5355 IoCopyCurrentIrpStackLocationToNext(Irp);
5356
5357 ClassReleaseRemoveLock(DeviceObject, Irp);
5358 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5359 goto SetStatusAndReturn;
5360 } else {
5361 fdoExtension = DeviceObject->DeviceExtension;
5362 }
5363
5364 srb->CdbLength = 6;
5365
5366 if(modifiedIoControlCode == IOCTL_STORAGE_RESERVE) {
5367 cdb->CDB6GENERIC.OperationCode = SCSIOP_RESERVE_UNIT;
5368 } else {
5369 cdb->CDB6GENERIC.OperationCode = SCSIOP_RELEASE_UNIT;
5370 }
5371
5372 //
5373 // Set timeout value.
5374 //
5375
5376 srb->TimeOutValue = fdoExtension->TimeOutValue;
5377
5378 status = ClassSendSrbAsynchronous(DeviceObject,
5379 srb,
5380 Irp,
5381 NULL,
5382 0,
5383 FALSE);
5384
5385 break;
5386 }
5387
5388 case IOCTL_STORAGE_EJECT_MEDIA:
5389 case IOCTL_STORAGE_LOAD_MEDIA:
5390 case IOCTL_STORAGE_LOAD_MEDIA2:{
5391
5392 //
5393 // Eject media.
5394 //
5395
5396 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5397
5398 if(!commonExtension->IsFdo) {
5399
5400 IoCopyCurrentIrpStackLocationToNext(Irp);
5401
5402 ClassReleaseRemoveLock(DeviceObject, Irp);
5403
5404 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5405 goto SetStatusAndReturn;
5406 } else {
5407 fdoExtension = DeviceObject->DeviceExtension;
5408 }
5409
5410 if(commonExtension->PagingPathCount != 0) {
5411
5412 DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
5413 "failure\n"));
5414
5415 status = STATUS_FILES_OPEN;
5416 Irp->IoStatus.Status = status;
5417
5418 Irp->IoStatus.Information = 0;
5419
5420 if(srb) {
5421 ExFreePool(srb);
5422 }
5423
5424 ClassReleaseRemoveLock(DeviceObject, Irp);
5425 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5426 goto SetStatusAndReturn;
5427 }
5428
5429 //
5430 // Synchronize with ejection control and ejection cleanup code as
5431 // well as other eject/load requests.
5432 //
5433
5434 KeEnterCriticalRegion();
5435 KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
5436 UserRequest,
5437 UserMode,
5438 FALSE,
5439 NULL);
5440
5441 if(fdoExtension->ProtectedLockCount != 0) {
5442
5443 DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
5444 "device - failure\n"));
5445
5446 status = STATUS_DEVICE_BUSY;
5447 Irp->IoStatus.Status = status;
5448 Irp->IoStatus.Information = 0;
5449
5450 if(srb) {
5451 ExFreePool(srb);
5452 }
5453
5454 ClassReleaseRemoveLock(DeviceObject, Irp);
5455 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5456
5457 KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
5458 IO_NO_INCREMENT,
5459 FALSE);
5460 KeLeaveCriticalRegion();
5461
5462 goto SetStatusAndReturn;
5463 }
5464
5465 srb->CdbLength = 6;
5466
5467 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
5468 cdb->START_STOP.LoadEject = 1;
5469
5470 if(modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
5471 cdb->START_STOP.Start = 0;
5472 } else {
5473 cdb->START_STOP.Start = 1;
5474 }
5475
5476 //
5477 // Set timeout value.
5478 //
5479
5480 srb->TimeOutValue = fdoExtension->TimeOutValue;
5481 status = ClassSendSrbAsynchronous(DeviceObject,
5482 srb,
5483 Irp,
5484 NULL,
5485 0,
5486 FALSE);
5487
5488 KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
5489 KeLeaveCriticalRegion();
5490
5491 break;
5492 }
5493
5494 case IOCTL_STORAGE_FIND_NEW_DEVICES: {
5495
5496 if(srb) {
5497 ExFreePool(srb);
5498 }
5499
5500 if(commonExtension->IsFdo) {
5501
5502 IoInvalidateDeviceRelations(
5503 ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
5504 BusRelations);
5505
5506 status = STATUS_SUCCESS;
5507 Irp->IoStatus.Status = status;
5508
5509 ClassReleaseRemoveLock(DeviceObject, Irp);
5510 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5511 }
5512 else {
5513
5514 IoCopyCurrentIrpStackLocationToNext(Irp);
5515
5516 ClassReleaseRemoveLock(DeviceObject, Irp);
5517 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5518 }
5519 break;
5520 }
5521
5522 case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
5523
5524 if(srb) {
5525 ExFreePool(srb);
5526 }
5527
5528 if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
5529 sizeof(STORAGE_DEVICE_NUMBER)) {
5530
5531 PSTORAGE_DEVICE_NUMBER deviceNumber =
5532 Irp->AssociatedIrp.SystemBuffer;
5533 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
5534 commonExtension->PartitionZeroExtension;
5535
5536 deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
5537 deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
5538 deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
5539
5540 status = STATUS_SUCCESS;
5541 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
5542
5543 } else {
5544 status = STATUS_BUFFER_TOO_SMALL;
5545 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
5546 }
5547
5548 Irp->IoStatus.Status = status;
5549 ClassReleaseRemoveLock(DeviceObject, Irp);
5550 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5551
5552 break;
5553 }
5554
5555 default: {
5556
5557 DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
5558 controlCode, DeviceObject));
5559
5560 //
5561 // Pass the device control to the next driver.
5562 //
5563
5564 if(srb) {
5565 ExFreePool(srb);
5566 }
5567
5568 //
5569 // Copy the Irp stack parameters to the next stack location.
5570 //
5571
5572 IoCopyCurrentIrpStackLocationToNext(Irp);
5573
5574 ClassReleaseRemoveLock(DeviceObject, Irp);
5575 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5576 break;
5577 }
5578
5579 } // end switch( ...
5580
5581 SetStatusAndReturn:
5582
5583 DBGTRACE(ClassDebugTrace, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
5584
5585 return status;
5586 } // end ClassDeviceControl()
5587 \f
5588 /*++////////////////////////////////////////////////////////////////////////////
5589
5590 ClassShutdownFlush()
5591
5592 Routine Description:
5593
5594 This routine is called for a shutdown and flush IRPs. These are sent by the
5595 system before it actually shuts down or when the file system does a flush.
5596 If it exists, the device-specific driver's routine will be invoked. If there
5597 wasn't one specified, the Irp will be completed with an Invalid device request.
5598
5599 Arguments:
5600
5601 DriverObject - Pointer to device object to being shutdown by system.
5602
5603 Irp - IRP involved.
5604
5605 Return Value:
5606
5607 NT Status
5608
5609 --*/
5610 NTSTATUS
5611 ClassShutdownFlush(
5612 IN PDEVICE_OBJECT DeviceObject,
5613 IN PIRP Irp
5614 )
5615 {
5616 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
5617
5618 ULONG isRemoved;
5619
5620 NTSTATUS status;
5621
5622 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
5623
5624 if(isRemoved) {
5625
5626 ClassReleaseRemoveLock(DeviceObject, Irp);
5627
5628 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
5629
5630 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5631
5632 return STATUS_DEVICE_DOES_NOT_EXIST;
5633 }
5634
5635 if (commonExtension->DevInfo->ClassShutdownFlush) {
5636
5637 //
5638 // Call the device-specific driver's routine.
5639 //
5640
5641 return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
5642 }
5643
5644 //
5645 // Device-specific driver doesn't support this.
5646 //
5647
5648 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
5649
5650 ClassReleaseRemoveLock(DeviceObject, Irp);
5651 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5652
5653 return STATUS_INVALID_DEVICE_REQUEST;
5654 } // end ClassShutdownFlush()
5655 \f
5656 /*++////////////////////////////////////////////////////////////////////////////
5657
5658 ClassCreateDeviceObject()
5659
5660 Routine Description:
5661
5662 This routine creates an object for the physical device specified and
5663 sets up the deviceExtension's function pointers for each entry point
5664 in the device-specific driver.
5665
5666 Arguments:
5667
5668 DriverObject - Pointer to driver object created by system.
5669
5670 ObjectNameBuffer - Dir. name of the object to create.
5671
5672 LowerDeviceObject - Pointer to the lower device object
5673
5674 IsFdo - should this be an fdo or a pdo
5675
5676 DeviceObject - Pointer to the device object pointer we will return.
5677
5678 Return Value:
5679
5680 NTSTATUS
5681
5682 --*/
5683 NTSTATUS
5684 ClassCreateDeviceObject(
5685 IN PDRIVER_OBJECT DriverObject,
5686 IN PCCHAR ObjectNameBuffer,
5687 IN PDEVICE_OBJECT LowerDevice,
5688 IN BOOLEAN IsFdo,
5689 IN OUT PDEVICE_OBJECT *DeviceObject
5690 )
5691 {
5692 BOOLEAN isPartitionable;
5693 STRING ntNameString;
5694 UNICODE_STRING ntUnicodeString;
5695 NTSTATUS status, status2;
5696 PDEVICE_OBJECT deviceObject = NULL;
5697
5698 ULONG characteristics;
5699
5700 PCLASS_DRIVER_EXTENSION
5701 driverExtension = IoGetDriverObjectExtension(DriverObject,
5702 CLASS_DRIVER_EXTENSION_KEY);
5703
5704 PCLASS_DEV_INFO devInfo;
5705
5706 PAGED_CODE();
5707
5708 *DeviceObject = NULL;
5709 RtlInitUnicodeString(&ntUnicodeString, NULL);
5710
5711 DebugPrint((2, "ClassCreateFdo: Create device object\n"));
5712
5713 ASSERT(LowerDevice);
5714
5715 //
5716 // Make sure that if we're making PDO's we have an enumeration routine
5717 //
5718
5719 isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
5720
5721 ASSERT(IsFdo || isPartitionable);
5722
5723 //
5724 // Grab the correct dev-info structure out of the init data
5725 //
5726
5727 if(IsFdo) {
5728 devInfo = &(driverExtension->InitData.FdoData);
5729 } else {
5730 devInfo = &(driverExtension->InitData.PdoData);
5731 }
5732
5733 characteristics = devInfo->DeviceCharacteristics;
5734
5735 if(ARGUMENT_PRESENT(ObjectNameBuffer)) {
5736 DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
5737
5738 RtlInitString(&ntNameString, ObjectNameBuffer);
5739
5740 status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
5741
5742 if (!NT_SUCCESS(status)) {
5743
5744 DebugPrint((1,
5745 "ClassCreateFdo: Cannot convert string %s\n",
5746 ObjectNameBuffer));
5747
5748 ntUnicodeString.Buffer = NULL;
5749 return status;
5750 }
5751 } else {
5752 DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
5753
5754 if(IsFdo == FALSE) {
5755
5756 //
5757 // PDO's have to have some sort of name.
5758 //
5759
5760 SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
5761 }
5762
5763 RtlInitUnicodeString(&ntUnicodeString, NULL);
5764 }
5765
5766 status = IoCreateDevice(DriverObject,
5767 devInfo->DeviceExtensionSize,
5768 &ntUnicodeString,
5769 devInfo->DeviceType,
5770 devInfo->DeviceCharacteristics,
5771 FALSE,
5772 &deviceObject);
5773
5774 if (!NT_SUCCESS(status)) {
5775
5776 DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
5777 status));
5778 ASSERT(deviceObject == NULL);
5779
5780 //
5781 // buffer is not used any longer here.
5782 //
5783
5784 if (ntUnicodeString.Buffer != NULL) {
5785 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5786 ExFreePool(ntUnicodeString.Buffer);
5787 RtlInitUnicodeString(&ntUnicodeString, NULL);
5788 }
5789
5790 } else {
5791
5792 PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
5793
5794 RtlZeroMemory(
5795 deviceObject->DeviceExtension,
5796 devInfo->DeviceExtensionSize);
5797
5798 //
5799 // Setup version code
5800 //
5801
5802 commonExtension->Version = 0x03;
5803
5804 //
5805 // Setup the remove lock and event
5806 //
5807
5808 commonExtension->IsRemoved = NO_REMOVE;
5809 commonExtension->RemoveLock = 0;
5810 KeInitializeEvent(&commonExtension->RemoveEvent,
5811 SynchronizationEvent,
5812 FALSE);
5813
5814 #if DBG
5815 KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
5816 commonExtension->RemoveTrackingList = NULL;
5817 #else
5818 commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
5819 commonExtension->RemoveTrackingList = (PVOID) -1;
5820 #endif
5821
5822 //
5823 // Acquire the lock once. This reference will be released when the
5824 // remove IRP has been received.
5825 //
5826
5827 ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
5828
5829 //
5830 // Store a pointer to the driver extension so we don't have to do
5831 // lookups to get it.
5832 //
5833
5834 commonExtension->DriverExtension = driverExtension;
5835
5836 //
5837 // Fill in entry points
5838 //
5839
5840 commonExtension->DevInfo = devInfo;
5841
5842 //
5843 // Initialize some of the common values in the structure
5844 //
5845
5846 commonExtension->DeviceObject = deviceObject;
5847
5848 commonExtension->LowerDeviceObject = NULL;
5849
5850 if(IsFdo) {
5851
5852 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
5853
5854 commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
5855
5856 //
5857 // Set the initial device object flags.
5858 //
5859
5860 SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
5861
5862 //
5863 // Clear the PDO list
5864 //
5865
5866 commonExtension->ChildList = NULL;
5867
5868 commonExtension->DriverData =
5869 ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
5870
5871 if(isPartitionable) {
5872
5873 commonExtension->PartitionNumber = 0;
5874 } else {
5875 commonExtension->PartitionNumber = (ULONG) (-1L);
5876 }
5877
5878 fdoExtension->DevicePowerState = PowerDeviceD0;
5879
5880 KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
5881 SynchronizationEvent,
5882 TRUE);
5883
5884 KeInitializeEvent(&fdoExtension->ChildLock,
5885 SynchronizationEvent,
5886 TRUE);
5887
5888 status = ClasspAllocateReleaseRequest(deviceObject);
5889
5890 if(!NT_SUCCESS(status)) {
5891 IoDeleteDevice(deviceObject);
5892 *DeviceObject = NULL;
5893
5894 if (ntUnicodeString.Buffer != NULL) {
5895 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5896 ExFreePool(ntUnicodeString.Buffer);
5897 RtlInitUnicodeString(&ntUnicodeString, NULL);
5898 }
5899
5900 return status;
5901 }
5902
5903 } else {
5904
5905 PPHYSICAL_DEVICE_EXTENSION pdoExtension =
5906 deviceObject->DeviceExtension;
5907
5908 PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
5909 LowerDevice->DeviceExtension;
5910
5911 SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
5912
5913 commonExtension->PartitionZeroExtension = p0Extension;
5914
5915 //
5916 // Stick this onto the PDO list
5917 //
5918
5919 ClassAddChild(p0Extension, pdoExtension, TRUE);
5920
5921 commonExtension->DriverData = (PVOID) (pdoExtension + 1);
5922
5923 //
5924 // Get the top of stack for the lower device - this allows
5925 // filters to get stuck in between the partitions and the
5926 // physical disk.
5927 //
5928
5929 commonExtension->LowerDeviceObject =
5930 IoGetAttachedDeviceReference(LowerDevice);
5931
5932 //
5933 // Pnp will keep a reference to the lower device object long
5934 // after this partition has been deleted. Dereference now so
5935 // we don't have to deal with it later.
5936 //
5937
5938 ObDereferenceObject(commonExtension->LowerDeviceObject);
5939 }
5940
5941 KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
5942
5943 commonExtension->IsFdo = IsFdo;
5944
5945 commonExtension->DeviceName = ntUnicodeString;
5946
5947 commonExtension->PreviousState = 0xff;
5948
5949 InitializeDictionary(&(commonExtension->FileObjectDictionary));
5950
5951 commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
5952 }
5953
5954 *DeviceObject = deviceObject;
5955
5956 return status;
5957 } // end ClassCreateDeviceObject()
5958 \f
5959 /*++////////////////////////////////////////////////////////////////////////////
5960
5961 ClassClaimDevice()
5962
5963 Routine Description:
5964
5965 This function claims a device in the port driver. The port driver object
5966 is updated with the correct driver object if the device is successfully
5967 claimed.
5968
5969 Arguments:
5970
5971 LowerDeviceObject - Supplies the base port device object.
5972
5973 Release - Indicates the logical unit should be released rather than claimed.
5974
5975 Return Value:
5976
5977 Returns a status indicating success or failure of the operation.
5978
5979 --*/
5980 NTSTATUS
5981 ClassClaimDevice(
5982 IN PDEVICE_OBJECT LowerDeviceObject,
5983 IN BOOLEAN Release
5984 )
5985 {
5986 IO_STATUS_BLOCK ioStatus;
5987 PIRP irp;
5988 PIO_STACK_LOCATION irpStack;
5989 KEVENT event;
5990 NTSTATUS status;
5991 SCSI_REQUEST_BLOCK srb;
5992
5993 PAGED_CODE();
5994
5995 //
5996 // Clear the SRB fields.
5997 //
5998
5999 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
6000
6001 //
6002 // Write length to SRB.
6003 //
6004
6005 srb.Length = sizeof(SCSI_REQUEST_BLOCK);
6006
6007 srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
6008 SRB_FUNCTION_CLAIM_DEVICE;
6009
6010 //
6011 // Set the event object to the unsignaled state.
6012 // It will be used to signal request completion
6013 //
6014
6015 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
6016
6017 //
6018 // Build synchronous request with no transfer.
6019 //
6020
6021 irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
6022 LowerDeviceObject,
6023 NULL,
6024 0,
6025 NULL,
6026 0,
6027 TRUE,
6028 &event,
6029 &ioStatus);
6030
6031 if (irp == NULL) {
6032 DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
6033 return STATUS_INSUFFICIENT_RESOURCES;
6034 }
6035
6036 irpStack = IoGetNextIrpStackLocation(irp);
6037
6038 //
6039 // Save SRB address in next stack for port driver.
6040 //
6041
6042 irpStack->Parameters.Scsi.Srb = &srb;
6043
6044 //
6045 // Set up IRP Address.
6046 //
6047
6048 srb.OriginalRequest = irp;
6049
6050 //
6051 // Call the port driver with the request and wait for it to complete.
6052 //
6053
6054 status = IoCallDriver(LowerDeviceObject, irp);
6055 if (status == STATUS_PENDING) {
6056
6057 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
6058 status = ioStatus.Status;
6059 }
6060
6061 //
6062 // If this is a release request, then just decrement the reference count
6063 // and return. The status does not matter.
6064 //
6065
6066 if (Release) {
6067
6068 // ObDereferenceObject(LowerDeviceObject);
6069 return STATUS_SUCCESS;
6070 }
6071
6072 if (!NT_SUCCESS(status)) {
6073 return status;
6074 }
6075
6076 ASSERT(srb.DataBuffer != NULL);
6077 ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
6078
6079 return status;
6080 } // end ClassClaimDevice()
6081 \f
6082 /*++////////////////////////////////////////////////////////////////////////////
6083
6084 ClassInternalIoControl()
6085
6086 Routine Description:
6087
6088 This routine passes internal device controls to the port driver.
6089 Internal device controls are used by higher level drivers both for ioctls
6090 and to pass through scsi requests.
6091
6092 If the IoControlCode does not match any of the handled ioctls and is
6093 a valid system address then the request will be treated as an SRB and
6094 passed down to the lower driver. If the IoControlCode is not a valid
6095 system address the ioctl will be failed.
6096
6097 Callers must therefore be extremely cautious to pass correct, initialized
6098 values to this function.
6099
6100 Arguments:
6101
6102 DeviceObject - Supplies a pointer to the device object for this request.
6103
6104 Irp - Supplies the Irp making the request.
6105
6106 Return Value:
6107
6108 Returns back a STATUS_PENDING or a completion status.
6109
6110 --*/
6111 NTSTATUS
6112 ClassInternalIoControl(
6113 IN PDEVICE_OBJECT DeviceObject,
6114 IN PIRP Irp
6115 )
6116 {
6117 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6118
6119 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
6120 PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
6121
6122 ULONG isRemoved;
6123
6124 PSCSI_REQUEST_BLOCK srb;
6125
6126 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
6127
6128 if(isRemoved) {
6129
6130 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
6131
6132 ClassReleaseRemoveLock(DeviceObject, Irp);
6133
6134 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
6135
6136 return STATUS_DEVICE_DOES_NOT_EXIST;
6137 }
6138
6139 //
6140 // Get a pointer to the SRB.
6141 //
6142
6143 srb = irpStack->Parameters.Scsi.Srb;
6144
6145 //
6146 // Set the parameters in the next stack location.
6147 //
6148
6149 if(commonExtension->IsFdo) {
6150 nextStack->Parameters.Scsi.Srb = srb;
6151 nextStack->MajorFunction = IRP_MJ_SCSI;
6152 nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
6153
6154 } else {
6155
6156 IoCopyCurrentIrpStackLocationToNext(Irp);
6157 }
6158
6159 ClassReleaseRemoveLock(DeviceObject, Irp);
6160
6161 return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
6162 } // end ClassInternalIoControl()
6163 \f
6164 /*++////////////////////////////////////////////////////////////////////////////
6165
6166 ClassQueryTimeOutRegistryValue()
6167
6168 Routine Description:
6169
6170 This routine determines whether a reg key for a user-specified timeout
6171 value exists. This should be called at initialization time.
6172
6173 Arguments:
6174
6175 DeviceObject - Pointer to the device object we are retrieving the timeout
6176 value for
6177
6178 Return Value:
6179
6180 None, but it sets a new default timeout for a class of devices.
6181
6182 --*/
6183 ULONG
6184 ClassQueryTimeOutRegistryValue(
6185 IN PDEVICE_OBJECT DeviceObject
6186 )
6187 {
6188 //
6189 // Find the appropriate reg. key
6190 //
6191
6192 PCLASS_DRIVER_EXTENSION
6193 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
6194 CLASS_DRIVER_EXTENSION_KEY);
6195
6196 PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
6197
6198 PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
6199 PWSTR path;
6200 NTSTATUS status;
6201 LONG timeOut = 0;
6202 ULONG zero = 0;
6203 ULONG size;
6204
6205 PAGED_CODE();
6206
6207 if (!registryPath) {
6208 return 0;
6209 }
6210
6211 parameters = ExAllocatePoolWithTag(NonPagedPool,
6212 sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
6213 '1BcS');
6214
6215 if (!parameters) {
6216 return 0;
6217 }
6218
6219 size = registryPath->MaximumLength + sizeof(WCHAR);
6220 path = ExAllocatePoolWithTag(NonPagedPool, size, '2BcS');
6221
6222 if (!path) {
6223 ExFreePool(parameters);
6224 return 0;
6225 }
6226
6227 RtlZeroMemory(path,size);
6228 RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
6229
6230
6231 //
6232 // Check for the Timeout value.
6233 //
6234
6235 RtlZeroMemory(parameters,
6236 (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
6237
6238 parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
6239 parameters[0].Name = L"TimeOutValue";
6240 parameters[0].EntryContext = &timeOut;
6241 parameters[0].DefaultType = REG_DWORD;
6242 parameters[0].DefaultData = &zero;
6243 parameters[0].DefaultLength = sizeof(ULONG);
6244
6245 status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
6246 path,
6247 parameters,
6248 NULL,
6249 NULL);
6250
6251 if (!(NT_SUCCESS(status))) {
6252 timeOut = 0;
6253 }
6254
6255 ExFreePool(parameters);
6256 ExFreePool(path);
6257
6258 DebugPrint((2,
6259 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
6260 timeOut));
6261
6262
6263 return timeOut;
6264
6265 } // end ClassQueryTimeOutRegistryValue()
6266 \f
6267 /*++////////////////////////////////////////////////////////////////////////////
6268
6269 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
6270
6271 Routine Description:
6272
6273 This routine executes when the port driver has completed a check verify
6274 ioctl. It will set the status of the master Irp, copy the media change
6275 count and complete the request.
6276
6277 Arguments:
6278
6279 Fdo - Supplies the functional device object which represents the logical unit.
6280
6281 Irp - Supplies the Irp which has completed.
6282
6283 Context - NULL
6284
6285 Return Value:
6286
6287 NT status
6288
6289 --*/
6290 NTSTATUS
6291 ClassCheckVerifyComplete(
6292 IN PDEVICE_OBJECT Fdo,
6293 IN PIRP Irp,
6294 IN PVOID Context
6295 )
6296 {
6297 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
6298 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6299
6300 PIRP originalIrp;
6301
6302 ASSERT_FDO(Fdo);
6303
6304 originalIrp = irpStack->Parameters.Others.Argument1;
6305
6306 //
6307 // Copy the media change count and status
6308 //
6309
6310 *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
6311 fdoExtension->MediaChangeCount;
6312
6313 DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
6314 "device %d is %lx - saved as %lx\n",
6315 fdoExtension->DeviceNumber,
6316 fdoExtension->MediaChangeCount,
6317 *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
6318
6319 originalIrp->IoStatus.Status = Irp->IoStatus.Status;
6320 originalIrp->IoStatus.Information = sizeof(ULONG);
6321
6322 ClassReleaseRemoveLock(Fdo, originalIrp);
6323 ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
6324
6325 IoFreeIrp(Irp);
6326
6327 return STATUS_MORE_PROCESSING_REQUIRED;
6328
6329 } // end ClassCheckVerifyComplete()
6330 \f
6331 /*++////////////////////////////////////////////////////////////////////////////
6332
6333 ClassGetDescriptor()
6334
6335 Routine Description:
6336
6337 This routine will perform a query for the specified property id and will
6338 allocate a non-paged buffer to store the data in. It is the responsibility
6339 of the caller to ensure that this buffer is freed.
6340
6341 This routine must be run at IRQL_PASSIVE_LEVEL
6342
6343 Arguments:
6344
6345 DeviceObject - the device to query
6346 DeviceInfo - a location to store a pointer to the buffer we allocate
6347
6348 Return Value:
6349
6350 status
6351 if status is unsuccessful *DeviceInfo will be set to NULL, else the
6352 buffer allocated on behalf of the caller.
6353
6354 --*/
6355 NTSTATUS
6356 ClassGetDescriptor(
6357 IN PDEVICE_OBJECT DeviceObject,
6358 IN PSTORAGE_PROPERTY_ID PropertyId,
6359 OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
6360 )
6361 {
6362 STORAGE_PROPERTY_QUERY query;
6363 IO_STATUS_BLOCK ioStatus;
6364
6365 PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
6366 ULONG length;
6367
6368 UCHAR pass = 0;
6369
6370 PAGED_CODE();
6371
6372 //
6373 // Set the passed-in descriptor pointer to NULL as default
6374 //
6375
6376 *Descriptor = NULL;
6377
6378
6379 RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
6380 query.PropertyId = *PropertyId;
6381 query.QueryType = PropertyStandardQuery;
6382
6383 //
6384 // On the first pass we just want to get the first few
6385 // bytes of the descriptor so we can read it's size
6386 //
6387
6388 descriptor = (PVOID)&query;
6389
6390 ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
6391
6392 ClassSendDeviceIoControlSynchronous(
6393 IOCTL_STORAGE_QUERY_PROPERTY,
6394 DeviceObject,
6395 &query,
6396 sizeof(STORAGE_PROPERTY_QUERY),
6397 sizeof(ULONG) * 2,
6398 FALSE,
6399 &ioStatus
6400 );
6401
6402 if(!NT_SUCCESS(ioStatus.Status)) {
6403
6404 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6405 "query properties #1\n", ioStatus.Status));
6406 return ioStatus.Status;
6407 }
6408
6409 if (descriptor->Size == 0) {
6410
6411 //
6412 // This DebugPrint is to help third-party driver writers
6413 //
6414
6415 DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
6416 "%x\n", ioStatus.Status));
6417 return STATUS_UNSUCCESSFUL;
6418
6419 }
6420
6421 //
6422 // This time we know how much data there is so we can
6423 // allocate a buffer of the correct size
6424 //
6425
6426 length = descriptor->Size;
6427
6428 descriptor = ExAllocatePoolWithTag(NonPagedPool, length, '4BcS');
6429
6430 if(descriptor == NULL) {
6431
6432 DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
6433 "(%d bytes)\n", length));
6434 return STATUS_INSUFFICIENT_RESOURCES;
6435 }
6436
6437 //
6438 // setup the query again, as it was overwritten above
6439 //
6440
6441 RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
6442 query.PropertyId = *PropertyId;
6443 query.QueryType = PropertyStandardQuery;
6444
6445 //
6446 // copy the input to the new outputbuffer
6447 //
6448
6449 RtlCopyMemory(descriptor,
6450 &query,
6451 sizeof(STORAGE_PROPERTY_QUERY)
6452 );
6453
6454 ClassSendDeviceIoControlSynchronous(
6455 IOCTL_STORAGE_QUERY_PROPERTY,
6456 DeviceObject,
6457 descriptor,
6458 sizeof(STORAGE_PROPERTY_QUERY),
6459 length,
6460 FALSE,
6461 &ioStatus
6462 );
6463
6464 if(!NT_SUCCESS(ioStatus.Status)) {
6465
6466 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6467 "query properties #1\n", ioStatus.Status));
6468 ExFreePool(descriptor);
6469 return ioStatus.Status;
6470 }
6471
6472 //
6473 // return the memory we've allocated to the caller
6474 //
6475
6476 *Descriptor = descriptor;
6477 return ioStatus.Status;
6478 } // end ClassGetDescriptor()
6479 \f
6480 /*++////////////////////////////////////////////////////////////////////////////
6481
6482 ClassSignalCompletion()
6483
6484 Routine Description:
6485
6486 This completion routine will signal the event given as context and then
6487 return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
6488 the responsibility of the routine waiting on the event to complete the
6489 request and free the event.
6490
6491 Arguments:
6492
6493 DeviceObject - a pointer to the device object
6494
6495 Irp - a pointer to the irp
6496
6497 Event - a pointer to the event to signal
6498
6499 Return Value:
6500
6501 STATUS_MORE_PROCESSING_REQUIRED
6502
6503 --*/
6504 NTSTATUS
6505 ClassSignalCompletion(
6506 IN PDEVICE_OBJECT DeviceObject,
6507 IN PIRP Irp,
6508 IN PKEVENT Event
6509 )
6510 {
6511 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
6512
6513 return STATUS_MORE_PROCESSING_REQUIRED;
6514 } // end ClassSignalCompletion()
6515 \f
6516 /*++////////////////////////////////////////////////////////////////////////////
6517
6518 ClassPnpQueryFdoRelations()
6519
6520 Routine Description:
6521
6522 This routine will call the driver's enumeration routine to update the
6523 list of PDO's. It will then build a response to the
6524 IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
6525 the irp.
6526
6527 Arguments:
6528
6529 Fdo - a pointer to the functional device object we are enumerating
6530
6531 Irp - a pointer to the enumeration request
6532
6533 Return Value:
6534
6535 status
6536
6537 --*/
6538 NTSTATUS
6539 ClassPnpQueryFdoRelations(
6540 IN PDEVICE_OBJECT Fdo,
6541 IN PIRP Irp
6542 )
6543 {
6544 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6545 PCLASS_DRIVER_EXTENSION
6546 driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
6547 CLASS_DRIVER_EXTENSION_KEY);
6548 NTSTATUS status;
6549
6550 PAGED_CODE();
6551
6552 //
6553 // If there's already an enumeration in progress then don't start another
6554 // one.
6555 //
6556
6557 if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
6558 status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
6559 }
6560
6561 Irp->IoStatus.Information = (ULONG_PTR) NULL;
6562
6563 Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
6564 Fdo,
6565 BusRelations,
6566 (PDEVICE_RELATIONS)&Irp->IoStatus.Information);
6567 InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
6568
6569 return Irp->IoStatus.Status;
6570 } // end ClassPnpQueryFdoRelations()
6571 \f
6572 /*++////////////////////////////////////////////////////////////////////////////
6573
6574 ClassMarkChildrenMissing()
6575
6576 Routine Description:
6577
6578 This routine will call ClassMarkChildMissing() for all children.
6579 It acquires the ChildLock before calling ClassMarkChildMissing().
6580
6581 Arguments:
6582
6583 Fdo - the "bus's" device object, such as the disk FDO for non-removable
6584 disks with multiple partitions.
6585
6586 Return Value:
6587
6588 None
6589
6590 --*/
6591 VOID
6592 ClassMarkChildrenMissing(
6593 IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
6594 )
6595 {
6596 PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
6597 PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
6598
6599 PAGED_CODE();
6600
6601 ClassAcquireChildLock(Fdo);
6602
6603 while (nextChild){
6604 PPHYSICAL_DEVICE_EXTENSION tmpChild;
6605
6606 /*
6607 * ClassMarkChildMissing will also dequeue the child extension.
6608 * So get the next pointer before calling ClassMarkChildMissing.
6609 */
6610 tmpChild = nextChild;
6611 nextChild = tmpChild->CommonExtension.ChildList;
6612 ClassMarkChildMissing(tmpChild, FALSE);
6613 }
6614 ClassReleaseChildLock(Fdo);
6615 return;
6616 } // end ClassMarkChildrenMissing()
6617 \f
6618 /*++////////////////////////////////////////////////////////////////////////////
6619
6620 ClassMarkChildMissing()
6621
6622 Routine Description:
6623
6624 This routine will make an active child "missing." If the device has never
6625 been enumerated then it will be deleted on the spot. If the device has
6626 not been enumerated then it will be marked as missing so that we can
6627 not report it in the next device enumeration.
6628
6629 Arguments:
6630
6631 Child - the child device to be marked as missing.
6632
6633 AcquireChildLock - TRUE if the child lock should be acquired before removing
6634 the missing child. FALSE if the child lock is already
6635 acquired by this thread.
6636
6637 Return Value:
6638
6639 returns whether or not the child device object has previously been reported
6640 to PNP.
6641
6642 --*/
6643 BOOLEAN
6644 ClassMarkChildMissing(
6645 IN PPHYSICAL_DEVICE_EXTENSION Child,
6646 IN BOOLEAN AcquireChildLock
6647 )
6648 {
6649 BOOLEAN returnValue = Child->IsEnumerated;
6650
6651 PAGED_CODE();
6652 ASSERT_PDO(Child->DeviceObject);
6653
6654 Child->IsMissing = TRUE;
6655
6656 //
6657 // Make sure this child is not in the active list.
6658 //
6659
6660 ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
6661 Child,
6662 AcquireChildLock);
6663
6664 if(Child->IsEnumerated == FALSE) {
6665 ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
6666 }
6667
6668 return returnValue;
6669 } // end ClassMarkChildMissing()
6670 \f
6671 /*++////////////////////////////////////////////////////////////////////////////
6672
6673 ClassRetrieveDeviceRelations()
6674
6675 Routine Description:
6676
6677 This routine will allocate a buffer to hold the specified list of
6678 relations. It will then fill in the list with referenced device pointers
6679 and will return the request.
6680
6681 Arguments:
6682
6683 Fdo - pointer to the FDO being queried
6684
6685 RelationType - what type of relations are being queried
6686
6687 DeviceRelations - a location to store a pointer to the response
6688
6689 Return Value:
6690
6691 status
6692
6693 --*/
6694 NTSTATUS
6695 ClassRetrieveDeviceRelations(
6696 IN PDEVICE_OBJECT Fdo,
6697 IN DEVICE_RELATION_TYPE RelationType,
6698 OUT PDEVICE_RELATIONS *DeviceRelations
6699 )
6700 {
6701 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6702
6703 ULONG count = 0;
6704 ULONG i;
6705
6706 PPHYSICAL_DEVICE_EXTENSION nextChild;
6707
6708 ULONG relationsSize;
6709 PDEVICE_RELATIONS deviceRelations = NULL;
6710
6711 NTSTATUS status;
6712
6713 PAGED_CODE();
6714
6715 ClassAcquireChildLock(fdoExtension);
6716
6717 nextChild = fdoExtension->CommonExtension.ChildList;
6718
6719 //
6720 // Count the number of PDO's attached to this disk
6721 //
6722
6723 while(nextChild != NULL) {
6724 PCOMMON_DEVICE_EXTENSION commonExtension;
6725
6726 commonExtension = &(nextChild->CommonExtension);
6727
6728 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6729 (nextChild->IsMissing == FALSE));
6730
6731 nextChild = commonExtension->ChildList;
6732
6733 count++;
6734 };
6735
6736 relationsSize = (sizeof(DEVICE_RELATIONS) +
6737 (count * sizeof(PDEVICE_OBJECT)));
6738
6739 deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
6740
6741 if(deviceRelations == NULL) {
6742
6743 DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
6744 "%d bytes for device relations\n", relationsSize));
6745
6746 ClassReleaseChildLock(fdoExtension);
6747
6748 return STATUS_INSUFFICIENT_RESOURCES;
6749 }
6750
6751 RtlZeroMemory(deviceRelations, relationsSize);
6752
6753 nextChild = fdoExtension->CommonExtension.ChildList;
6754 i = count - 1;
6755
6756 while(nextChild != NULL) {
6757 PCOMMON_DEVICE_EXTENSION commonExtension;
6758
6759 commonExtension = &(nextChild->CommonExtension);
6760
6761 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6762 (nextChild->IsMissing == FALSE));
6763
6764 deviceRelations->Objects[i--] = nextChild->DeviceObject;
6765
6766 status = ObReferenceObjectByPointer(
6767 nextChild->DeviceObject,
6768 0,
6769 NULL,
6770 KernelMode);
6771 ASSERT(NT_SUCCESS(status));
6772
6773 nextChild->IsEnumerated = TRUE;
6774 nextChild = commonExtension->ChildList;
6775 }
6776
6777 ASSERTMSG("Child list has changed: ", i == -1);
6778
6779 deviceRelations->Count = count;
6780 *DeviceRelations = deviceRelations;
6781 ClassReleaseChildLock(fdoExtension);
6782 return STATUS_SUCCESS;
6783 } // end ClassRetrieveDeviceRelations()
6784 \f
6785 /*++////////////////////////////////////////////////////////////////////////////
6786
6787 ClassGetPdoId()
6788
6789 Routine Description:
6790
6791 This routine will call into the driver to retrieve a copy of one of it's
6792 id strings.
6793
6794 Arguments:
6795
6796 Pdo - a pointer to the pdo being queried
6797
6798 IdType - which type of id string is being queried
6799
6800 IdString - an allocated unicode string structure which the driver
6801 can fill in.
6802
6803 Return Value:
6804
6805 status
6806
6807 --*/
6808 NTSTATUS
6809 ClassGetPdoId(
6810 IN PDEVICE_OBJECT Pdo,
6811 IN BUS_QUERY_ID_TYPE IdType,
6812 IN PUNICODE_STRING IdString
6813 )
6814 {
6815 PCLASS_DRIVER_EXTENSION
6816 driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject,
6817 CLASS_DRIVER_EXTENSION_KEY);
6818
6819 ASSERT_PDO(Pdo);
6820 ASSERT(driverExtension->InitData.ClassQueryId);
6821
6822 PAGED_CODE();
6823
6824 return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
6825 } // end ClassGetPdoId()
6826 \f
6827 /*++////////////////////////////////////////////////////////////////////////////
6828
6829 ClassQueryPnpCapabilities()
6830
6831 Routine Description:
6832
6833 This routine will call into the class driver to retrieve it's pnp
6834 capabilities.
6835
6836 Arguments:
6837
6838 PhysicalDeviceObject - The physical device object to retrieve properties
6839 for.
6840
6841 Return Value:
6842
6843 status
6844
6845 --*/
6846 NTSTATUS
6847 ClassQueryPnpCapabilities(
6848 IN PDEVICE_OBJECT DeviceObject,
6849 IN PDEVICE_CAPABILITIES Capabilities
6850 )
6851 {
6852 PCLASS_DRIVER_EXTENSION driverExtension =
6853 ClassGetDriverExtension(DeviceObject->DriverObject);
6854 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6855
6856 PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
6857
6858 PAGED_CODE();
6859
6860 ASSERT(DeviceObject);
6861 ASSERT(Capabilities);
6862
6863 if(commonExtension->IsFdo) {
6864 queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
6865 } else {
6866 queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
6867 }
6868
6869 if(queryRoutine) {
6870 return queryRoutine(DeviceObject,
6871 Capabilities);
6872 } else {
6873 return STATUS_NOT_IMPLEMENTED;
6874 }
6875 } // end ClassQueryPnpCapabilities()
6876 \f
6877 /*++////////////////////////////////////////////////////////////////////////////
6878
6879 ClassInvalidateBusRelations()
6880
6881 Routine Description:
6882
6883 This routine re-enumerates the devices on the "bus". It will call into
6884 the driver's ClassEnumerate routine to update the device objects
6885 immediately. It will then schedule a bus re-enumeration for pnp by calling
6886 IoInvalidateDeviceRelations.
6887
6888 Arguments:
6889
6890 Fdo - a pointer to the functional device object for this bus
6891
6892 Return Value:
6893
6894 none
6895
6896 --*/
6897 VOID
6898 ClassInvalidateBusRelations(
6899 IN PDEVICE_OBJECT Fdo
6900 )
6901 {
6902 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6903 PCLASS_DRIVER_EXTENSION
6904 driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
6905 CLASS_DRIVER_EXTENSION_KEY);
6906
6907 NTSTATUS status = STATUS_SUCCESS;
6908
6909 PAGED_CODE();
6910
6911 ASSERT_FDO(Fdo);
6912 ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
6913
6914 if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
6915 status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
6916 }
6917 InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
6918
6919 if(!NT_SUCCESS(status)) {
6920
6921 DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
6922 "returned %lx\n", status));
6923 }
6924
6925 IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
6926
6927 return;
6928 } // end ClassInvalidateBusRelations()
6929 \f
6930 /*++////////////////////////////////////////////////////////////////////////////
6931
6932 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
6933
6934 Routine Description:
6935
6936 This routine is called to handle the "removal" of a device. It will
6937 forward the request downwards if necesssary, call into the driver
6938 to release any necessary resources (memory, events, etc) and then
6939 will delete the device object.
6940
6941 Arguments:
6942
6943 DeviceObject - a pointer to the device object being removed
6944
6945 RemoveType - indicates what type of remove this is (regular or surprise).
6946
6947 Return Value:
6948
6949 status
6950
6951 --*/
6952 NTSTATUS
6953 ClassRemoveDevice(
6954 IN PDEVICE_OBJECT DeviceObject,
6955 IN UCHAR RemoveType
6956 )
6957 {
6958 PCLASS_DRIVER_EXTENSION
6959 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
6960 CLASS_DRIVER_EXTENSION_KEY);
6961 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6962 PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
6963 PCLASS_WMI_INFO classWmiInfo;
6964 BOOLEAN proceedWithRemove = TRUE;
6965 NTSTATUS status;
6966
6967 PAGED_CODE();
6968
6969 commonExtension->IsRemoved = REMOVE_PENDING;
6970
6971 /*
6972 * Deregister from WMI.
6973 */
6974 classWmiInfo = commonExtension->IsFdo ?
6975 &driverExtension->InitData.FdoData.ClassWmiInfo :
6976 &driverExtension->InitData.PdoData.ClassWmiInfo;
6977 if (classWmiInfo->GuidRegInfo){
6978 status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
6979 DBGTRACE(ClassDebugInfo, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
6980 }
6981
6982 /*
6983 * If we exposed a "shingle" (a named device interface openable by CreateFile)
6984 * then delete it now.
6985 */
6986 if (commonExtension->MountedDeviceInterfaceName.Buffer){
6987 IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
6988 RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
6989 RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
6990 }
6991
6992 //
6993 // If this is a surprise removal we leave the device around - which means
6994 // we don't have to (or want to) drop the remove lock and wait for pending
6995 // requests to complete.
6996 //
6997
6998 if (RemoveType == IRP_MN_REMOVE_DEVICE){
6999
7000 //
7001 // Release the lock we acquired when the device object was created.
7002 //
7003
7004 ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
7005
7006 DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
7007 commonExtension->RemoveLock));
7008
7009 KeWaitForSingleObject(&commonExtension->RemoveEvent,
7010 Executive,
7011 KernelMode,
7012 FALSE,
7013 NULL);
7014
7015 DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject));
7016
7017 if(commonExtension->IsFdo) {
7018
7019 DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
7020 "remove request.\n", DeviceObject));
7021
7022 }
7023 else {
7024 PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
7025
7026 if (pdoExtension->IsMissing){
7027 /*
7028 * The child partition PDO is missing, so we are going to go ahead
7029 * and delete it for the remove.
7030 */
7031 DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
7032 }
7033 else {
7034 /*
7035 * We got a remove for a child partition PDO which is not actually missing.
7036 * So we will NOT actually delete it.
7037 */
7038 DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
7039
7040 //
7041 // Reacquire the remove lock for the next time this comes around.
7042 //
7043
7044 ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
7045
7046 //
7047 // the device wasn't missing so it's not really been removed.
7048 //
7049
7050 commonExtension->IsRemoved = NO_REMOVE;
7051
7052 IoInvalidateDeviceRelations(
7053 commonExtension->PartitionZeroExtension->LowerPdo,
7054 BusRelations);
7055
7056 proceedWithRemove = FALSE;
7057 }
7058 }
7059 }
7060
7061
7062 if (proceedWithRemove){
7063
7064 /*
7065 * Call the class driver's remove handler.
7066 * All this is supposed to do is clean up its data and device interfaces.
7067 */
7068 ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
7069 status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
7070 ASSERT(NT_SUCCESS(status));
7071 status = STATUS_SUCCESS;
7072
7073 if (commonExtension->IsFdo){
7074 PDEVICE_OBJECT pdo;
7075 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
7076
7077 ClasspDisableTimer(fdoExtension->DeviceObject);
7078
7079 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7080
7081 PPHYSICAL_DEVICE_EXTENSION child;
7082
7083 //
7084 // Cleanup the media detection resources now that the class driver
7085 // has stopped it's timer (if any) and we can be sure they won't
7086 // call us to do detection again.
7087 //
7088
7089 ClassCleanupMediaChangeDetection(fdoExtension);
7090
7091 //
7092 // Cleanup any Failure Prediction stuff
7093 //
7094 if (fdoExtension->FailurePredictionInfo) {
7095 ExFreePool(fdoExtension->FailurePredictionInfo);
7096 fdoExtension->FailurePredictionInfo = NULL;
7097 }
7098
7099 /*
7100 * Ordinarily all child PDOs will be removed by the time
7101 * that the parent gets the REMOVE_DEVICE.
7102 * However, if a child PDO has been created but has not
7103 * been announced in a QueryDeviceRelations, then it is
7104 * just a private data structure unknown to pnp, and we have
7105 * to delete it ourselves.
7106 */
7107 ClassAcquireChildLock(fdoExtension);
7108 while (child = ClassRemoveChild(fdoExtension, NULL, FALSE)){
7109
7110 //
7111 // Yank the pdo. This routine will unlink the device from the
7112 // pdo list so NextPdo will point to the next one when it's
7113 // complete.
7114 //
7115 child->IsMissing = TRUE;
7116 ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
7117 }
7118 ClassReleaseChildLock(fdoExtension);
7119 }
7120 else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
7121 /*
7122 * This is a surprise-remove on the parent FDO.
7123 * We will mark the child PDOs as missing so that they
7124 * will actually get deleted when they get a REMOVE_DEVICE.
7125 */
7126 ClassMarkChildrenMissing(fdoExtension);
7127 }
7128
7129 ClasspFreeReleaseRequest(DeviceObject);
7130
7131 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7132
7133 //
7134 // Free FDO-specific data structs
7135 //
7136 if (fdoExtension->PrivateFdoData){
7137
7138 DestroyAllTransferPackets(DeviceObject);
7139
7140 ExFreePool(fdoExtension->PrivateFdoData);
7141 fdoExtension->PrivateFdoData = NULL;
7142 }
7143
7144 if (commonExtension->DeviceName.Buffer) {
7145 ExFreePool(commonExtension->DeviceName.Buffer);
7146 RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
7147 }
7148
7149 if (fdoExtension->AdapterDescriptor) {
7150 ExFreePool(fdoExtension->AdapterDescriptor);
7151 fdoExtension->AdapterDescriptor = NULL;
7152 }
7153
7154 if (fdoExtension->DeviceDescriptor) {
7155 ExFreePool(fdoExtension->DeviceDescriptor);
7156 fdoExtension->DeviceDescriptor = NULL;
7157 }
7158
7159 //
7160 // Detach our device object from the stack - there's no reason
7161 // to hold off our cleanup any longer.
7162 //
7163
7164 IoDetachDevice(lowerDeviceObject);
7165 }
7166 }
7167 else {
7168 /*
7169 * This is a child partition PDO.
7170 * We have already determined that it was previously marked
7171 * as missing. So if this is a REMOVE_DEVICE, we will actually
7172 * delete it.
7173 */
7174 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7175 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
7176 commonExtension->PartitionZeroExtension;
7177 PPHYSICAL_DEVICE_EXTENSION pdoExtension =
7178 (PPHYSICAL_DEVICE_EXTENSION) commonExtension;
7179
7180 //
7181 // See if this device is in the child list (if this was a suprise
7182 // removal it might be) and remove it.
7183 //
7184
7185 ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
7186 }
7187 }
7188
7189 commonExtension->PartitionLength.QuadPart = 0;
7190
7191 if (RemoveType == IRP_MN_REMOVE_DEVICE){
7192 IoDeleteDevice(DeviceObject);
7193 }
7194 }
7195
7196 return STATUS_SUCCESS;
7197 } // end ClassRemoveDevice()
7198 \f
7199 /*++////////////////////////////////////////////////////////////////////////////
7200
7201 ClassGetDriverExtension()
7202
7203 Routine Description:
7204
7205 This routine will return the classpnp's driver extension.
7206
7207 Arguments:
7208
7209 DriverObject - the driver object for which to get classpnp's extension
7210
7211 Return Value:
7212
7213 Either NULL if none, or a pointer to the driver extension
7214
7215 --*/
7216 PCLASS_DRIVER_EXTENSION
7217 ClassGetDriverExtension(
7218 IN PDRIVER_OBJECT DriverObject
7219 )
7220 {
7221 return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
7222 } // end ClassGetDriverExtension()
7223 \f
7224 /*++////////////////////////////////////////////////////////////////////////////
7225
7226 ClasspStartIo()
7227
7228 Routine Description:
7229
7230 This routine wraps the class driver's start io routine. If the device
7231 is being removed it will complete any requests with
7232 STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
7233
7234 Arguments:
7235
7236 Return Value:
7237
7238 none
7239
7240 --*/
7241 VOID
7242 ClasspStartIo(
7243 IN PDEVICE_OBJECT DeviceObject,
7244 IN PIRP Irp
7245 )
7246 {
7247 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7248
7249 //
7250 // We're already holding the remove lock so just check the variable and
7251 // see what's going on.
7252 //
7253
7254 if(commonExtension->IsRemoved) {
7255
7256 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
7257
7258 ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
7259 ClassReleaseRemoveLock(DeviceObject, Irp);
7260 ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
7261 IoStartNextPacket(DeviceObject, FALSE);
7262 ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
7263 return;
7264 }
7265
7266 commonExtension->DriverExtension->InitData.ClassStartIo(
7267 DeviceObject,
7268 Irp);
7269
7270 return;
7271 } // ClasspStartIo()
7272 \f
7273 /*++////////////////////////////////////////////////////////////////////////////
7274
7275 ClassUpdateInformationInRegistry()
7276
7277 Routine Description:
7278
7279 This routine has knowledge about the layout of the device map information
7280 in the registry. It will update this information to include a value
7281 entry specifying the dos device name that is assumed to get assigned
7282 to this NT device name. For more information on this assigning of the
7283 dos device name look in the drive support routine in the hal that assigns
7284 all dos names.
7285
7286 Since some versions of some device's firmware did not work and some
7287 vendors did not bother to follow the specification, the entire inquiry
7288 information must also be stored in the registry so than someone can
7289 figure out the firmware version.
7290
7291 Arguments:
7292
7293 DeviceObject - A pointer to the device object for the tape device.
7294
7295 Return Value:
7296
7297 None
7298
7299 --*/
7300 VOID
7301 ClassUpdateInformationInRegistry(
7302 IN PDEVICE_OBJECT Fdo,
7303 IN PCHAR DeviceName,
7304 IN ULONG DeviceNumber,
7305 IN PINQUIRYDATA InquiryData,
7306 IN ULONG InquiryDataLength
7307 )
7308 {
7309 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
7310 NTSTATUS status;
7311 SCSI_ADDRESS scsiAddress;
7312 OBJECT_ATTRIBUTES objectAttributes;
7313 PUCHAR buffer;
7314 STRING string;
7315 UNICODE_STRING unicodeName;
7316 UNICODE_STRING unicodeRegistryPath;
7317 UNICODE_STRING unicodeData;
7318 HANDLE targetKey;
7319 IO_STATUS_BLOCK ioStatus;
7320
7321
7322 PAGED_CODE();
7323
7324 ASSERT(DeviceName);
7325 fdoExtension = Fdo->DeviceExtension;
7326 buffer = NULL;
7327 targetKey = NULL;
7328 RtlZeroMemory(&unicodeName, sizeof(UNICODE_STRING));
7329 RtlZeroMemory(&unicodeData, sizeof(UNICODE_STRING));
7330 RtlZeroMemory(&unicodeRegistryPath, sizeof(UNICODE_STRING));
7331
7332 TRY {
7333
7334 //
7335 // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
7336 //
7337
7338 ClassSendDeviceIoControlSynchronous(
7339 IOCTL_SCSI_GET_ADDRESS,
7340 Fdo,
7341 &scsiAddress,
7342 0,
7343 sizeof(SCSI_ADDRESS),
7344 FALSE,
7345 &ioStatus
7346 );
7347
7348 if (!NT_SUCCESS(ioStatus.Status)) {
7349
7350 status = ioStatus.Status;
7351 DebugPrint((1,
7352 "UpdateInformationInRegistry: Get Address failed %lx\n",
7353 status));
7354 LEAVE;
7355
7356 } else {
7357
7358 DebugPrint((1,
7359 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
7360 scsiAddress.PortNumber,
7361 scsiAddress.PathId,
7362 scsiAddress.TargetId,
7363 scsiAddress.Lun));
7364
7365 }
7366
7367 //
7368 // Allocate a buffer for the reg. spooge.
7369 //
7370
7371 buffer = ExAllocatePoolWithTag(PagedPool, 1024, '6BcS');
7372
7373 if (buffer == NULL) {
7374
7375 //
7376 // There is not return value for this. Since this is done at
7377 // claim device time (currently only system initialization) getting
7378 // the registry information correct will be the least of the worries.
7379 //
7380
7381 LEAVE;
7382 }
7383
7384 sprintf(buffer,
7385 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
7386 scsiAddress.PortNumber,
7387 scsiAddress.PathId,
7388 scsiAddress.TargetId,
7389 scsiAddress.Lun);
7390
7391 RtlInitString(&string, buffer);
7392
7393 status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
7394 &string,
7395 TRUE);
7396
7397 if (!NT_SUCCESS(status)) {
7398 LEAVE;
7399 }
7400
7401 //
7402 // Open the registry key for the scsi information for this
7403 // scsibus, target, lun.
7404 //
7405
7406 InitializeObjectAttributes(&objectAttributes,
7407 &unicodeRegistryPath,
7408 OBJ_CASE_INSENSITIVE,
7409 NULL,
7410 NULL);
7411
7412 status = ZwOpenKey(&targetKey,
7413 KEY_READ | KEY_WRITE,
7414 &objectAttributes);
7415
7416 if (!NT_SUCCESS(status)) {
7417 LEAVE;
7418 }
7419
7420 //
7421 // Now construct and attempt to create the registry value
7422 // specifying the device name in the appropriate place in the
7423 // device map.
7424 //
7425
7426 RtlInitUnicodeString(&unicodeName, L"DeviceName");
7427
7428 sprintf(buffer, "%s%d", DeviceName, DeviceNumber);
7429 RtlInitString(&string, buffer);
7430 status = RtlAnsiStringToUnicodeString(&unicodeData,
7431 &string,
7432 TRUE);
7433 if (NT_SUCCESS(status)) {
7434 status = ZwSetValueKey(targetKey,
7435 &unicodeName,
7436 0,
7437 REG_SZ,
7438 unicodeData.Buffer,
7439 unicodeData.Length);
7440 }
7441
7442 //
7443 // if they sent in data, update the registry
7444 //
7445
7446 if (InquiryDataLength) {
7447
7448 ASSERT(InquiryData);
7449
7450 RtlInitUnicodeString(&unicodeName, L"InquiryData");
7451 status = ZwSetValueKey(targetKey,
7452 &unicodeName,
7453 0,
7454 REG_BINARY,
7455 InquiryData,
7456 InquiryDataLength);
7457 }
7458
7459 // that's all, except to clean up.
7460
7461 } FINALLY {
7462
7463 if (unicodeData.Buffer) {
7464 RtlFreeUnicodeString(&unicodeData);
7465 }
7466 if (unicodeRegistryPath.Buffer) {
7467 RtlFreeUnicodeString(&unicodeRegistryPath);
7468 }
7469 if (targetKey) {
7470 ZwClose(targetKey);
7471 }
7472 if (buffer) {
7473 ExFreePool(buffer);
7474 }
7475
7476 }
7477
7478 } // end ClassUpdateInformationInRegistry()
7479 \f
7480 /*++////////////////////////////////////////////////////////////////////////////
7481
7482 ClasspSendSynchronousCompletion()
7483
7484 Routine Description:
7485
7486 This completion routine will set the user event in the irp after
7487 freeing the irp and the associated MDL (if any).
7488
7489 Arguments:
7490
7491 DeviceObject - the device object which requested the completion routine
7492
7493 Irp - the irp being completed
7494
7495 Context - unused
7496
7497 Return Value:
7498
7499 STATUS_MORE_PROCESSING_REQUIRED
7500
7501 --*/
7502 NTSTATUS
7503 ClasspSendSynchronousCompletion(
7504 IN PDEVICE_OBJECT DeviceObject,
7505 IN PIRP Irp,
7506 IN PVOID Context
7507 )
7508 {
7509 DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
7510 DeviceObject, Irp, Context));
7511 //
7512 // First set the status and information fields in the io status block
7513 // provided by the caller.
7514 //
7515
7516 *(Irp->UserIosb) = Irp->IoStatus;
7517
7518 //
7519 // Unlock the pages for the data buffer.
7520 //
7521
7522 if(Irp->MdlAddress) {
7523 MmUnlockPages(Irp->MdlAddress);
7524 IoFreeMdl(Irp->MdlAddress);
7525 }
7526
7527 //
7528 // Signal the caller's event.
7529 //
7530
7531 KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
7532
7533 //
7534 // Free the MDL and the IRP.
7535 //
7536
7537 IoFreeIrp(Irp);
7538
7539 return STATUS_MORE_PROCESSING_REQUIRED;
7540 } // end ClasspSendSynchronousCompletion()
7541 \f
7542 /*++
7543
7544 ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
7545
7546 --*/
7547 VOID
7548 ClasspRegisterMountedDeviceInterface(
7549 IN PDEVICE_OBJECT DeviceObject
7550 )
7551 {
7552
7553 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7554 BOOLEAN isFdo = commonExtension->IsFdo;
7555
7556 PDEVICE_OBJECT pdo;
7557 UNICODE_STRING interfaceName;
7558
7559 NTSTATUS status;
7560
7561 if(isFdo) {
7562
7563 PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
7564
7565 functionalExtension =
7566 (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
7567 pdo = functionalExtension->LowerPdo;
7568 } else {
7569 pdo = DeviceObject;
7570 }
7571
7572 status = IoRegisterDeviceInterface(
7573 pdo,
7574 &MOUNTDEV_MOUNTED_DEVICE_GUID,
7575 NULL,
7576 &interfaceName
7577 );
7578
7579 if(NT_SUCCESS(status)) {
7580
7581 //
7582 // Copy the interface name before setting the interface state - the
7583 // name is needed by the components we notify.
7584 //
7585
7586 commonExtension->MountedDeviceInterfaceName = interfaceName;
7587 status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
7588
7589 if(!NT_SUCCESS(status)) {
7590 RtlFreeUnicodeString(&interfaceName);
7591 }
7592 }
7593
7594 if(!NT_SUCCESS(status)) {
7595 RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
7596 NULL);
7597 }
7598 return;
7599 } // end ClasspRegisterMountedDeviceInterface()
7600 \f
7601 /*++////////////////////////////////////////////////////////////////////////////
7602
7603 ClassSendDeviceIoControlSynchronous()
7604
7605 Routine Description:
7606
7607 This routine is based upon IoBuildDeviceIoControlRequest(). It has been
7608 modified to reduce code and memory by not double-buffering the io, using
7609 the same buffer for both input and output, allocating and deallocating
7610 the mdl on behalf of the caller, and waiting for the io to complete.
7611
7612 This routine also works around the rare cases in which APC's are disabled.
7613 Since IoBuildDeviceIoControl() used APC's to signal completion, this had
7614 led to a number of difficult-to-detect hangs, where the irp was completed,
7615 but the event passed to IoBuild..() was still being waited upon by the
7616 caller.
7617
7618 Arguments:
7619
7620 IoControlCode - the IOCTL to send
7621
7622 TargetDeviceObject - the device object that should handle the ioctl
7623
7624 Buffer - the input and output buffer, or NULL if no input/output
7625
7626 InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
7627
7628 OutputBufferLength - the number of bytes to be filled in upon success
7629
7630 InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
7631
7632 IoStatus - the status block that contains the results of the operation
7633
7634 Return Value:
7635
7636 --*/
7637 VOID
7638 ClassSendDeviceIoControlSynchronous(
7639 IN ULONG IoControlCode,
7640 IN PDEVICE_OBJECT TargetDeviceObject,
7641 IN OUT PVOID Buffer OPTIONAL,
7642 IN ULONG InputBufferLength,
7643 IN ULONG OutputBufferLength,
7644 IN BOOLEAN InternalDeviceIoControl,
7645 OUT PIO_STATUS_BLOCK IoStatus
7646 )
7647 {
7648 PIRP irp;
7649 PIO_STACK_LOCATION irpSp;
7650 ULONG method;
7651
7652 PAGED_CODE();
7653
7654 irp = NULL;
7655 method = IoControlCode & 3;
7656
7657
7658 #if DBG // Begin Argument Checking (nop in fre version)
7659
7660 ASSERT(ARGUMENT_PRESENT(IoStatus));
7661
7662 if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
7663 ASSERT(ARGUMENT_PRESENT(Buffer));
7664 }
7665 else {
7666 ASSERT(!ARGUMENT_PRESENT(Buffer));
7667 }
7668 #endif
7669
7670 //
7671 // Begin by allocating the IRP for this request. Do not charge quota to
7672 // the current process for this IRP.
7673 //
7674
7675 irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
7676 if (!irp) {
7677 (*IoStatus).Information = 0;
7678 (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7679 return;
7680 }
7681
7682 //
7683 // Get a pointer to the stack location of the first driver which will be
7684 // invoked. This is where the function codes and the parameters are set.
7685 //
7686
7687 irpSp = IoGetNextIrpStackLocation(irp);
7688
7689 //
7690 // Set the major function code based on the type of device I/O control
7691 // function the caller has specified.
7692 //
7693
7694 if (InternalDeviceIoControl) {
7695 irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
7696 } else {
7697 irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
7698 }
7699
7700 //
7701 // Copy the caller's parameters to the service-specific portion of the
7702 // IRP for those parameters that are the same for all four methods.
7703 //
7704
7705 irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
7706 irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
7707 irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
7708
7709 //
7710 // Get the method bits from the I/O control code to determine how the
7711 // buffers are to be passed to the driver.
7712 //
7713
7714 switch (method) {
7715 // case 0
7716 case METHOD_BUFFERED: {
7717 if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
7718
7719 irp->AssociatedIrp.SystemBuffer =
7720 ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
7721 max(InputBufferLength, OutputBufferLength),
7722 CLASS_TAG_DEVICE_CONTROL
7723 );
7724
7725 if (irp->AssociatedIrp.SystemBuffer == NULL) {
7726 IoFreeIrp(irp);
7727 (*IoStatus).Information = 0;
7728 (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7729 return;
7730 }
7731
7732 if (InputBufferLength != 0) {
7733 RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,
7734 Buffer,
7735 InputBufferLength);
7736 }
7737 } // end of buffering
7738
7739 irp->UserBuffer = Buffer;
7740 break;
7741 }
7742
7743 // case 1, case 2
7744 case METHOD_IN_DIRECT:
7745 case METHOD_OUT_DIRECT: {
7746
7747
7748 if (InputBufferLength != 0) {
7749 irp->AssociatedIrp.SystemBuffer = Buffer;
7750 }
7751
7752 if (OutputBufferLength != 0) {
7753
7754 irp->MdlAddress = IoAllocateMdl(Buffer,
7755 OutputBufferLength,
7756 FALSE, FALSE,
7757 (PIRP) NULL);
7758
7759 if (irp->MdlAddress == NULL) {
7760 IoFreeIrp(irp);
7761 (*IoStatus).Information = 0;
7762 (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7763 return;
7764 }
7765
7766 if (method == METHOD_IN_DIRECT) {
7767 MmProbeAndLockPages(irp->MdlAddress,
7768 KernelMode,
7769 IoReadAccess);
7770 } else if (method == METHOD_OUT_DIRECT) {
7771 MmProbeAndLockPages(irp->MdlAddress,
7772 KernelMode,
7773 IoWriteAccess);
7774 } else {
7775 ASSERT(!"If other methods reach here, code is out of date");
7776 }
7777 }
7778 break;
7779 }
7780
7781 // case 3
7782 case METHOD_NEITHER: {
7783
7784 ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
7785 IoStatus->Information = 0;
7786 IoStatus->Status = STATUS_NOT_SUPPORTED;
7787 return;
7788 break;
7789 }
7790 } // end of switch(method)
7791
7792 irp->Tail.Overlay.Thread = PsGetCurrentThread();
7793
7794 //
7795 // send the irp synchronously
7796 //
7797
7798 ClassSendIrpSynchronous(TargetDeviceObject, irp);
7799
7800 //
7801 // copy the iostatus block for the caller
7802 //
7803
7804 *IoStatus = irp->IoStatus;
7805
7806 //
7807 // free any allocated resources
7808 //
7809
7810 switch (method) {
7811 case METHOD_BUFFERED: {
7812
7813 ASSERT(irp->UserBuffer == Buffer);
7814
7815 //
7816 // first copy the buffered result, if any
7817 // Note that there are no security implications in
7818 // not checking for success since only drivers can
7819 // call into this routine anyways...
7820 //
7821
7822 if (OutputBufferLength != 0) {
7823 RtlCopyMemory(Buffer, // irp->UserBuffer
7824 irp->AssociatedIrp.SystemBuffer,
7825 OutputBufferLength
7826 );
7827 }
7828
7829 //
7830 // then free the memory allocated to buffer the io
7831 //
7832
7833 if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
7834 ExFreePool(irp->AssociatedIrp.SystemBuffer);
7835 irp->AssociatedIrp.SystemBuffer = NULL;
7836 }
7837 break;
7838 }
7839
7840 case METHOD_IN_DIRECT:
7841 case METHOD_OUT_DIRECT: {
7842
7843 //
7844 // we alloc a mdl if there is an output buffer specified
7845 // free it here after unlocking the pages
7846 //
7847
7848 if (OutputBufferLength != 0) {
7849 ASSERT(irp->MdlAddress != NULL);
7850 MmUnlockPages(irp->MdlAddress);
7851 IoFreeMdl(irp->MdlAddress);
7852 irp->MdlAddress = (PMDL) NULL;
7853 }
7854 break;
7855 }
7856
7857 case METHOD_NEITHER: {
7858 ASSERT(!"Code is out of date");
7859 break;
7860 }
7861 }
7862
7863 //
7864 // we always have allocated an irp. free it here.
7865 //
7866
7867 IoFreeIrp(irp);
7868 irp = (PIRP) NULL;
7869
7870 //
7871 // return the io status block's status to the caller
7872 //
7873
7874 return;
7875 } // end ClassSendDeviceIoControlSynchronous()
7876 \f
7877 /*++////////////////////////////////////////////////////////////////////////////
7878
7879 ClassForwardIrpSynchronous()
7880
7881 Routine Description:
7882
7883 Forwards a given irp to the next lower device object.
7884
7885 Arguments:
7886
7887 CommonExtension - the common class extension
7888
7889 Irp - the request to forward down the stack
7890
7891 Return Value:
7892
7893 --*/
7894 NTSTATUS
7895 ClassForwardIrpSynchronous(
7896 IN PCOMMON_DEVICE_EXTENSION CommonExtension,
7897 IN PIRP Irp
7898 )
7899 {
7900 IoCopyCurrentIrpStackLocationToNext(Irp);
7901 return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
7902 } // end ClassForwardIrpSynchronous()
7903 \f
7904 /*++////////////////////////////////////////////////////////////////////////////
7905
7906 ClassSendIrpSynchronous()
7907
7908 Routine Description:
7909
7910 This routine sends the given irp to the given device object, and waits for
7911 it to complete. On debug versions, will print out a debug message and
7912 optionally assert for "lost" irps based upon classpnp's globals
7913
7914 Arguments:
7915
7916 TargetDeviceObject - the device object to handle this irp
7917
7918 Irp - the request to be sent
7919
7920 Return Value:
7921
7922 --*/
7923 NTSTATUS
7924 ClassSendIrpSynchronous(
7925 IN PDEVICE_OBJECT TargetDeviceObject,
7926 IN PIRP Irp
7927 )
7928 {
7929 KEVENT event;
7930 NTSTATUS status;
7931
7932 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
7933 ASSERT(TargetDeviceObject != NULL);
7934 ASSERT(Irp != NULL);
7935 ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
7936
7937 //
7938 // ISSUE-2000/02/20-henrygab What if APCs are disabled?
7939 // May need to enter critical section before IoCallDriver()
7940 // until the event is hit?
7941 //
7942
7943 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
7944 IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
7945 TRUE, TRUE, TRUE);
7946
7947 status = IoCallDriver(TargetDeviceObject, Irp);
7948
7949 if (status == STATUS_PENDING) {
7950
7951 #if DBG
7952 LARGE_INTEGER timeout;
7953
7954 timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
7955 ClasspnpGlobals.SecondsToWaitForIrps);
7956
7957 do {
7958 status = KeWaitForSingleObject(&event,
7959 Executive,
7960 KernelMode,
7961 FALSE,
7962 &timeout);
7963
7964
7965 if (status == STATUS_TIMEOUT) {
7966
7967 //
7968 // This DebugPrint should almost always be investigated by the
7969 // party who sent the irp and/or the current owner of the irp.
7970 // Synchronous Irps should not take this long (currently 30
7971 // seconds) without good reason. This points to a potentially
7972 // serious problem in the underlying device stack.
7973 //
7974
7975 DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
7976 "complete within %x seconds\n",
7977 TargetDeviceObject, Irp,
7978 ClasspnpGlobals.SecondsToWaitForIrps
7979 ));
7980
7981 if (ClasspnpGlobals.BreakOnLostIrps != 0) {
7982 ASSERT(!" - Irp failed to complete within 30 seconds - ");
7983 }
7984 }
7985
7986
7987 } while (status==STATUS_TIMEOUT);
7988 #else
7989 KeWaitForSingleObject(&event,
7990 Executive,
7991 KernelMode,
7992 FALSE,
7993 NULL);
7994 #endif
7995
7996 status = Irp->IoStatus.Status;
7997 }
7998
7999 return status;
8000 } // end ClassSendIrpSynchronous()
8001 \f
8002 /*++////////////////////////////////////////////////////////////////////////////
8003
8004 ClassGetVpb()
8005
8006 Routine Description:
8007
8008 This routine returns the current VPB (Volume Parameter Block) for the
8009 given device object.
8010 The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
8011 of DEVICE_OBJECT; hence this exported function.
8012
8013 Arguments:
8014
8015 DeviceObject - the device to get the VPB for
8016
8017 Return Value:
8018
8019 the VPB, or NULL if none.
8020
8021 --*/
8022 PVPB
8023 ClassGetVpb(
8024 IN PDEVICE_OBJECT DeviceObject
8025 )
8026 {
8027 return DeviceObject->Vpb;
8028 } // end ClassGetVpb()
8029 \f
8030 /*++
8031
8032 ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
8033
8034 --*/
8035 NTSTATUS
8036 ClasspAllocateReleaseRequest(
8037 IN PDEVICE_OBJECT Fdo
8038 )
8039 {
8040 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8041 PIO_STACK_LOCATION irpStack;
8042
8043 KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
8044
8045 fdoExtension->ReleaseQueueNeeded = FALSE;
8046 fdoExtension->ReleaseQueueInProgress = FALSE;
8047 fdoExtension->ReleaseQueueIrpFromPool = FALSE;
8048
8049 //
8050 // The class driver is responsible for allocating a properly sized irp,
8051 // or ClassReleaseQueue will attempt to do it on the first error.
8052 //
8053
8054 fdoExtension->ReleaseQueueIrp = NULL;
8055
8056 //
8057 // Write length to SRB.
8058 //
8059
8060 fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
8061
8062 return STATUS_SUCCESS;
8063 } // end ClasspAllocateReleaseRequest()
8064 \f
8065 /*++
8066
8067 ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
8068
8069 --*/
8070 VOID
8071 ClasspFreeReleaseRequest(
8072 IN PDEVICE_OBJECT Fdo
8073 )
8074 {
8075 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8076 //KIRQL oldIrql;
8077
8078 ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
8079
8080 //
8081 // free anything the driver allocated
8082 //
8083
8084 if (fdoExtension->ReleaseQueueIrp) {
8085 if (fdoExtension->ReleaseQueueIrpFromPool) {
8086 ExFreePool(fdoExtension->ReleaseQueueIrp);
8087 } else {
8088 IoFreeIrp(fdoExtension->ReleaseQueueIrp);
8089 }
8090 fdoExtension->ReleaseQueueIrp = NULL;
8091 }
8092
8093 //
8094 // free anything that we allocated
8095 //
8096
8097 if ((fdoExtension->PrivateFdoData) &&
8098 (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
8099
8100 ExFreePool(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
8101 fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
8102 fdoExtension->PrivateFdoData->ReleaseQueueIrp = NULL;
8103 }
8104
8105 return;
8106 } // end ClasspFreeReleaseRequest()
8107 \f
8108 /*++////////////////////////////////////////////////////////////////////////////
8109
8110 ClassReleaseQueue()
8111
8112 Routine Description:
8113
8114 This routine issues an internal device control command
8115 to the port driver to release a frozen queue. The call
8116 is issued asynchronously as ClassReleaseQueue will be invoked
8117 from the IO completion DPC (and will have no context to
8118 wait for a synchronous call to complete).
8119
8120 This routine must be called with the remove lock held.
8121
8122 Arguments:
8123
8124 Fdo - The functional device object for the device with the frozen queue.
8125
8126 Return Value:
8127
8128 None.
8129
8130 --*/
8131 VOID
8132 ClassReleaseQueue(
8133 IN PDEVICE_OBJECT Fdo
8134 )
8135 {
8136 ClasspReleaseQueue(Fdo, NULL);
8137 return;
8138 } // end ClassReleaseQueue()
8139 \f
8140 /*++////////////////////////////////////////////////////////////////////////////
8141
8142 ClasspAllocateReleaseQueueIrp()
8143
8144 Routine Description:
8145
8146 This routine allocates the release queue irp held in classpnp's private
8147 extension. This was added to allow no-memory conditions to be more
8148 survivable.
8149
8150 Return Value:
8151
8152 NT_SUCCESS value.
8153
8154 Notes:
8155
8156 Does not grab the spinlock. Should only be called from StartDevice()
8157 routine. May be called elsewhere for poorly-behaved drivers that cause
8158 the queue to lockup before the device is started. This should *never*
8159 occur, since it's illegal to send a request to a non-started PDO. This
8160 condition is checked for in ClasspReleaseQueue().
8161
8162 --*/
8163 NTSTATUS
8164 ClasspAllocateReleaseQueueIrp(
8165 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8166 )
8167 {
8168 KIRQL oldIrql;
8169 UCHAR lowerStackSize;
8170
8171 //
8172 // do an initial check w/o the spinlock
8173 //
8174
8175 if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8176 return STATUS_SUCCESS;
8177 }
8178
8179
8180 lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
8181
8182 //
8183 // don't allocate one if one is in progress! this means whoever called
8184 // this routine didn't check if one was in progress.
8185 //
8186
8187 ASSERT(!(FdoExtension->ReleaseQueueInProgress));
8188
8189 FdoExtension->PrivateFdoData->ReleaseQueueIrp =
8190 ExAllocatePoolWithTag(NonPagedPool,
8191 IoSizeOfIrp(lowerStackSize),
8192 CLASS_TAG_RELEASE_QUEUE
8193 );
8194
8195 if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
8196 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
8197 "release queue irp\n"));
8198 return STATUS_INSUFFICIENT_RESOURCES;
8199 }
8200 IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
8201 IoSizeOfIrp(lowerStackSize),
8202 lowerStackSize);
8203 FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
8204
8205 return STATUS_SUCCESS;
8206 }
8207
8208
8209 /*++////////////////////////////////////////////////////////////////////////////
8210
8211 ClasspReleaseQueue()
8212
8213 Routine Description:
8214
8215 This routine issues an internal device control command
8216 to the port driver to release a frozen queue. The call
8217 is issued asynchronously as ClassReleaseQueue will be invoked
8218 from the IO completion DPC (and will have no context to
8219 wait for a synchronous call to complete).
8220
8221 This routine must be called with the remove lock held.
8222
8223 Arguments:
8224
8225 Fdo - The functional device object for the device with the frozen queue.
8226
8227 ReleaseQueueIrp - If this irp is supplied then the test to determine whether
8228 a release queue request is in progress will be ignored.
8229 The irp provided must be the IRP originally allocated
8230 for release queue requests (so this parameter can only
8231 really be provided by the release queue completion
8232 routine.)
8233
8234 Return Value:
8235
8236 None.
8237
8238 --*/
8239 VOID
8240 ClasspReleaseQueue(
8241 IN PDEVICE_OBJECT Fdo,
8242 IN PIRP ReleaseQueueIrp OPTIONAL
8243 )
8244 {
8245 PIO_STACK_LOCATION irpStack;
8246 PIRP irp;
8247 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8248 PDEVICE_OBJECT lowerDevice;
8249 PSCSI_REQUEST_BLOCK srb;
8250 KIRQL currentIrql;
8251
8252 lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
8253
8254 //
8255 // we raise irql seperately so we're not swapped out or suspended
8256 // while holding the release queue irp in this routine. this lets
8257 // us release the spin lock before lowering irql.
8258 //
8259
8260 KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
8261
8262 KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8263
8264 //
8265 // make sure that if they passed us an irp, it matches our allocated irp.
8266 //
8267
8268 ASSERT((ReleaseQueueIrp == NULL) ||
8269 (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
8270
8271 //
8272 // ASSERT that we've already allocated this. (should not occur)
8273 // try to allocate it anyways, then finally bugcheck if
8274 // there's still no memory...
8275 //
8276
8277 ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
8278 if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8279 ClasspAllocateReleaseQueueIrp(fdoExtension);
8280 }
8281 if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8282 KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
8283 }
8284
8285 if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
8286
8287 //
8288 // Someone is already using the irp - just set the flag to indicate that
8289 // we need to release the queue again.
8290 //
8291
8292 fdoExtension->ReleaseQueueNeeded = TRUE;
8293 KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8294 KeLowerIrql(currentIrql);
8295 return;
8296
8297 }
8298
8299 //
8300 // Mark that there is a release queue in progress and drop the spinlock.
8301 //
8302
8303 fdoExtension->ReleaseQueueInProgress = TRUE;
8304 if (ReleaseQueueIrp) {
8305 irp = ReleaseQueueIrp;
8306 } else {
8307 irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
8308 }
8309 srb = &(fdoExtension->ReleaseQueueSrb);
8310
8311 KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8312
8313 ASSERT(irp != NULL);
8314
8315 irpStack = IoGetNextIrpStackLocation(irp);
8316
8317 irpStack->MajorFunction = IRP_MJ_SCSI;
8318
8319 srb->OriginalRequest = irp;
8320
8321 //
8322 // Store the SRB address in next stack for port driver.
8323 //
8324
8325 irpStack->Parameters.Scsi.Srb = srb;
8326
8327 //
8328 // If this device is removable then flush the queue. This will also
8329 // release it.
8330 //
8331
8332 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
8333 srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
8334 }
8335 else {
8336 srb->Function = SRB_FUNCTION_RELEASE_QUEUE;
8337 }
8338
8339 ClassAcquireRemoveLock(Fdo, irp);
8340
8341 IoSetCompletionRoutine(irp,
8342 ClassReleaseQueueCompletion,
8343 Fdo,
8344 TRUE,
8345 TRUE,
8346 TRUE);
8347
8348 IoCallDriver(lowerDevice, irp);
8349
8350 KeLowerIrql(currentIrql);
8351
8352 return;
8353
8354 } // end ClassReleaseQueue()
8355 \f
8356 /*++////////////////////////////////////////////////////////////////////////////
8357
8358 ClassReleaseQueueCompletion()
8359
8360 Routine Description:
8361
8362 This routine is called when an asynchronous I/O request
8363 which was issused by the class driver completes. Examples of such requests
8364 are release queue or START UNIT. This routine releases the queue if
8365 necessary. It then frees the context and the IRP.
8366
8367 Arguments:
8368
8369 DeviceObject - The device object for the logical unit; however since this
8370 is the top stack location the value is NULL.
8371
8372 Irp - Supplies a pointer to the Irp to be processed.
8373
8374 Context - Supplies the context to be used to process this request.
8375
8376 Return Value:
8377
8378 None.
8379
8380 --*/
8381 NTSTATUS
8382 ClassReleaseQueueCompletion(
8383 PDEVICE_OBJECT DeviceObject,
8384 PIRP Irp,
8385 PVOID Context
8386 )
8387 {
8388 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8389 KIRQL oldIrql;
8390
8391 BOOLEAN releaseQueueNeeded;
8392
8393 DeviceObject = Context;
8394
8395 fdoExtension = DeviceObject->DeviceExtension;
8396
8397 ClassReleaseRemoveLock(DeviceObject, Irp);
8398
8399 //
8400 // Grab the spinlock and clear the release queue in progress flag so others
8401 // can run. Save (and clear) the state of the release queue needed flag
8402 // so that we can issue a new release queue outside the spinlock.
8403 //
8404
8405 KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
8406
8407 releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
8408
8409 fdoExtension->ReleaseQueueNeeded = FALSE;
8410 fdoExtension->ReleaseQueueInProgress = FALSE;
8411
8412 KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
8413
8414 //
8415 // If we need a release queue then issue one now. Another processor may
8416 // have already started one in which case we'll try to issue this one after
8417 // it is done - but we should never recurse more than one deep.
8418 //
8419
8420 if(releaseQueueNeeded) {
8421 ClasspReleaseQueue(DeviceObject, Irp);
8422 }
8423
8424 //
8425 // Indicate the I/O system should stop processing the Irp completion.
8426 //
8427
8428 return STATUS_MORE_PROCESSING_REQUIRED;
8429
8430 } // ClassAsynchronousCompletion()
8431 \f
8432 /*++////////////////////////////////////////////////////////////////////////////
8433
8434 ClassAcquireChildLock()
8435
8436 Routine Description:
8437
8438 This routine acquires the lock protecting children PDOs. It may be
8439 acquired recursively by the same thread, but must be release by the
8440 thread once for each acquisition.
8441
8442 Arguments:
8443
8444 FdoExtension - the device whose child list is protected.
8445
8446 Return Value:
8447
8448 None
8449
8450 --*/
8451 VOID
8452 ClassAcquireChildLock(
8453 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8454 )
8455 {
8456 PAGED_CODE();
8457
8458 if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
8459 KeWaitForSingleObject(&FdoExtension->ChildLock,
8460 Executive, KernelMode,
8461 FALSE, NULL);
8462
8463 ASSERT(FdoExtension->ChildLockOwner == NULL);
8464 ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
8465
8466 FdoExtension->ChildLockOwner = KeGetCurrentThread();
8467 } else {
8468 ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
8469 }
8470
8471 FdoExtension->ChildLockAcquisitionCount++;
8472 return;
8473 }
8474 \f
8475 /*++////////////////////////////////////////////////////////////////////////////
8476
8477 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
8478
8479 Routine Description:
8480
8481 This routine releases the lock protecting children PDOs. It must be
8482 called once for each time ClassAcquireChildLock was called.
8483
8484 Arguments:
8485
8486 FdoExtension - the device whose child list is protected
8487
8488 Return Value:
8489
8490 None.
8491
8492 --*/
8493 VOID
8494 ClassReleaseChildLock(
8495 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8496 )
8497 {
8498 ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
8499 ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
8500
8501 FdoExtension->ChildLockAcquisitionCount -= 1;
8502
8503 if(FdoExtension->ChildLockAcquisitionCount == 0) {
8504 FdoExtension->ChildLockOwner = NULL;
8505 KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
8506 }
8507
8508 return;
8509 } // end ClassReleaseChildLock(
8510 \f
8511 /*++////////////////////////////////////////////////////////////////////////////
8512
8513 ClassAddChild()
8514
8515 Routine Description:
8516
8517 This routine will insert a new child into the head of the child list.
8518
8519 Arguments:
8520
8521 Parent - the child's parent (contains the head of the list)
8522 Child - the child to be inserted.
8523 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8524 it's already been acquired by or on behalf of the caller
8525 (FALSE).
8526
8527 Return Value:
8528
8529 None.
8530
8531 --*/
8532 VOID
8533 ClassAddChild(
8534 IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
8535 IN PPHYSICAL_DEVICE_EXTENSION Child,
8536 IN BOOLEAN AcquireLock
8537 )
8538 {
8539 if(AcquireLock) {
8540 ClassAcquireChildLock(Parent);
8541 }
8542
8543 #if DBG
8544 //
8545 // Make sure this child's not already in the list.
8546 //
8547 {
8548 PPHYSICAL_DEVICE_EXTENSION testChild;
8549
8550 for (testChild = Parent->CommonExtension.ChildList;
8551 testChild != NULL;
8552 testChild = testChild->CommonExtension.ChildList) {
8553
8554 ASSERT(testChild != Child);
8555 }
8556 }
8557 #endif
8558
8559 Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
8560 Parent->CommonExtension.ChildList = Child;
8561
8562 if(AcquireLock) {
8563 ClassReleaseChildLock(Parent);
8564 }
8565 return;
8566 } // end ClassAddChild()
8567 \f
8568 /*++////////////////////////////////////////////////////////////////////////////
8569
8570 ClassRemoveChild()
8571
8572 Routine Description:
8573
8574 This routine will remove a child from the child list.
8575
8576 Arguments:
8577
8578 Parent - the parent to be removed from.
8579
8580 Child - the child to be removed or NULL if the first child should be
8581 removed.
8582
8583 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8584 it's already been acquired by or on behalf of the caller
8585 (FALSE).
8586
8587 Return Value:
8588
8589 A pointer to the child which was removed or NULL if no such child could
8590 be found in the list (or if Child was NULL but the list is empty).
8591
8592 --*/
8593 PPHYSICAL_DEVICE_EXTENSION
8594 ClassRemoveChild(
8595 IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
8596 IN PPHYSICAL_DEVICE_EXTENSION Child,
8597 IN BOOLEAN AcquireLock
8598 )
8599 {
8600 if(AcquireLock) {
8601 ClassAcquireChildLock(Parent);
8602 }
8603
8604 TRY {
8605 PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
8606
8607 //
8608 // If the list is empty then bail out now.
8609 //
8610
8611 if(Parent->CommonExtension.ChildList == NULL) {
8612 Child = NULL;
8613 LEAVE;
8614 }
8615
8616 //
8617 // If the caller specified a child then find the child object before
8618 // it. If none was specified then the FDO is the child object before
8619 // the one we want to remove.
8620 //
8621
8622 if(Child != NULL) {
8623
8624 //
8625 // Scan through the child list to find the entry which points to
8626 // this one.
8627 //
8628
8629 do {
8630 ASSERT(previousChild != &Child->CommonExtension);
8631
8632 if(previousChild->ChildList == Child) {
8633 break;
8634 }
8635
8636 previousChild = &previousChild->ChildList->CommonExtension;
8637 } while(previousChild != NULL);
8638
8639 if(previousChild == NULL) {
8640 Child = NULL;
8641 LEAVE;
8642 }
8643 }
8644
8645 //
8646 // Save the next child away then unlink it from the list.
8647 //
8648
8649 Child = previousChild->ChildList;
8650 previousChild->ChildList = Child->CommonExtension.ChildList;
8651 Child->CommonExtension.ChildList = NULL;
8652
8653 } FINALLY {
8654 if(AcquireLock) {
8655 ClassReleaseChildLock(Parent);
8656 }
8657 }
8658 return Child;
8659 } // end ClassRemoveChild()
8660
8661 \f
8662 /*++
8663
8664 ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
8665
8666 --*/
8667 VOID
8668 ClasspRetryRequestDpc(
8669 IN PKDPC Dpc,
8670 IN PDEVICE_OBJECT DeviceObject,
8671 IN PVOID Arg1,
8672 IN PVOID Arg2
8673 )
8674 {
8675 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8676 PCOMMON_DEVICE_EXTENSION commonExtension;
8677 PCLASS_PRIVATE_FDO_DATA fdoData;
8678 PCLASS_RETRY_INFO retryList;
8679 KIRQL irql;
8680
8681
8682 commonExtension = DeviceObject->DeviceExtension;
8683 ASSERT(commonExtension->IsFdo);
8684 fdoExtension = DeviceObject->DeviceExtension;
8685 fdoData = fdoExtension->PrivateFdoData;
8686
8687
8688 KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
8689 {
8690 LARGE_INTEGER now;
8691 KeQueryTickCount(&now);
8692
8693 //
8694 // if CurrentTick is less than now
8695 // fire another DPC
8696 // else
8697 // retry entire list
8698 // endif
8699 //
8700
8701 if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
8702
8703 ClasspRetryDpcTimer(fdoData);
8704 retryList = NULL;
8705
8706 } else {
8707
8708 retryList = fdoData->Retry.ListHead;
8709 fdoData->Retry.ListHead = NULL;
8710 fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
8711 fdoData->Retry.Tick.QuadPart = (LONGLONG)0;
8712
8713 }
8714 }
8715 KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
8716
8717 while (retryList != NULL) {
8718
8719 PIRP irp;
8720
8721 irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
8722 DebugPrint((ClassDebugDelayedRetry, "ClassRetry: -- %p\n", irp));
8723 retryList = retryList->Next;
8724 #if DBG
8725 irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
8726 irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
8727 irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
8728 irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
8729 #endif
8730
8731 IoCallDriver(commonExtension->LowerDeviceObject, irp);
8732
8733 }
8734 return;
8735
8736 } // end ClasspRetryRequestDpc()
8737 \f
8738 /*++
8739
8740 ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
8741
8742 --*/
8743 VOID
8744 ClassRetryRequest(
8745 IN PDEVICE_OBJECT SelfDeviceObject,
8746 IN PIRP Irp,
8747 IN LARGE_INTEGER TimeDelta100ns // in 100ns units
8748 )
8749 {
8750 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8751 PCLASS_PRIVATE_FDO_DATA fdoData;
8752 PCLASS_RETRY_INFO retryInfo;
8753 PCLASS_RETRY_INFO *previousNext;
8754 LARGE_INTEGER delta;
8755 KIRQL irql;
8756
8757 //
8758 // this checks we aren't destroying irps
8759 //
8760 ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
8761
8762 fdoExtension = SelfDeviceObject->DeviceExtension;
8763 fdoData = fdoExtension->PrivateFdoData;
8764
8765 if (!fdoExtension->CommonExtension.IsFdo) {
8766
8767 //
8768 // this debug print/assertion should ALWAYS be investigated.
8769 // ClassRetryRequest can currently only be used by FDO's
8770 //
8771
8772 DebugPrint((ClassDebugError, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
8773 ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
8774 return;
8775
8776 }
8777
8778 if (TimeDelta100ns.QuadPart < 0) {
8779 ASSERT(!"ClassRetryRequest - must use positive delay");
8780 TimeDelta100ns.QuadPart *= -1;
8781 }
8782
8783 //
8784 // prepare what we can out of the loop
8785 //
8786
8787 retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
8788 RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
8789
8790 delta.QuadPart = (TimeDelta100ns.QuadPart / fdoData->Retry.Granularity);
8791 if (TimeDelta100ns.QuadPart % fdoData->Retry.Granularity) {
8792 delta.QuadPart ++; // round up to next tick
8793 }
8794 if (delta.QuadPart == (LONGLONG)0) {
8795 delta.QuadPart = MINIMUM_RETRY_UNITS;
8796 }
8797
8798 //
8799 // now determine if we should fire another DPC or not
8800 //
8801
8802 KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
8803
8804 //
8805 // always add request to the list
8806 //
8807
8808 retryInfo->Next = fdoData->Retry.ListHead;
8809 fdoData->Retry.ListHead = retryInfo;
8810
8811 if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
8812
8813 DebugPrint((ClassDebugDelayedRetry, "ClassRetry: +++ %p\n", Irp));
8814
8815 //
8816 // must be exactly one item on list
8817 //
8818
8819 ASSERT(fdoData->Retry.ListHead != NULL);
8820 ASSERT(fdoData->Retry.ListHead->Next == NULL);
8821
8822 //
8823 // if currentDelta is zero, always fire a DPC
8824 //
8825
8826 KeQueryTickCount(&fdoData->Retry.Tick);
8827 fdoData->Retry.Tick.QuadPart += delta.QuadPart;
8828 fdoData->Retry.Delta.QuadPart = delta.QuadPart;
8829 ClasspRetryDpcTimer(fdoData);
8830
8831 } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
8832
8833 //
8834 // if delta is greater than the list's current delta,
8835 // increase the DPC handling time by difference
8836 // and update the delta to new larger value
8837 // allow the DPC to re-fire itself if needed
8838 //
8839
8840 DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp));
8841
8842 //
8843 // must be at least two items on list
8844 //
8845
8846 ASSERT(fdoData->Retry.ListHead != NULL);
8847 ASSERT(fdoData->Retry.ListHead->Next != NULL);
8848
8849 fdoData->Retry.Tick.QuadPart -= fdoData->Retry.Delta.QuadPart;
8850 fdoData->Retry.Tick.QuadPart += delta.QuadPart;
8851
8852 fdoData->Retry.Delta.QuadPart = delta.QuadPart;
8853
8854 } else {
8855
8856 //
8857 // just inserting it on the list was enough
8858 //
8859
8860 DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp));
8861
8862 }
8863
8864
8865 KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
8866
8867
8868 } // end ClassRetryRequest()
8869 \f
8870 /*++
8871
8872 ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
8873
8874 --*/
8875 VOID
8876 ClasspRetryDpcTimer(
8877 IN PCLASS_PRIVATE_FDO_DATA FdoData
8878 )
8879 {
8880 LARGE_INTEGER fire;
8881
8882 ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
8883 ASSERT(FdoData->Retry.ListHead != NULL); // never fire an empty list
8884
8885 //
8886 // fire == (CurrentTick - now) * (100ns per tick)
8887 //
8888 // NOTE: Overflow is nearly impossible and is ignored here
8889 //
8890
8891 KeQueryTickCount(&fire);
8892 fire.QuadPart = FdoData->Retry.Tick.QuadPart - fire.QuadPart;
8893 fire.QuadPart *= FdoData->Retry.Granularity;
8894
8895 //
8896 // fire is now multiples of 100ns until should fire the timer.
8897 // if timer should already have expired, or would fire too quickly,
8898 // fire it in some arbitrary number of ticks to prevent infinitely
8899 // recursing.
8900 //
8901
8902 if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
8903 fire.QuadPart = MINIMUM_RETRY_UNITS;
8904 }
8905
8906 DebugPrint((ClassDebugDelayedRetry,
8907 "ClassRetry: ======= %I64x ticks\n",
8908 fire.QuadPart));
8909
8910 //
8911 // must use negative to specify relative time to fire
8912 //
8913
8914 fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
8915
8916 //
8917 // set the timer, since this is the first addition
8918 //
8919
8920 KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
8921
8922 return;
8923 } // end ClasspRetryDpcTimer()
8924 \f
8925 NTSTATUS
8926 ClasspInitializeHotplugInfo(
8927 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8928 )
8929 {
8930 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
8931 DEVICE_REMOVAL_POLICY deviceRemovalPolicy;
8932 NTSTATUS status;
8933 ULONG resultLength = 0;
8934 ULONG writeCacheOverride;
8935
8936 PAGED_CODE();
8937
8938 //
8939 // start with some default settings
8940 //
8941 RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
8942
8943 //
8944 // set the size (aka version)
8945 //
8946
8947 fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
8948
8949 //
8950 // set if the device has removable media
8951 //
8952
8953 if (FdoExtension->DeviceDescriptor->RemovableMedia) {
8954 fdoData->HotplugInfo.MediaRemovable = TRUE;
8955 } else {
8956 fdoData->HotplugInfo.MediaRemovable = FALSE;
8957 }
8958
8959 //
8960 // this refers to devices which, for reasons not yet understood,
8961 // do not fail PREVENT_MEDIA_REMOVAL requests even though they
8962 // have no way to lock the media into the drive. this allows
8963 // the filesystems to turn off delayed-write caching for these
8964 // devices as well.
8965 //
8966
8967 if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
8968 FDO_HACK_CANNOT_LOCK_MEDIA)) {
8969 fdoData->HotplugInfo.MediaHotplug = TRUE;
8970 } else {
8971 fdoData->HotplugInfo.MediaHotplug = FALSE;
8972 }
8973
8974
8975 //
8976 // Look into the registry to see if the user has chosen
8977 // to override the default setting for the removal policy
8978 //
8979
8980 RtlZeroMemory(&deviceRemovalPolicy, sizeof(DEVICE_REMOVAL_POLICY));
8981
8982 ClassGetDeviceParameter(FdoExtension,
8983 CLASSP_REG_SUBKEY_NAME,
8984 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
8985 (PULONG)&deviceRemovalPolicy);
8986
8987 if (deviceRemovalPolicy == 0)
8988 {
8989 //
8990 // Query the default removal policy from the kernel
8991 //
8992
8993 status = IoGetDeviceProperty(FdoExtension->LowerPdo,
8994 DevicePropertyRemovalPolicy,
8995 sizeof(DEVICE_REMOVAL_POLICY),
8996 (PVOID)&deviceRemovalPolicy,
8997 &resultLength);
8998 if (!NT_SUCCESS(status))
8999 {
9000 return status;
9001 }
9002
9003 if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
9004 {
9005 return STATUS_UNSUCCESSFUL;
9006 }
9007 }
9008
9009 //
9010 // use this info to set the DeviceHotplug setting
9011 // don't rely on DeviceCapabilities, since it can't properly
9012 // determine device relations, etc. let the kernel figure this
9013 // stuff out instead.
9014 //
9015
9016 if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
9017 fdoData->HotplugInfo.DeviceHotplug = TRUE;
9018 } else {
9019 fdoData->HotplugInfo.DeviceHotplug = FALSE;
9020 }
9021
9022 //
9023 // this refers to the *filesystem* caching, but has to be included
9024 // here since it's a per-device setting. this may change to be
9025 // stored by the system in the future.
9026 //
9027
9028 writeCacheOverride = FALSE;
9029 ClassGetDeviceParameter(FdoExtension,
9030 CLASSP_REG_SUBKEY_NAME,
9031 CLASSP_REG_WRITE_CACHE_VALUE_NAME,
9032 &writeCacheOverride);
9033
9034 if (writeCacheOverride) {
9035 fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
9036 } else {
9037 fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
9038 }
9039
9040 return STATUS_SUCCESS;
9041 }
9042 \f
9043 VOID
9044 ClasspScanForClassHacks(
9045 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
9046 IN ULONG_PTR Data
9047 )
9048 {
9049 PAGED_CODE();
9050
9051 //
9052 // remove invalid flags and save
9053 //
9054
9055 CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
9056 SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
9057 return;
9058 }
9059 \f
9060 VOID
9061 ClasspScanForSpecialInRegistry(
9062 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
9063 )
9064 {
9065 HANDLE deviceParameterHandle; // device instance key
9066 HANDLE classParameterHandle; // classpnp subkey
9067 OBJECT_ATTRIBUTES objectAttributes;
9068 UNICODE_STRING subkeyName;
9069 NTSTATUS status;
9070
9071 //
9072 // seeded in the ENUM tree by ClassInstaller
9073 //
9074 ULONG deviceHacks;
9075 RTL_QUERY_REGISTRY_TABLE queryTable[2]; // null terminated array
9076
9077 PAGED_CODE();
9078
9079 deviceParameterHandle = NULL;
9080 classParameterHandle = NULL;
9081 deviceHacks = 0;
9082
9083 status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
9084 PLUGPLAY_REGKEY_DEVICE,
9085 KEY_WRITE,
9086 &deviceParameterHandle
9087 );
9088
9089 if (!NT_SUCCESS(status)) {
9090 goto cleanupScanForSpecial;
9091 }
9092
9093 RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
9094 InitializeObjectAttributes(&objectAttributes,
9095 &subkeyName,
9096 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
9097 deviceParameterHandle,
9098 NULL
9099 );
9100
9101 status = ZwOpenKey( &classParameterHandle,
9102 KEY_READ,
9103 &objectAttributes
9104 );
9105
9106 if (!NT_SUCCESS(status)) {
9107 goto cleanupScanForSpecial;
9108 }
9109
9110 //
9111 // Zero out the memory
9112 //
9113
9114 RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
9115
9116 //
9117 // Setup the structure to read
9118 //
9119
9120 queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
9121 queryTable[0].Name = CLASSP_REG_HACK_VALUE_NAME;
9122 queryTable[0].EntryContext = &deviceHacks;
9123 queryTable[0].DefaultType = REG_DWORD;
9124 queryTable[0].DefaultData = &deviceHacks;
9125 queryTable[0].DefaultLength = 0;
9126
9127 //
9128 // read values
9129 //
9130
9131 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
9132 (PWSTR)classParameterHandle,
9133 &queryTable[0],
9134 NULL,
9135 NULL
9136 );
9137 if (!NT_SUCCESS(status)) {
9138 goto cleanupScanForSpecial;
9139 }
9140
9141 //
9142 // remove unknown values and save...
9143 //
9144
9145 KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
9146 "Classpnp => ScanForSpecial: HackFlags %#08x\n",
9147 deviceHacks));
9148
9149 CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
9150 SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
9151
9152
9153 cleanupScanForSpecial:
9154
9155 if (deviceParameterHandle) {
9156 ZwClose(deviceParameterHandle);
9157 }
9158
9159 if (classParameterHandle) {
9160 ZwClose(classParameterHandle);
9161 }
9162
9163 //
9164 // we should modify the system hive to include another key for us to grab
9165 // settings from. in this case: Classpnp\HackFlags
9166 //
9167 // the use of a DWORD value for the HackFlags allows 32 hacks w/o
9168 // significant use of the registry, and also reduces OEM exposure.
9169 //
9170 // definition of bit flags:
9171 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
9172 // cannot actually prevent removal.
9173 // 0x00000002 -- Device hard-hangs or times out for GESN requests.
9174 // 0xfffffffc -- Currently reserved, may be used later.
9175 //
9176
9177 return;
9178 }
9179
9180
9181
9182