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