Synchronize with trunk revision 59781.
[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 NTSTATUS
43 NTAPI
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 NTSTATUS
189 NTAPI
190 DiskInitFdo(
191 IN PDEVICE_OBJECT Fdo
192 )
193
194 /*++
195
196 Routine Description:
197
198 This routine is called to do one-time initialization of new device objects
199
200
201 Arguments:
202
203 Fdo - a pointer to the functional device object for this device
204
205 Return Value:
206
207 status
208
209 --*/
210
211 {
212 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
213
214 PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData;
215
216 //ULONG srbFlags = 0;
217
218 ULONG timeOut = 0;
219
220 ULONG bytesPerSector;
221 //UCHAR sectorShift;
222
223 //BOOLEAN dmActive = FALSE;
224 PULONG dmSkew;
225 //ULONG dmByteSkew;
226
227 NTSTATUS status;
228
229 PAGED_CODE();
230
231 //
232 // Build the lookaside list for srb's for the physical disk. Should only
233 // need a couple. If this fails then we don't have an emergency SRB so
234 // fail the call to initialize.
235 //
236
237 ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) fdoExtension,
238 PARTITION0_LIST_SIZE);
239
240 //
241 // Because all requests share a common sense buffer, it is possible
242 // for the buffer to be overwritten if the port driver completes
243 // multiple failed requests that require a request sense before the
244 // class driver's completion routine can consume the data in the buffer.
245 // To prevent this, we allow the port driver to allocate a unique sense
246 // buffer each time it needs one. We are responsible for freeing this
247 // buffer. This also allows the adapter to be configured to support
248 // additional sense data beyond the minimum 18 bytes.
249 //
250
251 fdoExtension->SrbFlags = SRB_FLAGS_PORT_DRIVER_ALLOCSENSE;
252
253 //
254 // Initialize the srb flags.
255 //
256
257 if (fdoExtension->DeviceDescriptor->CommandQueueing &&
258 fdoExtension->AdapterDescriptor->CommandQueueing) {
259
260 fdoExtension->SrbFlags = SRB_FLAGS_QUEUE_ACTION_ENABLE;
261
262 }
263
264 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
265 SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
266 }
267
268 //
269 // Look for controllers that require special flags.
270 //
271
272 ClassScanForSpecial(fdoExtension, DiskBadControllers, DiskSetSpecialHacks);
273
274 //
275 // Look into the registry to see if this device
276 // requires special attention - [ like a hack ]
277 //
278
279 DiskScanRegistryForSpecial(fdoExtension);
280
281 //srbFlags = fdoExtension->SrbFlags;
282
283 //
284 // Clear buffer for drive geometry.
285 //
286
287 RtlZeroMemory(&(fdoExtension->DiskGeometry),
288 sizeof(DISK_GEOMETRY));
289
290 //
291 // Allocate request sense buffer.
292 //
293
294 fdoExtension->SenseData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
295 SENSE_BUFFER_SIZE,
296 DISK_TAG_START);
297
298 if (fdoExtension->SenseData == NULL) {
299
300 //
301 // The buffer can not be allocated.
302 //
303
304 DebugPrint((1, "DiskInitFdo: Can not allocate request sense buffer\n"));
305
306 status = STATUS_INSUFFICIENT_RESOURCES;
307 return status;
308 }
309
310 //
311 // Physical device object will describe the entire
312 // device, starting at byte offset 0.
313 //
314
315 fdoExtension->CommonExtension.StartingOffset.QuadPart = (LONGLONG)(0);
316
317 //
318 // Set timeout value in seconds.
319 //
320
321 timeOut = ClassQueryTimeOutRegistryValue(Fdo);
322 if (timeOut) {
323 fdoExtension->TimeOutValue = timeOut;
324 } else {
325 fdoExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
326 }
327
328 //
329 // If this is a removable drive, build an entry in devicemap\scsi
330 // indicating it's physicaldriveN name, set up the appropriate
331 // update partitions routine and set the flags correctly.
332 // note: only do this after the timeout value is set, above.
333 //
334
335 if (fdoExtension->DeviceDescriptor->RemovableMedia) {
336 ClassUpdateInformationInRegistry( Fdo,
337 "PhysicalDrive",
338 fdoExtension->DeviceNumber,
339 NULL,
340 0);
341 //
342 // Enable media change notification for removable disks
343 //
344 ClassInitializeMediaChangeDetection(fdoExtension,
345 "Disk");
346
347 SET_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA);
348 diskData->UpdatePartitionRoutine = DiskUpdateRemovablePartitions;
349
350 } else {
351
352 SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
353 diskData->UpdatePartitionRoutine = DiskUpdatePartitions;
354
355 }
356
357 //
358 // Read the drive capacity. Don't use the disk version of the routine here
359 // since we don't know the disk signature yet - the disk version will
360 // attempt to determine the BIOS reported geometry.
361 //
362
363 status = ClassReadDriveCapacity(Fdo);
364
365 //
366 // If the read capcity failed then just return, unless this is a
367 // removable disk where a device object partition needs to be created.
368 //
369
370 if (!NT_SUCCESS(status) &&
371 !(Fdo->Characteristics & FILE_REMOVABLE_MEDIA)) {
372
373 DebugPrint((1,
374 "DiskInitFdo: Can't read capacity for device %p\n",
375 Fdo));
376
377 if (fdoExtension->DeviceDescriptor->RemovableMedia) {
378 fdoExtension->DiskGeometry.MediaType = RemovableMedia;
379 Fdo->Flags &= ~DO_VERIFY_VOLUME;
380 } else {
381 fdoExtension->DiskGeometry.MediaType = FixedMedia;
382 }
383
384 status = STATUS_SUCCESS;
385 }
386
387 //
388 // Set up sector size fields.
389 //
390 // Stack variables will be used to update
391 // the partition device extensions.
392 //
393 // The device extension field SectorShift is
394 // used to calculate sectors in I/O transfers.
395 //
396 // The DiskGeometry structure is used to service
397 // IOCTls used by the format utility.
398 //
399
400 bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
401
402 //
403 // Make sure sector size is not zero.
404 //
405
406 if (bytesPerSector == 0) {
407
408 //
409 // Default sector size for disk is 512.
410 //
411
412 bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector = 512;
413 }
414
415 //sectorShift = fdoExtension->SectorShift;
416
417 //
418 // Determine is DM Driver is loaded on an IDE drive that is
419 // under control of Atapi - this could be either a crashdump or
420 // an Atapi device is sharing the controller with an IDE disk.
421 //
422
423 HalExamineMBR(fdoExtension->CommonExtension.DeviceObject,
424 fdoExtension->DiskGeometry.BytesPerSector,
425 (ULONG)0x54,
426 (PVOID*)&dmSkew);
427
428 if (dmSkew) {
429
430 //
431 // Update the device extension, so that the call to IoReadPartitionTable
432 // will get the correct information. Any I/O to this disk will have
433 // to be skewed by *dmSkew sectors aka DMByteSkew.
434 //
435
436 fdoExtension->DMSkew = *dmSkew;
437 fdoExtension->DMActive = TRUE;
438 fdoExtension->DMByteSkew = fdoExtension->DMSkew * bytesPerSector;
439
440 //
441 // Save away the infomation that we need, since this deviceExtension will soon be
442 // blown away.
443 //
444
445 //dmActive = TRUE;
446 //dmByteSkew = fdoExtension->DMByteSkew;
447
448 }
449
450 #if defined(_X86_)
451 //
452 // Try to read the signature off the disk and determine the correct drive
453 // geometry based on that. This requires rereading the disk size to get
454 // the cylinder count updated correctly.
455 //
456
457 if(fdoExtension->DeviceDescriptor->RemovableMedia == FALSE) {
458 DiskReadSignature(Fdo);
459 DiskReadDriveCapacity(Fdo);
460 }
461 #endif
462
463 //
464 // Register interfaces for this device
465 //
466 {
467 UNICODE_STRING interfaceName;
468
469 RtlInitUnicodeString(&interfaceName, NULL);
470
471 status = IoRegisterDeviceInterface(fdoExtension->LowerPdo,
472 (LPGUID) &DiskClassGuid,
473 NULL,
474 &interfaceName);
475
476 if(NT_SUCCESS(status)) {
477
478 diskData->DiskInterfaceString = interfaceName;
479 status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
480
481 } else {
482 interfaceName.Buffer = NULL;
483 }
484
485 if(!NT_SUCCESS(status)) {
486
487 DebugPrint((1, "DiskInitFdo: Unable to register or set disk DCA "
488 "for fdo %p [%lx]\n", Fdo, status));
489
490 RtlFreeUnicodeString(&interfaceName);
491 RtlInitUnicodeString(&(diskData->DiskInterfaceString), NULL);
492 }
493 }
494
495 DiskCreateSymbolicLinks(Fdo);
496
497 //
498 // Determine the type of disk and enable failure preiction in the hardware
499 // and enable failure prediction polling.
500 //
501
502 if (InitSafeBootMode == 0)
503 {
504 DiskDetectFailurePrediction(fdoExtension,
505 &diskData->FailurePredictionCapability);
506
507 if (diskData->FailurePredictionCapability != FailurePredictionNone)
508 {
509 //
510 // Cool, we've got some sort of failure prediction, enable it
511 // at the hardware and then enable polling for it
512 //
513
514 //
515 // By default we allow performance to be degradeded if failure
516 // prediction is enabled.
517 //
518 // TODO: Make a registry entry ?
519 //
520
521 diskData->AllowFPPerfHit = TRUE;
522
523 //
524 // Enable polling only after Atapi and SBP2 add support for the new
525 // SRB flag that indicates that the request should not reset the
526 // drive spin down idle timer.
527 //
528
529 status = DiskEnableDisableFailurePredictPolling(fdoExtension,
530 TRUE,
531 DISK_DEFAULT_FAILURE_POLLING_PERIOD);
532
533 DebugPrint((3, "DiskInitFdo: Failure Prediction Poll enabled as "
534 "%d for device %p\n",
535 diskData->FailurePredictionCapability,
536 Fdo));
537 }
538 } else {
539
540 //
541 // In safe boot mode we do not enable failure prediction, as perhaps
542 // it is the reason why normal boot does not work
543 //
544
545 diskData->FailurePredictionCapability = FailurePredictionNone;
546
547 }
548
549 //
550 // Initialize the verify mutex
551 //
552
553 KeInitializeMutex(&diskData->VerifyMutex, MAX_SECTORS_PER_VERIFY);
554
555 return(STATUS_SUCCESS);
556
557 } // end DiskInitFdo()
558
559 NTSTATUS
560 NTAPI
561 DiskInitPdo(
562 IN PDEVICE_OBJECT Pdo
563 )
564
565 /*++
566
567 Routine Description:
568
569 This routine will create the well known names for a PDO and register
570 it's device interfaces.
571
572 --*/
573
574 {
575 PCOMMON_DEVICE_EXTENSION pdoExtension = Pdo->DeviceExtension;
576 PDISK_DATA diskData = pdoExtension->DriverData;
577
578 UNICODE_STRING interfaceName;
579
580 NTSTATUS status;
581
582 PAGED_CODE();
583
584 DiskCreateSymbolicLinks(Pdo);
585
586 //
587 // Register interfaces for this device
588 //
589
590 RtlInitUnicodeString(&interfaceName, NULL);
591
592 status = IoRegisterDeviceInterface(Pdo,
593 (LPGUID) &PartitionClassGuid,
594 NULL,
595 &interfaceName);
596
597 if(NT_SUCCESS(status)) {
598
599 diskData->PartitionInterfaceString = interfaceName;
600 status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
601
602 } else {
603 interfaceName.Buffer = NULL;
604 }
605
606 if(!NT_SUCCESS(status)) {
607 DebugPrint((1, "DiskInitPdo: Unable to register partition DCA for "
608 "pdo %p [%lx]\n", Pdo, status));
609
610 RtlFreeUnicodeString(&interfaceName);
611 RtlInitUnicodeString(&(diskData->PartitionInterfaceString), NULL);
612 }
613
614 return STATUS_SUCCESS;
615 }
616
617 NTSTATUS
618 NTAPI
619 DiskStartPdo(
620 IN PDEVICE_OBJECT Pdo
621 )
622
623 /*++
624
625 Routine Description:
626
627 This routine will create the well known names for a PDO and register
628 it's device interfaces.
629
630 --*/
631
632 {
633 PAGED_CODE();
634
635 return STATUS_SUCCESS;
636 }
637
638 NTSTATUS
639 NTAPI
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 NTSTATUS
658 NTAPI
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 NTSTATUS
782 NTAPI
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 VOID
863 NTAPI
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 VOID
987 NTAPI
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 NTSTATUS
1053 NTAPI
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 NTSTATUS
1161 NTAPI
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()