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