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