[CDROM_NEW] Import Microsoft CDROM class driver from GitHub
[reactos.git] / drivers / storage / class / cdrom_new / autorun.c
1 /*++
2
3 Copyright (C) Microsoft Corporation. All rights reserved.
4
5 Module Name:
6
7 autorun.c
8
9 Abstract:
10
11 Code for support of media change detection in the cd/dvd driver
12
13 Environment:
14
15 kernel mode only
16
17 Notes:
18
19
20 Revision History:
21
22 --*/
23
24 #include "stddef.h"
25 #include "string.h"
26
27 #include "ntddk.h"
28 #include "ntddstor.h"
29 #include "cdrom.h"
30 #include "mmc.h"
31 #include "ioctl.h"
32
33 #include "ntstrsafe.h"
34
35 #ifdef DEBUG_USE_WPP
36 #include "autorun.tmh"
37 #endif
38
39 #define GESN_TIMEOUT_VALUE (0x4)
40 #define GESN_BUFFER_SIZE (0x8)
41 #define GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS (2)
42
43 #define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20)
44 #define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification")
45 #define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN")
46 #define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN")
47
48 //
49 // Only send polling irp when device is fully powered up and a
50 // power down irp is not in progress.
51 //
52 // NOTE: This helps close a window in time where a polling irp could cause
53 // a drive to spin up right after it has powered down. The problem is
54 // that SCSIPORT, ATAPI and SBP2 will be in the process of powering
55 // down (which may take a few seconds), but won't know that. It would
56 // then get a polling irp which will be put into its queue since it
57 // the disk isn't powered down yet. Once the disk is powered down it
58 // will find the polling irp in the queue and then power up the
59 // device to do the poll. They do not want to check if the polling
60 // irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical
61 // path and would slow down all I/Os. A better way to fix this
62 // would be to serialize the polling and power down irps so that
63 // only one of them is sent to the device at a time.
64 //
65
66 _IRQL_requires_max_(PASSIVE_LEVEL)
67 BOOLEAN
68 DeviceIsMediaChangeDisabledDueToHardwareLimitation(
69 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
70 );
71
72 _IRQL_requires_max_(PASSIVE_LEVEL)
73 BOOLEAN
74 DeviceIsMediaChangeDisabledForClass(
75 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
76 );
77
78 _IRQL_requires_max_(PASSIVE_LEVEL)
79 NTSTATUS
80 DeviceMediaChangeDeviceInstanceOverride(
81 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
82 _Out_ PBOOLEAN Enabled
83 );
84
85 _IRQL_requires_max_(PASSIVE_LEVEL)
86 NTSTATUS
87 DeviceInitializeMcn(
88 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
89 _In_ BOOLEAN AllowDriveToSleep
90 );
91
92 _IRQL_requires_max_(PASSIVE_LEVEL)
93 NTSTATUS
94 DeviceInitializeGesn(
95 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
96 );
97
98 _IRQL_requires_max_(APC_LEVEL)
99 NTSTATUS
100 GesnDataInterpret(
101 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
102 _In_ PNOTIFICATION_EVENT_STATUS_HEADER Header,
103 _Out_ PBOOLEAN ResendImmediately
104 );
105
106 RTL_QUERY_REGISTRY_ROUTINE DeviceMediaChangeRegistryCallBack;
107
108 EVT_WDF_WORKITEM DeviceDisableGesn;
109
110 #if ALLOC_PRAGMA
111
112 #pragma alloc_text(PAGE, DeviceInitializeMediaChangeDetection)
113 #pragma alloc_text(PAGE, DeviceEnableMediaChangeDetection)
114 #pragma alloc_text(PAGE, DeviceDisableMediaChangeDetection)
115 #pragma alloc_text(PAGE, DeviceSendDelayedMediaChangeNotifications)
116 #pragma alloc_text(PAGE, DeviceReleaseMcnResources)
117 #pragma alloc_text(PAGE, DeviceMediaChangeRegistryCallBack)
118 #pragma alloc_text(PAGE, DeviceInitializeMcn)
119 #pragma alloc_text(PAGE, DeviceDisableGesn)
120
121 #pragma alloc_text(PAGE, DeviceIsMediaChangeDisabledDueToHardwareLimitation)
122 #pragma alloc_text(PAGE, DeviceMediaChangeDeviceInstanceOverride)
123 #pragma alloc_text(PAGE, DeviceIsMediaChangeDisabledForClass)
124
125 #pragma alloc_text(PAGE, DeviceDisableMainTimer)
126
127 #pragma alloc_text(PAGE, GesnDataInterpret)
128
129 #pragma alloc_text(PAGE, RequestSetupMcnRequest)
130 #pragma alloc_text(PAGE, RequestPostWorkMcnRequest)
131 #pragma alloc_text(PAGE, RequestSendMcnRequest)
132
133 //
134 // DeviceEnableMainTimer is called by EvtDeviceD0Entry which can't be made pageable
135 // so neither is DeviceEnableMainTimer
136 //
137 //#pragma alloc_text(PAGE, DeviceEnableMainTimer)
138
139 #pragma alloc_text(PAGE, DeviceInitializeGesn)
140
141 #pragma alloc_text(PAGE, RequestHandleMcnControl)
142
143 #endif
144
145 #pragma warning(push)
146 #pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
147
148
149 _IRQL_requires_max_(APC_LEVEL)
150 NTSTATUS
151 GesnDataInterpret(
152 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
153 _In_ PNOTIFICATION_EVENT_STATUS_HEADER Header,
154 _Out_ PBOOLEAN ResendImmediately
155 )
156 /*++
157
158 Routine Description:
159
160 This routine will interpret the data returned for a GESN command, and
161 (if appropriate) set the media change event, and broadcast the
162 appropriate events to user mode for applications who care.
163
164 Arguments:
165
166 DeviceExtension - the device extension
167
168 Header - the resulting data from a GESN event.
169 requires at least EIGHT valid bytes (header == 4, data == 4)
170
171 ResendImmediately - whether or not to immediately resend the request.
172 this should be FALSE if there was no event, FALSE if the reported
173 event was of the DEVICE BUSY class, else true.
174
175 Return Value:
176
177 STATUS_SUCCESS if successful, an error code otherwise
178
179 Notes:
180
181 DataBuffer must be at least four bytes of valid data (header == 4 bytes),
182 and have at least eight bytes of allocated memory (all events == 4 bytes).
183
184 The call to StartNextPacket may occur before this routine is completed.
185 the operational change notifications are informational in nature, and
186 while useful, are not neccessary to ensure proper operation. For example,
187 if the device morphs to no longer supporting WRITE commands, all further
188 write commands will fail. There exists a small timing window wherein
189 IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If
190 a device supports software write protect, it is expected that the
191 application can handle such a case.
192
193 NOTE: perhaps setting the updaterequired byte to one should be done here.
194 if so, it relies upon the setting of a 32-byte value to be an atomic
195 operation. unfortunately, there is no simple way to notify a class driver
196 which wants to know that the device behavior requires updating.
197
198 Not ready events may be sent every second. For example, if we were
199 to minimize the number of asynchronous notifications, an application may
200 register just after a large busy time was reported. This would then
201 prevent the application from knowing the device was busy until some
202 arbitrarily chosen timeout has occurred. Also, the GESN request would
203 have to still occur, since it checks for non-busy events (such as user
204 keybutton presses and media change events) as well. The specification
205 states that the lower-numered events get reported first, so busy events,
206 while repeating, will only be reported when all other events have been
207 cleared from the device.
208
209 --*/
210 {
211 NTSTATUS status = STATUS_SUCCESS;
212 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
213 LONG dataLength = 0;
214 LONG requiredLength = 0;
215 BOOLEAN inHomePosition = FALSE;
216
217 PAGED_CODE();
218
219 // note: don't allocate anything in this routine so that we can
220 // always just 'return'.
221 *ResendImmediately = FALSE;
222
223 if (Header->NEA)
224 {
225 return status;
226 }
227 if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS)
228 {
229 return status;
230 }
231
232 // HACKHACK - REF #0001
233 // This loop is only taken initially, due to the inability to reliably
234 // auto-detect drives that report events correctly at boot. When we
235 // detect this behavior during the normal course of running, we will
236 // disable the hack, allowing more efficient use of the system. This
237 // should occur "nearly" instantly, as the drive should have multiple
238 // events queue'd (ie. power, morphing, media).
239 if (info->Gesn.HackEventMask)
240 {
241 // all events use the low four bytes of zero to indicate
242 // that there was no change in status.
243 UCHAR thisEvent = Header->ClassEventData[0] & 0xf;
244 UCHAR lowestSetBit;
245 UCHAR thisEventBit = (1 << Header->NotificationClass);
246
247 if (!TEST_FLAG(info->Gesn.EventMask, thisEventBit))
248 {
249 // The drive is reporting an event that wasn't requested
250 return STATUS_DEVICE_PROTOCOL_ERROR;
251 }
252
253 // some bit magic here... this results in the lowest set bit only
254 lowestSetBit = info->Gesn.EventMask;
255 lowestSetBit &= (info->Gesn.EventMask - 1);
256 lowestSetBit ^= (info->Gesn.EventMask);
257
258 if (thisEventBit != lowestSetBit)
259 {
260 // HACKHACK - REF #0001
261 // the first time we ever see an event set that is not the lowest
262 // set bit in the request (iow, highest priority), we know that the
263 // hack is no longer required, as the device is ignoring "no change"
264 // events when a real event is waiting in the other requested queues.
265 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
266 "GESN::NONE: Compliant drive found, "
267 "removing GESN hack (%x, %x)\n",
268 thisEventBit, info->Gesn.EventMask));
269
270 info->Gesn.HackEventMask = FALSE;
271 }
272 else if (thisEvent == 0) // NOTIFICATION_*_EVENT_NO_CHANGE
273 {
274 // HACKHACK - REF #0001
275 // note: this hack prevents poorly implemented firmware from constantly
276 // returning "No Event". we do this by cycling through the
277 // supported list of events here.
278 SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit);
279 CLEAR_FLAG(info->Gesn.EventMask, thisEventBit);
280
281 // if we have cycled through all supported event types, then
282 // we need to reset the events we are asking about. else we
283 // want to resend this request immediately in case there was
284 // another event pending.
285 if (info->Gesn.EventMask == 0)
286 {
287 info->Gesn.EventMask = info->Gesn.NoChangeEventMask;
288 info->Gesn.NoChangeEventMask = 0;
289 }
290 else
291 {
292 *ResendImmediately = TRUE;
293 }
294 return status;
295 }
296
297 } // end if (info->Gesn.HackEventMask)
298
299 dataLength = (Header->EventDataLength[0] << 8) |
300 (Header->EventDataLength[1] & 0xff);
301 dataLength -= 2;
302 requiredLength = 4; // all events are four bytes
303
304 if (dataLength < requiredLength)
305 {
306 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
307 "error - GESN returned only %x bytes data for fdo %p\n",
308 dataLength, DeviceExtension->DeviceObject));
309
310 return STATUS_DEVICE_PROTOCOL_ERROR;
311 }
312
313 if (dataLength > requiredLength)
314 {
315 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
316 "error - GESN returned too many (%x) bytes data for fdo %p\n",
317 dataLength, DeviceExtension->DeviceObject));
318 }
319
320 if ((Header->ClassEventData[0] & 0xf) == 0)
321 {
322 // a zero event is a "no change event, so do not retry
323 return status;
324 }
325
326 // because a event other than "no change" occurred,
327 // we should immediately resend this request.
328 *ResendImmediately = TRUE;
329
330 switch (Header->NotificationClass)
331 {
332
333 case NOTIFICATION_OPERATIONAL_CHANGE_CLASS_EVENTS: // 0x01
334 {
335 PNOTIFICATION_OPERATIONAL_STATUS opChangeInfo =
336 (PNOTIFICATION_OPERATIONAL_STATUS)(Header->ClassEventData);
337 ULONG event;
338
339 if (opChangeInfo->OperationalEvent == NOTIFICATION_OPERATIONAL_EVENT_CHANGE_REQUESTED)
340 {
341 break;
342 }
343
344 event = (opChangeInfo->Operation[0] << 8) |
345 (opChangeInfo->Operation[1] ) ;
346
347 // Workaround some hardware that is buggy but prevalent in the market
348 // This hardware has the property that it will report OpChange events repeatedly,
349 // causing us to retry immediately so quickly that we will eventually disable
350 // GESN to prevent an infinite loop.
351 // (only one valid OpChange event type now, only two ever defined)
352 if (info->MediaChangeRetryCount >= 4)
353 {
354 //
355 // HACKHACK - REF #0002
356 // Some drives incorrectly report OpChange/Change (001b/0001h) events
357 // continuously when the tray has been ejected. This causes this routine
358 // to set ResendImmediately to "TRUE", and that results in our cycling
359 // 32 times immediately resending. At that point, we give up detecting
360 // the infinite retry loop, and disable GESN on these drives. This
361 // prevents Media Eject Request (from eject button) from being reported.
362 // Thus, instead we should attempt to workaround this issue by detecting
363 // this behavior.
364 //
365
366 static UCHAR const OpChangeMask = 0x02;
367
368 // At least one device reports "temporarily busy" (which is useless) on eject
369 // At least one device reports "OpChange" repeatedly when re-inserting media
370 // All seem to work well using this workaround
371
372 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN,
373 "GESN OpChange events are broken. Working around this problem in software (for WDFDEVICE %p)\n",
374 DeviceExtension->Device));
375
376 // OpChange is not the only bit set -- Media class is required....
377 NT_ASSERT(CountOfSetBitsUChar(info->Gesn.EventMask) != 1);
378
379 // Force the use of the hackhack (ref #0001) to workaround the
380 // issue noted this hackhack (ref #0002).
381 SET_FLAG(info->Gesn.NoChangeEventMask, OpChangeMask);
382 CLEAR_FLAG(info->Gesn.EventMask, OpChangeMask);
383 info->Gesn.HackEventMask = TRUE;
384
385 // don't request the opChange event again. use the method
386 // defined by hackhack (ref #0001) as the workaround.
387 if (info->Gesn.EventMask == 0)
388 {
389 info->Gesn.EventMask = info->Gesn.NoChangeEventMask;
390 info->Gesn.NoChangeEventMask = 0;
391 *ResendImmediately = FALSE;
392 }
393 else
394 {
395 *ResendImmediately = TRUE;
396 }
397
398 break;
399 }
400
401
402 if ((event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_ADDED) |
403 (event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_CHANGE))
404 {
405 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
406 "GESN says features added/changed for WDFDEVICE %p\n",
407 DeviceExtension->Device));
408
409 // don't notify that new media arrived, just set the
410 // DO_VERIFY to force a FS reload.
411
412 if (IsVolumeMounted(DeviceExtension->DeviceObject))
413 {
414 SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
415 }
416
417 // Call error handler with
418 // a "fake" media change error in case it needs to update
419 // internal structures as though a media change occurred.
420 {
421 SCSI_REQUEST_BLOCK srb = {0};
422 SENSE_DATA sense = {0};
423 NTSTATUS tempStatus;
424 BOOLEAN retry;
425
426 tempStatus = STATUS_MEDIA_CHANGED;
427 retry = FALSE;
428
429 srb.CdbLength = 6;
430 srb.Length = sizeof(SCSI_REQUEST_BLOCK);
431 srb.SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
432 srb.SenseInfoBuffer = &sense;
433 srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
434
435 sense.AdditionalSenseLength = sizeof(SENSE_DATA) -
436 RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
437
438 sense.SenseKey = SCSI_SENSE_UNIT_ATTENTION;
439 sense.AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
440
441 if (DeviceExtension->DeviceAdditionalData.ErrorHandler)
442 {
443 DeviceExtension->DeviceAdditionalData.ErrorHandler(DeviceExtension,
444 &srb,
445 &tempStatus,
446 &retry);
447 }
448 } // end error handler
449
450 }
451 break;
452 }
453
454 case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: // 0x3
455 {
456 PNOTIFICATION_EXTERNAL_STATUS externalInfo =
457 (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData);
458 DEVICE_EVENT_EXTERNAL_REQUEST externalData = {0};
459
460 // unfortunately, due to time constraints, we will only notify
461 // about keys being pressed, and not released. this makes keys
462 // single-function, but simplifies the code significantly.
463 if (externalInfo->ExternalEvent != NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN)
464 {
465 break;
466 }
467
468 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
469 "GESN::EXTERNAL: Event: %x Status %x Req %x\n",
470 externalInfo->ExternalEvent, externalInfo->ExternalStatus,
471 (externalInfo->Request[0] << 8) | externalInfo->Request[1]
472 ));
473
474 externalData.Version = 1;
475 externalData.DeviceClass = 0;
476 externalData.ButtonStatus = externalInfo->ExternalEvent;
477 externalData.Request = (externalInfo->Request[0] << 8) |
478 (externalInfo->Request[1] & 0xff);
479 KeQuerySystemTime(&(externalData.SystemTime));
480 externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement();
481
482 DeviceSendNotification(DeviceExtension,
483 &GUID_IO_DEVICE_EXTERNAL_REQUEST,
484 sizeof(DEVICE_EVENT_EXTERNAL_REQUEST),
485 &externalData);
486
487 return status;
488 }
489
490 case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: // 0x4
491 {
492 PNOTIFICATION_MEDIA_STATUS mediaInfo =
493 (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData);
494
495 if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) ||
496 (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE))
497 {
498 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
499 "GESN::MEDIA ARRIVAL, Status %x\n",
500 mediaInfo->MediaStatus));
501
502 if (IsVolumeMounted(DeviceExtension->DeviceObject))
503 {
504 SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
505 }
506 DeviceSetMediaChangeStateEx(DeviceExtension,
507 MediaPresent,
508 NULL);
509
510 // If media is inserted into slot loading type, mark the device active
511 // to not power off.
512 if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
513 (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) &&
514 (DeviceExtension->ZeroPowerODDInfo->Load == 0)) // Slot
515 {
516 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
517 "GesnDataInterpret: MediaArrival event detected, device marked as active\n"));
518
519 DeviceMarkActive(DeviceExtension, TRUE, FALSE);
520 }
521 }
522 else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL)
523 {
524 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
525 "GESN::MEDIA REMOVAL, Status %x\n",
526 mediaInfo->MediaStatus));
527
528 DeviceSetMediaChangeStateEx(DeviceExtension,
529 MediaNotPresent,
530 NULL);
531
532 // If media is removed from slot loading type, start powering off the device
533 // if it is ZPODD capable.
534 if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
535 (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) &&
536 (DeviceExtension->ZeroPowerODDInfo->Load == 0)) // Slot
537 {
538 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
539 "GesnDataInterpret: MediaRemoval event detected, device marked as idle\n"));
540
541 DeviceMarkActive(DeviceExtension, FALSE, FALSE);
542 }
543 }
544 else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST)
545 {
546 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
547 "GESN::MEDIA EJECTION, Status %x\n",
548 mediaInfo->MediaStatus));
549
550 DeviceSendNotification(DeviceExtension,
551 &GUID_IO_MEDIA_EJECT_REQUEST,
552 0,
553 NULL);
554 }
555
556 break;
557 }
558
559 case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: // lowest priority events...
560 {
561 PNOTIFICATION_BUSY_STATUS busyInfo =
562 (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData);
563 DEVICE_EVENT_BECOMING_READY busyData = {0};
564
565 // else we want to report the approximated time till it's ready.
566 busyData.Version = 1;
567 busyData.Reason = busyInfo->DeviceBusyStatus;
568 busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) |
569 (busyInfo->Time[1] & 0xff);
570
571 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
572 "GESN::BUSY: Event: %x Status %x Time %x\n",
573 busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus,
574 busyData.Estimated100msToReady
575 ));
576
577 // Ignore the notification if the time is small
578 if (busyData.Estimated100msToReady >= GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS)
579 {
580 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
581 "GesnDataInterpret: media BECOMING_READY\n"));
582
583 DeviceSendNotification(DeviceExtension,
584 &GUID_IO_DEVICE_BECOMING_READY,
585 sizeof(DEVICE_EVENT_BECOMING_READY),
586 &busyData);
587 }
588
589 // If manual loading operation is observed for slot loading type, start powering off the device
590 // if it is ZPODD capable.
591 if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
592 (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) &&
593 (DeviceExtension->ZeroPowerODDInfo->Load == 0) && // Drawer
594 (busyInfo->DeviceBusyEvent == NOTIFICATION_BUSY_EVENT_LO_CHANGE) &&
595 (busyInfo->DeviceBusyStatus == NOTIFICATION_BUSY_STATUS_NO_EVENT))
596 {
597 inHomePosition = DeviceZPODDIsInHomePosition(DeviceExtension);
598
599 if (inHomePosition == FALSE)
600 {
601 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
602 "GesnDataInterpret: LoChange event detected, device marked as active\n"));
603
604 DeviceMarkActive(DeviceExtension, TRUE, FALSE);
605 }
606 else
607 {
608 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
609 "GesnDataInterpret: LoChange event detected, device marked as idle\n"));
610
611 DeviceMarkActive(DeviceExtension, FALSE, FALSE);
612 }
613 }
614
615 break;
616 }
617
618 default:
619 {
620 break;
621 }
622
623 } // end switch on notification class
624
625 return status;
626 }
627
628
629 VOID
630 DeviceInternalSetMediaChangeState(
631 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
632 _In_ MEDIA_CHANGE_DETECTION_STATE NewState,
633 _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState
634 )
635 /*++
636
637 Routine Description:
638
639 This routine will (if appropriate) set the media change event for the
640 device. The event will be set if the media state is changed and
641 media change events are enabled. Otherwise the media state will be
642 tracked but the event will not be set.
643
644 This routine will lock out the other media change routines if possible
645 but if not a media change notification may be lost after the enable has
646 been completed.
647
648 Arguments:
649
650 DeviceExtension - the device extension
651
652 NewState - new state for setting
653
654 OldState - optional storage for the old state
655
656 Return Value:
657
658 none
659
660 --*/
661 {
662 #if DBG
663 LPCSTR states[] = {"Unknown", "Present", "Not Present", "Unavailable"};
664 #endif
665 MEDIA_CHANGE_DETECTION_STATE oldMediaState;
666 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
667 CLASS_MEDIA_CHANGE_CONTEXT mcnContext;
668
669 if (!((NewState >= MediaUnknown) && (NewState <= MediaUnavailable)))
670 {
671 return;
672 }
673
674 if (info == NULL)
675 {
676 return;
677 }
678
679 oldMediaState = info->LastKnownMediaDetectionState;
680 if (OldState)
681 {
682 *OldState = oldMediaState;
683 }
684
685 info->LastKnownMediaDetectionState = NewState;
686
687 // Increment MediaChangeCount on transition to MediaPresent
688 if (NewState == MediaPresent && oldMediaState != NewState)
689 {
690 InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
691 }
692
693 if (info->MediaChangeDetectionDisableCount != 0)
694 {
695 #if DBG
696 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
697 "DeviceInternalSetMediaChangeState: MCN not enabled, state "
698 "changed from %s to %s\n",
699 states[oldMediaState], states[NewState]));
700 #endif
701 return;
702 }
703 #if DBG
704 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
705 "DeviceInternalSetMediaChangeState: State change from %s to %s\n",
706 states[oldMediaState], states[NewState]));
707 #endif
708
709 if (info->LastReportedMediaDetectionState == info->LastKnownMediaDetectionState)
710 {
711 // Media is in the same state as we reported last time, no need to report again.
712 return;
713 }
714
715 // make the data useful -- it used to always be zero.
716 mcnContext.MediaChangeCount = DeviceExtension->MediaChangeCount;
717 mcnContext.NewState = NewState;
718
719 if (NewState == MediaPresent)
720 {
721 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
722 "DeviceInternalSetMediaChangeState: Reporting media ARRIVAL\n"));
723
724 DeviceSendNotification(DeviceExtension,
725 &GUID_IO_MEDIA_ARRIVAL,
726 sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
727 &mcnContext);
728 }
729 else if ((NewState == MediaNotPresent) || (NewState == MediaUnavailable))
730 {
731 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
732 "DeviceInternalSetMediaChangeState: Reporting media REMOVAL\n"));
733 DeviceSendNotification(DeviceExtension,
734 &GUID_IO_MEDIA_REMOVAL,
735 sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
736 &mcnContext);
737 }
738 else
739 {
740 // Don't notify of changed going to unknown.
741 return;
742 }
743
744 info->LastReportedMediaDetectionState = info->LastKnownMediaDetectionState;
745
746 return;
747 } // end DeviceInternalSetMediaChangeState()
748
749
750 VOID
751 DeviceSetMediaChangeStateEx(
752 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
753 _In_ MEDIA_CHANGE_DETECTION_STATE NewState,
754 _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState
755 )
756 /*++
757
758 Routine Description:
759
760 This routine will (if appropriate) set the media change event for the
761 device. The event will be set if the media state is changed and
762 media change events are enabled. Otherwise the media state will be
763 tracked but the event will not be set.
764
765 Arguments:
766
767 DeviceExtension - the device extension
768
769 NewState - new state for setting
770
771 OldState - optional storage for the old state
772
773 Return Value:
774
775 none
776
777 --*/
778 {
779 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
780 LARGE_INTEGER zero;
781 NTSTATUS status;
782
783 // timeout value must be 0, as this function can be called at DISPATCH_LEVEL.
784 zero.QuadPart = 0;
785
786 if (info == NULL)
787 {
788 return;
789 }
790
791 status = KeWaitForMutexObject(&info->MediaChangeMutex,
792 Executive,
793 KernelMode,
794 FALSE,
795 &zero);
796
797 if (status == STATUS_TIMEOUT)
798 {
799 // Someone else is in the process of setting the media state.
800 return;
801 }
802
803 // Change the media present state and signal an event, if applicable
804 DeviceInternalSetMediaChangeState(DeviceExtension, NewState, OldState);
805
806 KeReleaseMutex(&info->MediaChangeMutex, FALSE);
807
808 return;
809 } // end DeviceSetMediaChangeStateEx()
810
811
812 _IRQL_requires_max_(APC_LEVEL)
813 VOID
814 DeviceSendDelayedMediaChangeNotifications(
815 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
816 )
817 /*++
818
819 Routine Description:
820
821 This routine sends the so-called delayed media change notifications.
822 These notifications get accumulated while the MCN mechanism is disabled
823 and need to be sent to the application on MCN enabling, if MCN enabling
824 happens as a part of exclusive access unlock and the application has not
825 requested us explicitly to not send the delayed notifications.
826
827 Arguments:
828
829 DeviceExtension - the device extension
830
831 Return Value:
832
833 none
834
835 --*/
836 {
837 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
838 LARGE_INTEGER zero;
839 NTSTATUS status;
840
841 PAGED_CODE();
842
843 zero.QuadPart = 0;
844
845 if (info == NULL)
846 {
847 return;
848 }
849
850 status = KeWaitForMutexObject(&info->MediaChangeMutex,
851 Executive,
852 KernelMode,
853 FALSE,
854 &zero);
855
856 if (status == STATUS_TIMEOUT)
857 {
858 // Someone else is in the process of setting the media state.
859 // That's totally okay, we'll send delayed notifications later.
860 return;
861 }
862
863 // If the last reported state and the last known state are different and
864 // MCN is enabled, generate a notification based on the last known state.
865 if ((info->LastKnownMediaDetectionState != info->LastReportedMediaDetectionState) &&
866 (info->MediaChangeDetectionDisableCount == 0))
867 {
868 DeviceInternalSetMediaChangeState(DeviceExtension, info->LastKnownMediaDetectionState, NULL);
869 }
870
871 KeReleaseMutex(&info->MediaChangeMutex, FALSE);
872
873 return;
874 }
875
876
877 _IRQL_requires_max_(APC_LEVEL)
878 NTSTATUS
879 RequestSetupMcnRequest(
880 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
881 _In_ BOOLEAN UseGesn
882 )
883 /*++
884
885 Routine Description:
886
887 This routine sets up the fields of the request for MCN
888
889 Arguments:
890 DeviceExtension - device context
891
892 UseGesn - If TRUE, the device supports GESN and it's currently the mechanism for MCN
893
894 Return Value:
895 NTSTATUS
896
897 --*/
898 {
899 NTSTATUS status = STATUS_SUCCESS;
900 PSCSI_REQUEST_BLOCK srb;
901 PIRP irp;
902 PIO_STACK_LOCATION nextIrpStack;
903 PCDB cdb;
904 PVOID buffer;
905 WDF_REQUEST_REUSE_PARAMS params;
906 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
907
908 PAGED_CODE();
909
910 irp = WdfRequestWdmGetIrp(info->MediaChangeRequest);
911 NT_ASSERT(irp != NULL);
912
913 // deassign the MdlAddress, this is the value we assign explicitly.
914 // this is to prevent WdfRequestReuse to release the Mdl unexpectly.
915 if (irp->MdlAddress)
916 {
917 irp->MdlAddress = NULL;
918 }
919
920 if (NT_SUCCESS(status))
921 {
922 // Setup the IRP to perform a test unit ready.
923 WDF_REQUEST_REUSE_PARAMS_INIT(&params,
924 WDF_REQUEST_REUSE_NO_FLAGS,
925 STATUS_NOT_SUPPORTED);
926
927 status = WdfRequestReuse(info->MediaChangeRequest, &params);
928 }
929
930 if (NT_SUCCESS(status))
931 {
932 // Format the request.
933 status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
934 info->MediaChangeRequest,
935 IOCTL_SCSI_EXECUTE_IN,
936 NULL, NULL,
937 NULL, NULL,
938 NULL, NULL);
939
940 if (!NT_SUCCESS(status))
941 {
942 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
943 "RequestSetupMcnRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
944 status));
945 }
946 }
947
948 if (NT_SUCCESS(status))
949 {
950 RequestClearSendTime(info->MediaChangeRequest);
951
952 nextIrpStack = IoGetNextIrpStackLocation(irp);
953
954 nextIrpStack->Flags = SL_OVERRIDE_VERIFY_VOLUME;
955 nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
956 nextIrpStack->Parameters.Scsi.Srb = &(info->MediaChangeSrb);
957
958 // Prepare the SRB for execution.
959 srb = nextIrpStack->Parameters.Scsi.Srb;
960 buffer = info->SenseBuffer;
961 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
962 RtlZeroMemory(buffer, SENSE_BUFFER_SIZE);
963
964 srb->QueueTag = SP_UNTAGGED;
965 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
966 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
967 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
968 srb->SenseInfoBuffer = buffer;
969 srb->SrbStatus = 0;
970 srb->ScsiStatus = 0;
971 srb->OriginalRequest = irp;
972 srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
973
974 srb->SrbFlags = DeviceExtension->SrbFlags;
975 SET_FLAG(srb->SrbFlags, info->SrbFlags);
976
977 if (!UseGesn)
978 {
979 srb->TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT;
980 srb->CdbLength = 6;
981 srb->DataTransferLength = 0;
982 SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
983 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
984 srb->DataBuffer = NULL;
985 srb->DataTransferLength = 0;
986 irp->MdlAddress = NULL;
987
988 cdb = (PCDB) &srb->Cdb[0];
989 cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
990 }
991 else
992 {
993 NT_ASSERT(info->Gesn.Buffer);
994
995 srb->TimeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN
996
997 srb->CdbLength = 10;
998 SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
999 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
1000 srb->DataBuffer = info->Gesn.Buffer;
1001 srb->DataTransferLength = info->Gesn.BufferSize;
1002 irp->MdlAddress = info->Gesn.Mdl;
1003
1004 cdb = (PCDB) &srb->Cdb[0];
1005 cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode = SCSIOP_GET_EVENT_STATUS;
1006 cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1;
1007 cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] = (UCHAR)((info->Gesn.BufferSize) >> 8);
1008 cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] = (UCHAR)((info->Gesn.BufferSize) & 0xff);
1009 cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest = info->Gesn.EventMask;
1010 }
1011 }
1012
1013 return status;
1014 }
1015
1016
1017 VOID
1018 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
1019 DeviceDisableGesn(
1020 _In_ WDFWORKITEM WorkItem
1021 )
1022 /*++
1023
1024 Routine Description:
1025
1026 Work item routine to set the hack flag in the registry to disable GESN
1027 This routine is invoked when the device reports TOO many events that affects system
1028
1029 Arguments:
1030 WorkItem - the work item be perfromed.
1031
1032 Return Value:
1033 None
1034
1035 --*/
1036 {
1037 WDFDEVICE device = WdfWorkItemGetParentObject(WorkItem);
1038 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
1039
1040 PAGED_CODE();
1041
1042 //
1043 // Set the hack flag in the registry
1044 //
1045 DeviceSetParameter(deviceExtension,
1046 CLASSP_REG_SUBKEY_NAME,
1047 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
1048 CdromDetectionUnsupported);
1049
1050 WdfObjectDelete(WorkItem);
1051
1052 return;
1053 }
1054
1055 _IRQL_requires_max_(APC_LEVEL)
1056 BOOLEAN
1057 RequestPostWorkMcnRequest(
1058 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1059 )
1060 /*++
1061
1062 Routine Description:
1063
1064 This routine handles the completion of the test unit ready irps used to
1065 determine if the media has changed. If the media has changed, this code
1066 signals the named event to wake up other system services that react to
1067 media change (aka AutoPlay).
1068
1069 Arguments:
1070
1071 DeviceExtension - the device context
1072
1073 Return Value:
1074
1075 BOOLEAN - TRUE (needs retry); FALSE (shoule not retry)
1076
1077 --*/
1078 {
1079 NTSTATUS status = STATUS_SUCCESS;
1080 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
1081 PIRP irp;
1082 BOOLEAN retryImmediately = FALSE;
1083
1084 PAGED_CODE();
1085
1086 NT_ASSERT(info->MediaChangeRequest != NULL);
1087 irp = WdfRequestWdmGetIrp(info->MediaChangeRequest);
1088
1089 NT_ASSERT(!TEST_FLAG(info->MediaChangeSrb.SrbStatus, SRB_STATUS_QUEUE_FROZEN));
1090
1091 // use InterpretSenseInfo routine to check for media state, and also
1092 // to call ClassError() with correct parameters.
1093 if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS)
1094 {
1095 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "MCN request - failed - srb status=%x, sense=%x/%x/%x.\n",
1096 info->MediaChangeSrb.SrbStatus,
1097 ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->SenseKey,
1098 ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->AdditionalSenseCode,
1099 ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->AdditionalSenseCodeQualifier));
1100
1101 if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_NOT_POWERED)
1102 {
1103 // Release the queue if it is frozen.
1104 if (info->MediaChangeSrb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
1105 {
1106 DeviceReleaseQueue(DeviceExtension->Device);
1107 }
1108
1109 RequestSenseInfoInterpret(DeviceExtension,
1110 info->MediaChangeRequest,
1111 &info->MediaChangeSrb,
1112 0,
1113 &status,
1114 NULL);
1115 }
1116 }
1117 else
1118 {
1119 DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO = FALSE;
1120
1121 if (!info->Gesn.Supported)
1122 {
1123 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1124 "MCN request - succeeded (GESN NOT supported, setting MediaPresent).\n"));
1125
1126 // success != media for GESN case
1127 DeviceSetMediaChangeStateEx(DeviceExtension,
1128 MediaPresent,
1129 NULL);
1130 }
1131 else
1132 {
1133 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
1134 "MCN request - succeeded (GESN supported).\n"));
1135 }
1136 }
1137
1138 if (info->Gesn.Supported)
1139 {
1140 if (status == STATUS_DATA_OVERRUN)
1141 {
1142 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request - Data Overrun\n"));
1143 status = STATUS_SUCCESS;
1144 }
1145
1146 if (!NT_SUCCESS(status))
1147 {
1148 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request: GESN failed with status %x\n", status));
1149 }
1150 else
1151 {
1152 // for GESN, need to interpret the results of the data.
1153 // this may also require an immediate retry
1154 if (irp->IoStatus.Information == 8 )
1155 {
1156 GesnDataInterpret(DeviceExtension,
1157 (PVOID)info->Gesn.Buffer,
1158 &retryImmediately);
1159 }
1160
1161 } // end of NT_SUCCESS(status)
1162
1163 } // end of Info->Gesn.Supported
1164
1165 // free port-allocated sense buffer, if any.
1166 if (PORT_ALLOCATED_SENSE(DeviceExtension, &info->MediaChangeSrb))
1167 {
1168 FREE_PORT_ALLOCATED_SENSE_BUFFER(DeviceExtension, &info->MediaChangeSrb);
1169 }
1170
1171 // Remember the IRP and SRB for use the next time.
1172 NT_ASSERT(IoGetNextIrpStackLocation(irp));
1173 IoGetNextIrpStackLocation(irp)->Parameters.Scsi.Srb = &info->MediaChangeSrb;
1174
1175 // run a sanity check to make sure we're not recursing continuously
1176 if (retryImmediately)
1177 {
1178 info->MediaChangeRetryCount++;
1179
1180 if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES)
1181 {
1182 // Disable GESN on this device.
1183 // Create a work item to set the value in the registry
1184 WDF_OBJECT_ATTRIBUTES attributes;
1185 WDF_WORKITEM_CONFIG workitemConfig;
1186 WDFWORKITEM workItem;
1187
1188 WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
1189 attributes.ParentObject = DeviceExtension->Device;
1190
1191 WDF_WORKITEM_CONFIG_INIT(&workitemConfig, DeviceDisableGesn);
1192 workitemConfig.AutomaticSerialization = FALSE;
1193
1194 status = WdfWorkItemCreate(&workitemConfig,
1195 &attributes,
1196 &workItem);
1197
1198 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request: Disabling GESN for WDFDEVICE %p\n", DeviceExtension->Device));
1199
1200 if (NT_SUCCESS(status))
1201 {
1202 WdfWorkItemEnqueue(workItem);
1203 }
1204
1205 info->Gesn.Supported = FALSE;
1206 info->Gesn.EventMask = 0;
1207 info->Gesn.BufferSize = 0;
1208 info->MediaChangeRetryCount = 0;
1209 retryImmediately = FALSE;
1210 // should we log an error in event log?
1211 }
1212 }
1213 else
1214 {
1215 info->MediaChangeRetryCount = 0;
1216 }
1217
1218 return retryImmediately;
1219 }
1220
1221
1222 _IRQL_requires_max_(APC_LEVEL)
1223 BOOLEAN
1224 RequestSendMcnRequest(
1225 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1226 )
1227 /*++
1228
1229 Routine Description:
1230
1231 This routine sends the formatted MCN request sychronizely to lower driver.
1232
1233 Arguments:
1234
1235 DeviceExtension - the device context
1236
1237 Return Value:
1238 BOOLEAN - TRUE (requst successfully sent); FALSE (request failed to send)
1239
1240 --*/
1241 {
1242 BOOLEAN requestSent = FALSE;
1243 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
1244
1245 PAGED_CODE();
1246
1247 RequestSend(DeviceExtension,
1248 info->MediaChangeRequest,
1249 DeviceExtension->IoTarget,
1250 WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
1251 &requestSent);
1252
1253 return requestSent;
1254 } // end RequestSendMcnRequest()
1255
1256
1257
1258 _IRQL_requires_max_(PASSIVE_LEVEL)
1259 NTSTATUS
1260 DeviceInitializeMcn(
1261 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
1262 _In_ BOOLEAN AllowDriveToSleep
1263 )
1264 /*++
1265
1266 Routine Description:
1267
1268 This routine initialize the contents of MCN structure.
1269
1270 Arguments:
1271
1272 DeviceExtension - the device extension
1273
1274 AllowDriveToSleep - for CDROM, this parameter should be always FALSE
1275
1276 Return Value:
1277 NTSTATUS
1278
1279 --*/
1280 {
1281 NTSTATUS status = STATUS_SUCCESS;
1282 PMEDIA_CHANGE_DETECTION_INFO mediaChangeInfo = NULL;
1283 PIRP irp = NULL;
1284 PVOID senseBuffer = NULL;
1285 WDF_OBJECT_ATTRIBUTES attributes;
1286
1287 PAGED_CODE();
1288
1289 if (DeviceExtension->MediaChangeDetectionInfo != NULL)
1290 {
1291 //Already initialized.
1292 return STATUS_SUCCESS;
1293 }
1294
1295 DeviceExtension->KernelModeMcnContext.FileObject = (PVOID)-1;
1296 DeviceExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1;
1297 DeviceExtension->KernelModeMcnContext.LockCount = 0;
1298 DeviceExtension->KernelModeMcnContext.McnDisableCount = 0;
1299
1300 mediaChangeInfo = ExAllocatePoolWithTag(NonPagedPoolNx,
1301 sizeof(MEDIA_CHANGE_DETECTION_INFO),
1302 CDROM_TAG_MEDIA_CHANGE_DETECTION);
1303
1304 if (mediaChangeInfo == NULL)
1305 {
1306 status = STATUS_INSUFFICIENT_RESOURCES;
1307 }
1308 else
1309 {
1310 RtlZeroMemory(mediaChangeInfo, sizeof(MEDIA_CHANGE_DETECTION_INFO));
1311 }
1312
1313 if (NT_SUCCESS(status))
1314 {
1315 if ((DeviceExtension->PowerDescriptor != NULL) &&
1316 (DeviceExtension->PowerDescriptor->AsynchronousNotificationSupported != FALSE) &&
1317 (!TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_ASYNCHRONOUS_NOTIFICATION)))
1318 {
1319 mediaChangeInfo->AsynchronousNotificationSupported = TRUE;
1320 }
1321 }
1322
1323 // Allocate an IRP to carry the IOCTL_MCN_SYNC_FAKE_IOCTL.
1324 if (NT_SUCCESS(status))
1325 {
1326 irp = IoAllocateIrp(DeviceExtension->DeviceObject->StackSize, FALSE);
1327
1328 if (irp == NULL)
1329 {
1330 status = STATUS_INSUFFICIENT_RESOURCES;
1331 }
1332 }
1333
1334 if (NT_SUCCESS(status))
1335 {
1336 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
1337 CDROM_REQUEST_CONTEXT);
1338 attributes.ParentObject = DeviceExtension->Device;
1339 status = WdfRequestCreate(&attributes,
1340 DeviceExtension->IoTarget,
1341 &mediaChangeInfo->MediaChangeRequest);
1342 }
1343
1344 if (NT_SUCCESS(status))
1345 {
1346 // Preformat the media change request. With this being done, we never need to worry about
1347 // WdfIoTargetFormatRequestForInternalIoctlOthers ever failing later.
1348 status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
1349 mediaChangeInfo->MediaChangeRequest,
1350 IOCTL_SCSI_EXECUTE_IN,
1351 NULL, NULL,
1352 NULL, NULL,
1353 NULL, NULL);
1354 }
1355
1356 if (NT_SUCCESS(status))
1357 {
1358 senseBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
1359 SENSE_BUFFER_SIZE,
1360 CDROM_TAG_MEDIA_CHANGE_DETECTION);
1361 if (senseBuffer == NULL)
1362 {
1363 status = STATUS_INSUFFICIENT_RESOURCES;
1364 }
1365 }
1366
1367 if (NT_SUCCESS(status))
1368 {
1369 mediaChangeInfo->MediaChangeSyncIrp = irp;
1370 mediaChangeInfo->SenseBuffer = senseBuffer;
1371
1372 // Set default values for the media change notification
1373 // configuration.
1374 mediaChangeInfo->MediaChangeDetectionDisableCount = 0;
1375
1376 // Assume that there is initially no media in the device
1377 // only notify upper layers if there is something there
1378 mediaChangeInfo->LastKnownMediaDetectionState = MediaUnknown;
1379 mediaChangeInfo->LastReportedMediaDetectionState = MediaUnknown;
1380
1381 // setup all extra flags we'll be setting for this irp
1382 mediaChangeInfo->SrbFlags = 0;
1383
1384 SET_FLAG(mediaChangeInfo->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
1385 SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
1386 SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
1387
1388 if (AllowDriveToSleep) //FALSE for CD/DVD devices
1389 {
1390 SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
1391 }
1392
1393 KeInitializeMutex(&mediaChangeInfo->MediaChangeMutex, 0x100);
1394
1395 // It is ok to support media change events on this device.
1396 DeviceExtension->MediaChangeDetectionInfo = mediaChangeInfo;
1397
1398 // check the device supports GESN or not, initialize GESN structure if it supports.
1399 {
1400 // This is only valid for type5 devices.
1401 NTSTATUS tempStatus = STATUS_SUCCESS;
1402
1403 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1404 "DeviceInitializeMcn: Testing for GESN\n"));
1405 tempStatus = DeviceInitializeGesn(DeviceExtension);
1406
1407 if (NT_SUCCESS(tempStatus))
1408 {
1409 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1410 "DeviceInitializeMcn: GESN available for %p\n",
1411 DeviceExtension->DeviceObject));
1412 NT_ASSERT(mediaChangeInfo->Gesn.Supported );
1413 NT_ASSERT(mediaChangeInfo->Gesn.Buffer != NULL);
1414 NT_ASSERT(mediaChangeInfo->Gesn.BufferSize != 0);
1415 NT_ASSERT(mediaChangeInfo->Gesn.EventMask != 0);
1416 }
1417 else
1418 {
1419 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1420 "DeviceInitializeMcn: GESN *NOT* available for %p\n",
1421 DeviceExtension->DeviceObject));
1422 NT_ASSERT(!mediaChangeInfo->Gesn.Supported);
1423 NT_ASSERT(mediaChangeInfo->Gesn.Buffer == NULL);
1424 NT_ASSERT(mediaChangeInfo->Gesn.BufferSize == 0);
1425 NT_ASSERT(mediaChangeInfo->Gesn.EventMask == 0);
1426 mediaChangeInfo->Gesn.Supported = FALSE; // just in case....
1427 }
1428 }
1429 }
1430
1431 if (NT_SUCCESS(status))
1432 {
1433 // Register for display state change on AOAC capable systems so we can put the
1434 // device to low power state when not required.
1435 if (mediaChangeInfo->DisplayStateCallbackHandle == NULL)
1436 {
1437 POWER_PLATFORM_INFORMATION PlatformInfo = {0};
1438
1439 status = ZwPowerInformation(PlatformInformation,
1440 NULL,
1441 0,
1442 &PlatformInfo,
1443 sizeof(PlatformInfo));
1444
1445 if (NT_SUCCESS(status) && PlatformInfo.AoAc)
1446 {
1447 PoRegisterPowerSettingCallback(DeviceExtension->DeviceObject,
1448 &GUID_CONSOLE_DISPLAY_STATE,
1449 &DevicePowerSettingCallback,
1450 DeviceExtension,
1451 &mediaChangeInfo->DisplayStateCallbackHandle);
1452 }
1453
1454 // Ignore any failures above.
1455 status = STATUS_SUCCESS;
1456 }
1457 }
1458
1459 if (!NT_SUCCESS(status))
1460 {
1461 if (irp != NULL)
1462 {
1463 IoFreeIrp(irp);
1464 }
1465 FREE_POOL(senseBuffer);
1466 FREE_POOL(mediaChangeInfo);
1467 }
1468
1469 return status;
1470
1471 } // end DeviceInitializeMcn()
1472
1473
1474 _IRQL_requires_max_(PASSIVE_LEVEL)
1475 NTSTATUS
1476 DeviceInitializeGesn(
1477 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1478 )
1479 /*++
1480
1481 Routine Description:
1482
1483 This routine initialize the contents of GESN structure.
1484
1485 Arguments:
1486
1487 DeviceExtension - the device extension
1488
1489 Return Value:
1490 NTSTATUS
1491
1492 --*/
1493 {
1494 NTSTATUS status = STATUS_SUCCESS;
1495 PNOTIFICATION_EVENT_STATUS_HEADER header = NULL;
1496 CDROM_DETECTION_STATE detectionState = CdromDetectionUnknown;
1497 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = DeviceExtension->DeviceDescriptor;
1498 BOOLEAN retryImmediately = TRUE;
1499 ULONG i = 0;
1500 ULONG atapiResets = 0;
1501 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
1502
1503 PAGED_CODE();
1504
1505 NT_ASSERT(info != NULL);
1506
1507 // read if we already know the abilities of the device
1508 DeviceGetParameter(DeviceExtension,
1509 CLASSP_REG_SUBKEY_NAME,
1510 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
1511 (PULONG)&detectionState);
1512
1513 if (detectionState == CdromDetectionUnsupported)
1514 {
1515 status = STATUS_NOT_SUPPORTED;
1516 }
1517
1518 // check if the device has a hack flag saying never to try this.
1519 if (NT_SUCCESS(status) &&
1520 (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_GESN_IS_BAD)) )
1521 {
1522 DeviceSetParameter(DeviceExtension,
1523 CLASSP_REG_SUBKEY_NAME,
1524 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
1525 CdromDetectionUnsupported);
1526 status = STATUS_NOT_SUPPORTED;
1527 }
1528
1529 // else go through the process since we allocate buffers and
1530 // get all sorts of device settings.
1531 if (NT_SUCCESS(status))
1532 {
1533 if (info->Gesn.Buffer == NULL)
1534 {
1535 info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
1536 GESN_BUFFER_SIZE,
1537 CDROM_TAG_GESN);
1538 }
1539
1540 if (info->Gesn.Buffer == NULL)
1541 {
1542 status = STATUS_INSUFFICIENT_RESOURCES;
1543 }
1544 }
1545
1546 if (NT_SUCCESS(status))
1547 {
1548 if (info->Gesn.Mdl != NULL)
1549 {
1550 IoFreeMdl(info->Gesn.Mdl);
1551 }
1552
1553 info->Gesn.Mdl = IoAllocateMdl(info->Gesn.Buffer,
1554 GESN_BUFFER_SIZE,
1555 FALSE,
1556 FALSE,
1557 NULL);
1558 if (info->Gesn.Mdl == NULL)
1559 {
1560 status = STATUS_INSUFFICIENT_RESOURCES;
1561 }
1562 }
1563
1564 if (NT_SUCCESS(status))
1565 {
1566 MmBuildMdlForNonPagedPool(info->Gesn.Mdl);
1567 info->Gesn.BufferSize = GESN_BUFFER_SIZE;
1568 info->Gesn.EventMask = 0;
1569
1570 // all items are prepared to use GESN (except the event mask, so don't
1571 // optimize this part out!).
1572 //
1573 // now see if it really works. we have to loop through this because
1574 // many SAMSUNG (and one COMPAQ) drives timeout when requesting
1575 // NOT_READY events, even when the IMMEDIATE bit is set. :(
1576 //
1577 // using a drive list is cumbersome, so this might fix the problem.
1578 for (i = 0; (i < 16) && retryImmediately; i++)
1579 {
1580 status = RequestSetupMcnRequest(DeviceExtension, TRUE);
1581
1582 if (!NT_SUCCESS(status))
1583 {
1584 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
1585 "Setup Mcn request failed %x for WDFDEVICE %p\n",
1586 status, DeviceExtension->Device));
1587 break;
1588 }
1589
1590 NT_ASSERT(TEST_FLAG(info->MediaChangeSrb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE));
1591
1592 status = DeviceSendRequestSynchronously(DeviceExtension->Device, info->MediaChangeRequest, TRUE);
1593
1594 if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS)
1595 {
1596 // Release the queue if it is frozen.
1597 if (info->MediaChangeSrb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
1598 {
1599 DeviceReleaseQueue(DeviceExtension->Device);
1600 }
1601
1602 RequestSenseInfoInterpret(DeviceExtension,
1603 info->MediaChangeRequest,
1604 &(info->MediaChangeSrb),
1605 0,
1606 &status,
1607 NULL);
1608 }
1609
1610 if ((deviceDescriptor->BusType == BusTypeAtapi) &&
1611 (info->MediaChangeSrb.SrbStatus == SRB_STATUS_BUS_RESET))
1612 {
1613 //
1614 // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
1615 // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
1616 // the two. if we get this status four time consecutively,
1617 // stop trying this command. it is too late to change ATAPI
1618 // at this point, so special-case this here. (07/10/2001)
1619 // NOTE: any value more than 4 may cause the device to be
1620 // marked missing.
1621 //
1622 atapiResets++;
1623 if (atapiResets >= 4)
1624 {
1625 status = STATUS_IO_DEVICE_ERROR;
1626 break;
1627 }
1628 }
1629
1630 if (status == STATUS_DATA_OVERRUN)
1631 {
1632 status = STATUS_SUCCESS;
1633 }
1634
1635 if ((status == STATUS_INVALID_DEVICE_REQUEST) ||
1636 (status == STATUS_TIMEOUT) ||
1637 (status == STATUS_IO_DEVICE_ERROR) ||
1638 (status == STATUS_IO_TIMEOUT))
1639 {
1640 // with these error codes, we don't ever want to try this command
1641 // again on this device, since it reacts poorly.
1642 DeviceSetParameter( DeviceExtension,
1643 CLASSP_REG_SUBKEY_NAME,
1644 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
1645 CdromDetectionUnsupported);
1646 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
1647 "GESN test failed %x for WDFDEVICE %p\n",
1648 status, DeviceExtension->Device));
1649 break;
1650 }
1651
1652 if (!NT_SUCCESS(status))
1653 {
1654 // this may be other errors that should not disable GESN
1655 // for all future start_device calls.
1656 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
1657 "GESN test failed %x for WDFDEVICE %p\n",
1658 status, DeviceExtension->Device));
1659 break;
1660 }
1661 else if (i == 0)
1662 {
1663 // the first time, the request was just retrieving a mask of
1664 // available bits. use this to mask future requests.
1665 header = (PNOTIFICATION_EVENT_STATUS_HEADER)(info->Gesn.Buffer);
1666
1667 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1668 "WDFDEVICE %p supports event mask %x\n",
1669 DeviceExtension->Device, header->SupportedEventClasses));
1670
1671 if (TEST_FLAG(header->SupportedEventClasses,
1672 NOTIFICATION_MEDIA_STATUS_CLASS_MASK))
1673 {
1674 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1675 "GESN supports MCN\n"));
1676 }
1677 if (TEST_FLAG(header->SupportedEventClasses,
1678 NOTIFICATION_DEVICE_BUSY_CLASS_MASK))
1679 {
1680 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1681 "GESN supports DeviceBusy\n"));
1682 }
1683 if (TEST_FLAG(header->SupportedEventClasses,
1684 NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK))
1685 {
1686 if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags,
1687 FDO_HACK_GESN_IGNORE_OPCHANGE))
1688 {
1689 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1690 "GESN supports OpChange, but must ignore these events for compatibility\n"));
1691 CLEAR_FLAG(header->SupportedEventClasses,
1692 NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK);
1693 }
1694 else
1695 {
1696 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1697 "GESN supports OpChange\n"));
1698 }
1699 }
1700 info->Gesn.EventMask = header->SupportedEventClasses;
1701
1702 //
1703 // realistically, we are only considering the following events:
1704 // EXTERNAL REQUEST - this is being tested for play/stop/etc.
1705 // MEDIA STATUS - autorun and ejection requests.
1706 // DEVICE BUSY - to allow us to predict when media will be ready.
1707 // therefore, we should not bother querying for the other,
1708 // unknown events. clear all but the above flags.
1709 //
1710 info->Gesn.EventMask &= NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK |
1711 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK |
1712 NOTIFICATION_MEDIA_STATUS_CLASS_MASK |
1713 NOTIFICATION_DEVICE_BUSY_CLASS_MASK ;
1714
1715
1716 //
1717 // HACKHACK - REF #0001
1718 // Some devices will *never* report an event if we've also requested
1719 // that it report lower-priority events. this is due to a
1720 // misunderstanding in the specification wherein a "No Change" is
1721 // interpreted to be a real event. what should occur is that the
1722 // device should ignore "No Change" events when multiple event types
1723 // are requested unless there are no other events waiting. this
1724 // greatly reduces the number of requests that the host must send
1725 // to determine if an event has occurred. Since we must work on all
1726 // drives, default to enabling the hack until we find evidence of
1727 // proper firmware.
1728 //
1729 if (info->Gesn.EventMask == 0)
1730 {
1731 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1732 "GESN supported, but not mask we care about (%x) for FDO %p\n",
1733 header->SupportedEventClasses,
1734 DeviceExtension->DeviceObject));
1735 // NOTE: the status is still status_sucess.
1736 break;
1737 }
1738 else if (CountOfSetBitsUChar(info->Gesn.EventMask) == 1)
1739 {
1740 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1741 "GESN hack not required for FDO %p\n",
1742 DeviceExtension->DeviceObject));
1743 }
1744 else
1745 {
1746 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1747 "GESN hack enabled for FDO %p\n",
1748 DeviceExtension->DeviceObject));
1749 info->Gesn.HackEventMask = 1;
1750 }
1751 }
1752 else
1753 {
1754 // i > 0; not the first time looping through, so interpret the results.
1755 status = GesnDataInterpret(DeviceExtension,
1756 (PVOID)info->Gesn.Buffer,
1757 &retryImmediately);
1758
1759 if (!NT_SUCCESS(status))
1760 {
1761 // This drive does not support GESN correctly
1762 DeviceSetParameter( DeviceExtension,
1763 CLASSP_REG_SUBKEY_NAME,
1764 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
1765 CdromDetectionUnsupported);
1766 break;
1767 }
1768 }
1769 } // end 'for' loop of GESN requests....
1770 }
1771
1772 if (NT_SUCCESS(status))
1773 {
1774 //
1775 // we can only use this if it can be relied upon for media changes,
1776 // since we are (by definition) no longer going to be polling via
1777 // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
1778 // for this command (although a filter driver, such as one for burning
1779 // cd's, might still fake those errors).
1780 //
1781 // since we also rely upon NOT_READY events to change the cursor
1782 // into a "wait" cursor; GESN is still more reliable than other
1783 // methods, and includes eject button requests, so we'll use it
1784 // without DEVICE_BUSY in Windows Vista.
1785 //
1786
1787 if (TEST_FLAG(info->Gesn.EventMask, NOTIFICATION_MEDIA_STATUS_CLASS_MASK))
1788 {
1789 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1790 "Enabling GESN support for WDFDEVICE %p\n",
1791 DeviceExtension->Device));
1792 info->Gesn.Supported = TRUE;
1793
1794 DeviceSetParameter( DeviceExtension,
1795 CLASSP_REG_SUBKEY_NAME,
1796 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
1797 CdromDetectionSupported);
1798 }
1799 else
1800 {
1801 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1802 "GESN available but not enabled for WDFDEVICE %p\n",
1803 DeviceExtension->Device));
1804 status = STATUS_NOT_SUPPORTED;
1805 }
1806 }
1807
1808 if (!NT_SUCCESS(status))
1809 {
1810 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
1811 "GESN support detection failed for WDFDEVICE %p with status %08x\n",
1812 DeviceExtension->Device, status));
1813
1814 if (info->Gesn.Mdl)
1815 {
1816 PIRP irp = WdfRequestWdmGetIrp(info->MediaChangeRequest);
1817
1818 IoFreeMdl(info->Gesn.Mdl);
1819 info->Gesn.Mdl = NULL;
1820 irp->MdlAddress = NULL;
1821 }
1822
1823 FREE_POOL(info->Gesn.Buffer);
1824 info->Gesn.Supported = FALSE;
1825 info->Gesn.EventMask = 0;
1826 info->Gesn.BufferSize = 0;
1827 }
1828
1829 return status;
1830 }
1831
1832
1833 _IRQL_requires_max_(PASSIVE_LEVEL)
1834 NTSTATUS
1835 DeviceInitializeMediaChangeDetection(
1836 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
1837 )
1838 /*++
1839
1840 Routine Description:
1841
1842 This routine checks to see if it is safe to initialize MCN (the back end
1843 to autorun) for a given device. It will then check the device-type wide
1844 key "Autorun" in the service key (for legacy reasons), and then look in
1845 the device-specific key to potentially override that setting.
1846
1847 If MCN is to be enabled, all neccessary structures and memory are
1848 allocated and initialized.
1849
1850 This routine MUST be called only from the DeviceInit...() .
1851
1852 Arguments:
1853
1854 DeviceExtension - the device to initialize MCN for, if appropriate
1855
1856 Return Value:
1857
1858 NTSTATUS
1859
1860 --*/
1861 {
1862 NTSTATUS status = STATUS_SUCCESS;
1863
1864 BOOLEAN disabled = FALSE;
1865 BOOLEAN instanceOverride;
1866
1867 PAGED_CODE();
1868
1869 // NOTE: This assumes that DeviceInitializeMediaChangeDetection is always
1870 // called in the context of the DeviceInitDevicePhase2. If called
1871 // after then this check will have already been made and the
1872 // once a second timer will not have been enabled.
1873
1874 disabled = DeviceIsMediaChangeDisabledDueToHardwareLimitation(DeviceExtension);
1875
1876 if (disabled)
1877 {
1878 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1879 "DeviceInitializeMediaChangeDetection: Disabled due to hardware"
1880 "limitations for this device\n"));
1881 }
1882 else
1883 {
1884 // autorun should now be enabled by default for all media types.
1885 disabled = DeviceIsMediaChangeDisabledForClass(DeviceExtension);
1886
1887 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1888 "DeviceInitializeMediaChangeDetection: MCN is %s\n",
1889 (disabled ? "disabled" : "enabled")));
1890
1891 status = DeviceMediaChangeDeviceInstanceOverride(DeviceExtension,
1892 &instanceOverride); // default value
1893
1894 if (!NT_SUCCESS(status))
1895 {
1896 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1897 "DeviceInitializeMediaChangeDetection: Instance using default\n"));
1898 }
1899 else
1900 {
1901 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1902 "DeviceInitializeMediaChangeDetection: Instance override: %s MCN\n",
1903 (instanceOverride ? "Enabling" : "Disabling")));
1904 disabled = !instanceOverride;
1905 }
1906
1907 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1908 "DeviceInitializeMediaChangeDetection: Instance MCN is %s\n",
1909 (disabled ? "disabled" : "enabled")));
1910 }
1911
1912 if (!disabled)
1913 {
1914 // if the drive is not a CDROM, allow the drive to sleep
1915 status = DeviceInitializeMcn(DeviceExtension, FALSE);
1916 }
1917
1918 return status;
1919 } // end DeviceInitializeMediaChangeDetection()
1920
1921
1922 _IRQL_requires_max_(PASSIVE_LEVEL)
1923 NTSTATUS
1924 DeviceMediaChangeDeviceInstanceOverride(
1925 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
1926 _Out_ PBOOLEAN Enabled
1927 )
1928 /*++
1929
1930 Routine Description:
1931
1932 The user can override the global setting to enable or disable Autorun on a
1933 specific cdrom device via the control panel. This routine checks and/or
1934 sets this value.
1935
1936 Arguments:
1937
1938 DeviceExtension - the device to set/get the value for
1939
1940 Enabled - TRUE (Autorun is enabled)
1941 FALSE (Autorun is disabled)
1942 Return Value:
1943 NTSTATUS
1944
1945 --*/
1946 {
1947 NTSTATUS status = STATUS_UNSUCCESSFUL;
1948 WDFKEY deviceKey = NULL;
1949 WDFKEY subKey = NULL;
1950
1951 UNICODE_STRING subkeyName;
1952 UNICODE_STRING enableMcnValueName;
1953 UNICODE_STRING disableMcnValueName;
1954 ULONG alwaysEnable = 0;
1955 ULONG alwaysDisable = 0;
1956
1957 PAGED_CODE();
1958
1959 status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
1960 PLUGPLAY_REGKEY_DEVICE,
1961 KEY_ALL_ACCESS,
1962 WDF_NO_OBJECT_ATTRIBUTES,
1963 &deviceKey);
1964 if (!NT_SUCCESS(status))
1965 {
1966 // this can occur when a new device is added to the system
1967 // this is due to cdrom.sys being an 'essential' driver
1968 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1969 "DeviceMediaChangeDeviceInstanceOverride: "
1970 "Could not open device registry key [%lx]\n", status));
1971 }
1972 else
1973 {
1974 RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME);
1975
1976 status = WdfRegistryOpenKey(deviceKey,
1977 &subkeyName,
1978 KEY_READ,
1979 WDF_NO_OBJECT_ATTRIBUTES,
1980 &subKey);
1981 }
1982
1983 if (!NT_SUCCESS(status))
1984 {
1985 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1986 "DeviceMediaChangeDeviceInstanceOverride: "
1987 "subkey could not be created. %lx\n", status));
1988 }
1989 else
1990 {
1991 // Default to not changing autorun behavior, based upon setting
1992 // registryValue to zero.
1993 RtlInitUnicodeString(&enableMcnValueName, MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME);
1994 RtlInitUnicodeString(&disableMcnValueName, MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME);
1995
1996 // Ignore failures on reading of subkeys
1997 (VOID) WdfRegistryQueryULong(subKey,
1998 &enableMcnValueName,
1999 &alwaysEnable);
2000 (VOID) WdfRegistryQueryULong(subKey,
2001 &disableMcnValueName,
2002 &alwaysDisable);
2003 }
2004
2005 // set return value and cleanup
2006
2007 if (subKey != NULL)
2008 {
2009 WdfRegistryClose(subKey);
2010 }
2011
2012 if (deviceKey != NULL)
2013 {
2014 WdfRegistryClose(deviceKey);
2015 }
2016
2017 if (alwaysEnable && alwaysDisable)
2018 {
2019 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2020 "DeviceMediaChangeDeviceInstanceOverride: %s selected\n",
2021 "Both Enable and Disable set -- DISABLE"));
2022 NT_ASSERT(NT_SUCCESS(status));
2023 status = STATUS_SUCCESS;
2024 *Enabled = FALSE;
2025 }
2026 else if (alwaysDisable)
2027 {
2028 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2029 "DeviceMediaChangeDeviceInstanceOverride: %s selected\n",
2030 "DISABLE"));
2031 NT_ASSERT(NT_SUCCESS(status));
2032 status = STATUS_SUCCESS;
2033 *Enabled = FALSE;
2034 }
2035 else if (alwaysEnable)
2036 {
2037 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2038 "DeviceMediaChangeDeviceInstanceOverride: %s selected\n",
2039 "ENABLE"));
2040 NT_ASSERT(NT_SUCCESS(status));
2041 status = STATUS_SUCCESS;
2042 *Enabled = TRUE;
2043 }
2044 else
2045 {
2046 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2047 "DeviceMediaChangeDeviceInstanceOverride: %s selected\n",
2048 "DEFAULT"));
2049 status = STATUS_UNSUCCESSFUL;
2050 }
2051
2052 return status;
2053 } // end DeviceMediaChangeDeviceInstanceOverride()
2054
2055
2056 NTSTATUS
2057 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
2058 DeviceMediaChangeRegistryCallBack(
2059 _In_z_ PWSTR ValueName,
2060 _In_ ULONG ValueType,
2061 _In_reads_bytes_opt_(ValueLength) PVOID ValueData,
2062 _In_ ULONG ValueLength,
2063 _In_opt_ PVOID Context,
2064 _In_opt_ PVOID EntryContext
2065 )
2066 /*++
2067
2068 Routine Description:
2069
2070 This callback for a registry SZ or MULTI_SZ is called once for each
2071 SZ in the value. It will attempt to match the data with the
2072 UNICODE_STRING passed in as Context, and modify EntryContext if a
2073 match is found. Written for ClasspCheckRegistryForMediaChangeCompletion
2074
2075 Arguments:
2076
2077 ValueName - name of the key that was opened
2078 ValueType - type of data stored in the value (REG_SZ for this routine)
2079 ValueData - data in the registry, in this case a wide string
2080 ValueLength - length of the data including the terminating null
2081 Context - unicode string to compare against ValueData
2082 EntryContext - should be initialized to 0, will be set to 1 if match found
2083
2084 Return Value:
2085
2086 STATUS_SUCCESS
2087 EntryContext will be 1 if found
2088
2089 --*/
2090 {
2091 PULONG valueFound;
2092 PUNICODE_STRING deviceString;
2093 PWSTR keyValue;
2094
2095 PAGED_CODE();
2096
2097 UNREFERENCED_PARAMETER(ValueName);
2098
2099 if ((Context == NULL) || (EntryContext == NULL))
2100 {
2101 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
2102 "DeviceMediaChangeRegistryCallBack: NULL context should never be passed to registry call-back!\n"));
2103
2104 return STATUS_SUCCESS;
2105 }
2106
2107 // if we have already set the value to true, exit
2108 valueFound = EntryContext;
2109 if ((*valueFound) != 0)
2110 {
2111 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2112 "DeviceMediaChangeRegistryCallBack: already set to true\n"));
2113 return STATUS_SUCCESS;
2114 }
2115
2116 if (ValueLength == sizeof(WCHAR))
2117 {
2118 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
2119 "DeviceMediaChangeRegistryCallBack: NULL string should never be passed to registry call-back!\n"));
2120 return STATUS_SUCCESS;
2121 }
2122
2123 // if the data is not a terminated string, exit
2124 if (ValueType != REG_SZ)
2125 {
2126 return STATUS_SUCCESS;
2127 }
2128
2129 deviceString = Context;
2130 keyValue = ValueData;
2131 ValueLength -= sizeof(WCHAR); // ignore the null character
2132
2133 // do not compare more memory than is in deviceString
2134 if (ValueLength > deviceString->Length)
2135 {
2136 ValueLength = deviceString->Length;
2137 }
2138
2139 if (keyValue == NULL)
2140 {
2141 return STATUS_SUCCESS;
2142 }
2143
2144 // if the strings match, disable autorun
2145 if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength)
2146 {
2147 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "DeviceMediaChangeRegistryCallBack: Match found\n"));
2148 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "DeviceMediaChangeRegistryCallBack: DeviceString at %p\n",
2149 deviceString->Buffer));
2150 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
2151 "DeviceMediaChangeRegistryCallBack: KeyValue at %p\n",
2152 keyValue));
2153 (*valueFound) = TRUE;
2154 }
2155
2156 return STATUS_SUCCESS;
2157 } // end DeviceMediaChangeRegistryCallBack()
2158
2159
2160 _IRQL_requires_max_(PASSIVE_LEVEL)
2161 BOOLEAN
2162 DeviceIsMediaChangeDisabledDueToHardwareLimitation(
2163 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
2164 )
2165 /*++
2166
2167 Routine Description:
2168
2169 The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
2170 which to never enable MediaChangeNotification.
2171
2172 The user can override the global setting to enable or disable Autorun on a
2173 specific cdrom device via the control panel.
2174
2175 NOTE: It's intended that not use WdfRegistryQueryMultiString in this funciton,
2176 as it's much more complicated.(needs WDFCOLLECTION, WDFSTRING other than
2177 UNICODE_STRING)
2178
2179 Arguments:
2180
2181 FdoExtension -
2182 RegistryPath - pointer to the unicode string inside
2183 ...\CurrentControlSet\Services\Cdrom
2184
2185 Return Value:
2186
2187 TRUE - no autorun.
2188 FALSE - Autorun may be enabled
2189
2190 --*/
2191 {
2192 NTSTATUS status;
2193
2194 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = DeviceExtension->DeviceDescriptor;
2195 WDFKEY wdfKey;
2196 HANDLE serviceKey = NULL;
2197 RTL_QUERY_REGISTRY_TABLE parameters[2] = {0};
2198
2199 UNICODE_STRING deviceUnicodeString = {0};
2200 ANSI_STRING deviceString = {0};
2201 ULONG mediaChangeNotificationDisabled = 0;
2202
2203 PAGED_CODE();
2204
2205 // open the service key.
2206 status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
2207 KEY_ALL_ACCESS,
2208 WDF_NO_OBJECT_ATTRIBUTES,
2209 &wdfKey);
2210
2211 if(!NT_SUCCESS(status))
2212 {
2213 // NT_ASSERT(FALSE); __REACTOS__ : allow to fail (for 1st stage setup)
2214
2215 // always take the safe path. if we can't open the service key, disable autorun
2216 return TRUE;
2217 }
2218
2219 if(NT_SUCCESS(status))
2220 {
2221 // Determine if drive is in a list of those requiring
2222 // autorun to be disabled. this is stored in a REG_MULTI_SZ
2223 // named AutoRunAlwaysDisable. this is required as some autochangers
2224 // must load the disc to reply to ChkVerify request, causing them
2225 // to cycle discs continuously.
2226
2227 PWSTR nullMultiSz;
2228 PUCHAR vendorId = NULL;
2229 PUCHAR productId = NULL;
2230 PUCHAR revisionId = NULL;
2231 size_t length;
2232 size_t offset;
2233
2234 deviceString.Buffer = NULL;
2235 deviceUnicodeString.Buffer = NULL;
2236
2237 serviceKey = WdfRegistryWdmGetHandle(wdfKey);
2238
2239 // there may be nothing to check against
2240 if ((deviceDescriptor->VendorIdOffset == 0) &&
2241 (deviceDescriptor->ProductIdOffset == 0))
2242 {
2243 // no valid data in device extension.
2244 status = STATUS_INTERNAL_ERROR;
2245 }
2246
2247 // build deviceString using VendorId, Model and Revision.
2248 // this string will be used to checked if it's one of devices in registry disable list.
2249 if (NT_SUCCESS(status))
2250 {
2251 length = 0;
2252
2253 if (deviceDescriptor->VendorIdOffset == 0)
2254 {
2255 vendorId = NULL;
2256 }
2257 else
2258 {
2259 vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset;
2260 length = strlen((LPCSTR)vendorId);
2261 }
2262
2263 if ( deviceDescriptor->ProductIdOffset == 0 )
2264 {
2265 productId = NULL;
2266 }
2267 else
2268 {
2269 productId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductIdOffset;
2270 length += strlen((LPCSTR)productId);
2271 }
2272
2273 if ( deviceDescriptor->ProductRevisionOffset == 0 )
2274 {
2275 revisionId = NULL;
2276 }
2277 else
2278 {
2279 revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset;
2280 length += strlen((LPCSTR)revisionId);
2281 }
2282
2283 // allocate a buffer for the string
2284 deviceString.Length = (USHORT)( length );
2285 deviceString.MaximumLength = deviceString.Length + 1;
2286 deviceString.Buffer = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolNx,
2287 deviceString.MaximumLength,
2288 CDROM_TAG_AUTORUN_DISABLE
2289 );
2290 if (deviceString.Buffer == NULL)
2291 {
2292 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2293 "DeviceIsMediaChangeDisabledDueToHardwareLimitation: Unable to alloc string buffer\n" ));
2294 status = STATUS_INTERNAL_ERROR;
2295 }
2296 }
2297
2298 if (NT_SUCCESS(status))
2299 {
2300 // copy strings to the buffer
2301 offset = 0;
2302
2303 if (vendorId != NULL)
2304 {
2305 RtlCopyMemory(deviceString.Buffer + offset,
2306 vendorId,
2307 strlen((LPCSTR)vendorId));
2308 offset += strlen((LPCSTR)vendorId);
2309 }
2310
2311 if ( productId != NULL )
2312 {
2313 RtlCopyMemory(deviceString.Buffer + offset,
2314 productId,
2315 strlen((LPCSTR)productId));
2316 offset += strlen((LPCSTR)productId);
2317 }
2318 if ( revisionId != NULL )
2319 {
2320 RtlCopyMemory(deviceString.Buffer + offset,
2321 revisionId,
2322 strlen((LPCSTR)revisionId));
2323 offset += strlen((LPCSTR)revisionId);
2324 }
2325
2326 NT_ASSERT(offset == deviceString.Length);
2327
2328 #pragma warning(suppress:6386) // Not an issue as deviceString.Buffer is of size deviceString.MaximumLength, which is equal to (deviceString.Length + 1)
2329 deviceString.Buffer[deviceString.Length] = '\0'; // Null-terminated
2330
2331 // convert to unicode as registry deals with unicode strings
2332 status = RtlAnsiStringToUnicodeString( &deviceUnicodeString,
2333 &deviceString,
2334 TRUE
2335 );
2336 if (!NT_SUCCESS(status))
2337 {
2338 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2339 "DeviceIsMediaChangeDisabledDueToHardwareLimitation: cannot convert "
2340 "to unicode %lx\n", status));
2341 }
2342 }
2343
2344 if (NT_SUCCESS(status))
2345 {
2346 // query the value, setting valueFound to true if found
2347 nullMultiSz = L"\0";
2348 parameters[0].QueryRoutine = DeviceMediaChangeRegistryCallBack;
2349 parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
2350 parameters[0].Name = L"AutoRunAlwaysDisable";
2351 parameters[0].EntryContext = &mediaChangeNotificationDisabled;
2352 parameters[0].DefaultType = REG_MULTI_SZ;
2353 parameters[0].DefaultData = nullMultiSz;
2354 parameters[0].DefaultLength = 0;
2355
2356 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
2357 serviceKey,
2358 parameters,
2359 &deviceUnicodeString,
2360 NULL);
2361 UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning.
2362 }
2363 }
2364
2365 // Cleanup
2366 {
2367
2368 FREE_POOL( deviceString.Buffer );
2369 if (deviceUnicodeString.Buffer != NULL)
2370 {
2371 RtlFreeUnicodeString( &deviceUnicodeString );
2372 }
2373
2374 // handle serviceKey will be closed by framework while it closes registry key.
2375 WdfRegistryClose(wdfKey);
2376 }
2377
2378 if (mediaChangeNotificationDisabled > 0)
2379 {
2380 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2381 "DeviceIsMediaChangeDisabledDueToHardwareLimitation: Device is on MCN disable list\n"));
2382 }
2383
2384 return (mediaChangeNotificationDisabled > 0);
2385
2386 } // end DeviceIsMediaChangeDisabledDueToHardwareLimitation()
2387
2388
2389 _IRQL_requires_max_(PASSIVE_LEVEL)
2390 BOOLEAN
2391 DeviceIsMediaChangeDisabledForClass(
2392 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
2393 )
2394 /*++
2395
2396 Routine Description:
2397
2398 The user must specify that AutoPlay is to run on the platform
2399 by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
2400 Services\<SERVICE>\Autorun:REG_DWORD:1.
2401
2402 The user can override the global setting to enable or disable Autorun on a
2403 specific cdrom device via the control panel.
2404
2405 Arguments:
2406
2407 DeviceExtension - device extension
2408
2409 Return Value:
2410
2411 TRUE - Autorun is disabled for this class
2412 FALSE - Autorun is enabled for this class
2413
2414 --*/
2415 {
2416 NTSTATUS status;
2417 WDFKEY serviceKey = NULL;
2418 WDFKEY parametersKey = NULL;
2419
2420 UNICODE_STRING parameterKeyName;
2421 UNICODE_STRING valueName;
2422
2423 // Default to ENABLING MediaChangeNotification (!)
2424 ULONG mcnRegistryValue = 1;
2425
2426 PAGED_CODE();
2427
2428 // open the service key.
2429 status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
2430 KEY_ALL_ACCESS,
2431 WDF_NO_OBJECT_ATTRIBUTES,
2432 &serviceKey);
2433 if(!NT_SUCCESS(status))
2434 {
2435 // return the default value, which is the inverse of the registry setting default
2436 // since this routine asks if it's disabled
2437 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2438 "DeviceIsMediaChangeDisabledForClass: Defaulting to %s\n",
2439 (mcnRegistryValue ? "Enabled" : "Disabled")));
2440 return (BOOLEAN)(mcnRegistryValue == 0);
2441 }
2442 else
2443 {
2444 // Open the parameters key (if any) beneath the services key.
2445 RtlInitUnicodeString(&parameterKeyName, L"Parameters");
2446
2447 status = WdfRegistryOpenKey(serviceKey,
2448 &parameterKeyName,
2449 KEY_READ,
2450 WDF_NO_OBJECT_ATTRIBUTES,
2451 &parametersKey);
2452 }
2453
2454 if (!NT_SUCCESS(status))
2455 {
2456 parametersKey = NULL;
2457 }
2458
2459 RtlInitUnicodeString(&valueName, L"Autorun");
2460 // ignore failures
2461 status = WdfRegistryQueryULong(serviceKey,
2462 &valueName,
2463 &mcnRegistryValue);
2464
2465 if (NT_SUCCESS(status))
2466 {
2467 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
2468 "DeviceIsMediaChangeDisabledForClass: <Service>/Autorun flag = %d\n",
2469 mcnRegistryValue));
2470 }
2471
2472 if (parametersKey != NULL)
2473 {
2474 status = WdfRegistryQueryULong(parametersKey,
2475 &valueName,
2476 &mcnRegistryValue);
2477
2478 if (NT_SUCCESS(status))
2479 {
2480 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
2481 "DeviceIsMediaChangeDisabledForClass: <Service>/Parameters/Autorun flag = %d\n",
2482 mcnRegistryValue));
2483 }
2484
2485 WdfRegistryClose(parametersKey);
2486 }
2487
2488 WdfRegistryClose(serviceKey);
2489
2490 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2491 "DeviceIsMediaChangeDisabledForClass: Autoplay for device %p is %s\n",
2492 DeviceExtension->DeviceObject,
2493 (mcnRegistryValue ? "on" : "off")
2494 ));
2495
2496 // return if it is _disabled_, which is the
2497 // inverse of the registry setting
2498
2499 return (BOOLEAN)(!mcnRegistryValue);
2500 } // end DeviceIsMediaChangeDisabledForClass()
2501
2502
2503 _IRQL_requires_max_(APC_LEVEL)
2504 VOID
2505 DeviceEnableMediaChangeDetection(
2506 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
2507 _Inout_ PFILE_OBJECT_CONTEXT FileObjectContext,
2508 _In_ BOOLEAN IgnorePreviousMediaChanges
2509 )
2510 /*++
2511
2512 Routine Description:
2513
2514 When the disable count decrease to 0, enable the MCN
2515
2516 Arguments:
2517
2518 DeviceExtension - the device context
2519
2520 FileObjectContext - the file object context
2521
2522 IgnorePreviousMediaChanges - ignore all previous media changes
2523
2524 Return Value:
2525 None.
2526
2527 --*/
2528 {
2529 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
2530 LONG oldCount;
2531
2532 PAGED_CODE();
2533
2534 if (FileObjectContext)
2535 {
2536 InterlockedDecrement((PLONG)&(FileObjectContext->McnDisableCount));
2537 }
2538
2539 if (info == NULL)
2540 {
2541 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2542 "DeviceEnableMediaChangeDetection: not initialized\n"));
2543 return;
2544 }
2545
2546 (VOID) KeWaitForMutexObject(&info->MediaChangeMutex,
2547 UserRequest,
2548 KernelMode,
2549 FALSE,
2550 NULL);
2551
2552 oldCount = --info->MediaChangeDetectionDisableCount;
2553
2554 NT_ASSERT(oldCount >= 0);
2555
2556 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
2557 "DeviceEnableMediaChangeDetection: Disable count reduced to %d - \n",
2558 info->MediaChangeDetectionDisableCount));
2559
2560 if (oldCount == 0)
2561 {
2562 if (IgnorePreviousMediaChanges)
2563 {
2564 info->LastReportedMediaDetectionState = info->LastKnownMediaDetectionState;
2565 }
2566
2567 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN is enabled\n"));
2568 }
2569 else
2570 {
2571 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "MCD still disabled\n"));
2572 }
2573
2574 // Let something else run.
2575 KeReleaseMutex(&info->MediaChangeMutex, FALSE);
2576
2577 return;
2578 } // end DeviceEnableMediaChangeDetection()
2579
2580
2581 _IRQL_requires_max_(APC_LEVEL)
2582 VOID
2583 DeviceDisableMediaChangeDetection(
2584 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
2585 _Inout_ PFILE_OBJECT_CONTEXT FileObjectContext
2586 )
2587 /*++
2588
2589 Routine Description:
2590
2591 Increase the disable count.
2592
2593 Arguments:
2594
2595 DeviceExtension - the device context
2596
2597 FileObjectContext - the file object context
2598
2599 Return Value:
2600 None.
2601
2602 --*/
2603 {
2604 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
2605
2606 PAGED_CODE();
2607
2608 if (FileObjectContext)
2609 {
2610 InterlockedIncrement((PLONG)&(FileObjectContext->McnDisableCount));
2611 }
2612
2613 if (info == NULL)
2614 {
2615 return;
2616 }
2617
2618 (VOID) KeWaitForMutexObject(&info->MediaChangeMutex,
2619 UserRequest,
2620 KernelMode,
2621 FALSE,
2622 NULL);
2623
2624 info->MediaChangeDetectionDisableCount++;
2625
2626 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
2627 "DisableMediaChangeDetection: disable count is %d\n",
2628 info->MediaChangeDetectionDisableCount));
2629
2630 KeReleaseMutex(&info->MediaChangeMutex, FALSE);
2631
2632 return;
2633 } // end DeviceDisableMediaChangeDetection()
2634
2635
2636 _IRQL_requires_max_(APC_LEVEL)
2637 VOID
2638 DeviceReleaseMcnResources(
2639 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
2640 )
2641 /*++
2642
2643 Routine Description:
2644
2645 This routine will cleanup any resources allocated for MCN. It is called
2646 by classpnp during remove device, and therefore is not typically required
2647 by external drivers.
2648
2649 Arguments:
2650
2651 DeviceExtension - the device context
2652
2653 Return Value:
2654 None.
2655
2656 --*/
2657 {
2658 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
2659
2660 PAGED_CODE()
2661
2662 if(info == NULL)
2663 {
2664 return;
2665 }
2666
2667 if (info->Gesn.Mdl)
2668 {
2669 PIRP irp = WdfRequestWdmGetIrp(info->MediaChangeRequest);
2670 IoFreeMdl(info->Gesn.Mdl);
2671 irp->MdlAddress = NULL;
2672 }
2673 IoFreeIrp(info->MediaChangeSyncIrp);
2674 FREE_POOL(info->Gesn.Buffer);
2675 FREE_POOL(info->SenseBuffer);
2676
2677 if (info->DisplayStateCallbackHandle)
2678 {
2679 PoUnregisterPowerSettingCallback(info->DisplayStateCallbackHandle);
2680 info->DisplayStateCallbackHandle = NULL;
2681 }
2682
2683 FREE_POOL(info);
2684
2685 DeviceExtension->MediaChangeDetectionInfo = NULL;
2686
2687 return;
2688 } // end DeviceReleaseMcnResources()
2689
2690
2691 IO_COMPLETION_ROUTINE RequestMcnSyncIrpCompletion;
2692
2693 NTSTATUS
2694 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
2695 RequestMcnSyncIrpCompletion(
2696 _In_ PDEVICE_OBJECT DeviceObject,
2697 _In_ PIRP Irp,
2698 _In_reads_opt_(_Inexpressible_("varies")) PVOID Context
2699 )
2700 /*++
2701
2702 Routine Description:
2703
2704 The MCN work finishes, reset the fields to allow another MCN request
2705 be scheduled.
2706
2707 Arguments:
2708
2709 DeviceObject - device that the completion routine fires on.
2710
2711 Irp - The irp to be completed.
2712
2713 Context - IRP context
2714
2715 Return Value:
2716 NTSTATUS
2717
2718 --*/
2719 {
2720 PCDROM_DEVICE_EXTENSION DeviceExtension = NULL;
2721 PMEDIA_CHANGE_DETECTION_INFO info = NULL;
2722
2723 if (Context == NULL)
2724 {
2725 // this will never happen, but code must be there to prevent OACR warnings.
2726 return STATUS_MORE_PROCESSING_REQUIRED;
2727 }
2728
2729 DeviceExtension = (PCDROM_DEVICE_EXTENSION) Context;
2730 info = DeviceExtension->MediaChangeDetectionInfo;
2731
2732 #ifndef DEBUG
2733 UNREFERENCED_PARAMETER(Irp);
2734 #endif
2735 UNREFERENCED_PARAMETER(DeviceObject);
2736
2737 NT_ASSERT(Irp == info->MediaChangeSyncIrp);
2738
2739 IoReuseIrp(info->MediaChangeSyncIrp, STATUS_NOT_SUPPORTED);
2740
2741 // reset the value to let timer routine be able to send the next request.
2742 InterlockedCompareExchange((PLONG)&(info->MediaChangeRequestInUse), 0, 1);
2743
2744 return STATUS_MORE_PROCESSING_REQUIRED;
2745 }
2746
2747
2748 VOID
2749 RequestSetupMcnSyncIrp(
2750 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
2751 )
2752 /*++
2753
2754 Routine Description:
2755
2756 setup the MCN synchronization irp.
2757
2758 Arguments:
2759
2760 DeviceExtension - the device context
2761
2762 Return Value:
2763 None
2764
2765 --*/
2766 {
2767 PIRP irp = NULL;
2768 PIO_STACK_LOCATION irpStack = NULL;
2769 PIO_STACK_LOCATION nextIrpStack = NULL;
2770
2771 irp = DeviceExtension->MediaChangeDetectionInfo->MediaChangeSyncIrp;
2772 NT_ASSERT(irp != NULL);
2773
2774 //
2775 // For the driver that creates an IRP, there is no 'current' stack location.
2776 // Step down one IRP stack location so that the extra top one
2777 // becomes our 'current' one.
2778 //
2779 IoSetNextIrpStackLocation(irp);
2780
2781 /*
2782 * Cache our device object in the extra top IRP stack location
2783 * so we have it in our completion routine.
2784 */
2785 irpStack = IoGetCurrentIrpStackLocation(irp);
2786 irpStack->DeviceObject = DeviceExtension->DeviceObject;
2787
2788 //
2789 // If the irp is sent down when the volume needs to be
2790 // verified, CdRomUpdateGeometryCompletion won't complete
2791 // it since it's not associated with a thread. Marking
2792 // it to override the verify causes it always be sent
2793 // to the port driver
2794 //
2795 nextIrpStack = IoGetNextIrpStackLocation(irp);
2796
2797 SET_FLAG(nextIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
2798
2799 nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
2800 // pick up this IOCTL code as it's not normaly seen for CD/DVD drive and does not require input.
2801 // set other fields to make this IOCTL recognizable by CDROM.SYS
2802 nextIrpStack->Parameters.Others.Argument1 = RequestSetupMcnSyncIrp;
2803 nextIrpStack->Parameters.Others.Argument2 = RequestSetupMcnSyncIrp;
2804 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MCN_SYNC_FAKE_IOCTL; //Argument3.
2805 nextIrpStack->Parameters.Others.Argument4 = RequestSetupMcnSyncIrp;
2806
2807 IoSetCompletionRoutine(irp,
2808 RequestMcnSyncIrpCompletion,
2809 DeviceExtension,
2810 TRUE,
2811 TRUE,
2812 TRUE);
2813
2814 return;
2815 }
2816
2817
2818 VOID
2819 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
2820 DeviceMainTimerTickHandler(
2821 _In_ WDFTIMER Timer
2822 )
2823 /*++
2824
2825 Routine Description:
2826
2827 This routine setup a sync irp and send it to the serial queue.
2828 Serial queue will process MCN when receive this sync irp.
2829
2830 Arguments:
2831
2832 Timer - the timer object that fires.
2833
2834 Return Value:
2835 None
2836
2837 --*/
2838 {
2839 PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
2840 size_t dataLength = 0;
2841
2842 deviceExtension = WdfObjectGetTypedContext(WdfTimerGetParentObject(Timer), CDROM_DEVICE_EXTENSION);
2843
2844 (void) RequestHandleEventNotification(deviceExtension, NULL, NULL, &dataLength);
2845
2846 return;
2847 } // end DeviceMainTimerTickHandler()
2848
2849
2850 _IRQL_requires_max_(APC_LEVEL)
2851 NTSTATUS
2852 DeviceEnableMainTimer(
2853 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
2854 )
2855 /*++
2856
2857 Routine Description:
2858
2859 This routine will allocate timer related resources on the first time call.
2860 Start the timer.
2861
2862 Arguments:
2863
2864 DeviceExtension - the device context
2865
2866 Return Value:
2867 NTSTATUS
2868
2869 --*/
2870 {
2871 NTSTATUS status = STATUS_SUCCESS;
2872
2873 if ((DeviceExtension->MediaChangeDetectionInfo == NULL) ||
2874 (DeviceExtension->MediaChangeDetectionInfo->AsynchronousNotificationSupported != FALSE))
2875 {
2876 // Asynchronous Notification is enabled, timer not needed.
2877 return status;
2878 }
2879
2880 if (DeviceExtension->MainTimer == NULL)
2881 {
2882 //create main timer object.
2883 WDF_TIMER_CONFIG timerConfig;
2884 WDF_OBJECT_ATTRIBUTES timerAttributes;
2885
2886 WDF_TIMER_CONFIG_INIT(&timerConfig, DeviceMainTimerTickHandler);
2887
2888 // Polling frequently on virtual optical devices created by Hyper-V will
2889 // cause a significant perf / power hit. These devices need to be polled
2890 // less frequently for device state changes.
2891 if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_MSFT_VIRTUAL_ODD))
2892 {
2893 timerConfig.Period = 2000; // 2 seconds, in milliseconds.
2894 }
2895 else
2896 {
2897 timerConfig.Period = 1000; // 1 second, in milliseconds.
2898 }
2899
2900 timerConfig.TolerableDelay = 500; // 0.5 seconds, in milliseconds
2901
2902 //Set the autoSerialization to FALSE, as the parent device's
2903 //execute level is WdfExecutionLevelPassive.
2904 timerConfig.AutomaticSerialization = FALSE;
2905
2906 WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
2907 timerAttributes.ParentObject = DeviceExtension->Device;
2908 timerAttributes.ExecutionLevel = WdfExecutionLevelInheritFromParent;
2909
2910 status = WdfTimerCreate(&timerConfig,
2911 &timerAttributes,
2912 &DeviceExtension->MainTimer);
2913 }
2914
2915 if (NT_SUCCESS(status))
2916 {
2917 WdfTimerStart(DeviceExtension->MainTimer,WDF_REL_TIMEOUT_IN_MS(100));
2918
2919 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2920 "DeviceEnableMainTimer: Once a second timer enabled for WDFDEVICE %p\n",
2921 DeviceExtension->Device));
2922 }
2923 else
2924 {
2925 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2926 "DeviceEnableMainTimer: WDFDEVICE %p, Status %lx initializing timer\n",
2927 DeviceExtension->Device, status));
2928 }
2929
2930 return status;
2931 } // end DeviceEnableMainTimer()
2932
2933
2934 _IRQL_requires_max_(PASSIVE_LEVEL)
2935 VOID
2936 DeviceDisableMainTimer(
2937 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
2938 )
2939 /*++
2940
2941 Routine Description:
2942
2943 stop the timer.
2944
2945 Arguments:
2946
2947 DeviceExtension - device context
2948
2949 Return Value:
2950 None
2951
2952 --*/
2953 {
2954 PAGED_CODE();
2955
2956 if ((DeviceExtension->MediaChangeDetectionInfo == NULL) ||
2957 (DeviceExtension->MediaChangeDetectionInfo->AsynchronousNotificationSupported != FALSE))
2958 {
2959 // Asynchronous Notification is enabled, timer not needed.
2960 return;
2961 }
2962
2963 if (DeviceExtension->MainTimer != NULL)
2964 {
2965 //
2966 // we are only going to stop the actual timer in remove device routine.
2967 // it is the responsibility of the code within the timer routine to
2968 // check if the device is removed and not processing io for the final
2969 // call.
2970 // this keeps the code clean and prevents lots of bugs.
2971 //
2972 WdfTimerStop(DeviceExtension->MainTimer,TRUE);
2973
2974 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2975 "DeviceDisableMainTimer: Once a second timer disabled for device %p\n",
2976 DeviceExtension->Device));
2977 }
2978 else
2979 {
2980 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
2981 "DeviceDisableMainTimer: Timer never enabled\n"));
2982 }
2983
2984 return;
2985 } // end DeviceDisableMainTimer()
2986
2987
2988 _IRQL_requires_max_(APC_LEVEL)
2989 NTSTATUS
2990 RequestHandleMcnControl(
2991 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
2992 _In_ WDFREQUEST Request,
2993 _Out_ size_t * DataLength
2994 )
2995 /*++
2996
2997 Routine Description:
2998
2999 This routine handles the process of IOCTL_STORAGE_MCN_CONTROL
3000
3001 Arguments:
3002
3003 DeviceExtension - device context
3004
3005 Request - request object
3006
3007 RequestParameters - request parameters
3008
3009 DataLength - data transferred
3010
3011 Return Value:
3012 NTSTATUS
3013
3014 --*/
3015 {
3016 NTSTATUS status = STATUS_SUCCESS;
3017 WDFFILEOBJECT fileObject = NULL;
3018 PFILE_OBJECT_CONTEXT fileObjectContext = NULL;
3019 PPREVENT_MEDIA_REMOVAL mediaRemoval = NULL;
3020
3021 PAGED_CODE();
3022
3023 *DataLength = 0;
3024
3025 status = WdfRequestRetrieveInputBuffer(Request,
3026 sizeof(PREVENT_MEDIA_REMOVAL),
3027 &mediaRemoval,
3028 NULL);
3029
3030 if (NT_SUCCESS(status))
3031 {
3032 fileObject = WdfRequestGetFileObject(Request);
3033
3034 // Check to make sure we have a file object extension to keep track of this
3035 // request. If not we'll fail it before synchronizing.
3036 if (fileObject != NULL)
3037 {
3038 fileObjectContext = FileObjectGetContext(fileObject);
3039 }
3040
3041 if ((fileObjectContext == NULL) &&
3042 (WdfRequestGetRequestorMode(Request) == KernelMode))
3043 {
3044 fileObjectContext = &DeviceExtension->KernelModeMcnContext;
3045 }
3046
3047 if (fileObjectContext == NULL)
3048 {
3049 // This handle isn't setup correctly. We can't let the
3050 // operation go.
3051 status = STATUS_INVALID_PARAMETER;
3052 }
3053 }
3054
3055 if (NT_SUCCESS(status))
3056 {
3057 if (mediaRemoval->PreventMediaRemoval)
3058 {
3059 // This is a lock command. Reissue the command in case bus or
3060 // device was reset and the lock was cleared.
3061 DeviceDisableMediaChangeDetection(DeviceExtension, fileObjectContext);
3062 }
3063 else
3064 {
3065 if (fileObjectContext->McnDisableCount == 0)
3066 {
3067 status = STATUS_INVALID_DEVICE_STATE;
3068 }
3069 else
3070 {
3071 DeviceEnableMediaChangeDetection(DeviceExtension, fileObjectContext, TRUE);
3072 }
3073 }
3074 }
3075
3076 return status;
3077 } // end RequestHandleMcnControl()
3078
3079 #pragma warning(pop) // un-sets any local warning changes
3080