ReactOS  0.4.15-dev-3316-g067ca88
mmc.c
Go to the documentation of this file.
1 /*--
2 
3 Copyright (C) Microsoft Corporation. All rights reserved.
4 
5 Module Name:
6 
7  mmc.c
8 
9 Abstract:
10 
11  Include all funtions relate to MMC
12 
13 Environment:
14 
15  kernel mode only
16 
17 Notes:
18 
19 
20 Revision 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 
57 VOID
58 DeviceDeallocateMmcResources(
59  _In_ WDFDEVICE Device
60  )
61 /*++
62 
63 Routine Description:
64 
65  release MMC resources
66 
67 Arguments:
68 
69  Device - device object
70 
71 Return 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 
112 NTSTATUS
113 DeviceAllocateMmcResources(
114  _In_ WDFDEVICE Device
115  )
116 /*++
117 
118 Routine Description:
119 
120  allocate all MMC resources needed
121 
122 Arguments:
123 
124  Device - device object
125 
126 Return 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 
200 NTSTATUS
201 DeviceUpdateMmcCapabilities(
202  _In_ WDFDEVICE Device
203  )
204 /*++
205 
206 Routine Description:
207 
208  issue get congiguration command ans save result in device extension
209 
210 Arguments:
211 
212  Device - device object
213 
214 Return 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;
243  mmcData->StreamingWriteSupported = 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,
310  FeatureDvdCSS);
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....
368  final = WRITE_RETRY_DELAY_CD_4x;
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 
393 NTSTATUS
394 DeviceGetConfigurationWithAlloc(
395  _In_ WDFDEVICE Device,
397  PGET_CONFIGURATION_HEADER* Buffer, // this routine allocates this memory
400  ULONG const RequestedType
401  )
402 /*++
403 
404 Routine Description:
405 
406  This function will allocates configuration buffer and set the size.
407 
408 Arguments:
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 
416 Return Value:
417 
418  NTSTATUS
419 
420 NOTE: 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,
444  RequestedType);
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,
490  RequestedType);
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 
543 NTSTATUS
544 DeviceGetConfiguration(
545  _In_ WDFDEVICE Device,
548  _In_ ULONG const BufferSize,
551  _In_ ULONG const RequestedType
552  )
553 /*++
554 
555 Routine Description:
556 
557  This function is used to get configuration data.
558 
559 Arguments:
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 
568 Return Value:
569 
570  NTSTATUS
571 
572 NOTE: 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);
580  SCSI_REQUEST_BLOCK srb;
581  PCDB cdb = (PCDB)srb.Cdb;
582 
583  PAGED_CODE();
584 
586 
587  // when system is low resources we can receive empty buffer
588  if (Buffer == NULL || BufferSize < sizeof(GET_CONFIGURATION_HEADER))
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,
615  ValidBytes,
617  RequestedType);
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,
635  BufferSize,
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 
684 VOID
685 DeviceUpdateMmcWriteCapability(
688  ULONG const BufferSize,
689  BOOLEAN const CurrentOnly, // TRUE == can drive write now, FALSE == can drive ever write
693  )
694 /*++
695 
696 Routine Description:
697 
698  This function will allocates configuration buffer and set the size.
699 
700 Arguments:
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 
709 Return Value:
710 
711  NTSTATUS
712 
713 NOTE: 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,
743  BufferSize,
745  writableHeader = MmcDataFindFeaturePage(Buffer,
746  BufferSize,
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,
783  BufferSize,
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,
816  BufferSize,
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,
851  BufferSize,
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,
886  BufferSize,
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,
927  BufferSize,
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,
958  CurrentOnly,
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 
983 PVOID
984 MmcDataFindFeaturePage(
986  PGET_CONFIGURATION_HEADER FeatureBuffer,
987  ULONG const Length,
988  FEATURE_NUMBER const Feature
989  )
990 /*++
991 
992 Routine Description:
993 
994  search the specific feature from feature list buffer
995 
996 Arguments:
997 
998  FeatureBuffer - buffer of feature list
999  Length - size of the buffer
1000  Feature - feature wanted to find
1001 
1002 Return 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 
1084 VOID
1085 MmcDataFindProfileInProfiles(
1086  _In_ FEATURE_DATA_PROFILE_LIST const* ProfileHeader,
1088  _In_ BOOLEAN const CurrentOnly,
1090  )
1091 /*++
1092 
1093 Routine Description:
1094 
1095  search the specific feature from feature list buffer
1096 
1097 Arguments:
1098 
1099  ProfileHeader - buffer of profile list
1100  ProfileToFind - profile to be found
1101  CurrentOnly -
1102 
1103 Return 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));
1124  NT_ASSERT(FALSE);
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 
1156 LONGLONG
1158  FEATURE_PROFILE_TYPE const Profile
1159  )
1160 /*++
1161 
1162 Routine Description:
1163 
1164  determine the retry time based on profile
1165 
1166 Arguments:
1167 
1168  Profile -
1169 
1170 Return 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 
1277 LONGLONG
1279  _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1280  )
1281 /*++
1282 
1283 Routine Description:
1284 
1285  determine the retry time based on mode sense data
1286 
1287 Arguments:
1288 
1289  DeviceExtension - device context
1290 
1291 Return Value:
1292 
1293  LONGLONG - retry time
1294 
1295 --*/
1296 {
1297  NTSTATUS status;
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;
1320  CDVD_CAPABILITIES_PAGE const* page = NULL;
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 
1422 LONGLONG
1424  _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
1425  _In_ BOOLEAN UseLegacyNominalPerformance
1426  )
1427 /*++
1428 
1429 Routine Description:
1430 
1431  determine the retry time based on get performance data
1432 
1433 Arguments:
1434 
1435  DeviceExtension - device context
1436  UseLegacyNominalPerformance -
1437 
1438 Return 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 
1478  NTSTATUS status;
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
INTERNETFEATURELIST feature
Definition: misc.c:1719
struct _FEATURE_DATA_REAL_TIME_STREAMING * PFEATURE_DATA_REAL_TIME_STREAMING
enum _FEATURE_NUMBER * PFEATURE_NUMBER
#define RTL_FIELD_SIZE(type, field)
Definition: kdb_expr.c:84
BOOLEAN StreamingReadSupported
Definition: cdrom.h:253
PMDL CapabilitiesMdl
Definition: cdrom.h:270
FORCEINLINE VOID ScratchBuffer_EndUse(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension)
Definition: scratch.h:104
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:101
UCHAR Cdb[16]
Definition: srb.h:271
GLuint64EXT * result
Definition: glext.h:11304
ULONG const BOOLEAN const CurrentOnly
Definition: mmc.h:75
_IRQL_requires_max_(APC_LEVEL)
Definition: mmc.c:56
#define STATUS_DATA_OVERRUN
Definition: udferr_usr.h:152
BOOLEAN WriteAllowed
Definition: cdrom.h:250
#define _Out_
Definition: ms_sal.h:345
#define TRUE
Definition: types.h:120
#define CDROM_GET_CONFIGURATION_TIMEOUT
Definition: cdrom.h:126
GLuint GLuint GLsizei count
Definition: gl.h:1545
#define CdromMmcUpdateStarted
Definition: cdrom.h:243
CDROM_DATA DeviceAdditionalData
Definition: cdrom.h:598
unsigned char * PUCHAR
Definition: retypes.h:3
struct _CDB::_GET_CONFIGURATION GET_CONFIGURATION
#define CDROM_GET_PERFORMANCE_TIMEOUT
Definition: cdrom.h:129
MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS LONGLONG DeviceRetryTimeDetectionBasedOnModePage2A(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension)
ULONG DataTransferLength
Definition: srb.h:253
struct _MODE_PARAMETER_BLOCK MODE_PARAMETER_BLOCK
Definition: cdrw_hw.h:28
LONG NTSTATUS
Definition: precomp.h:26
struct _FEATURE_HEADER * PFEATURE_HEADER
#define WRITE_RETRY_DELAY_DVD_1x
Definition: cdrom.h:118
#define REVERSE_BYTES(Destination, Source)
Definition: scsi.h:3465
_Outptr_result_bytebuffer_all_ BytesReturned PGET_CONFIGURATION_HEADER _Out_ PULONG FEATURE_NUMBER const StartingFeature
Definition: mmc.h:51
#define STATUS_INVALID_DEVICE_REQUEST
Definition: udferr_usr.h:138
#define InterlockedCompareExchange
Definition: interlocked.h:104
MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS LONGLONG DeviceRetryTimeDetectionBasedOnGetPerformance(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ BOOLEAN UseLegacyNominalPerformance)
_In_ ULONG const _In_ FEATURE_NUMBER const Feature
Definition: cdrom.h:1075
struct _CDB::_MODE_SENSE10 MODE_SENSE10
UCHAR CdbLength
Definition: srb.h:250
GLuint buffer
Definition: glext.h:5915
WDFREQUEST CapabilitiesRequest
Definition: cdrom.h:265
#define MAXLONGLONG
static BOOL Write(PBYTE Address, PBYTE Data, SIZE_T Size)
Definition: vmhorizon.c:15
#define TRACE_LEVEL_INFORMATION
Definition: storswtr.h:29
ULONG TimeOutValue
Definition: srb.h:254
enum _FEATURE_PROFILE_TYPE FEATURE_PROFILE_TYPE
MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS LONGLONG DeviceRetryTimeGuessBasedOnProfile(FEATURE_PROFILE_TYPE const Profile)
#define WRITE_RETRY_DELAY_CD_4x
Definition: cdrom.h:116
ULONG const BOOLEAN const _Out_ PBOOLEAN _Out_ PFEATURE_NUMBER _Out_ PULONG BlockingFactor
Definition: mmc.h:75
_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:1039
#define STATUS_BUFFER_TOO_SMALL
Definition: shellext.h:69
#define WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(_attributes, _contexttype)
Definition: wdfobject.h:170
GLint limit
Definition: glext.h:10326
#define SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT
Definition: ntddmmc.h:16
#define STATUS_INTERNAL_ERROR
Definition: ntstatus.h:465
FEATURE_NUMBER ValidationSchema
Definition: cdrom.h:260
#define _Analysis_assume_(expr)
Definition: ms_sal.h:2901
#define ScratchBuffer_BeginUse(context)
Definition: scratch.h:87
#define FALSE
Definition: types.h:117
ULONG_PTR HackFlags
Definition: cdrom.h:344
#define _Out_writes_bytes_to_(size, count)
Definition: ms_sal.h:360
long LONG
Definition: pedump.c:60
#define CDROM_HACK_BAD_GET_CONFIG_SUPPORT
Definition: cdrom.h:96
descriptor
Definition: scsi.h:3951
unsigned char BOOLEAN
#define SCSIOP_MODE_SENSE10
Definition: cdrw_hw.h:946
static WCHAR available[MAX_STRING_RESOURCE_LEN]
Definition: object.c:2336
union _CDB * PCDB
#define _In_
Definition: ms_sal.h:308
Definition: module.h:566
struct _MODE_PARAMETER_HEADER10 MODE_PARAMETER_HEADER10
_In_ ULONG _In_opt_ WDFREQUEST _In_opt_ PVOID _In_ size_t _In_ PVOID _In_ size_t _Out_ size_t * DataLength
Definition: cdrom.h:1437
Definition: bufpool.h:45
#define C_ASSERT(e)
Definition: intsafe.h:73
return Found
Definition: dirsup.c:1270
Definition: bcd.h:202
#define TEST_FLAG(Flags, Bit)
Definition: cdrom.h:1495
UCHAR additionalLength
Definition: scsi.h:3943
WDF_DMA_PROFILE profile
#define MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS
Definition: cdromp.h:50
UCHAR Current
Definition: ntddmmc.h:30
int64_t LONGLONG
Definition: typedefs.h:68
#define _Ret_range_(lb, ub)
Definition: ms_sal.h:573
GLsizeiptr size
Definition: glext.h:5919
ULONG const BOOLEAN const _Out_ PBOOLEAN Writable
Definition: mmc.h:75
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
_In_ FEATURE_PROFILE_TYPE const ProfileToFind
Definition: mmc.h:95
ULONG dataLength
Definition: scsi.h:3751
#define RTL_SIZEOF_THROUGH_FIELD(type, field)
Definition: ntbasedef.h:672
VOID NTAPI IoFreeMdl(PMDL Mdl)
Definition: iomdl.c:146
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
#define MODE_PAGE_CAPABILITIES
Definition: cdrw_hw.h:854
struct _FEATURE_HEADER FEATURE_HEADER
unsigned char UCHAR
Definition: xmlstorage.h:181
char * PBOOLEAN
Definition: retypes.h:11
struct _CDB::_GET_PERFORMANCE GET_PERFORMANCE
#define SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL
Definition: ntddmmc.h:15
ULONG CapabilitiesBufferSize
Definition: cdrom.h:269
#define NOTHING
Definition: env_spec_w32.h:461
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
#define CdromMmcUpdateComplete
Definition: cdrom.h:241
_Must_inspect_result_ _In_ WDFDEVICE Device
Definition: wdfchildlist.h:474
enum _FEATURE_NUMBER FEATURE_NUMBER
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
#define TRACE_LEVEL_WARNING
Definition: storswtr.h:28
PMDL NTAPI IoAllocateMdl(IN PVOID VirtualAddress, IN ULONG Length, IN BOOLEAN SecondaryBuffer, IN BOOLEAN ChargeQuota, IN PIRP Irp)
Definition: iomdl.c:22
_Outptr_result_bytebuffer_all_ BytesReturned PGET_CONFIGURATION_HEADER _Out_ PULONG FEATURE_NUMBER const ULONG const RequestedType
Definition: mmc.h:51
#define STATUS_BUFFER_OVERFLOW
Definition: shellext.h:66
_In_ ULONG const _Out_ PULONG ValidBytes
Definition: mmc.h:64
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
static calc_node_t temp
Definition: rpn_ieee.c:38
#define RTL_NUMBER_OF(x)
Definition: RtlRegistry.c:12
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
unsigned int * PULONG
Definition: retypes.h:1
#define min(a, b)
Definition: monoChain.cc:55
#define NULL
Definition: types.h:112
Definition: bcd.h:201
#define SCSIOP_GET_CONFIGURATION
Definition: cdrw_hw.h:930
#define WRITE_RETRY_DELAY_HDDVD_1x
Definition: cdrom.h:121
#define CdromMmcUpdateRequired
Definition: cdrom.h:242
CDROM_MMC_EXTENSION Mmc
Definition: cdrom.h:341
LONGLONG ReadWriteRetryDelay100nsUnits
Definition: cdrom.h:384
ULONG const BOOLEAN const _Out_ PBOOLEAN _Out_ PFEATURE_NUMBER ValidationSchema
Definition: mmc.h:75
#define _Outptr_result_bytebuffer_all_(size)
Definition: ms_sal.h:477
VOID NTAPI IoFreeIrp(IN PIRP Irp)
Definition: irp.c:1666
unsigned int ULONG
Definition: retypes.h:1
struct _GET_CONFIGURATION_HEADER * PGET_CONFIGURATION_HEADER
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
PDEVICE_OBJECT DeviceObject
Definition: cdrom.h:493
PIRP NTAPI IoAllocateIrp(IN CCHAR StackSize, IN BOOLEAN ChargeQuota)
Definition: irp.c:615
#define STATUS_SUCCESS
Definition: shellext.h:65
#define SCSIOP_GET_PERFORMANCE
Definition: cdrw_hw.h:959
PIRP CapabilitiesIrp
Definition: cdrom.h:264
BOOLEAN StreamingWriteSupported
Definition: cdrom.h:254
signed int * PLONG
Definition: retypes.h:5
static SERVICE_STATUS status
Definition: service.c:31
BOOLEAN IsCssDvd
Definition: cdrom.h:252
#define APC_LEVEL
Definition: env_spec_w32.h:695
struct CFHEADER header
Definition: fdi.c:101
struct _FEATURE_DATA_RANDOM_READABLE * PFEATURE_DATA_RANDOM_READABLE
#define ExFreePool(addr)
Definition: env_spec_w32.h:352
#define BufferSize
Definition: mmc.h:75
_In_ WDFMEMORY _Out_opt_ size_t * BufferSize
Definition: wdfmemory.h:251
#define CDROM_TAG_FEATURE
Definition: cdrom.h:733
#define PAGED_CODE()
#define _In_reads_bytes_(size)
Definition: ms_sal.h:321
#define NT_ASSERT
Definition: rtlfuncs.h:3310
Definition: ps.c:97