3 Copyright (C) Microsoft Corporation. All rights reserved.
11 Include all funtions relate to MMC
39 #pragma alloc_text(PAGE, DeviceDeallocateMmcResources)
40 #pragma alloc_text(PAGE, DeviceAllocateMmcResources)
41 #pragma alloc_text(PAGE, DeviceUpdateMmcCapabilities)
42 #pragma alloc_text(PAGE, DeviceGetConfigurationWithAlloc)
43 #pragma alloc_text(PAGE, DeviceGetConfiguration)
44 #pragma alloc_text(PAGE, DeviceUpdateMmcWriteCapability)
45 #pragma alloc_text(PAGE, MmcDataFindFeaturePage)
46 #pragma alloc_text(PAGE, MmcDataFindProfileInProfiles)
47 #pragma alloc_text(PAGE, DeviceRetryTimeGuessBasedOnProfile)
48 #pragma alloc_text(PAGE, DeviceRetryTimeDetectionBasedOnModePage2A)
49 #pragma alloc_text(PAGE, DeviceRetryTimeDetectionBasedOnGetPerformance)
54 #pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
56 _IRQL_requires_max_(APC_LEVEL
)
58 DeviceDeallocateMmcResources(
69 Device - device object
77 PCDROM_DEVICE_EXTENSION deviceExtension
= DeviceGetExtension(Device
);
78 PCDROM_DATA cddata
= &(deviceExtension
->DeviceAdditionalData
);
79 PCDROM_MMC_EXTENSION mmcData
= &cddata
->Mmc
;
83 if (mmcData
->CapabilitiesIrp
)
85 IoFreeIrp(mmcData
->CapabilitiesIrp
);
86 mmcData
->CapabilitiesIrp
= NULL
;
88 if (mmcData
->CapabilitiesMdl
)
90 IoFreeMdl(mmcData
->CapabilitiesMdl
);
91 mmcData
->CapabilitiesMdl
= NULL
;
93 if (mmcData
->CapabilitiesBuffer
)
95 ExFreePool(mmcData
->CapabilitiesBuffer
);
96 mmcData
->CapabilitiesBuffer
= NULL
;
98 if (mmcData
->CapabilitiesRequest
)
100 WdfObjectDelete(mmcData
->CapabilitiesRequest
);
101 mmcData
->CapabilitiesRequest
= NULL
;
103 mmcData
->CapabilitiesBufferSize
= 0;
104 mmcData
->IsMmc
= FALSE
;
105 mmcData
->WriteAllowed
= FALSE
;
111 _IRQL_requires_max_(PASSIVE_LEVEL
)
113 DeviceAllocateMmcResources(
114 _In_ WDFDEVICE Device
120 allocate all MMC resources needed
124 Device - device object
132 NTSTATUS status
= STATUS_SUCCESS
;
133 PCDROM_DEVICE_EXTENSION deviceExtension
= DeviceGetExtension(Device
);
134 PCDROM_DATA cddata
= &(deviceExtension
->DeviceAdditionalData
);
135 PCDROM_MMC_EXTENSION mmcData
= &(cddata
->Mmc
);
136 WDF_OBJECT_ATTRIBUTES attributes
= {0};
140 NT_ASSERT(mmcData
->CapabilitiesBuffer
== NULL
);
141 NT_ASSERT(mmcData
->CapabilitiesBufferSize
== 0);
143 // allocate the buffer and set the buffer size.
144 // retrieve drive configuration information.
145 status
= DeviceGetConfigurationWithAlloc(Device
,
146 &mmcData
->CapabilitiesBuffer
,
147 &mmcData
->CapabilitiesBufferSize
,
149 SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL
);
150 if (!NT_SUCCESS(status
))
152 NT_ASSERT(mmcData
->CapabilitiesBuffer
== NULL
);
153 NT_ASSERT(mmcData
->CapabilitiesBufferSize
== 0);
157 NT_ASSERT(mmcData
->CapabilitiesBuffer
!= NULL
);
158 NT_ASSERT(mmcData
->CapabilitiesBufferSize
!= 0);
160 // Create an MDL over the new Buffer (allocated by DeviceGetConfiguration)
161 mmcData
->CapabilitiesMdl
= IoAllocateMdl(mmcData
->CapabilitiesBuffer
,
162 mmcData
->CapabilitiesBufferSize
,
164 if (mmcData
->CapabilitiesMdl
== NULL
)
166 ExFreePool(mmcData
->CapabilitiesBuffer
);
167 mmcData
->CapabilitiesBuffer
= NULL
;
168 mmcData
->CapabilitiesBufferSize
= 0;
169 return STATUS_INSUFFICIENT_RESOURCES
;
172 // Create an IRP from which we will create a WDFREQUEST
173 mmcData
->CapabilitiesIrp
= IoAllocateIrp(deviceExtension
->DeviceObject
->StackSize
+ 1, FALSE
);
174 if (mmcData
->CapabilitiesIrp
== NULL
)
176 IoFreeMdl(mmcData
->CapabilitiesMdl
);
177 mmcData
->CapabilitiesMdl
= NULL
;
178 ExFreePool(mmcData
->CapabilitiesBuffer
);
179 mmcData
->CapabilitiesBuffer
= NULL
;
180 mmcData
->CapabilitiesBufferSize
= 0;
181 return STATUS_INSUFFICIENT_RESOURCES
;
184 // create WDF request object
185 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes
,
186 CDROM_REQUEST_CONTEXT
);
187 status
= WdfRequestCreateFromIrp(&attributes
,
188 mmcData
->CapabilitiesIrp
,
190 &mmcData
->CapabilitiesRequest
);
191 if (!NT_SUCCESS(status
))
196 return STATUS_SUCCESS
;
199 _IRQL_requires_max_(PASSIVE_LEVEL
)
201 DeviceUpdateMmcCapabilities(
202 _In_ WDFDEVICE Device
208 issue get congiguration command ans save result in device extension
212 Device - device object
220 NTSTATUS status
= STATUS_SUCCESS
;
221 PCDROM_DEVICE_EXTENSION deviceExtension
= DeviceGetExtension(Device
);
222 PCDROM_DATA cdData
= &(deviceExtension
->DeviceAdditionalData
);
223 PCDROM_MMC_EXTENSION mmcData
= &(cdData
->Mmc
);
224 ULONG returnedBytes
= 0;
229 // first of all, check if we're still in the CdromMmcUpdateRequired state
230 // and, if yes, change it to CdromMmcUpdateStarted.
231 updateState
= InterlockedCompareExchange((PLONG
)&(cdData
->Mmc
.UpdateState
),
232 CdromMmcUpdateStarted
,
233 CdromMmcUpdateRequired
);
234 if (updateState
!= CdromMmcUpdateRequired
) {
235 // Mmc capabilities have been already updated or are in the process of
236 // being updated - just return STATUS_SUCCESS
237 return STATUS_SUCCESS
;
240 // default to read-only, no Streaming, non-blank
241 mmcData
->WriteAllowed
= FALSE
;
242 mmcData
->StreamingReadSupported
= FALSE
;
243 mmcData
->StreamingWriteSupported
= FALSE
;
245 // Issue command to update the drive capabilities.
246 // The failure of MMC update is not considered critical,
247 // so that we'll continue to process I/O even MMC update fails.
248 status
= DeviceGetConfiguration(Device
,
249 mmcData
->CapabilitiesBuffer
,
250 mmcData
->CapabilitiesBufferSize
,
253 SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT
);
255 if (NT_SUCCESS(status
) && // succeeded.
256 (mmcData
->CapabilitiesBufferSize
>= returnedBytes
)) // not overflow.
258 // update whether or not writes are allowed
259 // this should be the *ONLY* place writes are set to allowed
261 BOOLEAN writeAllowed
= FALSE
;
262 FEATURE_NUMBER validationSchema
= 0;
263 ULONG blockingFactor
= 1;
265 DeviceUpdateMmcWriteCapability(mmcData
->CapabilitiesBuffer
,
272 mmcData
->WriteAllowed
= writeAllowed
;
273 mmcData
->ValidationSchema
= validationSchema
;
274 mmcData
->Blocking
= blockingFactor
;
277 // Check if Streaming reads/writes are supported and cache
278 // this information for later use.
280 PFEATURE_HEADER header
;
281 ULONG minAdditionalLength
;
283 minAdditionalLength
= FIELD_OFFSET(FEATURE_DATA_REAL_TIME_STREAMING
, Reserved2
) -
284 sizeof(FEATURE_HEADER
);
286 header
= MmcDataFindFeaturePage(mmcData
->CapabilitiesBuffer
,
288 FeatureRealTimeStreaming
);
290 if ((header
!= NULL
) &&
292 (header
->AdditionalLength
>= minAdditionalLength
))
294 PFEATURE_DATA_REAL_TIME_STREAMING feature
= (PFEATURE_DATA_REAL_TIME_STREAMING
)header
;
296 // If Real-Time feature is current, then Streaming reads are supported for sure.
297 mmcData
->StreamingReadSupported
= TRUE
;
299 // Streaming writes are supported if an appropriate bit is set in the feature page.
300 mmcData
->StreamingWriteSupported
= (feature
->StreamRecording
== 1);
304 // update the flag to reflect that if the media is CSS protected DVD or CPPM-protected DVDAudio
306 PFEATURE_HEADER header
;
308 header
= DeviceFindFeaturePage(mmcData
->CapabilitiesBuffer
,
312 mmcData
->IsCssDvd
= (header
!= NULL
) && (header
->Current
);
315 // Update the guesstimate for the drive's write speed
316 // Use the GetConfig profile first as a quick-guess based
317 // on media "type", then continue with media-specific
318 // queries for older media types, and use GET_PERFORMANCE
319 // for all unknown/future media types.
322 // 1) Determine default based on profile (slowest for media)
323 // 2) Determine default based on MODE PAGE 2Ah
324 // 3) Determine default based on GET PERFORMANCE data
325 // 4) Choose fastest reported speed (-1 == none reported)
326 // 5) If all failed (returned -1), go with very safe (slow) default
328 // This ensures that the retries do not overload the drive's processor.
329 // Sending at highest possible speed for the media is OK, because the
330 // major downside is drive processor usage. (bus usage too, but most
331 // storage is becoming a point-to-point link.)
333 FEATURE_PROFILE_TYPE
const profile
=
334 mmcData
->CapabilitiesBuffer
->CurrentProfile
[0] << (8*1) |
335 mmcData
->CapabilitiesBuffer
->CurrentProfile
[1] << (8*0) ;
336 LONGLONG t1
= (LONGLONG
)-1;
337 LONGLONG t2
= (LONGLONG
)-1;
338 LONGLONG t3
= (LONGLONG
)-1;
339 LONGLONG t4
= (LONGLONG
)-1;
342 t1
= DeviceRetryTimeGuessBasedOnProfile(profile
);
343 t2
= DeviceRetryTimeDetectionBasedOnModePage2A(deviceExtension
);
344 t3
= DeviceRetryTimeDetectionBasedOnGetPerformance(deviceExtension
, TRUE
);
345 t4
= DeviceRetryTimeDetectionBasedOnGetPerformance(deviceExtension
, FALSE
);
347 // use the "fastest" value returned
351 final
= min(final
, t4
);
355 final
= min(final
, t3
);
359 final
= min(final
, t2
);
363 final
= min(final
, t1
);
365 if (final
== MAXLONGLONG
)
367 // worst case -- use relatively slow default....
368 final
= WRITE_RETRY_DELAY_CD_4x
;
371 cdData
->ReadWriteRetryDelay100nsUnits
= final
;
377 // Rediscovery of MMC capabilities has failed - we'll need to retry
378 cdData
->Mmc
.UpdateState
= CdromMmcUpdateRequired
;
381 // Change the state to CdromMmcUpdateComplete if it is CdromMmcUpdateStarted.
382 // If it is not, some error must have happened while this function was executed
383 // and the state is CdromMmcUpdateRequired now. In that case, we want to perform
384 // everything again, so we do not set CdromMmcUpdateComplete.
385 InterlockedCompareExchange((PLONG
)&(cdData
->Mmc
.UpdateState
),
386 CdromMmcUpdateComplete
,
387 CdromMmcUpdateStarted
);
392 _IRQL_requires_max_(PASSIVE_LEVEL
)
394 DeviceGetConfigurationWithAlloc(
395 _In_ WDFDEVICE Device
,
396 _Outptr_result_bytebuffer_all_(*BytesReturned
)
397 PGET_CONFIGURATION_HEADER
* Buffer
, // this routine allocates this memory
398 _Out_ PULONG BytesReturned
,
399 FEATURE_NUMBER
const StartingFeature
,
400 ULONG
const RequestedType
406 This function will allocates configuration buffer and set the size.
410 Device - device object
411 Buffer - to be allocated by this function
412 BytesReturned - size of the buffer
413 StartingFeature - the starting point of the feature list
420 NOTE: does not handle case where more than 65000 bytes are returned,
421 which requires multiple calls with different starting feature
426 NTSTATUS status
= STATUS_SUCCESS
;
427 GET_CONFIGURATION_HEADER header
= {0}; // eight bytes, not a lot
428 PGET_CONFIGURATION_HEADER buffer
= NULL
;
438 // send the first request down to just get the header
439 status
= DeviceGetConfiguration(Device
,
446 // now send command again, using information returned to allocate just enough memory
447 if (NT_SUCCESS(status
))
449 size
= header
.DataLength
[0] << 24 |
450 header
.DataLength
[1] << 16 |
451 header
.DataLength
[2] << 8 |
452 header
.DataLength
[3] << 0 ;
454 // the loop is in case that the retrieved data length is bigger than last time reported.
455 for (i
= 0; (i
< 4) && NT_SUCCESS(status
); i
++)
457 // the datalength field is the size *following* itself, so adjust accordingly
458 size
+= 4*sizeof(UCHAR
);
460 // make sure the size is reasonable
461 if (size
<= sizeof(FEATURE_HEADER
))
463 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_IOCTL
,
464 "DeviceGetConfigurationWithAlloc: drive reports only %x bytes?\n",
466 status
= STATUS_UNSUCCESSFUL
;
469 if (NT_SUCCESS(status
))
471 // allocate the memory
472 buffer
= (PGET_CONFIGURATION_HEADER
)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned
,
478 status
= STATUS_INSUFFICIENT_RESOURCES
;
482 if (NT_SUCCESS(status
))
484 // send the first request down to just get the header
485 status
= DeviceGetConfiguration(Device
,
492 if (!NT_SUCCESS(status
))
496 else if (returned
> size
)
499 status
= STATUS_INTERNAL_ERROR
;
503 // command succeeded.
504 if (NT_SUCCESS(status
))
506 returned
= buffer
->DataLength
[0] << 24 |
507 buffer
->DataLength
[1] << 16 |
508 buffer
->DataLength
[2] << 8 |
509 buffer
->DataLength
[3] << 0 ;
510 returned
+= 4*sizeof(UCHAR
);
512 if (returned
<= size
)
515 *BytesReturned
= returned
; // amount of 'safe' memory
516 // succes, get out of loop.
517 status
= STATUS_SUCCESS
;
522 // the data size is bigger than the buffer size, retry using new size....
528 } // end of for() loop
531 if (!NT_SUCCESS(status
))
533 // it failed after a number of attempts, so just fail.
534 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_IOCTL
,
535 "DeviceGetConfigurationWithAlloc: Failed %d attempts to get all feature "
536 "information\n", i
));
542 _IRQL_requires_max_(PASSIVE_LEVEL
)
544 DeviceGetConfiguration(
545 _In_ WDFDEVICE Device
,
546 _Out_writes_bytes_to_(BufferSize
, *ValidBytes
)
547 PGET_CONFIGURATION_HEADER Buffer
,
548 _In_ ULONG
const BufferSize
,
549 _Out_ PULONG ValidBytes
,
550 _In_ FEATURE_NUMBER
const StartingFeature
,
551 _In_ ULONG
const RequestedType
557 This function is used to get configuration data.
561 Device - device object
562 Buffer - buffer address to hold data.
563 BufferSize - size of the buffer
564 ValidBytes - valid data size in buffer
565 StartingFeature - the starting point of the feature list
572 NOTE: does not handle case where more than 64k bytes are returned,
573 which requires multiple calls with different starting feature
579 PCDROM_DEVICE_EXTENSION deviceExtension
= DeviceGetExtension(Device
);
580 SCSI_REQUEST_BLOCK srb
;
581 PCDB cdb
= (PCDB
)srb
.Cdb
;
585 NT_ASSERT(ValidBytes
);
587 // when system is low resources we can receive empty buffer
588 if (Buffer
== NULL
|| BufferSize
< sizeof(GET_CONFIGURATION_HEADER
))
590 return STATUS_BUFFER_TOO_SMALL
;
595 RtlZeroMemory(&srb
, sizeof(SCSI_REQUEST_BLOCK
));
596 RtlZeroMemory(Buffer
, BufferSize
);
598 if (TEST_FLAG(deviceExtension
->DeviceAdditionalData
.HackFlags
, CDROM_HACK_BAD_GET_CONFIG_SUPPORT
))
600 return STATUS_INVALID_DEVICE_REQUEST
;
603 #pragma warning(push)
604 #pragma warning(disable: 6386) // OACR will complain buffer overrun: the writable size is 'BufferSize' bytes, but '65532'
605 // bytes might be written, which is impossible because BufferSize > 0xFFFC.
607 if (BufferSize
> 0xFFFC)
609 // cannot request more than 0xFFFC bytes in one request
610 // Eventually will "stitch" together multiple requests if needed
611 // Today, no drive has anywhere close to 4k.....
612 return DeviceGetConfiguration(Device
,
622 srb
.TimeOutValue
= CDROM_GET_CONFIGURATION_TIMEOUT
;
625 cdb
->GET_CONFIGURATION
.OperationCode
= SCSIOP_GET_CONFIGURATION
;
626 cdb
->GET_CONFIGURATION
.RequestType
= (UCHAR
)RequestedType
;
627 cdb
->GET_CONFIGURATION
.StartingFeature
[0] = (UCHAR
)(StartingFeature
>> 8);
628 cdb
->GET_CONFIGURATION
.StartingFeature
[1] = (UCHAR
)(StartingFeature
& 0xff);
629 cdb
->GET_CONFIGURATION
.AllocationLength
[0] = (UCHAR
)(BufferSize
>> 8);
630 cdb
->GET_CONFIGURATION
.AllocationLength
[1] = (UCHAR
)(BufferSize
& 0xff);
632 status
= DeviceSendSrbSynchronously(Device
,
639 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_IOCTL
,
640 "DeviceGetConfiguration: Status was %x\n", status
));
642 if (NT_SUCCESS(status
) ||
643 (status
== STATUS_BUFFER_OVERFLOW
) ||
644 (status
== STATUS_DATA_OVERRUN
))
646 ULONG returned
= srb
.DataTransferLength
;
647 PGET_CONFIGURATION_HEADER header
= (PGET_CONFIGURATION_HEADER
)Buffer
;
648 ULONG available
= (header
->DataLength
[0] << (8*3)) |
649 (header
->DataLength
[1] << (8*2)) |
650 (header
->DataLength
[2] << (8*1)) |
651 (header
->DataLength
[3] << (8*0)) ;
653 available
+= RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER
, DataLength
);
655 _Analysis_assume_(srb
.DataTransferLength
<= BufferSize
);
657 // The true usable amount of data returned is the lesser of
658 // * the returned data per the srb.DataTransferLength field
659 // * the total size per the GET_CONFIGURATION_HEADER
660 // This is because ATAPI can't tell how many bytes really
661 // were transferred on success when using DMA.
662 if (available
< returned
)
664 returned
= available
;
667 NT_ASSERT(returned
<= BufferSize
);
668 *ValidBytes
= (ULONG
)returned
;
670 //This is succeed case
671 status
= STATUS_SUCCESS
;
675 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_IOCTL
,
676 "DeviceGetConfiguration: failed %x\n", status
));
683 _IRQL_requires_max_(APC_LEVEL
)
685 DeviceUpdateMmcWriteCapability(
686 _In_reads_bytes_(BufferSize
)
687 PGET_CONFIGURATION_HEADER Buffer
,
688 ULONG
const BufferSize
,
689 BOOLEAN
const CurrentOnly
, // TRUE == can drive write now, FALSE == can drive ever write
690 _Out_ PBOOLEAN Writable
,
691 _Out_ PFEATURE_NUMBER ValidationSchema
,
692 _Out_ PULONG BlockingFactor
698 This function will allocates configuration buffer and set the size.
703 BufferSize - size of the buffer
704 CurrentOnly - valid data size in buffer
705 Writable - the buffer is allocationed in non-paged pool.
706 validationSchema - the starting point of the feature list
713 NOTE: does not handle case where more than 64k bytes are returned,
714 which requires multiple calls with different starting feature
720 // this routine is used to check if the drive can currently (current==TRUE)
721 // or can ever (current==FALSE) write to media with the current CDROM.SYS
722 // driver. this check parses the GET_CONFIGURATION response data to search
723 // for the appropriate features and/or if they are current.
725 // this function should not allocate any resources, and thus may safely
726 // return from any point within the function.
731 *ValidationSchema
= 0;
735 // if the drive supports hardware defect management and random writes, that's
736 // sufficient to allow writes.
739 PFEATURE_HEADER defectHeader
;
740 PFEATURE_HEADER writableHeader
;
742 defectHeader
= MmcDataFindFeaturePage(Buffer
,
744 FeatureDefectManagement
);
745 writableHeader
= MmcDataFindFeaturePage(Buffer
,
747 FeatureRandomWritable
);
749 if (defectHeader
== NULL
|| writableHeader
== NULL
)
751 // cannot write this way
753 else if (!CurrentOnly
)
755 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
756 "DeviceUpdateMmcWriteCapability => Writes supported (defect management)\n"));
760 else if (defectHeader
->Current
&& writableHeader
->Current
)
762 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
763 "DeviceUpdateMmcWriteCapability => Writes *allowed* (defect management)\n"));
765 *ValidationSchema
= FeatureDefectManagement
;
770 // Certain validation schema require the blocking factor
771 // This is a best-effort attempt to ensure that illegal
772 // requests do not make it to drive
774 PFEATURE_HEADER header
;
775 ULONG additionalLength
;
777 // Certain validation schema require the blocking factor
778 // This is a best-effort attempt to ensure that illegal
779 // requests do not make it to drive
780 additionalLength
= RTL_SIZEOF_THROUGH_FIELD(FEATURE_DATA_RANDOM_READABLE
, Blocking
) - sizeof(FEATURE_HEADER
);
782 header
= MmcDataFindFeaturePage(Buffer
,
784 FeatureRandomReadable
);
786 if ((header
!= NULL
) &&
788 (header
->AdditionalLength
>= additionalLength
))
790 PFEATURE_DATA_RANDOM_READABLE feature
= (PFEATURE_DATA_RANDOM_READABLE
)header
;
791 *BlockingFactor
= (feature
->Blocking
[0] << 8) | feature
->Blocking
[1];
795 // the majority of features to indicate write capability
796 // indicate this by a single feature existance/current bit.
797 // thus, can use a table-based method for the majority
798 // of the detection....
801 FEATURE_NUMBER FeatureToFind
; // the ones allowed
802 FEATURE_NUMBER ValidationSchema
; // and their related schema
803 } FEATURE_TO_WRITE_SCHEMA_MAP
;
805 static FEATURE_TO_WRITE_SCHEMA_MAP
const FeaturesToAllowWritesWith
[] = {
806 { FeatureRandomWritable
, FeatureRandomWritable
},
807 { FeatureRigidRestrictedOverwrite
, FeatureRigidRestrictedOverwrite
},
808 { FeatureRestrictedOverwrite
, FeatureRestrictedOverwrite
},
809 { FeatureIncrementalStreamingWritable
, FeatureIncrementalStreamingWritable
},
813 for (count
= 0; count
< RTL_NUMBER_OF(FeaturesToAllowWritesWith
); count
++)
815 PFEATURE_HEADER header
= MmcDataFindFeaturePage(Buffer
,
817 FeaturesToAllowWritesWith
[count
].FeatureToFind
);
820 // cannot write using this method
822 else if (!CurrentOnly
)
824 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
825 "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
826 FeaturesToAllowWritesWith
[count
].FeatureToFind
831 else if (header
->Current
)
833 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
834 "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
835 FeaturesToAllowWritesWith
[count
].FeatureToFind
838 *ValidationSchema
= FeaturesToAllowWritesWith
[count
].ValidationSchema
;
844 // unfortunately, DVD+R media doesn't require IncrementalStreamingWritable feature
845 // to be explicitly set AND it has a seperate bit in the feature to indicate
846 // being able to write to this media type. Thus, use a special case of the above code.
848 PFEATURE_DATA_DVD_PLUS_R header
;
849 ULONG additionalLength
= FIELD_OFFSET(FEATURE_DATA_DVD_PLUS_R
, Reserved2
[0]) - sizeof(FEATURE_HEADER
);
850 header
= MmcDataFindFeaturePage(Buffer
,
854 if (header
== NULL
|| (header
->Header
.AdditionalLength
< additionalLength
) || (!header
->Write
))
856 // cannot write this way
858 else if (!CurrentOnly
)
860 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
861 "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
867 else if (header
->Header
.Current
)
869 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
870 "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
874 *ValidationSchema
= FeatureIncrementalStreamingWritable
;
879 // unfortunately, DVD+R DL media doesn't require IncrementalStreamingWritable feature
880 // to be explicitly set AND it has a seperate bit in the feature to indicate
881 // being able to write to this media type. Thus, use a special case of the above code.
883 PFEATURE_DATA_DVD_PLUS_R_DUAL_LAYER header
;
884 ULONG additionalLength
= FIELD_OFFSET(FEATURE_DATA_DVD_PLUS_R_DUAL_LAYER
, Reserved2
[0]) - sizeof(FEATURE_HEADER
);
885 header
= MmcDataFindFeaturePage(Buffer
,
887 FeatureDvdPlusRDualLayer
);
889 if (header
== NULL
|| (header
->Header
.AdditionalLength
< additionalLength
) || (!header
->Write
))
891 // cannot write this way
893 else if (!CurrentOnly
)
895 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
896 "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
897 FeatureDvdPlusRDualLayer
902 else if (header
->Header
.Current
)
904 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
905 "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
906 FeatureDvdPlusRDualLayer
909 *ValidationSchema
= FeatureIncrementalStreamingWritable
;
914 // There are currently a number of drives on the market
915 // that fail to report:
916 // (a) FeatureIncrementalStreamingWritable as current
917 // for CD-R / DVD-R profile.
918 // (b) FeatureRestrictedOverwrite as current for CD-RW
920 // (c) FeatureRigidRestrictedOverwrite as current for
923 // Thus, use the profiles also.
925 PFEATURE_HEADER header
;
926 header
= MmcDataFindFeaturePage(Buffer
,
930 if (header
!= NULL
&& header
->Current
)
932 // verify buffer bounds -- the below routine presumes full profile list provided
933 PUCHAR bufferEnd
= ((PUCHAR
)Buffer
) + BufferSize
;
934 PUCHAR headerEnd
= ((PUCHAR
)header
) + header
->AdditionalLength
+ RTL_SIZEOF_THROUGH_FIELD(FEATURE_HEADER
, AdditionalLength
);
935 if (bufferEnd
>= headerEnd
) // this _should_ never occurr, but....
937 // Profiles don't contain any data other than current/not current.
938 // thus, can generically loop through them to see if any of the
939 // below (in order of preference) are current.
941 FEATURE_PROFILE_TYPE ProfileToFind
; // the ones allowed
942 FEATURE_NUMBER ValidationSchema
; // and their related schema
943 } PROFILE_TO_WRITE_SCHEMA_MAP
;
945 static PROFILE_TO_WRITE_SCHEMA_MAP
const ProfilesToAllowWritesWith
[] = {
946 { ProfileDvdRewritable
, FeatureRigidRestrictedOverwrite
},
947 { ProfileCdRewritable
, FeatureRestrictedOverwrite
},
948 { ProfileDvdRecordable
, FeatureIncrementalStreamingWritable
},
949 { ProfileCdRecordable
, FeatureIncrementalStreamingWritable
},
953 for (count
= 0; count
< RTL_NUMBER_OF(ProfilesToAllowWritesWith
); count
++)
955 BOOLEAN exists
= FALSE
;
956 MmcDataFindProfileInProfiles((PFEATURE_DATA_PROFILE_LIST
)header
,
957 ProfilesToAllowWritesWith
[count
].ProfileToFind
,
962 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_INIT
,
963 "DeviceUpdateMmcWriteCapability => Writes %s (profile %04x)\n",
964 (CurrentOnly
? "*allowed*" : "supported"),
969 *ValidationSchema
= ProfilesToAllowWritesWith
[count
].ValidationSchema
;
973 } // end if (bufferEnd >= headerEnd)
975 } // end if (header != NULL && header->Current)
978 // nothing matched to say it's writable.....
982 _IRQL_requires_max_(APC_LEVEL
)
984 MmcDataFindFeaturePage(
985 _In_reads_bytes_(Length
)
986 PGET_CONFIGURATION_HEADER FeatureBuffer
,
988 FEATURE_NUMBER
const Feature
994 search the specific feature from feature list buffer
998 FeatureBuffer - buffer of feature list
999 Length - size of the buffer
1000 Feature - feature wanted to find
1004 PVOID - if found, pointer of starting address of the specific feature.
1015 if (Length
< sizeof(GET_CONFIGURATION_HEADER
) + sizeof(FEATURE_HEADER
)) {
1019 // Calculate the length of valid data available in the
1020 // capabilities buffer from the DataLength field
1021 REVERSE_BYTES(&validLength
, FeatureBuffer
->DataLength
);
1022 validLength
+= RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER
, DataLength
);
1024 // set limit to point to first illegal address
1025 limit
= (PUCHAR
)FeatureBuffer
;
1026 limit
+= min(Length
, validLength
);
1028 // set buffer to point to first page
1029 buffer
= FeatureBuffer
->Data
;
1031 // loop through each page until we find the requested one, or
1032 // until it's not safe to access the entire feature header
1033 // (if equal, have exactly enough for the feature header)
1034 while (buffer
+ sizeof(FEATURE_HEADER
) <= limit
)
1036 PFEATURE_HEADER header
= (PFEATURE_HEADER
)buffer
;
1037 FEATURE_NUMBER thisFeature
;
1039 thisFeature
= (header
->FeatureCode
[0] << 8) |
1040 (header
->FeatureCode
[1]);
1042 if (thisFeature
== Feature
)
1046 // if don't have enough memory to safely access all the feature
1047 // information, return NULL
1049 temp
+= sizeof(FEATURE_HEADER
);
1050 temp
+= header
->AdditionalLength
;
1054 // this means the transfer was cut-off, an insufficiently
1055 // small buffer was given, or other arbitrary error. since
1056 // it's not safe to view the amount of data (even though
1057 // the header is safe) in this feature, pretend it wasn't
1058 // transferred at all...
1059 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_GENERAL
,
1060 "Feature %x exists, but not safe to access all its "
1061 "data. returning NULL\n", Feature
));
1070 if ((header
->AdditionalLength
% 4) &&
1071 !(Feature
>= 0xff00 && Feature
<= 0xffff))
1076 buffer
+= sizeof(FEATURE_HEADER
);
1077 buffer
+= header
->AdditionalLength
;
1083 _IRQL_requires_max_(APC_LEVEL
)
1085 MmcDataFindProfileInProfiles(
1086 _In_ FEATURE_DATA_PROFILE_LIST
const* ProfileHeader
,
1087 _In_ FEATURE_PROFILE_TYPE
const ProfileToFind
,
1088 _In_ BOOLEAN
const CurrentOnly
,
1089 _Out_ PBOOLEAN Found
1093 Routine Description:
1095 search the specific feature from feature list buffer
1099 ProfileHeader - buffer of profile list
1100 ProfileToFind - profile to be found
1105 Found - found or not
1109 FEATURE_DATA_PROFILE_LIST_EX
const * profile
;
1110 ULONG numberOfProfiles
;
1115 // initialize output
1119 if (ProfileHeader
->Header
.AdditionalLength
% 2 != 0)
1121 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_GENERAL
,
1122 "Profile total length %x is not integral multiple of 4\n",
1123 ProfileHeader
->Header
.AdditionalLength
));
1128 // calculate number of profiles
1129 numberOfProfiles
= ProfileHeader
->Header
.AdditionalLength
/ 4;
1130 profile
= ProfileHeader
->Profiles
; // zero-sized array
1132 // loop through profiles
1133 for (i
= 0; i
< numberOfProfiles
; i
++)
1135 FEATURE_PROFILE_TYPE currentProfile
;
1137 currentProfile
= (profile
->ProfileNumber
[0] << 8) |
1138 (profile
->ProfileNumber
[1] & 0xff);
1140 if (currentProfile
== ProfileToFind
)
1142 if (profile
->Current
|| (!CurrentOnly
))
1154 _IRQL_requires_max_(APC_LEVEL
)
1155 _Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS
)
1157 DeviceRetryTimeGuessBasedOnProfile(
1158 FEATURE_PROFILE_TYPE
const Profile
1162 Routine Description:
1164 determine the retry time based on profile
1172 LONGLONG - retry time
1176 LONGLONG result
= -1; // this means we have no idea
1182 case ProfileInvalid
: // = 0x0000,
1183 case ProfileNonRemovableDisk
: // = 0x0001,
1184 case ProfileRemovableDisk
: // = 0x0002,
1185 case ProfileMOErasable
: // = 0x0003,
1186 case ProfileMOWriteOnce
: // = 0x0004,
1187 case ProfileAS_MO
: // = 0x0005,
1188 // Reserved 0x0006 - 0x0007,
1189 // Reserved 0x000b - 0x000f,
1190 // Reserved 0x0017 - 0x0019
1191 // Reserved 0x001C - 001F
1192 // Reserved 0x0023 - 0x0029
1193 // Reserved 0x002C - 0x003F
1194 // Reserved 0x0044 - 0x004F
1195 // Reserved 0x0053 - 0xfffe
1196 case ProfileNonStandard
: // = 0xffff
1199 NOTHING
; // no default
1203 case ProfileCdrom
: // = 0x0008,
1204 case ProfileCdRecordable
: // = 0x0009,
1205 case ProfileCdRewritable
: // = 0x000a,
1206 case ProfileDDCdrom
: // = 0x0020, // obsolete
1207 case ProfileDDCdRecordable
: // = 0x0021, // obsolete
1208 case ProfileDDCdRewritable
: // = 0x0022, // obsolete
1210 // 4x is ok as all CD drives have
1211 // at least 64k*4 (256k) buffer
1212 // and this is just a first-pass
1213 // guess based only on profile
1214 result
= WRITE_RETRY_DELAY_CD_4x
;
1217 case ProfileDvdRom
: // = 0x0010,
1218 case ProfileDvdRecordable
: // = 0x0011,
1219 case ProfileDvdRam
: // = 0x0012,
1220 case ProfileDvdRewritable
: // = 0x0013, // restricted overwrite
1221 case ProfileDvdRWSequential
: // = 0x0014,
1222 case ProfileDvdDashRLayerJump
: // = 0x0016,
1223 case ProfileDvdPlusRW
: // = 0x001A,
1224 case ProfileDvdPlusR
: // = 0x001B,
1226 result
= WRITE_RETRY_DELAY_DVD_1x
;
1229 case ProfileDvdDashRDualLayer
: // = 0x0015,
1230 case ProfileDvdPlusRWDualLayer
: // = 0x002A,
1231 case ProfileDvdPlusRDualLayer
: // = 0x002B,
1233 result
= WRITE_RETRY_DELAY_DVD_1x
;
1237 case ProfileBDRom
: // = 0x0040,
1238 case ProfileBDRSequentialWritable
: // = 0x0041, // BD-R 'SRM'
1239 case ProfileBDRRandomWritable
: // = 0x0042, // BD-R 'RRM'
1240 case ProfileBDRewritable
: // = 0x0043,
1242 // I could not find specifications for the
1243 // minimal 1x data rate for BD media. Use
1244 // HDDVD values for now, since they are
1245 // likely to be similar. Also, all media
1246 // except for CD, DVD, and AS-MO should
1247 // already fully support GET_CONFIG, so
1248 // this guess is only used if we fail to
1249 // get a performance descriptor....
1250 result
= WRITE_RETRY_DELAY_HDDVD_1x
;
1254 case ProfileHDDVDRom
: // = 0x0050,
1255 case ProfileHDDVDRecordable
: // = 0x0051,
1256 case ProfileHDDVDRam
: // = 0x0052,
1258 // All HDDVD drives support GET_PERFORMANCE
1259 // so this guess is fine at 1x....
1260 result
= WRITE_RETRY_DELAY_HDDVD_1x
;
1264 // addition of any further profile types is not
1265 // technically required as GET PERFORMANCE
1266 // should succeed for all future drives. However,
1267 // it is useful in case GET PERFORMANCE does
1268 // fail for other reasons (i.e. bus resets, etc)
1270 } // end switch(Profile)
1275 _IRQL_requires_max_(APC_LEVEL
)
1276 _Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS
)
1278 DeviceRetryTimeDetectionBasedOnModePage2A(
1279 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1283 Routine Description:
1285 determine the retry time based on mode sense data
1289 DeviceExtension - device context
1293 LONGLONG - retry time
1298 ULONG transferSize
= min(0xFFF0, DeviceExtension
->ScratchContext
.ScratchBufferSize
);
1300 LONGLONG result
= -1;
1304 ScratchBuffer_BeginUse(DeviceExtension
);
1306 RtlZeroMemory(&cdb
, sizeof(CDB
));
1308 cdb
.MODE_SENSE10
.OperationCode
= SCSIOP_MODE_SENSE10
;
1309 cdb
.MODE_SENSE10
.Dbd
= 1;
1310 cdb
.MODE_SENSE10
.PageCode
= MODE_PAGE_CAPABILITIES
;
1311 cdb
.MODE_SENSE10
.AllocationLength
[0] = (UCHAR
)(transferSize
>> 8);
1312 cdb
.MODE_SENSE10
.AllocationLength
[1] = (UCHAR
)(transferSize
& 0xFF);
1314 status
= ScratchBuffer_ExecuteCdb(DeviceExtension
, NULL
, transferSize
, TRUE
, &cdb
, 10);
1316 // analyze the data on success....
1317 if (NT_SUCCESS(status
))
1319 MODE_PARAMETER_HEADER10
const* header
= DeviceExtension
->ScratchContext
.ScratchBuffer
;
1320 CDVD_CAPABILITIES_PAGE
const* page
= NULL
;
1321 ULONG dataLength
= (header
->ModeDataLength
[0] << (8*1)) |
1322 (header
->ModeDataLength
[1] << (8*0)) ;
1324 // no possible overflow
1325 if (dataLength
!= 0)
1327 dataLength
+= RTL_SIZEOF_THROUGH_FIELD(MODE_PARAMETER_HEADER10
, ModeDataLength
);
1330 // If it's not abundantly clear, we really don't trust the drive
1331 // to be returning valid data. Get the page pointer and usable
1332 // size of the page here...
1333 if (dataLength
< sizeof(MODE_PARAMETER_HEADER10
))
1337 else if (dataLength
> DeviceExtension
->ScratchContext
.ScratchBufferSize
)
1341 else if ((header
->BlockDescriptorLength
[1] == 0) &&
1342 (header
->BlockDescriptorLength
[0] == 0))
1344 dataLength
-= sizeof(MODE_PARAMETER_HEADER10
);
1345 page
= (CDVD_CAPABILITIES_PAGE
const *)(header
+ 1);
1347 else if ((header
->BlockDescriptorLength
[1] == 0) &&
1348 (header
->BlockDescriptorLength
[0] == sizeof(MODE_PARAMETER_BLOCK
)))
1350 dataLength
-= sizeof(MODE_PARAMETER_HEADER10
);
1351 dataLength
-= min(dataLength
, sizeof(MODE_PARAMETER_BLOCK
));
1352 page
= (CDVD_CAPABILITIES_PAGE
const *)
1353 ( ((PUCHAR
)header
) +
1354 sizeof(MODE_PARAMETER_HEADER10
) +
1355 sizeof(MODE_PARAMETER_BLOCK
)
1359 // Change dataLength from the size available per the header to
1360 // the size available per the page itself.
1361 if ((page
!= NULL
) &&
1362 (dataLength
>= RTL_SIZEOF_THROUGH_FIELD(CDVD_CAPABILITIES_PAGE
, PageLength
))
1365 dataLength
= min(dataLength
, ((ULONG
)(page
->PageLength
) + 2));
1368 // Ignore the page if the fastest write speed field isn't available.
1369 if ((page
!= NULL
) &&
1370 (dataLength
< RTL_SIZEOF_THROUGH_FIELD(CDVD_CAPABILITIES_PAGE
, WriteSpeedMaximum
))
1373 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_INIT
,
1374 "ModePage 2Ah was requested, but drive reported "
1375 "only %x bytes (%x needed). Ignoring.\n",
1377 RTL_SIZEOF_THROUGH_FIELD(CDVD_CAPABILITIES_PAGE
, WriteSpeedMaximum
)
1382 // Verify the page we requested is the one the drive actually provided
1383 if ((page
!= NULL
) && (page
->PageCode
!= MODE_PAGE_CAPABILITIES
))
1385 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_INIT
,
1386 "ModePage 2Ah was requested, but drive reported "
1393 // If _everything_ succeeded, then use the speed value in the page!
1397 (page
->WriteSpeedMaximum
[0] << (8*1)) |
1398 (page
->WriteSpeedMaximum
[1] << (8*0)) ;
1399 // stored as 1,000 byte increments...
1401 // typically stored at 2448 bytes/sector due to CD media
1402 // error up to 20% high by presuming it returned 2048 data
1403 // and convert to sectors/second
1405 // currently: sectors/sec
1406 // ignore too-small or zero values
1409 result
= ConvertSectorsPerSecondTo100nsUnitsFor64kWrite(temp
);
1414 ScratchBuffer_EndUse(DeviceExtension
);
1420 _IRQL_requires_max_(APC_LEVEL
)
1421 _Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS
)
1423 DeviceRetryTimeDetectionBasedOnGetPerformance(
1424 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
,
1425 _In_ BOOLEAN UseLegacyNominalPerformance
1429 Routine Description:
1431 determine the retry time based on get performance data
1435 DeviceExtension - device context
1436 UseLegacyNominalPerformance -
1440 LONGLONG - retry time
1444 typedef struct _GET_PERFORMANCE_HEADER
{
1445 UCHAR TotalDataLength
[4]; // not including this field
1448 UCHAR Reserved0
: 6;
1450 } GET_PERFORMANCE_HEADER
, *PGET_PERFORMANCE_HEADER
;
1451 C_ASSERT( sizeof(GET_PERFORMANCE_HEADER
) == 8);
1453 typedef struct _GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
{
1455 UCHAR StartPerformance
[4];
1457 UCHAR EndPerformance
[4];
1458 } GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
, *PGET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
;
1459 C_ASSERT( sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
) == 16);
1462 typedef struct _GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
{
1463 UCHAR MixedReadWrite
: 1;
1464 UCHAR GuaranteedForWholeMedia
: 1;
1465 UCHAR Reserved0_RDD
: 1;
1466 UCHAR WriteRotationControl
: 2;
1467 UCHAR Reserved1
: 3;
1470 UCHAR MediaCapacity
[4];
1471 UCHAR ReadSpeedKilobytesPerSecond
[4];
1472 UCHAR WriteSpeedKilobytesPerSecond
[4];
1473 } GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
, *PGET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
;
1474 C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
) == 16);
1479 LONGLONG result
= -1;
1481 // transfer size -- descriptors + 8 byte header
1482 // Note: this size is identical for both descriptor types
1483 C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
) == sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
));
1485 ULONG
const maxDescriptors
= min(200, (DeviceExtension
->ScratchContext
.ScratchBufferSize
-sizeof(GET_PERFORMANCE_HEADER
))/sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
));
1486 ULONG validDescriptors
= 0;
1487 ULONG transferSize
= sizeof(GET_PERFORMANCE_HEADER
) + (maxDescriptors
*sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
));
1492 ScratchBuffer_BeginUse(DeviceExtension
);
1494 RtlZeroMemory(&cdb
, sizeof(CDB
));
1496 if (UseLegacyNominalPerformance
)
1498 cdb
.GET_PERFORMANCE
.OperationCode
= SCSIOP_GET_PERFORMANCE
;
1499 cdb
.GET_PERFORMANCE
.Except
= 0;
1500 cdb
.GET_PERFORMANCE
.Write
= 1;
1501 cdb
.GET_PERFORMANCE
.Tolerance
= 2; // only defined option
1502 cdb
.GET_PERFORMANCE
.MaximumNumberOfDescriptors
[1] = (UCHAR
)maxDescriptors
;
1503 cdb
.GET_PERFORMANCE
.Type
= 0; // legacy nominal descriptors
1507 cdb
.GET_PERFORMANCE
.OperationCode
= SCSIOP_GET_PERFORMANCE
;
1508 cdb
.GET_PERFORMANCE
.MaximumNumberOfDescriptors
[1] = (UCHAR
)maxDescriptors
;
1509 cdb
.GET_PERFORMANCE
.Type
= 3; // write speed
1512 status
= ScratchBuffer_ExecuteCdbEx(DeviceExtension
, NULL
, transferSize
, TRUE
, &cdb
, 12, CDROM_GET_PERFORMANCE_TIMEOUT
);
1514 // determine how many valid descriptors there actually are
1515 if (NT_SUCCESS(status
))
1517 GET_PERFORMANCE_HEADER
const* header
= (GET_PERFORMANCE_HEADER
const*)DeviceExtension
->ScratchContext
.ScratchBuffer
;
1518 ULONG temp1
= (header
->TotalDataLength
[0] << (8*3)) |
1519 (header
->TotalDataLength
[1] << (8*2)) |
1520 (header
->TotalDataLength
[2] << (8*1)) |
1521 (header
->TotalDataLength
[3] << (8*0)) ;
1523 // adjust data size for header
1524 if (temp1
+ (ULONG
)RTL_SIZEOF_THROUGH_FIELD(GET_PERFORMANCE_HEADER
, TotalDataLength
) < temp1
)
1528 else if (temp1
!= 0)
1530 temp1
+= RTL_SIZEOF_THROUGH_FIELD(GET_PERFORMANCE_HEADER
, TotalDataLength
);
1537 else if (temp1
<= sizeof(GET_PERFORMANCE_HEADER
))
1539 // only the header returned, no descriptors
1541 else if (UseLegacyNominalPerformance
&&
1542 ((header
->Except
!= 0) || (header
->Write
== 0))
1545 // bad data being returned -- ignore it
1547 else if (!UseLegacyNominalPerformance
&&
1548 ((header
->Except
!= 0) || (header
->Write
!= 0))
1551 // returning Performance (Type 0) data, not requested Write Speed (Type 3) data
1553 else if ( (temp1
- sizeof(GET_PERFORMANCE_HEADER
)) % sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
) != 0)
1555 // Note: this size is identical for both descriptor types
1556 C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
) == sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
));
1558 // not returning valid data....
1560 else // save how many are usable
1562 // Note: this size is identical for both descriptor types
1563 C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
) == sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
));
1565 // take the smaller usable value
1566 temp1
= min(temp1
, DeviceExtension
->ScratchContext
.ScratchSrb
->DataTransferLength
);
1567 // then determine the usable descriptors
1568 validDescriptors
= (temp1
- sizeof(GET_PERFORMANCE_HEADER
)) / sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
);
1572 // The drive likely supports this command.
1573 // Verify the data makes sense.
1574 if (NT_SUCCESS(status
))
1577 GET_PERFORMANCE_HEADER
const* header
= (GET_PERFORMANCE_HEADER
const*)DeviceExtension
->ScratchContext
.ScratchBuffer
;
1578 GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
const* descriptor
= (GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
const*)(header
+1); // pointer math
1580 // NOTE: We could write this loop twice, once for each write descriptor type
1581 // However, the only fields of interest are the writeKBps field (Type 3) and
1582 // the EndPerformance field (Type 0), which both exist in the same exact
1583 // location and have essentially the same meaning. So, just use the same
1584 // loop/structure pointers for both of the to simplify the readability of
1585 // this code. The C_ASSERT()s here verify this at compile-time.
1587 C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
) == sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
));
1588 C_ASSERT( FIELD_OFFSET(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
, WriteSpeedKilobytesPerSecond
) ==
1589 FIELD_OFFSET(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
, EndPerformance
)
1591 C_ASSERT( RTL_FIELD_SIZE(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR
, WriteSpeedKilobytesPerSecond
) ==
1592 RTL_FIELD_SIZE(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR
, EndPerformance
)
1595 // loop through them all, and find the fastest listed write speed
1596 for (i
= 0; NT_SUCCESS(status
) && (i
<validDescriptors
); descriptor
++, i
++)
1598 ULONG
const writeKBps
=
1599 (descriptor
->WriteSpeedKilobytesPerSecond
[0] << (8*3)) |
1600 (descriptor
->WriteSpeedKilobytesPerSecond
[1] << (8*2)) |
1601 (descriptor
->WriteSpeedKilobytesPerSecond
[2] << (8*1)) |
1602 (descriptor
->WriteSpeedKilobytesPerSecond
[3] << (8*0)) ;
1604 // Avoid overflow and still have good estimates
1605 // 0x1 0000 0000 / 1000 == 0x00418937 == maximum writeKBps to multiple first
1606 ULONG
const sectorsPerSecond
=
1607 (writeKBps
> 0x00418937) ? // would overflow occur by multiplying by 1000?
1608 ((writeKBps
/ 2048) * 1000) : // must divide first, minimal loss of accuracy
1609 ((writeKBps
* 1000) / 2048) ; // must multiply first, avoid loss of accuracy
1611 if (sectorsPerSecond
<= 0)
1613 break; // out of the loop -- no longer valid data (very defensive programming)
1616 // we have at least one valid result, so prevent returning -1 as our result
1617 if (result
== -1) { result
= MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS
; }
1619 // take the fastest speed (smallest wait time) we've found thus far
1620 result
= min(result
, ConvertSectorsPerSecondTo100nsUnitsFor64kWrite(sectorsPerSecond
));
1624 ScratchBuffer_EndUse(DeviceExtension
);
1629 #pragma warning(pop) // un-sets any local warning changes