* Sync up to trunk head (r65353).
[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 #include "classp.h"
25
26 #include <stddef.h>
27
28 #include <initguid.h>
29 #include <mountdev.h>
30
31 #ifdef ALLOC_PRAGMA
32 #pragma alloc_text(INIT, DriverEntry)
33 #pragma alloc_text(PAGE, ClassAddDevice)
34 #pragma alloc_text(PAGE, ClassClaimDevice)
35 #pragma alloc_text(PAGE, ClassCreateDeviceObject)
36 #pragma alloc_text(PAGE, ClassDispatchPnp)
37 #pragma alloc_text(PAGE, ClassGetDescriptor)
38 #pragma alloc_text(PAGE, ClassGetPdoId)
39 #pragma alloc_text(PAGE, ClassInitialize)
40 #pragma alloc_text(PAGE, ClassInitializeEx)
41 #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
42 #pragma alloc_text(PAGE, ClassMarkChildMissing)
43 #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
44 #pragma alloc_text(PAGE, ClassModeSense)
45 #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
46 #pragma alloc_text(PAGE, ClassPnpStartDevice)
47 #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
48 #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
49 #pragma alloc_text(PAGE, ClassRemoveDevice)
50 #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
51 #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
52 #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
53 #pragma alloc_text(PAGE, ClassUnload)
54 #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
55 #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
56 #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
57 #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
58 #pragma alloc_text(PAGE, ClasspScanForClassHacks)
59 #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
60 #endif
61
62 ULONG ClassPnpAllowUnload = TRUE;
63
64
65 #define FirstDriveLetter 'C'
66 #define LastDriveLetter 'Z'
67
68
69
70 /*++////////////////////////////////////////////////////////////////////////////
71
72 DriverEntry()
73
74 Routine Description:
75
76 Temporary entry point needed to initialize the class system dll.
77 It doesn't do anything.
78
79 Arguments:
80
81 DriverObject - Pointer to the driver object created by the system.
82
83 Return Value:
84
85 STATUS_SUCCESS
86
87 --*/
88 NTSTATUS
89 NTAPI
90 DriverEntry(
91 IN PDRIVER_OBJECT DriverObject,
92 IN PUNICODE_STRING RegistryPath
93 )
94 {
95 return STATUS_SUCCESS;
96 }
97
98 /*++////////////////////////////////////////////////////////////////////////////
99
100 ClassInitialize()
101
102 Routine Description:
103
104 This routine is called by a class driver during its
105 DriverEntry routine to initialize the driver.
106
107 Arguments:
108
109 Argument1 - Driver Object.
110 Argument2 - Registry Path.
111 InitializationData - Device-specific driver's initialization data.
112
113 Return Value:
114
115 A valid return code for a DriverEntry routine.
116
117 --*/
118 ULONG
119 NTAPI
120 ClassInitialize(
121 IN PVOID Argument1,
122 IN PVOID Argument2,
123 IN PCLASS_INIT_DATA InitializationData
124 )
125 {
126 PDRIVER_OBJECT DriverObject = Argument1;
127 PUNICODE_STRING RegistryPath = Argument2;
128
129 PCLASS_DRIVER_EXTENSION driverExtension;
130
131 NTSTATUS status;
132
133 PAGED_CODE();
134
135 DebugPrint((3,"\n\nSCSI Class Driver\n"));
136
137 ClasspInitializeDebugGlobals();
138
139 //
140 // Validate the length of this structure. This is effectively a
141 // version check.
142 //
143
144 if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
145
146 //
147 // This DebugPrint is to help third-party driver writers
148 //
149
150 DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
151 return (ULONG) STATUS_REVISION_MISMATCH;
152 }
153
154 //
155 // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
156 // are not required entry points.
157 //
158
159 if ((!InitializationData->FdoData.ClassDeviceControl) ||
160 (!((InitializationData->FdoData.ClassReadWriteVerification) ||
161 (InitializationData->ClassStartIo))) ||
162 (!InitializationData->ClassAddDevice) ||
163 (!InitializationData->FdoData.ClassStartDevice)) {
164
165 //
166 // This DebugPrint is to help third-party driver writers
167 //
168
169 DebugPrint((0,
170 "ClassInitialize: Class device-specific driver missing required "
171 "FDO entry\n"));
172
173 return (ULONG) STATUS_REVISION_MISMATCH;
174 }
175
176 if ((InitializationData->ClassEnumerateDevice) &&
177 ((!InitializationData->PdoData.ClassDeviceControl) ||
178 (!InitializationData->PdoData.ClassStartDevice) ||
179 (!((InitializationData->PdoData.ClassReadWriteVerification) ||
180 (InitializationData->ClassStartIo))))) {
181
182 //
183 // This DebugPrint is to help third-party driver writers
184 //
185
186 DebugPrint((0, "ClassInitialize: Class device-specific missing "
187 "required PDO entry\n"));
188
189 return (ULONG) STATUS_REVISION_MISMATCH;
190 }
191
192 if((InitializationData->FdoData.ClassStopDevice == NULL) ||
193 ((InitializationData->ClassEnumerateDevice != NULL) &&
194 (InitializationData->PdoData.ClassStopDevice == NULL))) {
195
196 //
197 // This DebugPrint is to help third-party driver writers
198 //
199
200 DebugPrint((0, "ClassInitialize: Class device-specific missing "
201 "required PDO entry\n"));
202 ASSERT(FALSE);
203 return (ULONG) STATUS_REVISION_MISMATCH;
204 }
205
206 //
207 // Setup the default power handlers if the class driver didn't provide
208 // any.
209 //
210
211 if(InitializationData->FdoData.ClassPowerDevice == NULL) {
212 InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
213 }
214
215 if((InitializationData->ClassEnumerateDevice != NULL) &&
216 (InitializationData->PdoData.ClassPowerDevice == NULL)) {
217 InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
218 }
219
220 //
221 // warn that unload is not supported
222 //
223 // ISSUE-2000/02/03-peterwie
224 // We should think about making this a fatal error.
225 //
226
227 if(InitializationData->ClassUnload == NULL) {
228
229 //
230 // This DebugPrint is to help third-party driver writers
231 //
232
233 DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
234 RegistryPath));
235 }
236
237 //
238 // Create an extension for the driver object
239 //
240
241 status = IoAllocateDriverObjectExtension(DriverObject,
242 CLASS_DRIVER_EXTENSION_KEY,
243 sizeof(CLASS_DRIVER_EXTENSION),
244 (PVOID *)&driverExtension);
245
246 if(NT_SUCCESS(status)) {
247
248 //
249 // Copy the registry path into the driver extension so we can use it later
250 //
251
252 driverExtension->RegistryPath.Length = RegistryPath->Length;
253 driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
254
255 driverExtension->RegistryPath.Buffer =
256 ExAllocatePoolWithTag(PagedPool,
257 RegistryPath->MaximumLength,
258 '1CcS');
259
260 if(driverExtension->RegistryPath.Buffer == NULL) {
261
262 status = STATUS_INSUFFICIENT_RESOURCES;
263 return status;
264 }
265
266 RtlCopyUnicodeString(
267 &(driverExtension->RegistryPath),
268 RegistryPath);
269
270 //
271 // Copy the initialization data into the driver extension so we can reuse
272 // it during our add device routine
273 //
274
275 RtlCopyMemory(
276 &(driverExtension->InitData),
277 InitializationData,
278 sizeof(CLASS_INIT_DATA));
279
280 driverExtension->DeviceCount = 0;
281
282 } else if (status == STATUS_OBJECT_NAME_COLLISION) {
283
284 //
285 // The extension already exists - get a pointer to it
286 //
287
288 driverExtension = IoGetDriverObjectExtension(DriverObject,
289 CLASS_DRIVER_EXTENSION_KEY);
290
291 ASSERT(driverExtension != NULL);
292
293 } else {
294
295 DebugPrint((1, "ClassInitialize: Class driver extension could not be "
296 "allocated %lx\n", status));
297 return status;
298 }
299
300 //
301 // Update driver object with entry points.
302 //
303
304 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreateClose;
305 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassCreateClose;
306 DriverObject->MajorFunction[IRP_MJ_READ] = ClassReadWrite;
307 DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassReadWrite;
308 DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassInternalIoControl;
309 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch;
310 DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassShutdownFlush;
311 DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush;
312 DriverObject->MajorFunction[IRP_MJ_PNP] = ClassDispatchPnp;
313 DriverObject->MajorFunction[IRP_MJ_POWER] = ClassDispatchPower;
314 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl;
315
316 if (InitializationData->ClassStartIo) {
317 DriverObject->DriverStartIo = ClasspStartIo;
318 }
319
320 if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) {
321 DriverObject->DriverUnload = ClassUnload;
322 } else {
323 DriverObject->DriverUnload = NULL;
324 }
325
326 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
327
328 DbgPrint("Driver is ready to go\n");
329 status = STATUS_SUCCESS;
330 return status;
331 } // end ClassInitialize()
332
333 /*++////////////////////////////////////////////////////////////////////////////
334
335 ClassInitializeEx()
336
337 Routine Description:
338
339 This routine is allows the caller to do any extra initialization or
340 setup that is not done in ClassInitialize. The operation is
341 controlled by the GUID that is passed and the contents of the Data
342 parameter is dependent upon the GUID.
343
344 This is the list of supported operations:
345
346 Guid - GUID_CLASSPNP_QUERY_REGINFOEX
347 Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
348
349 Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
350 callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
351 former callback allows the driver to specify the name of the
352 mof resource.
353
354 Arguments:
355
356 DriverObject
357 Guid
358 Data
359
360 Return Value:
361
362 Status Code
363
364 --*/
365 ULONG
366 NTAPI
367 ClassInitializeEx(
368 IN PDRIVER_OBJECT DriverObject,
369 IN LPGUID Guid,
370 IN PVOID Data
371 )
372 {
373 PCLASS_DRIVER_EXTENSION driverExtension;
374
375 NTSTATUS status;
376
377 PAGED_CODE();
378
379 driverExtension = IoGetDriverObjectExtension( DriverObject,
380 CLASS_DRIVER_EXTENSION_KEY
381 );
382 if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
383 {
384 PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
385
386 //
387 // Indicate the device supports PCLASS_QUERY_REGINFO_EX
388 // callback instead of PCLASS_QUERY_REGINFO callback.
389 //
390 List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
391
392 if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
393 {
394 driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
395 driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
396 status = STATUS_SUCCESS;
397 } else {
398 status = STATUS_INVALID_PARAMETER;
399 }
400 } else {
401 status = STATUS_NOT_SUPPORTED;
402 }
403
404 return(status);
405
406 } // end ClassInitializeEx()
407
408 /*++////////////////////////////////////////////////////////////////////////////
409
410 ClassUnload()
411
412 Routine Description:
413
414 called when there are no more references to the driver. this allows
415 drivers to be updated without rebooting.
416
417 Arguments:
418
419 DriverObject - a pointer to the driver object that is being unloaded
420
421 Status:
422
423 --*/
424 VOID
425 NTAPI
426 ClassUnload(
427 IN PDRIVER_OBJECT DriverObject
428 )
429 {
430 PCLASS_DRIVER_EXTENSION driverExtension;
431 //NTSTATUS status;
432
433 PAGED_CODE();
434
435 ASSERT( DriverObject->DeviceObject == NULL );
436
437 driverExtension = IoGetDriverObjectExtension( DriverObject,
438 CLASS_DRIVER_EXTENSION_KEY
439 );
440
441 ASSERT(driverExtension != NULL);
442 ASSERT(driverExtension->RegistryPath.Buffer != NULL);
443 ASSERT(driverExtension->InitData.ClassUnload != NULL);
444
445 DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
446 &driverExtension->RegistryPath));
447
448 //
449 // attempt to process the driver's unload routine first.
450 //
451
452 driverExtension->InitData.ClassUnload(DriverObject);
453
454 //
455 // free own allocated resources and return
456 //
457
458 ExFreePool( driverExtension->RegistryPath.Buffer );
459 driverExtension->RegistryPath.Buffer = NULL;
460 driverExtension->RegistryPath.Length = 0;
461 driverExtension->RegistryPath.MaximumLength = 0;
462
463 return;
464 } // end ClassUnload()
465
466 /*++////////////////////////////////////////////////////////////////////////////
467
468 ClassAddDevice()
469
470 Routine Description:
471
472 SCSI class driver add device routine. This is called by pnp when a new
473 physical device come into being.
474
475 This routine will call out to the class driver to verify that it should
476 own this device then will create and attach a device object and then hand
477 it to the driver to initialize and create symbolic links
478
479 Arguments:
480
481 DriverObject - a pointer to the driver object that this is being created for
482 PhysicalDeviceObject - a pointer to the physical device object
483
484 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
485 STATUS_SUCCESS if the creation and attachment was successful
486 status of device creation and initialization
487
488 --*/
489 NTSTATUS
490 NTAPI
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
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 NTAPI
532 ClassDispatchPnp(
533 IN PDEVICE_OBJECT DeviceObject,
534 IN PIRP Irp
535 )
536 {
537 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
538 BOOLEAN isFdo = commonExtension->IsFdo;
539
540 PCLASS_DRIVER_EXTENSION driverExtension;
541 PCLASS_INIT_DATA initData;
542 PCLASS_DEV_INFO devInfo;
543
544 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
545
546 NTSTATUS status = Irp->IoStatus.Status;
547 BOOLEAN completeRequest = TRUE;
548 BOOLEAN lockReleased = FALSE;
549
550 PAGED_CODE();
551
552 //
553 // Extract all the useful information out of the driver object
554 // extension
555 //
556
557 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
558 CLASS_DRIVER_EXTENSION_KEY);
559 if (driverExtension){
560
561 initData = &(driverExtension->InitData);
562
563 if(isFdo) {
564 devInfo = &(initData->FdoData);
565 } else {
566 devInfo = &(initData->PdoData);
567 }
568
569 ClassAcquireRemoveLock(DeviceObject, Irp);
570
571 DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
572 DeviceObject, Irp,
573 irpStack->MinorFunction,
574 isFdo ? "fdo" : "pdo",
575 DeviceObject));
576 DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
577 DeviceObject, Irp,
578 commonExtension->PreviousState,
579 commonExtension->CurrentState));
580
581 switch(irpStack->MinorFunction) {
582
583 case IRP_MN_START_DEVICE: {
584
585 //
586 // if this is sent to the FDO we should forward it down the
587 // attachment chain before we start the FDO.
588 //
589
590 if (isFdo) {
591 status = ClassForwardIrpSynchronous(commonExtension, Irp);
592 }
593 else {
594 status = STATUS_SUCCESS;
595 }
596
597 if (NT_SUCCESS(status)){
598 status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
599 }
600
601 break;
602 }
603
604
605 case IRP_MN_QUERY_DEVICE_RELATIONS: {
606
607 DEVICE_RELATION_TYPE type =
608 irpStack->Parameters.QueryDeviceRelations.Type;
609
610 PDEVICE_RELATIONS deviceRelations = NULL;
611
612 if(!isFdo) {
613
614 if(type == TargetDeviceRelation) {
615
616 //
617 // Device relations has one entry built in to it's size.
618 //
619
620 status = STATUS_INSUFFICIENT_RESOURCES;
621
622 deviceRelations = ExAllocatePoolWithTag(PagedPool,
623 sizeof(DEVICE_RELATIONS),
624 '2CcS');
625
626 if(deviceRelations != NULL) {
627
628 RtlZeroMemory(deviceRelations,
629 sizeof(DEVICE_RELATIONS));
630
631 Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
632
633 deviceRelations->Count = 1;
634 deviceRelations->Objects[0] = DeviceObject;
635 ObReferenceObject(deviceRelations->Objects[0]);
636
637 status = STATUS_SUCCESS;
638 }
639
640 } else {
641 //
642 // PDO's just complete enumeration requests without altering
643 // the status.
644 //
645
646 status = Irp->IoStatus.Status;
647 }
648
649 break;
650
651 } else if (type == BusRelations) {
652
653 ASSERT(commonExtension->IsInitialized);
654
655 //
656 // Make sure we support enumeration
657 //
658
659 if(initData->ClassEnumerateDevice == NULL) {
660
661 //
662 // Just send the request down to the lower driver. Perhaps
663 // It can enumerate children.
664 //
665
666 } else {
667
668 //
669 // Re-enumerate the device
670 //
671
672 status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
673
674 if(!NT_SUCCESS(status)) {
675 completeRequest = TRUE;
676 break;
677 }
678 }
679 }
680
681 IoCopyCurrentIrpStackLocationToNext(Irp);
682 ClassReleaseRemoveLock(DeviceObject, Irp);
683 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
684 completeRequest = FALSE;
685
686 break;
687 }
688
689 case IRP_MN_QUERY_ID: {
690
691 BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
692 UNICODE_STRING unicodeString;
693
694 if(isFdo) {
695
696 //
697 // FDO's should just forward the query down to the lower
698 // device objects
699 //
700
701 IoCopyCurrentIrpStackLocationToNext(Irp);
702 ClassReleaseRemoveLock(DeviceObject, Irp);
703
704 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
705 completeRequest = FALSE;
706 break;
707 }
708
709 //
710 // PDO's need to give an answer - this is easy for now
711 //
712
713 RtlInitUnicodeString(&unicodeString, NULL);
714
715 status = ClassGetPdoId(DeviceObject,
716 idType,
717 &unicodeString);
718
719 if(status == STATUS_NOT_IMPLEMENTED) {
720 //
721 // The driver doesn't implement this ID (whatever it is).
722 // Use the status out of the IRP so that we don't mangle a
723 // response from someone else.
724 //
725
726 status = Irp->IoStatus.Status;
727 } else if(NT_SUCCESS(status)) {
728 Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
729 } else {
730 Irp->IoStatus.Information = (ULONG_PTR) NULL;
731 }
732
733 break;
734 }
735
736 case IRP_MN_QUERY_STOP_DEVICE:
737 case IRP_MN_QUERY_REMOVE_DEVICE: {
738
739 DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
740 DeviceObject, Irp,
741 ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
742 "STOP" : "REMOVE")));
743
744 //
745 // If this device is in use for some reason (paging, etc...)
746 // then we need to fail the request.
747 //
748
749 if(commonExtension->PagingPathCount != 0) {
750
751 DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
752 "path and cannot be removed\n",
753 DeviceObject, Irp));
754 status = STATUS_DEVICE_BUSY;
755 break;
756 }
757
758 //
759 // Check with the class driver to see if the query operation
760 // can succeed.
761 //
762
763 if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
764 status = devInfo->ClassStopDevice(DeviceObject,
765 irpStack->MinorFunction);
766 } else {
767 status = devInfo->ClassRemoveDevice(DeviceObject,
768 irpStack->MinorFunction);
769 }
770
771 if(NT_SUCCESS(status)) {
772
773 //
774 // ASSERT that we never get two queries in a row, as
775 // this will severly mess up the state machine
776 //
777 ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
778 commonExtension->PreviousState = commonExtension->CurrentState;
779 commonExtension->CurrentState = irpStack->MinorFunction;
780
781 if(isFdo) {
782 DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
783 "%s irp\n", DeviceObject, Irp,
784 ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
785 "STOP" : "REMOVE")));
786 status = ClassForwardIrpSynchronous(commonExtension, Irp);
787 }
788 }
789 DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
790 DeviceObject, Irp, status));
791
792 break;
793 }
794
795 case IRP_MN_CANCEL_STOP_DEVICE:
796 case IRP_MN_CANCEL_REMOVE_DEVICE: {
797
798 //
799 // Check with the class driver to see if the query or cancel
800 // operation can succeed.
801 //
802
803 if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
804 status = devInfo->ClassStopDevice(DeviceObject,
805 irpStack->MinorFunction);
806 ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
807 "never be failed\n", NT_SUCCESS(status));
808 } else {
809 status = devInfo->ClassRemoveDevice(DeviceObject,
810 irpStack->MinorFunction);
811 ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
812 "never be failed\n", NT_SUCCESS(status));
813 }
814
815 Irp->IoStatus.Status = status;
816
817 //
818 // We got a CANCEL - roll back to the previous state only
819 // if the current state is the respective QUERY state.
820 //
821
822 if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
823 (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
824 ) ||
825 ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
826 (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
827 )
828 ) {
829
830 commonExtension->CurrentState =
831 commonExtension->PreviousState;
832 commonExtension->PreviousState = 0xff;
833
834 }
835
836 if(isFdo) {
837 IoCopyCurrentIrpStackLocationToNext(Irp);
838 ClassReleaseRemoveLock(DeviceObject, Irp);
839 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
840 completeRequest = FALSE;
841 } else {
842 status = STATUS_SUCCESS;
843 }
844
845 break;
846 }
847
848 case IRP_MN_STOP_DEVICE: {
849
850 //
851 // These all mean nothing to the class driver currently. The
852 // port driver will handle all queueing when necessary.
853 //
854
855 DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
856 DeviceObject, Irp,
857 (isFdo ? "fdo" : "pdo")
858 ));
859
860 ASSERT(commonExtension->PagingPathCount == 0);
861
862 //
863 // ISSUE-2000/02/03-peterwie
864 // if we stop the timer here then it means no class driver can
865 // do i/o in its ClassStopDevice routine. This is because the
866 // retry (among other things) is tied into the tick handler
867 // and disabling retries could cause the class driver to deadlock.
868 // Currently no class driver we're aware of issues i/o in its
869 // Stop routine but this is a case we may want to defend ourself
870 // against.
871 //
872
873 if (DeviceObject->Timer) {
874 IoStopTimer(DeviceObject);
875 }
876
877 status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
878
879 ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
880 "never be failed\n", NT_SUCCESS(status));
881
882 if(isFdo) {
883 status = ClassForwardIrpSynchronous(commonExtension, Irp);
884 }
885
886 if(NT_SUCCESS(status)) {
887 commonExtension->CurrentState = irpStack->MinorFunction;
888 commonExtension->PreviousState = 0xff;
889 }
890
891 break;
892 }
893
894 case IRP_MN_REMOVE_DEVICE:
895 case IRP_MN_SURPRISE_REMOVAL: {
896
897 UCHAR removeType = irpStack->MinorFunction;
898
899 if (commonExtension->PagingPathCount != 0) {
900 DBGTRACE(ClassDebugWarning, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
901 }
902
903 //
904 // Release the lock for this IRP before calling in.
905 //
906 ClassReleaseRemoveLock(DeviceObject, Irp);
907 lockReleased = TRUE;
908
909 /*
910 * If a timer was started on the device, stop it.
911 */
912 if (DeviceObject->Timer) {
913 IoStopTimer(DeviceObject);
914 }
915
916 /*
917 * "Fire-and-forget" the remove irp to the lower stack.
918 * Don't touch the irp (or the irp stack!) after this.
919 */
920 if (isFdo) {
921 IoCopyCurrentIrpStackLocationToNext(Irp);
922 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
923 ASSERT(NT_SUCCESS(status));
924 completeRequest = FALSE;
925 }
926 else {
927 status = STATUS_SUCCESS;
928 }
929
930 /*
931 * Do our own cleanup and call the class driver's remove
932 * cleanup routine.
933 * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
934 * so don't touch the extension after this.
935 */
936 commonExtension->PreviousState = commonExtension->CurrentState;
937 commonExtension->CurrentState = removeType;
938 ClassRemoveDevice(DeviceObject, removeType);
939
940 break;
941 }
942
943 case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
944
945 switch(irpStack->Parameters.UsageNotification.Type) {
946
947 case DeviceUsageTypePaging: {
948
949 BOOLEAN setPagable;
950
951 if((irpStack->Parameters.UsageNotification.InPath) &&
952 (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
953
954 //
955 // Device isn't started. Don't allow adding a
956 // paging file, but allow a removal of one.
957 //
958
959 status = STATUS_DEVICE_NOT_READY;
960 break;
961 }
962
963 ASSERT(commonExtension->IsInitialized);
964
965 //
966 // need to synchronize this now...
967 //
968
969 KeEnterCriticalRegion();
970 status = KeWaitForSingleObject(&commonExtension->PathCountEvent,
971 Executive, KernelMode,
972 FALSE, NULL);
973 ASSERT(NT_SUCCESS(status));
974 status = STATUS_SUCCESS;
975
976 //
977 // If the volume is removable we should try to lock it in
978 // place or unlock it once per paging path count
979 //
980
981 if (commonExtension->IsFdo){
982 status = ClasspEjectionControl(
983 DeviceObject,
984 Irp,
985 InternalMediaLock,
986 (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
987 }
988
989 if (!NT_SUCCESS(status)){
990 KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
991 KeLeaveCriticalRegion();
992 break;
993 }
994
995 //
996 // if removing last paging device, need to set DO_POWER_PAGABLE
997 // bit here, and possible re-set it below on failure.
998 //
999
1000 setPagable = FALSE;
1001
1002 if (!irpStack->Parameters.UsageNotification.InPath &&
1003 commonExtension->PagingPathCount == 1
1004 ) {
1005
1006 //
1007 // removing last paging file
1008 // must have DO_POWER_PAGABLE bits set, but only
1009 // if noone set the DO_POWER_INRUSH bit
1010 //
1011
1012
1013 if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1014 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1015 "paging file removed, but "
1016 "DO_POWER_INRUSH was set, so NOT "
1017 "setting DO_POWER_PAGABLE\n",
1018 DeviceObject, Irp));
1019 } else {
1020 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1021 "paging file removed, "
1022 "setting DO_POWER_PAGABLE\n",
1023 DeviceObject, Irp));
1024 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1025 setPagable = TRUE;
1026 }
1027
1028 }
1029
1030 //
1031 // forward the irp before finishing handling the
1032 // special cases
1033 //
1034
1035 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1036
1037 //
1038 // now deal with the failure and success cases.
1039 // note that we are not allowed to fail the irp
1040 // once it is sent to the lower drivers.
1041 //
1042
1043 if (NT_SUCCESS(status)) {
1044
1045 IoAdjustPagingPathCount(
1046 (PLONG)&commonExtension->PagingPathCount,
1047 irpStack->Parameters.UsageNotification.InPath);
1048
1049 if (irpStack->Parameters.UsageNotification.InPath) {
1050 if (commonExtension->PagingPathCount == 1) {
1051 DebugPrint((2, "ClassDispatchPnp (%p,%p): "
1052 "Clearing PAGABLE bit\n",
1053 DeviceObject, Irp));
1054 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1055 }
1056 }
1057
1058 } else {
1059
1060 //
1061 // cleanup the changes done above
1062 //
1063
1064 if (setPagable == TRUE) {
1065 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
1066 "PAGABLE bit due to irp failure\n",
1067 DeviceObject, Irp));
1068 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1069 setPagable = FALSE;
1070 }
1071
1072 //
1073 // relock or unlock the media if needed.
1074 //
1075
1076 if (commonExtension->IsFdo) {
1077
1078 ClasspEjectionControl(
1079 DeviceObject,
1080 Irp,
1081 InternalMediaLock,
1082 (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
1083 }
1084 }
1085
1086 //
1087 // set the event so the next one can occur.
1088 //
1089
1090 KeSetEvent(&commonExtension->PathCountEvent,
1091 IO_NO_INCREMENT, FALSE);
1092 KeLeaveCriticalRegion();
1093 break;
1094 }
1095
1096 case DeviceUsageTypeHibernation: {
1097
1098 IoAdjustPagingPathCount(
1099 (PLONG)&commonExtension->HibernationPathCount,
1100 irpStack->Parameters.UsageNotification.InPath
1101 );
1102 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1103 if (!NT_SUCCESS(status)) {
1104 IoAdjustPagingPathCount(
1105 (PLONG)&commonExtension->HibernationPathCount,
1106 !irpStack->Parameters.UsageNotification.InPath
1107 );
1108 }
1109
1110 break;
1111 }
1112
1113 case DeviceUsageTypeDumpFile: {
1114 IoAdjustPagingPathCount(
1115 (PLONG)&commonExtension->DumpPathCount,
1116 irpStack->Parameters.UsageNotification.InPath
1117 );
1118 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1119 if (!NT_SUCCESS(status)) {
1120 IoAdjustPagingPathCount(
1121 (PLONG)&commonExtension->DumpPathCount,
1122 !irpStack->Parameters.UsageNotification.InPath
1123 );
1124 }
1125
1126 break;
1127 }
1128
1129 default: {
1130 status = STATUS_INVALID_PARAMETER;
1131 break;
1132 }
1133 }
1134 break;
1135 }
1136
1137 case IRP_MN_QUERY_CAPABILITIES: {
1138
1139 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1140 DeviceObject, Irp));
1141
1142 if(!isFdo) {
1143
1144 status = ClassQueryPnpCapabilities(
1145 DeviceObject,
1146 irpStack->Parameters.DeviceCapabilities.Capabilities
1147 );
1148
1149 break;
1150
1151 } else {
1152
1153 PDEVICE_CAPABILITIES deviceCapabilities;
1154 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1155 PCLASS_PRIVATE_FDO_DATA fdoData;
1156
1157 fdoExtension = DeviceObject->DeviceExtension;
1158 fdoData = fdoExtension->PrivateFdoData;
1159 deviceCapabilities =
1160 irpStack->Parameters.DeviceCapabilities.Capabilities;
1161
1162 //
1163 // forward the irp before handling the special cases
1164 //
1165
1166 status = ClassForwardIrpSynchronous(commonExtension, Irp);
1167 if (!NT_SUCCESS(status)) {
1168 break;
1169 }
1170
1171 //
1172 // we generally want to remove the device from the hotplug
1173 // applet, which requires the SR-OK bit to be set.
1174 // only when the user specifies that they are capable of
1175 // safely removing things do we want to clear this bit
1176 // (saved in WriteCacheEnableOverride)
1177 //
1178 // setting of this bit is done either above, or by the
1179 // lower driver.
1180 //
1181 // note: may not be started, so check we have FDO data first.
1182 //
1183
1184 if (fdoData &&
1185 fdoData->HotplugInfo.WriteCacheEnableOverride) {
1186 if (deviceCapabilities->SurpriseRemovalOK) {
1187 DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
1188 "device capabilities due to hotplug "
1189 "device or media\n"));
1190 }
1191 deviceCapabilities->SurpriseRemovalOK = FALSE;
1192 }
1193 break;
1194
1195 } // end QUERY_CAPABILITIES for FDOs
1196
1197 ASSERT(FALSE);
1198 break;
1199
1200
1201 } // end QUERY_CAPABILITIES
1202
1203 default: {
1204
1205 if (isFdo){
1206 IoCopyCurrentIrpStackLocationToNext(Irp);
1207
1208 ClassReleaseRemoveLock(DeviceObject, Irp);
1209 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1210
1211 completeRequest = FALSE;
1212 }
1213
1214 break;
1215 }
1216 }
1217 }
1218 else {
1219 ASSERT(driverExtension);
1220 status = STATUS_INTERNAL_ERROR;
1221 }
1222
1223 if (completeRequest){
1224 Irp->IoStatus.Status = status;
1225
1226 if (!lockReleased){
1227 ClassReleaseRemoveLock(DeviceObject, Irp);
1228 }
1229
1230 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1231
1232 DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
1233 }
1234 else {
1235 /*
1236 * The irp is already completed so don't touch it.
1237 * This may be a remove so don't touch the device extension.
1238 */
1239 DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
1240 }
1241
1242 return status;
1243 } // end ClassDispatchPnp()
1244
1245 /*++////////////////////////////////////////////////////////////////////////////
1246
1247 ClassPnpStartDevice()
1248
1249 Routine Description:
1250
1251 Storage class driver routine for IRP_MN_START_DEVICE requests.
1252 This routine kicks off any device specific initialization
1253
1254 Arguments:
1255
1256 DeviceObject - a pointer to the device object
1257
1258 Irp - a pointer to the io request packet
1259
1260 Return Value:
1261
1262 none
1263
1264 --*/
1265 NTSTATUS NTAPI ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
1266 {
1267 PCLASS_DRIVER_EXTENSION driverExtension;
1268 PCLASS_INIT_DATA initData;
1269
1270 PCLASS_DEV_INFO devInfo;
1271
1272 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1273 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1274 BOOLEAN isFdo = commonExtension->IsFdo;
1275
1276 BOOLEAN isMountedDevice = TRUE;
1277 //UNICODE_STRING interfaceName;
1278
1279 BOOLEAN timerStarted;
1280
1281 NTSTATUS status = STATUS_SUCCESS;
1282
1283 PAGED_CODE();
1284
1285 driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
1286 CLASS_DRIVER_EXTENSION_KEY);
1287
1288 initData = &(driverExtension->InitData);
1289 if(isFdo) {
1290 devInfo = &(initData->FdoData);
1291 } else {
1292 devInfo = &(initData->PdoData);
1293 }
1294
1295 ASSERT(devInfo->ClassInitDevice != NULL);
1296 ASSERT(devInfo->ClassStartDevice != NULL);
1297
1298 if (!commonExtension->IsInitialized){
1299
1300 //
1301 // perform FDO/PDO specific initialization
1302 //
1303
1304 if (isFdo){
1305 STORAGE_PROPERTY_ID propertyId;
1306
1307 //
1308 // allocate a private extension for class data
1309 //
1310
1311 if (fdoExtension->PrivateFdoData == NULL) {
1312 fdoExtension->PrivateFdoData =
1313 ExAllocatePoolWithTag(NonPagedPool,
1314 sizeof(CLASS_PRIVATE_FDO_DATA),
1315 CLASS_TAG_PRIVATE_DATA
1316 );
1317 }
1318
1319 if (fdoExtension->PrivateFdoData == NULL) {
1320 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
1321 "private fdo data\n"));
1322 return STATUS_INSUFFICIENT_RESOURCES;
1323 }
1324
1325 //
1326 // initialize the struct's various fields.
1327 //
1328
1329 RtlZeroMemory(fdoExtension->PrivateFdoData,
1330 sizeof(CLASS_PRIVATE_FDO_DATA)
1331 );
1332 KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
1333 KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
1334 ClasspRetryRequestDpc,
1335 DeviceObject);
1336 KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
1337 fdoExtension->PrivateFdoData->Retry.Granularity =
1338 KeQueryTimeIncrement();
1339 commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
1340
1341 //
1342 // NOTE: the old interface allowed the class driver to allocate
1343 // this. this was unsafe for low-memory conditions. allocate one
1344 // unconditionally now, and modify our internal functions to use
1345 // our own exclusively as it is the only safe way to do this.
1346 //
1347
1348 status = ClasspAllocateReleaseQueueIrp(fdoExtension);
1349 if (!NT_SUCCESS(status)) {
1350 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
1351 "private release queue irp\n"));
1352 return status;
1353 }
1354
1355 //
1356 // Call port driver to get adapter capabilities.
1357 //
1358
1359 propertyId = StorageAdapterProperty;
1360
1361 status = ClassGetDescriptor(
1362 commonExtension->LowerDeviceObject,
1363 &propertyId,
1364 (PSTORAGE_DESCRIPTOR_HEADER *)&fdoExtension->AdapterDescriptor);
1365
1366 if(!NT_SUCCESS(status)) {
1367
1368 //
1369 // This DebugPrint is to help third-party driver writers
1370 //
1371
1372 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1373 "[ADAPTER] failed %lx\n", status));
1374 return status;
1375 }
1376
1377 //
1378 // Call port driver to get device descriptor.
1379 //
1380
1381 propertyId = StorageDeviceProperty;
1382
1383 status = ClassGetDescriptor(
1384 commonExtension->LowerDeviceObject,
1385 &propertyId,
1386 (PSTORAGE_DESCRIPTOR_HEADER *)&fdoExtension->DeviceDescriptor);
1387
1388 if(!NT_SUCCESS(status)) {
1389
1390 //
1391 // This DebugPrint is to help third-party driver writers
1392 //
1393
1394 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1395 "[DEVICE] failed %lx\n", status));
1396 return status;
1397 }
1398
1399 ClasspScanForSpecialInRegistry(fdoExtension);
1400 ClassScanForSpecial(fdoExtension,
1401 ClassBadItems,
1402 ClasspScanForClassHacks);
1403
1404 //
1405 // allow perf to be re-enabled after a given number of failed IOs
1406 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
1407 //
1408
1409 {
1410 ULONG t = 0;
1411 ClassGetDeviceParameter(fdoExtension,
1412 CLASSP_REG_SUBKEY_NAME,
1413 CLASSP_REG_PERF_RESTORE_VALUE_NAME,
1414 &t);
1415 if (t >= CLASS_PERF_RESTORE_MINIMUM) {
1416 fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
1417 }
1418 }
1419
1420
1421 //
1422 // compatibility comes first. writable cd media will not
1423 // get a SYNCH_CACHE on power down.
1424 //
1425
1426 if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
1427 SET_FLAG(fdoExtension->PrivateFdoData->HackFlags,
1428 FDO_HACK_NO_SYNC_CACHE);
1429 }
1430
1431 //
1432 // initialize the hotplug information only after the ScanForSpecial
1433 // routines, as it relies upon the hack flags.
1434 //
1435
1436 status = ClasspInitializeHotplugInfo(fdoExtension);
1437
1438 if (!NT_SUCCESS(status)) {
1439 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
1440 "hotplug information %lx\n", status));
1441 return status;
1442 }
1443
1444 /*
1445 * Allocate/initialize TRANSFER_PACKETs and related resources.
1446 */
1447 status = InitializeTransferPackets(DeviceObject);
1448 }
1449
1450 //
1451 // ISSUE - drivers need to disable write caching on the media
1452 // if hotplug and !useroverride. perhaps we should
1453 // allow registration of a callback to enable/disable
1454 // write cache instead.
1455 //
1456
1457 if (NT_SUCCESS(status)){
1458 status = devInfo->ClassInitDevice(DeviceObject);
1459 }
1460
1461 }
1462
1463 if (!NT_SUCCESS(status)){
1464
1465 //
1466 // Just bail out - the remove that comes down will clean up the
1467 // initialized scraps.
1468 //
1469
1470 return status;
1471 } else {
1472 commonExtension->IsInitialized = TRUE;
1473
1474 if (commonExtension->IsFdo) {
1475 fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
1476 }
1477
1478 }
1479
1480 //
1481 // If device requests autorun functionality or a once a second callback
1482 // then enable the once per second timer.
1483 //
1484 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
1485 // called in the context of the ClassInitDevice callback. If called
1486 // after then this check will have already been made and the
1487 // once a second timer will not have been enabled.
1488 //
1489 if ((isFdo) &&
1490 ((initData->ClassTick != NULL) ||
1491 (fdoExtension->MediaChangeDetectionInfo != NULL) ||
1492 ((fdoExtension->FailurePredictionInfo != NULL) &&
1493 (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
1494 {
1495 ClasspEnableTimer(DeviceObject);
1496 timerStarted = TRUE;
1497 } else {
1498 timerStarted = FALSE;
1499 }
1500
1501 //
1502 // NOTE: the timer looks at commonExtension->CurrentState now
1503 // to prevent Media Change Notification code from running
1504 // until the device is started, but allows the device
1505 // specific tick handler to run. therefore it is imperative
1506 // that commonExtension->CurrentState not be updated until
1507 // the device specific startdevice handler has finished.
1508 //
1509
1510 status = devInfo->ClassStartDevice(DeviceObject);
1511
1512 if(NT_SUCCESS(status)) {
1513 commonExtension->CurrentState = IRP_MN_START_DEVICE;
1514
1515 if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
1516 isMountedDevice = FALSE;
1517 }
1518
1519 if((DeviceObject->DeviceType != FILE_DEVICE_DISK) &&
1520 (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM)) {
1521
1522 isMountedDevice = FALSE;
1523 }
1524
1525
1526 if(isMountedDevice) {
1527 ClasspRegisterMountedDeviceInterface(DeviceObject);
1528 }
1529
1530 if((commonExtension->IsFdo) &&
1531 (devInfo->ClassWmiInfo.GuidRegInfo != NULL)) {
1532
1533 IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
1534 }
1535 } else {
1536
1537 if (timerStarted) {
1538 ClasspDisableTimer(DeviceObject);
1539 }
1540 }
1541
1542 return status;
1543 }
1544
1545
1546 /*++////////////////////////////////////////////////////////////////////////////
1547
1548 ClassReadWrite()
1549
1550 Routine Description:
1551
1552 This is the system entry point for read and write requests. The
1553 device-specific handler is invoked to perform any validation necessary.
1554
1555 If the device object is a PDO (partition object) then the request will
1556 simply be adjusted for Partition0 and issued to the lower device driver.
1557
1558 IF the device object is an FDO (paritition 0 object), the number of bytes
1559 in the request are checked against the maximum byte counts that the adapter
1560 supports and requests are broken up into
1561 smaller sizes if necessary.
1562
1563 Arguments:
1564
1565 DeviceObject - a pointer to the device object for this request
1566
1567 Irp - IO request
1568
1569 Return Value:
1570
1571 NT Status
1572
1573 --*/
1574 NTSTATUS NTAPI ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
1575 {
1576 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1577 PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
1578 PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
1579 //LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
1580 ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
1581 ULONG isRemoved;
1582 NTSTATUS status;
1583
1584 /*
1585 * Grab the remove lock. If we can't acquire it, bail out.
1586 */
1587 isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
1588 if (isRemoved) {
1589 Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
1590 ClassReleaseRemoveLock(DeviceObject, Irp);
1591 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1592 status = STATUS_DEVICE_DOES_NOT_EXIST;
1593 }
1594 else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
1595 (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
1596 !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
1597
1598 /*
1599 * DO_VERIFY_VOLUME is set for the device object,
1600 * but this request is not itself a verify request.
1601 * So fail this request.
1602 */
1603 IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
1604 Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
1605 Irp->IoStatus.Information = 0;
1606 ClassReleaseRemoveLock(DeviceObject, Irp);
1607 ClassCompleteRequest(DeviceObject, Irp, 0);
1608 status = STATUS_VERIFY_REQUIRED;
1609 }
1610 else {
1611
1612 /*
1613 * Since we've bypassed the verify-required tests we don't need to repeat
1614 * them with this IRP - in particular we don't want to worry about
1615 * hitting them at the partition 0 level if the request has come through
1616 * a non-zero partition.
1617 */
1618 currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
1619
1620 /*
1621 * Call the miniport driver's pre-pass filter to check if we
1622 * should continue with this transfer.
1623 */
1624 ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
1625 status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
1626 if (!NT_SUCCESS(status)){
1627 ASSERT(Irp->IoStatus.Status == status);
1628 ClassReleaseRemoveLock(DeviceObject, Irp);
1629 ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
1630 }
1631 else if (status == STATUS_PENDING){
1632 /*
1633 * ClassReadWriteVerification queued this request.
1634 * So don't touch the irp anymore.
1635 */
1636 }
1637 else {
1638
1639 if (transferByteCount == 0) {
1640 /*
1641 * Several parts of the code turn 0 into 0xffffffff,
1642 * so don't process a zero-length request any further.
1643 */
1644 Irp->IoStatus.Status = STATUS_SUCCESS;
1645 Irp->IoStatus.Information = 0;
1646 ClassReleaseRemoveLock(DeviceObject, Irp);
1647 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1648 status = STATUS_SUCCESS;
1649 }
1650 else {
1651 /*
1652 * If the driver has its own StartIo routine, call it.
1653 */
1654 if (commonExtension->DriverExtension->InitData.ClassStartIo) {
1655 IoMarkIrpPending(Irp);
1656 IoStartPacket(DeviceObject, Irp, NULL, NULL);
1657 status = STATUS_PENDING;
1658 }
1659 else {
1660 /*
1661 * The driver does not have its own StartIo routine.
1662 * So process this request ourselves.
1663 */
1664
1665 /*
1666 * Add partition byte offset to make starting byte relative to
1667 * beginning of disk.
1668 */
1669 currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
1670 commonExtension->StartingOffset.QuadPart;
1671
1672 if (commonExtension->IsFdo){
1673
1674 /*
1675 * Add in any skew for the disk manager software.
1676 */
1677 currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
1678 commonExtension->PartitionZeroExtension->DMByteSkew;
1679
1680 /*
1681 * Perform the actual transfer(s) on the hardware
1682 * to service this request.
1683 */
1684 ServiceTransferRequest(DeviceObject, Irp);
1685 status = STATUS_PENDING;
1686 }
1687 else {
1688 /*
1689 * This is a child PDO enumerated for our FDO by e.g. disk.sys
1690 * and owned by e.g. partmgr. Send it down to the next device
1691 * and the same irp will come back to us for the FDO.
1692 */
1693 IoCopyCurrentIrpStackLocationToNext(Irp);
1694 ClassReleaseRemoveLock(DeviceObject, Irp);
1695 status = IoCallDriver(lowerDeviceObject, Irp);
1696 }
1697 }
1698 }
1699 }
1700 }
1701
1702 return status;
1703 }
1704
1705
1706 /*++////////////////////////////////////////////////////////////////////////////
1707
1708 ClassReadDriveCapacity()
1709
1710 Routine Description:
1711
1712 This routine sends a READ CAPACITY to the requested device, updates
1713 the geometry information in the device object and returns
1714 when it is complete. This routine is synchronous.
1715
1716 This routine must be called with the remove lock held or some other
1717 assurance that the Fdo will not be removed while processing.
1718
1719 Arguments:
1720
1721 DeviceObject - Supplies a pointer to the device object that represents
1722 the device whose capacity is to be read.
1723
1724 Return Value:
1725
1726 Status is returned.
1727
1728 --*/
1729 NTSTATUS NTAPI ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo)
1730 {
1731 READ_CAPACITY_DATA readCapacityBuffer = {0};
1732 NTSTATUS status;
1733 PMDL driveCapMdl;
1734
1735 driveCapMdl = BuildDeviceInputMdl(&readCapacityBuffer, sizeof(READ_CAPACITY_DATA));
1736 if (driveCapMdl){
1737
1738 TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
1739 if (pkt){
1740 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
1741 KEVENT event;
1742 //NTSTATUS pktStatus;
1743 IRP pseudoIrp = {0};
1744
1745 /*
1746 * Our engine needs an "original irp" to write the status back to
1747 * and to count down packets (one in this case).
1748 * Just use a pretend irp for this.
1749 */
1750 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
1751 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
1752 pseudoIrp.IoStatus.Information = 0;
1753 pseudoIrp.MdlAddress = driveCapMdl;
1754
1755 /*
1756 * Set this up as a SYNCHRONOUS transfer, submit it,
1757 * and wait for the packet to complete. The result
1758 * status will be written to the original irp.
1759 */
1760 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1761 SetupDriveCapacityTransferPacket( pkt,
1762 &readCapacityBuffer,
1763 sizeof(READ_CAPACITY_DATA),
1764 &event,
1765 &pseudoIrp);
1766 SubmitTransferPacket(pkt);
1767 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1768
1769 status = pseudoIrp.IoStatus.Status;
1770
1771 /*
1772 * If we got an UNDERRUN, retry exactly once.
1773 * (The transfer_packet engine didn't retry because the result
1774 * status was success).
1775 */
1776 if (NT_SUCCESS(status) &&
1777 (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA))){
1778 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG)pseudoIrp.IoStatus.Information, sizeof(READ_CAPACITY_DATA)));
1779
1780 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
1781 if (pkt){
1782 pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
1783 pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
1784 pseudoIrp.IoStatus.Information = 0;
1785 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1786 SetupDriveCapacityTransferPacket( pkt,
1787 &readCapacityBuffer,
1788 sizeof(READ_CAPACITY_DATA),
1789 &event,
1790 &pseudoIrp);
1791 SubmitTransferPacket(pkt);
1792 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1793 status = pseudoIrp.IoStatus.Status;
1794 if (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA)){
1795 status = STATUS_DEVICE_BUSY;
1796 }
1797 }
1798 else {
1799 status = STATUS_INSUFFICIENT_RESOURCES;
1800 }
1801 }
1802
1803
1804 if (NT_SUCCESS(status)){
1805 /*
1806 * The request succeeded.
1807 * Read out and store the drive information.
1808 */
1809 ULONG cylinderSize;
1810 ULONG bytesPerSector;
1811 ULONG tmp;
1812 ULONG lastSector;
1813
1814 /*
1815 * Read the bytesPerSector value,
1816 * which is big-endian in the returned buffer.
1817 * Default to the standard 512 bytes.
1818 */
1819 tmp = readCapacityBuffer.BytesPerBlock;
1820 ((PFOUR_BYTE)&bytesPerSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
1821 ((PFOUR_BYTE)&bytesPerSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
1822 ((PFOUR_BYTE)&bytesPerSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
1823 ((PFOUR_BYTE)&bytesPerSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
1824 if (bytesPerSector == 0) {
1825 bytesPerSector = 512;
1826 }
1827 else {
1828 /*
1829 * Clear all but the highest set bit.
1830 * That will give us a bytesPerSector value that is a power of 2.
1831 */
1832 while (bytesPerSector & (bytesPerSector-1)) {
1833 bytesPerSector &= bytesPerSector-1;
1834 }
1835 }
1836 fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
1837
1838 //
1839 // Copy last sector in reverse byte order.
1840 //
1841
1842 tmp = readCapacityBuffer.LogicalBlockAddress;
1843 ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
1844 ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
1845 ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
1846 ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
1847
1848 //
1849 // Calculate sector to byte shift.
1850 //
1851
1852 WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
1853
1854 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
1855 fdoExt->DiskGeometry.BytesPerSector));
1856
1857 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
1858 lastSector + 1));
1859
1860 if (fdoExt->DMActive){
1861 DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
1862 fdoExt->DMSkew));
1863 lastSector -= fdoExt->DMSkew;
1864 }
1865
1866 /*
1867 * Check to see if we have a geometry we should be using already.
1868 */
1869 cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
1870 fdoExt->DiskGeometry.SectorsPerTrack);
1871 if (cylinderSize == 0){
1872 DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
1873 "values from %#x/%#x to %#x/%#x\n",
1874 fdoExt->DiskGeometry.TracksPerCylinder,
1875 fdoExt->DiskGeometry.SectorsPerTrack,
1876 0xff,
1877 0x3f));
1878
1879 fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
1880 fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
1881
1882
1883 cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
1884 fdoExt->DiskGeometry.SectorsPerTrack);
1885 }
1886
1887 //
1888 // Calculate number of cylinders.
1889 //
1890
1891 fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/cylinderSize);
1892
1893 //
1894 // if there are zero cylinders, then the device lied AND it's
1895 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
1896 // this can fit into a single LONGLONG, so create another usable
1897 // geometry, even if it's unusual looking. This allows small,
1898 // non-standard devices, such as Sony's Memory Stick, to show
1899 // up as having a partition.
1900 //
1901
1902 if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
1903 fdoExt->DiskGeometry.SectorsPerTrack = 1;
1904 fdoExt->DiskGeometry.TracksPerCylinder = 1;
1905 fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector;
1906 }
1907
1908
1909 //
1910 // Calculate media capacity in bytes.
1911 //
1912
1913 fdoExt->CommonExtension.PartitionLength.QuadPart =
1914 ((LONGLONG)(lastSector + 1)) << fdoExt->SectorShift;
1915
1916 /*
1917 * Is this removable or fixed media
1918 */
1919 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
1920 fdoExt->DiskGeometry.MediaType = RemovableMedia;
1921 }
1922 else {
1923 fdoExt->DiskGeometry.MediaType = FixedMedia;
1924 }
1925 }
1926 else {
1927 /*
1928 * The request failed.
1929 */
1930
1931 //
1932 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
1933 // what happens when the disk's sector size is bigger than
1934 // 512 bytes and we hit this code path? this is untested.
1935 //
1936 // If the read capacity fails, set the geometry to reasonable parameter
1937 // so things don't fail at unexpected places. Zero the geometry
1938 // except for the bytes per sector and sector shift.
1939 //
1940
1941 /*
1942 * This request can sometimes fail legitimately
1943 * (e.g. when a SCSI device is attached but turned off)
1944 * so this is not necessarily a device/driver bug.
1945 */
1946 DBGTRACE(ClassDebugWarning, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo, status));
1947
1948 /*
1949 * Write in a default disk geometry which we HOPE is right (??).
1950 * BUGBUG !!
1951 */
1952 RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
1953 fdoExt->DiskGeometry.BytesPerSector = 512;
1954 fdoExt->SectorShift = 9;
1955 fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
1956
1957 /*
1958 * Is this removable or fixed media
1959 */
1960 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
1961 fdoExt->DiskGeometry.MediaType = RemovableMedia;
1962 }
1963 else {
1964 fdoExt->DiskGeometry.MediaType = FixedMedia;
1965 }
1966 }
1967
1968 }
1969 else {
1970 status = STATUS_INSUFFICIENT_RESOURCES;
1971 }
1972
1973 FreeDeviceInputMdl(driveCapMdl);
1974 }
1975 else {
1976 status = STATUS_INSUFFICIENT_RESOURCES;
1977 }
1978
1979 return status;
1980 }
1981
1982
1983 /*++////////////////////////////////////////////////////////////////////////////
1984
1985 ClassSendStartUnit()
1986
1987 Routine Description:
1988
1989 Send command to SCSI unit to start or power up.
1990 Because this command is issued asynchronounsly, that is, without
1991 waiting on it to complete, the IMMEDIATE flag is not set. This
1992 means that the CDB will not return until the drive has powered up.
1993 This should keep subsequent requests from being submitted to the
1994 device before it has completely spun up.
1995
1996 This routine is called from the InterpretSense routine, when a
1997 request sense returns data indicating that a drive must be
1998 powered up.
1999
2000 This routine may also be called from a class driver's error handler,
2001 or anytime a non-critical start device should be sent to the device.
2002
2003 Arguments:
2004
2005 Fdo - The functional device object for the stopped device.
2006
2007 Return Value:
2008
2009 None.
2010
2011 --*/
2012 VOID
2013 NTAPI
2014 ClassSendStartUnit(
2015 IN PDEVICE_OBJECT Fdo
2016 )
2017 {
2018 PIO_STACK_LOCATION irpStack;
2019 PIRP irp;
2020 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2021 PSCSI_REQUEST_BLOCK srb;
2022 PCOMPLETION_CONTEXT context;
2023 PCDB cdb;
2024
2025 //
2026 // Allocate Srb from nonpaged pool.
2027 //
2028
2029 context = ExAllocatePoolWithTag(NonPagedPool,
2030 sizeof(COMPLETION_CONTEXT),
2031 '6CcS');
2032
2033 if(context == NULL) {
2034
2035 //
2036 // ISSUE-2000/02/03-peterwie
2037 // This code path was inheritted from the NT 4.0 class2.sys driver.
2038 // It needs to be changed to survive low-memory conditions.
2039 //
2040
2041 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
2042 }
2043
2044 //
2045 // Save the device object in the context for use by the completion
2046 // routine.
2047 //
2048
2049 context->DeviceObject = Fdo;
2050 srb = &context->Srb;
2051
2052 //
2053 // Zero out srb.
2054 //
2055
2056 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
2057
2058 //
2059 // Write length to SRB.
2060 //
2061
2062 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2063
2064 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2065
2066 //
2067 // Set timeout value large enough for drive to spin up.
2068 //
2069
2070 srb->TimeOutValue = START_UNIT_TIMEOUT;
2071
2072 //
2073 // Set the transfer length.
2074 //
2075
2076 srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
2077 SRB_FLAGS_DISABLE_AUTOSENSE |
2078 SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
2079
2080 //
2081 // Build the start unit CDB.
2082 //
2083
2084 srb->CdbLength = 6;
2085 cdb = (PCDB)srb->Cdb;
2086
2087 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
2088 cdb->START_STOP.Start = 1;
2089 cdb->START_STOP.Immediate = 0;
2090 cdb->START_STOP.LogicalUnitNumber = srb->Lun;
2091
2092 //
2093 // Build the asynchronous request to be sent to the port driver.
2094 // Since this routine is called from a DPC the IRP should always be
2095 // available.
2096 //
2097
2098 irp = IoAllocateIrp(Fdo->StackSize, FALSE);
2099
2100 if(irp == NULL) {
2101
2102 //
2103 // ISSUE-2000/02/03-peterwie
2104 // This code path was inheritted from the NT 4.0 class2.sys driver.
2105 // It needs to be changed to survive low-memory conditions.
2106 //
2107
2108 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
2109
2110 }
2111
2112 ClassAcquireRemoveLock(Fdo, irp);
2113
2114 IoSetCompletionRoutine(irp,
2115 ClassAsynchronousCompletion,
2116 context,
2117 TRUE,
2118 TRUE,
2119 TRUE);
2120
2121 irpStack = IoGetNextIrpStackLocation(irp);
2122 irpStack->MajorFunction = IRP_MJ_SCSI;
2123 srb->OriginalRequest = irp;
2124
2125 //
2126 // Store the SRB address in next stack for port driver.
2127 //
2128
2129 irpStack->Parameters.Scsi.Srb = srb;
2130
2131 //
2132 // Call the port driver with the IRP.
2133 //
2134
2135 IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
2136
2137 return;
2138
2139 } // end StartUnit()
2140
2141 /*++////////////////////////////////////////////////////////////////////////////
2142
2143 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
2144
2145 Routine Description:
2146
2147 This routine is called when an asynchronous I/O request
2148 which was issused by the class driver completes. Examples of such requests
2149 are release queue or START UNIT. This routine releases the queue if
2150 necessary. It then frees the context and the IRP.
2151
2152 Arguments:
2153
2154 DeviceObject - The device object for the logical unit; however since this
2155 is the top stack location the value is NULL.
2156
2157 Irp - Supplies a pointer to the Irp to be processed.
2158
2159 Context - Supplies the context to be used to process this request.
2160
2161 Return Value:
2162
2163 None.
2164
2165 --*/
2166 NTSTATUS
2167 NTAPI
2168 ClassAsynchronousCompletion(
2169 PDEVICE_OBJECT DeviceObject,
2170 PIRP Irp,
2171 PVOID Context
2172 )
2173 {
2174 PCOMPLETION_CONTEXT context = Context;
2175 PSCSI_REQUEST_BLOCK srb;
2176
2177 if(DeviceObject == NULL) {
2178
2179 DeviceObject = context->DeviceObject;
2180 }
2181
2182 srb = &context->Srb;
2183
2184 //
2185 // If this is an execute srb, then check the return status and make sure.
2186 // the queue is not frozen.
2187 //
2188
2189 if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
2190
2191 //
2192 // Check for a frozen queue.
2193 //
2194
2195 if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
2196
2197 //
2198 // Unfreeze the queue getting the device object from the context.
2199 //
2200
2201 ClassReleaseQueue(context->DeviceObject);
2202 }
2203 }
2204
2205 { // free port-allocated sense buffer if we can detect
2206
2207 if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
2208
2209 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
2210 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2211 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2212 }
2213
2214 } else {
2215
2216 ASSERT(!TEST_FLAG(srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
2217
2218 }
2219 }
2220
2221
2222 //
2223 // Free the context and the Irp.
2224 //
2225
2226 if (Irp->MdlAddress != NULL) {
2227 MmUnlockPages(Irp->MdlAddress);
2228 IoFreeMdl(Irp->MdlAddress);
2229
2230 Irp->MdlAddress = NULL;
2231 }
2232
2233 ClassReleaseRemoveLock(DeviceObject, Irp);
2234
2235 ExFreePool(context);
2236 IoFreeIrp(Irp);
2237
2238 //
2239 // Indicate the I/O system should stop processing the Irp completion.
2240 //
2241
2242 return STATUS_MORE_PROCESSING_REQUIRED;
2243
2244 } // end ClassAsynchronousCompletion()
2245
2246 VOID NTAPI ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp)
2247 {
2248 //PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
2249 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
2250 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
2251 //PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
2252 PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
2253 ULONG entireXferLen = currentSp->Parameters.Read.Length;
2254 PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
2255 LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
2256 PTRANSFER_PACKET pkt;
2257 SINGLE_LIST_ENTRY pktList;
2258 PSINGLE_LIST_ENTRY slistEntry;
2259 ULONG numPackets;
2260 //KIRQL oldIrql;
2261 ULONG i;
2262
2263 /*
2264 * Compute the number of hw xfers we'll have to do.
2265 * Calculate this without allowing for an overflow condition.
2266 */
2267 ASSERT(fdoData->HwMaxXferLen >= PAGE_SIZE);
2268 numPackets = entireXferLen/fdoData->HwMaxXferLen;
2269 if (entireXferLen % fdoData->HwMaxXferLen){
2270 numPackets++;
2271 }
2272
2273 /*
2274 * First get all the TRANSFER_PACKETs that we'll need at once.
2275 * Use our 'simple' slist functions since we don't need interlocked.
2276 */
2277 SimpleInitSlistHdr(&pktList);
2278 for (i = 0; i < numPackets; i++){
2279 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
2280 if (pkt){
2281 SimplePushSlist(&pktList, &pkt->SlistEntry);
2282 }
2283 else {
2284 break;
2285 }
2286 }
2287
2288 if (i == numPackets){
2289 /*
2290 * Initialize the original IRP's status to success.
2291 * If any of the packets fail, they will set it to an error status.
2292 * The IoStatus.Information field will be incremented to the
2293 * transfer length as the pieces complete.
2294 */
2295 Irp->IoStatus.Status = STATUS_SUCCESS;
2296 Irp->IoStatus.Information = 0;
2297
2298 /*
2299 * Store the number of transfer pieces inside the original IRP.
2300 * It will be used to count down the pieces as they complete.
2301 */
2302 Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
2303
2304 /*
2305 * We are proceeding with the transfer.
2306 * Mark the client IRP pending since it may complete on a different thread.
2307 */
2308 IoMarkIrpPending(Irp);
2309
2310 /*
2311 * Transmit the pieces of the transfer.
2312 */
2313 while (entireXferLen > 0){
2314 ULONG thisPieceLen = MIN(fdoData->HwMaxXferLen, entireXferLen);
2315
2316 /*
2317 * Set up a TRANSFER_PACKET for this piece and send it.
2318 */
2319 slistEntry = SimplePopSlist(&pktList);
2320 ASSERT(slistEntry);
2321 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2322 SetupReadWriteTransferPacket( pkt,
2323 bufPtr,
2324 thisPieceLen,
2325 targetLocation,
2326 Irp);
2327 SubmitTransferPacket(pkt);
2328
2329 entireXferLen -= thisPieceLen;
2330 bufPtr += thisPieceLen;
2331 targetLocation.QuadPart += thisPieceLen;
2332 }
2333 ASSERT(SimpleIsSlistEmpty(&pktList));
2334 }
2335 else if (i >= 1){
2336 /*
2337 * We were unable to get all the TRANSFER_PACKETs we need,
2338 * but we did get at least one.
2339 * That means that we are in extreme low-memory stress.
2340 * We'll try doing this transfer using a single packet.
2341 * The port driver is certainly also in stress, so use one-page
2342 * transfers.
2343 */
2344
2345 /*
2346 * Free all but one of the TRANSFER_PACKETs.
2347 */
2348 while (i-- > 1){
2349 slistEntry = SimplePopSlist(&pktList);
2350 ASSERT(slistEntry);
2351 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2352 EnqueueFreeTransferPacket(Fdo, pkt);
2353 }
2354
2355 /*
2356 * Get the single TRANSFER_PACKET that we'll be using.
2357 */
2358 slistEntry = SimplePopSlist(&pktList);
2359 ASSERT(slistEntry);
2360 ASSERT(SimpleIsSlistEmpty(&pktList));
2361 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2362 DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt));
2363
2364 /*
2365 * Set default status and the number of transfer packets (one)
2366 * inside the original irp.
2367 */
2368 Irp->IoStatus.Status = STATUS_SUCCESS;
2369 Irp->IoStatus.Information = 0;
2370 Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
2371
2372 /*
2373 * Mark the client irp pending since it may complete on
2374 * another thread.
2375 */
2376 IoMarkIrpPending(Irp);
2377
2378 /*
2379 * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
2380 */
2381 SetupReadWriteTransferPacket( pkt,
2382 bufPtr,
2383 entireXferLen,
2384 targetLocation,
2385 Irp);
2386 InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
2387 StepLowMemRetry(pkt);
2388 }
2389 else {
2390 /*
2391 * We were unable to get ANY TRANSFER_PACKETs.
2392 * Defer this client irp until some TRANSFER_PACKETs free up.
2393 */
2394 DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp));
2395 IoMarkIrpPending(Irp);
2396 EnqueueDeferredClientIrp(fdoData, Irp);
2397 }
2398
2399 }
2400
2401 /*++////////////////////////////////////////////////////////////////////////////
2402
2403 ClassIoComplete()
2404
2405 Routine Description:
2406
2407 This routine executes when the port driver has completed a request.
2408 It looks at the SRB status in the completing SRB and if not success
2409 it checks for valid request sense buffer information. If valid, the
2410 info is used to update status with more precise message of type of
2411 error. This routine deallocates the SRB.
2412
2413 This routine should only be placed on the stack location for a class
2414 driver FDO.
2415
2416 Arguments:
2417
2418 Fdo - Supplies the device object which represents the logical
2419 unit.
2420
2421 Irp - Supplies the Irp which has completed.
2422
2423 Context - Supplies a pointer to the SRB.
2424
2425 Return Value:
2426
2427 NT status
2428
2429 --*/
2430 NTSTATUS
2431 NTAPI
2432 ClassIoComplete(
2433 IN PDEVICE_OBJECT Fdo,
2434 IN PIRP Irp,
2435 IN PVOID Context
2436 )
2437 {
2438 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
2439 PSCSI_REQUEST_BLOCK srb = Context;
2440 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2441 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
2442 NTSTATUS status;
2443 BOOLEAN retry;
2444 BOOLEAN callStartNextPacket;
2445
2446 ASSERT(fdoExtension->CommonExtension.IsFdo);
2447
2448 //
2449 // Check SRB status for success of completing request.
2450 //
2451
2452 if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
2453 ULONG retryInterval;
2454
2455 DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
2456
2457 //
2458 // Release the queue if it is frozen.
2459 //
2460
2461 if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
2462 ClassReleaseQueue(Fdo);
2463 }
2464
2465 retry = ClassInterpretSenseInfo(
2466 Fdo,
2467 srb,
2468 irpStack->MajorFunction,
2469 irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
2470 irpStack->Parameters.DeviceIoControl.IoControlCode :
2471 0,
2472 MAXIMUM_RETRIES -
2473 ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
2474 &status,
2475 &retryInterval);
2476
2477 //
2478 // If the status is verified required and the this request
2479 // should bypass verify required then retry the request.
2480 //
2481
2482 if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
2483 status == STATUS_VERIFY_REQUIRED) {
2484
2485 status = STATUS_IO_DEVICE_ERROR;
2486 retry = TRUE;
2487 }
2488
2489 if (retry && ((*(PCHAR*)&irpStack->Parameters.Others.Argument4)--)) {
2490
2491 //
2492 // Retry request.
2493 //
2494
2495 DebugPrint((1, "Retry request %p\n", Irp));
2496
2497 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2498 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2499 }
2500
2501 RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
2502 return STATUS_MORE_PROCESSING_REQUIRED;
2503 }
2504
2505 } else {
2506
2507 //
2508 // Set status for successful request
2509 //
2510 fdoData->LoggedTURFailureSinceLastIO = FALSE;
2511 ClasspPerfIncrementSuccessfulIo(fdoExtension);
2512 status = STATUS_SUCCESS;
2513 } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
2514
2515
2516 //
2517 // ensure we have returned some info, and it matches what the
2518 // original request wanted for PAGING operations only
2519 //
2520
2521 if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
2522 ASSERT(Irp->IoStatus.Information != 0);
2523 ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
2524 }
2525
2526 //
2527 // remember if the caller wanted to skip calling IoStartNextPacket.
2528 // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
2529 // calls. this setting only affects device objects with StartIo routines.
2530 //
2531
2532 callStartNextPacket = !TEST_FLAG(srb->SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
2533 if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
2534 callStartNextPacket = FALSE;
2535 }
2536
2537 //
2538 // Free the srb
2539 //
2540
2541 if(!TEST_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
2542
2543 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2544 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2545 }
2546
2547 if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
2548 ClassFreeOrReuseSrb(fdoExtension, srb);
2549 }
2550 else {
2551 DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
2552 ExFreePool(srb);
2553 }
2554
2555 } else {
2556
2557 DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
2558 "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
2559 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2560 DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
2561 " because SRB_CLASS_FLAGS_PERSISTANT set\n",
2562 srb->SenseInfoBuffer));
2563 }
2564
2565 }
2566
2567 //
2568 // Set status in completing IRP.
2569 //
2570
2571 Irp->IoStatus.Status = status;
2572
2573 //
2574 // Set the hard error if necessary.
2575 //
2576
2577 if (!NT_SUCCESS(status) &&
2578 IoIsErrorUserInduced(status) &&
2579 (Irp->Tail.Overlay.Thread != NULL)
2580 ) {
2581
2582 //
2583 // Store DeviceObject for filesystem, and clear
2584 // in IoStatus.Information field.
2585 //
2586
2587 IoSetHardErrorOrVerifyDevice(Irp, Fdo);
2588 Irp->IoStatus.Information = 0;
2589 }
2590
2591 //
2592 // If pending has be returned for this irp then mark the current stack as
2593 // pending.
2594 //
2595
2596 if (Irp->PendingReturned) {
2597 IoMarkIrpPending(Irp);
2598 }
2599
2600 if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
2601 if (callStartNextPacket) {
2602 KIRQL oldIrql;
2603 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
2604 IoStartNextPacket(Fdo, FALSE);
2605 KeLowerIrql(oldIrql);
2606 }
2607 }
2608
2609 ClassReleaseRemoveLock(Fdo, Irp);
2610
2611 return status;
2612
2613 } // end ClassIoComplete()
2614
2615 /*++////////////////////////////////////////////////////////////////////////////
2616
2617 ClassSendSrbSynchronous()
2618
2619 Routine Description:
2620
2621 This routine is called by SCSI device controls to complete an
2622 SRB and send it to the port driver synchronously (ie wait for
2623 completion). The CDB is already completed along with the SRB CDB
2624 size and request timeout value.
2625
2626 Arguments:
2627
2628 Fdo - Supplies the functional device object which represents the target.
2629
2630 Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
2631
2632 BufferAddress - Supplies the address of the buffer.
2633
2634 BufferLength - Supplies the length in bytes of the buffer.
2635
2636 WriteToDevice - Indicates the data should be transfer to the device.
2637
2638 Return Value:
2639
2640 NTSTATUS indicating the final results of the operation.
2641
2642 If NT_SUCCESS(), then the amount of usable data is contained in the field
2643 Srb->DataTransferLength
2644
2645 --*/
2646 NTSTATUS
2647 NTAPI
2648 ClassSendSrbSynchronous(
2649 PDEVICE_OBJECT Fdo,
2650 PSCSI_REQUEST_BLOCK Srb,
2651 PVOID BufferAddress,
2652 ULONG BufferLength,
2653 BOOLEAN WriteToDevice
2654 )
2655 {
2656
2657 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2658 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
2659 IO_STATUS_BLOCK ioStatus;
2660 //ULONG controlType;
2661 PIRP irp;
2662 PIO_STACK_LOCATION irpStack;
2663 KEVENT event;
2664 PUCHAR senseInfoBuffer;
2665 ULONG retryCount = MAXIMUM_RETRIES;
2666 NTSTATUS status;
2667 BOOLEAN retry;
2668
2669 //
2670 // NOTE: This code is only pagable because we are not freezing
2671 // the queue. Allowing the queue to be frozen from a pagable
2672 // routine could leave the queue frozen as we try to page in
2673 // the code to unfreeze the queue. The result would be a nice
2674 // case of deadlock. Therefore, since we are unfreezing the
2675 // queue regardless of the result, just set the NO_FREEZE_QUEUE
2676 // flag in the SRB.
2677 //
2678
2679 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2680 ASSERT(fdoExtension->CommonExtension.IsFdo);
2681
2682 //
2683 // Write length to SRB.
2684 //
2685
2686 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2687
2688 //
2689 // Set SCSI bus address.
2690 //
2691
2692 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2693
2694 //
2695 // Enable auto request sense.
2696 //
2697
2698 Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
2699
2700 //
2701 // Sense buffer is in aligned nonpaged pool.
2702 //
2703 //
2704 senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
2705 SENSE_BUFFER_SIZE,
2706 '7CcS');
2707
2708 if (senseInfoBuffer == NULL) {
2709
2710 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
2711 "buffer\n"));
2712 return(STATUS_INSUFFICIENT_RESOURCES);
2713 }
2714
2715 Srb->SenseInfoBuffer = senseInfoBuffer;
2716 Srb->DataBuffer = BufferAddress;
2717
2718 //
2719 // Start retries here.
2720 //
2721
2722 retry:
2723
2724 //
2725 // use fdoextension's flags by default.
2726 // do not move out of loop, as the flag may change due to errors
2727 // sending this command.
2728 //
2729
2730 Srb->SrbFlags = fdoExtension->SrbFlags;
2731
2732 if(BufferAddress != NULL) {
2733 if(WriteToDevice) {
2734 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
2735 } else {
2736 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
2737 }
2738 }
2739
2740 //
2741 // Initialize the QueueAction field.
2742 //
2743
2744 Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
2745
2746 //
2747 // Disable synchronous transfer for these requests.
2748 // Disable freezing the queue, since all we do is unfreeze it anyways.
2749 //
2750
2751 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
2752 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
2753
2754 //
2755 // Set the event object to the unsignaled state.
2756 // It will be used to signal request completion.
2757 //
2758
2759 KeInitializeEvent(&event, NotificationEvent, FALSE);
2760
2761 //
2762 // Build device I/O control request with METHOD_NEITHER data transfer.
2763 // We'll queue a completion routine to cleanup the MDL's and such ourself.
2764 //
2765
2766 irp = IoAllocateIrp(
2767 (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
2768 FALSE);
2769
2770 if(irp == NULL) {
2771 ExFreePool(senseInfoBuffer);
2772 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
2773 return(STATUS_INSUFFICIENT_RESOURCES);
2774 }
2775
2776 //
2777 // Get next stack location.
2778 //
2779
2780 irpStack = IoGetNextIrpStackLocation(irp);
2781
2782 //
2783 // Set up SRB for execute scsi request. Save SRB address in next stack
2784 // for the port driver.
2785 //
2786
2787 irpStack->MajorFunction = IRP_MJ_SCSI;
2788 irpStack->Parameters.Scsi.Srb = Srb;
2789
2790 IoSetCompletionRoutine(irp,
2791 ClasspSendSynchronousCompletion,
2792 Srb,
2793 TRUE,
2794 TRUE,
2795 TRUE);
2796
2797 irp->UserIosb = &ioStatus;
2798 irp->UserEvent = &event;
2799
2800 if(BufferAddress) {
2801 //
2802 // Build an MDL for the data buffer and stick it into the irp. The
2803 // completion routine will unlock the pages and free the MDL.
2804 //
2805
2806 irp->MdlAddress = IoAllocateMdl( BufferAddress,
2807 BufferLength,
2808 FALSE,
2809 FALSE,
2810 irp );
2811 if (irp->MdlAddress == NULL) {
2812 ExFreePool(senseInfoBuffer);
2813 Srb->SenseInfoBuffer = NULL;
2814 IoFreeIrp( irp );
2815 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
2816 return STATUS_INSUFFICIENT_RESOURCES;
2817 }
2818
2819 _SEH2_TRY {
2820
2821 //
2822 // the io manager unlocks these pages upon completion
2823 //
2824
2825 MmProbeAndLockPages( irp->MdlAddress,
2826 KernelMode,
2827 (WriteToDevice ? IoReadAccess :
2828 IoWriteAccess));
2829
2830 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2831 status = _SEH2_GetExceptionCode();
2832
2833 ExFreePool(senseInfoBuffer);
2834 Srb->SenseInfoBuffer = NULL;
2835 IoFreeMdl(irp->MdlAddress);
2836 IoFreeIrp(irp);
2837
2838 DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
2839 "locking buffer\n", status));
2840 _SEH2_YIELD(return status);
2841 } _SEH2_END;
2842 }
2843
2844 //
2845 // Set the transfer length.
2846 //
2847
2848 Srb->DataTransferLength = BufferLength;
2849
2850 //
2851 // Zero out status.
2852 //
2853
2854 Srb->ScsiStatus = Srb->SrbStatus = 0;
2855 Srb->NextSrb = 0;
2856
2857 //
2858 // Set up IRP Address.
2859 //
2860
2861 Srb->OriginalRequest = irp;
2862
2863 //
2864 // Call the port driver with the request and wait for it to complete.
2865 //
2866
2867 status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
2868
2869 if (status == STATUS_PENDING) {
2870 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
2871 status = ioStatus.Status;
2872 }
2873
2874 //
2875 // Check that request completed without error.
2876 //
2877
2878 if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
2879
2880 ULONG retryInterval;
2881
2882 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)));
2883
2884 //
2885 // assert that the queue is not frozen
2886 //
2887
2888 ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
2889
2890 //
2891 // Update status and determine if request should be retried.
2892 //
2893
2894 retry = ClassInterpretSenseInfo(Fdo,
2895 Srb,
2896 IRP_MJ_SCSI,
2897 0,
2898 MAXIMUM_RETRIES - retryCount,
2899 &status,
2900 &retryInterval);
2901
2902
2903 if (retry) {
2904
2905 if ((status == STATUS_DEVICE_NOT_READY &&
2906 ((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode ==
2907 SCSI_ADSENSE_LUN_NOT_READY) ||
2908 (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
2909
2910 LARGE_INTEGER delay;
2911
2912 //
2913 // Delay for at least 2 seconds.
2914 //
2915
2916 if(retryInterval < 2) {
2917 retryInterval = 2;
2918 }
2919
2920 delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval);
2921
2922 //
2923 // Stall for a while to let the device become ready
2924 //
2925
2926 KeDelayExecutionThread(KernelMode, FALSE, &delay);
2927
2928 }
2929
2930 //
2931 // If retries are not exhausted then retry this operation.
2932 //
2933
2934 if (retryCount--) {
2935
2936 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
2937 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
2938 }
2939
2940 goto retry;
2941 }
2942 }
2943
2944 } else {
2945 fdoData->LoggedTURFailureSinceLastIO = FALSE;
2946 status = STATUS_SUCCESS;
2947 }
2948
2949 //
2950 // required even though we allocated our own, since the port driver may
2951 // have allocated one also
2952 //
2953
2954 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
2955 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
2956 }
2957
2958 Srb->SenseInfoBuffer = NULL;
2959 ExFreePool(senseInfoBuffer);
2960
2961 return status;
2962 }
2963
2964 /*++////////////////////////////////////////////////////////////////////////////
2965
2966 ClassInterpretSenseInfo()
2967
2968 Routine Description:
2969
2970 This routine interprets the data returned from the SCSI
2971 request sense. It determines the status to return in the
2972 IRP and whether this request can be retried.
2973
2974 Arguments:
2975
2976 DeviceObject - Supplies the device object associated with this request.
2977
2978 Srb - Supplies the scsi request block which failed.
2979
2980 MajorFunctionCode - Supplies the function code to be used for logging.
2981
2982 IoDeviceCode - Supplies the device code to be used for logging.
2983
2984 Status - Returns the status for the request.
2985
2986 Return Value:
2987
2988 BOOLEAN TRUE: Drivers should retry this request.
2989 FALSE: Drivers should not retry this request.
2990
2991 --*/
2992 BOOLEAN
2993 NTAPI
2994 ClassInterpretSenseInfo(
2995 IN PDEVICE_OBJECT Fdo,
2996 IN PSCSI_REQUEST_BLOCK Srb,
2997 IN UCHAR MajorFunctionCode,
2998 IN ULONG IoDeviceCode,
2999 IN ULONG RetryCount,
3000 OUT NTSTATUS *Status,
3001 OUT OPTIONAL ULONG *RetryInterval
3002 )
3003 {
3004 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
3005 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
3006
3007 PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
3008
3009 BOOLEAN retry = TRUE;
3010 BOOLEAN logError = FALSE;
3011 BOOLEAN unhandledError = FALSE;
3012 BOOLEAN incrementErrorCount = FALSE;
3013
3014 ULONG badSector = 0;
3015 ULONG uniqueId = 0;
3016
3017 NTSTATUS logStatus;
3018
3019 ULONG readSector;
3020 ULONG index;
3021
3022 ULONG retryInterval = 0;
3023 KIRQL oldIrql;
3024
3025
3026 logStatus = -1;
3027
3028 if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
3029
3030 //
3031 // Log anything remotely incorrect about paging i/o
3032 //
3033
3034 logError = TRUE;
3035 uniqueId = 301;
3036 logStatus = IO_WARNING_PAGING_FAILURE;
3037 }
3038
3039 //
3040 // Check that request sense buffer is valid.
3041 //
3042
3043 ASSERT(fdoExtension->CommonExtension.IsFdo);
3044
3045
3046 //
3047 // must handle the SRB_STATUS_INTERNAL_ERROR case first,
3048 // as it has all the flags set.
3049 //
3050
3051 if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
3052
3053 DebugPrint((ClassDebugSenseInfo,
3054 "ClassInterpretSenseInfo: Internal Error code is %x\n",
3055 Srb->InternalStatus));
3056
3057 retry = FALSE;
3058 *Status = Srb->InternalStatus;
3059
3060 } else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
3061 (Srb->SenseInfoBufferLength >=
3062 offsetof(SENSE_DATA, CommandSpecificInformation))) {
3063
3064 //
3065 // Zero the additional sense code and additional sense code qualifier
3066 // if they were not returned by the device.
3067 //
3068
3069 readSector = senseBuffer->AdditionalSenseLength +
3070 offsetof(SENSE_DATA, AdditionalSenseLength);
3071
3072 if (readSector > Srb->SenseInfoBufferLength) {
3073 readSector = Srb->SenseInfoBufferLength;
3074 }
3075
3076 if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCode)) {
3077 senseBuffer->AdditionalSenseCode = 0;
3078 }
3079
3080 if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCodeQualifier)) {
3081 senseBuffer->AdditionalSenseCodeQualifier = 0;
3082 }
3083
3084 DebugPrint((ClassDebugSenseInfo,
3085 "ClassInterpretSenseInfo: Error code is %x\n",
3086 senseBuffer->ErrorCode));
3087 DebugPrint((ClassDebugSenseInfo,
3088 "ClassInterpretSenseInfo: Sense key is %x\n",
3089 senseBuffer->SenseKey));
3090 DebugPrint((ClassDebugSenseInfo,
3091 "ClassInterpretSenseInfo: Additional sense code is %x\n",
3092 senseBuffer->AdditionalSenseCode));
3093 DebugPrint((ClassDebugSenseInfo,
3094 "ClassInterpretSenseInfo: Additional sense code qualifier "
3095 "is %x\n",
3096 senseBuffer->AdditionalSenseCodeQualifier));
3097
3098
3099 switch (senseBuffer->SenseKey & 0xf) {
3100
3101 case SCSI_SENSE_NOT_READY: {
3102
3103 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3104 "Device not ready\n"));
3105 *Status = STATUS_DEVICE_NOT_READY;
3106
3107 switch (senseBuffer->AdditionalSenseCode) {
3108
3109 case SCSI_ADSENSE_LUN_NOT_READY: {
3110
3111 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3112 "Lun not ready\n"));
3113
3114 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3115
3116 case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
3117 DEVICE_EVENT_BECOMING_READY notReady;
3118
3119 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3120 "Operation In Progress\n"));
3121 retryInterval = NOT_READY_RETRY_INTERVAL;
3122
3123 RtlZeroMemory(&notReady, sizeof(DEVICE_EVENT_BECOMING_READY));
3124 notReady.Version = 1;
3125 notReady.Reason = 2;
3126 notReady.Estimated100msToReady = retryInterval * 10;
3127 ClasspSendNotification(fdoExtension,
3128 &GUID_IO_DEVICE_BECOMING_READY,
3129 sizeof(DEVICE_EVENT_BECOMING_READY),
3130 &notReady);
3131
3132 break;
3133 }
3134
3135 case SCSI_SENSEQ_BECOMING_READY: {
3136 DEVICE_EVENT_BECOMING_READY notReady;
3137
3138 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3139 "In process of becoming ready\n"));
3140 retryInterval = NOT_READY_RETRY_INTERVAL;
3141
3142 RtlZeroMemory(&notReady, sizeof(DEVICE_EVENT_BECOMING_READY));
3143 notReady.Version = 1;
3144 notReady.Reason = 1;
3145 notReady.Estimated100msToReady = retryInterval * 10;
3146 ClasspSendNotification(fdoExtension,
3147 &GUID_IO_DEVICE_BECOMING_READY,
3148 sizeof(DEVICE_EVENT_BECOMING_READY),
3149 &notReady);
3150 break;
3151 }
3152
3153 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
3154 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3155 "Long write in progress\n"));
3156 retry = FALSE;
3157 break;
3158 }
3159
3160 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
3161 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3162 "Manual intervention required\n"));
3163 *Status = STATUS_NO_MEDIA_IN_DEVICE;
3164 retry = FALSE;
3165 break;
3166 }
3167
3168 case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
3169 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3170 "Format in progress\n"));
3171 retry = FALSE;
3172 break;
3173 }
3174
3175 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
3176
3177 if(!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
3178 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
3179
3180 DebugPrint((ClassDebugSenseInfo,
3181 "ClassInterpretSenseInfo: "
3182 "not ready, cause unknown\n"));
3183 /*
3184 Many non-WHQL certified drives (mostly CD-RW) return
3185 this when they have no media instead of the obvious
3186 choice of:
3187
3188 SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
3189
3190 These drives should not pass WHQL certification due
3191 to this discrepency.
3192
3193 */
3194 retry = FALSE;
3195 break;
3196
3197 } else {
3198
3199 //
3200 // Treat this as init command required and fall through.
3201 //
3202 }
3203 }
3204
3205 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
3206 default: {
3207 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3208 "Initializing command required\n"));
3209
3210 //
3211 // This sense code/additional sense code
3212 // combination may indicate that the device
3213 // needs to be started. Send an start unit if this
3214 // is a disk device.
3215 //
3216
3217 if(TEST_FLAG(fdoExtension->DeviceFlags,
3218 DEV_SAFE_START_UNIT) &&
3219 !TEST_FLAG(Srb->SrbFlags,
3220 SRB_CLASS_FLAGS_LOW_PRIORITY)) {
3221 ClassSendStartUnit(Fdo);
3222 }
3223 break;
3224 }
3225
3226
3227 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
3228 break;
3229 }
3230
3231 case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
3232 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3233 "No Media in device.\n"));
3234 *Status = STATUS_NO_MEDIA_IN_DEVICE;
3235 retry = FALSE;
3236
3237 //
3238 // signal MCN that there isn't any media in the device
3239 //
3240 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
3241 DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
3242 "No Media in a non-removable device %p\n",
3243 Fdo));
3244 }
3245 ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
3246
3247 break;
3248 }
3249 } // end switch (senseBuffer->AdditionalSenseCode)
3250
3251 break;
3252 } // end SCSI_SENSE_NOT_READY
3253
3254 case SCSI_SENSE_DATA_PROTECT: {
3255 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3256 "Media write protected\n"));
3257 *Status = STATUS_MEDIA_WRITE_PROTECTED;
3258 retry = FALSE;
3259 break;
3260 } // end SCSI_SENSE_DATA_PROTECT
3261
3262 case SCSI_SENSE_MEDIUM_ERROR: {
3263 DebugPrint((ClassDebugSenseInfo,"ClassInterpretSenseInfo: "
3264 "Medium Error (bad block)\n"));
3265 *Status = STATUS_DEVICE_DATA_ERROR;
3266
3267 retry = FALSE;
3268 logError = TRUE;
3269 uniqueId = 256;
3270 logStatus = IO_ERR_BAD_BLOCK;
3271
3272 //
3273 // Check if this error is due to unknown format
3274 //
3275 if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA){
3276
3277 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3278
3279 case SCSI_SENSEQ_UNKNOWN_FORMAT: {
3280
3281 *Status = STATUS_UNRECOGNIZED_MEDIA;
3282
3283 //
3284 // Log error only if this is a paging request
3285 //
3286 if(!TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
3287 logError = FALSE;
3288 }
3289 break;
3290 }
3291
3292 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
3293
3294 *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
3295 logError = FALSE;
3296 break;
3297
3298 }
3299 default: {
3300 break;
3301 }
3302 } // end switch AdditionalSenseCodeQualifier
3303
3304 } // end SCSI_ADSENSE_INVALID_MEDIA
3305
3306 break;
3307
3308 } // end SCSI_SENSE_MEDIUM_ERROR
3309
3310 case SCSI_SENSE_HARDWARE_ERROR: {
3311 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3312 "Hardware error\n"));
3313 *Status = STATUS_IO_DEVICE_ERROR;
3314 logError = TRUE;
3315 uniqueId = 257;
3316 logStatus = IO_ERR_CONTROLLER_ERROR;
3317 break;
3318 } // end SCSI_SENSE_HARDWARE_ERROR
3319
3320 case SCSI_SENSE_ILLEGAL_REQUEST: {
3321
3322 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3323 "Illegal SCSI request\n"));
3324 *Status = STATUS_INVALID_DEVICE_REQUEST;
3325 retry = FALSE;
3326
3327 switch (senseBuffer->AdditionalSenseCode) {
3328
3329 case SCSI_ADSENSE_ILLEGAL_COMMAND: {
3330 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3331 "Illegal command\n"));
3332 break;
3333 }
3334
3335 case SCSI_ADSENSE_ILLEGAL_BLOCK: {
3336 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3337 "Illegal block address\n"));
3338 *Status = STATUS_NONEXISTENT_SECTOR;
3339 break;
3340 }
3341
3342 case SCSI_ADSENSE_INVALID_LUN: {
3343 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3344 "Invalid LUN\n"));
3345 *Status = STATUS_NO_SUCH_DEVICE;
3346 break;
3347 }
3348
3349 case SCSI_ADSENSE_MUSIC_AREA: {
3350 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3351 "Music area\n"));
3352 break;
3353 }
3354
3355 case SCSI_ADSENSE_DATA_AREA: {
3356 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3357 "Data area\n"));
3358 break;
3359 }
3360
3361 case SCSI_ADSENSE_VOLUME_OVERFLOW: {
3362 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3363 "Volume overflow\n"));
3364 break;
3365 }
3366
3367 case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
3368 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3369 "Copy protection failure\n"));
3370
3371 *Status = STATUS_COPY_PROTECTION_FAILURE;
3372
3373 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3374 case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
3375 DebugPrint((ClassDebugSenseInfo,
3376 "ClassInterpretSenseInfo: "
3377 "Authentication failure\n"));
3378 *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
3379 break;
3380 case SCSI_SENSEQ_KEY_NOT_PRESENT:
3381 DebugPrint((ClassDebugSenseInfo,
3382 "ClassInterpretSenseInfo: "
3383 "Key not present\n"));
3384 *Status = STATUS_CSS_KEY_NOT_PRESENT;
3385 break;
3386 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
3387 DebugPrint((ClassDebugSenseInfo,
3388 "ClassInterpretSenseInfo: "
3389 "Key not established\n"));
3390 *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
3391 break;
3392 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
3393 DebugPrint((ClassDebugSenseInfo,
3394 "ClassInterpretSenseInfo: "
3395 "Read of scrambled sector w/o "
3396 "authentication\n"));
3397 *Status = STATUS_CSS_SCRAMBLED_SECTOR;
3398 break;
3399 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
3400 DebugPrint((ClassDebugSenseInfo,
3401 "ClassInterpretSenseInfo: "
3402 "Media region does not logical unit "
3403 "region\n"));
3404 *Status = STATUS_CSS_REGION_MISMATCH;
3405 break;
3406 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
3407 DebugPrint((ClassDebugSenseInfo,
3408 "ClassInterpretSenseInfo: "
3409 "Region set error -- region may "
3410 "be permanent\n"));
3411 *Status = STATUS_CSS_RESETS_EXHAUSTED;
3412 break;
3413 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
3414
3415 break;
3416 }
3417
3418
3419 case SCSI_ADSENSE_INVALID_CDB: {
3420 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3421 "Invalid CDB\n"));
3422
3423 //
3424 // Note: the retry interval is not typically used.
3425 // it is set here only because a ClassErrorHandler
3426 // cannot set the retryInterval, and the error may
3427 // require a few commands to be sent to clear whatever
3428 // caused this condition (i.e. disk clears the write
3429 // cache, requiring at least two commands)
3430 //
3431 // hopefully, this shortcoming can be changed for
3432 // blackcomb.
3433 //
3434
3435 retryInterval = 3;
3436 break;
3437 }
3438
3439 } // end switch (senseBuffer->AdditionalSenseCode)
3440
3441 break;
3442 } // end SCSI_SENSE_ILLEGAL_REQUEST
3443
3444 case SCSI_SENSE_UNIT_ATTENTION: {
3445
3446 //PVPB vpb;
3447 ULONG count;
3448
3449 //
3450 // A media change may have occured so increment the change
3451 // count for the physical device
3452 //
3453
3454 count =</