xref: /reactos/drivers/storage/class/cdrom/init.c (revision 98e8827a)
1 /*--
2 
3 Copyright (C) Microsoft Corporation. All rights reserved.
4 
5 Module Name:
6 
7     init.c
8 
9 Abstract:
10 
11     Initialization routines for CDROM
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 
25 #include "ntddk.h"
26 #include "ntddstor.h"
27 #include "ntstrsafe.h"
28 #include "devpkey.h"
29 
30 #include "cdrom.h"
31 #include "scratch.h"
32 #include "mmc.h"
33 
34 #ifdef DEBUG_USE_WPP
35 #include "init.tmh"
36 #endif
37 
38 _IRQL_requires_max_(APC_LEVEL)
39 NTSTATUS
40 DeviceInitAllocateBuffers(
41     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
42     );
43 
44 _IRQL_requires_max_(PASSIVE_LEVEL)
45 NTSTATUS
46 DeviceRetrieveScsiAddress(
47     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
48     _In_ PSCSI_ADDRESS           ScsiAddress
49     );
50 
51 _IRQL_requires_max_(PASSIVE_LEVEL)
52 NTSTATUS
53 DeviceRetrieveDescriptorsAndTransferLength(
54     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
55     );
56 
57 _IRQL_requires_max_(APC_LEVEL)
58 VOID
59 DeviceScanSpecialDevices(
60     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
61     );
62 
63 _IRQL_requires_max_(PASSIVE_LEVEL)
64 NTSTATUS
65 DeviceInitMmcContext(
66     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
67     );
68 
69 _IRQL_requires_max_(PASSIVE_LEVEL)
70 NTSTATUS
71 DeviceGetMmcSupportInfo(
72     _In_  PCDROM_DEVICE_EXTENSION   DeviceExtension,
73     _Out_ PBOOLEAN                  IsMmcDevice
74     );
75 
76 #if (NTDDI_VERSION >= NTDDI_WIN8)
77 _IRQL_requires_max_(PASSIVE_LEVEL)
78 NTSTATUS
79 DeviceIsPortable(
80     _In_  PCDROM_DEVICE_EXTENSION   DeviceExtension,
81     _Out_ PBOOLEAN                  IsPortable
82     );
83 #endif
84 
85 
86 #ifdef ALLOC_PRAGMA
87 
88 #pragma alloc_text(PAGE, DeviceClaimRelease)
89 #pragma alloc_text(PAGE, DeviceEvtSelfManagedIoInit)
90 
91 #pragma alloc_text(PAGE, DeviceInitReleaseQueueContext)
92 #pragma alloc_text(PAGE, DeviceInitAllocateBuffers)
93 #pragma alloc_text(PAGE, DeviceInitPowerContext)
94 #pragma alloc_text(PAGE, DeviceCreateWellKnownName)
95 #pragma alloc_text(PAGE, DeviceRetrieveScsiAddress)
96 #pragma alloc_text(PAGE, DeviceRetrieveDescriptorsAndTransferLength)
97 #pragma alloc_text(PAGE, DeviceInitializeHotplugInfo)
98 #pragma alloc_text(PAGE, DeviceScanSpecialDevices)
99 #pragma alloc_text(PAGE, DeviceGetTimeOutValueFromRegistry)
100 #pragma alloc_text(PAGE, DeviceGetMmcSupportInfo)
101 #pragma alloc_text(PAGE, DeviceRetrieveDescriptor)
102 #pragma alloc_text(PAGE, DeviceRetrieveHackFlagsFromRegistry)
103 #pragma alloc_text(PAGE, DeviceScanForSpecial)
104 #pragma alloc_text(PAGE, DeviceHackFlagsScan)
105 #pragma alloc_text(PAGE, DeviceInitMmcContext)
106 #pragma alloc_text(PAGE, ScanForSpecialHandler)
107 #pragma alloc_text(PAGE, DeviceSetRawReadInfo)
108 #pragma alloc_text(PAGE, DeviceInitializeDvd)
109 #pragma alloc_text(PAGE, DeviceCacheDeviceInquiryData)
110 
111 #if (NTDDI_VERSION >= NTDDI_WIN8)
112 #pragma alloc_text(PAGE, DeviceIsPortable)
113 #endif
114 
115 #endif
116 
117 #pragma warning(push)
118 #pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
119 #pragma warning(disable:26000) // read overflow reported because of pointer type conversion
120 
121 _IRQL_requires_max_(PASSIVE_LEVEL)
122 NTSTATUS
123 DeviceClaimRelease(
124     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
125     _In_ BOOLEAN                 Release
126     )
127 /*++
128 
129 Routine Description:
130 
131     This function claims a device in the port driver.  The port driver object
132     is updated with the correct driver object if the device is successfully
133     claimed.
134 
135 Arguments:
136 
137     Device - The WDFDEVICE that needs to be claimed or released.
138 
139     Release - Indicates the logical unit should be released rather than claimed.
140 
141 Return Value:
142 
143     Returns a status indicating success or failure of the operation.
144 
145 --*/
146 {
147     NTSTATUS                status;
148     SCSI_REQUEST_BLOCK      srb = {0};
149     WDF_MEMORY_DESCRIPTOR   descriptor;
150     WDFREQUEST              request;
151     WDF_OBJECT_ATTRIBUTES   attributes;
152 
153     PAGED_CODE();
154 
155     //Create a request
156     WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
157                                             CDROM_REQUEST_CONTEXT);
158 
159     status = WdfRequestCreate(&attributes,
160                               DeviceExtension->IoTarget,
161                               &request);
162 
163     if (NT_SUCCESS(status))
164     {
165         //fill up srb structure
166         srb.OriginalRequest = WdfRequestWdmGetIrp(request);
167         NT_ASSERT(srb.OriginalRequest != NULL);
168 
169         srb.Length = sizeof(SCSI_REQUEST_BLOCK);
170 
171         srb.Function = Release
172                         ? SRB_FUNCTION_RELEASE_DEVICE
173                         : SRB_FUNCTION_CLAIM_DEVICE;
174 
175 
176         WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&descriptor,
177                                           &srb,
178                                           sizeof(srb));
179 
180         status = WdfIoTargetSendInternalIoctlOthersSynchronously(DeviceExtension->IoTarget,
181                                                                  request,
182                                                                  IOCTL_SCSI_EXECUTE_NONE,
183                                                                  &descriptor,
184                                                                  NULL,
185                                                                  NULL,
186                                                                  NULL,
187                                                                  NULL);
188 
189         NT_ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
190 
191         // The request should be deleted.
192         WdfObjectDelete(request);
193 
194         if (!NT_SUCCESS(status))
195         {
196             TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
197                         "DeviceClaimRelease: Failed to %s device, status: 0x%X\n",
198                         Release ? "Release" : "Claim",
199                         status));
200         }
201     }
202     else
203     {
204         TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
205                     "DeviceClaimRelease: Failed to create request, status: 0x%X\n",
206                     status));
207     }
208 
209     if (Release)
210     {
211         // We only release the device when we don't want to manage it.
212         // The failure status does not matter.
213         status = STATUS_SUCCESS;
214     }
215 
216     return status;
217 } // end DeviceClaimRelease()
218 
219 
220 NTSTATUS
221 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
222 DeviceEvtSelfManagedIoInit(
223     _In_ WDFDEVICE      Device
224     )
225 /*++
226 
227 Routine Description:
228 
229     This routine is called only once after the device is added in system, so it's used to do
230     hardware-dependent device initialization work and resource allocation.
231     If this routine fails, DeviceEvtSelfManagedIoCleanup will be invoked by the framework.
232 
233 Arguments:
234 
235     Device - Handle to device object
236 
237 Return Value:
238 
239     NTSTATUS
240 
241 --*/
242 {
243     NTSTATUS                status = STATUS_SUCCESS;
244     PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
245 
246     PAGED_CODE();
247 
248     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,
249                 "DeviceEvtSelfManagedIoInit: WDFDEVICE %p is being started.\n",
250                 Device));
251 
252     deviceExtension = DeviceGetExtension(Device);
253 
254     // 1. Set/retrieve basic information, some of the following operations may depend on it
255     if (NT_SUCCESS(status))
256     {
257         // We do not care if this function fails, SCSI address is mainly for debugging/tracing purposes.
258         (VOID) DeviceRetrieveScsiAddress(deviceExtension, &deviceExtension->ScsiAddress);
259     }
260 
261     if (NT_SUCCESS(status))
262     {
263         status = DeviceRetrieveDescriptorsAndTransferLength(deviceExtension);
264     }
265 
266     if (NT_SUCCESS(status))
267     {
268         // This function should be called after DeviceRetrieveDescriptorsAndTransferLength()
269         // It depends on MaxTransferLenth fields.
270         status = DeviceInitAllocateBuffers(deviceExtension);
271     }
272 
273     // 2. The following functions depend on the allocated buffers.
274 
275     // perf re-enable after failing. Q: Is this one used by cdrom.sys?
276     if (NT_SUCCESS(status))
277     {
278         // allow perf to be re-enabled after a given number of failed IOs
279         // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
280         ULONG t = CLASS_PERF_RESTORE_MINIMUM;
281 
282         DeviceGetParameter(deviceExtension,
283                            CLASSP_REG_SUBKEY_NAME,
284                            CLASSP_REG_PERF_RESTORE_VALUE_NAME,
285                            &t);
286         if (t >= CLASS_PERF_RESTORE_MINIMUM)
287         {
288             deviceExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
289         }
290     }
291 
292     // 3. Retrieve information about special devices and hack flags.
293     if (NT_SUCCESS(status))
294     {
295         DeviceRetrieveHackFlagsFromRegistry(deviceExtension);
296         // scan for bad items.
297         DeviceScanForSpecial(deviceExtension, CdRomBadItems, DeviceHackFlagsScan);
298         // Check to see if it's a special device that needs special error process.
299         DeviceScanSpecialDevices(deviceExtension);  // may send command to device
300     }
301 
302     // 4. Initialize the hotplug information only after the ScanForSpecial routines,
303     // as it relies upon the hack flags - deviceExtension->PrivateFdoData->HackFlags.
304     if (NT_SUCCESS(status))
305     {
306         status = DeviceInitializeHotplugInfo(deviceExtension);
307     }
308 
309     if (NT_SUCCESS(status))
310     {
311         // cache the device's inquiry data
312         status = DeviceCacheDeviceInquiryData(deviceExtension);
313         if (!NT_SUCCESS(status))
314         {
315             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
316                         "Failed to cache the device's inquiry data, failng %!STATUS!\n",
317                         status
318                         ));
319         }
320     }
321 
322     // 5. Initialize MMC context, media change notification stuff and read media capacity
323     if (NT_SUCCESS(status))
324     {
325         status = DeviceInitializeMediaChangeDetection(deviceExtension);
326     }
327     if (NT_SUCCESS(status))
328     {
329         status = DeviceInitMmcContext(deviceExtension);
330     }
331     if (NT_SUCCESS(status))
332     {
333         status = DeviceInitializeZPODD(deviceExtension);
334     }
335     if (NT_SUCCESS(status))
336     {
337         // Do READ CAPACITY. This SCSI command returns the last sector address
338         // on the device and the bytes per sector. These are used to calculate
339         // the drive capacity in bytes.
340         status = MediaReadCapacity(Device);
341 
342         // If READ CAPACITY succeeded, we can safely conclude that there is a media present
343         if (NT_SUCCESS(status))
344         {
345             DeviceSetMediaChangeStateEx(deviceExtension,
346                                         MediaPresent,
347                                         NULL);
348         }
349 
350         // READ CAPACITY is not critical for init, ignore all errors occuring during its execution
351         status = STATUS_SUCCESS;
352     }
353 
354     // 6. Perform DVD-specific initialization
355     if (NT_SUCCESS(status))
356     {
357         status = DeviceInitializeDvd(Device);
358     }
359 
360     // 7. Miscellaneous initialization actions
361     if (NT_SUCCESS(status))
362     {
363         if (deviceExtension->PrivateFdoData != NULL)
364         {
365             deviceExtension->PrivateFdoData->Perf.OriginalSrbFlags = deviceExtension->SrbFlags;
366         }
367 
368         if (deviceExtension->DeviceAdditionalData.Mmc.IsWriter)
369         {
370             // OPC can really take this long per IMAPIv1 timeout....
371             deviceExtension->TimeOutValue = max(deviceExtension->TimeOutValue, SCSI_CDROM_OPC_TIMEOUT);
372         }
373     }
374 
375     // 8. Enable the main timer, create ARC name as needed
376     if (NT_SUCCESS(status))
377     {
378         // Device successfully added and initialized, increase CdRomCount.
379         IoGetConfigurationInformation()->CdRomCount++;
380 
381         deviceExtension->IsInitialized = TRUE;
382 
383         DeviceEnableMainTimer(deviceExtension);
384 
385     }
386 
387 #if (NTDDI_VERSION >= NTDDI_WIN8)
388     // 9. Set volume interface properties
389     if (NT_SUCCESS(status))
390     {
391         BOOLEAN isCritical = FALSE;
392         BOOLEAN isPortable = FALSE;
393         BOOLEAN isRemovable = TEST_FLAG(deviceExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA);
394         DEVPROP_BOOLEAN propCritical = DEVPROP_FALSE;
395         DEVPROP_BOOLEAN propPortable = DEVPROP_FALSE;
396         DEVPROP_BOOLEAN propRemovable = DEVPROP_FALSE;
397 
398         status = DeviceIsPortable(deviceExtension, &isPortable);
399 
400         if (NT_SUCCESS(status))
401         {
402             if (isPortable) {
403                 SET_FLAG(deviceExtension->DeviceObject->Characteristics, FILE_PORTABLE_DEVICE);
404             }
405 
406             propPortable = isPortable ? DEVPROP_TRUE : DEVPROP_FALSE;
407 
408             status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
409                                                       &DEVPKEY_Storage_Portable,
410                                                       0,
411                                                       0,
412                                                       DEVPROP_TYPE_BOOLEAN,
413                                                       sizeof(DEVPROP_BOOLEAN),
414                                                       &propPortable);
415         }
416 
417         if (NT_SUCCESS(status))
418         {
419             propRemovable = isRemovable ? DEVPROP_TRUE : DEVPROP_FALSE;
420 
421             status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
422                                                       &DEVPKEY_Storage_Removable_Media,
423                                                       0,
424                                                       0,
425                                                       DEVPROP_TYPE_BOOLEAN,
426                                                       sizeof(DEVPROP_BOOLEAN),
427                                                       &propRemovable);
428         }
429 
430         if (NT_SUCCESS(status))
431         {
432             isCritical = TEST_FLAG(deviceExtension->DeviceObject->Flags,
433                                    (DO_SYSTEM_SYSTEM_PARTITION |
434                                     DO_SYSTEM_BOOT_PARTITION   |
435                                     DO_SYSTEM_CRITICAL_PARTITION));
436 
437             propCritical = isCritical ? DEVPROP_TRUE : DEVPROP_FALSE;
438 
439             status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
440                                                       &DEVPKEY_Storage_System_Critical,
441                                                       0,
442                                                       0,
443                                                       DEVPROP_TYPE_BOOLEAN,
444                                                       sizeof(DEVPROP_BOOLEAN),
445                                                       &propCritical);
446         }
447 
448     }
449 #endif
450 
451     return status;
452 }
453 
454 
455 _IRQL_requires_max_(APC_LEVEL)
456 NTSTATUS
457 DeviceInitReleaseQueueContext(
458     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
459     )
460 /*++
461 
462 Routine Description:
463 
464     Part of device initialize routine. Initialize ReleaseQueue related stuff.
465 
466 Arguments:
467 
468     DeviceExtension - device extension of WDFDEVICE.
469 
470 Return Value:
471 
472     NTSTATUS
473 
474 --*/
475 {
476     NTSTATUS                status = STATUS_SUCCESS;
477     WDF_OBJECT_ATTRIBUTES   attributes;
478 
479     PAGED_CODE();
480 
481     WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
482                                             CDROM_REQUEST_CONTEXT);
483     attributes.ParentObject = DeviceExtension->Device;
484 
485     status = WdfRequestCreate(&attributes,
486                               DeviceExtension->IoTarget,
487                               &(DeviceExtension->ReleaseQueueRequest));
488 
489     if (!NT_SUCCESS(status))
490     {
491         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "Cannot create the release queue request\n"));
492 
493         return status;
494     }
495 
496     // Initialize ReleaseQueueInputMemory, a wrapper around ReleaseQueueSrb
497     WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
498     attributes.ParentObject = DeviceExtension->ReleaseQueueRequest;
499 
500     status = WdfMemoryCreatePreallocated(&attributes,
501                                          &DeviceExtension->ReleaseQueueSrb,
502                                          sizeof(SCSI_REQUEST_BLOCK),
503                                          &DeviceExtension->ReleaseQueueInputMemory);
504     if (!NT_SUCCESS(status))
505     {
506         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "Failed to allocate ReleaseQueueSrb.\n"));
507 
508         return status;
509     }
510 
511     // Preformat the release queue request here to ensure that this call will never
512     // fail during an actual release of the queue.
513     if (NT_SUCCESS(status))
514     {
515         status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
516                                                                 DeviceExtension->ReleaseQueueRequest,
517                                                                 IOCTL_SCSI_EXECUTE_NONE,
518                                                                 DeviceExtension->ReleaseQueueInputMemory,
519                                                                 NULL,
520                                                                 NULL,
521                                                                 NULL,
522                                                                 NULL,
523                                                                 NULL);
524     }
525 
526     // Set a CompletionRoutine callback function for ReleaseQueueRequest.
527     if (NT_SUCCESS(status))
528     {
529         WdfRequestSetCompletionRoutine(DeviceExtension->ReleaseQueueRequest,
530                                        DeviceReleaseQueueCompletion,
531                                        DeviceExtension->Device);
532     }
533 
534     // Create a spinlock for ReleaseQueueRequest
535     WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
536     attributes.ParentObject = DeviceExtension->Device;
537 
538     status = WdfSpinLockCreate(&attributes,
539                                &(DeviceExtension->ReleaseQueueSpinLock));
540 
541     if (!NT_SUCCESS(status))
542     {
543         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,
544                     "DeviceInitReleaseQueueContext: Cannot create the release queue spinlock\n"));
545 
546         return status;
547     }
548 
549     // Initialize miscellaneous ReleaseQueue related fields
550     DeviceExtension->ReleaseQueueNeeded = FALSE;
551     DeviceExtension->ReleaseQueueInProgress = FALSE;
552     DeviceExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
553 
554     return status;
555 }
556 
557 _IRQL_requires_max_(APC_LEVEL)
558 NTSTATUS
559 DeviceInitPowerContext(
560     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
561     )
562 /*++
563 
564 Routine Description:
565 
566     Part of device initialize routine. Initialize PowerContext related stuff.
567 
568 Arguments:
569 
570     DeviceExtension - device extension of WDFDEVICE.
571 
572 Return Value:
573 
574     NTSTATUS
575 
576 --*/
577 {
578     NTSTATUS                status = STATUS_SUCCESS;
579     WDF_OBJECT_ATTRIBUTES   attributes;
580 
581     PAGED_CODE();
582 
583     // create request object for Power operations
584 
585     WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
586                                             CDROM_REQUEST_CONTEXT);
587     attributes.ParentObject = DeviceExtension->Device;
588 
589     status = WdfRequestCreate(&attributes,
590                               DeviceExtension->IoTarget,
591                               &(DeviceExtension->PowerContext.PowerRequest) );
592 
593     if (!NT_SUCCESS(status))
594     {
595         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "Cannot create the power request object.\n"));
596 
597         return status;
598     }
599 
600     // Preformat the power request. With this being done, we never need to worry about
601     // WdfIoTargetFormatRequestForInternalIoctlOthers ever failing later.
602     status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
603                                                             DeviceExtension->PowerContext.PowerRequest,
604                                                             IOCTL_SCSI_EXECUTE_IN,
605                                                             NULL, NULL,
606                                                             NULL, NULL,
607                                                             NULL, NULL);
608     return status;
609 }
610 
611 _IRQL_requires_max_(APC_LEVEL)
612 NTSTATUS
613 DeviceCreateWellKnownName(
614     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
615     )
616 /*++
617 
618 Routine Description:
619 
620     This routine creates a symbolic link to the cdrom device object
621     under \dosdevices.  The number of the cdrom device does not neccessarily
622     match between \dosdevices and \device, but usually will be the same.
623 
624     Saves the buffer
625 
626 Arguments:
627 
628     DeviceObject -
629 
630 Return Value:
631 
632     NTSTATUS
633 
634 --*/
635 {
636     NTSTATUS        status = STATUS_SUCCESS;
637     UNICODE_STRING  unicodeLinkName = {0};
638     WCHAR           wideLinkName[64] = {0};
639     PWCHAR          savedName;
640 
641     LONG            cdromNumber = DeviceExtension->DeviceNumber;
642 
643     PAGED_CODE();
644 
645     // if already linked, assert then return
646     if (DeviceExtension->DeviceAdditionalData.WellKnownName.Buffer != NULL)
647     {
648 
649         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
650                     "DeviceCreateWellKnownName: link already exists %p\n",
651                     DeviceExtension->DeviceAdditionalData.WellKnownName.Buffer));
652 
653         NT_ASSERT(FALSE);
654 
655         return STATUS_UNSUCCESSFUL;
656     }
657 
658     // find an unused CdRomNN to link to.
659     // It's doing this way because the same might be used for other device in another driver.
660     do
661     {
662         status = RtlStringCchPrintfW((NTSTRSAFE_PWSTR)wideLinkName,
663                                      RTL_NUMBER_OF(wideLinkName),
664                                      L"\\DosDevices\\CdRom%d",
665                                      cdromNumber);
666         if (!NT_SUCCESS(status))
667         {
668             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
669                         "DeviceCreateWellKnownName: Format symbolic link failed with error: 0x%X\n", status));
670             return status;
671         }
672 
673         RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
674 
675         status = WdfDeviceCreateSymbolicLink(DeviceExtension->Device,
676                                              &unicodeLinkName);
677 
678         cdromNumber++;
679 
680     } while((status == STATUS_OBJECT_NAME_COLLISION) ||
681             (status == STATUS_OBJECT_NAME_EXISTS));
682 
683     if (!NT_SUCCESS(status))
684     {
685         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
686                     "DeviceCreateWellKnownName: Error %lx linking %wZ to "
687                     "device %wZ\n",
688                     status,
689                     &unicodeLinkName,
690                     &(DeviceExtension->DeviceName)));
691         return status;
692     }
693 
694     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
695                 "DeviceCreateWellKnownName: successfully linked %wZ "
696                 "to device %wZ\n",
697                 &unicodeLinkName,
698                 &(DeviceExtension->DeviceName)));
699 
700     // Save away the symbolic link name in the driver data block.  We need
701     // it so we can delete the link when the device is removed.
702     savedName = ExAllocatePoolWithTag(PagedPool,
703                                       unicodeLinkName.MaximumLength,
704                                       CDROM_TAG_STRINGS);
705 
706     if (savedName == NULL)
707     {
708         // Test Note: test path should excise here to see if the symbolic is deleted by framework.
709         // IoDeleteSymbolicLink(&unicodeLinkName);
710         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
711                     "DeviceCreateWellKnownName: unable to allocate memory.\n"));
712 
713         return STATUS_INSUFFICIENT_RESOURCES;
714     }
715 
716     RtlZeroMemory(savedName, unicodeLinkName.MaximumLength);
717     RtlCopyMemory(savedName, unicodeLinkName.Buffer, unicodeLinkName.MaximumLength);
718 
719     RtlInitUnicodeString(&(DeviceExtension->DeviceAdditionalData.WellKnownName), savedName);
720 
721     // the name was saved and the link created
722 
723     return STATUS_SUCCESS;
724 }
725 
726 _IRQL_requires_max_(PASSIVE_LEVEL)
727 NTSTATUS
728 DeviceRetrieveScsiAddress(
729     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
730     _In_ PSCSI_ADDRESS           ScsiAddress
731     )
732 /*++
733 
734 Routine Description:
735 
736     retrieve SCSI address information and put into device extension
737 
738 Arguments:
739 
740     DeviceExtension - device context.
741     ScsiAddress - the buffer to put the scsi address info.
742 
743 Return Value:
744 
745     NTSTATUS
746 
747 --*/
748 {
749     NTSTATUS                status;
750     WDF_MEMORY_DESCRIPTOR   outputDescriptor;
751 
752     PAGED_CODE();
753 
754     if ((DeviceExtension == NULL) ||
755         (ScsiAddress == NULL))
756     {
757         return STATUS_INVALID_PARAMETER;
758     }
759 
760     //Get IOTARGET for sending request to port driver.
761     WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor,
762                                       (PVOID)ScsiAddress,
763                                       sizeof(SCSI_ADDRESS));
764 
765     status = WdfIoTargetSendIoctlSynchronously(DeviceExtension->IoTarget,
766                                                NULL,
767                                                IOCTL_SCSI_GET_ADDRESS,
768                                                NULL,
769                                                &outputDescriptor,
770                                                NULL,
771                                                NULL);
772 
773     if (!NT_SUCCESS(status))
774     {
775         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
776                     "DeviceRetrieveScsiAddress: Get Address failed %lx\n",
777                     status));
778     }
779     else
780     {
781         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
782                     "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
783                     ScsiAddress->PortNumber,
784                     ScsiAddress->PathId,
785                     ScsiAddress->TargetId,
786                     ScsiAddress->Lun));
787     }
788 
789     return status;
790 }
791 
792 _IRQL_requires_max_(APC_LEVEL)
793 NTSTATUS
794 DeviceInitAllocateBuffers(
795     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
796     )
797 /*++
798 
799 Routine Description:
800 
801     Part of device initialize routine.
802     Allocate all buffers in Device Extension.
803 
804 Arguments:
805 
806     DeviceExtension - device extension of WDFDEVICE.
807 
808 Return Value:
809 
810     NTSTATUS
811 
812 --*/
813 {
814     NTSTATUS            status = STATUS_SUCCESS;
815     PVOID               senseData = NULL;
816 
817     PAGED_CODE();
818 
819     // allocate a private extension for class data
820     if (DeviceExtension->PrivateFdoData == NULL)
821     {
822         DeviceExtension->PrivateFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
823                                                                 sizeof(CDROM_PRIVATE_FDO_DATA),
824                                                                 CDROM_TAG_PRIVATE_DATA);
825     }
826 
827     if (DeviceExtension->PrivateFdoData == NULL)
828     {
829         status = STATUS_INSUFFICIENT_RESOURCES;
830     }
831     else
832     {
833         // initialize the struct's various fields.
834         RtlZeroMemory(DeviceExtension->PrivateFdoData, sizeof(CDROM_PRIVATE_FDO_DATA));
835     }
836 
837     // Allocate request sense buffer.
838     senseData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
839                                       SENSE_BUFFER_SIZE,
840                                       CDROM_TAG_SENSE_INFO);
841 
842     if (senseData == NULL)
843     {
844         // The buffer cannot be allocated.
845         status = STATUS_INSUFFICIENT_RESOURCES;
846     }
847     else
848     {
849         // Set the sense data pointer in the device extension.
850         DeviceExtension->SenseData = senseData;
851     }
852 
853     // Allocate scratch buffer -- Must occur after determining
854     // max transfer size, but before other CD specific detection
855     // (which relies upon this buffer).
856     if (!ScratchBuffer_Allocate(DeviceExtension))
857     {
858         status = STATUS_INSUFFICIENT_RESOURCES;
859         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
860                     "Failed to allocate scratch buffer, failing  %!STATUS!\n",
861                     status
862                     ));
863     }
864 
865     return status;
866 }
867 
868 _IRQL_requires_max_(PASSIVE_LEVEL)
869 NTSTATUS
870 DeviceRetrieveDescriptorsAndTransferLength(
871     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
872     )
873 /*++
874 
875 Routine Description:
876 
877     Part of device initialize routine.
878     Retrieve Device Descriptor and Adaptor Descriptor.
879 
880 Arguments:
881 
882     DeviceExtension - device extension of WDFDEVICE.
883 
884 Return Value:
885 
886     NTSTATUS
887 
888 --*/
889 {
890     NTSTATUS            status = STATUS_SUCCESS;
891     STORAGE_PROPERTY_ID propertyId;
892 
893     PAGED_CODE();
894 
895     if (NT_SUCCESS(status))
896     {
897         // Call port driver to get adapter capabilities.
898         propertyId = StorageAdapterProperty;
899 
900         status = DeviceRetrieveDescriptor(DeviceExtension->Device,
901                                           &propertyId,
902                                           (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->AdapterDescriptor);
903     }
904     if (NT_SUCCESS(status))
905     {
906         // Call port driver to get device descriptor.
907         propertyId = StorageDeviceProperty;
908 
909         status = DeviceRetrieveDescriptor(DeviceExtension->Device,
910                                           &propertyId,
911                                           (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->DeviceDescriptor);
912     }
913     if (NT_SUCCESS(status))
914     {
915         // Call port driver to get device power property.
916         // Not all port drivers support this property, and it's not fatal if this query fails.
917         propertyId = StorageDevicePowerProperty;
918 
919         (void) DeviceRetrieveDescriptor(DeviceExtension->Device,
920                                         &propertyId,
921                                         (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->PowerDescriptor);
922     }
923 
924     if (NT_SUCCESS(status))
925     {
926         // Determine the maximum page-aligned and non-page-aligned transfer
927         // lengths here, so we needn't do this in common READ/WRITE code paths
928 
929         // start with the number of pages the adapter can support
930         ULONG maxAlignedTransfer = DeviceExtension->AdapterDescriptor->MaximumPhysicalPages;
931         ULONG maxUnalignedTransfer = DeviceExtension->AdapterDescriptor->MaximumPhysicalPages;
932 
933 
934         // Unaligned buffers could cross a page boundary.
935         if (maxUnalignedTransfer > 1)
936         {
937             maxUnalignedTransfer--;
938         }
939 
940         // if we'd overflow multiplying by page size, just max out the
941         // transfer length allowed by the number of pages limit.
942         if (maxAlignedTransfer >= (((ULONG)-1) / PAGE_SIZE))
943         {
944             maxAlignedTransfer = (ULONG)-1;
945         }
946         else
947         {
948             maxAlignedTransfer *= PAGE_SIZE;
949         }
950 
951         if (maxUnalignedTransfer >= (((ULONG)-1) / PAGE_SIZE))
952         {
953             maxUnalignedTransfer = (ULONG)-1;
954         }
955         else
956         {
957             maxUnalignedTransfer *= PAGE_SIZE;
958         }
959 
960         // finally, take the smaller of the above and the adapter's
961         // reported maximum number of bytes per transfer.
962         maxAlignedTransfer   = min(maxAlignedTransfer,   DeviceExtension->AdapterDescriptor->MaximumTransferLength);
963         maxUnalignedTransfer = min(maxUnalignedTransfer, DeviceExtension->AdapterDescriptor->MaximumTransferLength);
964 
965         // Make sure the values are reasonable and not zero.
966         maxAlignedTransfer   = max(maxAlignedTransfer,   PAGE_SIZE);
967         maxUnalignedTransfer = max(maxUnalignedTransfer, PAGE_SIZE);
968 
969         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
970                   "Device %p Max aligned/unaligned transfer size is %x/%x\n",
971                   DeviceExtension->Device,
972                   maxAlignedTransfer,
973                   maxUnalignedTransfer
974                   ));
975         DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes = maxAlignedTransfer;
976         DeviceExtension->DeviceAdditionalData.MaxUnalignedTransferBytes = maxUnalignedTransfer;
977     }
978     else
979     {
980         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "DeviceRetrieveDescriptorsAndTransferLength failed %lx\n", status));
981     }
982 
983     return status;
984 }
985 
986 
987 _IRQL_requires_max_(PASSIVE_LEVEL)
988 NTSTATUS
989 DeviceRetrieveDescriptor(
990     _In_ WDFDEVICE                              Device,
991     _In_ PSTORAGE_PROPERTY_ID                   PropertyId,
992     _Outptr_ PSTORAGE_DESCRIPTOR_HEADER*        Descriptor
993     )
994 /*++
995 
996 Routine Description:
997 
998     This routine will perform a query for the specified property id and will
999     allocate a non-paged buffer to store the data in.  It is the responsibility
1000     of the caller to ensure that this buffer is freed.
1001 
1002     This routine must be run at IRQL_PASSIVE_LEVEL
1003 
1004 Arguments:
1005 
1006     Device - the device object
1007     PropertyId - type of property to retrieve
1008     Descriptor - buffer allocated in this function to hold the descriptor data
1009 
1010 Return Value:
1011 
1012     status
1013 
1014 --*/
1015 {
1016     NTSTATUS                status;
1017     WDF_MEMORY_DESCRIPTOR   memoryDescriptor;
1018 
1019     STORAGE_PROPERTY_QUERY  query = {0};
1020     ULONG                   bufferLength = 0;
1021 
1022     PSTORAGE_DESCRIPTOR_HEADER  descriptor = NULL;
1023     PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
1024 
1025     PAGED_CODE();
1026 
1027     // Set the passed-in descriptor pointer to NULL as default
1028     *Descriptor = NULL;
1029 
1030     // On the first pass we just want to get the first few
1031     // bytes of the descriptor so we can read it's size
1032     query.PropertyId = *PropertyId;
1033     query.QueryType = PropertyStandardQuery;
1034 
1035     descriptor = (PVOID)&query;
1036 
1037     NT_ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
1038 
1039     WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor,
1040                                       (PVOID)&query,
1041                                       sizeof(STORAGE_PROPERTY_QUERY));
1042 
1043     status = WdfIoTargetSendIoctlSynchronously(deviceExtension->IoTarget,
1044                                                NULL,
1045                                                IOCTL_STORAGE_QUERY_PROPERTY,
1046                                                &memoryDescriptor,
1047                                                &memoryDescriptor,
1048                                                NULL,
1049                                                NULL);
1050 
1051     if(!NT_SUCCESS(status))
1052     {
1053         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "DeviceRetrieveDescriptor: error %lx trying to "
1054                        "query properties #1\n", status));
1055         return status;
1056     }
1057 
1058     if (descriptor->Size == 0)
1059     {
1060         // This DebugPrint is to help third-party driver writers
1061         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "DeviceRetrieveDescriptor: size returned was zero?! (status "
1062                     "%x\n", status));
1063         return STATUS_UNSUCCESSFUL;
1064     }
1065 
1066     // This time we know how much data there is so we can
1067     // allocate a buffer of the correct size
1068     bufferLength = descriptor->Size;
1069     NT_ASSERT(bufferLength >= sizeof(STORAGE_PROPERTY_QUERY));
1070     bufferLength = max(bufferLength, sizeof(STORAGE_PROPERTY_QUERY));
1071 
1072     descriptor = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CDROM_TAG_DESCRIPTOR);
1073 
1074     if(descriptor == NULL)
1075     {
1076         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "DeviceRetrieveDescriptor: unable to memory for descriptor "
1077                     "(%d bytes)\n", bufferLength));
1078         return STATUS_INSUFFICIENT_RESOURCES;
1079     }
1080 
1081     // setup the query again, as it was overwritten above
1082     RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
1083     query.PropertyId = *PropertyId;
1084     query.QueryType = PropertyStandardQuery;
1085 
1086     // copy the input to the new outputbuffer
1087     RtlCopyMemory(descriptor,
1088                   &query,
1089                   sizeof(STORAGE_PROPERTY_QUERY));
1090 
1091     WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor,
1092                                       (PVOID)descriptor,
1093                                       bufferLength);
1094 
1095     status = WdfIoTargetSendIoctlSynchronously(deviceExtension->IoTarget,
1096                                                NULL,
1097                                                IOCTL_STORAGE_QUERY_PROPERTY,
1098                                                &memoryDescriptor,
1099                                                &memoryDescriptor,
1100                                                NULL,
1101                                                NULL);
1102 
1103     if(!NT_SUCCESS(status))
1104     {
1105         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "DeviceRetrieveDescriptor: error %lx trying to "
1106                        "query properties #1\n", status));
1107         FREE_POOL(descriptor);
1108 
1109         return status;
1110     }
1111 
1112     // return the memory we've allocated to the caller
1113     *Descriptor = descriptor;
1114 
1115     return status;
1116 } // end DeviceRetrieveDescriptor()
1117 
1118 _IRQL_requires_max_(PASSIVE_LEVEL)
1119 VOID
1120 DeviceRetrieveHackFlagsFromRegistry(
1121     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1122     )
1123 /*++
1124 
1125 Routine Description:
1126 
1127     try to retrieve hack flages from registry and put the information in
1128     device extension.
1129 
1130 Arguments:
1131 
1132     DeviceExtension - the device context
1133 
1134 Return Value:
1135 
1136     none
1137 
1138 --*/
1139 {
1140     NTSTATUS        status = STATUS_SUCCESS;
1141     WDFKEY          hardwareKey = NULL;
1142     WDFKEY          subKey = NULL;
1143     ULONG           deviceHacks = 0;
1144 
1145     DECLARE_CONST_UNICODE_STRING(subKeyName, CLASSP_REG_SUBKEY_NAME);
1146     DECLARE_CONST_UNICODE_STRING(valueName, CLASSP_REG_HACK_VALUE_NAME);
1147 
1148     PAGED_CODE();
1149 
1150     status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
1151                                       PLUGPLAY_REGKEY_DEVICE,
1152                                       KEY_READ,
1153                                       WDF_NO_OBJECT_ATTRIBUTES,
1154                                       &hardwareKey);
1155     if (NT_SUCCESS(status))
1156     {
1157         status = WdfRegistryOpenKey(hardwareKey,
1158                                     &subKeyName,
1159                                     KEY_READ,
1160                                     WDF_NO_OBJECT_ATTRIBUTES,
1161                                     &subKey);
1162 
1163         if (NT_SUCCESS(status))
1164         {
1165             status = WdfRegistryQueryULong(subKey,
1166                                            &valueName,
1167                                            &deviceHacks);
1168             if (NT_SUCCESS(status))
1169             {
1170                 // remove unknown values and save...
1171                 CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
1172                 SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, deviceHacks);
1173             }
1174 
1175             WdfRegistryClose(subKey);
1176         }
1177 
1178         WdfRegistryClose(hardwareKey);
1179     }
1180 
1181 
1182     //
1183     // we should modify the system hive to include another key for us to grab
1184     // settings from.  in this case:  Classpnp\HackFlags
1185     //
1186     // the use of a DWORD value for the HackFlags allows 32 hacks w/o
1187     // significant use of the registry, and also reduces OEM exposure.
1188     //
1189     // definition of bit flags:
1190     //   0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
1191     //                 cannot actually prevent removal.
1192     //   0x00000002 -- Device hard-hangs or times out for GESN requests.
1193     //   0x00000008 -- Device does not support RESERVE(6) and RELEASE(6).
1194     //   0x00000010 -- Device may incorrecly report operational changes in GESN.
1195     //   0x00000020 -- Device does not support streaming READ(12) / WRITE(12).
1196     //   0x00000040 -- Device does not support asynchronous notification.
1197     //   0xffffff80 -- Currently reserved, may be used later.
1198     //
1199 
1200     return;
1201 }
1202 
1203 _IRQL_requires_max_(APC_LEVEL)
1204 VOID DeviceScanForSpecial(
1205     _In_ PCDROM_DEVICE_EXTENSION          DeviceExtension,
1206     _In_ CDROM_SCAN_FOR_SPECIAL_INFO      DeviceList[],
1207     _In_ PCDROM_SCAN_FOR_SPECIAL_HANDLER  Function)
1208 /*++
1209 
1210 Routine Description:
1211 
1212     scan the list of devices that should be hacked or not supported.
1213 
1214 Arguments:
1215 
1216     DeviceExtension - the device context
1217     DeviceList - the device list
1218     Function - function used to scan from the list.
1219 
1220 Return Value:
1221 
1222     none
1223 
1224 --*/
1225 {
1226     PSTORAGE_DEVICE_DESCRIPTOR  deviceDescriptor;
1227     PUCHAR                      vendorId;
1228     PUCHAR                      productId;
1229     PUCHAR                      productRevision;
1230     UCHAR                       nullString[] = "";
1231 
1232     PAGED_CODE();
1233     NT_ASSERT(DeviceList);
1234     NT_ASSERT(Function);
1235 
1236     if (DeviceList == NULL)
1237     {
1238         return;
1239     }
1240     if (Function == NULL)
1241     {
1242         return;
1243     }
1244 
1245     deviceDescriptor = DeviceExtension->DeviceDescriptor;
1246 
1247     // SCSI sets offsets to -1, ATAPI sets to 0.  check for both.
1248     if (deviceDescriptor->VendorIdOffset != 0 &&
1249         deviceDescriptor->VendorIdOffset != -1)
1250     {
1251         vendorId = ((PUCHAR)deviceDescriptor);
1252         vendorId += deviceDescriptor->VendorIdOffset;
1253     }
1254     else
1255     {
1256         vendorId = nullString;
1257     }
1258 
1259     if (deviceDescriptor->ProductIdOffset != 0 &&
1260         deviceDescriptor->ProductIdOffset != -1)
1261     {
1262         productId = ((PUCHAR)deviceDescriptor);
1263         productId += deviceDescriptor->ProductIdOffset;
1264     }
1265     else
1266     {
1267         productId = nullString;
1268     }
1269 
1270     if (deviceDescriptor->ProductRevisionOffset != 0 &&
1271         deviceDescriptor->ProductRevisionOffset != -1)
1272     {
1273         productRevision = ((PUCHAR)deviceDescriptor);
1274         productRevision += deviceDescriptor->ProductRevisionOffset;
1275     }
1276     else
1277     {
1278         productRevision = nullString;
1279     }
1280 
1281     // loop while the device list is valid (not null-filled)
1282     for (;(DeviceList->VendorId        != NULL ||
1283            DeviceList->ProductId       != NULL ||
1284            DeviceList->ProductRevision != NULL); DeviceList++)
1285     {
1286         if (StringsAreMatched(DeviceList->VendorId,        (LPSTR)vendorId) &&
1287             StringsAreMatched(DeviceList->ProductId,       (LPSTR)productId) &&
1288             StringsAreMatched(DeviceList->ProductRevision, (LPSTR)productRevision)
1289             )
1290         {
1291 
1292             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: Found matching "
1293                         "controller Ven: %s Prod: %s Rev: %s\n",
1294                         (LPCSTR)vendorId, (LPCSTR)productId, (LPCSTR)productRevision));
1295 
1296             // pass the context to the call back routine and exit
1297             (Function)(DeviceExtension, DeviceList->Data);
1298 
1299             // for CHK builds, try to prevent wierd stacks by having a debug
1300             // print here. it's a hack, but i know of no other way to prevent
1301             // the stack from being wrong.
1302             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: "
1303                         "completed callback\n"));
1304             return;
1305 
1306         } // else the strings did not match
1307 
1308     } // none of the devices matched.
1309 
1310     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: no match found for %p\n",
1311                 DeviceExtension->DeviceObject));
1312     return;
1313 
1314 } // end DeviceScanForSpecial()
1315 
1316 _IRQL_requires_max_(APC_LEVEL)
1317 VOID
1318 DeviceHackFlagsScan(
1319     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
1320     _In_ ULONG_PTR                Data
1321     )
1322 {
1323     PAGED_CODE();
1324 
1325     // remove invalid flags and save
1326     CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
1327     SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, Data);
1328 
1329     return;
1330 }
1331 
1332 
1333 _IRQL_requires_max_(PASSIVE_LEVEL)
1334 NTSTATUS
1335 DeviceInitializeHotplugInfo(
1336     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1337     )
1338 /*++
1339 
1340 Routine Description:
1341 
1342     Retrieve information into struc STORAGE_HOTPLUG_INFO in DeviceExtension
1343     initialize the hotplug information only after the ScanForSpecial routines,
1344     as it relies upon the hack flags - DeviceExtension->PrivateFdoData->HackFlags.
1345 
1346 Arguments:
1347 
1348     DeviceExtension - the device context
1349 
1350 Return Value:
1351 
1352     NTSTATUS
1353 
1354 --*/
1355 {
1356     NTSTATUS                status = STATUS_SUCCESS;
1357     PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData;
1358     DEVICE_REMOVAL_POLICY   deviceRemovalPolicy = 0;
1359     ULONG                   resultLength = 0;
1360     ULONG                   writeCacheOverride;
1361 
1362     PAGED_CODE();
1363 
1364     // start with some default settings
1365     RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
1366 
1367     // set the size (aka version)
1368     fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
1369 
1370     // set if the device has removable media
1371     if (DeviceExtension->DeviceDescriptor->RemovableMedia)
1372     {
1373         fdoData->HotplugInfo.MediaRemovable = TRUE;
1374     }
1375     else
1376     {
1377         fdoData->HotplugInfo.MediaRemovable = FALSE;
1378     }
1379 
1380     //
1381     // this refers to devices which, for reasons not yet understood,
1382     // do not fail PREVENT_MEDIA_REMOVAL requests even though they
1383     // have no way to lock the media into the drive.  this allows
1384     // the filesystems to turn off delayed-write caching for these
1385     // devices as well.
1386     //
1387 
1388     if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_CANNOT_LOCK_MEDIA))
1389     {
1390         fdoData->HotplugInfo.MediaHotplug = TRUE;
1391     }
1392     else
1393     {
1394         fdoData->HotplugInfo.MediaHotplug = FALSE;
1395     }
1396 
1397     // Query the default removal policy from the kernel
1398     status = WdfDeviceQueryProperty(DeviceExtension->Device,
1399                                     DevicePropertyRemovalPolicy,
1400                                     sizeof(DEVICE_REMOVAL_POLICY),
1401                                     (PVOID)&deviceRemovalPolicy,
1402                                     &resultLength);
1403     if (NT_SUCCESS(status))
1404     {
1405         if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
1406         {
1407             status = STATUS_UNSUCCESSFUL;
1408         }
1409     }
1410 
1411     if (NT_SUCCESS(status))
1412     {
1413         // Look into the registry to see if the user has chosen
1414         // to override the default setting for the removal policy.
1415         // User can override only if the default removal policy is
1416         // orderly or suprise removal.
1417 
1418         if ((deviceRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
1419             (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval))
1420         {
1421             DEVICE_REMOVAL_POLICY userRemovalPolicy = 0;
1422 
1423             DeviceGetParameter(DeviceExtension,
1424                                 CLASSP_REG_SUBKEY_NAME,
1425                                 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
1426                                 (PULONG)&userRemovalPolicy);
1427 
1428             // Validate the override value and use it only if it is an
1429             // allowed value.
1430             if ((userRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
1431                 (userRemovalPolicy == RemovalPolicyExpectSurpriseRemoval))
1432             {
1433                 deviceRemovalPolicy = userRemovalPolicy;
1434             }
1435         }
1436 
1437         // use this info to set the DeviceHotplug setting
1438         // don't rely on DeviceCapabilities, since it can't properly
1439         // determine device relations, etc.  let the kernel figure this
1440         // stuff out instead.
1441         if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)
1442         {
1443             fdoData->HotplugInfo.DeviceHotplug = TRUE;
1444         }
1445         else
1446         {
1447             fdoData->HotplugInfo.DeviceHotplug = FALSE;
1448         }
1449 
1450         // this refers to the *filesystem* caching, but has to be included
1451         // here since it's a per-device setting.  this may change to be
1452         // stored by the system in the future.
1453         writeCacheOverride = FALSE;
1454         DeviceGetParameter(DeviceExtension,
1455                             CLASSP_REG_SUBKEY_NAME,
1456                             CLASSP_REG_WRITE_CACHE_VALUE_NAME,
1457                             &writeCacheOverride);
1458 
1459         if (writeCacheOverride)
1460         {
1461             fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
1462         }
1463         else
1464         {
1465             fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
1466         }
1467     }
1468 
1469     if (!NT_SUCCESS(status))
1470     {
1471         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "Could not initialize hotplug information %lx\n", status));
1472     }
1473 
1474     return status;
1475 }
1476 
1477 
1478 _IRQL_requires_max_(PASSIVE_LEVEL)
1479 NTSTATUS
1480 DeviceInitMmcContext(
1481     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1482     )
1483 /*++
1484 
1485 Routine Description:
1486 
1487     This routine initializes and populates the internal data structures that are
1488     used to discover various MMC-defined capabilities of the device.
1489 
1490     This routine will not clean up allocate resources if it fails - that
1491     is left for device stop/removal routines
1492 
1493 Arguments:
1494 
1495     DeviceExtension - device extension
1496 
1497 Return Value:
1498 
1499     NTSTATUS
1500 
1501 --*/
1502 {
1503     NTSTATUS                    status = STATUS_SUCCESS;
1504 
1505     PAGED_CODE();
1506 
1507     DeviceExtension->DeviceAdditionalData.Mmc.IsMmc = FALSE;
1508     DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = FALSE;
1509     DeviceExtension->DeviceAdditionalData.Mmc.IsWriter = FALSE;
1510     DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
1511     DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd = FALSE;
1512     DeviceExtension->DeviceAdditionalData.DriveDeviceType = FILE_DEVICE_CD_ROM;
1513 
1514     // Determine if the drive is MMC-Capable
1515     if (NT_SUCCESS(status))
1516     {
1517         status = DeviceGetMmcSupportInfo(DeviceExtension,
1518                                          &DeviceExtension->DeviceAdditionalData.Mmc.IsMmc);
1519 
1520         if (!NT_SUCCESS(status))
1521         {
1522             //Currently, only low resource error comes here.
1523             //That is a success case for unsupporting this command.
1524             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
1525                         "DeviceInitMmcContext: Failed to get the support info for GET CONFIGURATION "
1526                         "command, failng %!STATUS!\n", status
1527                         ));
1528 
1529             DeviceExtension->DeviceAdditionalData.Mmc.IsMmc = FALSE;
1530             status = STATUS_SUCCESS;
1531         }
1532     }
1533 
1534     if (NT_SUCCESS(status) && DeviceExtension->DeviceAdditionalData.Mmc.IsMmc)
1535     {
1536         // the drive supports at least a subset of MMC commands
1537         // (and therefore supports READ_CD, etc...)
1538 
1539         // allocate a buffer for all the capabilities and such
1540         status = DeviceAllocateMmcResources(DeviceExtension->Device);
1541     }
1542 
1543     if (NT_SUCCESS(status) && DeviceExtension->DeviceAdditionalData.Mmc.IsMmc)
1544     {
1545         PFEATURE_HEADER header = NULL;
1546         FEATURE_NUMBER  validationSchema;
1547         ULONG           blockingFactor;
1548 
1549         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
1550                   "DeviceInitMmcContext: FDO %p GET CONFIGURATION buffer %p\n",
1551                   DeviceExtension->Device,
1552                   DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer
1553                   ));
1554 
1555         // Update several properties using the retrieved Configuration Data.
1556 
1557         // print all the feature pages (DBG only)
1558         DevicePrintAllFeaturePages(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
1559                                    DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize);
1560 
1561         // if AACS feature exists, enable AACS flag in the driver
1562         header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
1563                                        DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
1564                                        FeatureAACS);
1565         if (header)
1566         {
1567             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
1568                         "DeviceInitMmcContext: Reporting AACS support for device due to "
1569                         "GET CONFIGURATION showing support\n"
1570                         ));
1571             DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = TRUE;
1572         }
1573 
1574 #ifdef ENABLE_AACS_TESTING
1575         DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = TRUE; // just force it true for testing
1576 #endif // ENABLE_AACS_TESTING
1577 
1578         // Check if it's a DVD device
1579         header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
1580                                        DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
1581                                        FeatureDvdRead);
1582         if (header != NULL)
1583         {
1584             DeviceExtension->DeviceAdditionalData.DriveDeviceType = FILE_DEVICE_DVD;
1585         }
1586 
1587         // check if drive is writer
1588         DeviceUpdateMmcWriteCapability(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
1589                                        DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
1590                                        FALSE,                //Check if the drive has the ability to write.
1591                                        (PBOOLEAN)&(DeviceExtension->DeviceAdditionalData.Mmc.IsWriter),
1592                                        &validationSchema,
1593                                        &blockingFactor);
1594 
1595         // check if there is a CSS protected DVD or CPPM-protected DVDAudio media in drive.
1596         header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
1597                                        DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
1598                                        FeatureDvdCSS);
1599 
1600         DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd = (header != NULL) && (header->Current);
1601 
1602         // Flag the StartIo routine to update its state and hook in the error handler
1603         DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
1604         DeviceExtension->DeviceAdditionalData.ErrorHandler = DeviceErrorHandlerForMmc;
1605 
1606         SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
1607 
1608         // Read the CDROM mode sense page to get additional info for raw read requests.
1609         // only valid for MMC devices
1610         DeviceSetRawReadInfo(DeviceExtension);
1611     }
1612 
1613     // Set Read-Only device flag for non-MMC device.
1614     if (!(DeviceExtension->DeviceAdditionalData.Mmc.IsMmc))
1615     {
1616         ULONG  deviceCharacteristics = WdfDeviceGetCharacteristics(DeviceExtension->Device);
1617 
1618         deviceCharacteristics |= FILE_READ_ONLY_DEVICE;
1619 
1620         WdfDeviceSetCharacteristics(DeviceExtension->Device, deviceCharacteristics);
1621 
1622         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
1623                    "DeviceInitMmcContext: FDO %p Device is not an MMC compliant device, so setting "
1624                    "to read-only (legacy) mode",
1625                    DeviceExtension->Device
1626                    ));
1627     }
1628 
1629     // Set DEV_SAFE_START_UNIT flag for newer devices.
1630     if (DeviceExtension->DeviceAdditionalData.DriveDeviceType == FILE_DEVICE_DVD)
1631     {
1632         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
1633                     "DeviceInitMmcContext: DVD Devices require START UNIT\n"));
1634         SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
1635 
1636     }
1637     else if ((DeviceExtension->DeviceDescriptor->BusType != BusTypeScsi)  &&
1638              (DeviceExtension->DeviceDescriptor->BusType != BusTypeAtapi) &&
1639              (DeviceExtension->DeviceDescriptor->BusType != BusTypeUnknown)
1640              )
1641     {
1642         // devices on the newer busses require START_UNIT
1643         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
1644                   "DeviceInitMmcContext: Devices for newer buses require START UNIT\n"));
1645         SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
1646     }
1647 
1648     return status;
1649 }
1650 
1651 
1652 _IRQL_requires_max_(PASSIVE_LEVEL)
1653 ULONG
1654 DeviceGetTimeOutValueFromRegistry()
1655 /*++
1656 
1657 Routine Description:
1658 
1659     get the device time out value from registry
1660 
1661 Arguments:
1662 
1663     None
1664 
1665 Return Value:
1666 
1667     ULONG - value of timeout
1668 
1669 --*/
1670 {
1671     NTSTATUS    status;
1672     WDFKEY      registryKey = NULL;
1673     ULONG        timeOutValue = 0;
1674 
1675     DECLARE_CONST_UNICODE_STRING(registryValueName, L"TimeOutValue");
1676 
1677     PAGED_CODE();
1678 
1679     // open the service key.
1680     status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
1681                                                 KEY_READ,
1682                                                 WDF_NO_OBJECT_ATTRIBUTES,
1683                                                 &registryKey);
1684 
1685     if (NT_SUCCESS(status))
1686     {
1687         status = WdfRegistryQueryULong(registryKey,
1688                                        &registryValueName,
1689                                        &timeOutValue);
1690 
1691         WdfRegistryClose(registryKey);
1692     }
1693 
1694     if (!NT_SUCCESS(status))
1695     {
1696         timeOutValue = 0;
1697     }
1698 
1699     return timeOutValue;
1700 
1701 } // end DeviceGetTimeOutValueFromRegistry()
1702 
1703 
1704 _IRQL_requires_max_(APC_LEVEL)
1705 VOID
1706 DeviceScanSpecialDevices(
1707     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1708     )
1709 /*++
1710 
1711 Routine Description:
1712 
1713     This function checks to see if an SCSI logical unit requires an special
1714     initialization or error processing.
1715 
1716 Arguments:
1717 
1718     Device - device object.
1719 
1720 Return Value:
1721 
1722     None.
1723 
1724 --*/
1725 {
1726 
1727     PAGED_CODE();
1728 
1729     // set our hack flags
1730     DeviceScanForSpecial(DeviceExtension, CdromHackItems, ScanForSpecialHandler);
1731 
1732     //
1733     // All CDRom's can ignore the queue lock failure for power operations
1734     // and do not require handling the SpinUp case (unknown result of sending
1735     // a cdrom a START_UNIT command -- may eject disks?)
1736     //
1737     // We send the stop command mostly to stop outstanding asynch operations
1738     // (like audio playback) from running when the system is powered off.
1739     // Because of this and the unlikely chance that a PLAY command will be
1740     // sent in the window between the STOP and the time the machine powers down
1741     // we don't require queue locks.  This is important because without them
1742     // classpnp's power routines will send the START_STOP_UNIT command to the
1743     // device whether or not it supports locking (atapi does not support locking
1744     // and if we requested them we would end up not stopping audio on atapi
1745     // devices).
1746 //    SET_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_DISABLE_SPIN_UP);
1747 //    SET_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_NO_QUEUE_LOCK);
1748 
1749     if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_TOSHIBA_SD_W1101))
1750     {
1751         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
1752                     "DeviceScanSpecialDevices: Found Toshiba SD-W1101 DVD-RAM "
1753                     "-- This drive will *NOT* support DVD-ROM playback.\n"));
1754     }
1755     else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_HITACHI_GD_2000))
1756     {
1757         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
1758                     "DeviceScanSpecialDevices: Found Hitachi GD-2000\n"));
1759 
1760         // Setup an error handler to spin up the drive when it idles out
1761         // since it seems to like to fail to spin itself back up on its
1762         // own for a REPORT_KEY command.  It may also lose the AGIDs that
1763         // it has given, which will result in DVD playback failures.
1764         // This routine will just do what it can...
1765         DeviceExtension->DeviceAdditionalData.ErrorHandler = DeviceErrorHandlerForHitachiGD2000;
1766 
1767         // this drive may require START_UNIT commands to spin
1768         // the drive up when it's spun itself down.
1769         SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
1770     }
1771     else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_FUJITSU_FMCD_10x))
1772     {
1773         // When Read command is issued to FMCD-101 or FMCD-102 and there is a music
1774         // cd in it. It takes longer time than SCSI_CDROM_TIMEOUT before returning
1775         // error status.
1776         DeviceExtension->TimeOutValue = 20;
1777     }
1778     else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_DEC_RRD))
1779     {
1780         NTSTATUS                   status;
1781         PMODE_PARM_READ_WRITE_DATA modeParameters;
1782         SCSI_REQUEST_BLOCK         srb = {0};
1783         PCDB                       cdb;
1784 
1785         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
1786                     "DeviceScanSpecialDevices:  Found DEC RRD.\n"));
1787 
1788         DeviceExtension->DeviceAdditionalData.IsDecRrd = TRUE;
1789 
1790         // Setup an error handler to reinitialize the cd rom after it is reset?
1791         //
1792         //DeviceExtension->DevInfo->ClassError = DecRrdProcessError;
1793 
1794         // Found a DEC RRD cd-rom.  These devices do not pass MS HCT
1795         // multi-media tests because the DEC firmware modifieds the block
1796         // from the PC-standard 2K to 512.  Change the block transfer size
1797         // back to the PC-standard 2K by using a mode select command.
1798 
1799         modeParameters = ExAllocatePoolWithTag(NonPagedPoolNx,
1800                                                sizeof(MODE_PARM_READ_WRITE_DATA),
1801                                                CDROM_TAG_MODE_DATA);
1802         if (modeParameters == NULL)
1803         {
1804             return;
1805         }
1806 
1807         RtlZeroMemory(modeParameters, sizeof(MODE_PARM_READ_WRITE_DATA));
1808         RtlZeroMemory(&srb,           sizeof(SCSI_REQUEST_BLOCK));
1809 
1810         // Set the block length to 2K.
1811         modeParameters->ParameterListHeader.BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
1812 
1813         // Set block length to 2K (0x0800) in Parameter Block.
1814         modeParameters->ParameterListBlock.BlockLength[0] = 0x00; //MSB
1815         modeParameters->ParameterListBlock.BlockLength[1] = 0x08;
1816         modeParameters->ParameterListBlock.BlockLength[2] = 0x00; //LSB
1817 
1818         // Build the mode select CDB.
1819         srb.CdbLength = 6;
1820         srb.TimeOutValue = DeviceExtension->TimeOutValue;
1821 
1822         cdb = (PCDB)srb.Cdb;
1823         cdb->MODE_SELECT.PFBit               = 1;
1824         cdb->MODE_SELECT.OperationCode       = SCSIOP_MODE_SELECT;
1825         cdb->MODE_SELECT.ParameterListLength = HITACHI_MODE_DATA_SIZE;
1826 
1827         // Send the request to the device.
1828         status = DeviceSendSrbSynchronously(DeviceExtension->Device,
1829                                             &srb,
1830                                             modeParameters,
1831                                             sizeof(MODE_PARM_READ_WRITE_DATA),
1832                                             TRUE,
1833                                             NULL);
1834 
1835         if (!NT_SUCCESS(status))
1836         {
1837             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
1838                         "DeviceScanSpecialDevices: Setting DEC RRD to 2K block"
1839                         "size failed [%x]\n", status));
1840         }
1841 
1842         ExFreePool(modeParameters);
1843     }
1844 
1845     return;
1846 }
1847 
1848 _IRQL_requires_max_(APC_LEVEL)
1849 VOID
1850 ScanForSpecialHandler(
1851     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
1852     _In_ ULONG_PTR               HackFlags
1853     )
1854 {
1855     PAGED_CODE();
1856 
1857     CLEAR_FLAG(HackFlags, CDROM_HACK_INVALID_FLAGS);
1858 
1859     DeviceExtension->DeviceAdditionalData.HackFlags = HackFlags;
1860 
1861     return;
1862 }
1863 
1864 
1865 _IRQL_requires_max_(PASSIVE_LEVEL)
1866 NTSTATUS
1867 DeviceCacheDeviceInquiryData(
1868     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1869     )
1870 /*++
1871 
1872 Routine Description:
1873 
1874     get inquiry data from device and cache it into device extension
1875     The first INQUIRY command sent is with 0x24 bytes required data,
1876     as ATAport driver always sends this to enumerate devices and 0x24
1877     bytes is the minimum data device should return by spec.
1878 
1879 Arguments:
1880 
1881     DeviceExtension - device extension.
1882 
1883 Return Value:
1884 
1885     NTSTATUS.
1886 
1887 --*/
1888 {
1889     NTSTATUS            status = STATUS_SUCCESS;
1890     SCSI_REQUEST_BLOCK  srb = {0};
1891     PCDB                cdb = (PCDB)(&srb.Cdb);
1892     PINQUIRYDATA        tmpInquiry = NULL;
1893 
1894     // by spec, device should return at least 36 bytes.
1895     ULONG               requestedInquiryTransferBytes = MINIMUM_CDROM_INQUIRY_SIZE;
1896     BOOLEAN             needResendCommand = TRUE;
1897     BOOLEAN             portDriverHack = FALSE;
1898 
1899     // this ensures that the strings vendorID, productID, and firmwareRevision
1900     // are all available in the inquiry data.  In addition, MMC spec requires
1901     // all type 5 devices to have minimum 36 bytes of inquiry.
1902     static const UCHAR minInquiryAdditionalLength =
1903                                     MINIMUM_CDROM_INQUIRY_SIZE -
1904                                     RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
1905 
1906     C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength) <= 8 );
1907     C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, ProductRevisionLevel) == MINIMUM_CDROM_INQUIRY_SIZE );
1908 
1909     PAGED_CODE();
1910 
1911     // short-circuit here for if already cached for this device
1912     // required to avoid use of scratch buffer after initialization
1913     // of MCN code.
1914     if (DeviceExtension->DeviceAdditionalData.CachedInquiryData != NULL)
1915     {
1916         NT_ASSERT(DeviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount != 0);
1917         return STATUS_SUCCESS;
1918     }
1919 
1920     // 1. retrieve the inquiry data length
1921 
1922     // 1.1 allocate inquiry data buffer
1923     tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
1924                                        requestedInquiryTransferBytes,
1925                                        CDROM_TAG_INQUIRY);
1926     if (tmpInquiry == NULL)
1927     {
1928         status = STATUS_INSUFFICIENT_RESOURCES;
1929     }
1930 
1931     // 1.2 send INQUIRY command
1932     if (NT_SUCCESS(status))
1933     {
1934         srb.CdbLength = 6;
1935         cdb->AsByte[0] = SCSIOP_INQUIRY;
1936         cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
1937         cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
1938 
1939         status = DeviceSendSrbSynchronously(DeviceExtension->Device,
1940                                             &srb,
1941                                             tmpInquiry,
1942                                             requestedInquiryTransferBytes,
1943                                             FALSE,
1944                                             NULL);
1945     }
1946 
1947     // 1.3 get required data length
1948     if (NT_SUCCESS(status))
1949     {
1950         if ((requestedInquiryTransferBytes == srb.DataTransferLength) &&
1951             (requestedInquiryTransferBytes == (tmpInquiry->AdditionalLength +
1952                                                RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength))) )
1953         {
1954             // device has only 36 bytes of INQUIRY data. do not need to resend the command.
1955             needResendCommand = FALSE;
1956         }
1957         else
1958         {
1959             // workaround an ATAPI.SYS bug where additional length field is set to zero
1960             if (tmpInquiry->AdditionalLength == 0)
1961             {
1962                 tmpInquiry->AdditionalLength = minInquiryAdditionalLength;
1963                 portDriverHack = TRUE;
1964             }
1965 
1966             requestedInquiryTransferBytes =
1967                                 tmpInquiry->AdditionalLength +
1968                                 RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
1969 
1970             if (requestedInquiryTransferBytes >= MINIMUM_CDROM_INQUIRY_SIZE)
1971             {
1972                 needResendCommand = TRUE;
1973             }
1974             else
1975             {
1976                 needResendCommand = FALSE;
1977                 //Length is small than minimum length, error out.
1978                 status = STATUS_DEVICE_PROTOCOL_ERROR;
1979             }
1980         }
1981     }
1982 
1983     // 2. retrieve the inquiry data if still needed.
1984 
1985     // 2.1 Clean up.
1986     if (NT_SUCCESS(status) && needResendCommand)
1987     {
1988         FREE_POOL(tmpInquiry);
1989         RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
1990 
1991         tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
1992                                            requestedInquiryTransferBytes,
1993                                            CDROM_TAG_INQUIRY);
1994         if (tmpInquiry == NULL)
1995         {
1996             status = STATUS_INSUFFICIENT_RESOURCES;
1997         }
1998     }
1999 
2000     // 2.2 resend INQUIRY command
2001     if (NT_SUCCESS(status) && needResendCommand)
2002     {
2003         srb.CdbLength = 6;
2004         cdb->AsByte[0] = SCSIOP_INQUIRY;
2005         cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
2006         cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
2007 
2008         status = DeviceSendSrbSynchronously( DeviceExtension->Device,
2009                                              &srb,
2010                                              tmpInquiry,
2011                                              requestedInquiryTransferBytes,
2012                                              FALSE,
2013                                              NULL);
2014 
2015         if (!NT_SUCCESS(status))
2016         {
2017             // Workaround for drive reports that it has more INQUIRY data than reality.
2018             if ((srb.SrbStatus == SRB_STATUS_DATA_OVERRUN) &&
2019                 (srb.DataTransferLength < requestedInquiryTransferBytes) &&
2020                 (srb.DataTransferLength >= MINIMUM_CDROM_INQUIRY_SIZE))
2021             {
2022                 //Port driver says buffer size mismatch (buffer underrun),
2023                 //retry with the real buffer size it could return.
2024                 requestedInquiryTransferBytes = srb.DataTransferLength;
2025 
2026                 FREE_POOL(tmpInquiry);
2027                 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
2028 
2029                 tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
2030                                                    requestedInquiryTransferBytes,
2031                                                    CDROM_TAG_INQUIRY);
2032                 if (tmpInquiry == NULL)
2033                 {
2034                     status = STATUS_INSUFFICIENT_RESOURCES;
2035                 }
2036                 else
2037                 {
2038                     srb.CdbLength = 6;
2039                     cdb->AsByte[0] = SCSIOP_INQUIRY;
2040                     cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
2041                     cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
2042 
2043                     status = DeviceSendSrbSynchronously(DeviceExtension->Device,
2044                                                         &srb,
2045                                                         tmpInquiry,
2046                                                         requestedInquiryTransferBytes,
2047                                                         FALSE,
2048                                                         NULL);
2049                 }
2050             }
2051         }
2052 
2053         //Check the transferred data length for safe.
2054         if (NT_SUCCESS(status))
2055         {
2056             requestedInquiryTransferBytes = srb.DataTransferLength;
2057 
2058             if (requestedInquiryTransferBytes < MINIMUM_CDROM_INQUIRY_SIZE)
2059             {
2060                 // should never occur
2061                 status = STATUS_DEVICE_PROTOCOL_ERROR;
2062             }
2063         }
2064 
2065         // ensure we got some non-zero data....
2066         // This is done so we don't accidentally work around the
2067         // ATAPI.SYS bug when no data was transferred.
2068         if (NT_SUCCESS(status) && portDriverHack)
2069         {
2070             PULONG  tmp = (PULONG)tmpInquiry;
2071             ULONG   i = MINIMUM_CDROM_INQUIRY_SIZE / sizeof(ULONG);
2072             C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, ProductRevisionLevel) % sizeof(ULONG) == 0 );
2073 
2074             // wouldn't you know it -- there is no RtlIsMemoryZero() function; Make one up.
2075             for ( ; i != 0; i--)
2076             {
2077                 if (*tmp != 0)
2078                 {
2079                     break; // out of this inner FOR loop -- guarantees 'i != 0'
2080                 }
2081                 tmp++;
2082             }
2083 
2084             if (i == 0) // all loop'd successfully
2085             {
2086                 // should never occur to successfully get all zero'd data
2087                 status = STATUS_DEVICE_PROTOCOL_ERROR;
2088             }
2089         }
2090     }
2091 
2092     // if everything succeeded, then (and only then) modify the device extension
2093     if (NT_SUCCESS(status))
2094     {
2095         DeviceExtension->DeviceAdditionalData.CachedInquiryData = tmpInquiry;
2096         DeviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount = requestedInquiryTransferBytes;
2097     }
2098     else
2099     {
2100         FREE_POOL(tmpInquiry);
2101     }
2102 
2103     return status;
2104 }
2105 
2106 _IRQL_requires_max_(PASSIVE_LEVEL)
2107 NTSTATUS
2108 DeviceGetMmcSupportInfo(
2109     _In_  PCDROM_DEVICE_EXTENSION   DeviceExtension,
2110     _Out_ PBOOLEAN                  IsMmcDevice
2111     )
2112 /*++
2113 
2114 Routine Description:
2115 
2116     check if the device is MMC capable.
2117 
2118 Arguments:
2119 
2120     DeviceExtension - device extension.
2121 
2122 Return Value:
2123 
2124     NTSTATUS.
2125     IsMmcDevice - TRUE (MMC capable); FALSE (not MMC device)
2126 
2127 --*/
2128 {
2129     NTSTATUS    status;
2130     ULONG       size;
2131     ULONG       previouslyFailed;
2132 
2133     PAGED_CODE();
2134 
2135     *IsMmcDevice  = FALSE;
2136 
2137     // read the registry in case the drive failed previously,
2138     // and a timeout is occurring.
2139     previouslyFailed = FALSE;
2140     DeviceGetParameter(DeviceExtension,
2141                        CDROM_SUBKEY_NAME,
2142                        CDROM_NON_MMC_DRIVE_NAME,
2143                        &previouslyFailed);
2144 
2145     if (previouslyFailed)
2146     {
2147         SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT);
2148     }
2149 
2150     // read from the registry in case the drive reports bad profile lengths
2151     previouslyFailed = FALSE;
2152     DeviceGetParameter(DeviceExtension,
2153                        CDROM_SUBKEY_NAME,
2154                        CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE,
2155                        &previouslyFailed);
2156 
2157     if (previouslyFailed)
2158     {
2159         SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_VENDOR_PROFILES);
2160     }
2161 
2162     // check for the ProfileList feature to determine if the drive is MMC compliant
2163     // and set the "size" local variable to total GetConfig data size available.
2164     // NOTE: This will exit this function in some error paths.
2165     {
2166         GET_CONFIGURATION_HEADER    localHeader = {0};
2167         ULONG                       usable = 0;
2168 
2169         status = DeviceGetConfiguration(DeviceExtension->Device,
2170                                         &localHeader,
2171                                         sizeof(GET_CONFIGURATION_HEADER),
2172                                         &usable,
2173                                         FeatureProfileList,
2174                                         SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
2175 
2176         if (status == STATUS_INVALID_DEVICE_REQUEST ||
2177             status == STATUS_NO_MEDIA_IN_DEVICE     ||
2178             status == STATUS_IO_DEVICE_ERROR        ||
2179             status == STATUS_IO_TIMEOUT)
2180         {
2181             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2182                        "GetConfiguration Failed (%x), device %p not mmc-compliant\n",
2183                        status, DeviceExtension->DeviceObject
2184                        ));
2185 
2186             previouslyFailed = TRUE;
2187             DeviceSetParameter( DeviceExtension,
2188                                 CDROM_SUBKEY_NAME,
2189                                 CDROM_NON_MMC_DRIVE_NAME,
2190                                 previouslyFailed);
2191 
2192             return STATUS_SUCCESS;
2193         }
2194         else if (!NT_SUCCESS(status))
2195         {
2196             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2197                        "GetConfiguration Failed, status %x -- defaulting to -ROM\n",
2198                        status));
2199 
2200             return STATUS_SUCCESS;
2201         }
2202         else if (usable < sizeof(GET_CONFIGURATION_HEADER))
2203         {
2204             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2205                        "GetConfiguration Failed, returned only %x bytes!\n", usable));
2206             previouslyFailed = TRUE;
2207             DeviceSetParameter( DeviceExtension,
2208                                 CDROM_SUBKEY_NAME,
2209                                 CDROM_NON_MMC_DRIVE_NAME,
2210                                 previouslyFailed);
2211 
2212             return STATUS_SUCCESS;
2213         }
2214 
2215         size = (localHeader.DataLength[0] << 24) |
2216                (localHeader.DataLength[1] << 16) |
2217                (localHeader.DataLength[2] <<  8) |
2218                (localHeader.DataLength[3] <<  0) ;
2219 
2220 
2221         if ((size <= 4) || (size + 4 < size))
2222         {
2223             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2224                        "GetConfiguration Failed, claims MMC support but doesn't "
2225                        "correctly return config length! (%x)\n",
2226                        size
2227                        ));
2228             previouslyFailed = TRUE;
2229             DeviceSetParameter( DeviceExtension,
2230                                 CDROM_SUBKEY_NAME,
2231                                 CDROM_NON_MMC_DRIVE_NAME,
2232                                 previouslyFailed);
2233 
2234             return STATUS_SUCCESS;
2235         }
2236         else if ((size % 4) != 0)
2237         {
2238             if ((size % 2) != 0)
2239             {
2240                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2241                            "GetConfiguration Failed, returned odd number of bytes %x!\n",
2242                            size
2243                            ));
2244                 previouslyFailed = TRUE;
2245                 DeviceSetParameter( DeviceExtension,
2246                                     CDROM_SUBKEY_NAME,
2247                                     CDROM_NON_MMC_DRIVE_NAME,
2248                                     previouslyFailed);
2249 
2250                 return STATUS_SUCCESS;
2251             }
2252             else
2253             {
2254                 if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_VENDOR_PROFILES))
2255                 {
2256                     // we've already caught this and ASSERT'd once, so don't do it again
2257                 }
2258                 else
2259                 {
2260                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2261                                "GetConfiguration returned a size that is not per spec (%x bytes), this is probably because of a vendor specific data header with a size not divisible by 4.\n",
2262                                size
2263                                ));
2264                     previouslyFailed = TRUE;
2265                     DeviceSetParameter(DeviceExtension,
2266                                        CDROM_SUBKEY_NAME,
2267                                        CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE,
2268                                        previouslyFailed);
2269                 }
2270             }
2271         }
2272 
2273         size += 4; // sizeof the datalength fields
2274     }
2275 
2276     *IsMmcDevice = TRUE;
2277 
2278     // This code doesn't handle total get config size over 64k
2279     NT_ASSERT( size <= MAXUSHORT );
2280 
2281     // Check for SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE support in the device.
2282     // NOTE: This will exit this function in some error paths.
2283     {
2284         ULONG   featureSize = sizeof(GET_CONFIGURATION_HEADER)+sizeof(FEATURE_HEADER);
2285         ULONG   usable = 0;
2286 
2287         PGET_CONFIGURATION_HEADER configBuffer = ExAllocatePoolWithTag(
2288                                                             NonPagedPoolNx,
2289                                                             featureSize,
2290                                                             CDROM_TAG_GET_CONFIG);
2291 
2292         if (configBuffer == NULL)
2293         {
2294             return STATUS_INSUFFICIENT_RESOURCES;
2295         }
2296 
2297         // read the registry in case the drive failed previously,
2298         // and a timeout is occurring.
2299         previouslyFailed = FALSE;
2300         DeviceGetParameter( DeviceExtension,
2301                             CDROM_SUBKEY_NAME,
2302                             CDROM_TYPE_ONE_GET_CONFIG_NAME,
2303                             &previouslyFailed);
2304 
2305         if (previouslyFailed)
2306         {
2307             SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
2308             FREE_POOL(configBuffer);
2309             return STATUS_SUCCESS;
2310         }
2311 
2312         // Get only the config and feature header
2313         status = DeviceGetConfiguration(DeviceExtension->Device,
2314                                         configBuffer,
2315                                         featureSize,
2316                                         &usable,
2317                                         FeatureProfileList,
2318                                         SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE);
2319 
2320         if (!NT_SUCCESS(status) || (usable < featureSize))
2321         {
2322             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2323                        "Type One GetConfiguration Failed. Usable buffer size: %d\n", usable));
2324             previouslyFailed = TRUE;
2325         }
2326         else
2327         {
2328             PFEATURE_HEADER featureHeader;
2329             ULONG           totalAvailableBytes = 0;
2330             ULONG           expectedAvailableBytes = 0;
2331 
2332             REVERSE_BYTES(&totalAvailableBytes, configBuffer->DataLength);
2333             totalAvailableBytes += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
2334 
2335             featureHeader = (PFEATURE_HEADER) ((PUCHAR)configBuffer + sizeof(GET_CONFIGURATION_HEADER));
2336             expectedAvailableBytes = sizeof(GET_CONFIGURATION_HEADER) +
2337                                      sizeof(FEATURE_HEADER) +
2338                                      featureHeader->AdditionalLength;
2339 
2340             if (totalAvailableBytes > expectedAvailableBytes)
2341             {
2342                 // Device is returning more than required size. Most likely the device
2343                 // is returning TYPE ALL data. Set the flag to use TYPE ALL for TYPE ONE
2344                 // requets
2345                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2346                            "Type One GetConfiguration Failed. "
2347                            "Device returned %d bytes instead of %d bytes\n",
2348                            size, featureSize));
2349 
2350                 previouslyFailed = TRUE;
2351             }
2352         }
2353 
2354         FREE_POOL(configBuffer);
2355 
2356         if (previouslyFailed == TRUE)
2357         {
2358             DeviceSetParameter( DeviceExtension,
2359                                 CDROM_SUBKEY_NAME,
2360                                 CDROM_TYPE_ONE_GET_CONFIG_NAME,
2361                                 previouslyFailed);
2362 
2363             SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
2364         }
2365     }
2366 
2367     return status;
2368 }
2369 
2370 
2371 _IRQL_requires_max_(APC_LEVEL)
2372 NTSTATUS
2373 DeviceSetRawReadInfo(
2374     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
2375     )
2376 /*++
2377 
2378 Routine Description:
2379 
2380     This routine reads the CDROM capabilities mode page and save information
2381     in the device extension needed for raw reads.
2382     NOTE: this function is only valid for MMC device
2383 
2384 Arguments:
2385 
2386     DeviceExtension - device context
2387 
2388 Return Value:
2389 
2390     NTSTATUS
2391 
2392 --*/
2393 {
2394     NTSTATUS    status = STATUS_SUCCESS;
2395     PUCHAR      buffer = NULL;
2396     ULONG       count = 0;
2397 
2398     PAGED_CODE();
2399 
2400     // Check whether the device can return C2 error flag bits and the block
2401     // error byte.  If so, save this info and fill in appropriate flag during
2402     // raw read requests.
2403 
2404     // Start by checking the GET_CONFIGURATION data
2405     {
2406         PFEATURE_DATA_CD_READ   cdReadHeader = NULL;
2407         ULONG                   additionalLength = sizeof(FEATURE_DATA_CD_READ) - sizeof(FEATURE_HEADER);
2408 
2409         cdReadHeader = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
2410                                             DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
2411                                             FeatureCdRead);
2412 
2413         if ((cdReadHeader != NULL) &&
2414             (cdReadHeader->Header.AdditionalLength >= additionalLength) &&
2415             (cdReadHeader->C2ErrorData)
2416             )
2417         {
2418             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2419                       "DeviceSetRawReadInfo: FDO %p GET_CONFIG shows ability to read C2 error bits\n",
2420                       DeviceExtension->DeviceObject
2421                       ));
2422             DeviceExtension->DeviceAdditionalData.Mmc.ReadCdC2Pointers = TRUE;    // Device returns C2 error info.
2423         }
2424     }
2425 
2426     // Unfortunately, the only way to check for the ability to read R-W subcode
2427     // information is via MODE_SENSE.  Do so here, and check the C2 bit as well
2428     // in case the drive has a firmware bug where it fails to report this ability
2429     // in GET_CONFIG (which some drives do).
2430     for (count = 0; count < 6; count++)
2431     {
2432         SCSI_REQUEST_BLOCK  srb = {0};
2433         PCDB                cdb = (PCDB)srb.Cdb;
2434         ULONG               bufferLength = 0;
2435 
2436         // Build the MODE SENSE CDB.  Try 10-byte CDB first.
2437         if ((count/3) == 0)
2438         {
2439             bufferLength = sizeof(CDVD_CAPABILITIES_PAGE)  +
2440                            sizeof(MODE_PARAMETER_HEADER10) +
2441                            sizeof(MODE_PARAMETER_BLOCK);
2442 
2443             cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
2444             cdb->MODE_SENSE10.Dbd = 1;
2445             cdb->MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES;
2446             cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferLength >> 8);
2447             cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferLength >> 0);
2448             srb.CdbLength = 10;
2449         }
2450         else
2451         {
2452             bufferLength = sizeof(CDVD_CAPABILITIES_PAGE) +
2453                            sizeof(MODE_PARAMETER_HEADER)  +
2454                            sizeof(MODE_PARAMETER_BLOCK);
2455 
2456             cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
2457             cdb->MODE_SENSE.Dbd = 1;
2458             cdb->MODE_SENSE.PageCode = MODE_PAGE_CAPABILITIES;
2459             cdb->MODE_SENSE.AllocationLength = (UCHAR)bufferLength;
2460             srb.CdbLength = 6;
2461         }
2462 
2463         // Set timeout value from device extension.
2464         srb.TimeOutValue = DeviceExtension->TimeOutValue;
2465 
2466         buffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CDROM_TAG_MODE_DATA);
2467 
2468         if (buffer == NULL)
2469         {
2470             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
2471                       "DeviceSetRawReadInfo: cannot allocate "
2472                       "buffer, so not setting raw read info for FDO %p\n",
2473                       DeviceExtension->DeviceObject
2474                       ));
2475             status = STATUS_INSUFFICIENT_RESOURCES;
2476             goto FnExit;
2477         }
2478 
2479         RtlZeroMemory(buffer, bufferLength);
2480 
2481         status = DeviceSendSrbSynchronously(DeviceExtension->Device,
2482                                             &srb,
2483                                             buffer,
2484                                             bufferLength,
2485                                             FALSE,
2486                                             NULL);
2487 
2488         if (NT_SUCCESS(status) ||
2489             (status == STATUS_DATA_OVERRUN) ||
2490             (status == STATUS_BUFFER_OVERFLOW))
2491         {
2492             PCDVD_CAPABILITIES_PAGE capabilities = NULL;
2493 
2494             // determine where the capabilities page really is
2495             if ((count/3) == 0)
2496             {
2497                 PMODE_PARAMETER_HEADER10 p = (PMODE_PARAMETER_HEADER10)buffer;
2498                 capabilities = (PCDVD_CAPABILITIES_PAGE)(buffer +
2499                                                          sizeof(MODE_PARAMETER_HEADER10) +
2500                                                          (p->BlockDescriptorLength[0] * 256) +
2501                                                          p->BlockDescriptorLength[1]);
2502             }
2503             else
2504             {
2505                 PMODE_PARAMETER_HEADER p = (PMODE_PARAMETER_HEADER)buffer;
2506                 capabilities = (PCDVD_CAPABILITIES_PAGE)(buffer +
2507                                                          sizeof(MODE_PARAMETER_HEADER) +
2508                                                          p->BlockDescriptorLength);
2509             }
2510 
2511             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2512                       "DeviceSetRawReadInfo: FDO %p CDVD Capabilities buffer %p\n",
2513                       DeviceExtension->DeviceObject,
2514                       buffer
2515                       ));
2516 
2517             if (capabilities->PageCode == MODE_PAGE_CAPABILITIES)
2518             {
2519                 if (capabilities->C2Pointers)
2520                 {
2521                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2522                               "DeviceSetRawReadInfo: FDO %p supports C2 error bits in READ_CD command\n",
2523                               DeviceExtension->DeviceObject
2524                               ));
2525                     DeviceExtension->DeviceAdditionalData.Mmc.ReadCdC2Pointers = TRUE;
2526                 }
2527 
2528                 if (capabilities->RWSupported)
2529                 {
2530                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2531                               "DeviceSetRawReadInfo: FDO %p supports raw subcode in READ_CD command\n",
2532                               DeviceExtension->DeviceObject
2533                               ));
2534                     DeviceExtension->DeviceAdditionalData.Mmc.ReadCdSubCode = TRUE;
2535                 }
2536 
2537                 break;
2538             }
2539         }
2540 
2541         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
2542                   "DeviceSetRawReadInfo: FDO %p failed %x byte mode sense, status %x\n",
2543                   DeviceExtension->DeviceObject,
2544                   (((count/3) == 0) ? 10 : 6),
2545                   status
2546                   ));
2547 
2548         FREE_POOL(buffer);
2549     }
2550 
2551     if (count == 6)
2552     {
2553         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
2554                   "DeviceSetRawReadInfo: FDO %p couldn't get mode sense data\n",
2555                   DeviceExtension->DeviceObject
2556                   ));
2557     }
2558 
2559 FnExit:
2560 
2561     if (buffer)
2562     {
2563         FREE_POOL(buffer);
2564     }
2565 
2566     return status;
2567 }
2568 
2569 
2570 _IRQL_requires_max_(APC_LEVEL)
2571 NTSTATUS
2572 DeviceInitializeDvd(
2573     _In_ WDFDEVICE Device
2574     )
2575 /*++
2576 
2577 Routine Description:
2578 
2579     This routine sets the region of DVD drive
2580     NOTE: this routine uses ScratchBuffer, it must be called after ScratchBuffer allocated.
2581 
2582 Arguments:
2583 
2584     Device - device object
2585 
2586 Return Value:
2587 
2588     NTSTATUS
2589 
2590 --*/
2591 
2592 {
2593     NTSTATUS                    status = STATUS_SUCCESS;
2594     PCDROM_DEVICE_EXTENSION     deviceExtension;
2595     PDVD_COPY_PROTECT_KEY       copyProtectKey = NULL;
2596     PDVD_RPC_KEY                rpcKey = NULL;
2597     ULONG                       bufferLen = 0;
2598     size_t                      bytesReturned;
2599 
2600     PAGED_CODE();
2601 
2602     deviceExtension = DeviceGetExtension(Device);
2603 
2604     // check to see if we have a DVD device
2605     if (deviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
2606     {
2607         return STATUS_SUCCESS;
2608     }
2609 
2610     // we got a DVD drive.
2611     bufferLen = DVD_RPC_KEY_LENGTH;
2612     copyProtectKey = (PDVD_COPY_PROTECT_KEY)ExAllocatePoolWithTag(PagedPool,
2613                                                                   bufferLen,
2614                                                                   DVD_TAG_RPC2_CHECK);
2615 
2616     if (copyProtectKey == NULL)
2617     {
2618         return STATUS_INSUFFICIENT_RESOURCES;
2619     }
2620 
2621     // get the device region
2622     RtlZeroMemory (copyProtectKey, bufferLen);
2623     copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
2624     copyProtectKey->KeyType = DvdGetRpcKey;
2625 
2626     // perform IOCTL_DVD_READ_KEY
2627     status = DvdStartSessionReadKey(deviceExtension,
2628                                     IOCTL_DVD_READ_KEY,
2629                                     NULL,
2630                                     copyProtectKey,
2631                                     DVD_RPC_KEY_LENGTH,
2632                                     copyProtectKey,
2633                                     DVD_RPC_KEY_LENGTH,
2634                                     &bytesReturned);
2635 
2636     if (NT_SUCCESS(status))
2637     {
2638         rpcKey = (PDVD_RPC_KEY)copyProtectKey->KeyData;
2639 
2640         // TypeCode of zero means that no region has been set.
2641         if (rpcKey->TypeCode == 0)
2642         {
2643             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP,
2644                         "DVD Initialize (%p): must choose DVD region\n",
2645                         Device));
2646             deviceExtension->DeviceAdditionalData.PickDvdRegion = 1;
2647 
2648             // set the device region code to be the same as region code on media.
2649             if (deviceExtension->DeviceAdditionalData.Mmc.IsCssDvd)
2650             {
2651                 DevicePickDvdRegion(Device);
2652             }
2653         }
2654     }
2655 
2656     FREE_POOL(copyProtectKey);
2657 
2658     // return status of IOCTL_DVD_READ_KEY will be ignored.
2659     return STATUS_SUCCESS;
2660 }
2661 
2662 
2663 #if (NTDDI_VERSION >= NTDDI_WIN8)
2664 _IRQL_requires_max_(PASSIVE_LEVEL)
2665 NTSTATUS
2666 DeviceIsPortable(
2667     _In_  PCDROM_DEVICE_EXTENSION   DeviceExtension,
2668     _Out_ PBOOLEAN                  IsPortable
2669     )
2670 /*++
2671 
2672 Routine Description:
2673 
2674     This routine checks if the volume is on a portable storage device.
2675 
2676 Arguments:
2677 
2678     DeviceExtension - device context
2679     IsPortable - device is portable
2680 
2681 Return Value:
2682 
2683     NTSTATUS.
2684 
2685 --*/
2686 
2687 {
2688     DEVPROP_BOOLEAN isInternal = DEVPROP_FALSE;
2689     BOOLEAN         isPortable = FALSE;
2690     ULONG           size       = 0;
2691     NTSTATUS        status     = STATUS_SUCCESS;
2692     DEVPROPTYPE     type       = DEVPROP_TYPE_EMPTY;
2693 
2694     PAGED_CODE();
2695 
2696     *IsPortable = FALSE;
2697 
2698     // Check to see if the underlying device object is in local machine container
2699     status = IoGetDevicePropertyData(DeviceExtension->LowerPdo,
2700                                      &DEVPKEY_Device_InLocalMachineContainer,
2701                                      0,
2702                                      0,
2703                                      sizeof(isInternal),
2704                                      &isInternal,
2705                                      &size,
2706                                      &type);
2707 
2708     if (!NT_SUCCESS(status))
2709     {
2710         goto Cleanup;
2711     }
2712 
2713     NT_ASSERT(size == sizeof(isInternal));
2714     NT_ASSERT(type == DEVPROP_TYPE_BOOLEAN);
2715 
2716     // Volume is hot-pluggable if the disk pdo container id differs from that of root device
2717     if (isInternal == DEVPROP_TRUE)
2718     {
2719         goto Cleanup;
2720     }
2721 
2722     isPortable = TRUE;
2723 
2724     // Examine the bus type to  ensure that this really is a fixed device
2725     if (DeviceExtension->DeviceDescriptor->BusType == BusTypeFibre ||
2726         DeviceExtension->DeviceDescriptor->BusType == BusTypeiScsi ||
2727         DeviceExtension->DeviceDescriptor->BusType == BusTypeRAID)
2728     {
2729         isPortable = FALSE;
2730     }
2731 
2732     *IsPortable = isPortable;
2733 
2734 Cleanup:
2735 
2736     return status;
2737 }
2738 #endif
2739 
2740 
2741 #pragma warning(pop) // un-sets any local warning changes
2742 
2743