* Sync up to trunk r55544.
[reactos.git] / drivers / storage / class / disk_new / pnp.c
1 /*++
2
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4
5 Module Name:
6
7 pnp.c
8
9 Abstract:
10
11 SCSI disk class driver
12
13 Environment:
14
15 kernel mode only
16
17 Notes:
18
19 Revision History:
20
21 --*/
22
23 #include "disk.h"
24
25 extern NTSYSAPI ULONG InitSafeBootMode;
26
27 #ifdef ALLOC_PRAGMA
28
29 #pragma alloc_text(PAGE, DiskAddDevice)
30 #pragma alloc_text(PAGE, DiskInitFdo)
31 #pragma alloc_text(PAGE, DiskInitPdo)
32 #pragma alloc_text(PAGE, DiskStartFdo)
33 #pragma alloc_text(PAGE, DiskStartPdo)
34 #pragma alloc_text(PAGE, DiskQueryId)
35 #pragma alloc_text(PAGE, DiskGenerateDeviceName)
36 #pragma alloc_text(PAGE, DiskCreateSymbolicLinks)
37 #pragma alloc_text(PAGE, DiskDeleteSymbolicLinks)
38 #pragma alloc_text(PAGE, DiskRemoveDevice)
39
40 #endif
41
42 \f
43 NTSTATUS
44 DiskAddDevice(
45 IN PDRIVER_OBJECT DriverObject,
46 IN PDEVICE_OBJECT PhysicalDeviceObject
47 )
48
49 /*++
50
51 Routine Description:
52
53 This routine gets a port drivers capabilities, obtains the
54 inquiry data, searches the SCSI bus for the port driver and creates
55 the device objects for the disks found.
56
57 Arguments:
58
59 DriverObject - Pointer to driver object created by system.
60
61 Pdo - Device object use to send requests to port driver.
62
63 Return Value:
64
65 True is returned if one disk was found and successfully created.
66
67 --*/
68
69 {
70 ULONG rootPartitionMountable = FALSE;
71
72 PCONFIGURATION_INFORMATION configurationInformation;
73 ULONG diskCount;
74
75 NTSTATUS status;
76
77 PAGED_CODE();
78
79 //
80 // See if we should be allowing file systems to mount on partition zero.
81 //
82
83 TRY {
84 HANDLE deviceKey;
85
86 UNICODE_STRING diskKeyName;
87 OBJECT_ATTRIBUTES objectAttributes;
88 HANDLE diskKey;
89
90 RTL_QUERY_REGISTRY_TABLE queryTable[2];
91
92 status = IoOpenDeviceRegistryKey(PhysicalDeviceObject,
93 PLUGPLAY_REGKEY_DEVICE,
94 KEY_READ,
95 &deviceKey);
96
97 if(!NT_SUCCESS(status)) {
98 DebugPrint((1, "DiskAddDevice: Error %#08lx opening device key "
99 "for pdo %#08lx\n",
100 status, PhysicalDeviceObject));
101 LEAVE;
102 }
103
104 RtlInitUnicodeString(&diskKeyName, L"Disk");
105 InitializeObjectAttributes(&objectAttributes,
106 &diskKeyName,
107 OBJ_CASE_INSENSITIVE,
108 deviceKey,
109 NULL);
110
111 status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes);
112 ZwClose(deviceKey);
113
114 if(!NT_SUCCESS(status)) {
115 DebugPrint((1, "DiskAddDevice: Error %#08lx opening disk key "
116 "for pdo %#08lx device key %#x\n",
117 status, PhysicalDeviceObject, deviceKey));
118 LEAVE;
119 }
120
121 RtlZeroMemory(queryTable, sizeof(queryTable));
122
123 queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
124 queryTable[0].Name = L"RootPartitionMountable";
125 queryTable[0].EntryContext = &(rootPartitionMountable);
126
127 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
128 diskKey,
129 queryTable,
130 NULL,
131 NULL);
132
133 if(!NT_SUCCESS(status)) {
134 DebugPrint((1, "DiskAddDevice: Error %#08lx reading value from "
135 "disk key %#x for pdo %#08lx\n",
136 status, diskKey, PhysicalDeviceObject));
137 }
138
139 ZwClose(diskKey);
140
141 } FINALLY {
142
143 //
144 // Do nothing.
145 //
146
147 if(!NT_SUCCESS(status)) {
148 DebugPrint((1, "DiskAddDevice: Will %sallow file system to mount on "
149 "partition zero of disk %#08lx\n",
150 (rootPartitionMountable ? "" : "not "),
151 PhysicalDeviceObject));
152 }
153 }
154
155 //
156 // Create device objects for disk
157 //
158
159 diskCount = 0;
160
161 status = DiskCreateFdo(
162 DriverObject,
163 PhysicalDeviceObject,
164 &diskCount,
165 (BOOLEAN) !rootPartitionMountable
166 );
167
168 //
169 // Get the number of disks already initialized.
170 //
171
172 configurationInformation = IoGetConfigurationInformation();
173
174 if (NT_SUCCESS(status)) {
175
176 //
177 // Increment system disk device count.
178 //
179
180 configurationInformation->DiskCount++;
181
182 }
183
184 return status;
185
186 } // end DiskAddDevice()
187
188
189 \f
190 NTSTATUS
191 DiskInitFdo(
192 IN PDEVICE_OBJECT Fdo
193 )
194
195 /*++
196
197 Routine Description:
198
199 This routine is called to do one-time initialization of new device objects
200
201
202 Arguments:
203
204 Fdo - a pointer to the functional device object for this device
205
206 Return Value:
207
208 status
209
210 --*/
211
212 {
213 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
214
215 PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData;
216
217 ULONG srbFlags = 0;
218
219 ULONG timeOut = 0;
220
221 ULONG bytesPerSector;
222 UCHAR sectorShift;
223
224 BOOLEAN dmActive = FALSE;
225 PULONG dmSkew;
226 ULONG dmByteSkew;
227
228 NTSTATUS status;
229
230 PAGED_CODE();
231
232 //
233 // Build the lookaside list for srb's for the physical disk. Should only
234 // need a couple. If this fails then we don't have an emergency SRB so
235 // fail the call to initialize.
236 //
237
238 ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) fdoExtension,
239 PARTITION0_LIST_SIZE);
240
241 //
242 // Because all requests share a common sense buffer, it is possible
243 // for the buffer to be overwritten if the port driver completes
244 // multiple failed requests that require a request sense before the
245 // class driver's completion routine can consume the data in the buffer.
246 // To prevent this, we allow the port driver to allocate a unique sense
247 // buffer each time it needs one. We are responsible for freeing this
248 // buffer. This also allows the adapter to be configured to support
249 // additional sense data beyond the minimum 18 bytes.
250 //
251
252 fdoExtension->SrbFlags = SRB_FLAGS_PORT_DRIVER_ALLOCSENSE;
253
254 //
255 // Initialize the srb flags.
256 //
257
258 if (fdoExtension->DeviceDescriptor->CommandQueueing &&
259 fdoExtension->AdapterDescriptor->CommandQueueing) {
260
261 fdoExtension->SrbFlags = SRB_FLAGS_QUEUE_ACTION_ENABLE;
262
263 }
264
265 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
266 SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
267 }
268
269 //
270 // Look for controllers that require special flags.
271 //
272
273 ClassScanForSpecial(fdoExtension, DiskBadControllers, DiskSetSpecialHacks);
274
275 //
276 // Look into the registry to see if this device
277 // requires special attention - [ like a hack ]
278 //
279
280 DiskScanRegistryForSpecial(fdoExtension);
281
282 srbFlags = fdoExtension->SrbFlags;
283
284 //
285 // Clear buffer for drive geometry.
286 //
287
288 RtlZeroMemory(&(fdoExtension->DiskGeometry),
289 sizeof(DISK_GEOMETRY));
290
291 //
292 // Allocate request sense buffer.
293 //
294
295 fdoExtension->SenseData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
296 SENSE_BUFFER_SIZE,
297 DISK_TAG_START);
298
299 if (fdoExtension->SenseData == NULL) {
300
301 //
302 // The buffer can not be allocated.
303 //
304
305 DebugPrint((1, "DiskInitFdo: Can not allocate request sense buffer\n"));
306
307 status = STATUS_INSUFFICIENT_RESOURCES;
308 return status;
309 }
310
311 //
312 // Physical device object will describe the entire
313 // device, starting at byte offset 0.
314 //
315
316 fdoExtension->CommonExtension.StartingOffset.QuadPart = (LONGLONG)(0);
317
318 //
319 // Set timeout value in seconds.
320 //
321
322 timeOut = ClassQueryTimeOutRegistryValue(Fdo);
323 if (timeOut) {
324 fdoExtension->TimeOutValue = timeOut;
325 } else {
326 fdoExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
327 }
328
329 //
330 // If this is a removable drive, build an entry in devicemap\scsi
331 // indicating it's physicaldriveN name, set up the appropriate
332 // update partitions routine and set the flags correctly.
333 // note: only do this after the timeout value is set, above.
334 //
335
336 if (fdoExtension->DeviceDescriptor->RemovableMedia) {
337 ClassUpdateInformationInRegistry( Fdo,
338 "PhysicalDrive",
339 fdoExtension->DeviceNumber,
340 NULL,
341 0);
342 //
343 // Enable media change notification for removable disks
344 //
345 ClassInitializeMediaChangeDetection(fdoExtension,
346 "Disk");
347
348 SET_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA);
349 diskData->UpdatePartitionRoutine = DiskUpdateRemovablePartitions;
350
351 } else {
352
353 SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
354 diskData->UpdatePartitionRoutine = DiskUpdatePartitions;
355
356 }
357
358 //
359 // Read the drive capacity. Don't use the disk version of the routine here
360 // since we don't know the disk signature yet - the disk version will
361 // attempt to determine the BIOS reported geometry.
362 //
363
364 status = ClassReadDriveCapacity(Fdo);
365
366 //
367 // If the read capcity failed then just return, unless this is a
368 // removable disk where a device object partition needs to be created.
369 //
370
371 if (!NT_SUCCESS(status) &&
372 !(Fdo->Characteristics & FILE_REMOVABLE_MEDIA)) {
373
374 DebugPrint((1,
375 "DiskInitFdo: Can't read capacity for device %p\n",
376 Fdo));
377
378 if (fdoExtension->DeviceDescriptor->RemovableMedia) {
379 fdoExtension->DiskGeometry.MediaType = RemovableMedia;
380 Fdo->Flags &= ~DO_VERIFY_VOLUME;
381 } else {
382 fdoExtension->DiskGeometry.MediaType = FixedMedia;
383 }
384
385 status = STATUS_SUCCESS;
386 }
387
388 //
389 // Set up sector size fields.
390 //
391 // Stack variables will be used to update
392 // the partition device extensions.
393 //
394 // The device extension field SectorShift is
395 // used to calculate sectors in I/O transfers.
396 //
397 // The DiskGeometry structure is used to service
398 // IOCTls used by the format utility.
399 //
400
401 bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
402
403 //
404 // Make sure sector size is not zero.
405 //
406
407 if (bytesPerSector == 0) {
408
409 //
410 // Default sector size for disk is 512.
411 //
412
413 bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector = 512;
414 }
415
416 sectorShift = fdoExtension->SectorShift;
417
418 //
419 // Determine is DM Driver is loaded on an IDE drive that is
420 // under control of Atapi - this could be either a crashdump or
421 // an Atapi device is sharing the controller with an IDE disk.
422 //
423
424 HalExamineMBR(fdoExtension->CommonExtension.DeviceObject,
425 fdoExtension->DiskGeometry.BytesPerSector,
426 (ULONG)0x54,
427 (PVOID*)&dmSkew);
428
429 if (dmSkew) {
430
431 //
432 // Update the device extension, so that the call to IoReadPartitionTable
433 // will get the correct information. Any I/O to this disk will have
434 // to be skewed by *dmSkew sectors aka DMByteSkew.
435 //
436
437 fdoExtension->DMSkew = *dmSkew;
438 fdoExtension->DMActive = TRUE;
439 fdoExtension->DMByteSkew = fdoExtension->DMSkew * bytesPerSector;
440
441 //
442 // Save away the infomation that we need, since this deviceExtension will soon be
443 // blown away.
444 //
445
446 dmActive = TRUE;
447 dmByteSkew = fdoExtension->DMByteSkew;
448
449 }
450
451 #if defined(_X86_)
452 //
453 // Try to read the signature off the disk and determine the correct drive
454 // geometry based on that. This requires rereading the disk size to get
455 // the cylinder count updated correctly.
456 //
457
458 if(fdoExtension->DeviceDescriptor->RemovableMedia == FALSE) {
459 DiskReadSignature(Fdo);
460 DiskReadDriveCapacity(Fdo);
461 }
462 #endif
463
464 //
465 // Register interfaces for this device
466 //
467 {
468 UNICODE_STRING interfaceName;
469
470 RtlInitUnicodeString(&interfaceName, NULL);
471
472 status = IoRegisterDeviceInterface(fdoExtension->LowerPdo,
473 (LPGUID) &DiskClassGuid,
474 NULL,
475 &interfaceName);
476
477 if(NT_SUCCESS(status)) {
478
479 diskData->DiskInterfaceString = interfaceName;
480 status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
481
482 } else {
483 interfaceName.Buffer = NULL;
484 }
485
486 if(!NT_SUCCESS(status)) {
487
488 DebugPrint((1, "DiskInitFdo: Unable to register or set disk DCA "
489 "for fdo %p [%lx]\n", Fdo, status));
490
491 RtlFreeUnicodeString(&interfaceName);
492 RtlInitUnicodeString(&(diskData->DiskInterfaceString), NULL);
493 }
494 }
495
496 DiskCreateSymbolicLinks(Fdo);
497
498 //
499 // Determine the type of disk and enable failure preiction in the hardware
500 // and enable failure prediction polling.
501 //
502
503 if (InitSafeBootMode == 0)
504 {
505 DiskDetectFailurePrediction(fdoExtension,
506 &diskData->FailurePredictionCapability);
507
508 if (diskData->FailurePredictionCapability != FailurePredictionNone)
509 {
510 //
511 // Cool, we've got some sort of failure prediction, enable it
512 // at the hardware and then enable polling for it
513 //
514
515 //
516 // By default we allow performance to be degradeded if failure
517 // prediction is enabled.
518 //
519 // TODO: Make a registry entry ?
520 //
521
522 diskData->AllowFPPerfHit = TRUE;
523
524 //
525 // Enable polling only after Atapi and SBP2 add support for the new
526 // SRB flag that indicates that the request should not reset the
527 // drive spin down idle timer.
528 //
529
530 status = DiskEnableDisableFailurePredictPolling(fdoExtension,
531 TRUE,
532 DISK_DEFAULT_FAILURE_POLLING_PERIOD);
533
534 DebugPrint((3, "DiskInitFdo: Failure Prediction Poll enabled as "
535 "%d for device %p\n",
536 diskData->FailurePredictionCapability,
537 Fdo));
538 }
539 } else {
540
541 //
542 // In safe boot mode we do not enable failure prediction, as perhaps
543 // it is the reason why normal boot does not work
544 //
545
546 diskData->FailurePredictionCapability = FailurePredictionNone;
547
548 }
549
550 //
551 // Initialize the verify mutex
552 //
553
554 KeInitializeMutex(&diskData->VerifyMutex, MAX_SECTORS_PER_VERIFY);
555
556 return(STATUS_SUCCESS);
557
558 } // end DiskInitFdo()
559
560 \f
561 NTSTATUS
562 DiskInitPdo(
563 IN PDEVICE_OBJECT Pdo
564 )
565
566 /*++
567
568 Routine Description:
569
570 This routine will create the well known names for a PDO and register
571 it's device interfaces.
572
573 --*/
574
575 {
576 PCOMMON_DEVICE_EXTENSION pdoExtension = Pdo->DeviceExtension;
577 PDISK_DATA diskData = pdoExtension->DriverData;
578
579 UNICODE_STRING interfaceName;
580
581 NTSTATUS status;
582
583 PAGED_CODE();
584
585 DiskCreateSymbolicLinks(Pdo);
586
587 //
588 // Register interfaces for this device
589 //
590
591 RtlInitUnicodeString(&interfaceName, NULL);
592
593 status = IoRegisterDeviceInterface(Pdo,
594 (LPGUID) &PartitionClassGuid,
595 NULL,
596 &interfaceName);
597
598 if(NT_SUCCESS(status)) {
599
600 diskData->PartitionInterfaceString = interfaceName;
601 status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
602
603 } else {
604 interfaceName.Buffer = NULL;
605 }
606
607 if(!NT_SUCCESS(status)) {
608 DebugPrint((1, "DiskInitPdo: Unable to register partition DCA for "
609 "pdo %p [%lx]\n", Pdo, status));
610
611 RtlFreeUnicodeString(&interfaceName);
612 RtlInitUnicodeString(&(diskData->PartitionInterfaceString), NULL);
613 }
614
615 return STATUS_SUCCESS;
616 }
617
618 \f
619 NTSTATUS
620 DiskStartPdo(
621 IN PDEVICE_OBJECT Pdo
622 )
623
624 /*++
625
626 Routine Description:
627
628 This routine will create the well known names for a PDO and register
629 it's device interfaces.
630
631 --*/
632
633 {
634 PAGED_CODE();
635
636 return STATUS_SUCCESS;
637 }
638
639 NTSTATUS
640 DiskStopDevice(
641 IN PDEVICE_OBJECT DeviceObject,
642 IN UCHAR Type
643 )
644
645 {
646 PFUNCTIONAL_DEVICE_EXTENSION fdo = DeviceObject->DeviceExtension;
647
648 if(fdo->CommonExtension.IsFdo) {
649 DiskAcquirePartitioningLock(fdo);
650 DiskInvalidatePartitionTable(fdo, TRUE);
651 DiskReleasePartitioningLock(fdo);
652 }
653
654 return STATUS_SUCCESS;
655 }
656
657 \f
658 NTSTATUS
659 DiskQueryId(
660 IN PDEVICE_OBJECT Pdo,
661 IN BUS_QUERY_ID_TYPE IdType,
662 IN PUNICODE_STRING UnicodeIdString
663 )
664
665 /*++
666
667 Routine Description:
668
669 This routine generates the PNP id's for the disk's "children". If the
670 specified ID isn't one that the routine can generate it must return
671 STATUS_NOT_IMPLEMENTED so classpnp will know not to do anything with the
672 PNP request's status.
673
674 This routine allocates the buffer for the UnicodeIdString. It is the
675 caller's responsibility to free the buffer when it's done.
676
677 Arguments:
678
679 Pdo - a pointer to the PDO we are to generate an ID for
680
681 IdType - the type of ID to be generated
682
683 UnicodeIdString - a string to put the results into.
684
685 Return Value:
686
687 STATUS_SUCCCESS if successful
688
689 STATUS_NOT_IMPLEMENTED if the IdType is not one supported by this routine
690
691 error status otherwise.
692
693 --*/
694
695 {
696 ANSI_STRING ansiIdString;
697
698 NTSTATUS status;
699
700 PAGED_CODE();
701 ASSERT_PDO(Pdo);
702
703 if(IdType == BusQueryDeviceID) {
704
705 if((Pdo->Characteristics & FILE_REMOVABLE_MEDIA) == 0) {
706 RtlInitAnsiString(&ansiIdString, "STORAGE\\Partition");
707 return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE);
708 }
709
710 RtlInitAnsiString(&ansiIdString,
711 "STORAGE\\RemovableMedia");
712
713 return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE);
714 }
715
716 if(IdType == BusQueryInstanceID) {
717
718 PPHYSICAL_DEVICE_EXTENSION pdoExtension = Pdo->DeviceExtension;
719 PCOMMON_DEVICE_EXTENSION commonExtension = Pdo->DeviceExtension;
720 PDISK_DATA diskData = commonExtension->PartitionZeroExtension->CommonExtension.DriverData;
721
722 UCHAR string[64];
723
724 if((Pdo->Characteristics & FILE_REMOVABLE_MEDIA) == 0) {
725
726 if (diskData->PartitionStyle == PARTITION_STYLE_MBR) {
727 sprintf(string, "S%08lx_O%I64lx_L%I64lx",
728 diskData->Mbr.Signature,
729 commonExtension->StartingOffset,
730 commonExtension->PartitionLength);
731 } else {
732 sprintf(string,
733 "S%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02xS_O%I64lx_L%I64lx",
734 diskData->Efi.DiskId.Data1,
735 diskData->Efi.DiskId.Data2,
736 diskData->Efi.DiskId.Data3,
737 diskData->Efi.DiskId.Data4[0],
738 diskData->Efi.DiskId.Data4[1],
739 diskData->Efi.DiskId.Data4[2],
740 diskData->Efi.DiskId.Data4[3],
741 diskData->Efi.DiskId.Data4[4],
742 diskData->Efi.DiskId.Data4[5],
743 diskData->Efi.DiskId.Data4[6],
744 diskData->Efi.DiskId.Data4[7],
745 commonExtension->StartingOffset,
746 commonExtension->PartitionLength);
747 }
748 } else {
749 sprintf(string, "RM");
750 }
751
752 RtlInitAnsiString(&ansiIdString, string);
753
754 return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE);
755 }
756
757 if((IdType == BusQueryHardwareIDs) || (IdType == BusQueryCompatibleIDs)) {
758
759 RtlInitAnsiString(&ansiIdString, "STORAGE\\Volume");
760
761 UnicodeIdString->MaximumLength = (USHORT) RtlAnsiStringToUnicodeSize(&ansiIdString) + sizeof(UNICODE_NULL);
762
763 UnicodeIdString->Buffer = ExAllocatePoolWithTag(PagedPool,
764 UnicodeIdString->MaximumLength,
765 DISK_TAG_PNP_ID);
766
767 if(UnicodeIdString->Buffer == NULL) {
768 return STATUS_INSUFFICIENT_RESOURCES;
769 }
770
771 RtlZeroMemory(UnicodeIdString->Buffer, UnicodeIdString->MaximumLength);
772
773 return RtlAnsiStringToUnicodeString(UnicodeIdString,
774 &ansiIdString,
775 FALSE);
776 }
777
778 return STATUS_NOT_IMPLEMENTED;
779 }
780
781 \f
782 NTSTATUS
783 DiskGenerateDeviceName(
784 IN BOOLEAN IsFdo,
785 IN ULONG DeviceNumber,
786 IN OPTIONAL ULONG PartitionNumber,
787 IN OPTIONAL PLARGE_INTEGER StartingOffset,
788 IN OPTIONAL PLARGE_INTEGER PartitionLength,
789 OUT PUCHAR *RawName
790 )
791
792 /*++
793
794 Routine Description:
795
796 This routine will allocate a unicode string buffer and then fill it in
797 with a generated name for the specified device object.
798
799 It is the responsibility of the user to allocate a UNICODE_STRING structure
800 to pass in and to free UnicodeName->Buffer when done with it.
801
802 Arguments:
803
804 DeviceObject - a pointer to the device object
805
806 UnicodeName - a unicode string to put the name buffer into
807
808 Return Value:
809
810 status
811
812 --*/
813
814 //#define PDO_NAME_FORMAT "\\Device\\Harddisk%d\\DP(%d)%d"
815 #define PDO_NAME_FORMAT "\\Device\\Harddisk%d\\DP(%d)%#I64x-%#I64x+%lx"
816 #define FDO_NAME_FORMAT "\\Device\\Harddisk%d\\DR%d"
817
818 //#define PDO_NAME_FORMAT (PDO_BASE_NAME "+%#I64x+%#I64x+%#lx")
819
820 {
821 UCHAR rawName[64];
822 static ULONG diskDeviceSequenceNumber = 0;
823
824 PAGED_CODE();
825
826 if(!IsFdo) {
827
828 ASSERT(ARGUMENT_PRESENT((PVOID)(ULONG_PTR) PartitionNumber));
829 ASSERT(ARGUMENT_PRESENT(PartitionLength));
830 ASSERT(ARGUMENT_PRESENT(StartingOffset));
831
832 sprintf(rawName, PDO_NAME_FORMAT, DeviceNumber, PartitionNumber,
833 StartingOffset->QuadPart,
834 PartitionLength->QuadPart,
835 diskDeviceSequenceNumber++);
836 } else {
837
838 ASSERT(!ARGUMENT_PRESENT((PVOID)(ULONG_PTR) PartitionNumber));
839 ASSERT(!ARGUMENT_PRESENT(PartitionLength));
840 ASSERT(!ARGUMENT_PRESENT(StartingOffset));
841
842 sprintf(rawName, FDO_NAME_FORMAT, DeviceNumber,
843 diskDeviceSequenceNumber++);
844
845 }
846
847 *RawName = ExAllocatePoolWithTag(PagedPool,
848 strlen(rawName) + 1,
849 DISK_TAG_NAME);
850
851 if(*RawName == NULL) {
852 return STATUS_INSUFFICIENT_RESOURCES;
853 }
854
855 strcpy(*RawName, rawName);
856
857 DebugPrint((2, "DiskGenerateDeviceName: generated \"%s\"\n", rawName));
858
859 return STATUS_SUCCESS;
860 }
861
862 \f
863 VOID
864 DiskCreateSymbolicLinks(
865 IN PDEVICE_OBJECT DeviceObject
866 )
867
868 /*++
869
870 Routine Description:
871
872 This routine will generate a symbolic link for the specified device object
873 using the well known form \\Device\HarddiskX\PartitionY, where X and Y are
874 filled in using the partition information in the device object's extension.
875
876 This routine will not try to delete any previous symbolic link for the
877 same generated name - the caller must make sure the symbolic link has
878 been broken before calling this routine.
879
880 Arguments:
881
882 DeviceObject - the device object to make a well known name for
883
884 Return Value:
885
886 STATUS
887
888 --*/
889
890 {
891 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
892
893 PDISK_DATA diskData = commonExtension->DriverData;
894
895 WCHAR wideSourceName[64];
896 UNICODE_STRING unicodeSourceName;
897
898 NTSTATUS status;
899
900 PAGED_CODE();
901
902 //
903 // Build the destination for the link first using the device name
904 // stored in the device object
905 //
906
907 ASSERT(commonExtension->DeviceName.Buffer);
908
909 if(!diskData->LinkStatus.WellKnownNameCreated) {
910 //
911 // Put together the source name using the partition and device number
912 // in the device extension and disk data segment
913 //
914
915 swprintf(wideSourceName, L"\\Device\\Harddisk%d\\Partition%d",
916 commonExtension->PartitionZeroExtension->DeviceNumber,
917 (commonExtension->IsFdo ?
918 0 :
919 commonExtension->PartitionNumber));
920
921 RtlInitUnicodeString(&unicodeSourceName, wideSourceName);
922
923 DebugPrint((1, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n",
924 &unicodeSourceName,
925 &commonExtension->DeviceName));
926
927 status = IoCreateSymbolicLink(&unicodeSourceName,
928 &commonExtension->DeviceName);
929
930 #if DBG
931
932 if((status == STATUS_OBJECT_NAME_EXISTS) ||
933 (status == STATUS_OBJECT_NAME_COLLISION)) {
934
935 DebugPrint((1, "DiskCreateSymbolicLink: name %wZ already exists\n",
936 &unicodeSourceName));
937 }
938 #endif
939
940 if(NT_SUCCESS(status)){
941 diskData->LinkStatus.WellKnownNameCreated = TRUE;
942 }
943 }
944
945 if((!diskData->LinkStatus.PhysicalDriveLinkCreated) &&
946 (commonExtension->IsFdo)) {
947
948 //
949 // Create a physical drive N link using the device number we saved
950 // away during AddDevice.
951 //
952
953 swprintf(wideSourceName,
954 L"\\DosDevices\\PhysicalDrive%d",
955 commonExtension->PartitionZeroExtension->DeviceNumber);
956
957 RtlInitUnicodeString(&unicodeSourceName, wideSourceName);
958
959 DebugPrint((1, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n",
960 &unicodeSourceName,
961 &(commonExtension->DeviceName)));
962
963 status = IoCreateSymbolicLink(&unicodeSourceName,
964 &(commonExtension->DeviceName));
965
966 #if DBG
967
968 if((status == STATUS_OBJECT_NAME_EXISTS) ||
969 (status == STATUS_OBJECT_NAME_COLLISION)) {
970
971 DebugPrint((1, "DiskCreateSymbolicLink: name %wZ already exists\n",
972 &unicodeSourceName));
973 }
974 #endif
975
976 if(NT_SUCCESS(status)) {
977 diskData->LinkStatus.PhysicalDriveLinkCreated = TRUE;
978 }
979 } else if(commonExtension->IsFdo == FALSE) {
980 diskData->LinkStatus.PhysicalDriveLinkCreated = FALSE;
981 }
982
983 return;
984 }
985
986 \f
987 VOID
988 DiskDeleteSymbolicLinks(
989 IN PDEVICE_OBJECT DeviceObject
990 )
991
992 /*++
993
994 Routine Description:
995
996 This routine will delete the well known name (symlink) for the specified
997 device. It generates the link name using information stored in the
998 device extension
999
1000 Arguments:
1001
1002 DeviceObject - the device object we are unlinking
1003
1004 Return Value:
1005
1006 status
1007
1008 --*/
1009
1010 {
1011 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1012 PDISK_DATA diskData = commonExtension->DriverData;
1013
1014 WCHAR wideLinkName[64];
1015 UNICODE_STRING unicodeLinkName;
1016
1017 PAGED_CODE();
1018
1019 if(diskData->LinkStatus.WellKnownNameCreated) {
1020
1021 swprintf(wideLinkName,
1022 L"\\Device\\Harddisk%d\\Partition%d",
1023 commonExtension->PartitionZeroExtension->DeviceNumber,
1024 (commonExtension->IsFdo ? 0 :
1025 commonExtension->PartitionNumber));
1026
1027 RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
1028
1029 IoDeleteSymbolicLink(&unicodeLinkName);
1030
1031 diskData->LinkStatus.WellKnownNameCreated = FALSE;
1032 }
1033
1034 if(diskData->LinkStatus.PhysicalDriveLinkCreated) {
1035
1036 ASSERT_FDO(DeviceObject);
1037
1038 swprintf(wideLinkName,
1039 L"\\DosDevices\\PhysicalDrive%d",
1040 commonExtension->PartitionZeroExtension->DeviceNumber);
1041
1042 RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
1043
1044 IoDeleteSymbolicLink(&unicodeLinkName);
1045
1046 diskData->LinkStatus.PhysicalDriveLinkCreated = FALSE;
1047 }
1048
1049 return;
1050 }
1051
1052 \f
1053 NTSTATUS
1054 DiskRemoveDevice(
1055 IN PDEVICE_OBJECT DeviceObject,
1056 IN UCHAR Type
1057 )
1058
1059 /*++
1060
1061 Routine Description:
1062
1063 This routine will release any resources the device may have allocated for
1064 this device object and return.
1065
1066 Arguments:
1067
1068 DeviceObject - the device object being removed
1069
1070 Return Value:
1071
1072 status
1073
1074 --*/
1075
1076 {
1077 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1078 PDISK_DATA diskData = commonExtension->DriverData;
1079
1080 PAGED_CODE();
1081
1082 //
1083 // Handle query and cancel
1084 //
1085
1086 if((Type == IRP_MN_QUERY_REMOVE_DEVICE) ||
1087 (Type == IRP_MN_CANCEL_REMOVE_DEVICE)) {
1088 return STATUS_SUCCESS;
1089 }
1090
1091 if(commonExtension->IsFdo) {
1092
1093 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
1094 DeviceObject->DeviceExtension;
1095
1096 //
1097 // Purge the cached partition table (if any).
1098 //
1099
1100 DiskAcquirePartitioningLock(fdoExtension);
1101 DiskInvalidatePartitionTable(fdoExtension, TRUE);
1102 DiskReleasePartitioningLock(fdoExtension);
1103
1104 //
1105 // Delete our object directory.
1106 //
1107
1108 if(fdoExtension->AdapterDescriptor) {
1109 ExFreePool(fdoExtension->AdapterDescriptor);
1110 fdoExtension->AdapterDescriptor = NULL;
1111 }
1112
1113 if(fdoExtension->DeviceDescriptor) {
1114 ExFreePool(fdoExtension->DeviceDescriptor);
1115 fdoExtension->DeviceDescriptor = NULL;
1116 }
1117
1118 if(fdoExtension->SenseData) {
1119 ExFreePool(fdoExtension->SenseData);
1120 fdoExtension->SenseData = NULL;
1121 }
1122
1123 if(fdoExtension->DeviceDirectory != NULL) {
1124 ZwMakeTemporaryObject(fdoExtension->DeviceDirectory);
1125 ZwClose(fdoExtension->DeviceDirectory);
1126 fdoExtension->DeviceDirectory = NULL;
1127 }
1128
1129 if(Type == IRP_MN_REMOVE_DEVICE) {
1130 IoGetConfigurationInformation()->DiskCount--;
1131 }
1132
1133 } else {
1134
1135 PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
1136
1137 }
1138
1139 DiskDeleteSymbolicLinks(DeviceObject);
1140
1141 //
1142 // Release the mounted device interface if we've set it.
1143 //
1144
1145 if(diskData->PartitionInterfaceString.Buffer != NULL) {
1146 IoSetDeviceInterfaceState(&(diskData->PartitionInterfaceString), FALSE);
1147 RtlFreeUnicodeString(&(diskData->PartitionInterfaceString));
1148 RtlInitUnicodeString(&(diskData->PartitionInterfaceString), NULL);
1149 }
1150 if(diskData->DiskInterfaceString.Buffer != NULL) {
1151 IoSetDeviceInterfaceState(&(diskData->DiskInterfaceString), FALSE);
1152 RtlFreeUnicodeString(&(diskData->DiskInterfaceString));
1153 RtlInitUnicodeString(&(diskData->DiskInterfaceString), NULL);
1154 }
1155
1156 ClassDeleteSrbLookasideList(commonExtension);
1157 return STATUS_SUCCESS;
1158 }
1159
1160 \f
1161 NTSTATUS
1162 DiskStartFdo(
1163 IN PDEVICE_OBJECT Fdo
1164 )
1165
1166 /*++
1167
1168 Routine Description:
1169
1170 This routine will query the underlying device for any information necessary
1171 to complete initialization of the device. This will include physical
1172 disk geometry, mode sense information and such.
1173
1174 This routine does not perform partition enumeration - that is left to the
1175 re-enumeration routine
1176
1177 If this routine fails it will return an error value. It does not clean up
1178 any resources - that is left for the Stop/Remove routine.
1179
1180 Arguments:
1181
1182 Fdo - a pointer to the functional device object for this device
1183
1184 Return Value:
1185
1186 status
1187
1188 --*/
1189
1190 {
1191 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
1192 PCOMMON_DEVICE_EXTENSION commonExtension = &(fdoExtension->CommonExtension);
1193 PDISK_DATA diskData = commonExtension->DriverData;
1194 STORAGE_HOTPLUG_INFO hotplugInfo;
1195 ULONG writeCacheOverride = DiskWriteCacheDefault;
1196 DISK_CACHE_INFORMATION cacheInfo;
1197 NTSTATUS status;
1198
1199 PAGED_CODE();
1200
1201 //
1202 // Get the hotplug information, so we can turn off write cache if needed
1203 //
1204 // NOTE: Capabilities info is not good enough to determine hotplugedness
1205 // as we cannot determine device relations information and other
1206 // dependencies. Get the hotplug info instead
1207 //
1208
1209 {
1210 PIRP irp;
1211 KEVENT event;
1212 IO_STATUS_BLOCK statusBlock;
1213
1214 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1215 RtlZeroMemory(&hotplugInfo, sizeof(STORAGE_HOTPLUG_INFO));
1216
1217 irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_HOTPLUG_INFO,
1218 Fdo,
1219 NULL,
1220 0L,
1221 &hotplugInfo,
1222 sizeof(STORAGE_HOTPLUG_INFO),
1223 FALSE,
1224 &event,
1225 &statusBlock);
1226
1227 if (irp != NULL) {
1228
1229 // send to self -- classpnp handles this
1230 status = IoCallDriver(Fdo, irp);
1231 if (status == STATUS_PENDING) {
1232 KeWaitForSingleObject(&event,
1233 Executive,
1234 KernelMode,
1235 FALSE,
1236 NULL);
1237 status = statusBlock.Status;
1238 }
1239 }
1240 }
1241
1242 //
1243 // Clear the DEV_WRITE_CACHE flag now and set
1244 // it below only if we read that from the disk
1245 //
1246
1247 CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
1248
1249 if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE))
1250 {
1251 //
1252 // This flag overrides the user's setting, because faulty firmware
1253 // may cause the filesystem to refuse to format media on this device
1254 //
1255 DebugPrint((1,
1256 "DiskStartFdo: Shutting off write cache for %p due to %s\n",
1257 Fdo,
1258 "Possible Firmware Issue"));
1259
1260 writeCacheOverride = DiskWriteCacheDisable;
1261 }
1262 else
1263 {
1264 //
1265 // Look into the registry to see if the user
1266 // has chosen to override the default setting
1267 //
1268 ClassGetDeviceParameter(fdoExtension,
1269 DiskDeviceParameterSubkey,
1270 DiskDeviceUserWriteCacheSetting,
1271 &writeCacheOverride);
1272
1273 if (writeCacheOverride == DiskWriteCacheDefault)
1274 {
1275 //
1276 // The user has not overridden the default settings
1277 //
1278 if (hotplugInfo.DeviceHotplug && !hotplugInfo.WriteCacheEnableOverride)
1279 {
1280 DebugPrint((1,
1281 "DiskStartFdo: Shutting off write cache for %p due to %s\n",
1282 Fdo,
1283 "Hotplug Device"));
1284
1285 writeCacheOverride = DiskWriteCacheDisable;
1286 }
1287 else if (hotplugInfo.MediaHotplug)
1288 {
1289 DebugPrint((1,
1290 "DiskStartFdo: Shutting off write cache for %p due to %s\n",
1291 Fdo,
1292 "Hotplug (unlockable) Media"));
1293
1294 writeCacheOverride = DiskWriteCacheDisable;
1295 }
1296 else
1297 {
1298 //
1299 // We enable write cache if this device has no specific issues
1300 //
1301 writeCacheOverride = DiskWriteCacheEnable;
1302 }
1303 }
1304 }
1305
1306 //
1307 // Query the disk to see if write cache is enabled
1308 // and set the DEV_WRITE_CACHE flag appropriately
1309 //
1310
1311 RtlZeroMemory(&cacheInfo, sizeof(DISK_CACHE_INFORMATION));
1312
1313 status = DiskGetCacheInformation(fdoExtension, &cacheInfo);
1314
1315 if (NT_SUCCESS(status))
1316 {
1317 if (cacheInfo.WriteCacheEnabled == TRUE)
1318 {
1319 if (writeCacheOverride == DiskWriteCacheDisable)
1320 {
1321 //
1322 // Write cache is currently enabled on this
1323 // device, but we would like to turn it off
1324 //
1325 cacheInfo.WriteCacheEnabled = FALSE;
1326
1327 status = DiskSetCacheInformation(fdoExtension, &cacheInfo);
1328 }
1329 else
1330 {
1331 //
1332 // The write cache setting either matches
1333 // our needs or we don't care
1334 //
1335 SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
1336 }
1337 }
1338 else
1339 {
1340 if (writeCacheOverride == DiskWriteCacheEnable)
1341 {
1342 //
1343 // Write cache is currently disabled on this
1344 // device, but we would like to turn it on
1345 //
1346 cacheInfo.WriteCacheEnabled = TRUE;
1347
1348 status = DiskSetCacheInformation(fdoExtension, &cacheInfo);
1349
1350 SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
1351 }
1352 }
1353 }
1354
1355 //
1356 // In the event that there's a cached partition table flush it now.
1357 //
1358
1359 DiskAcquirePartitioningLock(fdoExtension);
1360 DiskInvalidatePartitionTable(fdoExtension, TRUE);
1361 DiskReleasePartitioningLock(fdoExtension);
1362
1363 //
1364 // Get the SCSI address if it's available for use with SMART ioctls.
1365 //
1366
1367 {
1368 PIRP irp;
1369 KEVENT event;
1370 IO_STATUS_BLOCK statusBlock;
1371
1372 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1373
1374 irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS,
1375 commonExtension->LowerDeviceObject,
1376 NULL,
1377 0L,
1378 &(diskData->ScsiAddress),
1379 sizeof(SCSI_ADDRESS),
1380 FALSE,
1381 &event,
1382 &statusBlock);
1383
1384 if(irp != NULL) {
1385
1386
1387 status = IoCallDriver(commonExtension->LowerDeviceObject, irp);
1388
1389 if(status == STATUS_PENDING) {
1390 KeWaitForSingleObject(&event,
1391 Executive,
1392 KernelMode,
1393 FALSE,
1394 NULL);
1395 status = statusBlock.Status;
1396 }
1397 }
1398 }
1399
1400 return STATUS_SUCCESS;
1401
1402 } // end DiskStartFdo()
1403