[DISK]
[reactos.git] / reactos / drivers / storage / class / disk / disk.c
1 /*
2 * PROJECT: ReactOS Storage Stack
3 * LICENSE: DDK - see license.txt in the root dir
4 * FILE: drivers/storage/disk/disk.c
5 * PURPOSE: Disk class driver
6 * PROGRAMMERS: Based on a source code sample from Microsoft NT4 DDK
7 */
8
9 #include <ntddk.h>
10 #include <ntdddisk.h>
11 #include <scsi.h>
12 #include <ntddscsi.h>
13 #include <mountdev.h>
14 #include <mountmgr.h>
15 #include <ntiologc.h>
16 #include <include/class2.h>
17 #include <stdio.h>
18
19 #define NDEBUG
20 #include <debug.h>
21
22
23 #ifdef POOL_TAGGING
24 #ifdef ExAllocatePool
25 #undef ExAllocatePool
26 #endif
27 #define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'DscS')
28 #endif
29
30 typedef enum {
31 NotInitialized,
32 Initializing,
33 Initialized
34 } PARTITION_LIST_STATE;
35
36 //
37 // Disk device data
38 //
39
40 typedef struct _DISK_DATA {
41
42 //
43 // Partition chain
44 //
45
46 PDEVICE_EXTENSION NextPartition;
47
48 //
49 // Disk signature (from MBR)
50 //
51
52 ULONG Signature;
53
54 //
55 // MBR checksum
56 //
57
58 ULONG MbrCheckSum;
59
60 //
61 // Number of hidden sectors for BPB.
62 //
63
64 ULONG HiddenSectors;
65
66 //
67 // Partition number of this device object
68 //
69 // This field is set during driver initialization or when the partition
70 // is created to identify a parition to the system.
71 //
72
73 ULONG PartitionNumber;
74
75 //
76 // This field is the ordinal of a partition as it appears on a disk.
77 //
78
79 ULONG PartitionOrdinal;
80
81 //
82 // Partition type of this device object
83 //
84 // This field is set by:
85 //
86 // 1) Initially set according to the partition list entry partition
87 // type returned by IoReadPartitionTable.
88 //
89 // 2) Subsequently set by the IOCTL_DISK_SET_PARTITION_INFORMATION
90 // I/O control function when IoSetPartitionInformation function
91 // successfully updates the partition type on the disk.
92 //
93
94 UCHAR PartitionType;
95
96 //
97 // Boot indicator - indicates whether this partition is a bootable (active)
98 // partition for this device
99 //
100 // This field is set according to the partition list entry boot indicator
101 // returned by IoReadPartitionTable.
102 //
103
104 BOOLEAN BootIndicator;
105
106 //
107 // DriveNotReady - inidicates that the this device is currenly not ready
108 // because there is no media in the device.
109 //
110
111 BOOLEAN DriveNotReady;
112
113 //
114 // State of PartitionList initialization
115 //
116
117 PARTITION_LIST_STATE PartitionListState;
118
119 } DISK_DATA, *PDISK_DATA;
120
121 //
122 // Define a general structure of identfing disk controllers with bad
123 // hardware.
124 //
125
126 typedef struct _BAD_CONTROLLER_INFORMATION {
127 PCHAR InquiryString;
128 BOOLEAN DisableTaggedQueuing;
129 BOOLEAN DisableSynchronousTransfers;
130 BOOLEAN DisableDisconnects;
131 BOOLEAN DisableWriteCache;
132 }BAD_CONTROLLER_INFORMATION, *PBAD_CONTROLLER_INFORMATION;
133
134 BAD_CONTROLLER_INFORMATION const ScsiDiskBadControllers[] = {
135 { "TOSHIBA MK538FB 60", TRUE, FALSE, FALSE, FALSE },
136 { "CONNER CP3500", FALSE, TRUE, FALSE, FALSE },
137 { "OLIVETTICP3500", FALSE, TRUE, FALSE, FALSE },
138 { "SyQuest SQ5110 CHC", TRUE, TRUE, FALSE, FALSE },
139 { "SEAGATE ST41601N 0102", FALSE, TRUE, FALSE, FALSE },
140 { "SEAGATE ST3655N", FALSE, FALSE, FALSE, TRUE },
141 { "SEAGATE ST3390N", FALSE, FALSE, FALSE, TRUE },
142 { "SEAGATE ST12550N", FALSE, FALSE, FALSE, TRUE },
143 { "SEAGATE ST32430N", FALSE, FALSE, FALSE, TRUE },
144 { "SEAGATE ST31230N", FALSE, FALSE, FALSE, TRUE },
145 { "SEAGATE ST15230N", FALSE, FALSE, FALSE, TRUE },
146 { "FUJITSU M2652S-512", TRUE, FALSE, FALSE, FALSE },
147 { "MAXTOR MXT-540SL I1.2", TRUE, FALSE, FALSE, FALSE },
148 { "COMPAQ PD-1", FALSE, TRUE, FALSE, FALSE }
149 };
150
151
152 #define NUMBER_OF_BAD_CONTROLLERS (sizeof(ScsiDiskBadControllers) / sizeof(BAD_CONTROLLER_INFORMATION))
153 #define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + sizeof(DISK_DATA)
154
155 #define MODE_DATA_SIZE 192
156 #define VALUE_BUFFER_SIZE 2048
157 #define SCSI_DISK_TIMEOUT 10
158 #define PARTITION0_LIST_SIZE 4
159
160 \f
161 NTSTATUS
162 NTAPI
163 DriverEntry(
164 IN PDRIVER_OBJECT DriverObject,
165 IN PUNICODE_STRING RegistryPath
166 );
167
168 BOOLEAN
169 NTAPI
170 ScsiDiskDeviceVerification(
171 IN PINQUIRYDATA InquiryData
172 );
173
174 BOOLEAN
175 NTAPI
176 FindScsiDisks(
177 IN PDRIVER_OBJECT DriveObject,
178 IN PUNICODE_STRING RegistryPath,
179 IN PCLASS_INIT_DATA InitializationData,
180 IN PDEVICE_OBJECT PortDeviceObject,
181 IN ULONG PortNumber
182 );
183
184 NTSTATUS
185 NTAPI
186 ScsiDiskCreateClose (
187 IN PDEVICE_OBJECT DeviceObject,
188 IN PIRP Irp
189 );
190
191 NTSTATUS
192 NTAPI
193 ScsiDiskReadWriteVerification(
194 IN PDEVICE_OBJECT DeviceObject,
195 IN PIRP Irp
196 );
197
198 NTSTATUS
199 NTAPI
200 ScsiDiskDeviceControl(
201 IN PDEVICE_OBJECT DeviceObject,
202 IN PIRP Irp
203 );
204
205 VOID
206 NTAPI
207 ScsiDiskProcessError(
208 PDEVICE_OBJECT DeviceObject,
209 PSCSI_REQUEST_BLOCK Srb,
210 NTSTATUS *Status,
211 BOOLEAN *Retry
212 );
213
214 NTSTATUS
215 NTAPI
216 ScsiDiskShutdownFlush(
217 IN PDEVICE_OBJECT DeviceObject,
218 IN PIRP Irp
219 );
220
221 VOID
222 NTAPI
223 DisableWriteCache(
224 IN PDEVICE_OBJECT DeviceObject,
225 IN PSCSI_INQUIRY_DATA LunInfo
226 );
227
228 BOOLEAN
229 NTAPI
230 ScsiDiskModeSelect(
231 IN PDEVICE_OBJECT DeviceObject,
232 IN PCHAR ModeSelectBuffer,
233 IN ULONG Length,
234 IN BOOLEAN SavePage
235 );
236
237 BOOLEAN
238 NTAPI
239 IsFloppyDevice(
240 IN PDEVICE_OBJECT DeviceObject
241 );
242
243 BOOLEAN
244 NTAPI
245 CalculateMbrCheckSum(
246 IN PDEVICE_EXTENSION DeviceExtension,
247 OUT PULONG Checksum
248 );
249
250 BOOLEAN
251 NTAPI
252 EnumerateBusKey(
253 IN PDEVICE_EXTENSION DeviceExtension,
254 HANDLE BusKey,
255 PULONG DiskNumber
256 );
257
258 VOID
259 NTAPI
260 UpdateGeometry(
261 IN PDEVICE_EXTENSION DeviceExtension
262 );
263
264 NTSTATUS
265 NTAPI
266 UpdateRemovableGeometry (
267 IN PDEVICE_OBJECT DeviceObject,
268 IN PIRP Irp
269 );
270
271 NTSTATUS
272 NTAPI
273 CreateDiskDeviceObject(
274 IN PDRIVER_OBJECT DriverObject,
275 IN PUNICODE_STRING RegistryPath,
276 IN PDEVICE_OBJECT PortDeviceObject,
277 IN ULONG PortNumber,
278 IN PULONG DeviceCount,
279 IN PIO_SCSI_CAPABILITIES PortCapabilities,
280 IN PSCSI_INQUIRY_DATA LunInfo,
281 IN PCLASS_INIT_DATA InitData
282 );
283
284 NTSTATUS
285 NTAPI
286 CreatePartitionDeviceObjects(
287 IN PDEVICE_OBJECT PhysicalDeviceObject,
288 IN PUNICODE_STRING RegistryPath
289 );
290
291 VOID
292 NTAPI
293 UpdateDeviceObjects(
294 IN PDEVICE_OBJECT DeviceObject,
295 IN PIRP Irp
296 );
297
298 VOID
299 NTAPI
300 ScanForSpecial(
301 PDEVICE_OBJECT DeviceObject,
302 PSCSI_INQUIRY_DATA LunInfo,
303 PIO_SCSI_CAPABILITIES PortCapabilities
304 );
305
306 VOID
307 NTAPI
308 ResetScsiBus(
309 IN PDEVICE_OBJECT DeviceObject
310 );
311
312 NTSTATUS
313 NTAPI
314 ScsiDiskFileSystemControl(PDEVICE_OBJECT DeviceObject,
315 PIRP Irp);
316
317 #ifdef ALLOC_PRAGMA
318 #pragma alloc_text(PAGE, DriverEntry)
319 #pragma alloc_text(PAGE, FindScsiDisks)
320 #pragma alloc_text(PAGE, CreateDiskDeviceObject)
321 #pragma alloc_text(PAGE, CalculateMbrCheckSum)
322 #pragma alloc_text(PAGE, EnumerateBusKey)
323 #pragma alloc_text(PAGE, UpdateGeometry)
324 #pragma alloc_text(PAGE, IsFloppyDevice)
325 #pragma alloc_text(PAGE, ScanForSpecial)
326 #pragma alloc_text(PAGE, ScsiDiskDeviceControl)
327 #pragma alloc_text(PAGE, ScsiDiskModeSelect)
328 #endif
329
330 \f
331 NTSTATUS
332 NTAPI
333 DriverEntry(
334 IN PDRIVER_OBJECT DriverObject,
335 IN PUNICODE_STRING RegistryPath
336 )
337
338 /*++
339
340 Routine Description:
341
342 This routine initializes the SCSI hard disk class driver.
343
344 Arguments:
345
346 DriverObject - Pointer to driver object created by system.
347
348 RegistryPath - Pointer to the name of the services node for this driver.
349
350 Return Value:
351
352 The function value is the final status from the initialization operation.
353
354 --*/
355
356 {
357 CLASS_INIT_DATA InitializationData;
358
359 //
360 // Zero InitData
361 //
362
363 RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
364
365 //
366 // Set sizes
367 //
368
369 InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
370 InitializationData.DeviceExtensionSize = DEVICE_EXTENSION_SIZE;
371
372 InitializationData.DeviceType = FILE_DEVICE_DISK;
373 InitializationData.DeviceCharacteristics = 0;
374
375 //
376 // Set entry points
377 //
378
379 InitializationData.ClassError = ScsiDiskProcessError;
380 InitializationData.ClassReadWriteVerification = ScsiDiskReadWriteVerification;
381 InitializationData.ClassFindDevices = FindScsiDisks;
382 InitializationData.ClassFindDeviceCallBack = ScsiDiskDeviceVerification;
383 InitializationData.ClassDeviceControl = ScsiDiskDeviceControl;
384 InitializationData.ClassShutdownFlush = ScsiDiskShutdownFlush;
385 InitializationData.ClassCreateClose = NULL;
386
387 //
388 // Call the class init routine
389 //
390
391 return ScsiClassInitialize( DriverObject, RegistryPath, &InitializationData);
392
393 } // end DriverEntry()
394
395
396 \f
397 BOOLEAN
398 NTAPI
399 ScsiDiskDeviceVerification(
400 IN PINQUIRYDATA InquiryData
401 )
402
403 /*++
404
405 Routine Description:
406
407 This routine checks InquiryData for the correct device type and qualifier.
408
409 Arguments:
410
411 InquiryData - Pointer to the inquiry data for the device in question.
412
413 Return Value:
414
415 True is returned if the correct device type is found.
416
417 --*/
418 {
419
420 if (((InquiryData->DeviceType == DIRECT_ACCESS_DEVICE) ||
421 (InquiryData->DeviceType == OPTICAL_DEVICE)) &&
422 InquiryData->DeviceTypeQualifier == 0) {
423
424 return TRUE;
425
426 } else {
427 return FALSE;
428 }
429 }
430
431 \f
432 BOOLEAN
433 NTAPI
434 FindScsiDisks(
435 IN PDRIVER_OBJECT DriverObject,
436 IN PUNICODE_STRING RegistryPath,
437 IN PCLASS_INIT_DATA InitializationData,
438 IN PDEVICE_OBJECT PortDeviceObject,
439 IN ULONG PortNumber
440 )
441
442 /*++
443
444 Routine Description:
445
446 This routine gets a port drivers capabilities, obtains the
447 inquiry data, searches the SCSI bus for the port driver and creates
448 the device objects for the disks found.
449
450 Arguments:
451
452 DriverObject - Pointer to driver object created by system.
453
454 PortDeviceObject - Device object use to send requests to port driver.
455
456 PortNumber - Number for port driver. Used to pass on to
457 CreateDiskDeviceObjects() and create device objects.
458
459 Return Value:
460
461 True is returned if one disk was found and successfully created.
462
463 --*/
464
465 {
466 PIO_SCSI_CAPABILITIES portCapabilities;
467 PULONG diskCount;
468 PCONFIGURATION_INFORMATION configurationInformation;
469 PCHAR buffer;
470 PSCSI_INQUIRY_DATA lunInfo;
471 PSCSI_ADAPTER_BUS_INFO adapterInfo;
472 PINQUIRYDATA inquiryData;
473 ULONG scsiBus;
474 ULONG adapterDisk;
475 NTSTATUS status;
476 BOOLEAN foundOne = FALSE;
477
478 PAGED_CODE();
479
480 //
481 // Call port driver to get adapter capabilities.
482 //
483
484 status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities);
485
486 if (!NT_SUCCESS(status)) {
487 DebugPrint((1,"FindScsiDevices: ScsiClassGetCapabilities failed\n"));
488 return(FALSE);
489 }
490
491 //
492 // Call port driver to get inquiry information to find disks.
493 //
494
495 status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer);
496
497 if (!NT_SUCCESS(status)) {
498 DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n"));
499 return(FALSE);
500 }
501
502 //
503 // Do a quick scan of the devices on this adapter to determine how many
504 // disks are on this adapter. This is used to determine the number of
505 // SRB zone elements to allocate.
506 //
507
508 adapterDisk = 0;
509 adapterInfo = (PVOID) buffer;
510
511 adapterDisk = ScsiClassFindUnclaimedDevices(InitializationData, adapterInfo);
512
513 //
514 // Allocate a zone of SRB for disks on this adapter.
515 //
516
517 if (adapterDisk == 0) {
518
519 //
520 // No free disks were found.
521 //
522
523 return(FALSE);
524 }
525
526 //
527 // Get the number of disks already initialized.
528 //
529
530 configurationInformation = IoGetConfigurationInformation();
531 diskCount = &configurationInformation->DiskCount;
532
533 //
534 // For each SCSI bus this adapter supports ...
535 //
536
537 for (scsiBus=0; scsiBus < (ULONG)adapterInfo->NumberOfBuses; scsiBus++) {
538
539 //
540 // Get the SCSI bus scan data for this bus.
541 //
542
543 lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
544
545 //
546 // Search list for unclaimed disk devices.
547 //
548
549 while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {
550
551 inquiryData = (PVOID)lunInfo->InquiryData;
552
553 if (((inquiryData->DeviceType == DIRECT_ACCESS_DEVICE) ||
554 (inquiryData->DeviceType == OPTICAL_DEVICE)) &&
555 inquiryData->DeviceTypeQualifier == 0 &&
556 (!lunInfo->DeviceClaimed)) {
557
558 DebugPrint((1,
559 "FindScsiDevices: Vendor string is %.24s\n",
560 inquiryData->VendorId));
561
562 //
563 // Create device objects for disk
564 //
565
566 status = CreateDiskDeviceObject(DriverObject,
567 RegistryPath,
568 PortDeviceObject,
569 PortNumber,
570 diskCount,
571 portCapabilities,
572 lunInfo,
573 InitializationData);
574
575 if (NT_SUCCESS(status)) {
576
577 //
578 // Increment system disk device count.
579 //
580
581 (*diskCount)++;
582 foundOne = TRUE;
583
584 }
585 }
586
587 //
588 // Get next LunInfo.
589 //
590
591 if (lunInfo->NextInquiryDataOffset == 0) {
592 break;
593 }
594
595 lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset);
596
597 }
598 }
599
600 //
601 // Buffer is allocated by ScsiClassGetInquiryData and must be free returning.
602 //
603
604 ExFreePool(buffer);
605
606 return(foundOne);
607
608 } // end FindScsiDisks()
609
610 \f
611 NTSTATUS
612 NTAPI
613 CreateDiskDeviceObject(
614 IN PDRIVER_OBJECT DriverObject,
615 IN PUNICODE_STRING RegistryPath,
616 IN PDEVICE_OBJECT PortDeviceObject,
617 IN ULONG PortNumber,
618 IN PULONG DeviceCount,
619 IN PIO_SCSI_CAPABILITIES PortCapabilities,
620 IN PSCSI_INQUIRY_DATA LunInfo,
621 IN PCLASS_INIT_DATA InitData
622 )
623
624 /*++
625
626 Routine Description:
627
628 This routine creates an object for the physical device and then searches
629 the device for partitions and creates an object for each partition.
630
631 Arguments:
632
633 DriverObject - Pointer to driver object created by system.
634
635 PortDeviceObject - Miniport device object.
636
637 PortNumber - port number. Used in creating disk objects.
638
639 DeviceCount - Number of previously installed devices.
640
641 PortCapabilities - Capabilities of this SCSI port.
642
643 LunInfo - LUN specific information.
644
645 Return Value:
646
647 NTSTATUS
648
649 --*/
650 {
651 CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
652 STRING ntNameString;
653 UNICODE_STRING ntUnicodeString;
654 OBJECT_ATTRIBUTES objectAttributes;
655 HANDLE handle;
656 NTSTATUS status;
657 PDEVICE_OBJECT deviceObject = NULL;
658 //PDEVICE_OBJECT physicalDevice;
659 PDISK_GEOMETRY_EX diskGeometry = NULL;
660 PDEVICE_EXTENSION deviceExtension = NULL;
661 //PDEVICE_EXTENSION physicalDeviceExtension;
662 UCHAR pathId = LunInfo->PathId;
663 UCHAR targetId = LunInfo->TargetId;
664 UCHAR lun = LunInfo->Lun;
665 //BOOLEAN writeCache;
666 PVOID senseData = NULL;
667 //ULONG srbFlags;
668 ULONG timeOut = 0;
669 BOOLEAN srbListInitialized = FALSE;
670
671
672 PAGED_CODE();
673
674 //
675 // Set up an object directory to contain the objects for this
676 // device and all its partitions.
677 //
678
679 sprintf(ntNameBuffer,
680 "\\Device\\Harddisk%lu",
681 *DeviceCount);
682
683 RtlInitString(&ntNameString,
684 ntNameBuffer);
685
686 status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
687 &ntNameString,
688 TRUE);
689
690 if (!NT_SUCCESS(status)) {
691 return(status);
692 }
693
694 InitializeObjectAttributes(&objectAttributes,
695 &ntUnicodeString,
696 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
697 NULL,
698 NULL);
699
700 status = ZwCreateDirectoryObject(&handle,
701 DIRECTORY_ALL_ACCESS,
702 &objectAttributes);
703
704 RtlFreeUnicodeString(&ntUnicodeString);
705
706 if (!NT_SUCCESS(status)) {
707
708 DebugPrint((1,
709 "CreateDiskDeviceObjects: Could not create directory %s\n",
710 ntNameBuffer));
711
712 return(status);
713 }
714
715 //
716 // Claim the device.
717 //
718
719 status = ScsiClassClaimDevice(PortDeviceObject,
720 LunInfo,
721 FALSE,
722 &PortDeviceObject);
723
724 if (!NT_SUCCESS(status)) {
725 ZwMakeTemporaryObject(handle);
726 ZwClose(handle);
727 return status;
728 }
729
730 //
731 // Create a device object for this device. Each physical disk will
732 // have at least one device object. The required device object
733 // describes the entire device. Its directory path is
734 // \Device\HarddiskN\Partition0, where N = device number.
735 //
736
737 sprintf(ntNameBuffer,
738 "\\Device\\Harddisk%lu\\Partition0",
739 *DeviceCount);
740
741
742 status = ScsiClassCreateDeviceObject(DriverObject,
743 ntNameBuffer,
744 NULL,
745 &deviceObject,
746 InitData);
747
748 if (!NT_SUCCESS(status)) {
749
750 DebugPrint((1,
751 "CreateDiskDeviceObjects: Can not create device object %s\n",
752 ntNameBuffer));
753
754 goto CreateDiskDeviceObjectsExit;
755 }
756
757 //
758 // Indicate that IRPs should include MDLs for data transfers.
759 //
760
761 deviceObject->Flags |= DO_DIRECT_IO;
762
763 //
764 // Check if this is during initialization. If not indicate that
765 // system initialization already took place and this disk is ready
766 // to be accessed.
767 //
768
769 if (!RegistryPath) {
770 deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
771 }
772
773 //
774 // Check for removable media support.
775 //
776
777 if (((PINQUIRYDATA)LunInfo->InquiryData)->RemovableMedia) {
778 deviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
779 }
780
781 //
782 // Set up required stack size in device object.
783 //
784
785 deviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1;
786
787 deviceExtension = deviceObject->DeviceExtension;
788
789 //
790 // Allocate spinlock for split request completion.
791 //
792
793 KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
794
795 //
796 // Initialize lock count to zero. The lock count is used to
797 // disable the ejection mechanism on devices that support
798 // removable media. Only the lock count in the physical
799 // device extension is used.
800 //
801
802 deviceExtension->LockCount = 0;
803
804 //
805 // Save system disk number.
806 //
807
808 deviceExtension->DeviceNumber = *DeviceCount;
809
810 //
811 // Copy port device object pointer to the device extension.
812 //
813
814 deviceExtension->PortDeviceObject = PortDeviceObject;
815
816 //
817 // Set the alignment requirements for the device based on the
818 // host adapter requirements
819 //
820
821 if (PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) {
822 deviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
823 }
824
825 //
826 // This is the physical device object.
827 //
828
829 //physicalDevice = deviceObject;
830 //physicalDeviceExtension = deviceExtension;
831
832 //
833 // Save address of port driver capabilities.
834 //
835
836 deviceExtension->PortCapabilities = PortCapabilities;
837
838 //
839 // Build the lookaside list for srb's for the physical disk. Should only
840 // need a couple.
841 //
842
843 ScsiClassInitializeSrbLookasideList(deviceExtension,
844 PARTITION0_LIST_SIZE);
845
846 srbListInitialized = TRUE;
847
848 //
849 // Initialize the srb flags.
850 //
851
852 if (((PINQUIRYDATA)LunInfo->InquiryData)->CommandQueue &&
853 PortCapabilities->TaggedQueuing) {
854
855 deviceExtension->SrbFlags = SRB_FLAGS_QUEUE_ACTION_ENABLE;
856
857 } else {
858
859 deviceExtension->SrbFlags = 0;
860
861 }
862
863 //
864 // Allow queued requests if this is not removable media.
865 //
866
867 if (!(deviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
868
869 deviceExtension->SrbFlags |= SRB_FLAGS_NO_QUEUE_FREEZE;
870
871 }
872
873 //
874 // Look for controller that require special flags.
875 //
876
877 ScanForSpecial(deviceObject,
878 LunInfo,
879 PortCapabilities);
880
881 //srbFlags = deviceExtension->SrbFlags;
882
883 //
884 // Allocate buffer for drive geometry.
885 //
886
887 diskGeometry = ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY_EX));
888
889 if (diskGeometry == NULL) {
890
891 DebugPrint((1,
892 "CreateDiskDeviceObjects: Can not allocate disk geometry buffer\n"));
893 status = STATUS_INSUFFICIENT_RESOURCES;
894 goto CreateDiskDeviceObjectsExit;
895 }
896
897 deviceExtension->DiskGeometry = diskGeometry;
898
899 //
900 // Allocate request sense buffer.
901 //
902
903 senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
904
905 if (senseData == NULL) {
906
907 //
908 // The buffer can not be allocated.
909 //
910
911 DebugPrint((1,
912 "CreateDiskDeviceObjects: Can not allocate request sense buffer\n"));
913
914 status = STATUS_INSUFFICIENT_RESOURCES;
915 goto CreateDiskDeviceObjectsExit;
916 }
917
918 //
919 // Set the sense data pointer in the device extension.
920 //
921
922 deviceExtension->SenseData = senseData;
923
924 //
925 // Physical device object will describe the entire
926 // device, starting at byte offset 0.
927 //
928
929 deviceExtension->StartingOffset.QuadPart = (LONGLONG)(0);
930
931 //
932 // TargetId/LUN describes a device location on the SCSI bus.
933 // This information comes from the inquiry buffer.
934 //
935
936 deviceExtension->PortNumber = (UCHAR)PortNumber;
937 deviceExtension->PathId = pathId;
938 deviceExtension->TargetId = targetId;
939 deviceExtension->Lun = lun;
940
941 //
942 // Set timeout value in seconds.
943 //
944
945 timeOut = ScsiClassQueryTimeOutRegistryValue(RegistryPath);
946 if (timeOut) {
947 deviceExtension->TimeOutValue = timeOut;
948 } else {
949 deviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
950 }
951
952 //
953 // Back pointer to device object.
954 //
955
956 deviceExtension->DeviceObject = deviceObject;
957
958 //
959 // If this is a removable device, then make sure it is not a floppy.
960 // Perform a mode sense command to determine the media type. Note
961 // IsFloppyDevice also checks for write cache enabled.
962 //
963
964 if (IsFloppyDevice(deviceObject) && deviceObject->Characteristics & FILE_REMOVABLE_MEDIA &&
965 (((PINQUIRYDATA)LunInfo->InquiryData)->DeviceType == DIRECT_ACCESS_DEVICE)) {
966
967 status = STATUS_NO_SUCH_DEVICE;
968 goto CreateDiskDeviceObjectsExit;
969 }
970
971 DisableWriteCache(deviceObject,LunInfo);
972
973 //writeCache = deviceExtension->DeviceFlags & DEV_WRITE_CACHE;
974
975 //
976 // NOTE: At this point one device object has been successfully created.
977 // from here on out return success.
978 //
979
980 //
981 // Do READ CAPACITY. This SCSI command
982 // returns the number of bytes on a device.
983 // Device extension is updated with device size.
984 //
985
986 status = ScsiClassReadDriveCapacity(deviceObject);
987
988 //
989 // If the read capcity failed then just return, unless this is a
990 // removable disk where a device object partition needs to be created.
991 //
992
993 if (!NT_SUCCESS(status) &&
994 !(deviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
995
996 DebugPrint((1,
997 "CreateDiskDeviceObjects: Can't read capacity for device %s\n",
998 ntNameBuffer));
999
1000 return(STATUS_SUCCESS);
1001
1002 } else {
1003
1004 //
1005 // Make sure the volume verification bit is off so that
1006 // IoReadPartitionTable will work.
1007 //
1008
1009 deviceObject->Flags &= ~DO_VERIFY_VOLUME;
1010 }
1011
1012 status = CreatePartitionDeviceObjects(deviceObject, RegistryPath);
1013
1014 if (NT_SUCCESS(status))
1015 return STATUS_SUCCESS;
1016
1017
1018 CreateDiskDeviceObjectsExit:
1019
1020 //
1021 // Release the device since an error occurred.
1022 //
1023
1024 ScsiClassClaimDevice(PortDeviceObject,
1025 LunInfo,
1026 TRUE,
1027 NULL);
1028
1029 if (diskGeometry != NULL) {
1030 ExFreePool(diskGeometry);
1031 }
1032
1033 if (senseData != NULL) {
1034 ExFreePool(senseData);
1035 }
1036
1037 if (deviceObject != NULL) {
1038
1039 if (srbListInitialized) {
1040 ExDeleteNPagedLookasideList(&deviceExtension->SrbLookasideListHead);
1041 }
1042
1043 IoDeleteDevice(deviceObject);
1044 }
1045
1046 //
1047 // Delete directory and return.
1048 //
1049
1050 if (!NT_SUCCESS(status)) {
1051 ZwMakeTemporaryObject(handle);
1052 }
1053
1054 ZwClose(handle);
1055
1056 return(status);
1057
1058 } // end CreateDiskDeviceObjects()
1059
1060 \f
1061 NTSTATUS
1062 NTAPI
1063 CreatePartitionDeviceObjects(
1064 IN PDEVICE_OBJECT PhysicalDeviceObject,
1065 IN PUNICODE_STRING RegistryPath
1066 )
1067 {
1068 CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
1069 ULONG partitionNumber = 0;
1070 NTSTATUS status;
1071 PDEVICE_OBJECT deviceObject = NULL;
1072 PDISK_GEOMETRY_EX diskGeometry = NULL;
1073 PDRIVE_LAYOUT_INFORMATION partitionList = NULL;
1074 PDEVICE_EXTENSION deviceExtension;
1075 PDEVICE_EXTENSION physicalDeviceExtension;
1076 PCLASS_INIT_DATA initData = NULL;
1077 PDISK_DATA diskData;
1078 PDISK_DATA physicalDiskData;
1079 ULONG bytesPerSector;
1080 UCHAR sectorShift;
1081 ULONG srbFlags;
1082 ULONG dmByteSkew = 0;
1083 PULONG dmSkew;
1084 BOOLEAN dmActive = FALSE;
1085 ULONG numberListElements = 0;
1086
1087
1088 //
1089 // Get physical device geometry information for partition table reads.
1090 //
1091
1092 physicalDeviceExtension = PhysicalDeviceObject->DeviceExtension;
1093 diskGeometry = physicalDeviceExtension->DiskGeometry;
1094 bytesPerSector = diskGeometry->Geometry.BytesPerSector;
1095
1096 //
1097 // Make sure sector size is not zero.
1098 //
1099
1100 if (bytesPerSector == 0) {
1101
1102 //
1103 // Default sector size for disk is 512.
1104 //
1105
1106 bytesPerSector = diskGeometry->Geometry.BytesPerSector = 512;
1107 }
1108
1109 sectorShift = physicalDeviceExtension->SectorShift;
1110
1111 //
1112 // Set pointer to disk data area that follows device extension.
1113 //
1114
1115 diskData = (PDISK_DATA)(physicalDeviceExtension + 1);
1116 diskData->PartitionListState = Initializing;
1117
1118 //
1119 // Determine is DM Driver is loaded on an IDE drive that is
1120 // under control of Atapi - this could be either a crashdump or
1121 // an Atapi device is sharing the controller with an IDE disk.
1122 //
1123
1124 HalExamineMBR(PhysicalDeviceObject,
1125 physicalDeviceExtension->DiskGeometry->Geometry.BytesPerSector,
1126 (ULONG)0x54,
1127 (PVOID)&dmSkew);
1128
1129 if (dmSkew) {
1130
1131 //
1132 // Update the device extension, so that the call to IoReadPartitionTable
1133 // will get the correct information. Any I/O to this disk will have
1134 // to be skewed by *dmSkew sectors aka DMByteSkew.
1135 //
1136
1137 physicalDeviceExtension->DMSkew = *dmSkew;
1138 physicalDeviceExtension->DMActive = TRUE;
1139 physicalDeviceExtension->DMByteSkew = physicalDeviceExtension->DMSkew * bytesPerSector;
1140
1141 //
1142 // Save away the infomation that we need, since this deviceExtension will soon be
1143 // blown away.
1144 //
1145
1146 dmActive = TRUE;
1147 dmByteSkew = physicalDeviceExtension->DMByteSkew;
1148
1149 }
1150
1151 //
1152 // Create objects for all the partitions on the device.
1153 //
1154
1155 status = IoReadPartitionTable(PhysicalDeviceObject,
1156 physicalDeviceExtension->DiskGeometry->Geometry.BytesPerSector,
1157 TRUE,
1158 (PVOID)&partitionList);
1159
1160 //
1161 // If the I/O read partition table failed and this is a removable device,
1162 // then fix up the partition list to make it look like there is one
1163 // zero length partition.
1164 //
1165 DPRINT("IoReadPartitionTable() status: 0x%08X\n", status);
1166 if ((!NT_SUCCESS(status) || partitionList->PartitionCount == 0) &&
1167 PhysicalDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
1168
1169 if (!NT_SUCCESS(status)) {
1170
1171 //
1172 // Remember this disk is not ready.
1173 //
1174
1175 diskData->DriveNotReady = TRUE;
1176
1177 } else {
1178
1179 //
1180 // Free the partition list allocated by IoReadPartitionTable.
1181 //
1182
1183 ExFreePool(partitionList);
1184 }
1185
1186 //
1187 // Allocate and zero a partition list.
1188 //
1189
1190 partitionList = ExAllocatePool(NonPagedPool, sizeof(*partitionList));
1191
1192
1193 if (partitionList != NULL) {
1194
1195 RtlZeroMemory( partitionList, sizeof( *partitionList ));
1196
1197 //
1198 // Set the partition count to one and the status to success
1199 // so one device object will be created. Set the partition type
1200 // to a bogus value.
1201 //
1202
1203 partitionList->PartitionCount = 1;
1204
1205 status = STATUS_SUCCESS;
1206 }
1207 }
1208
1209 if (NT_SUCCESS(status)) {
1210
1211 //
1212 // Record disk signature.
1213 //
1214
1215 diskData->Signature = partitionList->Signature;
1216
1217 //
1218 // If disk signature is zero, then calculate the MBR checksum.
1219 //
1220
1221 if (!diskData->Signature) {
1222
1223 if (!CalculateMbrCheckSum(physicalDeviceExtension,
1224 &diskData->MbrCheckSum)) {
1225
1226 DebugPrint((1,
1227 "SCSIDISK: Can't calculate MBR checksum for disk %x\n",
1228 physicalDeviceExtension->DeviceNumber));
1229 } else {
1230
1231 DebugPrint((2,
1232 "SCSIDISK: MBR checksum for disk %x is %x\n",
1233 physicalDeviceExtension->DeviceNumber,
1234 diskData->MbrCheckSum));
1235 }
1236 }
1237
1238 //
1239 // Check the registry and determine if the BIOS knew about this drive. If
1240 // it did then update the geometry with the BIOS information.
1241 //
1242
1243 UpdateGeometry(physicalDeviceExtension);
1244
1245 srbFlags = physicalDeviceExtension->SrbFlags;
1246
1247 initData = ExAllocatePool(NonPagedPool, sizeof(CLASS_INIT_DATA));
1248 if (!initData)
1249 {
1250 DebugPrint((1,
1251 "Disk.CreatePartionDeviceObjects - Allocation of initData failed\n"));
1252
1253 status = STATUS_INSUFFICIENT_RESOURCES;
1254 goto CreatePartitionDeviceObjectsExit;
1255 }
1256
1257 RtlZeroMemory(initData, sizeof(CLASS_INIT_DATA));
1258
1259 initData->InitializationDataSize = sizeof(CLASS_INIT_DATA);
1260 initData->DeviceExtensionSize = DEVICE_EXTENSION_SIZE;
1261 initData->DeviceType = FILE_DEVICE_DISK;
1262 initData->DeviceCharacteristics = PhysicalDeviceObject->Characteristics;
1263 initData->ClassError = physicalDeviceExtension->ClassError;
1264 initData->ClassReadWriteVerification = physicalDeviceExtension->ClassReadWriteVerification;
1265 initData->ClassFindDevices = physicalDeviceExtension->ClassFindDevices;
1266 initData->ClassDeviceControl = physicalDeviceExtension->ClassDeviceControl;
1267 initData->ClassShutdownFlush = physicalDeviceExtension->ClassShutdownFlush;
1268 initData->ClassCreateClose = physicalDeviceExtension->ClassCreateClose;
1269 initData->ClassStartIo = physicalDeviceExtension->ClassStartIo;
1270
1271 //
1272 // Create device objects for the device partitions (if any).
1273 // PartitionCount includes physical device partition 0,
1274 // so only one partition means no objects to create.
1275 //
1276
1277 DebugPrint((2,
1278 "CreateDiskDeviceObjects: Number of partitions is %d\n",
1279 partitionList->PartitionCount));
1280
1281 for (partitionNumber = 0; partitionNumber <
1282 partitionList->PartitionCount; partitionNumber++) {
1283
1284 //
1285 // Create partition object and set up partition parameters.
1286 //
1287
1288 sprintf(ntNameBuffer,
1289 "\\Device\\Harddisk%lu\\Partition%lu",
1290 physicalDeviceExtension->DeviceNumber,
1291 partitionNumber + 1);
1292
1293 DebugPrint((2,
1294 "CreateDiskDeviceObjects: Create device object %s\n",
1295 ntNameBuffer));
1296
1297 status = ScsiClassCreateDeviceObject(PhysicalDeviceObject->DriverObject,
1298 ntNameBuffer,
1299 PhysicalDeviceObject,
1300 &deviceObject,
1301 initData);
1302
1303 if (!NT_SUCCESS(status)) {
1304
1305 DebugPrint((1, "CreateDiskDeviceObjects: Can't create device object for %s\n", ntNameBuffer));
1306
1307 break;
1308 }
1309
1310 //
1311 // Set up device object fields.
1312 //
1313
1314 deviceObject->Flags |= DO_DIRECT_IO;
1315
1316 //
1317 // Check if this is during initialization. If not indicate that
1318 // system initialization already took place and this disk is ready
1319 // to be accessed.
1320 //
1321
1322 if (!RegistryPath) {
1323 deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1324 }
1325
1326 deviceObject->StackSize = (CCHAR)physicalDeviceExtension->PortDeviceObject->StackSize + 1;
1327
1328 //
1329 // Set up device extension fields.
1330 //
1331
1332 deviceExtension = deviceObject->DeviceExtension;
1333
1334 if (dmActive) {
1335
1336 //
1337 // Restore any saved DM values.
1338 //
1339
1340 deviceExtension->DMByteSkew = dmByteSkew;
1341 deviceExtension->DMSkew = *dmSkew;
1342 deviceExtension->DMActive = TRUE;
1343
1344 }
1345
1346 //
1347 // Link new device extension to previous disk data
1348 // to support dynamic partitioning.
1349 //
1350
1351 diskData->NextPartition = deviceExtension;
1352
1353 //
1354 // Get pointer to new disk data.
1355 //
1356
1357 diskData = (PDISK_DATA)(deviceExtension + 1);
1358
1359 //
1360 // Set next partition pointer to NULL in case this is the
1361 // last partition.
1362 //
1363
1364 diskData->NextPartition = NULL;
1365
1366 //
1367 // Allocate spinlock for zoning for split-request completion.
1368 //
1369
1370 KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
1371
1372 //
1373 // Copy port device object pointer to device extension.
1374 //
1375
1376 deviceExtension->PortDeviceObject = physicalDeviceExtension->PortDeviceObject;
1377
1378 //
1379 // Set the alignment requirements for the device based on the
1380 // host adapter requirements
1381 //
1382
1383 if (physicalDeviceExtension->PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) {
1384 deviceObject->AlignmentRequirement = physicalDeviceExtension->PortDeviceObject->AlignmentRequirement;
1385 }
1386
1387
1388 if (srbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) {
1389 numberListElements = 30;
1390 } else {
1391 numberListElements = 8;
1392 }
1393
1394 //
1395 // Build the lookaside list for srb's for this partition based on
1396 // whether the adapter and disk can do tagged queueing.
1397 //
1398
1399 ScsiClassInitializeSrbLookasideList(deviceExtension,
1400 numberListElements);
1401
1402 deviceExtension->SrbFlags = srbFlags;
1403
1404 //
1405 // Set the sense-data pointer in the device extension.
1406 //
1407
1408 deviceExtension->SenseData = physicalDeviceExtension->SenseData;
1409 deviceExtension->PortCapabilities = physicalDeviceExtension->PortCapabilities;
1410 deviceExtension->DiskGeometry = diskGeometry;
1411 diskData->PartitionOrdinal = diskData->PartitionNumber = partitionNumber + 1;
1412 diskData->PartitionType = partitionList->PartitionEntry[partitionNumber].PartitionType;
1413 diskData->BootIndicator = partitionList->PartitionEntry[partitionNumber].BootIndicator;
1414
1415 DebugPrint((2, "CreateDiskDeviceObjects: Partition type is %x\n",
1416 diskData->PartitionType));
1417
1418 deviceExtension->StartingOffset = partitionList->PartitionEntry[partitionNumber].StartingOffset;
1419 deviceExtension->PartitionLength = partitionList->PartitionEntry[partitionNumber].PartitionLength;
1420 diskData->HiddenSectors = partitionList->PartitionEntry[partitionNumber].HiddenSectors;
1421 deviceExtension->PortNumber = physicalDeviceExtension->PortNumber;
1422 deviceExtension->PathId = physicalDeviceExtension->PathId;
1423 deviceExtension->TargetId = physicalDeviceExtension->TargetId;
1424 deviceExtension->Lun = physicalDeviceExtension->Lun;
1425
1426 //
1427 // Check for removable media support.
1428 //
1429
1430 if (PhysicalDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
1431 deviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
1432 }
1433
1434 //
1435 // Set timeout value in seconds.
1436 //
1437
1438 deviceExtension->TimeOutValue = physicalDeviceExtension->TimeOutValue;
1439 deviceExtension->DiskGeometry->Geometry.BytesPerSector = bytesPerSector;
1440 deviceExtension->SectorShift = sectorShift;
1441 deviceExtension->DeviceObject = deviceObject;
1442 deviceExtension->DeviceFlags |= physicalDeviceExtension->DeviceFlags;
1443
1444 } // end for (partitionNumber) ...
1445
1446 //
1447 // Free the buffer allocated by reading the
1448 // partition table.
1449 //
1450
1451 ExFreePool(partitionList);
1452
1453 if (dmSkew) {
1454 ExFreePool(dmSkew);
1455 }
1456
1457 } else {
1458
1459 CreatePartitionDeviceObjectsExit:
1460
1461 if (partitionList) {
1462 ExFreePool(partitionList);
1463 }
1464 if (initData) {
1465 ExFreePool(initData);
1466 }
1467
1468 if (dmSkew) {
1469 ExFreePool(dmSkew);
1470 }
1471
1472 return status;
1473
1474 } // end if...else
1475
1476
1477 physicalDiskData = (PDISK_DATA)(physicalDeviceExtension + 1);
1478 physicalDiskData->PartitionListState = Initialized;
1479
1480 return(STATUS_SUCCESS);
1481
1482
1483 } // end CreatePartitionDeviceObjects()
1484
1485 \f
1486 NTSTATUS
1487 NTAPI
1488 ScsiDiskReadWriteVerification(
1489 IN PDEVICE_OBJECT DeviceObject,
1490 IN PIRP Irp
1491 )
1492
1493 /*++
1494
1495 Routine Description:
1496
1497 I/O System entry for read and write requests to SCSI disks.
1498
1499 Arguments:
1500
1501 DeviceObject - Pointer to driver object created by system.
1502 Irp - IRP involved.
1503
1504 Return Value:
1505
1506 NT Status
1507
1508 --*/
1509
1510 {
1511 PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
1512 PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
1513 ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
1514 LARGE_INTEGER startingOffset;
1515
1516 //
1517 // HACK: How can we end here with null sector size?!
1518 //
1519
1520 if (deviceExtension->DiskGeometry->Geometry.BytesPerSector == 0) {
1521 DPRINT1("Hack! Received invalid sector size\n");
1522 deviceExtension->DiskGeometry->Geometry.BytesPerSector = 512;
1523 }
1524
1525 //
1526 // Verify parameters of this request.
1527 // Check that ending sector is within partition and
1528 // that number of bytes to transfer is a multiple of
1529 // the sector size.
1530 //
1531
1532 startingOffset.QuadPart = (currentIrpStack->Parameters.Read.ByteOffset.QuadPart +
1533 transferByteCount);
1534
1535 if ((startingOffset.QuadPart > deviceExtension->PartitionLength.QuadPart) ||
1536 (transferByteCount & (deviceExtension->DiskGeometry->Geometry.BytesPerSector - 1))) {
1537
1538 //
1539 // This error maybe caused by the fact that the drive is not ready.
1540 //
1541
1542 if (((PDISK_DATA)(deviceExtension + 1))->DriveNotReady) {
1543
1544 //
1545 // Flag this as a user errror so that a popup is generated.
1546 //
1547
1548 Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
1549 IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
1550
1551 } else {
1552
1553 //
1554 // Note fastfat depends on this parameter to determine when to
1555 // remount do to a sector size change.
1556 //
1557
1558 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
1559 }
1560
1561 if (startingOffset.QuadPart > deviceExtension->PartitionLength.QuadPart) {
1562 DPRINT1("Reading beyond partition end! startingOffset: %I64d, PartitionLength: %I64d\n", startingOffset.QuadPart, deviceExtension->PartitionLength.QuadPart);
1563 }
1564
1565 if (transferByteCount & (deviceExtension->DiskGeometry->Geometry.BytesPerSector - 1)) {
1566 DPRINT1("Not reading sectors! TransferByteCount: %lu, BytesPerSector: %lu\n", transferByteCount, deviceExtension->DiskGeometry->Geometry.BytesPerSector);
1567 }
1568
1569 if (Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) {
1570 DPRINT1("Failing due to device not ready!\n");
1571 }
1572
1573 return STATUS_INVALID_PARAMETER;
1574 }
1575
1576 return STATUS_SUCCESS;
1577
1578 } // end ScsiDiskReadWrite()
1579
1580 \f
1581 NTSTATUS
1582 NTAPI
1583 ScsiDiskDeviceControl(
1584 PDEVICE_OBJECT DeviceObject,
1585 PIRP Irp
1586 )
1587
1588 /*++
1589
1590 Routine Description:
1591
1592 I/O system entry for device controls to SCSI disks.
1593
1594 Arguments:
1595
1596 DeviceObject - Pointer to driver object created by system.
1597 Irp - IRP involved.
1598
1599 Return Value:
1600
1601 Status is returned.
1602
1603 --*/
1604
1605 {
1606 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
1607 PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
1608 PDISK_DATA diskData = (PDISK_DATA)(deviceExtension + 1);
1609 PSCSI_REQUEST_BLOCK srb;
1610 PCDB cdb;
1611 PMODE_PARAMETER_HEADER modeData;
1612 PIRP irp2;
1613 ULONG length;
1614 NTSTATUS status;
1615 KEVENT event;
1616 IO_STATUS_BLOCK ioStatus;
1617
1618 PAGED_CODE();
1619
1620 srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
1621
1622 if (srb == NULL) {
1623
1624 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1625 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1626 return(STATUS_INSUFFICIENT_RESOURCES);
1627 }
1628
1629 //
1630 // Write zeros to Srb.
1631 //
1632
1633 RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
1634
1635 cdb = (PCDB)srb->Cdb;
1636
1637 switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
1638
1639 case SMART_GET_VERSION: {
1640
1641 ULONG_PTR buffer;
1642 PSRB_IO_CONTROL srbControl;
1643 PGETVERSIONINPARAMS versionParams;
1644
1645 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
1646 sizeof(GETVERSIONINPARAMS)) {
1647 status = STATUS_INVALID_PARAMETER;
1648 break;
1649 }
1650
1651 //
1652 // Create notification event object to be used to signal the
1653 // request completion.
1654 //
1655
1656 KeInitializeEvent(&event, NotificationEvent, FALSE);
1657
1658 srbControl = ExAllocatePool(NonPagedPool,
1659 sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS));
1660
1661 if (!srbControl) {
1662 status = STATUS_INSUFFICIENT_RESOURCES;
1663 break;
1664 }
1665
1666 //
1667 // fill in srbControl fields
1668 //
1669
1670 srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
1671 RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
1672 srbControl->Timeout = deviceExtension->TimeOutValue;
1673 srbControl->Length = sizeof(GETVERSIONINPARAMS);
1674 srbControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION;
1675
1676 //
1677 // Point to the 'buffer' portion of the SRB_CONTROL
1678 //
1679
1680 buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1681
1682 //
1683 // Ensure correct target is set in the cmd parameters.
1684 //
1685
1686 versionParams = (PGETVERSIONINPARAMS)buffer;
1687 versionParams->bIDEDeviceMap = deviceExtension->TargetId;
1688
1689 //
1690 // Copy the IOCTL parameters to the srb control buffer area.
1691 //
1692
1693 RtlMoveMemory((PVOID)buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(GETVERSIONINPARAMS));
1694
1695
1696 irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
1697 deviceExtension->PortDeviceObject,
1698 srbControl,
1699 sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
1700 srbControl,
1701 sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
1702 FALSE,
1703 &event,
1704 &ioStatus);
1705
1706 if (irp2 == NULL) {
1707 status = STATUS_INSUFFICIENT_RESOURCES;
1708 break;
1709 }
1710
1711 //
1712 // Call the port driver with the request and wait for it to complete.
1713 //
1714
1715 status = IoCallDriver(deviceExtension->PortDeviceObject, irp2);
1716
1717 if (status == STATUS_PENDING) {
1718 KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
1719 status = ioStatus.Status;
1720 }
1721
1722 //
1723 // If successful, copy the data received into the output buffer.
1724 // This should only fail in the event that the IDE driver is older than this driver.
1725 //
1726
1727 if (NT_SUCCESS(status)) {
1728
1729 buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1730
1731 RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, (PVOID)buffer, sizeof(GETVERSIONINPARAMS));
1732 Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS);
1733 }
1734
1735 ExFreePool(srbControl);
1736 break;
1737 }
1738
1739 case SMART_RCV_DRIVE_DATA: {
1740
1741 PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
1742 ULONG controlCode = 0;
1743 PSRB_IO_CONTROL srbControl;
1744 ULONG_PTR buffer;
1745
1746 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
1747 (sizeof(SENDCMDINPARAMS) - 1)) {
1748 status = STATUS_INVALID_PARAMETER;
1749 break;
1750
1751 } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
1752 (sizeof(SENDCMDOUTPARAMS) + 512 - 1)) {
1753 status = STATUS_INVALID_PARAMETER;
1754 break;
1755 }
1756
1757 //
1758 // Create notification event object to be used to signal the
1759 // request completion.
1760 //
1761
1762 KeInitializeEvent(&event, NotificationEvent, FALSE);
1763
1764 if (cmdInParameters->irDriveRegs.bCommandReg == ID_CMD) {
1765
1766 length = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
1767 controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
1768
1769 } else if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {
1770 switch (cmdInParameters->irDriveRegs.bFeaturesReg) {
1771 case READ_ATTRIBUTES:
1772 controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
1773 length = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
1774 break;
1775 case READ_THRESHOLDS:
1776 controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
1777 length = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
1778 break;
1779 default:
1780 status = STATUS_INVALID_PARAMETER;
1781 break;
1782 }
1783 } else {
1784
1785 status = STATUS_INVALID_PARAMETER;
1786 }
1787
1788 if (controlCode == 0) {
1789 status = STATUS_INVALID_PARAMETER;
1790 break;
1791 }
1792
1793 srbControl = ExAllocatePool(NonPagedPool,
1794 sizeof(SRB_IO_CONTROL) + length);
1795
1796 if (!srbControl) {
1797 status = STATUS_INSUFFICIENT_RESOURCES;
1798 break;
1799 }
1800
1801 //
1802 // fill in srbControl fields
1803 //
1804
1805 srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
1806 RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
1807 srbControl->Timeout = deviceExtension->TimeOutValue;
1808 srbControl->Length = length;
1809 srbControl->ControlCode = controlCode;
1810
1811 //
1812 // Point to the 'buffer' portion of the SRB_CONTROL
1813 //
1814
1815 buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1816
1817 //
1818 // Ensure correct target is set in the cmd parameters.
1819 //
1820
1821 cmdInParameters->bDriveNumber = deviceExtension->TargetId;
1822
1823 //
1824 // Copy the IOCTL parameters to the srb control buffer area.
1825 //
1826
1827 RtlMoveMemory((PVOID)buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
1828
1829 irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
1830 deviceExtension->PortDeviceObject,
1831 srbControl,
1832 sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
1833 srbControl,
1834 sizeof(SRB_IO_CONTROL) + length,
1835 FALSE,
1836 &event,
1837 &ioStatus);
1838
1839 if (irp2 == NULL) {
1840 status = STATUS_INSUFFICIENT_RESOURCES;
1841 break;
1842 }
1843
1844 //
1845 // Call the port driver with the request and wait for it to complete.
1846 //
1847
1848 status = IoCallDriver(deviceExtension->PortDeviceObject, irp2);
1849
1850 if (status == STATUS_PENDING) {
1851 KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
1852 status = ioStatus.Status;
1853 }
1854
1855 //
1856 // If successful, copy the data received into the output buffer
1857 //
1858
1859 buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1860
1861 if (NT_SUCCESS(status)) {
1862
1863 RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, (PVOID)buffer, length - 1);
1864 Irp->IoStatus.Information = length - 1;
1865
1866 } else {
1867
1868 RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, (PVOID)buffer, (sizeof(SENDCMDOUTPARAMS) - 1));
1869 Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1;
1870
1871 }
1872
1873 ExFreePool(srbControl);
1874 break;
1875
1876 }
1877
1878 case SMART_SEND_DRIVE_COMMAND: {
1879
1880 PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
1881 PSRB_IO_CONTROL srbControl;
1882 ULONG controlCode = 0;
1883 ULONG_PTR buffer;
1884
1885 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
1886 (sizeof(SENDCMDINPARAMS) - 1)) {
1887 status = STATUS_INVALID_PARAMETER;
1888 break;
1889
1890 } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
1891 (sizeof(SENDCMDOUTPARAMS) - 1)) {
1892 status = STATUS_INVALID_PARAMETER;
1893 break;
1894 }
1895
1896 //
1897 // Create notification event object to be used to signal the
1898 // request completion.
1899 //
1900
1901 KeInitializeEvent(&event, NotificationEvent, FALSE);
1902
1903 length = 0;
1904
1905 if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {
1906 switch (cmdInParameters->irDriveRegs.bFeaturesReg) {
1907
1908 case ENABLE_SMART:
1909 controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
1910 break;
1911
1912 case DISABLE_SMART:
1913 controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
1914 break;
1915
1916 case RETURN_SMART_STATUS:
1917
1918 //
1919 // Ensure bBuffer is at least 2 bytes (to hold the values of
1920 // cylinderLow and cylinderHigh).
1921 //
1922
1923 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
1924 (sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS))) {
1925
1926 status = STATUS_INVALID_PARAMETER;
1927 break;
1928 }
1929
1930 controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
1931 length = sizeof(IDEREGS);
1932 break;
1933
1934 case ENABLE_DISABLE_AUTOSAVE:
1935 controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
1936 break;
1937
1938 case SAVE_ATTRIBUTE_VALUES:
1939 controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
1940 break;
1941
1942 case EXECUTE_OFFLINE_DIAGS:
1943 controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
1944 break;
1945
1946 default:
1947 status = STATUS_INVALID_PARAMETER;
1948 break;
1949 }
1950 } else {
1951
1952 status = STATUS_INVALID_PARAMETER;
1953 }
1954
1955 if (controlCode == 0) {
1956 status = STATUS_INVALID_PARAMETER;
1957 break;
1958 }
1959
1960 length += (sizeof(SENDCMDOUTPARAMS) > sizeof(SENDCMDINPARAMS)) ? sizeof(SENDCMDOUTPARAMS) : sizeof(SENDCMDINPARAMS);
1961 srbControl = ExAllocatePool(NonPagedPool,
1962 sizeof(SRB_IO_CONTROL) + length);
1963
1964 if (!srbControl) {
1965 status = STATUS_INSUFFICIENT_RESOURCES;
1966 break;
1967 }
1968
1969 //
1970 // fill in srbControl fields
1971 //
1972
1973 srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
1974 RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
1975 srbControl->Timeout = deviceExtension->TimeOutValue;
1976 srbControl->Length = length;
1977
1978 //
1979 // Point to the 'buffer' portion of the SRB_CONTROL
1980 //
1981
1982 buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1983
1984 //
1985 // Ensure correct target is set in the cmd parameters.
1986 //
1987
1988 cmdInParameters->bDriveNumber = deviceExtension->TargetId;
1989
1990 //
1991 // Copy the IOCTL parameters to the srb control buffer area.
1992 //
1993
1994 RtlMoveMemory((PVOID)buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
1995
1996 srbControl->ControlCode = controlCode;
1997
1998 irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
1999 deviceExtension->PortDeviceObject,
2000 srbControl,
2001 sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
2002 srbControl,
2003 sizeof(SRB_IO_CONTROL) + length,
2004 FALSE,
2005 &event,
2006 &ioStatus);
2007
2008 if (irp2 == NULL) {
2009 status = STATUS_INSUFFICIENT_RESOURCES;
2010 break;
2011 }
2012
2013 //
2014 // Call the port driver with the request and wait for it to complete.
2015 //
2016
2017 status = IoCallDriver(deviceExtension->PortDeviceObject, irp2);
2018
2019 if (status == STATUS_PENDING) {
2020 KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
2021 status = ioStatus.Status;
2022 }
2023
2024 //
2025 // Copy the data received into the output buffer. Since the status buffer
2026 // contains error information also, always perform this copy. IO will will
2027 // either pass this back to the app, or zero it, in case of error.
2028 //
2029
2030 buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
2031
2032 //
2033 // Update the return buffer size based on the sub-command.
2034 //
2035
2036 if (cmdInParameters->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) {
2037 length = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS);
2038 } else {
2039 length = sizeof(SENDCMDOUTPARAMS) - 1;
2040 }
2041
2042 RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, (PVOID)buffer, length);
2043 Irp->IoStatus.Information = length;
2044
2045 ExFreePool(srbControl);
2046 break;
2047
2048 }
2049
2050 case IOCTL_DISK_GET_DRIVE_GEOMETRY:
2051 case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
2052 {
2053
2054 PDEVICE_EXTENSION physicalDeviceExtension;
2055 PDISK_DATA physicalDiskData;
2056 BOOLEAN removable = FALSE;
2057 BOOLEAN listInitialized = FALSE;
2058
2059 if ((irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY &&
2060 irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2061 sizeof(DISK_GEOMETRY)) ||
2062 (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY_EX &&
2063 irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2064 sizeof(DISK_GEOMETRY_EX))) {
2065
2066 status = STATUS_INFO_LENGTH_MISMATCH;
2067 break;
2068 }
2069
2070 status = STATUS_SUCCESS;
2071
2072 physicalDeviceExtension = deviceExtension->PhysicalDevice->DeviceExtension;
2073 physicalDiskData = (PDISK_DATA)(physicalDeviceExtension + 1);
2074
2075 removable = (BOOLEAN)DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA;
2076 listInitialized = (physicalDiskData->PartitionListState == Initialized);
2077
2078 if (removable || (!listInitialized))
2079 {
2080 //
2081 // Issue ReadCapacity to update device extension
2082 // with information for current media.
2083 //
2084
2085 status = ScsiClassReadDriveCapacity(deviceExtension->PhysicalDevice);
2086
2087 }
2088
2089 if (removable) {
2090
2091 if (!NT_SUCCESS(status)) {
2092
2093 //
2094 // Note the drive is not ready.
2095 //
2096
2097 diskData->DriveNotReady = TRUE;
2098
2099 break;
2100 }
2101
2102 //
2103 // Note the drive is now ready.
2104 //
2105
2106 diskData->DriveNotReady = FALSE;
2107
2108 } else if (NT_SUCCESS(status)) {
2109
2110 // ReadDriveCapacity was allright, create Partition Objects
2111
2112 if (physicalDiskData->PartitionListState == NotInitialized) {
2113 status = CreatePartitionDeviceObjects(deviceExtension->PhysicalDevice, NULL);
2114 }
2115 }
2116
2117 if (NT_SUCCESS(status)) {
2118
2119 //
2120 // Copy drive geometry information from device extension.
2121 //
2122
2123 RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
2124 deviceExtension->DiskGeometry,
2125 (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) ?
2126 sizeof(DISK_GEOMETRY) :
2127 sizeof(DISK_GEOMETRY_EX));
2128
2129 status = STATUS_SUCCESS;
2130 Irp->IoStatus.Information =
2131 (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) ?
2132 sizeof(DISK_GEOMETRY) :
2133 sizeof(DISK_GEOMETRY_EX);
2134 }
2135
2136 break;
2137
2138 }
2139
2140 case IOCTL_DISK_VERIFY:
2141
2142 {
2143
2144 PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
2145 LARGE_INTEGER byteOffset;
2146 ULONG sectorOffset;
2147 USHORT sectorCount;
2148
2149 //
2150 // Validate buffer length.
2151 //
2152
2153 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2154 sizeof(VERIFY_INFORMATION)) {
2155
2156 status = STATUS_INFO_LENGTH_MISMATCH;
2157 break;
2158 }
2159
2160 //
2161 // Verify sectors
2162 //
2163
2164 srb->CdbLength = 10;
2165
2166 cdb->CDB10.OperationCode = SCSIOP_VERIFY;
2167
2168 //
2169 // Add disk offset to starting sector.
2170 //
2171
2172 byteOffset.QuadPart = deviceExtension->StartingOffset.QuadPart +
2173 verifyInfo->StartingOffset.QuadPart;
2174
2175 //
2176 // Convert byte offset to sector offset.
2177 //
2178
2179 sectorOffset = (ULONG)(byteOffset.QuadPart >> deviceExtension->SectorShift);
2180
2181 //
2182 // Convert ULONG byte count to USHORT sector count.
2183 //
2184
2185 sectorCount = (USHORT)(verifyInfo->Length >> deviceExtension->SectorShift);
2186
2187 //
2188 // Move little endian values into CDB in big endian format.
2189 //
2190
2191 cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&sectorOffset)->Byte3;
2192 cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&sectorOffset)->Byte2;
2193 cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&sectorOffset)->Byte1;
2194 cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&sectorOffset)->Byte0;
2195
2196 cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&sectorCount)->Byte1;
2197 cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&sectorCount)->Byte0;
2198
2199 //
2200 // The verify command is used by the NT FORMAT utility and
2201 // requests are sent down for 5% of the volume size. The
2202 // request timeout value is calculated based on the number of
2203 // sectors verified.
2204 //
2205
2206 srb->TimeOutValue = ((sectorCount + 0x7F) >> 7) *
2207 deviceExtension->TimeOutValue;
2208
2209 status = ScsiClassSendSrbAsynchronous(DeviceObject,
2210 srb,
2211 Irp,
2212 NULL,
2213 0,
2214 FALSE);
2215
2216 return(status);
2217
2218 }
2219
2220 case IOCTL_DISK_GET_PARTITION_INFO:
2221
2222 //
2223 // Return the information about the partition specified by the device
2224 // object. Note that no information is ever returned about the size
2225 // or partition type of the physical disk, as this doesn't make any
2226 // sense.
2227 //
2228
2229 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2230 sizeof(PARTITION_INFORMATION)) {
2231
2232 status = STATUS_INFO_LENGTH_MISMATCH;
2233
2234 }
2235 #if 0 // HACK: ReactOS partition numbers must be wrong
2236 else if (diskData->PartitionNumber == 0) {
2237
2238 //
2239 // Paritition zero is not a partition so this is not a
2240 // reasonable request.
2241 //
2242
2243 status = STATUS_INVALID_DEVICE_REQUEST;
2244
2245 }
2246 #endif
2247 else {
2248
2249 PPARTITION_INFORMATION outputBuffer;
2250
2251 if (diskData->PartitionNumber == 0) {
2252 DPRINT1("HACK: Handling partition 0 request!\n");
2253 //ASSERT(FALSE);
2254 }
2255
2256 //
2257 // Update the geometry in case it has changed.
2258 //
2259
2260 status = UpdateRemovableGeometry (DeviceObject, Irp);
2261
2262 if (!NT_SUCCESS(status)) {
2263
2264 //
2265 // Note the drive is not ready.
2266 //
2267
2268 diskData->DriveNotReady = TRUE;
2269 break;
2270 }
2271
2272 //
2273 // Note the drive is now ready.
2274 //
2275
2276 diskData->DriveNotReady = FALSE;
2277
2278 outputBuffer =
2279 (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
2280
2281 outputBuffer->PartitionType = diskData->PartitionType;
2282 outputBuffer->StartingOffset = deviceExtension->StartingOffset;
2283 outputBuffer->PartitionLength.QuadPart = (diskData->PartitionNumber) ?
2284 deviceExtension->PartitionLength.QuadPart : 2305843009213693951LL; // HACK
2285 outputBuffer->HiddenSectors = diskData->HiddenSectors;
2286 outputBuffer->PartitionNumber = diskData->PartitionNumber;
2287 outputBuffer->BootIndicator = diskData->BootIndicator;
2288 outputBuffer->RewritePartition = FALSE;
2289 outputBuffer->RecognizedPartition =
2290 IsRecognizedPartition(diskData->PartitionType);
2291
2292 status = STATUS_SUCCESS;
2293 Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
2294 }
2295
2296 break;
2297
2298 case IOCTL_DISK_GET_PARTITION_INFO_EX:
2299
2300 //
2301 // Return the information about the partition specified by the device
2302 // object. Note that no information is ever returned about the size
2303 // or partition type of the physical disk, as this doesn't make any
2304 // sense.
2305 //
2306
2307 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2308 sizeof(PARTITION_INFORMATION_EX)) {
2309
2310 status = STATUS_INFO_LENGTH_MISMATCH;
2311
2312 }
2313 else if (diskData->PartitionNumber == 0) {
2314
2315 //
2316 // Paritition zero is not a partition so this is not a
2317 // reasonable request.
2318 //
2319
2320 status = STATUS_INVALID_DEVICE_REQUEST;
2321
2322 }
2323 else {
2324
2325 PPARTITION_INFORMATION_EX outputBuffer;
2326
2327 //
2328 // Update the geometry in case it has changed.
2329 //
2330
2331 status = UpdateRemovableGeometry (DeviceObject, Irp);
2332
2333 if (!NT_SUCCESS(status)) {
2334
2335 //
2336 // Note the drive is not ready.
2337 //
2338
2339 diskData->DriveNotReady = TRUE;
2340 break;
2341 }
2342
2343 //
2344 // Note the drive is now ready.
2345 //
2346
2347 diskData->DriveNotReady = FALSE;
2348
2349 if (diskData->PartitionType == 0 && (diskData->PartitionNumber > 0)) {
2350
2351 status = STATUS_INVALID_DEVICE_REQUEST;
2352 break;
2353 }
2354
2355 outputBuffer =
2356 (PPARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer;
2357
2358 //
2359 // FIXME: hack of the year, assume that partition is MBR
2360 // Thing that can obviously be wrong...
2361 //
2362
2363 outputBuffer->PartitionStyle = PARTITION_STYLE_MBR;
2364 outputBuffer->Mbr.PartitionType = diskData->PartitionType;
2365 outputBuffer->StartingOffset = deviceExtension->StartingOffset;
2366 outputBuffer->PartitionLength.QuadPart = deviceExtension->PartitionLength.QuadPart;
2367 outputBuffer->Mbr.HiddenSectors = diskData->HiddenSectors;
2368 outputBuffer->PartitionNumber = diskData->PartitionNumber;
2369 outputBuffer->Mbr.BootIndicator = diskData->BootIndicator;
2370 outputBuffer->RewritePartition = FALSE;
2371 outputBuffer->Mbr.RecognizedPartition =
2372 IsRecognizedPartition(diskData->PartitionType);
2373
2374 status = STATUS_SUCCESS;
2375 Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX);
2376 }
2377
2378 break;
2379
2380 case IOCTL_DISK_SET_PARTITION_INFO:
2381
2382 if (diskData->PartitionNumber == 0) {
2383
2384 status = STATUS_UNSUCCESSFUL;
2385
2386 } else {
2387
2388 PSET_PARTITION_INFORMATION inputBuffer =
2389 (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
2390
2391 //
2392 // Validate buffer length.
2393 //
2394
2395 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2396 sizeof(SET_PARTITION_INFORMATION)) {
2397
2398 status = STATUS_INFO_LENGTH_MISMATCH;
2399 break;
2400 }
2401
2402 //
2403 // The HAL routines IoGet- and IoSetPartitionInformation were
2404 // developed before support of dynamic partitioning and therefore
2405 // don't distinguish between partition ordinal (that is the order
2406 // of a partition on a disk) and the partition number. (The
2407 // partition number is assigned to a partition to identify it to
2408 // the system.) Use partition ordinals for these legacy calls.
2409 //
2410
2411 status = IoSetPartitionInformation(
2412 deviceExtension->PhysicalDevice,
2413 deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2414 diskData->PartitionOrdinal,
2415 inputBuffer->PartitionType);
2416
2417 if (NT_SUCCESS(status)) {
2418
2419 diskData->PartitionType = inputBuffer->PartitionType;
2420 }
2421 }
2422
2423 break;
2424
2425 case IOCTL_DISK_GET_DRIVE_LAYOUT:
2426
2427 //
2428 // Return the partition layout for the physical drive. Note that
2429 // the layout is returned for the actual physical drive, regardless
2430 // of which partition was specified for the request.
2431 //
2432
2433 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2434 sizeof(DRIVE_LAYOUT_INFORMATION)) {
2435 status = STATUS_INFO_LENGTH_MISMATCH;
2436
2437 } else {
2438
2439 PDRIVE_LAYOUT_INFORMATION partitionList;
2440 PDEVICE_EXTENSION physicalExtension = deviceExtension;
2441 PPARTITION_INFORMATION partitionEntry;
2442 PDISK_DATA diskData;
2443 ULONG tempSize;
2444 ULONG i;
2445
2446 //
2447 // Read partition information.
2448 //
2449
2450 status = IoReadPartitionTable(deviceExtension->PhysicalDevice,
2451 deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2452 FALSE,
2453 &partitionList);
2454
2455 if (!NT_SUCCESS(status)) {
2456 break;
2457 }
2458
2459 //
2460 // The disk layout has been returned in the partitionList
2461 // buffer. Determine its size and, if the data will fit
2462 // into the intermediatery buffer, return it.
2463 //
2464
2465 tempSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION,PartitionEntry[0]);
2466 tempSize += partitionList->PartitionCount *
2467 sizeof(PARTITION_INFORMATION);
2468
2469 if (tempSize >
2470 irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
2471
2472 status = STATUS_BUFFER_TOO_SMALL;
2473 ExFreePool(partitionList);
2474 break;
2475 }
2476
2477 //
2478 // Walk partition list to associate partition numbers with
2479 // partition entries.
2480 //
2481
2482 for (i = 0; i < partitionList->PartitionCount; i++) {
2483
2484 //
2485 // Walk partition chain anchored at physical disk extension.
2486 //
2487
2488 deviceExtension = physicalExtension;
2489 diskData = (PDISK_DATA)(deviceExtension + 1);
2490
2491 do {
2492
2493 deviceExtension = diskData->NextPartition;
2494
2495 //
2496 // Check if this is the last partition in the chain.
2497 //
2498
2499 if (!deviceExtension) {
2500 break;
2501 }
2502
2503 //
2504 // Get the partition device extension from disk data.
2505 //
2506
2507 diskData = (PDISK_DATA)(deviceExtension + 1);
2508
2509 //
2510 // Check if this partition is not currently being used.
2511 //
2512
2513 if (!deviceExtension->PartitionLength.QuadPart) {
2514 continue;
2515 }
2516
2517 partitionEntry = &partitionList->PartitionEntry[i];
2518
2519 //
2520 // Check if empty, or describes extended partiton or hasn't changed.
2521 //
2522
2523 if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
2524 IsContainerPartition(partitionEntry->PartitionType)) {
2525 continue;
2526 }
2527
2528 //
2529 // Check if new partition starts where this partition starts.
2530 //
2531
2532 if (partitionEntry->StartingOffset.QuadPart !=
2533 deviceExtension->StartingOffset.QuadPart) {
2534 continue;
2535 }
2536
2537 //
2538 // Check if partition length is the same.
2539 //
2540
2541 if (partitionEntry->PartitionLength.QuadPart ==
2542 deviceExtension->PartitionLength.QuadPart) {
2543
2544 //
2545 // Partitions match. Update partition number.
2546 //
2547
2548 partitionEntry->PartitionNumber =
2549 diskData->PartitionNumber;
2550 break;
2551 }
2552
2553 } while (TRUE);
2554 }
2555
2556 //
2557 // Copy partition information to system buffer.
2558 //
2559
2560 RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
2561 partitionList,
2562 tempSize);
2563 status = STATUS_SUCCESS;
2564 Irp->IoStatus.Information = tempSize;
2565
2566 //
2567 // Finally, free the buffer allocated by reading the
2568 // partition table.
2569 //
2570
2571 ExFreePool(partitionList);
2572 }
2573
2574 break;
2575
2576 case IOCTL_DISK_SET_DRIVE_LAYOUT:
2577
2578 {
2579
2580 //
2581 // Update the disk with new partition information.
2582 //
2583
2584 PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer;
2585
2586 //
2587 // Validate buffer length.
2588 //
2589
2590 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2591 sizeof(DRIVE_LAYOUT_INFORMATION)) {
2592
2593 status = STATUS_INFO_LENGTH_MISMATCH;
2594 break;
2595 }
2596
2597 length = sizeof(DRIVE_LAYOUT_INFORMATION) +
2598 (partitionList->PartitionCount - 1) * sizeof(PARTITION_INFORMATION);
2599
2600
2601 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2602 length) {
2603
2604 status = STATUS_BUFFER_TOO_SMALL;
2605 break;
2606 }
2607
2608 //
2609 // Verify that device object is for physical disk.
2610 //
2611
2612 if (deviceExtension->PhysicalDevice->DeviceExtension != deviceExtension) {
2613 status = STATUS_INVALID_PARAMETER;
2614 break;
2615 }
2616
2617 //
2618 // Walk through partition table comparing partitions to
2619 // existing partitions to create, delete and change
2620 // device objects as necessary.
2621 //
2622
2623 UpdateDeviceObjects(DeviceObject,
2624 Irp);
2625
2626 //
2627 // Write changes to disk.
2628 //
2629
2630 status = IoWritePartitionTable(
2631 deviceExtension->DeviceObject,
2632 deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2633 deviceExtension->DiskGeometry->Geometry.SectorsPerTrack,
2634 deviceExtension->DiskGeometry->Geometry.TracksPerCylinder,
2635 partitionList);
2636 }
2637
2638 //
2639 // Update IRP with bytes returned.
2640 //
2641
2642 if (NT_SUCCESS(status)) {
2643 Irp->IoStatus.Information = length;
2644 }
2645
2646 break;
2647
2648 case IOCTL_DISK_REASSIGN_BLOCKS:
2649
2650 //
2651 // Map defective blocks to new location on disk.
2652 //
2653
2654 {
2655
2656 PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer;
2657 ULONG bufferSize;
2658 ULONG blockNumber;
2659 ULONG blockCount;
2660
2661 //
2662 // Validate buffer length.
2663 //
2664
2665 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2666 sizeof(REASSIGN_BLOCKS)) {
2667
2668 status = STATUS_INFO_LENGTH_MISMATCH;
2669 break;
2670 }
2671
2672 bufferSize = sizeof(REASSIGN_BLOCKS) +
2673 (badBlocks->Count - 1) * sizeof(ULONG);
2674
2675 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2676 bufferSize) {
2677
2678 status = STATUS_INFO_LENGTH_MISMATCH;
2679 break;
2680 }
2681
2682 //
2683 // Build the data buffer to be transferred in the input buffer.
2684 // The format of the data to the device is:
2685 //
2686 // 2 bytes Reserved
2687 // 2 bytes Length
2688 // x * 4 btyes Block Address
2689 //
2690 // All values are big endian.
2691 //
2692
2693 badBlocks->Reserved = 0;
2694 blockCount = badBlocks->Count;
2695
2696 //
2697 // Convert # of entries to # of bytes.
2698 //
2699
2700 blockCount *= 4;
2701 badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF);
2702 badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00);
2703
2704 //
2705 // Convert back to number of entries.
2706 //
2707
2708 blockCount /= 4;
2709
2710 for (; blockCount > 0; blockCount--) {
2711
2712 blockNumber = badBlocks->BlockNumber[blockCount-1];
2713
2714 REVERSE_BYTES((PFOUR_BYTE) &badBlocks->BlockNumber[blockCount-1],
2715 (PFOUR_BYTE) &blockNumber);
2716 }
2717
2718 srb->CdbLength = 6;
2719
2720 cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS;
2721
2722 //
2723 // Set timeout value.
2724 //
2725
2726 srb->TimeOutValue = deviceExtension->TimeOutValue;
2727
2728 status = ScsiClassSendSrbSynchronous(DeviceObject,
2729 srb,
2730 badBlocks,
2731 bufferSize,
2732 TRUE);
2733
2734 Irp->IoStatus.Status = status;
2735 Irp->IoStatus.Information = 0;
2736 ExFreePool(srb);
2737 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2738 }
2739
2740 return(status);
2741
2742 case IOCTL_DISK_IS_WRITABLE:
2743
2744 //
2745 // Determine if the device is writable.
2746 //
2747
2748 modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
2749
2750 if (modeData == NULL) {
2751 status = STATUS_INSUFFICIENT_RESOURCES;
2752 break;
2753 }
2754
2755 RtlZeroMemory(modeData, MODE_DATA_SIZE);
2756
2757 length = ScsiClassModeSense(DeviceObject,
2758 (PCHAR) modeData,
2759 MODE_DATA_SIZE,
2760 MODE_SENSE_RETURN_ALL);
2761
2762 if (length < sizeof(MODE_PARAMETER_HEADER)) {
2763
2764 //
2765 // Retry the request in case of a check condition.
2766 //
2767
2768 length = ScsiClassModeSense(DeviceObject,
2769 (PCHAR) modeData,
2770 MODE_DATA_SIZE,
2771 MODE_SENSE_RETURN_ALL);
2772
2773 if (length < sizeof(MODE_PARAMETER_HEADER)) {
2774 status = STATUS_IO_DEVICE_ERROR;
2775 ExFreePool(modeData);
2776 break;
2777 }
2778 }
2779
2780 if (modeData->DeviceSpecificParameter & MODE_DSP_WRITE_PROTECT) {
2781 status = STATUS_MEDIA_WRITE_PROTECTED;
2782 } else {
2783 status = STATUS_SUCCESS;
2784 }
2785
2786 ExFreePool(modeData);
2787 break;
2788
2789 case IOCTL_DISK_INTERNAL_SET_VERIFY:
2790
2791 //
2792 // If the caller is kernel mode, set the verify bit.
2793 //
2794
2795 if (Irp->RequestorMode == KernelMode) {
2796 DeviceObject->Flags |= DO_VERIFY_VOLUME;
2797 }
2798 status = STATUS_SUCCESS;
2799 break;
2800
2801 case IOCTL_DISK_INTERNAL_CLEAR_VERIFY:
2802
2803 //
2804 // If the caller is kernel mode, clear the verify bit.
2805 //
2806
2807 if (Irp->RequestorMode == KernelMode) {
2808 DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
2809 }
2810 status = STATUS_SUCCESS;
2811 break;
2812
2813 case IOCTL_DISK_FIND_NEW_DEVICES:
2814
2815 //
2816 // Search for devices that have been powered on since the last
2817 // device search or system initialization.
2818 //
2819
2820 DebugPrint((3,"CdRomDeviceControl: Find devices\n"));
2821 status = DriverEntry(DeviceObject->DriverObject,
2822 NULL);
2823
2824 Irp->IoStatus.Status = status;
2825 ExFreePool(srb);
2826 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2827 return status;
2828
2829 case IOCTL_DISK_MEDIA_REMOVAL:
2830
2831 //
2832 // If the disk is not removable then don't allow this command.
2833 //
2834
2835 if (!(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
2836 status = STATUS_INVALID_DEVICE_REQUEST;
2837 break;
2838 }
2839
2840 //
2841 // Fall through and let the class driver process the request.
2842 //
2843
2844 case IOCTL_DISK_GET_LENGTH_INFO:
2845
2846 //
2847 // Validate buffer length.
2848 //
2849
2850 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2851 sizeof(GET_LENGTH_INFORMATION)) {
2852 status = STATUS_BUFFER_TOO_SMALL;
2853
2854 } else {
2855
2856 PGET_LENGTH_INFORMATION lengthInformation = Irp->AssociatedIrp.SystemBuffer;
2857
2858 //
2859 // Update the geometry in case it has changed.
2860 //
2861
2862 status = UpdateRemovableGeometry (DeviceObject, Irp);
2863
2864 if (!NT_SUCCESS(status)) {
2865
2866 //
2867 // Note the drive is not ready.
2868 //
2869
2870 diskData->DriveNotReady = TRUE;
2871 break;
2872 }
2873
2874 //
2875 // Note the drive is now ready.
2876 //
2877
2878 diskData->DriveNotReady = FALSE;
2879
2880 //
2881 // Output data, and return
2882 //
2883
2884 lengthInformation->Length.QuadPart = deviceExtension->PartitionLength.QuadPart;
2885 status = STATUS_SUCCESS;
2886 Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
2887 }
2888
2889 break;
2890
2891 default:
2892
2893 //
2894 // Free the Srb, since it is not needed.
2895 //
2896
2897 ExFreePool(srb);
2898
2899 //
2900 // Pass the request to the common device control routine.
2901 //
2902
2903 return(ScsiClassDeviceControl(DeviceObject, Irp));
2904
2905 break;
2906
2907 } // end switch( ...
2908
2909 Irp->IoStatus.Status = status;
2910
2911 if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
2912
2913 IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
2914 }
2915
2916 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2917 ExFreePool(srb);
2918 return(status);
2919
2920 } // end ScsiDiskDeviceControl()
2921 \f
2922 NTSTATUS
2923 NTAPI
2924 ScsiDiskShutdownFlush (
2925 IN PDEVICE_OBJECT DeviceObject,
2926 IN PIRP Irp
2927 )
2928
2929 /*++
2930
2931 Routine Description:
2932
2933 This routine is called for a shutdown and flush IRPs. These are sent by the
2934 system before it actually shuts down or when the file system does a flush.
2935 A synchronize cache command is sent to the device if it is write caching.
2936 If the device is removable an unlock command will be sent. This routine
2937 will sent a shutdown or flush Srb to the port driver.
2938
2939 Arguments:
2940
2941 DriverObject - Pointer to device object to being shutdown by system.
2942
2943 Irp - IRP involved.
2944
2945 Return Value:
2946
2947 NT Status
2948
2949 --*/
2950
2951 {
2952 PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
2953 PIO_STACK_LOCATION irpStack;
2954 PSCSI_REQUEST_BLOCK srb;
2955 NTSTATUS status;
2956 PCDB cdb;
2957
2958 //
2959 // Allocate SCSI request block.
2960 //
2961
2962 srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
2963
2964 if (srb == NULL) {
2965
2966 //
2967 // Set the status and complete the request.
2968 //
2969
2970 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
2971 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2972 return(STATUS_INSUFFICIENT_RESOURCES);
2973 }
2974
2975 RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
2976
2977 //
2978 // Write length to SRB.
2979 //
2980
2981 srb->Length = SCSI_REQUEST_BLOCK_SIZE;
2982
2983 //
2984 // Set SCSI bus address.
2985 //
2986
2987 srb->PathId = deviceExtension->PathId;
2988 srb->TargetId = deviceExtension->TargetId;
2989 srb->Lun = deviceExtension->Lun;
2990
2991 //
2992 // Set timeout value and mark the request as not being a tagged request.
2993 //
2994
2995 srb->TimeOutValue = deviceExtension->TimeOutValue * 4;
2996 srb->QueueTag = SP_UNTAGGED;
2997 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
2998 srb->SrbFlags = deviceExtension->SrbFlags;
2999
3000 //
3001 // If the write cache is enabled then send a synchronize cache request.
3002 //
3003
3004 if (deviceExtension->DeviceFlags & DEV_WRITE_CACHE) {
3005
3006 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
3007 srb->CdbLength = 10;
3008
3009 srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
3010
3011 status = ScsiClassSendSrbSynchronous(DeviceObject,
3012 srb,
3013 NULL,
3014 0,
3015 TRUE);
3016
3017 DebugPrint((1, "ScsiDiskShutdownFlush: Synchonize cache sent. Status = %lx\n", status ));
3018 }
3019
3020 //
3021 // Unlock the device if it is removable and this is a shutdown.
3022 //
3023
3024 irpStack = IoGetCurrentIrpStackLocation(Irp);
3025
3026 if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA &&
3027 irpStack->MajorFunction == IRP_MJ_SHUTDOWN) {
3028
3029 srb->CdbLength = 6;
3030 cdb = (PVOID) srb->Cdb;
3031 cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
3032 cdb->MEDIA_REMOVAL.Prevent = FALSE;
3033
3034 //
3035 // Set timeout value.
3036 //
3037
3038 srb->TimeOutValue = deviceExtension->TimeOutValue;
3039 status = ScsiClassSendSrbSynchronous(DeviceObject,
3040 srb,
3041 NULL,
3042 0,
3043 TRUE);
3044
3045 DebugPrint((1, "ScsiDiskShutdownFlush: Unlock device request sent. Status = %lx\n", status ));
3046 }
3047
3048 srb->CdbLength = 0;
3049
3050 //
3051 // Save a few parameters in the current stack location.
3052 //
3053
3054 srb->Function = irpStack->MajorFunction == IRP_MJ_SHUTDOWN ?
3055 SRB_FUNCTION_SHUTDOWN : SRB_FUNCTION_FLUSH;
3056
3057 //
3058 // Set the retry count to zero.
3059 //
3060
3061 irpStack->Parameters.Others.Argument4 = (PVOID) 0;
3062
3063 //
3064 // Set up IoCompletion routine address.
3065 //
3066
3067 IoSetCompletionRoutine(Irp, ScsiClassIoComplete, srb, TRUE, TRUE, TRUE);
3068
3069 //
3070 // Get next stack location and
3071 // set major function code.
3072 //
3073
3074 irpStack = IoGetNextIrpStackLocation(Irp);
3075
3076 irpStack->MajorFunction = IRP_MJ_SCSI;
3077
3078 //
3079 // Set up SRB for execute scsi request.
3080 // Save SRB address in next stack for port driver.
3081 //
3082
3083 irpStack->Parameters.Scsi.Srb = srb;
3084
3085 //
3086 // Set up Irp Address.
3087 //
3088
3089 srb->OriginalRequest = Irp;
3090
3091 //
3092 // Call the port driver to process the request.
3093 //
3094
3095 return(IoCallDriver(deviceExtension->PortDeviceObject, Irp));
3096
3097 } // end ScsiDiskShutdown()
3098
3099 \f
3100 BOOLEAN
3101 NTAPI
3102 IsFloppyDevice(
3103 PDEVICE_OBJECT DeviceObject
3104 )
3105 /*++
3106
3107 Routine Description:
3108
3109 The routine performs the necessary functions to determine if a device is
3110 really a floppy rather than a harddisk. This is done by a mode sense
3111 command. First, a check is made to see if the medimum type is set. Second
3112 a check is made for the flexible parameters mode page. Also a check is
3113 made to see if the write cache is enabled.
3114
3115 Arguments:
3116
3117 DeviceObject - Supplies the device object to be tested.
3118
3119 Return Value:
3120
3121 Return TRUE if the indicated device is a floppy.
3122
3123 --*/
3124 {
3125 PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
3126 PVOID modeData;
3127 PUCHAR pageData;
3128 ULONG length;
3129
3130 PAGED_CODE();
3131
3132 modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
3133
3134 if (modeData == NULL) {
3135 return(FALSE);
3136 }
3137
3138 RtlZeroMemory(modeData, MODE_DATA_SIZE);
3139
3140 length = ScsiClassModeSense(DeviceObject,
3141 modeData,
3142 MODE_DATA_SIZE,
3143 MODE_SENSE_RETURN_ALL);
3144
3145 if (length < sizeof(MODE_PARAMETER_HEADER)) {
3146
3147 //
3148 // Retry the request in case of a check condition.
3149 //
3150
3151 length = ScsiClassModeSense(DeviceObject,
3152 modeData,
3153 MODE_DATA_SIZE,
3154 MODE_SENSE_RETURN_ALL);
3155
3156 if (length < sizeof(MODE_PARAMETER_HEADER)) {
3157
3158 ExFreePool(modeData);
3159 return(FALSE);
3160
3161 }
3162 }
3163
3164 //
3165 // If the length is greater than length indicated by the mode data reset
3166 // the data to the mode data.
3167 //
3168
3169 if (length > (ULONG) ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) {
3170 length = ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1;
3171 }
3172
3173 //
3174 // Look for the flexible disk mode page.
3175 //
3176
3177 pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE, TRUE);
3178
3179 if (pageData != NULL) {
3180
3181 DebugPrint((1, "Scsidisk: Flexible disk page found, This is a floppy.\n"));
3182 ExFreePool(modeData);
3183 return(TRUE);
3184 }
3185
3186 //
3187 // Check to see if the write cache is enabled.
3188 //
3189
3190 pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_CACHING, TRUE);
3191
3192 //
3193 // Assume that write cache is disabled or not supported.
3194 //
3195
3196 deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3197
3198 //
3199 // Check if valid caching page exists.
3200 //
3201
3202 if (pageData != NULL) {
3203
3204 //
3205 // Check if write cache is disabled.
3206 //
3207
3208 if (((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable) {
3209
3210 DebugPrint((1,
3211 "SCSIDISK: Disk write cache enabled\n"));
3212
3213 //
3214 // Check if forced unit access (FUA) is supported.
3215 //
3216
3217 if (((PMODE_PARAMETER_HEADER)modeData)->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED) {
3218
3219 deviceExtension->DeviceFlags |= DEV_WRITE_CACHE;
3220
3221 } else {
3222
3223 DebugPrint((1,
3224 "SCSIDISK: Disk does not support FUA or DPO\n"));
3225
3226 //
3227 // TODO: Log this.
3228 //
3229
3230 }
3231 }
3232 }
3233
3234 ExFreePool(modeData);
3235 return(FALSE);
3236
3237 } // end IsFloppyDevice()
3238
3239 \f
3240 BOOLEAN
3241 NTAPI
3242 ScsiDiskModeSelect(
3243 IN PDEVICE_OBJECT DeviceObject,
3244 IN PCHAR ModeSelectBuffer,
3245 IN ULONG Length,
3246 IN BOOLEAN SavePage
3247 )
3248
3249 /*++
3250
3251 Routine Description:
3252
3253 This routine sends a mode select command.
3254
3255 Arguments:
3256
3257 DeviceObject - Supplies the device object associated with this request.
3258
3259 ModeSelectBuffer - Supplies a buffer containing the page data.
3260
3261 Length - Supplies the length in bytes of the mode select buffer.
3262
3263 SavePage - Indicates that parameters should be written to disk.
3264
3265 Return Value:
3266
3267 Length of the transferred data is returned.
3268
3269 --*/
3270 {
3271 PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
3272 PCDB cdb;
3273 SCSI_REQUEST_BLOCK srb;
3274 ULONG retries = 1;
3275 ULONG length2;
3276 NTSTATUS status;
3277 ULONG_PTR buffer;
3278 PMODE_PARAMETER_BLOCK blockDescriptor;
3279
3280 PAGED_CODE();
3281
3282 length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
3283
3284 //
3285 // Allocate buffer for mode select header, block descriptor, and mode page.
3286 //
3287
3288 buffer = (ULONG_PTR)ExAllocatePool(NonPagedPoolCacheAligned,length2);
3289
3290 RtlZeroMemory((PVOID)buffer, length2);
3291
3292 //
3293 // Set length in header to size of mode page.
3294 //
3295
3296 ((PMODE_PARAMETER_HEADER)buffer)->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
3297
3298 blockDescriptor = (PMODE_PARAMETER_BLOCK)(buffer + 1);
3299
3300 //
3301 // Set size
3302 //
3303
3304 blockDescriptor->BlockLength[1]=0x02;
3305
3306 //
3307 // Copy mode page to buffer.
3308 //
3309
3310 RtlCopyMemory((PVOID)(buffer + 3), ModeSelectBuffer, Length);
3311
3312 //
3313 // Zero SRB.
3314 //
3315
3316 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
3317
3318 //
3319 // Build the MODE SELECT CDB.
3320 //
3321
3322 srb.CdbLength = 6;
3323 cdb = (PCDB)srb.Cdb;
3324
3325 //
3326 // Set timeout value from device extension.
3327 //
3328
3329 srb.TimeOutValue = deviceExtension->TimeOutValue * 2;
3330
3331 cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
3332 cdb->MODE_SELECT.SPBit = SavePage;
3333 cdb->MODE_SELECT.PFBit = 1;
3334 cdb->MODE_SELECT.ParameterListLength = (UCHAR)(length2);
3335
3336 Retry:
3337
3338 status = ScsiClassSendSrbSynchronous(DeviceObject,
3339 &srb,
3340 (PVOID)buffer,
3341 length2,
3342 TRUE);
3343
3344
3345 if (status == STATUS_VERIFY_REQUIRED) {
3346
3347 //
3348 // Routine ScsiClassSendSrbSynchronous does not retry requests returned with
3349 // this status.
3350 //
3351
3352 if (retries--) {
3353
3354 //
3355 // Retry request.
3356 //
3357
3358 goto Retry;
3359 }
3360
3361 } else if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
3362 status = STATUS_SUCCESS;
3363 }
3364
3365 ExFreePool((PVOID)buffer);
3366
3367 if (NT_SUCCESS(status)) {
3368 return(TRUE);
3369 } else {
3370 return(FALSE);
3371 }
3372
3373 } // end SciDiskModeSelect()
3374
3375 \f
3376 VOID
3377 NTAPI
3378 DisableWriteCache(
3379 IN PDEVICE_OBJECT DeviceObject,
3380 IN PSCSI_INQUIRY_DATA LunInfo
3381 )
3382
3383 {
3384 PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
3385 PINQUIRYDATA InquiryData = (PINQUIRYDATA)LunInfo->InquiryData;
3386 BAD_CONTROLLER_INFORMATION const *controller;
3387 ULONG j,length;
3388 PVOID modeData;
3389 PUCHAR pageData;
3390
3391 for (j = 0; j < NUMBER_OF_BAD_CONTROLLERS; j++) {
3392
3393 controller = &ScsiDiskBadControllers[j];
3394
3395 if (!controller->DisableWriteCache || strncmp(controller->InquiryString, (PCCHAR)InquiryData->VendorId, strlen(controller->InquiryString))) {
3396 continue;
3397 }
3398
3399 DebugPrint((1, "ScsiDisk.DisableWriteCache, Found bad controller! %s\n", controller->InquiryString));
3400
3401 modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
3402
3403 if (modeData == NULL) {
3404
3405 DebugPrint((1,
3406 "ScsiDisk.DisableWriteCache: Check for write-cache enable failed\n"));
3407 return;
3408 }
3409
3410 RtlZeroMemory(modeData, MODE_DATA_SIZE);
3411
3412 length = ScsiClassModeSense(DeviceObject,
3413 modeData,
3414 MODE_DATA_SIZE,
3415 MODE_SENSE_RETURN_ALL);
3416
3417 if (length < sizeof(MODE_PARAMETER_HEADER)) {
3418
3419 //
3420 // Retry the request in case of a check condition.
3421 //
3422
3423 length = ScsiClassModeSense(DeviceObject,
3424 modeData,
3425 MODE_DATA_SIZE,
3426 MODE_SENSE_RETURN_ALL);
3427
3428 if (length < sizeof(MODE_PARAMETER_HEADER)) {
3429
3430
3431 DebugPrint((1,
3432 "ScsiDisk.DisableWriteCache: Mode Sense failed\n"));
3433
3434 ExFreePool(modeData);
3435 return;
3436
3437 }
3438 }
3439
3440 //
3441 // If the length is greater than length indicated by the mode data reset
3442 // the data to the mode data.
3443 //
3444
3445 if (length > (ULONG) ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) {
3446 length = ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1;
3447 }
3448
3449 //
3450 // Check to see if the write cache is enabled.
3451 //
3452
3453 pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_CACHING, TRUE);
3454
3455 //
3456 // Assume that write cache is disabled or not supported.
3457 //
3458
3459 deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3460
3461 //
3462 // Check if valid caching page exists.
3463 //
3464
3465 if (pageData != NULL) {
3466
3467 BOOLEAN savePage = FALSE;
3468
3469 savePage = (BOOLEAN)(((PMODE_CACHING_PAGE)pageData)->PageSavable);
3470
3471 //
3472 // Check if write cache is disabled.
3473 //
3474