xref: /reactos/drivers/storage/class/cdrom/common.c (revision 1734f297)
1 /*++
2 
3 Copyright (C) Microsoft Corporation. All rights reserved.
4 
5 Module Name:
6 
7     common.c
8 
9 Abstract:
10 
11     shared private routines for cdrom.sys
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 
29 #include "cdrom.h"
30 #include "scratch.h"
31 
32 
33 #ifdef DEBUG_USE_WPP
34 #include "common.tmh"
35 #endif
36 
37 #ifdef ALLOC_PRAGMA
38 
39 #pragma alloc_text(PAGE, DeviceGetParameter)
40 #pragma alloc_text(PAGE, DeviceSetParameter)
41 #pragma alloc_text(PAGE, DeviceSendSrbSynchronously)
42 #pragma alloc_text(PAGE, DevicePickDvdRegion)
43 #pragma alloc_text(PAGE, StringsAreMatched)
44 #pragma alloc_text(PAGE, PerformEjectionControl)
45 #pragma alloc_text(PAGE, DeviceFindFeaturePage)
46 #pragma alloc_text(PAGE, DevicePrintAllFeaturePages)
47 #pragma alloc_text(PAGE, DeviceRegisterInterface)
48 #pragma alloc_text(PAGE, DeviceRestoreDefaultSpeed)
49 #pragma alloc_text(PAGE, DeviceSendRequestSynchronously)
50 #pragma alloc_text(PAGE, MediaReadCapacity)
51 #pragma alloc_text(PAGE, MediaReadCapacityDataInterpret)
52 #pragma alloc_text(PAGE, DeviceRetrieveModeSenseUsingScratch)
53 #pragma alloc_text(PAGE, ModeSenseFindSpecificPage)
54 #pragma alloc_text(PAGE, DeviceUnlockExclusive)
55 
56 #endif
57 
58 LPCSTR LockTypeStrings[] = {"Simple",
59                             "Secure",
60                             "Internal"
61                             };
62 
63 VOID
64 RequestSetReceivedTime(
65     _In_ WDFREQUEST Request
66     )
67 {
68     PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
69     LARGE_INTEGER          temp;
70 
71     KeQueryTickCount(&temp);
72 
73     requestContext->TimeReceived = temp;
74 
75     return;
76 }
77 
78 VOID
79 RequestSetSentTime(
80     _In_ WDFREQUEST Request
81     )
82 {
83     PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
84     LARGE_INTEGER          temp;
85 
86     KeQueryTickCount(&temp);
87 
88     if (requestContext->TimeSentDownFirstTime.QuadPart == 0)
89     {
90         requestContext->TimeSentDownFirstTime = temp;
91     }
92 
93     requestContext->TimeSentDownLasttTime = temp;
94 
95     if (requestContext->OriginalRequest != NULL)
96     {
97         PCDROM_REQUEST_CONTEXT originalRequestContext = RequestGetContext(requestContext->OriginalRequest);
98 
99         if (originalRequestContext->TimeSentDownFirstTime.QuadPart == 0)
100         {
101             originalRequestContext->TimeSentDownFirstTime = temp;
102         }
103 
104         originalRequestContext->TimeSentDownLasttTime = temp;
105     }
106 
107     return;
108 }
109 
110 VOID
111 RequestClearSendTime(
112     _In_ WDFREQUEST Request
113     )
114 /*
115 Routine Description:
116 
117     This function is used to clean SentTime fields in reusable request context.
118 
119 Arguments:
120     Request -
121 
122 Return Value:
123     N/A
124 
125 */
126 {
127     PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
128 
129     requestContext->TimeSentDownFirstTime.QuadPart = 0;
130     requestContext->TimeSentDownLasttTime.QuadPart = 0;
131 
132     return;
133 }
134 
135 _IRQL_requires_max_(PASSIVE_LEVEL)
136 VOID
137 DeviceGetParameter(
138     _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
139     _In_opt_ PWSTR                  SubkeyName,
140     _In_ PWSTR                      ParameterName,
141     _Inout_ PULONG                  ParameterValue  // also default value
142     )
143 /*++
144 Routine Description:
145 
146     retrieve device parameter from registry.
147 
148 Arguments:
149 
150     DeviceExtension - device context.
151 
152     SubkeyName - name of subkey
153 
154     ParameterName - the registry parameter to be retrieved
155 
156 Return Value:
157 
158     ParameterValue - registry value retrieved
159 
160 --*/
161 {
162     NTSTATUS        status;
163     WDFKEY          rootKey = NULL;
164     WDFKEY          subKey = NULL;
165     UNICODE_STRING  registrySubKeyName;
166     UNICODE_STRING  registryValueName;
167     ULONG           defaultParameterValue;
168 
169     PAGED_CODE();
170 
171     RtlInitUnicodeString(&registryValueName, ParameterName);
172 
173     if (SubkeyName != NULL)
174     {
175         RtlInitUnicodeString(&registrySubKeyName, SubkeyName);
176     }
177 
178     // open the hardware key
179     status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
180                                       PLUGPLAY_REGKEY_DEVICE,
181                                       KEY_READ,
182                                       WDF_NO_OBJECT_ATTRIBUTES,
183                                       &rootKey);
184 
185     // open the sub key
186     if (NT_SUCCESS(status) && (SubkeyName != NULL))
187     {
188         status = WdfRegistryOpenKey(rootKey,
189                                     &registrySubKeyName,
190                                     KEY_READ,
191                                     WDF_NO_OBJECT_ATTRIBUTES,
192                                     &subKey);
193 
194         if (!NT_SUCCESS(status))
195         {
196             WdfRegistryClose(rootKey);
197             rootKey = NULL;
198         }
199     }
200 
201     if (NT_SUCCESS(status) && (rootKey != NULL))
202     {
203         defaultParameterValue = *ParameterValue;
204 
205         status = WdfRegistryQueryULong((subKey != NULL) ? subKey : rootKey,
206                                        &registryValueName,
207                                        ParameterValue);
208 
209         if (!NT_SUCCESS(status))
210         {
211             *ParameterValue = defaultParameterValue; // use default value
212         }
213     }
214 
215     // close what we open
216     if (subKey != NULL)
217     {
218         WdfRegistryClose(subKey);
219         subKey = NULL;
220     }
221 
222     if (rootKey != NULL)
223     {
224         WdfRegistryClose(rootKey);
225         rootKey = NULL;
226     }
227 
228     // Windows 2000 SP3 uses the driver-specific key, so look in there
229     if (!NT_SUCCESS(status))
230     {
231         // open the software key
232         status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
233                                           PLUGPLAY_REGKEY_DRIVER,
234                                           KEY_READ,
235                                           WDF_NO_OBJECT_ATTRIBUTES,
236                                           &rootKey);
237 
238         // open the sub key
239         if (NT_SUCCESS(status) && (SubkeyName != NULL))
240         {
241             status = WdfRegistryOpenKey(rootKey,
242                                         &registrySubKeyName,
243                                         KEY_READ,
244                                         WDF_NO_OBJECT_ATTRIBUTES,
245                                         &subKey);
246 
247             if (!NT_SUCCESS(status))
248             {
249                 WdfRegistryClose(rootKey);
250                 rootKey = NULL;
251             }
252         }
253 
254         if (NT_SUCCESS(status) && (rootKey != NULL))
255         {
256             defaultParameterValue = *ParameterValue;
257 
258             status = WdfRegistryQueryULong((subKey != NULL) ? subKey : rootKey,
259                                            &registryValueName,
260                                            ParameterValue);
261 
262             if (!NT_SUCCESS(status))
263             {
264                 *ParameterValue = defaultParameterValue; // use default value
265             }
266             else
267             {
268                 // Migrate the value over to the device-specific key
269                 DeviceSetParameter(DeviceExtension, SubkeyName, ParameterName, *ParameterValue);
270             }
271         }
272 
273         // close what we open
274         if (subKey != NULL)
275         {
276             WdfRegistryClose(subKey);
277             subKey = NULL;
278         }
279 
280         if (rootKey != NULL)
281         {
282             WdfRegistryClose(rootKey);
283             rootKey = NULL;
284         }
285     }
286 
287     return;
288 
289 } // end DeviceetParameter()
290 
291 
292 _IRQL_requires_max_(PASSIVE_LEVEL)
293 NTSTATUS
294 DeviceSetParameter(
295     _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
296     _In_opt_z_ PWSTR                SubkeyName,
297     _In_ PWSTR                      ParameterName,
298     _In_ ULONG                      ParameterValue
299     )
300 /*++
301 Routine Description:
302 
303     set parameter to registry.
304 
305 Arguments:
306 
307     DeviceExtension - device context.
308 
309     SubkeyName - name of subkey
310 
311     ParameterName - the registry parameter to be retrieved
312 
313     ParameterValue - registry value to be set
314 
315 Return Value:
316     NTSTATUS
317 
318 --*/
319 {
320     NTSTATUS        status;
321     WDFKEY          rootKey = NULL;
322     WDFKEY          subKey = NULL;
323     UNICODE_STRING  registrySubKeyName;
324     UNICODE_STRING  registryValueName;
325 
326     PAGED_CODE();
327 
328     RtlInitUnicodeString(&registryValueName, ParameterName);
329 
330     if (SubkeyName != NULL)
331     {
332         RtlInitUnicodeString(&registrySubKeyName, SubkeyName);
333     }
334 
335     // open the hardware key
336     status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
337                                       PLUGPLAY_REGKEY_DEVICE,
338                                       KEY_READ | KEY_WRITE,
339                                       WDF_NO_OBJECT_ATTRIBUTES,
340                                       &rootKey);
341 
342     // open the sub key
343     if (NT_SUCCESS(status) && (SubkeyName != NULL))
344     {
345         status = WdfRegistryOpenKey(rootKey,
346                                     &registrySubKeyName,
347                                     KEY_READ | KEY_WRITE,
348                                     WDF_NO_OBJECT_ATTRIBUTES,
349                                     &subKey);
350 
351         if (!NT_SUCCESS(status))
352         {
353             WdfRegistryClose(rootKey);
354             rootKey = NULL;
355         }
356     }
357 
358     if (NT_SUCCESS(status) && (rootKey != NULL))
359     {
360         status = WdfRegistryAssignULong((subKey != NULL) ? subKey : rootKey,
361                                         &registryValueName,
362                                         ParameterValue);
363     }
364 
365     // close what we open
366     if (subKey != NULL)
367     {
368         WdfRegistryClose(subKey);
369         subKey = NULL;
370     }
371 
372     if (rootKey != NULL)
373     {
374         WdfRegistryClose(rootKey);
375         rootKey = NULL;
376     }
377 
378     return status;
379 
380 } // end DeviceSetParameter()
381 
382 
383 _IRQL_requires_max_(APC_LEVEL)
384 NTSTATUS
385 DeviceSendRequestSynchronously(
386     _In_ WDFDEVICE    Device,
387     _In_ WDFREQUEST   Request,
388     _In_ BOOLEAN      RequestFormated
389     )
390 /*++
391 Routine Description:
392 
393     send a request to lower driver synchronously.
394 
395 Arguments:
396 
397     Device - device object.
398 
399     Request - request object
400 
401     RequestFormated - if the request is already formatted, will no do it in this function
402 
403 Return Value:
404     NTSTATUS
405 
406 --*/
407 {
408     NTSTATUS                    status = STATUS_SUCCESS;
409     PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
410     BOOLEAN                     requestCancelled = FALSE;
411     PCDROM_REQUEST_CONTEXT      requestContext = RequestGetContext(Request);
412 
413     PAGED_CODE();
414 
415     if (!RequestFormated)
416     {
417         // set request up for sending down
418         WdfRequestFormatRequestUsingCurrentType(Request);
419     }
420 
421     // get cancellation status for the original request
422     if (requestContext->OriginalRequest != NULL)
423     {
424         requestCancelled = WdfRequestIsCanceled(requestContext->OriginalRequest);
425     }
426 
427     if (!requestCancelled)
428     {
429         status = RequestSend(deviceExtension,
430                              Request,
431                              deviceExtension->IoTarget,
432                              WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
433                              NULL);
434     }
435     else
436     {
437         status = STATUS_CANCELLED;
438     }
439 
440     return status;
441 }
442 
443 
444 _IRQL_requires_max_(PASSIVE_LEVEL)
445 NTSTATUS
446 DeviceSendSrbSynchronously(
447     _In_ WDFDEVICE           Device,
448     _In_ PSCSI_REQUEST_BLOCK Srb,
449     _In_opt_ PVOID           BufferAddress,
450     _In_ ULONG               BufferLength,
451     _In_ BOOLEAN             WriteToDevice,
452     _In_opt_ WDFREQUEST      OriginalRequest
453     )
454 /*++
455 Routine Description:
456 
457     Send a SRB structure to lower driver synchronously.
458 
459     Process of this function:
460     1. Allocate SenseBuffer; Create Request; Allocate MDL
461     2. Do following loop if necessary
462        2.1 Reuse Request
463        2.2 Format Srb, Irp
464        2.3 Send Request
465        2.4 Error Intepret and retry decision making.
466     3. Release all allocated resosurces.
467 
468 Arguments:
469 
470     Device - device object.
471 
472     Request - request object
473 
474     RequestFormated - if the request is already formatted, will no do it in this function
475 
476 Return Value:
477     NTSTATUS
478 
479 NOTE:
480 The caller needs to setup following fields before calling this routine.
481         srb.CdbLength
482         srb.TimeOutValue
483         cdb
484 
485 BufferLength and WriteToDevice to control the data direction of the device
486     BufferLength = 0: No data transfer
487     BufferLenth != 0 && !WriteToDevice: get data from device
488     BufferLenth != 0 && WriteToDevice: send data to device
489 --*/
490 {
491     NTSTATUS                status;
492     PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
493     PCDROM_PRIVATE_FDO_DATA fdoData = deviceExtension->PrivateFdoData;
494     PUCHAR                  senseInfoBuffer = NULL;
495     ULONG                   retryCount = 0;
496     BOOLEAN                 retry = FALSE;
497     ULONG                   ioctlCode = 0;
498     WDFREQUEST              request = NULL;
499     PIRP                    irp = NULL;
500     PIO_STACK_LOCATION      nextStack = NULL;
501     PMDL                    mdlAddress = NULL;
502     BOOLEAN                 memoryLocked = FALSE;
503     WDF_OBJECT_ATTRIBUTES   attributes;
504     PZERO_POWER_ODD_INFO    zpoddInfo = deviceExtension->ZeroPowerODDInfo;
505 
506     PAGED_CODE();
507 
508     // NOTE: This code is only pagable because we are not freezing
509     //       the queue.  Allowing the queue to be frozen from a pagable
510     //       routine could leave the queue frozen as we try to page in
511     //       the code to unfreeze the queue.  The result would be a nice
512     //       case of deadlock.  Therefore, since we are unfreezing the
513     //       queue regardless of the result, just set the NO_FREEZE_QUEUE
514     //       flag in the SRB.
515     NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
516 
517     //1. allocate SenseBuffer and initiate Srb common fields
518     //   these fields will not be changed by lower driver.
519     {
520         // Write length to SRB.
521         Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
522         Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
523         Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
524 
525         // Sense buffer is in aligned nonpaged pool.
526         senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
527                                                 SENSE_BUFFER_SIZE,
528                                                 CDROM_TAG_SENSE_INFO);
529 
530         if (senseInfoBuffer == NULL)
531         {
532             status = STATUS_INSUFFICIENT_RESOURCES;
533 
534             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
535                        "DeviceSendSrbSynchronously: Can't allocate MDL\n"));
536 
537             goto Exit;
538         }
539 
540         Srb->SenseInfoBuffer = senseInfoBuffer;
541         Srb->DataBuffer = BufferAddress;
542 
543         // set timeout value to default value if it's not specifically set by caller.
544         if (Srb->TimeOutValue == 0)
545         {
546             Srb->TimeOutValue = deviceExtension->TimeOutValue;
547         }
548     }
549 
550     //2. Create Request object
551     {
552         WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
553                                                 CDROM_REQUEST_CONTEXT);
554 
555         status = WdfRequestCreate(&attributes,
556                                   deviceExtension->IoTarget,
557                                   &request);
558 
559         if (!NT_SUCCESS(status))
560         {
561             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
562                        "DeviceSendSrbSynchronously: Can't create request: %lx\n",
563                        status));
564 
565             goto Exit;
566         }
567 
568         irp = WdfRequestWdmGetIrp(request);
569     }
570 
571     // 3. Build an MDL for the data buffer and stick it into the irp.
572     if (BufferAddress != NULL)
573     {
574         mdlAddress = IoAllocateMdl( BufferAddress,
575                                     BufferLength,
576                                     FALSE,
577                                     FALSE,
578                                     irp );
579         if (mdlAddress == NULL)
580         {
581             status = STATUS_INSUFFICIENT_RESOURCES;
582 
583             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
584                        "DeviceSendSrbSynchronously: Can't allocate MDL\n"));
585 
586             goto Exit;
587         }
588 
589         _SEH2_TRY
590         {
591             MmProbeAndLockPages(mdlAddress,
592                                 KernelMode,
593                                 (WriteToDevice ? IoReadAccess : IoWriteAccess));
594         }
595         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
596         {
597             status = _SEH2_GetExceptionCode();
598 
599             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
600                        "DeviceSendSrbSynchronously: Exception %lx locking buffer\n", status));
601 
602             _SEH2_YIELD(goto Exit);
603         }
604         _SEH2_END;
605 
606         memoryLocked = TRUE;
607     }
608 
609     // 4. Format Srb, Irp; Send request and retry when necessary
610     do
611     {
612         // clear the control variable.
613         retry = FALSE;
614 
615         // 4.1 reuse the request object; set originalRequest field.
616         {
617             WDF_REQUEST_REUSE_PARAMS    params;
618             PCDROM_REQUEST_CONTEXT      requestContext = NULL;
619 
620             // deassign the MdlAddress, this is the value we assign explicitly.
621             // doing this can prevent WdfRequestReuse to release the Mdl unexpectly.
622             if (irp->MdlAddress)
623             {
624                 irp->MdlAddress = NULL;
625             }
626 
627             WDF_REQUEST_REUSE_PARAMS_INIT(&params,
628                                           WDF_REQUEST_REUSE_NO_FLAGS,
629                                           STATUS_SUCCESS);
630 
631             status = WdfRequestReuse(request, &params);
632 
633             if (!NT_SUCCESS(status))
634             {
635                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
636                            "DeviceSendSrbSynchronously: WdfRequestReuse failed, %!STATUS!\n",
637                            status));
638                 // exit the loop.
639                 break;
640             }
641 
642             // WDF requests to format the request befor sending it
643             status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
644                                                                     request,
645                                                                     ioctlCode,
646                                                                     NULL, NULL,
647                                                                     NULL, NULL,
648                                                                     NULL, NULL);
649 
650             if (!NT_SUCCESS(status))
651             {
652                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
653                            "DeviceSendSrbSynchronously: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
654                            status));
655                 // exit the loop.
656                 break;
657             }
658 
659             requestContext = RequestGetContext(request);
660             requestContext->OriginalRequest = OriginalRequest;
661         }
662 
663         // 4.2 Format Srb and Irp
664         {
665             Srb->OriginalRequest = irp;
666             Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
667             Srb->DataTransferLength = BufferLength;
668             Srb->SrbFlags = deviceExtension->SrbFlags;
669 
670             // Disable synchronous transfer for these requests.
671             SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
672             SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
673 
674             if (BufferAddress != NULL)
675             {
676                 if (WriteToDevice)
677                 {
678                     SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
679                     ioctlCode = IOCTL_SCSI_EXECUTE_OUT;
680                 }
681                 else
682                 {
683                     SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
684                     ioctlCode = IOCTL_SCSI_EXECUTE_IN;
685                 }
686             }
687             else
688             {
689                 ioctlCode = IOCTL_SCSI_EXECUTE_NONE;
690             }
691 
692 
693             // Zero out status.
694             Srb->ScsiStatus = 0;
695             Srb->SrbStatus = 0;
696             Srb->NextSrb = NULL;
697 
698             // irp related fields
699             irp->MdlAddress = mdlAddress;
700 
701             nextStack = IoGetNextIrpStackLocation(irp);
702 
703             nextStack->MajorFunction = IRP_MJ_SCSI;
704             nextStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode;
705             nextStack->Parameters.Scsi.Srb = Srb;
706         }
707 
708         // 4.3 send Request to lower driver.
709         status = DeviceSendRequestSynchronously(Device, request, TRUE);
710 
711         if (status != STATUS_CANCELLED)
712         {
713             NT_ASSERT(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_PENDING);
714             NT_ASSERT(status != STATUS_PENDING);
715             NT_ASSERT(!(Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN));
716 
717             // 4.4 error process.
718             if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS)
719             {
720                 LONGLONG    retryIntervalIn100ns = 0;
721 
722                 // Update status and determine if request should be retried.
723                 retry = RequestSenseInfoInterpret(deviceExtension,
724                                                   request,
725                                                   Srb,
726                                                   retryCount,
727                                                   &status,
728                                                   &retryIntervalIn100ns);
729 
730                 if (retry)
731                 {
732                     LARGE_INTEGER t;
733                     t.QuadPart = -retryIntervalIn100ns;
734                     retryCount++;
735                     KeDelayExecutionThread(KernelMode, FALSE, &t);
736                 }
737             }
738             else
739             {
740                 // Request succeeded.
741                 fdoData->LoggedTURFailureSinceLastIO = FALSE;
742                 status = STATUS_SUCCESS;
743                 retry = FALSE;
744             }
745         }
746     } while(retry);
747 
748     if ((zpoddInfo != NULL) &&
749         (zpoddInfo->MonitorStartStopUnit != FALSE) &&
750         (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS))
751     {
752         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
753                     "DeviceSendSrbSynchronously: soft eject detected, device marked as active\n"));
754 
755         DeviceMarkActive(deviceExtension, TRUE, FALSE);
756     }
757 
758     // 5. Release all allocated resources.
759 
760     // required even though we allocated our own, since the port driver may
761     // have allocated one also
762     if (PORT_ALLOCATED_SENSE(deviceExtension, Srb))
763     {
764         FREE_PORT_ALLOCATED_SENSE_BUFFER(deviceExtension, Srb);
765     }
766 
767 Exit:
768 
769     if (senseInfoBuffer != NULL)
770     {
771         FREE_POOL(senseInfoBuffer);
772     }
773 
774     Srb->SenseInfoBuffer = NULL;
775     Srb->SenseInfoBufferLength = 0;
776 
777     if (mdlAddress)
778     {
779         if (memoryLocked)
780         {
781             MmUnlockPages(mdlAddress);
782             memoryLocked = FALSE;
783         }
784 
785         IoFreeMdl(mdlAddress);
786         irp->MdlAddress = NULL;
787     }
788 
789     if (request)
790     {
791         WdfObjectDelete(request);
792     }
793 
794     return status;
795 }
796 
797 
798 VOID
799 DeviceSendNotification(
800     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
801     _In_ const GUID*              Guid,
802     _In_ ULONG                    ExtraDataSize,
803     _In_opt_ PVOID                ExtraData
804     )
805 /*++
806 Routine Description:
807 
808     send notification to other components
809 
810 Arguments:
811 
812     DeviceExtension - device context.
813 
814     Guid - GUID for the notification
815 
816     ExtraDataSize - data size along with notification
817 
818     ExtraData - data buffer send with notification
819 
820 Return Value:
821     None
822 
823 --*/
824 {
825     PTARGET_DEVICE_CUSTOM_NOTIFICATION  notification;
826     ULONG                               requiredSize;
827     NTSTATUS                            status;
828 
829     status = RtlULongAdd((sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)),
830                          ExtraDataSize,
831                          &requiredSize);
832 
833     if (!(NT_SUCCESS(status)) || (requiredSize > 0x0000ffff))
834     {
835         // MAX_USHORT, max total size for these events!
836         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
837                    "Error sending event: size too large! (%x)\n",
838                    requiredSize));
839         return;
840     }
841 
842     notification = ExAllocatePoolWithTag(NonPagedPoolNx,
843                                          requiredSize,
844                                          CDROM_TAG_NOTIFICATION);
845 
846     // if none allocated, exit
847     if (notification == NULL)
848     {
849         return;
850     }
851 
852     // Prepare and send the request!
853     RtlZeroMemory(notification, requiredSize);
854     notification->Version = 1;
855     notification->Size = (USHORT)(requiredSize);
856     notification->FileObject = NULL;
857     notification->NameBufferOffset = -1;
858     notification->Event = *Guid;
859 
860     if (ExtraData != NULL)
861     {
862         RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
863     }
864 
865     IoReportTargetDeviceChangeAsynchronous(DeviceExtension->LowerPdo,
866                                            notification,
867                                            NULL,
868                                            NULL);
869 
870     FREE_POOL(notification);
871 
872     return;
873 }
874 
875 
876 VOID
877 DeviceSendStartUnit(
878     _In_ WDFDEVICE Device
879     )
880 /*++
881 
882 Routine Description:
883 
884     Send command to SCSI unit to start or power up.
885     Because this command is issued asynchronounsly, that is, without
886     waiting on it to complete, the IMMEDIATE flag is not set. This
887     means that the CDB will not return until the drive has powered up.
888     This should keep subsequent requests from being submitted to the
889     device before it has completely spun up.
890 
891     This routine is called from the InterpretSense routine, when a
892     request sense returns data indicating that a drive must be
893     powered up.
894 
895     This routine may also be called from a class driver's error handler,
896     or anytime a non-critical start device should be sent to the device.
897 
898 Arguments:
899 
900     Device - The device object.
901 
902 Return Value:
903 
904     None.
905 
906 --*/
907 {
908     NTSTATUS                status = STATUS_SUCCESS;
909     PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
910     WDF_OBJECT_ATTRIBUTES   attributes;
911     WDFREQUEST              startUnitRequest = NULL;
912     WDFMEMORY               inputMemory = NULL;
913 
914     PCOMPLETION_CONTEXT     context = NULL;
915     PSCSI_REQUEST_BLOCK     srb = NULL;
916     PCDB                    cdb = NULL;
917 
918     deviceExtension = DeviceGetExtension(Device);
919 
920     if (NT_SUCCESS(status))
921     {
922         // Allocate Srb from nonpaged pool.
923         context = ExAllocatePoolWithTag(NonPagedPoolNx,
924                                         sizeof(COMPLETION_CONTEXT),
925                                         CDROM_TAG_COMPLETION_CONTEXT);
926 
927         if (context == NULL)
928         {
929             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
930                        "DeviceSendStartUnit: Failed to allocate completion context\n"));
931 
932             status = STATUS_INTERNAL_ERROR;
933         }
934     }
935 
936     if (NT_SUCCESS(status))
937     {
938         // Save the device object in the context for use by the completion
939         // routine.
940         context->Device = Device;
941         srb = &context->Srb;
942 
943         // Zero out srb.
944         RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
945 
946         // setup SRB structure.
947         srb->Length = sizeof(SCSI_REQUEST_BLOCK);
948         srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
949         srb->TimeOutValue = START_UNIT_TIMEOUT;
950 
951         srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
952                         SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
953 
954         // setup CDB
955         srb->CdbLength = 6;
956         cdb = (PCDB)srb->Cdb;
957 
958         cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
959         cdb->START_STOP.Start = 1;
960         cdb->START_STOP.Immediate = 0;
961         cdb->START_STOP.LogicalUnitNumber = srb->Lun;
962 
963         //Create Request for sending down to port driver
964         WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
965                                                 CDROM_REQUEST_CONTEXT);
966         attributes.ParentObject = deviceExtension->IoTarget;
967 
968         status = WdfRequestCreate(&attributes,
969                                   deviceExtension->IoTarget,
970                                   &startUnitRequest);
971     }
972 
973     if (NT_SUCCESS(status))
974     {
975         srb->OriginalRequest = WdfRequestWdmGetIrp(startUnitRequest);
976         NT_ASSERT(srb->OriginalRequest != NULL);
977 
978         //Prepare the request
979         WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
980         attributes.ParentObject = startUnitRequest;
981 
982         status = WdfMemoryCreatePreallocated(&attributes,
983                                              (PVOID)srb,
984                                              sizeof(SCSI_REQUEST_BLOCK),
985                                              &inputMemory);
986     }
987 
988     if (NT_SUCCESS(status))
989     {
990         status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
991                                                                 startUnitRequest,
992                                                                 IOCTL_SCSI_EXECUTE_NONE,
993                                                                 inputMemory,
994                                                                 NULL,
995                                                                 NULL,
996                                                                 NULL,
997                                                                 NULL,
998                                                                 NULL);
999     }
1000 
1001     if (NT_SUCCESS(status))
1002     {
1003         // Set a CompletionRoutine callback function.
1004         WdfRequestSetCompletionRoutine(startUnitRequest,
1005                                        DeviceAsynchronousCompletion,
1006                                        context);
1007 
1008         status = RequestSend(deviceExtension,
1009                              startUnitRequest,
1010                              deviceExtension->IoTarget,
1011                              0,
1012                              NULL);
1013     }
1014 
1015     // release resources when failed.
1016     if (!NT_SUCCESS(status))
1017     {
1018         FREE_POOL(context);
1019         if (startUnitRequest != NULL)
1020         {
1021             WdfObjectDelete(startUnitRequest);
1022         }
1023     }
1024 
1025     return;
1026 } // end StartUnit()
1027 
1028 
1029 VOID
1030 DeviceSendIoctlAsynchronously(
1031     _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
1032     _In_ ULONG                      IoControlCode,
1033     _In_ PDEVICE_OBJECT             TargetDeviceObject
1034     )
1035 /*++
1036 
1037 Routine Description:
1038 
1039     Send an IOCTL asynchronously
1040 
1041 Arguments:
1042 
1043     DeviceExtension - device context.
1044     IoControlCode - IOCTL code.
1045     TargetDeviceObject - target device object.
1046 
1047 Return Value:
1048 
1049     None.
1050 
1051 --*/
1052 {
1053     PIRP                irp = NULL;
1054     PIO_STACK_LOCATION  nextIrpStack = NULL;
1055 
1056     irp = IoAllocateIrp(DeviceExtension->DeviceObject->StackSize, FALSE);
1057 
1058     if (irp != NULL)
1059     {
1060         nextIrpStack = IoGetNextIrpStackLocation(irp);
1061 
1062         nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
1063 
1064         nextIrpStack->Parameters.DeviceIoControl.OutputBufferLength = 0;
1065         nextIrpStack->Parameters.DeviceIoControl.InputBufferLength = 0;
1066         nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
1067         nextIrpStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
1068 
1069         IoSetCompletionRoutine(irp,
1070                                RequestAsynchronousIrpCompletion,
1071                                DeviceExtension,
1072                                TRUE,
1073                                TRUE,
1074                                TRUE);
1075 
1076         (VOID) IoCallDriver(TargetDeviceObject, irp);
1077     }
1078 }
1079 
1080 NTSTATUS
1081 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
1082 RequestAsynchronousIrpCompletion(
1083     _In_ PDEVICE_OBJECT DeviceObject,
1084     _In_ PIRP Irp,
1085     _In_reads_opt_(_Inexpressible_("varies")) PVOID Context
1086     )
1087 /*++
1088 
1089 Routine Description:
1090 
1091     Free the Irp.
1092 
1093 Arguments:
1094 
1095     DeviceObject - device that the completion routine fires on.
1096 
1097     Irp - The irp to be completed.
1098 
1099     Context - IRP context
1100 
1101 Return Value:
1102     NTSTATUS
1103 
1104 --*/
1105 {
1106     UNREFERENCED_PARAMETER(DeviceObject);
1107     UNREFERENCED_PARAMETER(Context);
1108 
1109     IoFreeIrp(Irp);
1110 
1111     return STATUS_MORE_PROCESSING_REQUIRED;
1112 }
1113 
1114 VOID
1115 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
1116 DeviceAsynchronousCompletion(
1117     _In_ WDFREQUEST                       Request,
1118     _In_ WDFIOTARGET                      Target,
1119     _In_ PWDF_REQUEST_COMPLETION_PARAMS   Params,
1120     _In_ WDFCONTEXT                       Context
1121     )
1122 /*++
1123 
1124 Routine Description:
1125 
1126     This routine is called when an asynchronous I/O request
1127     which was issused by the class driver completes.  Examples of such requests
1128     are release queue or START UNIT. This routine releases the queue if
1129     necessary.  It then frees the context and the IRP.
1130 
1131 Arguments:
1132 
1133     DeviceObject - The device object for the logical unit; however since this
1134         is the top stack location the value is NULL.
1135 
1136     Irp - Supplies a pointer to the Irp to be processed.
1137 
1138     Context - Supplies the context to be used to process this request.
1139 
1140 Return Value:
1141 
1142     None.
1143 
1144 --*/
1145 {
1146     PCOMPLETION_CONTEXT     context = (PCOMPLETION_CONTEXT)Context;
1147     PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(context->Device);
1148 
1149     UNREFERENCED_PARAMETER(Target);
1150     UNREFERENCED_PARAMETER(Params);
1151 
1152     // If this is an execute srb, then check the return status and make sure.
1153     // the queue is not frozen.
1154     if (context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI)
1155     {
1156         // Check for a frozen queue.
1157         if (context->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
1158         {
1159             // Unfreeze the queue getting the device object from the context.
1160             DeviceReleaseQueue(context->Device);
1161         }
1162     }
1163 
1164     // free port-allocated sense buffer if we can detect
1165     //
1166     if (PORT_ALLOCATED_SENSE(deviceExtension, &context->Srb))
1167     {
1168         FREE_PORT_ALLOCATED_SENSE_BUFFER(deviceExtension, &context->Srb);
1169     }
1170 
1171     FREE_POOL(context);
1172 
1173     WdfObjectDelete(Request);
1174 
1175 } // end DeviceAsynchronousCompletion()
1176 
1177 
1178 VOID
1179 DeviceReleaseQueue(
1180     _In_ WDFDEVICE    Device
1181     )
1182 /*++
1183 
1184 Routine Description:
1185 
1186     This routine issues an internal device control command
1187     to the port driver to release a frozen queue. The call
1188     is issued asynchronously as DeviceReleaseQueue will be invoked
1189     from the IO completion DPC (and will have no context to
1190     wait for a synchronous call to complete).
1191 
1192     This routine must be called with the remove lock held.
1193 
1194 Arguments:
1195 
1196     Device - The functional device object for the device with the frozen queue.
1197 
1198 Return Value:
1199 
1200     None.
1201 
1202 --*/
1203 {
1204     PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
1205     PSCSI_REQUEST_BLOCK         srb = NULL;
1206     KIRQL                       currentIrql;
1207 
1208     // we raise irql seperately so we're not swapped out or suspended
1209     // while holding the release queue irp in this routine.  this lets
1210     // us release the spin lock before lowering irql.
1211     KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
1212 
1213     WdfSpinLockAcquire(deviceExtension->ReleaseQueueSpinLock);
1214 
1215     if (deviceExtension->ReleaseQueueInProgress)
1216     {
1217         // Someone is already doing this work - just set the flag to indicate that
1218         // we need to release the queue again.
1219         deviceExtension->ReleaseQueueNeeded = TRUE;
1220         WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
1221         KeLowerIrql(currentIrql);
1222 
1223         return;
1224     }
1225 
1226     // Mark that there is a release queue in progress and drop the spinlock.
1227     deviceExtension->ReleaseQueueInProgress = TRUE;
1228 
1229     WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
1230 
1231     srb = &(deviceExtension->ReleaseQueueSrb);
1232 
1233     // Optical media are removable, so we just flush the queue.  This will also release it.
1234     srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
1235 
1236     srb->OriginalRequest = WdfRequestWdmGetIrp(deviceExtension->ReleaseQueueRequest);
1237 
1238     // Set a CompletionRoutine callback function.
1239     WdfRequestSetCompletionRoutine(deviceExtension->ReleaseQueueRequest,
1240                                    DeviceReleaseQueueCompletion,
1241                                    Device);
1242     // Send the request. If an error occurs, complete the request.
1243     RequestSend(deviceExtension,
1244                 deviceExtension->ReleaseQueueRequest,
1245                 deviceExtension->IoTarget,
1246                 WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE,
1247                 NULL);
1248 
1249     KeLowerIrql(currentIrql);
1250 
1251     return;
1252 
1253 } // end DeviceReleaseQueue()
1254 
1255 VOID
1256 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
1257 DeviceReleaseQueueCompletion(
1258     _In_ WDFREQUEST                       Request,
1259     _In_ WDFIOTARGET                      Target,
1260     _In_ PWDF_REQUEST_COMPLETION_PARAMS   Params,
1261     _In_ WDFCONTEXT                       Context
1262     )
1263 /*++
1264 
1265 Routine Description:
1266 
1267     This routine is called when an asynchronous release queue request which
1268     was issused in DeviceReleaseQueue completes. This routine prepares for
1269     the next release queue request and resends it if necessary.
1270 
1271 Arguments:
1272 
1273     Request - The completed request.
1274 
1275     Target - IoTarget object
1276 
1277     Params - Completion parameters
1278 
1279     Context - WDFDEVICE object handle.
1280 
1281 Return Value:
1282 
1283     None.
1284 
1285 --*/
1286 {
1287     NTSTATUS                    status;
1288     WDFDEVICE                   device = Context;
1289     PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(device);
1290 
1291     BOOLEAN                     releaseQueueNeeded = FALSE;
1292     WDF_REQUEST_REUSE_PARAMS    params = {0};
1293 
1294     UNREFERENCED_PARAMETER(Target);
1295     UNREFERENCED_PARAMETER(Params);
1296 
1297     WDF_REQUEST_REUSE_PARAMS_INIT(&params,
1298                                   WDF_REQUEST_REUSE_NO_FLAGS,
1299                                   STATUS_SUCCESS);
1300 
1301     // Grab the spinlock and clear the release queue in progress flag so others
1302     // can run. Save (and clear) the state of the release queue needed flag
1303     // so that we can issue a new release queue outside the spinlock.
1304     WdfSpinLockAcquire(deviceExtension->ReleaseQueueSpinLock);
1305 
1306     releaseQueueNeeded = deviceExtension->ReleaseQueueNeeded;
1307 
1308     deviceExtension->ReleaseQueueNeeded = FALSE;
1309     deviceExtension->ReleaseQueueInProgress = FALSE;
1310 
1311     // Reuse the ReleaseQueueRequest for the next time.
1312     status = WdfRequestReuse(Request,&params);
1313 
1314     if (NT_SUCCESS(status))
1315     {
1316         // Preformat the ReleaseQueueRequest for the next time.
1317         // This should always succeed because it was already preformatted once during device initialization
1318         status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
1319                                                                 Request,
1320                                                                 IOCTL_SCSI_EXECUTE_NONE,
1321                                                                 deviceExtension->ReleaseQueueInputMemory,
1322                                                                 NULL,
1323                                                                 NULL,
1324                                                                 NULL,
1325                                                                 NULL,
1326                                                                 NULL);
1327     }
1328 
1329     if (!NT_SUCCESS(status))
1330     {
1331         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1332                    "DeviceReleaseQueueCompletion: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
1333                    status));
1334     }
1335 
1336     RequestClearSendTime(Request);
1337 
1338     WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
1339 
1340     // If we need a release queue then issue one now.  Another processor may
1341     // have already started one in which case we'll try to issue this one after
1342     // it is done - but we should never recurse more than one deep.
1343     if (releaseQueueNeeded)
1344     {
1345         DeviceReleaseQueue(device);
1346     }
1347 
1348     return;
1349 
1350 } // DeviceReleaseQueueCompletion()
1351 
1352 
1353 //
1354 // In order to provide better performance without the need to reboot,
1355 // we need to implement a self-adjusting method to set and clear the
1356 // srb flags based upon current performance.
1357 //
1358 // whenever there is an error, immediately grab the spin lock.  the
1359 // MP perf hit here is acceptable, since we're in an error path.  this
1360 // is also neccessary because we are guaranteed to be modifying the
1361 // SRB flags here, setting SuccessfulIO to zero, and incrementing the
1362 // actual error count (which is always done within this spinlock).
1363 //
1364 // whenever there is no error, increment a counter.  if there have been
1365 // errors on the device, and we've enabled dynamic perf, *and* we've
1366 // just crossed the perf threshhold, then grab the spin lock and
1367 // double check that the threshhold has, indeed been hit(*). then
1368 // decrement the error count, and if it's dropped sufficiently, undo
1369 // some of the safety changes made in the SRB flags due to the errors.
1370 //
1371 // * this works in all cases.  even if lots of ios occur after the
1372 //   previous guy went in and cleared the successfulio counter, that
1373 //   just means that we've hit the threshhold again, and so it's proper
1374 //   to run the inner loop again.
1375 //
1376 
1377 VOID
1378 DevicePerfIncrementErrorCount(
1379     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1380     )
1381 {
1382     PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData;
1383     KIRQL                   oldIrql;
1384     ULONG                   errors;
1385 
1386     KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
1387 
1388     fdoData->Perf.SuccessfulIO = 0; // implicit interlock
1389     errors = InterlockedIncrement((PLONG)&DeviceExtension->ErrorCount);
1390 
1391     if (errors >= CLASS_ERROR_LEVEL_1)
1392     {
1393         // If the error count has exceeded the error limit, then disable
1394         // any tagged queuing, multiple requests per lu queueing
1395         // and sychronous data transfers.
1396         //
1397         // Clearing the no queue freeze flag prevents the port driver
1398         // from sending multiple requests per logical unit.
1399         CLEAR_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
1400         CLEAR_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
1401 
1402         SET_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
1403 
1404         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1405                    "PerfIncrementErrorCount: Too many errors; disabling tagged queuing and "
1406                    "synchronous data tranfers.\n"));
1407     }
1408 
1409     if (errors >= CLASS_ERROR_LEVEL_2)
1410     {
1411         // If a second threshold is reached, disable disconnects.
1412         SET_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
1413         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1414                    "PerfIncrementErrorCount: Too many errors; disabling disconnects.\n"));
1415     }
1416 
1417     KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
1418     return;
1419 }
1420 
1421 
1422 _IRQL_requires_max_(APC_LEVEL)
1423 PVOID
1424 DeviceFindFeaturePage(
1425     _In_reads_bytes_(Length) PGET_CONFIGURATION_HEADER   FeatureBuffer,
1426     _In_ ULONG const                                Length,
1427     _In_ FEATURE_NUMBER const                       Feature
1428     )
1429 /*++
1430 Routine Description:
1431 
1432     find the specific feature page in the buffer
1433 
1434 Arguments:
1435 
1436     FeatureBuffer - buffer contains the device feature set.
1437 
1438     Length - buffer length
1439 
1440     Feature - the feature number looking for.
1441 
1442 Return Value:
1443 
1444     PVOID - pointer to the starting location of the specific feature in buffer.
1445 
1446 --*/
1447 {
1448     PUCHAR buffer;
1449     PUCHAR limit;
1450     ULONG  validLength;
1451 
1452     PAGED_CODE();
1453 
1454     if (Length < sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER))
1455     {
1456         return NULL;
1457     }
1458 
1459     // Calculate the length of valid data available in the
1460     // capabilities buffer from the DataLength field
1461     REVERSE_BYTES(&validLength, FeatureBuffer->DataLength);
1462 
1463     validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
1464 
1465     // set limit to point to first illegal address
1466     limit  = (PUCHAR)FeatureBuffer;
1467     limit += min(Length, validLength);
1468 
1469     // set buffer to point to first page
1470     buffer = FeatureBuffer->Data;
1471 
1472     // loop through each page until we find the requested one, or
1473     // until it's not safe to access the entire feature header
1474     // (if equal, have exactly enough for the feature header)
1475     while (buffer + sizeof(FEATURE_HEADER) <= limit)
1476     {
1477         PFEATURE_HEADER header = (PFEATURE_HEADER)buffer;
1478         FEATURE_NUMBER  thisFeature;
1479 
1480         thisFeature  = (header->FeatureCode[0] << 8) |
1481                        (header->FeatureCode[1]);
1482 
1483         if (thisFeature == Feature)
1484         {
1485             PUCHAR temp;
1486 
1487             // if don't have enough memory to safely access all the feature
1488             // information, return NULL
1489             temp = buffer;
1490             temp += sizeof(FEATURE_HEADER);
1491             temp += header->AdditionalLength;
1492 
1493             if (temp > limit)
1494             {
1495                 // this means the transfer was cut-off, an insufficiently
1496                 // small buffer was given, or other arbitrary error.  since
1497                 // it's not safe to view the amount of data (even though
1498                 // the header is safe) in this feature, pretend it wasn't
1499                 // transferred at all...
1500                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
1501                            "Feature %x exists, but not safe to access all its data.  returning NULL\n",
1502                            Feature));
1503                 return NULL;
1504             }
1505             else
1506             {
1507                 return buffer;
1508             }
1509         }
1510 
1511         if ((header->AdditionalLength % 4) &&
1512             !(Feature >= 0xff00 && Feature <= 0xffff))
1513         {
1514             return NULL;
1515         }
1516 
1517         buffer += sizeof(FEATURE_HEADER);
1518         buffer += header->AdditionalLength;
1519     }
1520 
1521     return NULL;
1522 }
1523 
1524 
1525 _IRQL_requires_max_(APC_LEVEL)
1526 VOID
1527 DevicePrintAllFeaturePages(
1528     _In_reads_bytes_(Usable) PGET_CONFIGURATION_HEADER   Buffer,
1529     _In_ ULONG const                                Usable
1530     )
1531 /*++
1532 Routine Description:
1533 
1534     print out all feature pages in the buffer
1535 
1536 Arguments:
1537 
1538     Buffer - buffer contains the device feature set.
1539 
1540     Usable -
1541 
1542 Return Value:
1543 
1544     none
1545 
1546 --*/
1547 {
1548 #if DBG
1549     PFEATURE_HEADER header;
1550 
1551     PAGED_CODE();
1552 
1553     ////////////////////////////////////////////////////////////////////////////////
1554     // items expected to ALWAYS be current if they exist
1555     ////////////////////////////////////////////////////////////////////////////////
1556 
1557     header = DeviceFindFeaturePage(Buffer, Usable, FeatureProfileList);
1558     if (header != NULL) {
1559         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1560                    "CdromGetConfiguration: CurrentProfile %x "
1561                    "with %x bytes of data at %p\n",
1562                    Buffer->CurrentProfile[0] << 8 |
1563                    Buffer->CurrentProfile[1],
1564                    Usable, Buffer));
1565     }
1566 
1567     header = DeviceFindFeaturePage(Buffer, Usable, FeatureCore);
1568     if (header) {
1569         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1570                    "CdromGetConfiguration: %s %s\n",
1571                    (header->Current ?
1572                     "Currently supports" : "Is able to support"),
1573                    "CORE Features"
1574                    ));
1575     }
1576 
1577     header = DeviceFindFeaturePage(Buffer, Usable, FeatureMorphing);
1578     if (header) {
1579         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1580                    "CdromGetConfiguration: %s %s\n",
1581                    (header->Current ?
1582                     "Currently supports" : "Is able to support"),
1583                    "Morphing"
1584                    ));
1585     }
1586 
1587     header = DeviceFindFeaturePage(Buffer, Usable, FeatureRemovableMedium);
1588     if (header) {
1589         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1590                    "CdromGetConfiguration: %s %s\n",
1591                    (header->Current ?
1592                     "Currently supports" : "Is able to support"),
1593                    "Removable Medium"
1594                    ));
1595     }
1596 
1597     header = DeviceFindFeaturePage(Buffer, Usable, FeaturePowerManagement);
1598     if (header) {
1599         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1600                    "CdromGetConfiguration: %s %s\n",
1601                    (header->Current ?
1602                     "Currently supports" : "Is able to support"),
1603                    "Power Management"
1604                    ));
1605     }
1606 
1607     header = DeviceFindFeaturePage(Buffer, Usable, FeatureEmbeddedChanger);
1608     if (header) {
1609         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1610                    "CdromGetConfiguration: %s %s\n",
1611                    (header->Current ?
1612                     "Currently supports" : "Is able to support"),
1613                    "Embedded Changer"
1614                    ));
1615     }
1616 
1617     header = DeviceFindFeaturePage(Buffer, Usable, FeatureMicrocodeUpgrade);
1618     if (header) {
1619         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1620                    "CdromGetConfiguration: %s %s\n",
1621                    (header->Current ?
1622                     "Currently supports" : "Is able to support"),
1623                    "Microcode Update"
1624                    ));
1625     }
1626 
1627     header = DeviceFindFeaturePage(Buffer, Usable, FeatureTimeout);
1628     if (header) {
1629         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1630                    "CdromGetConfiguration: %s %s\n",
1631                    (header->Current ?
1632                     "Currently supports" : "Is able to support"),
1633                    "Timeouts"
1634                    ));
1635     }
1636 
1637     header = DeviceFindFeaturePage(Buffer, Usable, FeatureLogicalUnitSerialNumber);
1638     if (header) {
1639         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1640                    "CdromGetConfiguration: %s %s\n",
1641                    (header->Current ?
1642                     "Currently supports" : "Is able to support"),
1643                    "LUN Serial Number"
1644                    ));
1645     }
1646 
1647     header = DeviceFindFeaturePage(Buffer, Usable, FeatureFirmwareDate);
1648     if (header) {
1649 
1650         ULONG featureSize = header->AdditionalLength;
1651         featureSize += RTL_SIZEOF_THROUGH_FIELD(FEATURE_HEADER, AdditionalLength);
1652 
1653         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1654                    "CdromGetConfiguration: %s %s\n",
1655                    (header->Current ?
1656                    "Currently supports" : "Is able to support"),
1657                    "Firmware Date"
1658                    ));
1659 
1660         if (featureSize >= RTL_SIZEOF_THROUGH_FIELD(FEATURE_DATA_FIRMWARE_DATE, Minute))
1661         {
1662             PFEATURE_DATA_FIRMWARE_DATE date = (PFEATURE_DATA_FIRMWARE_DATE)header;
1663             // show date as "YYYY/MM/DD hh:mm", which is 18 chars (17+NULL)
1664             UCHAR dateString[18] = { 0 };
1665             dateString[ 0] = date->Year[0];
1666             dateString[ 1] = date->Year[1];
1667             dateString[ 2] = date->Year[2];
1668             dateString[ 3] = date->Year[3];
1669             dateString[ 4] = '/';
1670             dateString[ 5] = date->Month[0];
1671             dateString[ 6] = date->Month[1];
1672             dateString[ 7] = '/';
1673             dateString[ 8] = date->Day[0];
1674             dateString[ 9] = date->Day[1];
1675             dateString[10] = ' ';
1676             dateString[11] = ' ';
1677             dateString[12] = date->Hour[0];
1678             dateString[13] = date->Hour[1];
1679             dateString[14] = ':';
1680             dateString[15] = date->Minute[0];
1681             dateString[16] = date->Minute[1];
1682             dateString[17] = 0;
1683             // SECONDS IS NOT AVAILABLE ON EARLY IMPLEMENTATIONS -- ignore it
1684             //dateString[17] = ':';
1685             //dateString[18] = date->Seconds[0];
1686             //dateString[19] = date->Seconds[1];
1687             //dateString[20] = 0;
1688             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1689                         "CdromGetConfiguration: Firmware Date/Time %s (UTC)\n",
1690                         (PCSTR)dateString
1691                         ));
1692         }
1693     }
1694 
1695 ////////////////////////////////////////////////////////////////////////////////
1696 // items expected not to always be current
1697 ////////////////////////////////////////////////////////////////////////////////
1698 
1699 
1700     header = DeviceFindFeaturePage(Buffer, Usable, FeatureWriteProtect);
1701     if (header) {
1702         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
1703                    "CdromGetConfiguration: %s %s\n",
1704                    (header->Current ?
1705                     "Currently supports" : "Is able to support"),
1706                    "Software Write Protect"
1707                    ));
1708     }
1709 
1710     header = DeviceFindFeaturePage(Buffer, Usable, FeatureRandomReadable);
1711     if (header) {
1712         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1713                    "CdromGetConfiguration: %s %s\n",
1714                    (header->Current ?
1715                     "Currently supports" : "Is able to support"),
1716                    "Random Reads"
1717                    ));
1718     }
1719 
1720     header = DeviceFindFeaturePage(Buffer, Usable, FeatureMultiRead);
1721     if (header) {
1722         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1723                    "CdromGetConfiguration: %s %s\n",
1724                    (header->Current ?
1725                     "Currently supports" : "Is able to support"),
1726                    "Multi-Read"
1727                    ));
1728     }
1729 
1730     header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdRead);
1731     if (header) {
1732         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1733                    "CdromGetConfiguration: %s %s\n",
1734                    (header->Current ?
1735                     "Currently supports" : "Is able to support"),
1736                    "reading from CD-ROM/R/RW"
1737                    ));
1738     }
1739 
1740     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdRead);
1741     if (header) {
1742         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1743                    "CdromGetConfiguration: %s %s\n",
1744                    (header->Current ?
1745                     "Currently supports" : "Is able to support"),
1746                    "DVD Structure Reads"
1747                    ));
1748     }
1749 
1750     header = DeviceFindFeaturePage(Buffer, Usable, FeatureRandomWritable);
1751     if (header) {
1752         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1753                    "CdromGetConfiguration: %s %s\n",
1754                    (header->Current ?
1755                     "Currently supports" : "Is able to support"),
1756                    "Random Writes"
1757                    ));
1758     }
1759 
1760     header = DeviceFindFeaturePage(Buffer, Usable, FeatureIncrementalStreamingWritable);
1761     if (header) {
1762         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1763                    "CdromGetConfiguration: %s %s\n",
1764                    (header->Current ?
1765                     "Currently supports" : "Is able to support"),
1766                    "Incremental Streaming Writing"
1767                    ));
1768     }
1769 
1770     header = DeviceFindFeaturePage(Buffer, Usable, FeatureSectorErasable);
1771     if (header) {
1772         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1773                    "CdromGetConfiguration: %s %s\n",
1774                    (header->Current ?
1775                     "Currently supports" : "Is able to support"),
1776                    "Sector Erasable Media"
1777                    ));
1778     }
1779 
1780     header = DeviceFindFeaturePage(Buffer, Usable, FeatureFormattable);
1781     if (header) {
1782         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1783                    "CdromGetConfiguration: %s %s\n",
1784                    (header->Current ?
1785                     "Currently supports" : "Is able to support"),
1786                    "Formatting"
1787                    ));
1788     }
1789 
1790     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDefectManagement);
1791     if (header) {
1792         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1793                    "CdromGetConfiguration: %s %s\n",
1794                    (header->Current ?
1795                     "Currently supports" : "Is able to support"),
1796                    "defect management"
1797                    ));
1798     }
1799 
1800     header = DeviceFindFeaturePage(Buffer, Usable, FeatureWriteOnce);
1801     if (header) {
1802         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1803                    "CdromGetConfiguration: %s %s\n",
1804                    (header->Current ?
1805                     "Currently supports" : "Is able to support"),
1806                    "Write Once Media"
1807                    ));
1808     }
1809 
1810     header = DeviceFindFeaturePage(Buffer, Usable, FeatureRestrictedOverwrite);
1811     if (header) {
1812         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1813                    "CdromGetConfiguration: %s %s\n",
1814                    (header->Current ?
1815                     "Currently supports" : "Is able to support"),
1816                    "Restricted Overwrites"
1817                    ));
1818     }
1819 
1820     header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdrwCAVWrite);
1821     if (header) {
1822         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1823                    "CdromGetConfiguration: %s %s\n",
1824                    (header->Current ?
1825                     "Currently supports" : "Is able to support"),
1826                    "CD-RW CAV recording"
1827                    ));
1828     }
1829 
1830     header = DeviceFindFeaturePage(Buffer, Usable, FeatureMrw);
1831     if (header) {
1832         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1833                    "CdromGetConfiguration: %s %s\n",
1834                    (header->Current ?
1835                     "Currently supports" : "Is able to support"),
1836                    "Mount Rainier media"
1837                    ));
1838     }
1839 
1840     header = DeviceFindFeaturePage(Buffer, Usable, FeatureEnhancedDefectReporting);
1841     if (header) {
1842         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1843                    "CdromGetConfiguration: %s %s\n",
1844                    (header->Current ?
1845                     "Currently supports" : "Is able to support"),
1846                    "Enhanced Defect Reporting"
1847                    ));
1848     }
1849 
1850     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdPlusRW);
1851     if (header) {
1852         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1853                    "CdromGetConfiguration: %s %s\n",
1854                    (header->Current ?
1855                     "Currently supports" : "Is able to support"),
1856                    "DVD+RW media"
1857                    ));
1858     }
1859 
1860     header = DeviceFindFeaturePage(Buffer, Usable, FeatureRigidRestrictedOverwrite);
1861     if (header) {
1862         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1863                    "CdromGetConfiguration: %s %s\n",
1864                    (header->Current ?
1865                     "Currently supports" : "Is able to support"),
1866                    "Rigid Restricted Overwrite"
1867                    ));
1868     }
1869 
1870     header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdTrackAtOnce);
1871     if (header) {
1872         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1873                    "CdromGetConfiguration: %s %s\n",
1874                    (header->Current ?
1875                     "Currently supports" : "Is able to support"),
1876                    "CD Recording (Track At Once)"
1877                    ));
1878     }
1879 
1880     header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdMastering);
1881     if (header) {
1882         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1883                    "CdromGetConfiguration: %s %s\n",
1884                    (header->Current ?
1885                     "Currently supports" : "Is able to support"),
1886                    "CD Recording (Mastering)"
1887                    ));
1888     }
1889 
1890     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdRecordableWrite);
1891     if (header) {
1892         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1893                    "CdromGetConfiguration: %s %s\n",
1894                    (header->Current ?
1895                     "Currently supports" : "Is able to support"),
1896                    "DVD Recording (Mastering)"
1897                    ));
1898     }
1899 
1900     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRead);
1901     if (header) {
1902         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1903                    "CdromGetConfiguration: %s %s\n",
1904                    (header->Current ?
1905                     "Currently supports" : "Is able to support"),
1906                    "DD CD Reading"
1907                    ));
1908     }
1909 
1910     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRWrite);
1911     if (header) {
1912         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1913                    "CdromGetConfiguration: %s %s\n",
1914                    (header->Current ?
1915                     "Currently supports" : "Is able to support"),
1916                    "DD CD-R Writing"
1917                    ));
1918     }
1919 
1920     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRWWrite);
1921     if (header) {
1922         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1923                    "CdromGetConfiguration: %s %s\n",
1924                    (header->Current ?
1925                     "Currently supports" : "Is able to support"),
1926                    "DD CD-RW Writing"
1927                    ));
1928     }
1929 
1930     header = DeviceFindFeaturePage(Buffer, Usable, FeatureLayerJumpRecording);
1931     if (header) {
1932         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1933                    "CdromGetConfiguration: %s %s\n",
1934                    (header->Current ?
1935                     "Currently supports" : "Is able to support"),
1936                    "Layer Jump Recording"
1937                    ));
1938     }
1939 
1940     header = DeviceFindFeaturePage(Buffer, Usable, FeatureHDDVDRead);
1941     if (header) {
1942         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1943                    "CdromGetConfiguration: %s %s\n",
1944                    (header->Current ?
1945                     "Currently supports" : "Is able to support"),
1946                    "HD-DVD Reading"
1947                    ));
1948     }
1949 
1950     header = DeviceFindFeaturePage(Buffer, Usable, FeatureHDDVDWrite);
1951     if (header) {
1952         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1953                    "CdromGetConfiguration: %s %s\n",
1954                    (header->Current ?
1955                     "Currently supports" : "Is able to support"),
1956                    "HD-DVD Writing"
1957                    ));
1958     }
1959 
1960 
1961     header = DeviceFindFeaturePage(Buffer, Usable, FeatureSMART);
1962     if (header) {
1963         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1964                    "CdromGetConfiguration: %s %s\n",
1965                    (header->Current ?
1966                     "Currently supports" : "Is able to support"),
1967                    "S.M.A.R.T."
1968                    ));
1969     }
1970 
1971     header = DeviceFindFeaturePage(Buffer, Usable, FeatureCDAudioAnalogPlay);
1972     if (header) {
1973         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1974                    "CdromGetConfiguration: %s %s\n",
1975                    (header->Current ?
1976                     "Currently supports" : "Is able to support"),
1977                    "Analogue CD Audio Operations"
1978                    ));
1979     }
1980 
1981     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdCSS);
1982     if (header) {
1983         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1984                    "CdromGetConfiguration: %s %s\n",
1985                    (header->Current ?
1986                     "Currently supports" : "Is able to support"),
1987                    "DVD CSS"
1988                    ));
1989     }
1990 
1991     header = DeviceFindFeaturePage(Buffer, Usable, FeatureRealTimeStreaming);
1992     if (header) {
1993         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
1994                    "CdromGetConfiguration: %s %s\n",
1995                    (header->Current ?
1996                     "Currently supports" : "Is able to support"),
1997                    "Real-time Streaming Reads"
1998                    ));
1999     }
2000 
2001     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDiscControlBlocks);
2002     if (header) {
2003         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
2004                    "CdromGetConfiguration: %s %s\n",
2005                    (header->Current ?
2006                     "Currently supports" : "Is able to support"),
2007                    "DVD Disc Control Blocks"
2008                    ));
2009     }
2010 
2011     header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdCPRM);
2012     if (header) {
2013         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
2014                    "CdromGetConfiguration: %s %s\n",
2015                    (header->Current ?
2016                     "Currently supports" : "Is able to support"),
2017                    "DVD CPRM"
2018                    ));
2019     }
2020 
2021     header = DeviceFindFeaturePage(Buffer, Usable, FeatureAACS);
2022     if (header) {
2023         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
2024                    "CdromGetConfiguration: %s %s\n",
2025                    (header->Current ?
2026                     "Currently supports" : "Is able to support"),
2027                    "AACS"
2028                    ));
2029     }
2030 
2031 #else
2032     PAGED_CODE();
2033 
2034     UNREFERENCED_PARAMETER(Usable);
2035     UNREFERENCED_PARAMETER(Buffer);
2036 
2037 #endif // DBG
2038     return;
2039 }
2040 
2041 
2042 _IRQL_requires_max_(PASSIVE_LEVEL)
2043 NTSTATUS
2044 MediaReadCapacity(
2045     _In_ WDFDEVICE Device
2046     )
2047 /*++
2048 Routine Description:
2049 
2050     Get media capacity
2051 
2052 Arguments:
2053 
2054     Device - the device that owns the media
2055 
2056 Return Value:
2057 
2058     NTSTATUS
2059 
2060 --*/
2061 {
2062     NTSTATUS                status;
2063     SCSI_REQUEST_BLOCK      srb;
2064     PCDB                    cdb = NULL;
2065     READ_CAPACITY_DATA      capacityData;
2066 
2067     PAGED_CODE();
2068 
2069     RtlZeroMemory(&srb, sizeof(srb));
2070     RtlZeroMemory(&capacityData, sizeof(capacityData));
2071 
2072     cdb = (PCDB)(&srb.Cdb);
2073 
2074     //Prepare SCSI command fields
2075     srb.CdbLength = 10;
2076     srb.TimeOutValue = CDROM_READ_CAPACITY_TIMEOUT;
2077     cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
2078 
2079     status = DeviceSendSrbSynchronously(Device,
2080                                         &srb,
2081                                         &capacityData,
2082                                         sizeof(READ_CAPACITY_DATA),
2083                                         FALSE,
2084                                         NULL);
2085 
2086     //Remember the result
2087     if (!NT_SUCCESS(status))
2088     {
2089         //Set the BytesPerBlock to zero, this is for safe as if error happens this field should stay zero (no change).
2090         //it will be treated as error case in MediaReadCapacityDataInterpret()
2091         capacityData.BytesPerBlock = 0;
2092     }
2093 
2094     MediaReadCapacityDataInterpret(Device, &capacityData);
2095 
2096     return status;
2097 }
2098 
2099 
2100 _IRQL_requires_max_(APC_LEVEL)
2101 VOID
2102 MediaReadCapacityDataInterpret(
2103     _In_ WDFDEVICE            Device,
2104     _In_ PREAD_CAPACITY_DATA  ReadCapacityBuffer
2105     )
2106 /*++
2107 Routine Description:
2108 
2109     Interpret media capacity and set corresponding fields in device context
2110 
2111 Arguments:
2112 
2113     Device - the device that owns the media
2114 
2115     ReadCapacityBuffer - data buffer of capacity
2116 
2117 Return Value:
2118 
2119     none
2120 
2121 --*/
2122 {
2123     PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
2124     ULONG                   lastSector = 0;
2125     ULONG                   bps = 0;
2126     ULONG                   lastBit = 0;
2127     ULONG                   bytesPerBlock = 0;
2128     BOOLEAN                 errorHappened = FALSE;
2129 
2130     PAGED_CODE();
2131 
2132     NT_ASSERT(ReadCapacityBuffer);
2133 
2134     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
2135                 "MediaReadCapacityDataInterpret: Entering\n"));
2136 
2137     // Swizzle bytes from Read Capacity and translate into
2138     // the necessary geometry information in the device extension.
2139     bytesPerBlock = ReadCapacityBuffer->BytesPerBlock;
2140 
2141     ((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&bytesPerBlock)->Byte3;
2142     ((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&bytesPerBlock)->Byte2;
2143     ((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&bytesPerBlock)->Byte1;
2144     ((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&bytesPerBlock)->Byte0;
2145 
2146     // Insure that bps is a power of 2.
2147     // This corrects a problem with the HP 4020i CDR where it
2148     // returns an incorrect number for bytes per sector.
2149     if (!bps)
2150     {
2151         // Set disk geometry to default values (per ISO 9660).
2152         bps = 2048;
2153         errorHappened = TRUE;
2154     }
2155     else
2156     {
2157         lastBit = (ULONG)(-1);
2158         while (bps)
2159         {
2160             lastBit++;
2161             bps = (bps >> 1);
2162         }
2163         bps = (1 << lastBit);
2164     }
2165 
2166     deviceExtension->DiskGeometry.BytesPerSector = bps;
2167 
2168     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
2169                "MediaReadCapacityDataInterpret: Calculated bps %#x\n",
2170                 deviceExtension->DiskGeometry.BytesPerSector));
2171 
2172     // Copy last sector in reverse byte order.
2173     bytesPerBlock = ReadCapacityBuffer->LogicalBlockAddress;
2174 
2175     ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&bytesPerBlock)->Byte3;
2176     ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&bytesPerBlock)->Byte2;
2177     ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&bytesPerBlock)->Byte1;
2178     ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&bytesPerBlock)->Byte0;
2179 
2180     // Calculate sector to byte shift.
2181     WHICH_BIT(bps, deviceExtension->SectorShift);
2182 
2183     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
2184                "MediaReadCapacityDataInterpret: Sector size is %d\n",
2185                deviceExtension->DiskGeometry.BytesPerSector));
2186 
2187     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
2188                "MediaReadCapacityDataInterpret: Number of Sectors is %d\n",
2189                lastSector + 1));
2190 
2191     // Calculate media capacity in bytes.
2192     if (errorHappened)
2193     {
2194         // Set disk geometry to default values (per ISO 9660).
2195         deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
2196     }
2197     else
2198     {
2199         deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
2200         deviceExtension->PartitionLength.QuadPart =
2201                     (deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
2202     }
2203 
2204     // we've defaulted to 32/64 forever.  don't want to change this now...
2205     deviceExtension->DiskGeometry.TracksPerCylinder = 0x40;
2206     deviceExtension->DiskGeometry.SectorsPerTrack = 0x20;
2207 
2208     // Calculate number of cylinders.
2209     deviceExtension->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1) / (32 * 64));
2210 
2211     deviceExtension->DiskGeometry.MediaType = RemovableMedia;
2212 
2213     return;
2214 }
2215 
2216 
2217 _IRQL_requires_max_(APC_LEVEL)
2218 VOID
2219 DevicePickDvdRegion(
2220     _In_ WDFDEVICE Device
2221     )
2222 /*++
2223 
2224 Routine Description:
2225 
2226     pick a default dvd region
2227 
2228 Arguments:
2229 
2230     Device - Device Object
2231 
2232 Return Value:
2233 
2234     none
2235 
2236 --*/
2237 {
2238     NTSTATUS                status;
2239     PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
2240 
2241     // these five pointers all point to dvdReadStructure or part of
2242     // its data, so don't deallocate them more than once!
2243     PDVD_READ_STRUCTURE         dvdReadStructure;
2244     PDVD_COPY_PROTECT_KEY       copyProtectKey;
2245     PDVD_COPYRIGHT_DESCRIPTOR   dvdCopyRight;
2246     PDVD_RPC_KEY                rpcKey;
2247     PDVD_SET_RPC_KEY            dvdRpcKey;
2248 
2249     size_t          bytesReturned = 0;
2250     ULONG           bufferLen = 0;
2251     UCHAR           mediaRegion = 0;
2252     ULONG           pickDvdRegion = 0;
2253     ULONG           defaultDvdRegion = 0;
2254     ULONG           dvdRegion = 0;
2255     WDFKEY          registryKey = NULL;
2256 
2257     DECLARE_CONST_UNICODE_STRING(registryValueName, DVD_DEFAULT_REGION);
2258 
2259     PAGED_CODE();
2260 
2261     if ((pickDvdRegion = InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, 0)) == 0)
2262     {
2263         // it was non-zero, so either another thread will do this, or
2264         // we no longer need to pick a region
2265         return;
2266     }
2267 
2268     bufferLen = max(
2269                     max(sizeof(DVD_DESCRIPTOR_HEADER) +
2270                             sizeof(DVD_COPYRIGHT_DESCRIPTOR),
2271                         sizeof(DVD_READ_STRUCTURE)
2272                         ),
2273                     max(DVD_RPC_KEY_LENGTH,
2274                         DVD_SET_RPC_KEY_LENGTH
2275                         )
2276                     );
2277 
2278     dvdReadStructure = (PDVD_READ_STRUCTURE)
2279                         ExAllocatePoolWithTag(PagedPool, bufferLen, DVD_TAG_DVD_REGION);
2280 
2281     if (dvdReadStructure == NULL)
2282     {
2283         InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, pickDvdRegion);
2284         return;
2285     }
2286 
2287     copyProtectKey = (PDVD_COPY_PROTECT_KEY)dvdReadStructure;
2288 
2289     dvdCopyRight = (PDVD_COPYRIGHT_DESCRIPTOR)
2290                         ((PDVD_DESCRIPTOR_HEADER)dvdReadStructure)->Data;
2291 
2292     // get the media region
2293     RtlZeroMemory (dvdReadStructure, bufferLen);
2294     dvdReadStructure->Format = DvdCopyrightDescriptor;
2295 
2296     // Build and send a request for READ_KEY
2297     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
2298                 "DevicePickDvdRegion (%p): Getting Copyright Descriptor\n",
2299                 Device));
2300 
2301     status = ReadDvdStructure(deviceExtension,
2302                               NULL,
2303                               dvdReadStructure,
2304                               sizeof(DVD_READ_STRUCTURE),
2305                               dvdReadStructure,
2306                               sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR),
2307                               &bytesReturned);
2308 
2309     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
2310                 "DevicePickDvdRegion (%p): Got Copyright Descriptor %x\n",
2311                 Device, status));
2312 
2313     if ((NT_SUCCESS(status)) &&
2314         (dvdCopyRight->CopyrightProtectionType == 0x01))
2315     {
2316         // keep the media region bitmap around
2317         // a 1 means ok to play
2318         if (dvdCopyRight->RegionManagementInformation == 0xff)
2319         {
2320             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2321                       "DevicePickDvdRegion (%p): RegionManagementInformation "
2322                       "is set to dis-allow playback for all regions.  This is "
2323                       "most likely a poorly authored disc.  defaulting to all "
2324                       "region disc for purpose of choosing initial region\n",
2325                       Device));
2326             dvdCopyRight->RegionManagementInformation = 0;
2327         }
2328 
2329         mediaRegion = ~dvdCopyRight->RegionManagementInformation;
2330     }
2331     else
2332     {
2333         // can't automatically pick a default region on a drive without media, so just exit
2334         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
2335                     "DevicePickDvdRegion (%p): failed to auto-choose a region due to status %x getting copyright descriptor\n",
2336                     Device, status));
2337         goto getout;
2338     }
2339 
2340     // get the device region
2341     RtlZeroMemory (copyProtectKey, bufferLen);
2342     copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
2343     copyProtectKey->KeyType = DvdGetRpcKey;
2344 
2345     // Build and send a request for READ_KEY for RPC key
2346     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
2347                 "DevicePickDvdRegion (%p): Getting RpcKey\n",
2348                 Device));
2349     status = DvdStartSessionReadKey(deviceExtension,
2350                                     IOCTL_DVD_READ_KEY,
2351                                     NULL,
2352                                     copyProtectKey,
2353                                     DVD_RPC_KEY_LENGTH,
2354                                     copyProtectKey,
2355                                     DVD_RPC_KEY_LENGTH,
2356                                     &bytesReturned);
2357 
2358     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
2359                 "DevicePickDvdRegion (%p): Got RpcKey %x\n",
2360                 Device, status));
2361 
2362     if (!NT_SUCCESS(status))
2363     {
2364         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
2365                     "DevicePickDvdRegion (%p): failed to get RpcKey from "
2366                     "a DVD Device\n", Device));
2367         goto getout;
2368     }
2369 
2370     // so we now have what we can get for the media region and the
2371     // drive region.  we will not set a region if the drive has one
2372     // set already (mask is not all 1's), nor will we set a region
2373     // if there are no more user resets available.
2374     rpcKey = (PDVD_RPC_KEY)copyProtectKey->KeyData;
2375 
2376     if (rpcKey->RegionMask != 0xff)
2377     {
2378         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2379                     "DevicePickDvdRegion (%p): not picking a region since "
2380                     "it is already chosen\n", Device));
2381         goto getout;
2382     }
2383 
2384     if (rpcKey->UserResetsAvailable <= 1)
2385     {
2386         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
2387                     "DevicePickDvdRegion (%p): not picking a region since "
2388                     "only one change remains\n", Device));
2389         goto getout;
2390     }
2391 
2392     // OOBE sets this key based upon the system locale
2393     status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
2394                                                 KEY_READ,
2395                                                 WDF_NO_OBJECT_ATTRIBUTES,
2396                                                 &registryKey);
2397 
2398     if (NT_SUCCESS(status))
2399     {
2400         status = WdfRegistryQueryULong(registryKey,
2401                                        &registryValueName,
2402                                        &defaultDvdRegion);
2403 
2404         WdfRegistryClose(registryKey);
2405     }
2406 
2407     if (!NT_SUCCESS(status))
2408     {
2409         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
2410                     "DevicePickDvdRegion (%p): failed to read registry value due to status %x\n",
2411                     Device, status));
2412 
2413         // by default the default Dvd region is 0
2414         defaultDvdRegion = 0;
2415         status = STATUS_SUCCESS;
2416     }
2417 
2418     if (defaultDvdRegion > DVD_MAX_REGION)
2419     {
2420         // the registry has a bogus default
2421         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
2422                     "DevicePickDvdRegion (%p): registry has a bogus default "
2423                     "region value of %x\n", Device, defaultDvdRegion));
2424 
2425         defaultDvdRegion = 0;
2426     }
2427 
2428     // if defaultDvdRegion == 0, it means no default.
2429 
2430     // we will select the initial dvd region for the user
2431 
2432     if ((defaultDvdRegion != 0) &&
2433         (mediaRegion & (1 << (defaultDvdRegion - 1))))
2434     {
2435         // first choice:
2436         // the media has region that matches
2437         // the default dvd region.
2438         dvdRegion = (1 << (defaultDvdRegion - 1));
2439 
2440         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2441                     "DevicePickDvdRegion (%p): Choice #1: media matches "
2442                     "drive's default, chose region %x\n", Device, dvdRegion));
2443     }
2444     else if (mediaRegion)
2445     {
2446         // second choice:
2447         // pick the lowest region number from the media
2448         UCHAR mask = 1;
2449         dvdRegion = 0;
2450 
2451         while (mediaRegion && !dvdRegion)
2452         {
2453             // pick the lowest bit
2454             dvdRegion = mediaRegion & mask;
2455             mask <<= 1;
2456         }
2457 
2458         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2459                     "DevicePickDvdRegion (%p): Choice #2: choosing lowest "
2460                     "media region %x\n", Device, dvdRegion));
2461     }
2462     else if (defaultDvdRegion)
2463     {
2464         // third choice:
2465         // default dvd region from the dvd class installer
2466         dvdRegion = (1 << (defaultDvdRegion - 1));
2467         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2468                     "DevicePickDvdRegion (%p): Choice #3: using default "
2469                     "region for this install %x\n", Device, dvdRegion));
2470     }
2471     else
2472     {
2473         // unable to pick one for the user -- this should rarely
2474         // happen, since the proppage dvd class installer sets
2475         // the key based upon the system locale
2476         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
2477                     "DevicePickDvdRegion (%p): Choice #4: failed to choose "
2478                     "a media region\n", Device));
2479         goto getout;
2480     }
2481 
2482     // now that we've chosen a region, set the region by sending the
2483     // appropriate request to the drive
2484     RtlZeroMemory (copyProtectKey, bufferLen);
2485     copyProtectKey->KeyLength = DVD_SET_RPC_KEY_LENGTH;
2486     copyProtectKey->KeyType = DvdSetRpcKey;
2487     dvdRpcKey = (PDVD_SET_RPC_KEY)copyProtectKey->KeyData;
2488     dvdRpcKey->PreferredDriveRegionCode = (UCHAR)~dvdRegion;
2489 
2490     // Build and send request for SEND_KEY
2491     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
2492                 "DevicePickDvdRegion (%p): Sending new Rpc Key to region %x\n",
2493                 Device, dvdRegion));
2494 
2495     status = DvdSendKey(deviceExtension,
2496                         NULL,
2497                         copyProtectKey,
2498                         DVD_SET_RPC_KEY_LENGTH,
2499                         &bytesReturned);
2500 
2501     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
2502                 "DevicePickDvdRegion (%p): Sent new Rpc Key %x\n",
2503                 Device, status));
2504 
2505     if (!NT_SUCCESS(status))
2506     {
2507         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "DevicePickDvdRegion (%p): unable to set dvd initial "
2508                      " region code (%x)\n", Device, status));
2509     }
2510     else
2511     {
2512         TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "DevicePickDvdRegion (%p): Successfully set dvd "
2513                      "initial region\n", Device));
2514 
2515         pickDvdRegion = 0;
2516     }
2517 
2518 getout:
2519     if (dvdReadStructure)
2520     {
2521         FREE_POOL(dvdReadStructure);
2522     }
2523 
2524     // update the new PickDvdRegion value
2525     InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, pickDvdRegion);
2526 
2527     return;
2528 }
2529 
2530 _IRQL_requires_max_(PASSIVE_LEVEL)
2531 NTSTATUS
2532 DeviceRegisterInterface(
2533     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
2534     _In_ CDROM_DEVICE_INTERFACES InterfaceType
2535     )
2536 /*++
2537 Routine Description:
2538 
2539     used to register device class interface or mount device interface
2540 
2541 Arguments:
2542 
2543     DeviceExtension - device context
2544 
2545     InterfaceType - interface type to be registered.
2546 
2547 Return Value:
2548 
2549     NTSTATUS
2550 
2551 --*/
2552 {
2553     NTSTATUS        status;
2554     WDFSTRING       string = NULL;
2555     GUID*           interfaceGuid = NULL;
2556     PUNICODE_STRING savingString = NULL;
2557     BOOLEAN         setRestricted = FALSE;
2558     UNICODE_STRING  localString;
2559 
2560     PAGED_CODE();
2561 
2562     //Get parameters
2563     switch(InterfaceType)
2564     {
2565     case CdRomDeviceInterface:
2566         interfaceGuid = (LPGUID)&GUID_DEVINTERFACE_CDROM;
2567         setRestricted = TRUE;
2568         savingString = &localString;
2569         break;
2570     case MountedDeviceInterface:
2571         interfaceGuid = (LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID;
2572         savingString = &(DeviceExtension->MountedDeviceInterfaceName);
2573         break;
2574     default:
2575         return STATUS_INVALID_PARAMETER;
2576     }
2577 
2578     status = WdfDeviceCreateDeviceInterface(DeviceExtension->Device,
2579                                             interfaceGuid,
2580                                             NULL);
2581 
2582     if (!NT_SUCCESS(status))
2583     {
2584         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
2585                     "DeviceRegisterInterface: Unable to register cdrom "
2586                     "DCA for fdo %p type: %s [%lx]\n",
2587                     DeviceExtension->Device,
2588                     (InterfaceType == CdRomDeviceInterface)? "CdRom Interface" : "Mounted Device Interface",
2589                     status));
2590     }
2591 
2592     // Retrieve interface string
2593     if (NT_SUCCESS(status))
2594     {
2595         // The string object will be released when its parent object is released.
2596         WDF_OBJECT_ATTRIBUTES  attributes;
2597 
2598         WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
2599         attributes.ParentObject = DeviceExtension->Device;
2600 
2601         status = WdfStringCreate(WDF_NO_OBJECT_ATTRIBUTES,
2602                                  NULL,
2603                                  &string);
2604     }
2605 
2606     if (NT_SUCCESS(status))
2607     {
2608         status = WdfDeviceRetrieveDeviceInterfaceString(DeviceExtension->Device,
2609                                                         interfaceGuid,
2610                                                         NULL,
2611                                                         string);
2612     }
2613 
2614     if (NT_SUCCESS(status))
2615     {
2616         WdfStringGetUnicodeString(string, savingString);
2617 
2618         if (setRestricted) {
2619 
2620 
2621             WdfObjectDelete(string);
2622         }
2623     }
2624 
2625     return status;
2626 } // end DeviceRegisterInterface()
2627 
2628 
2629 VOID
2630 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
2631 DeviceRestoreDefaultSpeed(
2632     _In_ WDFWORKITEM  WorkItem
2633     )
2634 /*++
2635 
2636 Routine Description:
2637 
2638     This workitem is called on a media change when the CDROM device
2639     speed should be restored to the default value.
2640 
2641 Arguments:
2642 
2643     Fdo      - Supplies the device object for the CDROM device.
2644     WorkItem - Supplies the pointer to the workitem.
2645 
2646 Return Value:
2647 
2648     None
2649 
2650 --*/
2651 {
2652     NTSTATUS                status;
2653     WDFDEVICE               device = WdfWorkItemGetParentObject(WorkItem);
2654     PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
2655     PPERFORMANCE_DESCRIPTOR perfDescriptor;
2656     ULONG                   transferLength = sizeof(PERFORMANCE_DESCRIPTOR);
2657     SCSI_REQUEST_BLOCK      srb = {0};
2658     PCDB                    cdb = (PCDB)srb.Cdb;
2659 
2660     PAGED_CODE();
2661 
2662     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceRestoreDefaultSpeed: Restore device speed for %p\n", device));
2663 
2664     perfDescriptor = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
2665                                            transferLength,
2666                                            CDROM_TAG_STREAM);
2667     if (perfDescriptor == NULL)
2668     {
2669         return;
2670     }
2671 
2672     RtlZeroMemory(perfDescriptor, transferLength);
2673 
2674     perfDescriptor->RestoreDefaults = TRUE;
2675 
2676     srb.TimeOutValue = deviceExtension->TimeOutValue;
2677 
2678     srb.CdbLength = 12;
2679     cdb->SET_STREAMING.OperationCode = SCSIOP_SET_STREAMING;
2680     REVERSE_BYTES_SHORT(&cdb->SET_STREAMING.ParameterListLength, &transferLength);
2681 
2682     status = DeviceSendSrbSynchronously(device,
2683                                         &srb,
2684                                         perfDescriptor,
2685                                         transferLength,
2686                                         TRUE,
2687                                         NULL);
2688     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2689                "DeviceRestoreDefaultSpeed: Set Streaming command completed with status: 0x%X\n", status));
2690 
2691     FREE_POOL(perfDescriptor);
2692     WdfObjectDelete(WorkItem);
2693 
2694     return;
2695 }
2696 
2697 // custom string match -- careful!
2698 _IRQL_requires_max_(APC_LEVEL)
2699 BOOLEAN
2700 StringsAreMatched(
2701     _In_opt_z_ PCHAR StringToMatch,
2702     _In_z_     PCHAR TargetString
2703     )
2704 /*++
2705 
2706 Routine Description:
2707 
2708     compares if two strings are identical
2709 
2710 Arguments:
2711 
2712     StringToMatch - source string.
2713     TargetString - target string.
2714 
2715 Return Value:
2716 
2717     BOOLEAN - TRUE (identical); FALSE (not match)
2718 
2719 --*/
2720 {
2721     size_t length;
2722 
2723     PAGED_CODE();
2724 
2725     NT_ASSERT(TargetString);
2726 
2727     // if no match requested, return TRUE
2728     if (StringToMatch == NULL)
2729     {
2730         return TRUE;
2731     }
2732 
2733     // cache the string length for efficiency
2734     length = strlen(StringToMatch);
2735 
2736     // ZERO-length strings may only match zero-length strings
2737     if (length == 0)
2738     {
2739         return (strlen(TargetString) == 0);
2740     }
2741 
2742     // strncmp returns zero if the strings match
2743     return (strncmp(StringToMatch, TargetString, length) == 0);
2744 }
2745 
2746 
2747 NTSTATUS
2748 RequestSetContextFields(
2749     _In_ WDFREQUEST    Request,
2750     _In_ PSYNC_HANDLER Handler
2751     )
2752 /*++
2753 
2754 Routine Description:
2755 
2756     set the request object context fields
2757 
2758 Arguments:
2759 
2760     Request - request object.
2761     Handler - the function that finally handles this request.
2762 
2763 Return Value:
2764 
2765     NTSTATUS
2766 
2767 --*/
2768 {
2769     NTSTATUS                status = STATUS_SUCCESS;
2770     PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(Request);
2771     PKEVENT                 syncEvent = NULL;
2772 
2773     syncEvent = ExAllocatePoolWithTag(NonPagedPoolNx,
2774                                       sizeof(KEVENT),
2775                                       CDROM_TAG_SYNC_EVENT);
2776 
2777     if (syncEvent == NULL)
2778     {
2779         // memory allocation failed.
2780         status = STATUS_INSUFFICIENT_RESOURCES;
2781     }
2782     else
2783     {
2784         // now, put the special synchronization information into the context
2785         requestContext->SyncRequired = TRUE;
2786         requestContext->SyncEvent = syncEvent;
2787         requestContext->SyncCallback = Handler;
2788 
2789         status = STATUS_SUCCESS;
2790     }
2791 
2792     return status;
2793 }
2794 
2795 
2796 NTSTATUS
2797 RequestDuidGetDeviceIdProperty(
2798     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
2799     _In_ WDFREQUEST              Request,
2800     _In_ WDF_REQUEST_PARAMETERS  RequestParameters,
2801     _Out_ size_t *               DataLength
2802     )
2803 /*++
2804 
2805 Routine Description:
2806 
2807 
2808 
2809 Arguments:
2810 
2811     DeviceExtension - device context
2812     Request - request object.
2813     RequestParameters - request parameter
2814     DataLength - transferred data length.
2815 
2816 Return Value:
2817 
2818     NTSTATUS
2819 
2820 --*/
2821 {
2822     NTSTATUS                        status = STATUS_SUCCESS;
2823     PSTORAGE_DEVICE_ID_DESCRIPTOR   deviceIdDescriptor = NULL;
2824     PSTORAGE_DESCRIPTOR_HEADER      descHeader = NULL;
2825     STORAGE_PROPERTY_ID             propertyId = StorageDeviceIdProperty;
2826 
2827     *DataLength = 0;
2828 
2829     // Get the VPD page 83h data.
2830     status = DeviceRetrieveDescriptor(DeviceExtension->Device,
2831                                       &propertyId,
2832                                       (PSTORAGE_DESCRIPTOR_HEADER*)&deviceIdDescriptor);
2833 
2834     if (NT_SUCCESS(status) && (deviceIdDescriptor == NULL))
2835     {
2836         status = STATUS_NOT_FOUND;
2837     }
2838 
2839     if (NT_SUCCESS(status))
2840     {
2841         status = WdfRequestRetrieveOutputBuffer(Request,
2842                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
2843                                                 &descHeader,
2844                                                 NULL);
2845     }
2846 
2847     if (NT_SUCCESS(status))
2848     {
2849         PSTORAGE_DEVICE_UNIQUE_IDENTIFIER   storageDuid = NULL;
2850         ULONG                               offset = descHeader->Size;
2851         PUCHAR                              dest = (PUCHAR)descHeader + offset;
2852         size_t                              outputBufferSize;
2853 
2854         outputBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
2855 
2856         // Adjust required size and potential destination location.
2857         status = RtlULongAdd(descHeader->Size, deviceIdDescriptor->Size, &descHeader->Size);
2858 
2859         if (NT_SUCCESS(status) &&
2860             (outputBufferSize < descHeader->Size))
2861         {
2862             // Output buffer is too small.  Return error and make sure
2863             // the caller gets info about required buffer size.
2864             *DataLength = descHeader->Size;
2865             status = STATUS_BUFFER_OVERFLOW;
2866         }
2867 
2868         if (NT_SUCCESS(status))
2869         {
2870             storageDuid = (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader;
2871             storageDuid->StorageDeviceIdOffset = offset;
2872 
2873             RtlCopyMemory(dest,
2874                           deviceIdDescriptor,
2875                           deviceIdDescriptor->Size);
2876 
2877             *DataLength = storageDuid->Size;
2878             status = STATUS_SUCCESS;
2879         }
2880 
2881         FREE_POOL(deviceIdDescriptor);
2882     }
2883 
2884     return status;
2885 }
2886 
2887 NTSTATUS
2888 RequestDuidGetDeviceProperty(
2889     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
2890     _In_ WDFREQUEST              Request,
2891     _In_ WDF_REQUEST_PARAMETERS  RequestParameters,
2892     _Out_ size_t *               DataLength
2893     )
2894 /*++
2895 
2896 Routine Description:
2897 
2898 
2899 
2900 Arguments:
2901 
2902     DeviceExtension - device context
2903     Request - request object.
2904     RequestParameters - request parameter
2905     DataLength - transferred data length.
2906 
2907 Return Value:
2908 
2909     NTSTATUS
2910 
2911 --*/
2912 {
2913     NTSTATUS                          status = STATUS_SUCCESS;
2914     PSTORAGE_DEVICE_DESCRIPTOR        deviceDescriptor = DeviceExtension->DeviceDescriptor;
2915     PSTORAGE_DESCRIPTOR_HEADER        descHeader = NULL;
2916     PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
2917     PUCHAR                            dest = NULL;
2918 
2919     if (deviceDescriptor == NULL)
2920     {
2921         status = STATUS_NOT_FOUND;
2922     }
2923 
2924     if (NT_SUCCESS(status))
2925     {
2926         status = WdfRequestRetrieveOutputBuffer(Request,
2927                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
2928                                                 &descHeader,
2929                                                 NULL);
2930     }
2931 
2932     if (NT_SUCCESS(status) &&
2933         (deviceDescriptor->SerialNumberOffset == 0))
2934     {
2935         status = STATUS_NOT_FOUND;
2936     }
2937 
2938     // Use this info only if serial number is available.
2939     if (NT_SUCCESS(status))
2940     {
2941         ULONG offset = descHeader->Size;
2942         size_t outputBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
2943 
2944         // Adjust required size and potential destination location.
2945         dest = (PUCHAR)descHeader + offset;
2946 
2947         status = RtlULongAdd(descHeader->Size, deviceDescriptor->Size, &descHeader->Size);
2948 
2949         if (NT_SUCCESS(status) &&
2950             (outputBufferSize < descHeader->Size))
2951         {
2952             // Output buffer is too small.  Return error and make sure
2953             // the caller get info about required buffer size.
2954             *DataLength = descHeader->Size;
2955             status = STATUS_BUFFER_OVERFLOW;
2956         }
2957 
2958         if (NT_SUCCESS(status))
2959         {
2960             storageDuid = (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader;
2961             storageDuid->StorageDeviceOffset = offset;
2962 
2963             RtlCopyMemory(dest,
2964                           deviceDescriptor,
2965                           deviceDescriptor->Size);
2966 
2967             *DataLength = storageDuid->Size;
2968             status = STATUS_SUCCESS;
2969         }
2970     }
2971 
2972     return status;
2973 }
2974 
2975 _IRQL_requires_max_(APC_LEVEL)
2976 ULONG
2977 DeviceRetrieveModeSenseUsingScratch(
2978     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
2979     _In_reads_bytes_(Length) PCHAR     ModeSenseBuffer,
2980     _In_ ULONG                    Length,
2981     _In_ UCHAR                    PageCode,
2982     _In_ UCHAR                    PageControl
2983     )
2984 /*++
2985 
2986 Routine Description:
2987 
2988     retrieve mode sense informaiton of the device
2989 
2990 Arguments:
2991 
2992     DeviceExtension - device context
2993     ModeSenseBuffer - buffer to savee the mode sense info.
2994     Length - buffer length
2995     PageCode - .
2996     PageControl -
2997 
2998 Return Value:
2999 
3000     ULONG - transferred data length
3001 
3002 --*/
3003 {
3004     NTSTATUS                    status = STATUS_SUCCESS;
3005     ULONG                       transferSize = min(Length, DeviceExtension->ScratchContext.ScratchBufferSize);
3006     CDB                         cdb;
3007 
3008     PAGED_CODE();
3009 
3010     ScratchBuffer_BeginUse(DeviceExtension);
3011 
3012     RtlZeroMemory(&cdb, sizeof(CDB));
3013     // Set up the CDB
3014     cdb.MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
3015     cdb.MODE_SENSE.PageCode = PageCode;
3016     cdb.MODE_SENSE.Pc = PageControl;
3017     cdb.MODE_SENSE.AllocationLength = (UCHAR)transferSize;
3018 
3019     status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, TRUE, &cdb, 6);
3020 
3021     if (NT_SUCCESS(status))
3022     {
3023         transferSize = min(Length, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
3024         RtlCopyMemory(ModeSenseBuffer,
3025                       DeviceExtension->ScratchContext.ScratchBuffer,
3026                       transferSize);
3027     }
3028 
3029     ScratchBuffer_EndUse(DeviceExtension);
3030 
3031     return transferSize;
3032 }
3033 
3034 _IRQL_requires_max_(APC_LEVEL)
3035 PVOID
3036 ModeSenseFindSpecificPage(
3037     _In_reads_bytes_(Length) PCHAR     ModeSenseBuffer,
3038     _In_ size_t                   Length,
3039     _In_ UCHAR                    PageMode,
3040     _In_ BOOLEAN                  Use6BytesCdb
3041     )
3042 /*++
3043 
3044 Routine Description:
3045 
3046     This routine scans through the mode sense data and finds the requested
3047     mode sense page code.
3048 
3049 Arguments:
3050     ModeSenseBuffer - Supplies a pointer to the mode sense data.
3051 
3052     Length - Indicates the length of valid data.
3053 
3054     PageMode - Supplies the page mode to be searched for.
3055 
3056     Use6BytesCdb - Indicates whether 6 or 10 byte mode sense was used.
3057 
3058 Return Value:
3059 
3060     A pointer to the the requested mode page.  If the mode page was not found
3061     then NULL is return.
3062 
3063 --*/
3064 {
3065     PCHAR limit;
3066     ULONG  parameterHeaderLength;
3067     PVOID  result = NULL;
3068 
3069     PAGED_CODE();
3070 
3071     limit = ModeSenseBuffer + Length;
3072     parameterHeaderLength = (Use6BytesCdb)
3073                             ? sizeof(MODE_PARAMETER_HEADER)
3074                             : sizeof(MODE_PARAMETER_HEADER10);
3075 
3076     if (Length >= parameterHeaderLength)
3077     {
3078         PMODE_PARAMETER_HEADER10 modeParam10;
3079         ULONG                    blockDescriptorLength;
3080 
3081         //  Skip the mode select header and block descriptors.
3082         if (Use6BytesCdb)
3083         {
3084             blockDescriptorLength = ((PMODE_PARAMETER_HEADER)ModeSenseBuffer)->BlockDescriptorLength;
3085         }
3086         else
3087         {
3088             modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
3089             blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
3090         }
3091 
3092         ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
3093 
3094         // ModeSenseBuffer now points at pages.  Walk the pages looking for the
3095         // requested page until the limit is reached.
3096         while (ModeSenseBuffer +
3097                RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit)
3098         {
3099             if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode)
3100             {
3101                 // found the mode page.  make sure it's safe to touch it all
3102                 // before returning the pointer to caller
3103                 if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit)
3104                 {
3105                     //  Return NULL since the page is not safe to access in full
3106                     result = NULL;
3107                 }
3108                 else
3109                 {
3110                     result = ModeSenseBuffer;
3111                 }
3112                 break;
3113             }
3114 
3115             // Advance to the next page which is 4-byte-aligned offset after this page.
3116             ModeSenseBuffer += ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
3117                                 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
3118         }
3119     }
3120 
3121     return result;
3122 } // end ModeSenseFindSpecificPage()
3123 
3124 
3125 _IRQL_requires_max_(PASSIVE_LEVEL)
3126 NTSTATUS
3127 PerformEjectionControl(
3128     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
3129     _In_ WDFREQUEST               Request,
3130     _In_ MEDIA_LOCK_TYPE          LockType,
3131     _In_ BOOLEAN                  Lock
3132     )
3133 /*++
3134 
3135 Routine Description:
3136 
3137     ejection control process
3138 
3139 Arguments:
3140 
3141     DeviceExtension - device extension
3142     Request - WDF request to be used for communication with the device
3143     LockType - the type of lock
3144     Lock - if TRUE, lock the device; if FALSE, unlock it
3145 
3146 Return Value:
3147 
3148     NTSTATUS
3149 
3150 --*/
3151 {
3152     NTSTATUS                status;
3153     PFILE_OBJECT_CONTEXT    fileObjectContext = NULL;
3154     SCSI_REQUEST_BLOCK      srb;
3155     PCDB                    cdb = NULL;
3156 
3157     LONG                    newLockCount = 0;
3158     LONG                    newProtectedLockCount = 0;
3159     LONG                    newInternalLockCount = 0;
3160     LONG                    newFileLockCount = 0;
3161     BOOLEAN                 countChanged = FALSE;
3162     BOOLEAN                 previouslyLocked = FALSE;
3163     BOOLEAN                 nowLocked = FALSE;
3164 
3165     PAGED_CODE();
3166 
3167     // Prevent race conditions while working with lock counts
3168     status = WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL);
3169     if (!NT_SUCCESS(status))
3170     {
3171         NT_ASSERT(FALSE);
3172     }
3173 
3174     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
3175                 "PerformEjectionControl: "
3176                 "Received request for %s lock type\n",
3177                 LockTypeStrings[LockType]
3178                 ));
3179 
3180 
3181     // If this is a "secured" request, retrieve the file object context
3182     if (LockType == SecureMediaLock)
3183     {
3184         WDFFILEOBJECT fileObject = NULL;
3185 
3186         fileObject = WdfRequestGetFileObject(Request);
3187 
3188         if (fileObject == NULL)
3189         {
3190             status = STATUS_INVALID_HANDLE;
3191 
3192             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
3193                             "FileObject does not match to the one in IRP_MJ_CREATE, KMDF returns NULL\n"));
3194             goto Exit;
3195         }
3196 
3197         fileObjectContext = FileObjectGetContext(fileObject);
3198         NT_ASSERT(fileObjectContext != NULL);
3199     }
3200 
3201     // Lock counts should never fall below 0
3202     NT_ASSERT(DeviceExtension->LockCount >= 0);
3203     NT_ASSERT(DeviceExtension->ProtectedLockCount >= 0);
3204     NT_ASSERT(DeviceExtension->InternalLockCount >= 0);
3205 
3206     // Get the current lock counts
3207     newLockCount = DeviceExtension->LockCount;
3208     newProtectedLockCount = DeviceExtension->ProtectedLockCount;
3209     newInternalLockCount = DeviceExtension->InternalLockCount;
3210     if (fileObjectContext)
3211     {
3212         // fileObjectContext->LockCount is ULONG and should always >= 0
3213         newFileLockCount = fileObjectContext->LockCount;
3214     }
3215 
3216     // Determine which lock counts need to be changed and how
3217     if (Lock && LockType == SimpleMediaLock)
3218     {
3219         newLockCount++;
3220         countChanged = TRUE;
3221     }
3222     else if (Lock && LockType == SecureMediaLock)
3223     {
3224         newFileLockCount++;
3225         newProtectedLockCount++;
3226         countChanged = TRUE;
3227     }
3228     else if (Lock && LockType == InternalMediaLock)
3229     {
3230         newInternalLockCount++;
3231         countChanged = TRUE;
3232     }
3233     else if (!Lock && LockType == SimpleMediaLock)
3234     {
3235         if (newLockCount != 0)
3236         {
3237             newLockCount--;
3238             countChanged = TRUE;
3239         }
3240     }
3241     else if (!Lock && LockType == SecureMediaLock)
3242     {
3243         if ( (newFileLockCount == 0) || (newProtectedLockCount == 0) )
3244         {
3245             status = STATUS_INVALID_DEVICE_STATE;
3246             goto Exit;
3247         }
3248         newFileLockCount--;
3249         newProtectedLockCount--;
3250         countChanged = TRUE;
3251     }
3252     else if (!Lock && LockType == InternalMediaLock)
3253     {
3254         NT_ASSERT(newInternalLockCount != 0);
3255         newInternalLockCount--;
3256         countChanged = TRUE;
3257     }
3258 
3259     if ( (DeviceExtension->LockCount != 0) ||
3260          (DeviceExtension->ProtectedLockCount != 0) ||
3261          (DeviceExtension->InternalLockCount != 0) )
3262     {
3263         previouslyLocked = TRUE;
3264     }
3265     if ( (newLockCount != 0) ||
3266          (newProtectedLockCount != 0) ||
3267          (newInternalLockCount != 0) )
3268     {
3269         nowLocked = TRUE;
3270     }
3271 
3272     // Only send command down to device when necessary
3273     if (previouslyLocked != nowLocked)
3274     {
3275         // Compose and send the PREVENT ALLOW MEDIA REMOVAL command.
3276         RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
3277 
3278         srb.CdbLength = 6;
3279         srb.TimeOutValue = DeviceExtension->TimeOutValue;
3280 
3281         cdb = (PCDB)&srb.Cdb;
3282         cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
3283         cdb->MEDIA_REMOVAL.Prevent = Lock;
3284 
3285         status = DeviceSendSrbSynchronously(DeviceExtension->Device,
3286                                             &srb,
3287                                             NULL,
3288                                             0,
3289                                             FALSE,
3290                                             Request);
3291     }
3292 
3293 Exit:
3294 
3295     // Store the updated lock counts on success
3296     if (countChanged && NT_SUCCESS(status))
3297     {
3298         DeviceExtension->LockCount = newLockCount;
3299         DeviceExtension->ProtectedLockCount = newProtectedLockCount;
3300         DeviceExtension->InternalLockCount = newInternalLockCount;
3301         if (fileObjectContext)
3302         {
3303             fileObjectContext->LockCount = newFileLockCount;
3304         }
3305     }
3306 
3307     WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock);
3308 
3309     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
3310                 "PerformEjectionControl: %!STATUS!, "
3311                 "Count Changed: %d, Command Sent: %d, "
3312                 "Current Counts: Internal: %x  Secure: %x  Simple: %x\n",
3313                 status,
3314                 countChanged,
3315                 previouslyLocked != nowLocked,
3316                 DeviceExtension->InternalLockCount,
3317                 DeviceExtension->ProtectedLockCount,
3318                 DeviceExtension->LockCount
3319                 ));
3320 
3321     return status;
3322 }
3323 
3324 
3325 _IRQL_requires_max_(APC_LEVEL)
3326 NTSTATUS
3327 DeviceUnlockExclusive(
3328     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
3329     _In_ WDFFILEOBJECT            FileObject,
3330     _In_ BOOLEAN                  IgnorePreviousMediaChanges
3331     )
3332 /*++
3333 
3334 Routine Description:
3335 
3336     to unlock the exclusive lock
3337 
3338 Arguments:
3339 
3340     DeviceExtension - device context
3341     FileObject - file object that currently holds the lock
3342     IgnorePreviousMediaChanges - if TRUE, ignore previously accumulated media changes
3343 
3344 Return Value:
3345 
3346     NTSTATUS
3347 
3348 --*/
3349 {
3350     NTSTATUS                        status = STATUS_SUCCESS;
3351     PCDROM_DATA                     cdData = &DeviceExtension->DeviceAdditionalData;
3352     PMEDIA_CHANGE_DETECTION_INFO    info = DeviceExtension->MediaChangeDetectionInfo;
3353     BOOLEAN                         ANPending = 0;
3354     LONG                            requestInUse = 0;
3355 
3356     PAGED_CODE();
3357 
3358     if (!EXCLUSIVE_MODE(cdData))
3359     {
3360         // Device is not locked for exclusive access.
3361         // Can not process unlock request.
3362         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
3363                     "RequestHandleExclusiveAccessUnlockDevice: Device not locked for exclusive access, can't unlock device.\n"));
3364         status = STATUS_INVALID_DEVICE_REQUEST;
3365     }
3366     else if (!EXCLUSIVE_OWNER(cdData, FileObject))
3367     {
3368         // Request not from the exclusive owner, can't unlock the device.
3369         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
3370                    "RequestHandleExclusiveAccessUnlockDevice: Unable to unlock device, invalid file object\n"));
3371 
3372         status = STATUS_INVALID_HANDLE;
3373     }
3374 
3375     if (NT_SUCCESS(status))
3376     {
3377         // Unless we were explicitly requested not to do so, generate a media removal notification
3378         // followed by a media arrival notification similar to volume lock/unlock file system events.
3379         if (!IgnorePreviousMediaChanges)
3380         {
3381             MEDIA_CHANGE_DETECTION_STATE previousMediaState = MediaUnknown;
3382 
3383             // Change the media state to "unavailable", which will cause a removal notification if the media
3384             // was previously present. At the same time, store the previous state in previousMediaState.
3385             DeviceSetMediaChangeStateEx(DeviceExtension, MediaUnavailable, &previousMediaState);
3386             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
3387                         "DeviceUnlockExclusive: Changing the media state to MediaUnavailable\n"));
3388 
3389             // Restore the previous media state, which will cause a media arrival notification if the media
3390             // was originally present.
3391             DeviceSetMediaChangeStateEx(DeviceExtension, previousMediaState, NULL);
3392         }
3393 
3394         // Set DO_VERIFY_VOLUME so that the file system will remount on it.
3395         if (IsVolumeMounted(DeviceExtension->DeviceObject))
3396         {
3397             SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
3398         }
3399 
3400         // Set MMC state to update required
3401         cdData->Mmc.WriteAllowed = FALSE;
3402         cdData->Mmc.UpdateState = CdromMmcUpdateRequired;
3403 
3404         // Send unlock notification
3405         DeviceSendNotification(DeviceExtension,
3406                                &GUID_IO_CDROM_EXCLUSIVE_UNLOCK,
3407                                0,
3408                                NULL);
3409 
3410         InterlockedExchangePointer((PVOID)&cdData->ExclusiveOwner,  NULL);
3411 
3412         if ((info != NULL) && (info->AsynchronousNotificationSupported != FALSE))
3413         {
3414             ANPending = info->ANSignalPendingDueToExclusiveLock;
3415             info->ANSignalPendingDueToExclusiveLock = FALSE;
3416 
3417             if ((ANPending != FALSE) && (info->MediaChangeDetectionDisableCount == 0))
3418             {
3419                 // if the request is not in use, mark it as such.
3420                 requestInUse = InterlockedCompareExchange((PLONG)&info->MediaChangeRequestInUse, 1, 0);
3421 
3422                 if (requestInUse == 0)
3423                 {
3424                     // The last MCN finished. ok to issue the new one.
3425                     RequestSetupMcnSyncIrp(DeviceExtension);
3426 
3427                     // The irp will go into KMDF framework and a request will be created there to represent it.
3428                     IoCallDriver(DeviceExtension->DeviceObject, info->MediaChangeSyncIrp);
3429                 }
3430             }
3431         }
3432     }
3433 
3434     return status;
3435 }
3436 
3437 
3438 VOID
3439 RequestCompletion(
3440     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
3441     _In_ WDFREQUEST              Request,
3442     _In_ NTSTATUS                Status,
3443     _In_ ULONG_PTR               Information
3444     )
3445 {
3446 #ifdef DBG
3447     ULONG                   ioctlCode = 0;
3448     WDF_REQUEST_PARAMETERS  requestParameters;
3449 
3450     // Get the Request parameters
3451     WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
3452     WdfRequestGetParameters(Request, &requestParameters);
3453 
3454     if (requestParameters.Type == WdfRequestTypeDeviceControl)
3455     {
3456         ioctlCode = requestParameters.Parameters.DeviceIoControl.IoControlCode;
3457 
3458         if (requestParameters.Parameters.DeviceIoControl.IoControlCode != IOCTL_MCN_SYNC_FAKE_IOCTL)
3459         {
3460             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
3461                        "Request complete - IOCTL - code: %X; Status: %X; Information: %X\n",
3462                        ioctlCode,
3463                        Status,
3464                        (ULONG)Information));
3465         }
3466         else
3467         {
3468             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
3469                        "Request complete - IOCTL - code: %X; Status: %X; Information: %X\n",
3470                        ioctlCode,
3471                        Status,
3472                        (ULONG)Information));
3473         }
3474     }
3475     else if (requestParameters.Type == WdfRequestTypeRead)
3476     {
3477         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
3478                    "Request complete - READ - Starting Offset: %X; Length: %X; Transferred Length: %X; Status: %X\n",
3479                    (ULONG)requestParameters.Parameters.Read.DeviceOffset,
3480                    (ULONG)requestParameters.Parameters.Read.Length,
3481                    (ULONG)Information,
3482                    Status));
3483     }
3484     else if (requestParameters.Type == WdfRequestTypeWrite)
3485     {
3486         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
3487                    "Request complete - WRITE - Starting Offset: %X; Length: %X; Transferred Length: %X; Status: %X\n",
3488                    (ULONG)requestParameters.Parameters.Write.DeviceOffset,
3489                    (ULONG)requestParameters.Parameters.Write.Length,
3490                    (ULONG)Information,
3491                    Status));
3492     }
3493 #endif
3494 
3495     if (IoIsErrorUserInduced(Status))
3496     {
3497         PIRP irp = WdfRequestWdmGetIrp(Request);
3498         if (irp->Tail.Overlay.Thread)
3499         {
3500             IoSetHardErrorOrVerifyDevice(irp, DeviceExtension->DeviceObject);
3501         }
3502     }
3503 
3504     if (!NT_SUCCESS(Status) && DeviceExtension->SurpriseRemoved == TRUE)
3505     {
3506         // IMAPI expects ERROR_DEV_NOT_EXISTS if recorder has been surprised removed,
3507         // or it will retry WRITE commands for up to 3 minutes
3508         // CDROM behavior should be consistent for all requests, including SCSI pass-through
3509         Status = STATUS_DEVICE_DOES_NOT_EXIST;
3510     }
3511 
3512     WdfRequestCompleteWithInformation(Request, Status, Information);
3513 
3514     return;
3515 }
3516 
3517 
3518 VOID
3519 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
3520 RequestDummyCompletionRoutine(
3521     _In_ WDFREQUEST                     Request,
3522     _In_ WDFIOTARGET                    Target,
3523     _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
3524     _In_ WDFCONTEXT                     Context
3525     )
3526 /*++
3527 
3528 Routine Description:
3529 
3530     This is a dummy competion routine that simply calls WdfRequestComplete. We have to use
3531     this dummy competion routine instead of WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET, because
3532     the latter causes the framework to not check if the I/O target is closed or not.
3533 
3534 Arguments:
3535 
3536     Request - completed request
3537     Target - the I/O target that completed the request
3538     Params - request parameters
3539     Context - not used
3540 
3541 Return Value:
3542 
3543     none
3544 
3545 --*/
3546 {
3547     UNREFERENCED_PARAMETER(Target);
3548     UNREFERENCED_PARAMETER(Params);
3549     UNREFERENCED_PARAMETER(Context);
3550 
3551     WdfRequestCompleteWithInformation(Request,
3552                                       WdfRequestGetStatus(Request),
3553                                       WdfRequestGetInformation(Request));
3554 }
3555 
3556 
3557 _IRQL_requires_max_(DISPATCH_LEVEL)
3558 NTSTATUS
3559 DeviceSendPowerDownProcessRequest(
3560     _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
3561     _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
3562     _In_opt_ PVOID Context
3563     )
3564 /*++
3565 
3566 Routine Description:
3567 
3568     This function is called during processing power down request.
3569     It is used to send either SYNC CACHE command or STOP UNIT command.
3570 
3571     Caller should set proper value in deviceExtension->PowerContext.PowerChangeState.PowerDown
3572     to trigger the correct command be sent.
3573 
3574 Arguments:
3575 
3576     DeviceExtension -
3577 
3578     CompletionRoutine - Completion routine that needs to be set for the request
3579 
3580     Context - Completion context associated with the completion routine
3581 
3582 Return Value:
3583 
3584     NTSTATUS
3585 
3586 --*/
3587 {
3588     NTSTATUS                    status;
3589     BOOLEAN                     requestSent = FALSE;
3590 
3591     BOOLEAN                     shouldRetry = TRUE;
3592     PCDB                        cdb = (PCDB)DeviceExtension->PowerContext.Srb.Cdb;
3593     ULONG                       timeoutValue = DeviceExtension->TimeOutValue;
3594     ULONG                       retryCount = 1;
3595 
3596     // reset some fields.
3597     DeviceExtension->PowerContext.RetryIntervalIn100ns = 0;
3598     status = PowerContextReuseRequest(DeviceExtension);
3599     RequestClearSendTime(DeviceExtension->PowerContext.PowerRequest);
3600 
3601     if (!NT_SUCCESS(status))
3602     {
3603         return status;
3604     }
3605 
3606     // set proper timeout value and max retry count.
3607     switch(DeviceExtension->PowerContext.PowerChangeState.PowerDown)
3608     {
3609     case PowerDownDeviceInitial:
3610     case PowerDownDeviceQuiesced:
3611     case PowerDownDeviceStopped:
3612         break;
3613 
3614     case PowerDownDeviceLocked:
3615         // Case of issuing SYNC CACHE command. Do not use power irp timeout remaining time in this case
3616         // as we want to give best try on SYNC CACHE command.
3617         retryCount = MAXIMUM_RETRIES;
3618         timeoutValue = DeviceExtension->TimeOutValue;
3619         break;
3620 
3621     case PowerDownDeviceFlushed:
3622     {
3623         // Case of issuing STOP UNIT command
3624         // As "Imme" bit is set to '1', this command should be completed in short time.
3625         // This command is at low importance, failure of this command has very small impact.
3626         ULONG secondsRemaining = 0;
3627 
3628 #if (WINVER >= 0x0601)
3629         // this API is introduced in Windows7
3630         PoQueryWatchdogTime(DeviceExtension->LowerPdo, &secondsRemaining);
3631 #endif
3632 
3633         if (secondsRemaining == 0)
3634         {
3635             // not able to retrieve remaining time from PoQueryWatchdogTime API, use default values.
3636             retryCount = MAXIMUM_RETRIES;
3637             timeoutValue = SCSI_CDROM_TIMEOUT;
3638         }
3639         else
3640         {
3641             // plan to leave about 30 seconds to lower level drivers if possible.
3642             if (secondsRemaining >= 32)
3643             {
3644                 retryCount = (secondsRemaining - 30)/SCSI_CDROM_TIMEOUT + 1;
3645                 timeoutValue = SCSI_CDROM_TIMEOUT;
3646 
3647                 if (retryCount > MAXIMUM_RETRIES)
3648                 {
3649                     retryCount = MAXIMUM_RETRIES;
3650                 }
3651 
3652                 if (retryCount == 1)
3653                 {
3654                     timeoutValue = secondsRemaining - 30;
3655                 }
3656             }
3657             else
3658             {
3659                 // issue the command with minimal timeout value and do not retry on it.
3660                 retryCount = 1;
3661                 timeoutValue = 2;
3662             }
3663         }
3664     }
3665         break;
3666     default:
3667         NT_ASSERT( FALSE );
3668         status = STATUS_NOT_IMPLEMENTED;
3669         return status;
3670     }
3671 
3672     DeviceExtension->PowerContext.RetryCount = retryCount;
3673 
3674     // issue command.
3675     while (shouldRetry)
3676     {
3677 
3678         // set SRB fields.
3679         DeviceExtension->PowerContext.Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
3680                                                      SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
3681                                                      SRB_FLAGS_NO_QUEUE_FREEZE |
3682                                                      SRB_FLAGS_BYPASS_LOCKED_QUEUE |
3683                                                      SRB_FLAGS_D3_PROCESSING;
3684 
3685         DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
3686         DeviceExtension->PowerContext.Srb.TimeOutValue = timeoutValue;
3687 
3688         if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceInitial)
3689         {
3690             DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_LOCK_QUEUE;
3691         }
3692         else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceLocked)
3693         {
3694             DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_QUIESCE_DEVICE;
3695         }
3696         else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced)
3697         {
3698             // Case of issuing SYNC CACHE command.
3699             DeviceExtension->PowerContext.Srb.CdbLength = 10;
3700             cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
3701         }
3702         else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceFlushed)
3703         {
3704             // Case of issuing STOP UNIT command.
3705             DeviceExtension->PowerContext.Srb.CdbLength = 6;
3706             cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
3707             cdb->START_STOP.Start = 0;
3708             cdb->START_STOP.Immediate = 1;
3709         }
3710         else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceStopped)
3711         {
3712             DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
3713         }
3714 
3715         // Set up completion routine and context if requested
3716         if (CompletionRoutine)
3717         {
3718             WdfRequestSetCompletionRoutine(DeviceExtension->PowerContext.PowerRequest,
3719                                            CompletionRoutine,
3720                                            Context);
3721         }
3722 
3723         status = RequestSend(DeviceExtension,
3724                              DeviceExtension->PowerContext.PowerRequest,
3725                              DeviceExtension->IoTarget,
3726                              CompletionRoutine ? 0 : WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
3727                              &requestSent);
3728 
3729         if (requestSent)
3730         {
3731             if ((CompletionRoutine == NULL) &&
3732                 (SRB_STATUS(DeviceExtension->PowerContext.Srb.SrbStatus) != SRB_STATUS_SUCCESS))
3733             {
3734                 TracePrint((TRACE_LEVEL_ERROR,
3735                             TRACE_FLAG_POWER,
3736                             "%p\tError occured when issuing %s command to device. Srb %p, Status %x\n",
3737                             DeviceExtension->PowerContext.PowerRequest,
3738                             (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced) ? "SYNC CACHE" : "STOP UNIT",
3739                             &DeviceExtension->PowerContext.Srb,
3740                             DeviceExtension->PowerContext.Srb.SrbStatus));
3741 
3742                 NT_ASSERT(!(TEST_FLAG(DeviceExtension->PowerContext.Srb.SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
3743 
3744                 shouldRetry = RequestSenseInfoInterpret(DeviceExtension,
3745                                                         DeviceExtension->PowerContext.PowerRequest,
3746                                                         &(DeviceExtension->PowerContext.Srb),
3747                                                         retryCount - DeviceExtension->PowerContext.RetryCount,
3748                                                         &status,
3749                                                         &(DeviceExtension->PowerContext.RetryIntervalIn100ns));
3750 
3751                 if (shouldRetry && (DeviceExtension->PowerContext.RetryCount-- == 0))
3752                 {
3753                     shouldRetry = FALSE;
3754                 }
3755             }
3756             else
3757             {
3758                 // succeeded, do not need to retry.
3759                 shouldRetry = FALSE;
3760             }
3761 
3762         }
3763         else
3764         {
3765             // request failed to be sent
3766             shouldRetry = FALSE;
3767         }
3768 
3769         if (shouldRetry)
3770         {
3771             LARGE_INTEGER t;
3772             t.QuadPart = -DeviceExtension->PowerContext.RetryIntervalIn100ns;
3773             KeDelayExecutionThread(KernelMode, FALSE, &t);
3774 
3775             status = PowerContextReuseRequest(DeviceExtension);
3776             if (!NT_SUCCESS(status))
3777             {
3778                 shouldRetry = FALSE;
3779             }
3780         }
3781     }
3782 
3783     if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced)
3784     {
3785         // record SYNC CACHE command completion time stamp.
3786         KeQueryTickCount(&DeviceExtension->PowerContext.Step1CompleteTime);
3787     }
3788 
3789     return status;
3790 }
3791 
3792 NTSTATUS
3793 RequestSend(
3794     _In_        PCDROM_DEVICE_EXTENSION DeviceExtension,
3795     _In_        WDFREQUEST              Request,
3796     _In_        WDFIOTARGET             IoTarget,
3797     _In_        ULONG                   Flags,
3798     _Out_opt_   PBOOLEAN                RequestSent
3799     )
3800 /*++
3801 
3802 Routine Description:
3803 
3804     Send the request to the target, wake up the device from Zero Power state if necessary.
3805 
3806 Arguments:
3807 
3808     DeviceExtension - device extension
3809     Request - the request to be sent
3810     IoTarget - target of the above request
3811     Flags - flags for the operation
3812     RequestSent - optional, if the request was sent
3813 
3814 Return Value:
3815 
3816     NTSTATUS
3817 
3818 --*/
3819 {
3820     NTSTATUS                 status  = STATUS_SUCCESS;
3821     BOOLEAN                  requestSent = FALSE;
3822     WDF_REQUEST_SEND_OPTIONS options;
3823 
3824     UNREFERENCED_PARAMETER(DeviceExtension);
3825 
3826     if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
3827         (DeviceExtension->ZeroPowerODDInfo->InZeroPowerState != FALSE))
3828     {
3829     }
3830 
3831     // Now send down the request
3832     if (NT_SUCCESS(status))
3833     {
3834         WDF_REQUEST_SEND_OPTIONS_INIT(&options, Flags);
3835 
3836         RequestSetSentTime(Request);
3837 
3838         // send request and check status
3839 
3840         // Disable SDV warning about infinitely waiting in caller's context:
3841         //   1. Some requests (such as SCSI_PASS_THROUGH, contains buffer from user space) need to be sent down in caller�s context.
3842         //      Consequently, these requests wait in caller�s context until they are allowed to be sent down.
3843         //   2. Considering the situation that during sleep, a request can be hold by storage port driver. When system resumes, any time out value (if we set using KMDF time out value) might be expires.
3844         //      This will cause the waiting request being failed (behavior change). We�d rather not set time out value.
3845 
3846         _Analysis_assume_(options.Timeout != 0);
3847         requestSent = WdfRequestSend(Request, IoTarget, &options);
3848         _Analysis_assume_(options.Timeout == 0);
3849 
3850         // If WdfRequestSend fails, or if the WDF_REQUEST_SEND_OPTION_SYNCHRONOUS flag is set,
3851         // the driver can call WdfRequestGetStatus immediately after calling WdfRequestSend.
3852         if ((requestSent == FALSE) ||
3853             (Flags & WDF_REQUEST_SEND_OPTION_SYNCHRONOUS))
3854         {
3855             status = WdfRequestGetStatus(Request);
3856 
3857             if (requestSent == FALSE)
3858             {
3859                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
3860                             "WdfRequestSend failed: %lx\n",
3861                             status
3862                             ));
3863             }
3864         }
3865         else
3866         {
3867             status = STATUS_SUCCESS;
3868         }
3869 
3870         if (RequestSent != NULL)
3871         {
3872             *RequestSent = requestSent;
3873         }
3874     }
3875 
3876     return status;
3877 }
3878 
3879