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
RequestSetReceivedTime(_In_ WDFREQUEST Request)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
RequestSetSentTime(_In_ WDFREQUEST Request)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
RequestClearSendTime(_In_ WDFREQUEST Request)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
_IRQL_requires_max_(PASSIVE_LEVEL)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(®istryValueName, ParameterName);
172
173 if (SubkeyName != NULL)
174 {
175 RtlInitUnicodeString(®istrySubKeyName, 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 ®istrySubKeyName,
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 ®istryValueName,
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 ®istrySubKeyName,
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 ®istryValueName,
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
_IRQL_requires_max_(PASSIVE_LEVEL)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(®istryValueName, ParameterName);
329
330 if (SubkeyName != NULL)
331 {
332 RtlInitUnicodeString(®istrySubKeyName, 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 ®istrySubKeyName,
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 ®istryValueName,
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
_IRQL_requires_max_(APC_LEVEL)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
_IRQL_requires_max_(PASSIVE_LEVEL)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(¶ms,
628 WDF_REQUEST_REUSE_NO_FLAGS,
629 STATUS_SUCCESS);
630
631 status = WdfRequestReuse(request, ¶ms);
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
DeviceSendNotification(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ const GUID * Guid,_In_ ULONG ExtraDataSize,_In_opt_ PVOID ExtraData)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
DeviceSendStartUnit(_In_ WDFDEVICE Device)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
DeviceSendIoctlAsynchronously(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ ULONG IoControlCode,_In_ PDEVICE_OBJECT TargetDeviceObject)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 */
DeviceAsynchronousCompletion(_In_ WDFREQUEST Request,_In_ WDFIOTARGET Target,_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,_In_ WDFCONTEXT Context)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
DeviceReleaseQueue(_In_ WDFDEVICE Device)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, ¤tIrql);
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 */
DeviceReleaseQueueCompletion(_In_ WDFREQUEST Request,_In_ WDFIOTARGET Target,_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,_In_ WDFCONTEXT Context)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(¶ms,
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,¶ms);
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
DevicePerfIncrementErrorCount(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension)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
_IRQL_requires_max_(APC_LEVEL)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
_IRQL_requires_max_(APC_LEVEL)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
_IRQL_requires_max_(PASSIVE_LEVEL)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
_IRQL_requires_max_(APC_LEVEL)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
_IRQL_requires_max_(APC_LEVEL)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 ®istryKey);
2397
2398 if (NT_SUCCESS(status))
2399 {
2400 status = WdfRegistryQueryULong(registryKey,
2401 ®istryValueName,
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
_IRQL_requires_max_(PASSIVE_LEVEL)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 */
DeviceRestoreDefaultSpeed(_In_ WDFWORKITEM WorkItem)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!
_IRQL_requires_max_(APC_LEVEL)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
RequestSetContextFields(_In_ WDFREQUEST Request,_In_ PSYNC_HANDLER Handler)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
RequestDuidGetDeviceIdProperty(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)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
RequestDuidGetDeviceProperty(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)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
_IRQL_requires_max_(APC_LEVEL)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
_IRQL_requires_max_(APC_LEVEL)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
_IRQL_requires_max_(PASSIVE_LEVEL)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
_IRQL_requires_max_(APC_LEVEL)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
RequestCompletion(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ NTSTATUS Status,_In_ ULONG_PTR Information)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 */
RequestDummyCompletionRoutine(_In_ WDFREQUEST Request,_In_ WDFIOTARGET Target,_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,_In_ WDFCONTEXT Context)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
_IRQL_requires_max_(DISPATCH_LEVEL)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
RequestSend(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDFIOTARGET IoTarget,_In_ ULONG Flags,_Out_opt_ PBOOLEAN RequestSent)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