ReactOS 0.4.15-dev-8241-g63935f8
mmc.c
Go to the documentation of this file.
1/*--
2
3Copyright (C) Microsoft Corporation. All rights reserved.
4
5Module Name:
6
7 mmc.c
8
9Abstract:
10
11 Include all funtions relate to MMC
12
13Environment:
14
15 kernel mode only
16
17Notes:
18
19
20Revision History:
21
22--*/
23
24#include "stddef.h"
25#include "string.h"
26
27#include "ntddk.h"
28#include "ntddstor.h"
29#include "cdrom.h"
30#include "mmc.h"
31#include "scratch.h"
32
33#ifdef DEBUG_USE_WPP
34#include "mmc.tmh"
35#endif
36
37#ifdef ALLOC_PRAGMA
38
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)
50
51#endif
52
53#pragma warning(push)
54#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
55
57VOID
58DeviceDeallocateMmcResources(
59 _In_ WDFDEVICE Device
60 )
61/*++
62
63Routine Description:
64
65 release MMC resources
66
67Arguments:
68
69 Device - device object
70
71Return Value:
72
73 none
74
75--*/
76{
77 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
78 PCDROM_DATA cddata = &(deviceExtension->DeviceAdditionalData);
79 PCDROM_MMC_EXTENSION mmcData = &cddata->Mmc;
80
81 PAGED_CODE();
82
83 if (mmcData->CapabilitiesIrp)
84 {
85 IoFreeIrp(mmcData->CapabilitiesIrp);
86 mmcData->CapabilitiesIrp = NULL;
87 }
88 if (mmcData->CapabilitiesMdl)
89 {
90 IoFreeMdl(mmcData->CapabilitiesMdl);
91 mmcData->CapabilitiesMdl = NULL;
92 }
93 if (mmcData->CapabilitiesBuffer)
94 {
95 ExFreePool(mmcData->CapabilitiesBuffer);
96 mmcData->CapabilitiesBuffer = NULL;
97 }
98 if (mmcData->CapabilitiesRequest)
99 {
100 WdfObjectDelete(mmcData->CapabilitiesRequest);
101 mmcData->CapabilitiesRequest = NULL;
102 }
103 mmcData->CapabilitiesBufferSize = 0;
104 mmcData->IsMmc = FALSE;
105 mmcData->WriteAllowed = FALSE;
106
107 return;
108}
109
110
113DeviceAllocateMmcResources(
114 _In_ WDFDEVICE Device
115 )
116/*++
117
118Routine Description:
119
120 allocate all MMC resources needed
121
122Arguments:
123
124 Device - device object
125
126Return Value:
127
128 NTSTATUS
129
130--*/
131{
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};
137
138 PAGED_CODE();
139
140 NT_ASSERT(mmcData->CapabilitiesBuffer == NULL);
141 NT_ASSERT(mmcData->CapabilitiesBufferSize == 0);
142
143 // allocate the buffer and set the buffer size.
144 // retrieve drive configuration information.
145 status = DeviceGetConfigurationWithAlloc(Device,
146 &mmcData->CapabilitiesBuffer,
147 &mmcData->CapabilitiesBufferSize,
150 if (!NT_SUCCESS(status))
151 {
152 NT_ASSERT(mmcData->CapabilitiesBuffer == NULL);
153 NT_ASSERT(mmcData->CapabilitiesBufferSize == 0);
154 return status;
155 }
156
157 NT_ASSERT(mmcData->CapabilitiesBuffer != NULL);
158 NT_ASSERT(mmcData->CapabilitiesBufferSize != 0);
159
160 // Create an MDL over the new Buffer (allocated by DeviceGetConfiguration)
161 mmcData->CapabilitiesMdl = IoAllocateMdl(mmcData->CapabilitiesBuffer,
162 mmcData->CapabilitiesBufferSize,
163 FALSE, FALSE, NULL);
164 if (mmcData->CapabilitiesMdl == NULL)
165 {
166 ExFreePool(mmcData->CapabilitiesBuffer);
167 mmcData->CapabilitiesBuffer = NULL;
168 mmcData->CapabilitiesBufferSize = 0;
170 }
171
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)
175 {
176 IoFreeMdl(mmcData->CapabilitiesMdl);
177 mmcData->CapabilitiesMdl = NULL;
178 ExFreePool(mmcData->CapabilitiesBuffer);
179 mmcData->CapabilitiesBuffer = NULL;
180 mmcData->CapabilitiesBufferSize = 0;
182 }
183
184 // create WDF request object
187 status = WdfRequestCreateFromIrp(&attributes,
188 mmcData->CapabilitiesIrp,
189 FALSE,
190 &mmcData->CapabilitiesRequest);
191 if (!NT_SUCCESS(status))
192 {
193 return status;
194 }
195
196 return STATUS_SUCCESS;
197}
198
201DeviceUpdateMmcCapabilities(
202 _In_ WDFDEVICE Device
203 )
204/*++
205
206Routine Description:
207
208 issue get congiguration command ans save result in device extension
209
210Arguments:
211
212 Device - device object
213
214Return Value:
215
216 NTSTATUS
217
218--*/
219{
221 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
222 PCDROM_DATA cdData = &(deviceExtension->DeviceAdditionalData);
223 PCDROM_MMC_EXTENSION mmcData = &(cdData->Mmc);
224 ULONG returnedBytes = 0;
225 LONG updateState;
226
227 PAGED_CODE();
228
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),
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;
238 }
239
240 // default to read-only, no Streaming, non-blank
241 mmcData->WriteAllowed = FALSE;
242 mmcData->StreamingReadSupported = FALSE;
244
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,
251 &returnedBytes,
254
255 if (NT_SUCCESS(status) && // succeeded.
256 (mmcData->CapabilitiesBufferSize >= returnedBytes)) // not overflow.
257 {
258 // update whether or not writes are allowed
259 // this should be the *ONLY* place writes are set to allowed
260 {
261 BOOLEAN writeAllowed = FALSE;
262 FEATURE_NUMBER validationSchema = 0;
263 ULONG blockingFactor = 1;
264
265 DeviceUpdateMmcWriteCapability(mmcData->CapabilitiesBuffer,
266 returnedBytes,
267 TRUE,
268 &writeAllowed,
269 &validationSchema,
270 &blockingFactor);
271
272 mmcData->WriteAllowed = writeAllowed;
273 mmcData->ValidationSchema = validationSchema;
274 mmcData->Blocking = blockingFactor;
275 }
276
277 // Check if Streaming reads/writes are supported and cache
278 // this information for later use.
279 {
281 ULONG minAdditionalLength;
282
284 sizeof(FEATURE_HEADER);
285
286 header = MmcDataFindFeaturePage(mmcData->CapabilitiesBuffer,
287 returnedBytes,
289
290 if ((header != NULL) &&
291 (header->Current) &&
292 (header->AdditionalLength >= minAdditionalLength))
293 {
295
296 // If Real-Time feature is current, then Streaming reads are supported for sure.
297 mmcData->StreamingReadSupported = TRUE;
298
299 // Streaming writes are supported if an appropriate bit is set in the feature page.
300 mmcData->StreamingWriteSupported = (feature->StreamRecording == 1);
301 }
302 }
303
304 // update the flag to reflect that if the media is CSS protected DVD or CPPM-protected DVDAudio
305 {
307
308 header = DeviceFindFeaturePage(mmcData->CapabilitiesBuffer,
309 returnedBytes,
311
312 mmcData->IsCssDvd = (header != NULL) && (header->Current);
313 }
314
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.
320 {
321 // pseudo-code:
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
327 //
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.)
332
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;
340 LONGLONG final;
341
343 t2 = DeviceRetryTimeDetectionBasedOnModePage2A(deviceExtension);
346
347 // use the "fastest" value returned
348 final = MAXLONGLONG;
349 if (t4 != -1)
350 {
351 final = min(final, t4);
352 }
353 if (t3 != -1)
354 {
355 final = min(final, t3);
356 }
357 if (t2 != -1)
358 {
359 final = min(final, t2);
360 }
361 if (t1 != -1)
362 {
363 final = min(final, t1);
364 }
365 if (final == MAXLONGLONG)
366 {
367 // worst case -- use relatively slow default....
369 }
370
371 cdData->ReadWriteRetryDelay100nsUnits = final;
372 }
373
374 }
375 else
376 {
377 // Rediscovery of MMC capabilities has failed - we'll need to retry
379 }
380
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.
388
389 return status;
390}
391
394DeviceGetConfigurationWithAlloc(
395 _In_ WDFDEVICE Device,
397 PGET_CONFIGURATION_HEADER* Buffer, // this routine allocates this memory
400 ULONG const RequestedType
401 )
402/*++
403
404Routine Description:
405
406 This function will allocates configuration buffer and set the size.
407
408Arguments:
409
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
414 RequestedType -
415
416Return Value:
417
418 NTSTATUS
419
420NOTE: does not handle case where more than 65000 bytes are returned,
421 which requires multiple calls with different starting feature
422 numbers.
423
424--*/
425{
427 GET_CONFIGURATION_HEADER header = {0}; // eight bytes, not a lot
429 ULONG returned = 0;
430 ULONG size = 0;
431 ULONG i = 0;
432
433 PAGED_CODE();
434
435 *Buffer = NULL;
436 *BytesReturned = 0;
437
438 // send the first request down to just get the header
439 status = DeviceGetConfiguration(Device,
440 &header,
441 sizeof(header),
442 &returned,
445
446 // now send command again, using information returned to allocate just enough memory
447 if (NT_SUCCESS(status))
448 {
449 size = header.DataLength[0] << 24 |
450 header.DataLength[1] << 16 |
451 header.DataLength[2] << 8 |
452 header.DataLength[3] << 0 ;
453
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++)
456 {
457 // the datalength field is the size *following* itself, so adjust accordingly
458 size += 4*sizeof(UCHAR);
459
460 // make sure the size is reasonable
461 if (size <= sizeof(FEATURE_HEADER))
462 {
463 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
464 "DeviceGetConfigurationWithAlloc: drive reports only %x bytes?\n",
465 size));
467 }
468
469 if (NT_SUCCESS(status))
470 {
471 // allocate the memory
472 buffer = (PGET_CONFIGURATION_HEADER)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
473 size,
475
476 if (buffer == NULL)
477 {
479 }
480 }
481
482 if (NT_SUCCESS(status))
483 {
484 // send the first request down to just get the header
485 status = DeviceGetConfiguration(Device,
486 buffer,
487 size,
488 &returned,
491
492 if (!NT_SUCCESS(status))
493 {
495 }
496 else if (returned > size)
497 {
500 }
501 }
502
503 // command succeeded.
504 if (NT_SUCCESS(status))
505 {
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);
511
512 if (returned <= size)
513 {
514 *Buffer = buffer;
515 *BytesReturned = returned; // amount of 'safe' memory
516 // succes, get out of loop.
518 break;
519 }
520 else
521 {
522 // the data size is bigger than the buffer size, retry using new size....
523 size = returned;
525 buffer = NULL;
526 }
527 }
528 } // end of for() loop
529 }
530
531 if (!NT_SUCCESS(status))
532 {
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));
537 }
538
539 return status;
540}
541
544DeviceGetConfiguration(
545 _In_ WDFDEVICE Device,
548 _In_ ULONG const BufferSize,
552 )
553/*++
554
555Routine Description:
556
557 This function is used to get configuration data.
558
559Arguments:
560
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
566 RequestedType -
567
568Return Value:
569
570 NTSTATUS
571
572NOTE: does not handle case where more than 64k bytes are returned,
573 which requires multiple calls with different starting feature
574 numbers.
575
576--*/
577{
579 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
581 PCDB cdb = (PCDB)srb.Cdb;
582
583 PAGED_CODE();
584
586
587 // when system is low resources we can receive empty buffer
589 {
591 }
592
593 *ValidBytes = 0;
594
595 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
597
599 {
601 }
602
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.
606
607 if (BufferSize > 0xFFFC)
608 {
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,
613 Buffer,
614 0xFFFC,
618 }
619#pragma warning(pop)
620
621 //Start real work
623 srb.CdbLength = 10;
624
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);
631
632 status = DeviceSendSrbSynchronously(Device,
633 &srb,
634 Buffer,
636 FALSE,
637 NULL);
638
639 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
640 "DeviceGetConfiguration: Status was %x\n", status));
641
642 if (NT_SUCCESS(status) ||
645 {
646 ULONG returned = srb.DataTransferLength;
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)) ;
652
654
656
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)
663 {
664 returned = available;
665 }
666
667 NT_ASSERT(returned <= BufferSize);
668 *ValidBytes = (ULONG)returned;
669
670 //This is succeed case
672 }
673 else
674 {
675 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
676 "DeviceGetConfiguration: failed %x\n", status));
677 }
678
679 return status;
680}
681
682
684VOID
685DeviceUpdateMmcWriteCapability(
688 ULONG const BufferSize,
689 BOOLEAN const CurrentOnly, // TRUE == can drive write now, FALSE == can drive ever write
693 )
694/*++
695
696Routine Description:
697
698 This function will allocates configuration buffer and set the size.
699
700Arguments:
701
702 Buffer -
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
707 BlockingFactor -
708
709Return Value:
710
711 NTSTATUS
712
713NOTE: does not handle case where more than 64k bytes are returned,
714 which requires multiple calls with different starting feature
715 numbers.
716
717--*/
718{
719 //
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.
724 //
725 // this function should not allocate any resources, and thus may safely
726 // return from any point within the function.
727 //
728 PAGED_CODE();
729
730 *Writable = FALSE;
731 *ValidationSchema = 0;
732 *BlockingFactor = 1;
733
734 //
735 // if the drive supports hardware defect management and random writes, that's
736 // sufficient to allow writes.
737 //
738 {
739 PFEATURE_HEADER defectHeader;
740 PFEATURE_HEADER writableHeader;
741
742 defectHeader = MmcDataFindFeaturePage(Buffer,
745 writableHeader = MmcDataFindFeaturePage(Buffer,
748
749 if (defectHeader == NULL || writableHeader == NULL)
750 {
751 // cannot write this way
752 }
753 else if (!CurrentOnly)
754 {
755 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
756 "DeviceUpdateMmcWriteCapability => Writes supported (defect management)\n"));
757 *Writable = TRUE;
758 return;
759 }
760 else if (defectHeader->Current && writableHeader->Current)
761 {
762 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
763 "DeviceUpdateMmcWriteCapability => Writes *allowed* (defect management)\n"));
764 *Writable = TRUE;
766 return;
767 }
768 }
769
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
773 {
776
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
781
782 header = MmcDataFindFeaturePage(Buffer,
785
786 if ((header != NULL) &&
787 (header->Current) &&
788 (header->AdditionalLength >= additionalLength))
789 {
791 *BlockingFactor = (feature->Blocking[0] << 8) | feature->Blocking[1];
792 }
793 }
794
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....
799 {
800 typedef struct {
801 FEATURE_NUMBER FeatureToFind; // the ones allowed
802 FEATURE_NUMBER ValidationSchema; // and their related schema
803 } FEATURE_TO_WRITE_SCHEMA_MAP;
804
805 static FEATURE_TO_WRITE_SCHEMA_MAP const FeaturesToAllowWritesWith[] = {
810 };
811
812 ULONG count;
813 for (count = 0; count < RTL_NUMBER_OF(FeaturesToAllowWritesWith); count++)
814 {
815 PFEATURE_HEADER header = MmcDataFindFeaturePage(Buffer,
817 FeaturesToAllowWritesWith[count].FeatureToFind);
818 if (header == NULL)
819 {
820 // cannot write using this method
821 }
822 else if (!CurrentOnly)
823 {
824 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
825 "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
826 FeaturesToAllowWritesWith[count].FeatureToFind
827 ));
828 *Writable = TRUE;
829 return;
830 }
831 else if (header->Current)
832 {
833 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
834 "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
835 FeaturesToAllowWritesWith[count].FeatureToFind
836 ));
837 *Writable = TRUE;
838 *ValidationSchema = FeaturesToAllowWritesWith[count].ValidationSchema;
839 return;
840 }
841 } // end count loop
842 }
843
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.
847 {
850 header = MmcDataFindFeaturePage(Buffer,
853
854 if (header == NULL || (header->Header.AdditionalLength < additionalLength) || (!header->Write))
855 {
856 // cannot write this way
857 }
858 else if (!CurrentOnly)
859 {
860 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
861 "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
863 ));
864 *Writable = TRUE;
865 return;
866 }
867 else if (header->Header.Current)
868 {
869 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
870 "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
872 ));
873 *Writable = TRUE;
875 return;
876 }
877 }
878
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.
882 {
885 header = MmcDataFindFeaturePage(Buffer,
888
889 if (header == NULL || (header->Header.AdditionalLength < additionalLength) || (!header->Write))
890 {
891 // cannot write this way
892 }
893 else if (!CurrentOnly)
894 {
895 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
896 "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
898 ));
899 *Writable = TRUE;
900 return;
901 }
902 else if (header->Header.Current)
903 {
904 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
905 "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
907 ));
908 *Writable = TRUE;
910 return;
911 }
912 }
913
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
919 // profile
920 // (c) FeatureRigidRestrictedOverwrite as current for
921 // DVD-RW profile
922 //
923 // Thus, use the profiles also.
924 {
926 header = MmcDataFindFeaturePage(Buffer,
929
930 if (header != NULL && header->Current)
931 {
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....
936 {
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.
940 typedef struct {
941 FEATURE_PROFILE_TYPE ProfileToFind; // the ones allowed
942 FEATURE_NUMBER ValidationSchema; // and their related schema
943 } PROFILE_TO_WRITE_SCHEMA_MAP;
944
945 static PROFILE_TO_WRITE_SCHEMA_MAP const ProfilesToAllowWritesWith[] = {
950 };
951
952 ULONG count;
953 for (count = 0; count < RTL_NUMBER_OF(ProfilesToAllowWritesWith); count++)
954 {
955 BOOLEAN exists = FALSE;
956 MmcDataFindProfileInProfiles((PFEATURE_DATA_PROFILE_LIST)header,
957 ProfilesToAllowWritesWith[count].ProfileToFind,
959 &exists);
960 if (exists)
961 {
962 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
963 "DeviceUpdateMmcWriteCapability => Writes %s (profile %04x)\n",
964 (CurrentOnly ? "*allowed*" : "supported"),
966 ));
967
968 *Writable = TRUE;
969 *ValidationSchema = ProfilesToAllowWritesWith[count].ValidationSchema;
970 return;
971 }
972 } // end count loop
973 } // end if (bufferEnd >= headerEnd)
974
975 } // end if (header != NULL && header->Current)
976 }
977
978 // nothing matched to say it's writable.....
979 return;
980}
981
983PVOID
984MmcDataFindFeaturePage(
986 PGET_CONFIGURATION_HEADER FeatureBuffer,
987 ULONG const Length,
989 )
990/*++
991
992Routine Description:
993
994 search the specific feature from feature list buffer
995
996Arguments:
997
998 FeatureBuffer - buffer of feature list
999 Length - size of the buffer
1000 Feature - feature wanted to find
1001
1002Return Value:
1003
1004 PVOID - if found, pointer of starting address of the specific feature.
1005 otherwise, NULL.
1006
1007--*/
1008{
1009 PUCHAR buffer;
1010 PUCHAR limit;
1011 ULONG validLength;
1012
1013 PAGED_CODE();
1014
1015 if (Length < sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)) {
1016 return NULL;
1017 }
1018
1019 // Calculate the length of valid data available in the
1020 // capabilities buffer from the DataLength field
1021 REVERSE_BYTES(&validLength, FeatureBuffer->DataLength);
1023
1024 // set limit to point to first illegal address
1025 limit = (PUCHAR)FeatureBuffer;
1026 limit += min(Length, validLength);
1027
1028 // set buffer to point to first page
1029 buffer = FeatureBuffer->Data;
1030
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)
1035 {
1037 FEATURE_NUMBER thisFeature;
1038
1039 thisFeature = (header->FeatureCode[0] << 8) |
1040 (header->FeatureCode[1]);
1041
1042 if (thisFeature == Feature)
1043 {
1044 PUCHAR temp;
1045
1046 // if don't have enough memory to safely access all the feature
1047 // information, return NULL
1048 temp = buffer;
1049 temp += sizeof(FEATURE_HEADER);
1050 temp += header->AdditionalLength;
1051
1052 if (temp > limit)
1053 {
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));
1062 return NULL;
1063 }
1064 else
1065 {
1066 return buffer;
1067 }
1068 }
1069
1070 if ((header->AdditionalLength % 4) &&
1071 !(Feature >= 0xff00 && Feature <= 0xffff))
1072 {
1073 return NULL;
1074 }
1075
1076 buffer += sizeof(FEATURE_HEADER);
1077 buffer += header->AdditionalLength;
1078
1079 }
1080 return NULL;
1081}
1082
1084VOID
1085MmcDataFindProfileInProfiles(
1086 _In_ FEATURE_DATA_PROFILE_LIST const* ProfileHeader,
1088 _In_ BOOLEAN const CurrentOnly,
1090 )
1091/*++
1092
1093Routine Description:
1094
1095 search the specific feature from feature list buffer
1096
1097Arguments:
1098
1099 ProfileHeader - buffer of profile list
1100 ProfileToFind - profile to be found
1101 CurrentOnly -
1102
1103Return Value:
1104
1105 Found - found or not
1106
1107--*/
1108{
1110 ULONG numberOfProfiles;
1111 ULONG i;
1112
1113 PAGED_CODE();
1114
1115 // initialize output
1116 *Found = FALSE;
1117
1118 // sanity check
1119 if (ProfileHeader->Header.AdditionalLength % 2 != 0)
1120 {
1121 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
1122 "Profile total length %x is not integral multiple of 4\n",
1123 ProfileHeader->Header.AdditionalLength));
1125 return;
1126 }
1127
1128 // calculate number of profiles
1129 numberOfProfiles = ProfileHeader->Header.AdditionalLength / 4;
1130 profile = ProfileHeader->Profiles; // zero-sized array
1131
1132 // loop through profiles
1133 for (i = 0; i < numberOfProfiles; i++)
1134 {
1135 FEATURE_PROFILE_TYPE currentProfile;
1136
1137 currentProfile = (profile->ProfileNumber[0] << 8) |
1138 (profile->ProfileNumber[1] & 0xff);
1139
1140 if (currentProfile == ProfileToFind)
1141 {
1142 if (profile->Current || (!CurrentOnly))
1143 {
1144 *Found = TRUE;
1145 }
1146 }
1147
1148 profile++;
1149 }
1150
1151 return;
1152}
1153
1158 FEATURE_PROFILE_TYPE const Profile
1159 )
1160/*++
1161
1162Routine Description:
1163
1164 determine the retry time based on profile
1165
1166Arguments:
1167
1168 Profile -
1169
1170Return Value:
1171
1172 LONGLONG - retry time
1173
1174--*/
1175{
1176 LONGLONG result = -1; // this means we have no idea
1177
1178 PAGED_CODE();
1179
1180 switch (Profile)
1181 {
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
1197 default:
1198 {
1199 NOTHING; // no default
1200 break;
1201 }
1202
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
1209 {
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
1215 break;
1216 }
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,
1225 {
1227 break;
1228 }
1229 case ProfileDvdDashRDualLayer: // = 0x0015,
1230 case ProfileDvdPlusRWDualLayer: // = 0x002A,
1231 case ProfileDvdPlusRDualLayer: // = 0x002B,
1232 {
1234 break;
1235 }
1236
1237 case ProfileBDRom: // = 0x0040,
1238 case ProfileBDRSequentialWritable: // = 0x0041, // BD-R 'SRM'
1239 case ProfileBDRRandomWritable: // = 0x0042, // BD-R 'RRM'
1240 case ProfileBDRewritable: // = 0x0043,
1241 {
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....
1251 break;
1252 }
1253
1254 case ProfileHDDVDRom: // = 0x0050,
1255 case ProfileHDDVDRecordable: // = 0x0051,
1256 case ProfileHDDVDRam: // = 0x0052,
1257 {
1258 // All HDDVD drives support GET_PERFORMANCE
1259 // so this guess is fine at 1x....
1261 break;
1262 }
1263
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)
1269
1270 } // end switch(Profile)
1271
1272 return result;
1273}
1274
1279 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1280 )
1281/*++
1282
1283Routine Description:
1284
1285 determine the retry time based on mode sense data
1286
1287Arguments:
1288
1289 DeviceExtension - device context
1290
1291Return Value:
1292
1293 LONGLONG - retry time
1294
1295--*/
1296{
1298 ULONG transferSize = min(0xFFF0, DeviceExtension->ScratchContext.ScratchBufferSize);
1299 CDB cdb;
1300 LONGLONG result = -1;
1301
1302 PAGED_CODE();
1303
1304 ScratchBuffer_BeginUse(DeviceExtension);
1305
1306 RtlZeroMemory(&cdb, sizeof(CDB));
1307 // Set up the 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);
1313
1314 status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, TRUE, &cdb, 10);
1315
1316 // analyze the data on success....
1317 if (NT_SUCCESS(status))
1318 {
1319 MODE_PARAMETER_HEADER10 const* header = DeviceExtension->ScratchContext.ScratchBuffer;
1321 ULONG dataLength = (header->ModeDataLength[0] << (8*1)) |
1322 (header->ModeDataLength[1] << (8*0)) ;
1323
1324 // no possible overflow
1325 if (dataLength != 0)
1326 {
1328 }
1329
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))
1334 {
1335 dataLength = 0;
1336 }
1337 else if (dataLength > DeviceExtension->ScratchContext.ScratchBufferSize)
1338 {
1339 dataLength = 0;
1340 }
1341 else if ((header->BlockDescriptorLength[1] == 0) &&
1342 (header->BlockDescriptorLength[0] == 0))
1343 {
1345 page = (CDVD_CAPABILITIES_PAGE const *)(header + 1);
1346 }
1347 else if ((header->BlockDescriptorLength[1] == 0) &&
1348 (header->BlockDescriptorLength[0] == sizeof(MODE_PARAMETER_BLOCK)))
1349 {
1352 page = (CDVD_CAPABILITIES_PAGE const *)
1353 ( ((PUCHAR)header) +
1354 sizeof(MODE_PARAMETER_HEADER10) +
1355 sizeof(MODE_PARAMETER_BLOCK)
1356 );
1357 }
1358
1359 // Change dataLength from the size available per the header to
1360 // the size available per the page itself.
1361 if ((page != NULL) &&
1363 )
1364 {
1365 dataLength = min(dataLength, ((ULONG)(page->PageLength) + 2));
1366 }
1367
1368 // Ignore the page if the fastest write speed field isn't available.
1369 if ((page != NULL) &&
1371 )
1372 {
1373 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
1374 "ModePage 2Ah was requested, but drive reported "
1375 "only %x bytes (%x needed). Ignoring.\n",
1376 dataLength,
1378 ));
1379 page = NULL;
1380 }
1381
1382 // Verify the page we requested is the one the drive actually provided
1383 if ((page != NULL) && (page->PageCode != MODE_PAGE_CAPABILITIES))
1384 {
1385 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
1386 "ModePage 2Ah was requested, but drive reported "
1387 "page %x\n",
1388 page->PageCode
1389 ));
1390 page = NULL;
1391 }
1392
1393 // If _everything_ succeeded, then use the speed value in the page!
1394 if (page != NULL)
1395 {
1396 ULONG temp =
1397 (page->WriteSpeedMaximum[0] << (8*1)) |
1398 (page->WriteSpeedMaximum[1] << (8*0)) ;
1399 // stored as 1,000 byte increments...
1400 temp *= 1000;
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
1404 temp /= 2048;
1405 // currently: sectors/sec
1406 // ignore too-small or zero values
1407 if (temp != 0)
1408 {
1409 result = ConvertSectorsPerSecondTo100nsUnitsFor64kWrite(temp);
1410 }
1411 }
1412 }
1413
1414 ScratchBuffer_EndUse(DeviceExtension);
1415
1416 return result;
1417}
1418
1419
1424 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
1425 _In_ BOOLEAN UseLegacyNominalPerformance
1426 )
1427/*++
1428
1429Routine Description:
1430
1431 determine the retry time based on get performance data
1432
1433Arguments:
1434
1435 DeviceExtension - device context
1436 UseLegacyNominalPerformance -
1437
1438Return Value:
1439
1440 LONGLONG - retry time
1441
1442--*/
1443{
1444 typedef struct _GET_PERFORMANCE_HEADER {
1445 UCHAR TotalDataLength[4]; // not including this field
1446 UCHAR Except : 1;
1447 UCHAR Write : 1;
1448 UCHAR Reserved0 : 6;
1449 UCHAR Reserved1[3];
1450 } GET_PERFORMANCE_HEADER, *PGET_PERFORMANCE_HEADER;
1451 C_ASSERT( sizeof(GET_PERFORMANCE_HEADER) == 8);
1452
1453 typedef struct _GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR {
1454 UCHAR StartLba[4];
1455 UCHAR StartPerformance[4];
1456 UCHAR EndLba[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);
1460
1461
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;
1468 UCHAR Reserved2[3];
1469
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);
1475
1477
1479 LONGLONG result = -1;
1480
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));
1484
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));
1488 CDB cdb;
1489
1490 PAGED_CODE();
1491
1492 ScratchBuffer_BeginUse(DeviceExtension);
1493
1494 RtlZeroMemory(&cdb, sizeof(CDB));
1495 // Set up the CDB
1496 if (UseLegacyNominalPerformance)
1497 {
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
1504 }
1505 else
1506 {
1507 cdb.GET_PERFORMANCE.OperationCode = SCSIOP_GET_PERFORMANCE;
1508 cdb.GET_PERFORMANCE.MaximumNumberOfDescriptors[1] = (UCHAR)maxDescriptors;
1509 cdb.GET_PERFORMANCE.Type = 3; // write speed
1510 }
1511
1512 status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, NULL, transferSize, TRUE, &cdb, 12, CDROM_GET_PERFORMANCE_TIMEOUT);
1513
1514 // determine how many valid descriptors there actually are
1515 if (NT_SUCCESS(status))
1516 {
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)) ;
1522
1523 // adjust data size for header
1524 if (temp1 + (ULONG)RTL_SIZEOF_THROUGH_FIELD(GET_PERFORMANCE_HEADER, TotalDataLength) < temp1)
1525 {
1526 temp1 = 0;
1527 }
1528 else if (temp1 != 0)
1529 {
1530 temp1 += RTL_SIZEOF_THROUGH_FIELD(GET_PERFORMANCE_HEADER, TotalDataLength);
1531 }
1532
1533 if (temp1 == 0)
1534 {
1535 // no data returned
1536 }
1537 else if (temp1 <= sizeof(GET_PERFORMANCE_HEADER))
1538 {
1539 // only the header returned, no descriptors
1540 }
1541 else if (UseLegacyNominalPerformance &&
1542 ((header->Except != 0) || (header->Write == 0))
1543 )
1544 {
1545 // bad data being returned -- ignore it
1546 }
1547 else if (!UseLegacyNominalPerformance &&
1548 ((header->Except != 0) || (header->Write != 0))
1549 )
1550 {
1551 // returning Performance (Type 0) data, not requested Write Speed (Type 3) data
1552 }
1553 else if ( (temp1 - sizeof(GET_PERFORMANCE_HEADER)) % sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR) != 0)
1554 {
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));
1557
1558 // not returning valid data....
1559 }
1560 else // save how many are usable
1561 {
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));
1564
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);
1569 }
1570 }
1571
1572 // The drive likely supports this command.
1573 // Verify the data makes sense.
1574 if (NT_SUCCESS(status))
1575 {
1576 ULONG i;
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
1579
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.
1586
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)
1590 );
1591 C_ASSERT( RTL_FIELD_SIZE(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR, WriteSpeedKilobytesPerSecond) ==
1592 RTL_FIELD_SIZE(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR, EndPerformance)
1593 );
1594
1595 // loop through them all, and find the fastest listed write speed
1596 for (i = 0; NT_SUCCESS(status) && (i <validDescriptors); descriptor++, i++)
1597 {
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)) ;
1603
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
1610
1611 if (sectorsPerSecond <= 0)
1612 {
1613 break; // out of the loop -- no longer valid data (very defensive programming)
1614 }
1615
1616 // we have at least one valid result, so prevent returning -1 as our result
1618
1619 // take the fastest speed (smallest wait time) we've found thus far
1620 result = min(result, ConvertSectorsPerSecondTo100nsUnitsFor64kWrite(sectorsPerSecond));
1621 }
1622 }
1623
1624 ScratchBuffer_EndUse(DeviceExtension);
1625
1626 return result;
1627}
1628
1629#pragma warning(pop) // un-sets any local warning changes
#define PAGED_CODE()
unsigned char BOOLEAN
#define RTL_NUMBER_OF(x)
Definition: RtlRegistry.c:12
LONG NTSTATUS
Definition: precomp.h:26
@ Reserved2
Definition: bcd.h:202
@ Reserved1
Definition: bcd.h:201
return Found
Definition: dirsup.c:1270
#define WRITE_RETRY_DELAY_CD_4x
Definition: cdrom.h:116
#define WRITE_RETRY_DELAY_DVD_1x
Definition: cdrom.h:118
#define CDROM_GET_CONFIGURATION_TIMEOUT
Definition: cdrom.h:126
#define CdromMmcUpdateRequired
Definition: cdrom.h:242
#define CDROM_HACK_BAD_GET_CONFIG_SUPPORT
Definition: cdrom.h:96
_In_ ULONG _In_opt_ WDFREQUEST _In_opt_ PVOID _In_ size_t _In_ PVOID _In_ size_t _Out_ size_t * DataLength
Definition: cdrom.h:1444
_In_ ULONG const _In_ FEATURE_NUMBER const Feature
Definition: cdrom.h:1077
#define TEST_FLAG(Flags, Bit)
Definition: cdrom.h:1495
#define CdromMmcUpdateStarted
Definition: cdrom.h:243
#define CDROM_GET_PERFORMANCE_TIMEOUT
Definition: cdrom.h:129
#define CDROM_TAG_FEATURE
Definition: cdrom.h:733
#define CdromMmcUpdateComplete
Definition: cdrom.h:241
#define WRITE_RETRY_DELAY_HDDVD_1x
Definition: cdrom.h:121
#define MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS
Definition: cdromp.h:50
#define SCSIOP_MODE_SENSE10
Definition: cdrw_hw.h:946
#define SCSIOP_GET_CONFIGURATION
Definition: cdrw_hw.h:930
#define SCSIOP_GET_PERFORMANCE
Definition: cdrw_hw.h:959
struct _MODE_PARAMETER_HEADER10 MODE_PARAMETER_HEADER10
union _CDB * PCDB
struct _MODE_PARAMETER_BLOCK MODE_PARAMETER_BLOCK
#define MODE_PAGE_CAPABILITIES
Definition: cdrw_hw.h:854
Definition: bufpool.h:45
#define BufferSize
Definition: mmc.h:75
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
static WCHAR available[MAX_STRING_RESOURCE_LEN]
Definition: object.c:2336
#define _IRQL_requires_max_(irql)
Definition: driverspecs.h:230
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
#define APC_LEVEL
Definition: env_spec_w32.h:695
#define ExFreePool(addr)
Definition: env_spec_w32.h:352
#define IoFreeMdl
Definition: fxmdl.h:89
#define IoAllocateMdl
Definition: fxmdl.h:88
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLsizeiptr size
Definition: glext.h:5919
GLuint buffer
Definition: glext.h:5915
GLint limit
Definition: glext.h:10326
GLuint64EXT * result
Definition: glext.h:11304
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define NOTHING
Definition: input_list.c:10
#define InterlockedCompareExchange
Definition: interlocked.h:104
#define C_ASSERT(e)
Definition: intsafe.h:73
#define RTL_FIELD_SIZE(type, field)
Definition: kdb_expr.c:86
#define profile
Definition: kernel32.h:12
ULONG const BOOLEAN const _Out_ PBOOLEAN Writable
Definition: mmc.h:77
_Outptr_result_bytebuffer_all_ BytesReturned PGET_CONFIGURATION_HEADER _Out_ PULONG FEATURE_NUMBER const ULONG const RequestedType
Definition: mmc.h:56
ULONG const BOOLEAN const _Out_ PBOOLEAN _Out_ PFEATURE_NUMBER ValidationSchema
Definition: mmc.h:78
MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS LONGLONG DeviceRetryTimeGuessBasedOnProfile(FEATURE_PROFILE_TYPE const Profile)
ULONG const BOOLEAN const CurrentOnly
Definition: mmc.h:76
MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS LONGLONG DeviceRetryTimeDetectionBasedOnGetPerformance(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ BOOLEAN UseLegacyNominalPerformance)
_Outptr_result_bytebuffer_all_ BytesReturned PGET_CONFIGURATION_HEADER _Out_ PULONG FEATURE_NUMBER const StartingFeature
Definition: mmc.h:54
_In_ ULONG const _Out_ PULONG ValidBytes
Definition: mmc.h:65
ULONG const BOOLEAN const _Out_ PBOOLEAN _Out_ PFEATURE_NUMBER _Out_ PULONG BlockingFactor
Definition: mmc.h:80
MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS LONGLONG DeviceRetryTimeDetectionBasedOnModePage2A(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension)
_In_ FEATURE_PROFILE_TYPE const ProfileToFind
Definition: mmc.h:95
INTERNETFEATURELIST feature
Definition: misc.c:1719
#define min(a, b)
Definition: monoChain.cc:55
#define _In_reads_bytes_(size)
Definition: ms_sal.h:321
#define _Out_writes_bytes_to_(size, count)
Definition: ms_sal.h:360
#define _Outptr_result_bytebuffer_all_(size)
Definition: ms_sal.h:477
#define _Ret_range_(lb, ub)
Definition: ms_sal.h:573
#define _Out_
Definition: ms_sal.h:345
#define _In_
Definition: ms_sal.h:308
#define _Analysis_assume_(expr)
Definition: ms_sal.h:2901
#define RTL_SIZEOF_THROUGH_FIELD(type, field)
Definition: ntbasedef.h:672
#define MAXLONGLONG
enum _FEATURE_NUMBER * PFEATURE_NUMBER
struct _FEATURE_DATA_RANDOM_READABLE * PFEATURE_DATA_RANDOM_READABLE
struct _FEATURE_HEADER * PFEATURE_HEADER
#define SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL
Definition: ntddmmc.h:15
#define SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT
Definition: ntddmmc.h:16
enum _FEATURE_NUMBER FEATURE_NUMBER
@ ProfileMOWriteOnce
Definition: ntddmmc.h:42
@ ProfileDvdRam
Definition: ntddmmc.h:49
@ ProfileBDRSequentialWritable
Definition: ntddmmc.h:62
@ ProfileInvalid
Definition: ntddmmc.h:38
@ ProfileDvdPlusR
Definition: ntddmmc.h:55
@ ProfileHDDVDRecordable
Definition: ntddmmc.h:66
@ ProfileCdRecordable
Definition: ntddmmc.h:45
@ ProfileAS_MO
Definition: ntddmmc.h:43
@ ProfileMOErasable
Definition: ntddmmc.h:41
@ ProfileNonStandard
Definition: ntddmmc.h:71
@ ProfileDvdRewritable
Definition: ntddmmc.h:50
@ ProfileDDCdrom
Definition: ntddmmc.h:56
@ ProfileDvdRecordable
Definition: ntddmmc.h:48
@ ProfileHDDVDRam
Definition: ntddmmc.h:67
@ ProfileDvdRWSequential
Definition: ntddmmc.h:51
@ ProfileBDRewritable
Definition: ntddmmc.h:64
@ ProfileDvdRom
Definition: ntddmmc.h:47
@ ProfileBDRRandomWritable
Definition: ntddmmc.h:63
@ ProfileDvdPlusRW
Definition: ntddmmc.h:54
@ ProfileDvdPlusRWDualLayer
Definition: ntddmmc.h:59
@ ProfileCdRewritable
Definition: ntddmmc.h:46
@ ProfileHDDVDRom
Definition: ntddmmc.h:65
@ ProfileNonRemovableDisk
Definition: ntddmmc.h:39
@ ProfileDDCdRecordable
Definition: ntddmmc.h:57
@ ProfileCdrom
Definition: ntddmmc.h:44
@ ProfileDvdDashRLayerJump
Definition: ntddmmc.h:53
@ ProfileDvdPlusRDualLayer
Definition: ntddmmc.h:60
@ ProfileDvdDashRDualLayer
Definition: ntddmmc.h:52
@ ProfileRemovableDisk
Definition: ntddmmc.h:40
@ ProfileDDCdRewritable
Definition: ntddmmc.h:58
@ ProfileBDRom
Definition: ntddmmc.h:61
enum _FEATURE_PROFILE_TYPE FEATURE_PROFILE_TYPE
struct _FEATURE_DATA_REAL_TIME_STREAMING * PFEATURE_DATA_REAL_TIME_STREAMING
struct _GET_CONFIGURATION_HEADER * PGET_CONFIGURATION_HEADER
@ FeatureRandomReadable
Definition: ntddmmc.h:80
@ FeatureDvdPlusR
Definition: ntddmmc.h:95
@ FeatureDvdPlusRDualLayer
Definition: ntddmmc.h:107
@ FeatureRestrictedOverwrite
Definition: ntddmmc.h:90
@ FeatureIncrementalStreamingWritable
Definition: ntddmmc.h:85
@ FeatureRandomWritable
Definition: ntddmmc.h:84
@ FeatureDefectManagement
Definition: ntddmmc.h:88
@ FeatureRealTimeStreaming
Definition: ntddmmc.h:121
@ FeatureProfileList
Definition: ntddmmc.h:75
@ FeatureDvdCSS
Definition: ntddmmc.h:120
@ FeatureRigidRestrictedOverwrite
Definition: ntddmmc.h:96
struct _FEATURE_HEADER FEATURE_HEADER
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
PIRP NTAPI IoAllocateIrp(IN CCHAR StackSize, IN BOOLEAN ChargeQuota)
Definition: irp.c:615
VOID NTAPI IoFreeIrp(IN PIRP Irp)
Definition: irp.c:1666
#define STATUS_INTERNAL_ERROR
Definition: ntstatus.h:465
long LONG
Definition: pedump.c:60
static calc_node_t temp
Definition: rpn_ieee.c:38
FORCEINLINE VOID ScratchBuffer_EndUse(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension)
Definition: scratch.h:104
#define ScratchBuffer_BeginUse(context)
Definition: scratch.h:87
descriptor
Definition: scsi.h:3951
UCHAR additionalLength
Definition: scsi.h:3943
#define REVERSE_BYTES(Destination, Source)
Definition: scsi.h:3465
ULONG dataLength
Definition: scsi.h:3751
#define STATUS_SUCCESS
Definition: shellext.h:65
#define STATUS_BUFFER_TOO_SMALL
Definition: shellext.h:69
#define STATUS_BUFFER_OVERFLOW
Definition: shellext.h:66
#define TRACE_LEVEL_WARNING
Definition: storswtr.h:28
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
#define TRACE_LEVEL_INFORMATION
Definition: storswtr.h:29
ULONG_PTR HackFlags
Definition: cdrom.h:344
CDROM_MMC_EXTENSION Mmc
Definition: cdrom.h:341
LONGLONG ReadWriteRetryDelay100nsUnits
Definition: cdrom.h:384
PDEVICE_OBJECT DeviceObject
Definition: cdrom.h:493
CDROM_DATA DeviceAdditionalData
Definition: cdrom.h:598
BOOLEAN WriteAllowed
Definition: cdrom.h:250
FEATURE_NUMBER ValidationSchema
Definition: cdrom.h:260
BOOLEAN StreamingReadSupported
Definition: cdrom.h:253
BOOLEAN IsCssDvd
Definition: cdrom.h:252
ULONG CapabilitiesBufferSize
Definition: cdrom.h:269
WDFREQUEST CapabilitiesRequest
Definition: cdrom.h:265
PMDL CapabilitiesMdl
Definition: cdrom.h:270
BOOLEAN StreamingWriteSupported
Definition: cdrom.h:254
PIRP CapabilitiesIrp
Definition: cdrom.h:264
UCHAR Current
Definition: ntddmmc.h:30
ULONG TimeOutValue
Definition: srb.h:262
UCHAR CdbLength
Definition: srb.h:258
UCHAR Cdb[16]
Definition: srb.h:279
ULONG DataTransferLength
Definition: srb.h:261
Definition: module.h:576
Definition: ps.c:97
uint32_t * PULONG
Definition: typedefs.h:59
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
unsigned char * PBOOLEAN
Definition: typedefs.h:53
int64_t LONGLONG
Definition: typedefs.h:68
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
int32_t * PLONG
Definition: typedefs.h:58
unsigned char * PUCHAR
Definition: typedefs.h:53
uint32_t ULONG
Definition: typedefs.h:59
#define STATUS_INVALID_DEVICE_REQUEST
Definition: udferr_usr.h:138
#define STATUS_DATA_OVERRUN
Definition: udferr_usr.h:152
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
Definition: cdrw_hw.h:28
struct _CDB::_MODE_SENSE10 MODE_SENSE10
struct _CDB::_GET_CONFIGURATION GET_CONFIGURATION
struct _CDB::_GET_PERFORMANCE GET_PERFORMANCE
static BOOL Write(PBYTE Address, PBYTE Data, SIZE_T Size)
Definition: vmhorizon.c:15
_Must_inspect_result_ _In_ WDFDEVICE Device
Definition: wdfchildlist.h:474
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_ ULONG _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PWDF_REQUEST_SEND_OPTIONS _Out_opt_ PULONG_PTR BytesReturned
Definition: wdfiotarget.h:1052
_In_ WDFMEMORY _Out_opt_ size_t * BufferSize
Definition: wdfmemory.h:254
#define WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(_attributes, _contexttype)
Definition: wdfobject.h:170
#define NT_ASSERT
Definition: rtlfuncs.h:3310
unsigned char UCHAR
Definition: xmlstorage.h:181