1 /*++
2
3 Copyright (C) Microsoft Corporation, 1991 - 2010
4
5 Module Name:
6
7 autorun.c
8
9 Abstract:
10
11 Code for support of media change detection in the class driver
12
13 Environment:
14
15 kernel mode only
16
17 Notes:
18
19
20 Revision History:
21
22 --*/
23
24 #include "classp.h"
25 #include "debug.h"
26
27 #ifdef DEBUG_USE_WPP
28 #include "autorun.tmh"
29 #endif
30
31 #define GESN_TIMEOUT_VALUE (0x4)
32 #define GESN_BUFFER_SIZE (0x8)
33 #define GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS (2)
34
35 #define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20)
36 #define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification")
37 #define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN")
38 #define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN")
39
40 const GUID StoragePredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;
41
42 //
43 // Only send polling irp when device is fully powered up, a
44 // power down irp is not in progress, and the screen is on.
45 //
46 // NOTE: This helps close a window in time where a polling irp could cause
47 // a drive to spin up right after it has powered down. The problem is
48 // that SCSIPORT, ATAPI and SBP2 will be in the process of powering
49 // down (which may take a few seconds), but won't know that. It would
50 // then get a polling irp which will be put into its queue since it
51 // the disk isn't powered down yet. Once the disk is powered down it
52 // will find the polling irp in the queue and then power up the
53 // device to do the poll. They do not want to check if the polling
54 // irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical
55 // path and would slow down all I/Os. A better way to fix this
56 // would be to serialize the polling and power down irps so that
57 // only one of them is sent to the device at a time.
58 //
59 static
60 BOOLEAN
ClasspCanSendPollingIrp(_In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension)61 ClasspCanSendPollingIrp(
62 _In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
63 )
64 {
65 return ((fdoExtension->DevicePowerState == PowerDeviceD0) &&
66 (fdoExtension->PowerDownInProgress == FALSE) &&
67 (ClasspScreenOff == FALSE));
68 }
69
70 BOOLEAN
71 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
72 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
73 IN PUNICODE_STRING RegistryPath
74 );
75
76 NTSTATUS
77 ClasspMediaChangeDeviceInstanceOverride(
78 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
79 OUT PBOOLEAN Enabled
80 );
81
82 BOOLEAN
83 ClasspIsMediaChangeDisabledForClass(
84 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
85 IN PUNICODE_STRING RegistryPath
86 );
87
88 VOID
89 ClasspSetMediaChangeStateEx(
90 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
91 IN MEDIA_CHANGE_DETECTION_STATE NewState,
92 IN BOOLEAN Wait,
93 IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
94 );
95
96 RTL_QUERY_REGISTRY_ROUTINE ClasspMediaChangeRegistryCallBack;
97
98 VOID
99 ClasspSendMediaStateIrp(
100 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
101 IN PMEDIA_CHANGE_DETECTION_INFO Info,
102 IN ULONG CountDown
103 );
104
105 IO_WORKITEM_ROUTINE ClasspFailurePredict;
106
107 NTSTATUS
108 ClasspInitializePolling(
109 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
110 IN BOOLEAN AllowDriveToSleep
111 );
112
113
114 IO_WORKITEM_ROUTINE ClasspDisableGesn;
115
116 IO_COMPLETION_ROUTINE ClasspMediaChangeDetectionCompletion;
117
118 KDEFERRED_ROUTINE ClasspTimerTick;
119
120 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
121 EXT_CALLBACK ClasspTimerTickEx;
122 #endif
123
124 BOOLEAN ClasspScreenOff = FALSE;
125
126 //
127 // Tick timer related defines.
128 //
129 #define TICK_TIMER_PERIOD_IN_MSEC 1000
130 #define TICK_TIMER_DELAY_IN_MSEC 1000
131
132 #if ALLOC_PRAGMA
133
134 #pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
135 #pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
136 #pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
137 #pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
138 #pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
139 #pragma alloc_text(PAGE, ClasspInitializePolling)
140 #pragma alloc_text(PAGE, ClasspDisableGesn)
141
142 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
143 #pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
144 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)
145
146 #pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)
147
148 #pragma alloc_text(PAGE, ClasspInitializeGesn)
149 #pragma alloc_text(PAGE, ClasspMcnControl)
150
151 #endif
152
153 // ISSUE -- make this public?
154 VOID
ClassSendEjectionNotification(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)155 ClassSendEjectionNotification(
156 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
157 )
158 {
159 //
160 // For post-NT5.1 work, need to move EjectSynchronizationEvent
161 // to be a MUTEX so we can attempt to grab it here and benefit
162 // from deadlock detection. This will allow checking if the media
163 // has been locked by programs before broadcasting these events.
164 // (what's the point of broadcasting if the media is not locked?)
165 //
166 // This would currently only be a slight optimization. For post-NT5.1,
167 // it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
168 // thereby cleaning up a lot of the ejection code. Then, when the
169 // ejection request occured, we could see if any locks for the media
170 // existed. if locked, broadcast. if not, we send the eject irp.
171 //
172
173 //
174 // for now, just always broadcast. make this a public routine,
175 // so class drivers can add special hacks to broadcast this for their
176 // non-MMC-compliant devices also from sense codes.
177 //
178
179 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassSendEjectionNotification: media EJECT_REQUEST"));
180 ClassSendNotification(FdoExtension,
181 &GUID_IO_MEDIA_EJECT_REQUEST,
182 0,
183 NULL);
184 return;
185 }
186
187
_IRQL_requires_max_(DISPATCH_LEVEL)188 _IRQL_requires_max_(DISPATCH_LEVEL)
189 VOID
190 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
191 ClassSendNotification(
192 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
193 _In_ const GUID * Guid,
194 _In_ ULONG ExtraDataSize,
195 _In_reads_bytes_opt_(ExtraDataSize) PVOID ExtraData
196 )
197 {
198 PTARGET_DEVICE_CUSTOM_NOTIFICATION notification;
199 ULONG requiredSize;
200 NTSTATUS status;
201
202 status = RtlULongAdd((sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)),
203 ExtraDataSize,
204 &requiredSize);
205
206 if (!(NT_SUCCESS(status)) || (requiredSize > 0x0000ffff)) {
207 // MAX_USHORT, max total size for these events!
208 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
209 "Error sending event: size too large! (%x)\n",
210 requiredSize));
211 return;
212 }
213
214 notification = ExAllocatePoolWithTag(NonPagedPoolNx,
215 requiredSize,
216 'oNcS');
217
218 //
219 // if none allocated, exit
220 //
221
222 if (notification == NULL) {
223 return;
224 }
225
226 //
227 // Prepare and send the request!
228 //
229
230 RtlZeroMemory(notification, requiredSize);
231 notification->Version = 1;
232 notification->Size = (USHORT)(requiredSize);
233 notification->FileObject = NULL;
234 notification->NameBufferOffset = -1;
235 notification->Event = *Guid;
236
237 if (ExtraData != NULL && ExtraDataSize != 0) {
238 RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
239 }
240
241 IoReportTargetDeviceChangeAsynchronous(FdoExtension->LowerPdo,
242 notification,
243 NULL, NULL);
244
245 FREE_POOL(notification);
246 return;
247 }
248
249
250 NTSTATUS
ClasspInterpretGesnData(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN PNOTIFICATION_EVENT_STATUS_HEADER Header,OUT PBOOLEAN ResendImmediately)251 ClasspInterpretGesnData(
252 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
253 IN PNOTIFICATION_EVENT_STATUS_HEADER Header,
254 OUT PBOOLEAN ResendImmediately
255 )
256
257 /*++
258
259 Routine Description:
260
261 This routine will interpret the data returned for a GESN command, and
262 (if appropriate) set the media change event, and broadcast the
263 appropriate events to user mode for applications who care.
264
265 Arguments:
266
267 FdoExtension - the device
268
269 DataBuffer - the resulting data from a GESN event.
270 requires at least EIGHT valid bytes (header == 4, data == 4)
271
272 ResendImmediately - whether or not to immediately resend the request.
273 this should be FALSE if there was no event, FALSE if the reported
274 event was of the DEVICE BUSY class, else true.
275
276 Return Value:
277
278 STATUS_SUCCESS if successful, an error code otherwise
279
280 Notes:
281
282 DataBuffer must be at least four bytes of valid data (header == 4 bytes),
283 and have at least eight bytes of allocated memory (all events == 4 bytes).
284
285 The call to StartNextPacket may occur before this routine is completed.
286 the operational change notifications are informational in nature, and
287 while useful, are not neccessary to ensure proper operation. For example,
288 if the device morphs to no longer supporting WRITE commands, all further
289 write commands will fail. There exists a small timing window wherein
290 IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If
291 a device supports software write protect, it is expected that the
292 application can handle such a case.
293
294 NOTE: perhaps setting the updaterequired byte to one should be done here.
295 if so, it relies upon the setting of a 32-byte value to be an atomic
296 operation. unfortunately, there is no simple way to notify a class driver
297 which wants to know that the device behavior requires updating.
298
299 Not ready events may be sent every second. For example, if we were
300 to minimize the number of asynchronous notifications, an application may
301 register just after a large busy time was reported. This would then
302 prevent the application from knowing the device was busy until some
303 arbitrarily chosen timeout has occurred. Also, the GESN request would
304 have to still occur, since it checks for non-busy events (such as user
305 keybutton presses and media change events) as well. The specification
306 states that the lower-numered events get reported first, so busy events,
307 while repeating, will only be reported when all other events have been
308 cleared from the device.
309
310 --*/
311
312 {
313 PMEDIA_CHANGE_DETECTION_INFO info;
314 LONG dataLength;
315 LONG requiredLength;
316 NTSTATUS status = STATUS_SUCCESS;
317
318 info = FdoExtension->MediaChangeDetectionInfo;
319
320 //
321 // note: don't allocate anything in this routine so that we can
322 // always just 'return'.
323 //
324
325 *ResendImmediately = FALSE;
326 if (Header->NEA) {
327 return status;
328 }
329 if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS) {
330 return status;
331 }
332
333 //
334 // HACKHACK - REF #0001
335 // This loop is only taken initially, due to the inability to reliably
336 // auto-detect drives that report events correctly at boot. When we
337 // detect this behavior during the normal course of running, we will
338 // disable the hack, allowing more efficient use of the system. This
339 // should occur "nearly" instantly, as the drive should have multiple
340 // events queue'd (ie. power, morphing, media).
341 //
342
343 if (info->Gesn.HackEventMask) {
344
345 //
346 // all events use the low four bytes of zero to indicate
347 // that there was no change in status.
348 //
349
350 UCHAR thisEvent = Header->ClassEventData[0] & 0xf;
351 UCHAR lowestSetBit;
352 UCHAR thisEventBit = (1 << Header->NotificationClass);
353
354 if (!TEST_FLAG(info->Gesn.EventMask, thisEventBit)) {
355
356 //
357 // The drive is reporting an event that wasn't requested
358 //
359
360 return STATUS_DEVICE_PROTOCOL_ERROR;
361 }
362
363 //
364 // some bit magic here... this results in the lowest set bit only
365 //
366
367 lowestSetBit = info->Gesn.EventMask;
368 lowestSetBit &= (info->Gesn.EventMask - 1);
369 lowestSetBit ^= (info->Gesn.EventMask);
370
371 if (thisEventBit != lowestSetBit) {
372
373 //
374 // HACKHACK - REF #0001
375 // the first time we ever see an event set that is not the lowest
376 // set bit in the request (iow, highest priority), we know that the
377 // hack is no longer required, as the device is ignoring "no change"
378 // events when a real event is waiting in the other requested queues.
379 //
380
381 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
382 "Classpnp => GESN::NONE: Compliant drive found, "
383 "removing GESN hack (%x, %x)\n",
384 thisEventBit, info->Gesn.EventMask));
385
386 info->Gesn.HackEventMask = FALSE;
387
388 } else if (thisEvent == 0) { // NOTIFICATION_*_EVENT_NO_CHANGE
389
390 //
391 // HACKHACK - REF #0001
392 // note: this hack prevents poorly implemented firmware from constantly
393 // returning "No Event". we do this by cycling through the
394 // supported list of events here.
395 //
396
397 SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit);
398 CLEAR_FLAG(info->Gesn.EventMask, thisEventBit);
399
400 //
401 // if we have cycled through all supported event types, then
402 // we need to reset the events we are asking about. else we
403 // want to resend this request immediately in case there was
404 // another event pending.
405 //
406
407 if (info->Gesn.EventMask == 0) {
408 info->Gesn.EventMask = info->Gesn.NoChangeEventMask;
409 info->Gesn.NoChangeEventMask = 0;
410 } else {
411 *ResendImmediately = TRUE;
412 }
413 return status;
414 }
415
416 } // end if (info->Gesn.HackEventMask)
417
418 dataLength =
419 (Header->EventDataLength[0] << 8) |
420 (Header->EventDataLength[1] & 0xff);
421 dataLength -= 2;
422 requiredLength = 4; // all events are four bytes
423
424 if (dataLength < requiredLength) {
425 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
426 "Classpnp => GESN returned only %x bytes data for fdo %p\n",
427 dataLength, FdoExtension->DeviceObject));
428
429 return STATUS_DEVICE_PROTOCOL_ERROR;
430 }
431 if (dataLength != requiredLength) {
432 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
433 "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
434 dataLength, FdoExtension->DeviceObject));
435 // dataLength = 4;
436 }
437
438 NT_ASSERT(dataLength == 4);
439
440 if ((Header->ClassEventData[0] & 0xf) == 0)
441 {
442 // a zero event is a "no change event, so do not retry
443 return status;
444 }
445
446 // because a event other than "no change" occurred,
447 // we should immediately resend this request.
448 *ResendImmediately = TRUE;
449
450
451 /*
452 ClassSendNotification(FdoExtension,
453 &GUID_IO_GENERIC_GESN_EVENT,
454 sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
455 Header)
456 */
457
458
459
460 switch (Header->NotificationClass) {
461
462 case NOTIFICATION_OPERATIONAL_CHANGE_CLASS_EVENTS: { // 0x01
463
464 PNOTIFICATION_OPERATIONAL_STATUS opChangeInfo =
465 (PNOTIFICATION_OPERATIONAL_STATUS)(Header->ClassEventData);
466 ULONG event;
467
468 if (opChangeInfo->OperationalEvent == NOTIFICATION_OPERATIONAL_EVENT_CHANGE_REQUESTED) {
469 break;
470 }
471
472 event = (opChangeInfo->Operation[0] << 8) |
473 (opChangeInfo->Operation[1] ) ;
474
475 // Workaround some hardware that is buggy but prevalent in the market
476 // This hardware has the property that it will report OpChange events repeatedly,
477 // causing us to retry immediately so quickly that we will eventually disable
478 // GESN to prevent an infinite loop.
479 // (only one valid OpChange event type now, only two ever defined)
480 if (info->MediaChangeRetryCount >= 4) {
481
482 //
483 // HACKHACK - REF #0002
484 // Some drives incorrectly report OpChange/Change (001b/0001h) events
485 // continuously when the tray has been ejected. This causes this routine
486 // to set ResendImmediately to "TRUE", and that results in our cycling
487 // 32 times immediately resending. At that point, we give up detecting
488 // the infinite retry loop, and disable GESN on these drives. This
489 // prevents Media Eject Request (from eject button) from being reported.
490 // Thus, instead we should attempt to workaround this issue by detecting
491 // this behavior.
492 //
493
494 static UCHAR const OpChangeMask = 0x02;
495
496 // At least one device reports "temporarily busy" (which is useless) on eject
497 // At least one device reports "OpChange" repeatedly when re-inserting media
498 // All seem to work well using this workaround
499
500 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN,
501 "Classpnp => GESN OpChange events are broken. Working around this "
502 "problem in software (for fdo %p)\n",
503 FdoExtension->DeviceObject));
504
505
506 // OpChange is not the only bit set -- Media class is required....
507 NT_ASSERT(CountOfSetBitsUChar(info->Gesn.EventMask) != 1);
508
509 //
510 // Force the use of the hackhack (ref #0001) to workaround the
511 // issue noted this hackhack (ref #0002).
512 //
513 SET_FLAG(info->Gesn.NoChangeEventMask, OpChangeMask);
514 CLEAR_FLAG(info->Gesn.EventMask, OpChangeMask);
515 info->Gesn.HackEventMask = TRUE;
516
517 //
518 // don't request the opChange event again. use the method
519 // defined by hackhack (ref #0001) as the workaround.
520 //
521
522 if (info->Gesn.EventMask == 0) {
523 info->Gesn.EventMask = info->Gesn.NoChangeEventMask;
524 info->Gesn.NoChangeEventMask = 0;
525 *ResendImmediately = FALSE;
526 } else {
527 *ResendImmediately = TRUE;
528 }
529
530 break;
531 }
532
533
534 if ((event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_ADDED) |
535 (event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_CHANGE)) {
536
537 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
538 "Classpnp => GESN says features added/changedfor fdo %p\n",
539 FdoExtension->DeviceObject));
540
541 // don't notify that new media arrived, just set the
542 // DO_VERIFY to force a FS reload.
543
544 if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
545 FILE_REMOVABLE_MEDIA) &&
546 (ClassGetVpb(FdoExtension->DeviceObject) != NULL) &&
547 (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED)
548 ) {
549
550 SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
551 }
552
553 //
554 // If there is a class specific error handler, call it with
555 // a "fake" media change error in case it needs to update
556 // internal structures as though a media change occurred.
557 //
558
559 if (FdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
560
561 SCSI_REQUEST_BLOCK srb = {0};
562 UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
563 PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
564 PSCSI_REQUEST_BLOCK srbPtr;
565
566 SENSE_DATA sense = {0};
567 NTSTATUS tempStatus;
568 BOOLEAN retry;
569
570 tempStatus = STATUS_MEDIA_CHANGED;
571 retry = FALSE;
572
573 sense.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT;
574
575 sense.AdditionalSenseLength = sizeof(SENSE_DATA) -
576 RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
577
578 sense.SenseKey = SCSI_SENSE_UNIT_ATTENTION;
579 sense.AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
580
581 //
582 // Send the right type of SRB to the class driver
583 //
584 if ((FdoExtension->CommonExtension.DriverExtension->SrbSupport &
585 CLASS_SRB_STORAGE_REQUEST_BLOCK) != 0) {
586 #ifdef _MSC_VER
587 #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
588 #endif
589 status = InitializeStorageRequestBlock(srbEx,
590 STORAGE_ADDRESS_TYPE_BTL8,
591 CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
592 1,
593 SrbExDataTypeScsiCdb16);
594 if (NT_SUCCESS(status)) {
595 SrbSetCdbLength(srbEx, 6);
596 srbEx->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
597 SrbSetSenseInfoBuffer(srbEx, &sense);
598 SrbSetSenseInfoBufferLength(srbEx, sizeof(sense));
599 srbPtr = (PSCSI_REQUEST_BLOCK)srbEx;
600 } else {
601 // should not happen. Revert to legacy SRB.
602 NT_ASSERT(FALSE);
603 srb.CdbLength = 6;
604 srb.Length = sizeof(SCSI_REQUEST_BLOCK);
605 srb.SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
606 srb.SenseInfoBuffer = &sense;
607 srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
608 srbPtr = &srb;
609 }
610 } else {
611 srb.CdbLength = 6;
612 srb.Length = sizeof(SCSI_REQUEST_BLOCK);
613 srb.SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
614 srb.SenseInfoBuffer = &sense;
615 srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
616 srbPtr = &srb;
617 }
618
619 FdoExtension->CommonExtension.DevInfo->ClassError(FdoExtension->DeviceObject,
620 srbPtr,
621 &tempStatus,
622 &retry);
623
624 } // end class error handler
625
626 }
627 break;
628 }
629
630 case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: { // 0x3
631
632 PNOTIFICATION_EXTERNAL_STATUS externalInfo =
633 (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData);
634 DEVICE_EVENT_EXTERNAL_REQUEST externalData = {0};
635
636 //
637 // unfortunately, due to time constraints, we will only notify
638 // about keys being pressed, and not released. this makes keys
639 // single-function, but simplifies the code significantly.
640 //
641
642 if (externalInfo->ExternalEvent != NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN) {
643 break;
644 }
645
646 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
647 "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n",
648 externalInfo->ExternalEvent, externalInfo->ExternalStatus,
649 (externalInfo->Request[0] << 8) | externalInfo->Request[1]
650 ));
651
652 externalData.Version = 1;
653 externalData.DeviceClass = 0;
654 externalData.ButtonStatus = externalInfo->ExternalEvent;
655 externalData.Request =
656 (externalInfo->Request[0] << 8) |
657 (externalInfo->Request[1] & 0xff);
658 KeQuerySystemTime(&(externalData.SystemTime));
659 externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement();
660
661 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));
662 ClassSendNotification(FdoExtension,
663 &GUID_IO_DEVICE_EXTERNAL_REQUEST,
664 sizeof(DEVICE_EVENT_EXTERNAL_REQUEST),
665 &externalData);
666 return status;
667 }
668
669 case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: { // 0x4
670
671 PNOTIFICATION_MEDIA_STATUS mediaInfo =
672 (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData);
673
674 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
675 "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
676 mediaInfo->MediaEvent, mediaInfo->MediaStatus));
677
678 if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) ||
679 (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) {
680
681
682 if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
683 FILE_REMOVABLE_MEDIA) &&
684 (ClassGetVpb(FdoExtension->DeviceObject) != NULL) &&
685 (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED)
686 ) {
687
688 SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
689
690 }
691 InterlockedIncrement((volatile LONG *)&FdoExtension->MediaChangeCount);
692 ClasspSetMediaChangeStateEx(FdoExtension,
693 MediaPresent,
694 FALSE,
695 TRUE);
696
697 } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) {
698
699 ClasspSetMediaChangeStateEx(FdoExtension,
700 MediaNotPresent,
701 FALSE,
702 TRUE);
703
704 } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) {
705
706 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
707 "Classpnp => GESN Ejection request received!\n"));
708 ClassSendEjectionNotification(FdoExtension);
709
710 }
711 break;
712
713 }
714
715 case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: { // lowest priority events...
716
717 PNOTIFICATION_BUSY_STATUS busyInfo =
718 (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData);
719 DEVICE_EVENT_BECOMING_READY busyData = {0};
720
721 //
722 // NOTE: we never actually need to immediately retry for these
723 // events: if one exists, the device is busy, and if not,
724 // we still don't want to retry.
725 //
726
727 *ResendImmediately = FALSE;
728
729 //
730 // else we want to report the approximated time till it's ready.
731 //
732
733 busyData.Version = 1;
734 busyData.Reason = busyInfo->DeviceBusyStatus;
735 busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) |
736 (busyInfo->Time[1] & 0xff);
737
738 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
739 "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n",
740 busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus,
741 busyData.Estimated100msToReady
742 ));
743
744 //
745 // Ignore the notification if the time is small
746 //
747 if (busyData.Estimated100msToReady < GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS) {
748 break;
749 }
750
751
752 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInterpretGesnData: media BECOMING_READY"));
753 ClassSendNotification(FdoExtension,
754 &GUID_IO_DEVICE_BECOMING_READY,
755 sizeof(DEVICE_EVENT_BECOMING_READY),
756 &busyData);
757 break;
758 }
759
760 default: {
761
762 break;
763
764 }
765
766 } // end switch on notification class
767 return status;
768 }
769
770 /*++////////////////////////////////////////////////////////////////////////////
771
772 ClasspInternalSetMediaChangeState()
773
774 Routine Description:
775
776 This routine will (if appropriate) set the media change event for the
777 device. The event will be set if the media state is changed and
778 media change events are enabled. Otherwise the media state will be
779 tracked but the event will not be set.
780
781 This routine will lock out the other media change routines if possible
782 but if not a media change notification may be lost after the enable has
783 been completed.
784
785 Arguments:
786
787 FdoExtension - the device
788
789 MediaPresent - indicates whether the device has media inserted into it
790 (TRUE) or not (FALSE).
791
792 Return Value:
793
794 none
795
796 --*/
797 VOID
ClasspInternalSetMediaChangeState(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN MEDIA_CHANGE_DETECTION_STATE NewState,IN BOOLEAN KnownStateChange)798 ClasspInternalSetMediaChangeState(
799 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
800 IN MEDIA_CHANGE_DETECTION_STATE NewState,
801 IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
802 )
803 {
804 #if DBG
805 PCSZ states[] = {"Unknown", "Present", "Not Present", "Unavailable"};
806 #endif
807 MEDIA_CHANGE_DETECTION_STATE oldMediaState;
808 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
809 CLASS_MEDIA_CHANGE_CONTEXT mcnContext;
810 PIO_WORKITEM workItem;
811
812 if (!((NewState >= MediaUnknown) && (NewState <= MediaUnavailable))) {
813 return;
814 }
815
816 if(info == NULL) {
817 return;
818 }
819
820 oldMediaState = InterlockedExchange(
821 (PLONG)(&info->MediaChangeDetectionState),
822 (LONG)NewState);
823
824 if((oldMediaState == MediaUnknown) && (!KnownStateChange)) {
825
826 //
827 // The media was in an indeterminate state before - don't notify for
828 // this change.
829 //
830
831 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
832 "ClassSetMediaChangeState: State was unknown - this may "
833 "not be a change\n"));
834 return;
835
836 } else if(oldMediaState == NewState) {
837
838 //
839 // Media is in the same state it was before.
840 //
841
842 return;
843 }
844
845 //
846 // Inform PartMgr that the media changed. It will need to propagate
847 // DO_VERIFY_VOLUME to each partition. Ensure that only one work item
848 // updates the disk's properties at any given time.
849 //
850 if (InterlockedCompareExchange((volatile LONG *)&FdoExtension->PrivateFdoData->UpdateDiskPropertiesWorkItemActive, 1, 0) == 0) {
851
852 workItem = IoAllocateWorkItem(FdoExtension->DeviceObject);
853
854 if (workItem) {
855
856 IoQueueWorkItem(workItem, ClasspUpdateDiskProperties, DelayedWorkQueue, workItem);
857
858 } else {
859
860 InterlockedExchange((volatile LONG *)&FdoExtension->PrivateFdoData->UpdateDiskPropertiesWorkItemActive, 0);
861 }
862 }
863
864 if(info->MediaChangeDetectionDisableCount != 0) {
865 #if DBG
866 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
867 "ClassSetMediaChangeState: MCN not enabled, state "
868 "changed from %s to %s\n",
869 states[oldMediaState], states[NewState]));
870 #endif
871 return;
872
873 }
874 #if DBG
875 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
876 "ClassSetMediaChangeState: State change from %s to %s\n",
877 states[oldMediaState], states[NewState]));
878 #endif
879
880 //
881 // make the data useful -- it used to always be zero.
882 //
883 mcnContext.MediaChangeCount = FdoExtension->MediaChangeCount;
884 mcnContext.NewState = NewState;
885
886 if (NewState == MediaPresent) {
887
888 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInternalSetMediaChangeState: media ARRIVAL"));
889 ClassSendNotification(FdoExtension,
890 &GUID_IO_MEDIA_ARRIVAL,
891 sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
892 &mcnContext);
893
894 }
895 else if ((NewState == MediaNotPresent) || (NewState == MediaUnavailable)) {
896
897 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInternalSetMediaChangeState: media REMOVAL"));
898 ClassSendNotification(FdoExtension,
899 &GUID_IO_MEDIA_REMOVAL,
900 sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
901 &mcnContext);
902
903 } else {
904
905 //
906 // Don't notify of changed going to unknown.
907 //
908
909 return;
910 }
911
912 return;
913 } // end ClasspInternalSetMediaChangeState()
914
915 /*++////////////////////////////////////////////////////////////////////////////
916
917 ClassSetMediaChangeState()
918
919 Routine Description:
920
921 This routine will (if appropriate) set the media change event for the
922 device. The event will be set if the media state is changed and
923 media change events are enabled. Otherwise the media state will be
924 tracked but the event will not be set.
925
926 This routine will lock out the other media change routines if possible
927 but if not a media change notification may be lost after the enable has
928 been completed.
929
930 Arguments:
931
932 FdoExtension - the device
933
934 MediaPresent - indicates whether the device has media inserted into it
935 (TRUE) or not (FALSE).
936
937 Wait - indicates whether the function should wait until it can acquire
938 the synchronization lock or not.
939
940 Return Value:
941
942 none
943
944 --*/
945
946 VOID
947 #ifdef _MSC_VER
948 #pragma prefast(suppress:26165, "The mutex won't be acquired in the case of a timeout.")
949 #endif
ClasspSetMediaChangeStateEx(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN MEDIA_CHANGE_DETECTION_STATE NewState,IN BOOLEAN Wait,IN BOOLEAN KnownStateChange)950 ClasspSetMediaChangeStateEx(
951 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
952 IN MEDIA_CHANGE_DETECTION_STATE NewState,
953 IN BOOLEAN Wait,
954 IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
955 )
956 {
957 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
958 LARGE_INTEGER zero;
959 NTSTATUS status;
960
961 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "> ClasspSetMediaChangeStateEx"));
962
963 //
964 // Reset SMART status on media removal as the old status may not be
965 // valid when there is no media in the device or when new media is
966 // inserted.
967 //
968
969 if (NewState == MediaNotPresent) {
970
971 FdoExtension->FailurePredicted = FALSE;
972 FdoExtension->FailureReason = 0;
973
974 }
975
976
977 zero.QuadPart = 0;
978
979 if(info == NULL) {
980 return;
981 }
982
983 status = KeWaitForMutexObject(&info->MediaChangeMutex,
984 Executive,
985 KernelMode,
986 FALSE,
987 ((Wait == TRUE) ? NULL : &zero));
988
989 if(status == STATUS_TIMEOUT) {
990
991 //
992 // Someone else is in the process of setting the media state
993 //
994
995 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
996 return;
997 }
998
999 //
1000 // Change the media present state and signal an event, if applicable
1001 //
1002
1003 ClasspInternalSetMediaChangeState(FdoExtension, NewState, KnownStateChange);
1004
1005 KeReleaseMutex(&info->MediaChangeMutex, FALSE);
1006
1007 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "< ClasspSetMediaChangeStateEx"));
1008
1009 return;
1010 } // end ClassSetMediaChangeStateEx()
1011
_IRQL_requires_max_(PASSIVE_LEVEL)1012 _IRQL_requires_max_(PASSIVE_LEVEL)
1013 VOID
1014 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
1015 ClassSetMediaChangeState(
1016 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1017 _In_ MEDIA_CHANGE_DETECTION_STATE NewState,
1018 _In_ BOOLEAN Wait
1019 )
1020 {
1021 ClasspSetMediaChangeStateEx(FdoExtension, NewState, Wait, FALSE);
1022 return;
1023 }
1024
1025 /*++////////////////////////////////////////////////////////////////////////////
1026
1027 ClasspMediaChangeDetectionCompletion()
1028
1029 Routine Description:
1030
1031 This routine handles the completion of the test unit ready irps used to
1032 determine if the media has changed. If the media has changed, this code
1033 signals the named event to wake up other system services that react to
1034 media change (aka AutoPlay).
1035
1036 Arguments:
1037
1038 DeviceObject - the object for the completion
1039 Irp - the IRP being completed
1040 Context - the SRB from the IRP
1041
1042 Return Value:
1043
1044 NTSTATUS
1045
1046 --*/
1047 NTSTATUS
1048 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClasspMediaChangeDetectionCompletion(PDEVICE_OBJECT DeviceObject,PIRP Irp,PVOID Context)1049 ClasspMediaChangeDetectionCompletion(
1050 PDEVICE_OBJECT DeviceObject,
1051 PIRP Irp,
1052 PVOID Context
1053 )
1054 {
1055 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1056 PCLASS_PRIVATE_FDO_DATA fdoData;
1057 PMEDIA_CHANGE_DETECTION_INFO info;
1058 NTSTATUS status;
1059 BOOLEAN retryImmediately = FALSE;
1060 PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER) Context;
1061
1062 _Analysis_assume_(Srb != NULL);
1063
1064 //
1065 // Since the class driver created this request, it's completion routine
1066 // will not get a valid device object handed in. Use the one in the
1067 // irp stack instead
1068 //
1069
1070 DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
1071 fdoExtension = DeviceObject->DeviceExtension;
1072 fdoData = fdoExtension->PrivateFdoData;
1073 info = fdoExtension->MediaChangeDetectionInfo;
1074
1075 NT_ASSERT(info->MediaChangeIrp != NULL);
1076 NT_ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
1077 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject, Irp));
1078
1079 /*
1080 * HACK for IoMega 2GB Jaz drive:
1081 * This drive spins down on its own to preserve the media.
1082 * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
1083 * InterpretSenseInfo routine would then call ClassSendStartUnit to spin the media up, which defeats the
1084 * purpose of the spindown.
1085 * So in this case, make this into a successful TUR.
1086 * This allows the drive to stay spun down until it is actually accessed again.
1087 * (If the media were actually removed, TUR would fail with 2/3a/0 ).
1088 * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
1089 * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
1090 */
1091
1092 if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) &&
1093 TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
1094
1095 PVOID senseData = SrbGetSenseInfoBuffer(Srb);
1096
1097 if (senseData) {
1098
1099 BOOLEAN validSense = TRUE;
1100 UCHAR senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
1101 UCHAR senseKey = 0;
1102 UCHAR additionalSenseCode = 0;
1103
1104 validSense = ScsiGetSenseKeyAndCodes(senseData,
1105 senseInfoBufferLength,
1106 SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
1107 &senseKey,
1108 &additionalSenseCode,
1109 NULL);
1110
1111 if (validSense &&
1112 senseKey == SCSI_SENSE_NOT_READY &&
1113 additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) {
1114 Srb->SrbStatus = SRB_STATUS_SUCCESS;
1115 }
1116 }
1117 }
1118
1119 //
1120 // use InterpretSenseInfo routine to check for media state, and also
1121 // to call ClassError() with correct parameters.
1122 //
1123 status = STATUS_SUCCESS;
1124 if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
1125
1126 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.",
1127 DBGGETSRBSTATUSSTR(Srb), DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));
1128
1129 InterpretSenseInfoWithoutHistory(DeviceObject,
1130 Irp,
1131 (PSCSI_REQUEST_BLOCK)Srb,
1132 IRP_MJ_SCSI,
1133 0,
1134 0,
1135 &status,
1136 NULL);
1137 }
1138 else {
1139
1140 fdoData->LoggedTURFailureSinceLastIO = FALSE;
1141
1142 if (!info->Gesn.Supported) {
1143
1144 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));
1145
1146 //
1147 // success != media for GESN case
1148 //
1149
1150 ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
1151
1152 }
1153 else {
1154 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
1155 }
1156 }
1157
1158 if (info->Gesn.Supported) {
1159
1160 if (status == STATUS_DATA_OVERRUN) {
1161 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - Overrun"));
1162 status = STATUS_SUCCESS;
1163 }
1164
1165 if (!NT_SUCCESS(status)) {
1166 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status));
1167 } else {
1168
1169 //
1170 // for GESN, need to interpret the results of the data.
1171 // this may also require an immediate retry
1172 //
1173
1174 if (Irp->IoStatus.Information == 8 ) {
1175 ClasspInterpretGesnData(fdoExtension,
1176 (PVOID)info->Gesn.Buffer,
1177 &retryImmediately);
1178 }
1179
1180 } // end of NT_SUCCESS(status)
1181
1182 } // end of Info->Gesn.Supported
1183
1184 //
1185 // free port-allocated sense buffer, if any.
1186 //
1187
1188 if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
1189 FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
1190 }
1191
1192 //
1193 // Remember the IRP and SRB for use the next time.
1194 //
1195
1196 NT_ASSERT(IoGetNextIrpStackLocation(Irp));
1197 IoGetNextIrpStackLocation(Irp)->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;
1198
1199 //
1200 // Reset the MCN timer.
1201 //
1202
1203 ClassResetMediaChangeTimer(fdoExtension);
1204
1205 //
1206 // run a sanity check to make sure we're not recursing continuously
1207 //
1208
1209 if (retryImmediately) {
1210
1211 info->MediaChangeRetryCount++;
1212
1213 if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES) {
1214
1215 //
1216 // Disable GESN on this device.
1217 // Create a work item to set the value in the registry
1218 //
1219
1220 PIO_WORKITEM workItem;
1221
1222 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion: Disabling GESN for device %p", DeviceObject));
1223
1224 workItem = IoAllocateWorkItem(DeviceObject);
1225
1226 if (workItem) {
1227 IoQueueWorkItem(workItem, ClasspDisableGesn, DelayedWorkQueue, workItem);
1228 }
1229
1230 info->Gesn.Supported = 0;
1231 info->Gesn.EventMask = 0;
1232 info->Gesn.BufferSize = 0;
1233 info->MediaChangeRetryCount = 0;
1234 retryImmediately = FALSE;
1235 }
1236
1237 } else {
1238
1239 info->MediaChangeRetryCount = 0;
1240
1241 }
1242
1243
1244 //
1245 // release the remove lock....
1246 //
1247
1248 {
1249 UCHAR uniqueValue = 0;
1250 ClassAcquireRemoveLock(DeviceObject, (PVOID)(&uniqueValue));
1251 ClassReleaseRemoveLock(DeviceObject, Irp);
1252
1253
1254 //
1255 // set the irp as not in use
1256 //
1257 {
1258 #if DBG
1259 volatile LONG irpWasInUse;
1260 irpWasInUse = InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1);
1261 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
1262 NT_ASSERT(irpWasInUse);
1263 #endif
1264 #else
1265 InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1);
1266 #endif
1267 }
1268
1269 //
1270 // now send it again before we release our last remove lock
1271 //
1272
1273 if (retryImmediately) {
1274 ClasspSendMediaStateIrp(fdoExtension, info, 0);
1275 }
1276 else {
1277 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - not retrying immediately"));
1278 }
1279
1280 //
1281 // release the temporary remove lock
1282 //
1283
1284 ClassReleaseRemoveLock(DeviceObject, (PVOID)(&uniqueValue));
1285 }
1286
1287 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspMediaChangeDetectionCompletion"));
1288
1289 return STATUS_MORE_PROCESSING_REQUIRED;
1290 }
1291
1292 /*++////////////////////////////////////////////////////////////////////////////
1293
1294 ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented
1295
1296 Routine Description:
1297
1298 This routine
1299
1300 Arguments:
1301
1302 DeviceObject -
1303 Irp -
1304
1305 Return Value:
1306
1307
1308 --*/
1309 PIRP
ClasspPrepareMcnIrp(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN PMEDIA_CHANGE_DETECTION_INFO Info,IN BOOLEAN UseGesn)1310 ClasspPrepareMcnIrp(
1311 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1312 IN PMEDIA_CHANGE_DETECTION_INFO Info,
1313 IN BOOLEAN UseGesn
1314 )
1315 {
1316 PSCSI_REQUEST_BLOCK srb;
1317 PSTORAGE_REQUEST_BLOCK srbEx;
1318 PIO_STACK_LOCATION irpStack;
1319 PIO_STACK_LOCATION nextIrpStack;
1320 NTSTATUS status;
1321 PCDB cdb;
1322 PIRP irp;
1323 PVOID buffer;
1324 UCHAR bufferLength;
1325 ULONG srbFlags;
1326 ULONG timeOutValue;
1327 UCHAR cdbLength;
1328 PVOID dataBuffer;
1329 ULONG dataTransferLength;
1330
1331 //
1332 // Setup the IRP to perform a test unit ready.
1333 //
1334
1335 irp = Info->MediaChangeIrp;
1336
1337 if (irp == NULL) {
1338 NT_ASSERT(irp);
1339 return NULL;
1340 }
1341
1342 //
1343 // don't keep sending this if the device is being removed.
1344 //
1345
1346 status = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp);
1347 if (status == REMOVE_COMPLETE) {
1348 NT_ASSERT(status != REMOVE_COMPLETE);
1349 return NULL;
1350 }
1351 else if (status == REMOVE_PENDING) {
1352 ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
1353 return NULL;
1354 }
1355 else {
1356 NT_ASSERT(status == NO_REMOVE);
1357 }
1358
1359 IoReuseIrp(irp, STATUS_NOT_SUPPORTED);
1360
1361 /*
1362 * For the driver that creates an IRP, there is no 'current' stack location.
1363 * Step down one IRP stack location so that the extra top one
1364 * becomes our 'current' one.
1365 */
1366 IoSetNextIrpStackLocation(irp);
1367
1368 /*
1369 * Cache our device object in the extra top IRP stack location
1370 * so we have it in our completion routine.
1371 */
1372 irpStack = IoGetCurrentIrpStackLocation(irp);
1373 irpStack->DeviceObject = FdoExtension->DeviceObject;
1374
1375 //
1376 // If the irp is sent down when the volume needs to be
1377 // verified, CdRomUpdateGeometryCompletion won't complete
1378 // it since it's not associated with a thread. Marking
1379 // it to override the verify causes it always be sent
1380 // to the port driver
1381 //
1382
1383 irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
1384
1385 nextIrpStack = IoGetNextIrpStackLocation(irp);
1386 nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
1387 nextIrpStack->Parameters.Scsi.Srb = &(Info->MediaChangeSrb.Srb);
1388
1389 //
1390 // Prepare the SRB for execution.
1391 //
1392
1393 buffer = Info->SenseBuffer;
1394 bufferLength = Info->SenseBufferLength;
1395
1396 NT_ASSERT(bufferLength > 0);
1397 RtlZeroMemory(buffer, bufferLength);
1398
1399 srbFlags = FdoExtension->SrbFlags;
1400 SET_FLAG(srbFlags, Info->SrbFlags);
1401
1402 timeOutValue = FdoExtension->TimeOutValue * 2;
1403 if (timeOutValue == 0) {
1404
1405 if (FdoExtension->TimeOutValue == 0) {
1406
1407 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
1408 "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
1409 "is set to zero?! -- resetting to 10\n"));
1410 timeOutValue = 10 * 2; // reasonable default
1411
1412 } else {
1413
1414 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
1415 "ClassSendTestUnitIrp: Someone set "
1416 "srb->TimeOutValue to zero?! -- resetting to %x\n",
1417 FdoExtension->TimeOutValue * 2));
1418 timeOutValue = FdoExtension->TimeOutValue * 2;
1419
1420 }
1421
1422 }
1423
1424 if (!UseGesn) {
1425 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
1426 irp->MdlAddress = NULL;
1427
1428 SET_FLAG(srbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
1429
1430 //
1431 // Set SRB_FLAGS_NO_KEEP_AWAKE for non-cdrom devices if these requests should
1432 // not prevent devices from going to sleep.
1433 //
1434 if ((FdoExtension->DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) &&
1435 (ClasspScreenOff == TRUE)) {
1436 SET_FLAG(srbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
1437 }
1438
1439 cdbLength = 6;
1440 dataBuffer = NULL;
1441 dataTransferLength = 0;
1442
1443 } else {
1444 NT_ASSERT(Info->Gesn.Buffer);
1445
1446 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
1447 irp->MdlAddress = Info->Gesn.Mdl;
1448
1449 SET_FLAG(srbFlags, SRB_FLAGS_DATA_IN);
1450 cdbLength = 10;
1451 dataBuffer = Info->Gesn.Buffer;
1452 dataTransferLength = Info->Gesn.BufferSize;
1453 timeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN
1454
1455 }
1456
1457 //
1458 // SRB used here is the MediaChangeSrb in _MEDIA_CHANGE_DETECTION_INFO.
1459 //
1460 srb = nextIrpStack->Parameters.Scsi.Srb;
1461 if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1462 srbEx = (PSTORAGE_REQUEST_BLOCK)nextIrpStack->Parameters.Scsi.Srb;
1463
1464 status = InitializeStorageRequestBlock(srbEx,
1465 STORAGE_ADDRESS_TYPE_BTL8,
1466 CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
1467 1,
1468 SrbExDataTypeScsiCdb16);
1469 if (!NT_SUCCESS(status)) {
1470 // should not happen
1471 NT_ASSERT(FALSE);
1472 return NULL;
1473 }
1474
1475 srbEx->RequestTag = SP_UNTAGGED;
1476 srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
1477 srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
1478 srbEx->SrbStatus = 0;
1479 srbEx->OriginalRequest = irp;
1480 srbEx->SrbFlags = srbFlags;
1481 srbEx->TimeOutValue = timeOutValue;
1482 srbEx->DataBuffer = dataBuffer;
1483 srbEx->DataTransferLength = dataTransferLength;
1484
1485 SrbSetScsiStatus(srbEx, 0);
1486 SrbSetSenseInfoBuffer(srbEx, buffer);
1487 SrbSetSenseInfoBufferLength(srbEx, bufferLength);
1488 SrbSetCdbLength(srbEx, cdbLength);
1489
1490 cdb = SrbGetCdb(srbEx);
1491
1492 } else {
1493 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
1494
1495 srb->QueueTag = SP_UNTAGGED;
1496 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
1497 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
1498 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
1499 srb->SenseInfoBuffer = buffer;
1500 srb->SenseInfoBufferLength = bufferLength;
1501 srb->SrbStatus = 0;
1502 srb->ScsiStatus = 0;
1503 srb->OriginalRequest = irp;
1504
1505 srb->SrbFlags = srbFlags;
1506 srb->TimeOutValue = timeOutValue;
1507 srb->CdbLength = cdbLength;
1508 srb->DataBuffer = dataBuffer;
1509 srb->DataTransferLength = dataTransferLength;
1510
1511 cdb = (PCDB) &srb->Cdb[0];
1512
1513 }
1514
1515 if (cdb) {
1516 if (!UseGesn) {
1517 cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
1518 } else {
1519 cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode =
1520 SCSIOP_GET_EVENT_STATUS;
1521 cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1;
1522 cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] =
1523 (UCHAR)((Info->Gesn.BufferSize) >> 8);
1524 cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] =
1525 (UCHAR)((Info->Gesn.BufferSize) & 0xff);
1526 cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest =
1527 Info->Gesn.EventMask;
1528 }
1529 }
1530
1531 IoSetCompletionRoutine(irp,
1532 ClasspMediaChangeDetectionCompletion,
1533 srb,
1534 TRUE,
1535 TRUE,
1536 TRUE);
1537
1538 return irp;
1539
1540 }
1541
1542 /*++////////////////////////////////////////////////////////////////////////////
1543
1544 ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented
1545
1546 Routine Description:
1547
1548 This routine
1549
1550 Arguments:
1551
1552 DeviceObject -
1553 Irp -
1554
1555 Return Value:
1556
1557 --*/
1558 VOID
ClasspSendMediaStateIrp(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN PMEDIA_CHANGE_DETECTION_INFO Info,IN ULONG CountDown)1559 ClasspSendMediaStateIrp(
1560 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1561 IN PMEDIA_CHANGE_DETECTION_INFO Info,
1562 IN ULONG CountDown
1563 )
1564 {
1565 BOOLEAN requestPending = FALSE;
1566 LONG irpInUse;
1567
1568 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "> ClasspSendMediaStateIrp"));
1569
1570 if (((FdoExtension->CommonExtension.CurrentState != IRP_MN_START_DEVICE) ||
1571 (FdoExtension->DevicePowerState != PowerDeviceD0)
1572 ) &&
1573 (!Info->MediaChangeIrpLost)) {
1574
1575 //
1576 // the device may be stopped, powered down, or otherwise queueing io,
1577 // so should not timeout the autorun irp (yet) -- set to zero ticks.
1578 // scattered code relies upon this to not prematurely "lose" an
1579 // autoplay irp that was queued.
1580 //
1581
1582 Info->MediaChangeIrpTimeInUse = 0;
1583 }
1584
1585 //
1586 // if the irp is not in use, mark it as such.
1587 //
1588
1589 irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 1, 0);
1590
1591 if (irpInUse) {
1592
1593 LONG timeInUse;
1594
1595 timeInUse = InterlockedIncrement(&Info->MediaChangeIrpTimeInUse);
1596
1597 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "ClasspSendMediaStateIrp: irp in use for "
1598 "%x seconds when synchronizing for MCD\n", timeInUse));
1599
1600 if (Info->MediaChangeIrpLost == FALSE) {
1601
1602 if (timeInUse > MEDIA_CHANGE_TIMEOUT_TIME) {
1603
1604 //
1605 // currently set to five minutes. hard to imagine a drive
1606 // taking that long to spin up.
1607 //
1608
1609 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
1610 "CdRom%d: Media Change Notification has lost "
1611 "it's irp and doesn't know where to find it. "
1612 "Leave it alone and it'll come home dragging "
1613 "it's stack behind it.\n",
1614 FdoExtension->DeviceNumber));
1615 Info->MediaChangeIrpLost = TRUE;
1616 }
1617 }
1618
1619 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspSendMediaStateIrp - irpInUse"));
1620 return;
1621
1622 }
1623
1624 TRY {
1625
1626 if (Info->MediaChangeDetectionDisableCount != 0) {
1627 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckMediaState: device %p has "
1628 " detection disabled \n", FdoExtension->DeviceObject));
1629 LEAVE;
1630 }
1631
1632 if (FdoExtension->DevicePowerState != PowerDeviceD0) {
1633
1634 //
1635 // It's possible that the device went to D3 while the screen was
1636 // off so we need to make sure that we send the IRP regardless
1637 // of the device's power state in order to wake the device back
1638 // up when the screen comes back on.
1639 // When the screen is off we set the SRB_FLAG_NO_KEEP_AWAKE flag
1640 // so that the lower driver does not power-up the device for this
1641 // request. When the screen comes back on, however, we want to
1642 // resume checking for media presence so we no longer set the flag.
1643 // When the device is in D3 we also stop the polling timer as well.
1644 //
1645
1646 //
1647 // NOTE: we don't increment the time in use until our power state
1648 // changes above. this way, we won't "lose" the autoplay irp.
1649 // it's up to the lower driver to determine if powering up is a
1650 // good idea.
1651 //
1652
1653 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1654 "ClassCheckMediaState: device %p needs to powerup "
1655 "to handle this io (may take a few extra seconds).\n",
1656 FdoExtension->DeviceObject));
1657 }
1658
1659 Info->MediaChangeIrpTimeInUse = 0;
1660 Info->MediaChangeIrpLost = FALSE;
1661
1662 if (CountDown == 0) {
1663
1664 PIRP irp;
1665
1666 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1667 "ClassCheckMediaState: timer expired\n"));
1668
1669 if (Info->MediaChangeDetectionDisableCount != 0) {
1670 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1671 "ClassCheckMediaState: detection disabled\n"));
1672 LEAVE;
1673 }
1674
1675 //
1676 // Prepare the IRP for the test unit ready
1677 //
1678
1679 irp = ClasspPrepareMcnIrp(FdoExtension,
1680 Info,
1681 Info->Gesn.Supported);
1682
1683 //
1684 // Issue the request.
1685 //
1686
1687 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
1688 "ClasspSendMediaStateIrp: Device %p getting TUR "
1689 " irp %p\n", FdoExtension->DeviceObject, irp));
1690
1691 if (irp == NULL) {
1692 LEAVE;
1693 }
1694
1695
1696 //
1697 // note: if we send it to the class dispatch routines, there is
1698 // a timing window here (since they grab the remove lock)
1699 // where we'd be removed. ELIMINATE the window by grabbing
1700 // the lock ourselves above and sending it to the lower
1701 // device object directly or to the device's StartIo
1702 // routine (which doesn't acquire the lock).
1703 //
1704
1705 requestPending = TRUE;
1706
1707 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, " ClasspSendMediaStateIrp - calling IoCallDriver."));
1708 IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
1709 }
1710
1711 } FINALLY {
1712
1713 if(requestPending == FALSE) {
1714 #if DBG
1715 irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1);
1716 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
1717 NT_ASSERT(irpInUse);
1718 #endif
1719 #else
1720 InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1);
1721 #endif
1722 }
1723
1724 }
1725
1726 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspSendMediaStateIrp"));
1727
1728 return;
1729 } // end ClasspSendMediaStateIrp()
1730
1731 /*++////////////////////////////////////////////////////////////////////////////
1732
1733 ClassCheckMediaState()
1734
1735 Routine Description:
1736
1737 This routine is called by the class driver to test for a media change
1738 condition and/or poll for disk failure prediction. It should be called
1739 from the class driver's IO timer routine once per second.
1740
1741 Arguments:
1742
1743 FdoExtension - the device extension
1744
1745 Return Value:
1746
1747 none
1748
1749 --*/
1750 VOID
1751 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassCheckMediaState(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)1752 ClassCheckMediaState(
1753 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1754 )
1755 {
1756 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
1757 LONG countDown;
1758
1759 if(info == NULL) {
1760 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1761 "ClassCheckMediaState: detection not enabled\n"));
1762 return;
1763 }
1764
1765 //
1766 // Media change support is active and the IRP is waiting. Decrement the
1767 // timer. There is no MP protection on the timer counter. This code
1768 // is the only code that will manipulate the timer counter and only one
1769 // instance of it should be running at any given time.
1770 //
1771
1772 countDown = InterlockedDecrement(&(info->MediaChangeCountDown));
1773
1774 //
1775 // Try to acquire the media change event. If we can't do it immediately
1776 // then bail out and assume the caller will try again later.
1777 //
1778 ClasspSendMediaStateIrp(FdoExtension,
1779 info,
1780 countDown);
1781
1782 return;
1783 } // end ClassCheckMediaState()
1784
1785 /*++////////////////////////////////////////////////////////////////////////////
1786
1787 ClassResetMediaChangeTimer()
1788
1789 Routine Description:
1790
1791 Resets the media change count down timer to the default number of seconds.
1792
1793 Arguments:
1794
1795 FdoExtension - the device to reset the timer for
1796
1797 Return Value:
1798
1799 None
1800
1801 --*/
1802 VOID
1803 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassResetMediaChangeTimer(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)1804 ClassResetMediaChangeTimer(
1805 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1806 )
1807 {
1808 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
1809
1810 if(info != NULL) {
1811 InterlockedExchange(&(info->MediaChangeCountDown),
1812 MEDIA_CHANGE_DEFAULT_TIME);
1813 }
1814 return;
1815 } // end ClassResetMediaChangeTimer()
1816
1817 /*++////////////////////////////////////////////////////////////////////////////
1818
1819 ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented
1820
1821 Routine Description:
1822
1823 This routine
1824
1825 Arguments:
1826
1827 DeviceObject -
1828 Irp -
1829
1830 Return Value:
1831
1832 --*/
1833 NTSTATUS
ClasspInitializePolling(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN BOOLEAN AllowDriveToSleep)1834 ClasspInitializePolling(
1835 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1836 IN BOOLEAN AllowDriveToSleep
1837 )
1838 {
1839 PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
1840
1841 PMEDIA_CHANGE_DETECTION_INFO info;
1842 PIRP irp;
1843
1844 PAGED_CODE();
1845
1846 if (FdoExtension->MediaChangeDetectionInfo != NULL) {
1847 return STATUS_SUCCESS;
1848 }
1849
1850 info = ExAllocatePoolWithTag(NonPagedPoolNx,
1851 sizeof(MEDIA_CHANGE_DETECTION_INFO),
1852 CLASS_TAG_MEDIA_CHANGE_DETECTION);
1853
1854 if (info != NULL) {
1855 RtlZeroMemory(info, sizeof(MEDIA_CHANGE_DETECTION_INFO));
1856
1857 FdoExtension->KernelModeMcnContext.FileObject = (PVOID)-1;
1858 FdoExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1;
1859 FdoExtension->KernelModeMcnContext.LockCount = 0;
1860 FdoExtension->KernelModeMcnContext.McnDisableCount = 0;
1861
1862 /*
1863 * Allocate an IRP to carry the Test-Unit-Ready.
1864 * Allocate an extra IRP stack location
1865 * so we can cache our device object in the top location.
1866 */
1867 irp = IoAllocateIrp((CCHAR)(fdo->StackSize+1), FALSE);
1868
1869 if (irp != NULL) {
1870
1871 PVOID buffer;
1872 BOOLEAN GesnSupported = FALSE;
1873
1874 buffer = ExAllocatePoolWithTag(
1875 NonPagedPoolNxCacheAligned,
1876 SENSE_BUFFER_SIZE_EX,
1877 CLASS_TAG_MEDIA_CHANGE_DETECTION);
1878
1879 if (buffer != NULL) {
1880
1881 info->MediaChangeIrp = irp;
1882 info->SenseBuffer = buffer;
1883 info->SenseBufferLength = SENSE_BUFFER_SIZE_EX;
1884
1885 //
1886 // Set default values for the media change notification
1887 // configuration.
1888 //
1889
1890 info->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
1891 info->MediaChangeDetectionDisableCount = 0;
1892
1893 //
1894 // Assume that there is initially no media in the device
1895 // only notify upper layers if there is something there
1896 //
1897
1898 info->MediaChangeDetectionState = MediaUnknown;
1899
1900 info->MediaChangeIrpTimeInUse = 0;
1901 info->MediaChangeIrpLost = FALSE;
1902
1903 //
1904 // setup all extra flags we'll be setting for this irp
1905 //
1906 info->SrbFlags = 0;
1907 if (AllowDriveToSleep) {
1908 SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
1909 }
1910 SET_FLAG(info->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
1911 SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
1912 SET_FLAG(info->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
1913
1914 KeInitializeMutex(&info->MediaChangeMutex, 0x100);
1915
1916 //
1917 // It is ok to support media change events on this
1918 // device.
1919 //
1920
1921 FdoExtension->MediaChangeDetectionInfo = info;
1922
1923 //
1924 // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
1925 // when the device supports DVD (no need to
1926 // check for FILE_DEVICE_DVD, as it's not a
1927 // valid check).
1928 //
1929
1930 if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) {
1931
1932 NTSTATUS status;
1933
1934 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1935 "ClasspInitializePolling: Testing for GESN\n"));
1936 status = ClasspInitializeGesn(FdoExtension, info);
1937 if (NT_SUCCESS(status)) {
1938 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1939 "ClasspInitializePolling: GESN available "
1940 "for %p\n", FdoExtension->DeviceObject));
1941 NT_ASSERT(info->Gesn.Supported );
1942 NT_ASSERT(info->Gesn.Buffer != NULL);
1943 NT_ASSERT(info->Gesn.BufferSize != 0);
1944 NT_ASSERT(info->Gesn.EventMask != 0);
1945 GesnSupported = TRUE;
1946 } else {
1947 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
1948 "ClasspInitializePolling: GESN *NOT* available "
1949 "for %p\n", FdoExtension->DeviceObject));
1950 }
1951 }
1952
1953 if (GesnSupported == FALSE) {
1954 NT_ASSERT(info->Gesn.Supported == 0);
1955 NT_ASSERT(info->Gesn.Buffer == NULL);
1956 NT_ASSERT(info->Gesn.BufferSize == 0);
1957 NT_ASSERT(info->Gesn.EventMask == 0);
1958 info->Gesn.Supported = 0; // just in case....
1959 }
1960
1961 //
1962 // Register for screen state notification. Will use this to
1963 // determine user presence.
1964 //
1965 if (ScreenStateNotificationHandle == NULL) {
1966 PoRegisterPowerSettingCallback(fdo,
1967 &GUID_CONSOLE_DISPLAY_STATE,
1968 &ClasspPowerSettingCallback,
1969 NULL,
1970 &ScreenStateNotificationHandle);
1971 }
1972
1973 return STATUS_SUCCESS;
1974 }
1975
1976 IoFreeIrp(irp);
1977 }
1978
1979 FREE_POOL(info);
1980 }
1981
1982 //
1983 // nothing to free here
1984 //
1985 return STATUS_INSUFFICIENT_RESOURCES;
1986
1987 } // end ClasspInitializePolling()
1988
1989 NTSTATUS
ClasspInitializeGesn(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN PMEDIA_CHANGE_DETECTION_INFO Info)1990 ClasspInitializeGesn(
1991 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1992 IN PMEDIA_CHANGE_DETECTION_INFO Info
1993 )
1994 {
1995 PNOTIFICATION_EVENT_STATUS_HEADER header;
1996 CLASS_DETECTION_STATE detectionState = ClassDetectionUnknown;
1997 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
1998 NTSTATUS status = STATUS_NOT_SUPPORTED;
1999 PIRP irp;
2000 KEVENT event;
2001 BOOLEAN retryImmediately;
2002 ULONG i;
2003 ULONG atapiResets;
2004 ULONG srbFlags;
2005
2006 PAGED_CODE();
2007 NT_ASSERT(Info == FdoExtension->MediaChangeDetectionInfo);
2008
2009 //
2010 // read if we already know the abilities of the device
2011 //
2012
2013 ClassGetDeviceParameter(FdoExtension,
2014 CLASSP_REG_SUBKEY_NAME,
2015 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
2016 (PULONG)&detectionState);
2017
2018 if (detectionState == ClassDetectionUnsupported) {
2019 goto ExitWithError;
2020 }
2021
2022 //
2023 // check if the device has a hack flag saying never to try this.
2024 //
2025
2026 if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
2027 FDO_HACK_GESN_IS_BAD)) {
2028
2029 ClassSetDeviceParameter(FdoExtension,
2030 CLASSP_REG_SUBKEY_NAME,
2031 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
2032 ClassDetectionUnsupported);
2033 goto ExitWithError;
2034
2035 }
2036
2037
2038 //
2039 // else go through the process since we allocate buffers and
2040 // get all sorts of device settings.
2041 //
2042
2043 if (Info->Gesn.Buffer == NULL) {
2044 Info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
2045 GESN_BUFFER_SIZE,
2046 '??cS');
2047 }
2048 if (Info->Gesn.Buffer == NULL) {
2049 status = STATUS_INSUFFICIENT_RESOURCES;
2050 goto ExitWithError;
2051 }
2052 if (Info->Gesn.Mdl != NULL) {
2053 IoFreeMdl(Info->Gesn.Mdl);
2054 }
2055 Info->Gesn.Mdl = IoAllocateMdl(Info->Gesn.Buffer,
2056 GESN_BUFFER_SIZE,
2057 FALSE, FALSE, NULL);
2058 if (Info->Gesn.Mdl == NULL) {
2059 status = STATUS_INSUFFICIENT_RESOURCES;
2060 goto ExitWithError;
2061 }
2062
2063 MmBuildMdlForNonPagedPool(Info->Gesn.Mdl);
2064 Info->Gesn.BufferSize = GESN_BUFFER_SIZE;
2065 Info->Gesn.EventMask = 0;
2066
2067 //
2068 // all items are prepared to use GESN (except the event mask, so don't
2069 // optimize this part out!).
2070 //
2071 // now see if it really works. we have to loop through this because
2072 // many SAMSUNG (and one COMPAQ) drives timeout when requesting
2073 // NOT_READY events, even when the IMMEDIATE bit is set. :(
2074 //
2075 // using a drive list is cumbersome, so this might fix the problem.
2076 //
2077
2078 deviceDescriptor = FdoExtension->DeviceDescriptor;
2079 atapiResets = 0;
2080 retryImmediately = TRUE;
2081 for (i = 0; i < 16 && retryImmediately == TRUE; i++) {
2082
2083 irp = ClasspPrepareMcnIrp(FdoExtension, Info, TRUE);
2084 if (irp == NULL) {
2085 status = STATUS_INSUFFICIENT_RESOURCES;
2086 goto ExitWithError;
2087 }
2088
2089 if (Info->MediaChangeSrb.Srb.Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
2090 srbFlags = Info->MediaChangeSrb.SrbEx.SrbFlags;
2091 } else {
2092 srbFlags = Info->MediaChangeSrb.Srb.SrbFlags;
2093 }
2094 NT_ASSERT(TEST_FLAG(srbFlags, SRB_FLAGS_NO_QUEUE_FREEZE));
2095
2096 //
2097 // replace the completion routine with a different one this time...
2098 //
2099
2100 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
2101 IoSetCompletionRoutine(irp,
2102 ClassSignalCompletion,
2103 &event,
2104 TRUE, TRUE, TRUE);
2105
2106 status = IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
2107
2108 if (status == STATUS_PENDING) {
2109 status = KeWaitForSingleObject(&event,
2110 Executive,
2111 KernelMode,
2112 FALSE,
2113 NULL);
2114 NT_ASSERT(NT_SUCCESS(status));
2115 }
2116 ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
2117
2118 if (SRB_STATUS(Info->MediaChangeSrb.Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
2119
2120 InterpretSenseInfoWithoutHistory(FdoExtension->DeviceObject,
2121 irp,
2122 &(Info->MediaChangeSrb.Srb),
2123 IRP_MJ_SCSI,
2124 0,
2125 0,
2126 &status,
2127 NULL);
2128 }
2129
2130 if ((deviceDescriptor->BusType == BusTypeAtapi) &&
2131 (Info->MediaChangeSrb.Srb.SrbStatus == SRB_STATUS_BUS_RESET)
2132 ) {
2133
2134 //
2135 // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
2136 // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
2137 // the two. if we get this status four time consecutively,
2138 // stop trying this command. it is too late to change ATAPI
2139 // at this point, so special-case this here. (07/10/2001)
2140 // NOTE: any value more than 4 may cause the device to be
2141 // marked missing.
2142 //
2143
2144 atapiResets++;
2145 if (atapiResets >= 4) {
2146 status = STATUS_IO_DEVICE_ERROR;
2147 goto ExitWithError;
2148 }
2149 }
2150
2151 if (status == STATUS_DATA_OVERRUN) {
2152 status = STATUS_SUCCESS;
2153 }
2154
2155 if ((status == STATUS_INVALID_DEVICE_REQUEST) ||
2156 (status == STATUS_TIMEOUT) ||
2157 (status == STATUS_IO_DEVICE_ERROR) ||
2158 (status == STATUS_IO_TIMEOUT)
2159 ) {
2160
2161 //
2162 // with these error codes, we don't ever want to try this command
2163 // again on this device, since it reacts poorly.
2164 //
2165
2166 ClassSetDeviceParameter(FdoExtension,
2167 CLASSP_REG_SUBKEY_NAME,
2168 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
2169 ClassDetectionUnsupported);
2170 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
2171 "Classpnp => GESN test failed %x for fdo %p\n",
2172 status, FdoExtension->DeviceObject));
2173 goto ExitWithError;
2174
2175
2176 }
2177
2178 if (!NT_SUCCESS(status)) {
2179
2180 //
2181 // this may be other errors that should not disable GESN
2182 // for all future start_device calls.
2183 //
2184
2185 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
2186 "Classpnp => GESN test failed %x for fdo %p\n",
2187 status, FdoExtension->DeviceObject));
2188 goto ExitWithError;
2189 }
2190
2191 if (i == 0) {
2192
2193 //
2194 // the first time, the request was just retrieving a mask of
2195 // available bits. use this to mask future requests.
2196 //
2197
2198 header = (PNOTIFICATION_EVENT_STATUS_HEADER)(Info->Gesn.Buffer);
2199
2200 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2201 "Classpnp => Fdo %p supports event mask %x\n",
2202 FdoExtension->DeviceObject, header->SupportedEventClasses));
2203
2204
2205 if (TEST_FLAG(header->SupportedEventClasses,
2206 NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) {
2207 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2208 "Classpnp => GESN supports MCN\n"));
2209 }
2210 if (TEST_FLAG(header->SupportedEventClasses,
2211 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) {
2212 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2213 "Classpnp => GESN supports DeviceBusy\n"));
2214 }
2215 if (TEST_FLAG(header->SupportedEventClasses,
2216 NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK)) {
2217
2218 if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
2219 FDO_HACK_GESN_IGNORE_OPCHANGE)) {
2220 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2221 "Classpnp => GESN supports OpChange, but "
2222 "must ignore these events for compatibility\n"));
2223 CLEAR_FLAG(header->SupportedEventClasses,
2224 NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK);
2225 } else {
2226 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2227 "Classpnp => GESN supports OpChange\n"));
2228 }
2229 }
2230 Info->Gesn.EventMask = header->SupportedEventClasses;
2231
2232 //
2233 // realistically, we are only considering the following events:
2234 // EXTERNAL REQUEST - this is being tested for play/stop/etc.
2235 // MEDIA STATUS - autorun and ejection requests.
2236 // DEVICE BUSY - to allow us to predict when media will be ready.
2237 // therefore, we should not bother querying for the other,
2238 // unknown events. clear all but the above flags.
2239 //
2240
2241 Info->Gesn.EventMask &=
2242 NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK |
2243 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK |
2244 NOTIFICATION_MEDIA_STATUS_CLASS_MASK |
2245 NOTIFICATION_DEVICE_BUSY_CLASS_MASK ;
2246
2247
2248 //
2249 // HACKHACK - REF #0001
2250 // Some devices will *never* report an event if we've also requested
2251 // that it report lower-priority events. this is due to a
2252 // misunderstanding in the specification wherein a "No Change" is
2253 // interpreted to be a real event. what should occur is that the
2254 // device should ignore "No Change" events when multiple event types
2255 // are requested unless there are no other events waiting. this
2256 // greatly reduces the number of requests that the host must send
2257 // to determine if an event has occurred. Since we must work on all
2258 // drives, default to enabling the hack until we find evidence of
2259 // proper firmware.
2260 //
2261 if (Info->Gesn.EventMask == 0) {
2262
2263 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2264 "Classpnp => GESN supported, but not mask we care "
2265 "about (%x) for FDO %p\n",
2266 header->SupportedEventClasses,
2267 FdoExtension->DeviceObject));
2268 goto ExitWithError;
2269
2270 } else if (CountOfSetBitsUChar(Info->Gesn.EventMask) == 1) {
2271
2272 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2273 "Classpnp => GESN hack not required for FDO %p\n",
2274 FdoExtension->DeviceObject));
2275
2276 } else {
2277
2278 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2279 "Classpnp => GESN hack enabled for FDO %p\n",
2280 FdoExtension->DeviceObject));
2281 Info->Gesn.HackEventMask = 1;
2282
2283 }
2284
2285 } else {
2286
2287 //
2288 // not the first time looping through, so interpret the results.
2289 //
2290
2291 status = ClasspInterpretGesnData(FdoExtension,
2292 (PVOID)Info->Gesn.Buffer,
2293 &retryImmediately);
2294
2295 if (!NT_SUCCESS(status)) {
2296
2297 //
2298 // This drive does not support GESN correctly
2299 //
2300
2301 ClassSetDeviceParameter(FdoExtension,
2302 CLASSP_REG_SUBKEY_NAME,
2303 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
2304 ClassDetectionUnsupported);
2305 goto ExitWithError;
2306 }
2307 }
2308
2309 } // end loop of GESN requests....
2310
2311 //
2312 // we can only use this if it can be relied upon for media changes,
2313 // since we are (by definition) no longer going to be polling via
2314 // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
2315 // for this command (although a filter driver, such as one for burning
2316 // cd's, might still fake those errors).
2317 //
2318 // since we also rely upon NOT_READY events to change the cursor
2319 // into a "wait" cursor; GESN is still more reliable than other
2320 // methods, and includes eject button requests, so we'll use it
2321 // without DEVICE_BUSY in Windows Vista.
2322 //
2323
2324 if (TEST_FLAG(Info->Gesn.EventMask, NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) {
2325
2326 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2327 "Classpnp => Enabling GESN support for fdo %p\n",
2328 FdoExtension->DeviceObject));
2329 Info->Gesn.Supported = TRUE;
2330
2331 ClassSetDeviceParameter(FdoExtension,
2332 CLASSP_REG_SUBKEY_NAME,
2333 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
2334 ClassDetectionSupported);
2335
2336 return STATUS_SUCCESS;
2337
2338 }
2339
2340 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2341 "Classpnp => GESN available but not enabled for fdo %p\n",
2342 FdoExtension->DeviceObject));
2343 goto ExitWithError;
2344
2345 // fall through...
2346
2347 ExitWithError:
2348 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
2349 "Classpnp => GESN support detection failed for fdo %p with status %08x\n",
2350 FdoExtension->DeviceObject, status));
2351
2352
2353 if (Info->Gesn.Mdl) {
2354 IoFreeMdl(Info->Gesn.Mdl);
2355 Info->Gesn.Mdl = NULL;
2356 }
2357 FREE_POOL(Info->Gesn.Buffer);
2358 Info->Gesn.Supported = 0;
2359 Info->Gesn.EventMask = 0;
2360 Info->Gesn.BufferSize = 0;
2361 return STATUS_NOT_SUPPORTED;
2362
2363 }
2364
2365
2366 //
2367 // Work item to set the hack flag in the registry to disable GESN
2368 // on devices that sends too many events
2369 //
2370
2371 VOID
2372 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClasspDisableGesn(IN PDEVICE_OBJECT Fdo,IN PVOID Context)2373 ClasspDisableGesn(
2374 IN PDEVICE_OBJECT Fdo,
2375 IN PVOID Context
2376 )
2377 {
2378 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2379 PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;
2380
2381 PAGED_CODE();
2382
2383 //
2384 // Set the hack flag in the registry
2385 //
2386 ClassSetDeviceParameter(fdoExtension,
2387 CLASSP_REG_SUBKEY_NAME,
2388 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
2389 ClassDetectionUnsupported);
2390 _Analysis_assume_(WorkItem != NULL);
2391 IoFreeWorkItem(WorkItem);
2392 }
2393
2394 /*++////////////////////////////////////////////////////////////////////////////
2395
2396 ClassInitializeTestUnitPolling()
2397
2398 Routine Description:
2399
2400 This routine will initialize MCN regardless of the settings stored
2401 in the registry. This should be used with caution, as some devices
2402 react badly to constant io. (i.e. never spin down, continuously cycling
2403 media in changers, ejection of media, etc.) It is highly suggested to
2404 use ClassInitializeMediaChangeDetection() instead.
2405
2406 Arguments:
2407
2408 FdoExtension is the device to poll
2409
2410 AllowDriveToSleep says whether to attempt to allow the drive to sleep
2411 or not. This only affects system-known spin down states, so if a
2412 drive spins itself down, this has no effect until the system spins
2413 it down.
2414
2415 Return Value:
2416
2417 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)2418 _IRQL_requires_max_(PASSIVE_LEVEL)
2419 NTSTATUS
2420 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
2421 ClassInitializeTestUnitPolling(
2422 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
2423 _In_ BOOLEAN AllowDriveToSleep
2424 )
2425 {
2426 return ClasspInitializePolling(FdoExtension, AllowDriveToSleep);
2427 } // end ClassInitializeTestUnitPolling()
2428
2429 /*++////////////////////////////////////////////////////////////////////////////
2430
2431 ClassInitializeMediaChangeDetection()
2432
2433 Routine Description:
2434
2435 This routine checks to see if it is safe to initialize MCN (the back end
2436 to autorun) for a given device. It will then check the device-type wide
2437 key "Autorun" in the service key (for legacy reasons), and then look in
2438 the device-specific key to potentially override that setting.
2439
2440 If MCN is to be enabled, all neccessary structures and memory are
2441 allocated and initialized.
2442
2443 This routine MUST be called only from the ClassInit() callback.
2444
2445 Arguments:
2446
2447 FdoExtension - the device to initialize MCN for, if appropriate
2448
2449 EventPrefix - unused, legacy argument. Set to zero.
2450
2451 Return Value:
2452
2453 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)2454 _IRQL_requires_max_(PASSIVE_LEVEL)
2455 VOID
2456 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
2457 ClassInitializeMediaChangeDetection(
2458 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
2459 _In_ PUCHAR EventPrefix
2460 )
2461 {
2462 PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
2463 NTSTATUS status;
2464
2465 PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension(
2466 fdo->DriverObject);
2467
2468 BOOLEAN disabledForBadHardware;
2469 BOOLEAN disabled;
2470 BOOLEAN instanceOverride;
2471
2472 UNREFERENCED_PARAMETER(EventPrefix);
2473
2474 PAGED_CODE();
2475
2476 //
2477 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
2478 // called in the context of the ClassInitDevice callback. If called
2479 // after then this check will have already been made and the
2480 // once a second timer will not have been enabled.
2481 //
2482
2483 disabledForBadHardware = ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2484 FdoExtension,
2485 &(driverExtension->RegistryPath)
2486 );
2487
2488 if (disabledForBadHardware) {
2489 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2490 "ClassInitializeMCN: Disabled due to hardware"
2491 "limitations for this device"));
2492 return;
2493 }
2494
2495 //
2496 // autorun should now be enabled by default for all media types.
2497 //
2498
2499 disabled = ClasspIsMediaChangeDisabledForClass(
2500 FdoExtension,
2501 &(driverExtension->RegistryPath)
2502 );
2503
2504 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2505 "ClassInitializeMCN: Class MCN is %s\n",
2506 (disabled ? "disabled" : "enabled")));
2507
2508 status = ClasspMediaChangeDeviceInstanceOverride(
2509 FdoExtension,
2510 &instanceOverride); // default value
2511
2512 if (!NT_SUCCESS(status)) {
2513 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2514 "ClassInitializeMCN: Instance using default\n"));
2515 } else {
2516 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2517 "ClassInitializeMCN: Instance override: %s MCN\n",
2518 (instanceOverride ? "Enabling" : "Disabling")));
2519 disabled = !instanceOverride;
2520 }
2521
2522 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2523 "ClassInitializeMCN: Instance MCN is %s\n",
2524 (disabled ? "disabled" : "enabled")));
2525
2526 if (disabled) {
2527 return;
2528 }
2529
2530 //
2531 // Do not allow drive to sleep for all types of devices initially.
2532 // For non-cdrom devices, allow devices to go to sleep if it's
2533 // unlikely a media change will occur (e.g. user not present).
2534 //
2535 ClasspInitializePolling(FdoExtension, FALSE);
2536
2537 return;
2538 } // end ClassInitializeMediaChangeDetection()
2539
2540 /*++////////////////////////////////////////////////////////////////////////////
2541
2542 ClasspMediaChangeDeviceInstanceOverride()
2543
2544 Routine Description:
2545
2546 The user can override the global setting to enable or disable Autorun on a
2547 specific cdrom device via the control panel. This routine checks and/or
2548 sets this value.
2549
2550 Arguments:
2551
2552 FdoExtension - the device to set/get the value for
2553 Value - the value to use in a set
2554 SetValue - whether to set the value
2555
2556 Return Value:
2557
2558 TRUE - Autorun is disabled
2559 FALSE - Autorun is not disabled (Default)
2560
2561 --*/
2562 NTSTATUS
ClasspMediaChangeDeviceInstanceOverride(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,OUT PBOOLEAN Enabled)2563 ClasspMediaChangeDeviceInstanceOverride(
2564 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
2565 OUT PBOOLEAN Enabled
2566 )
2567 {
2568 HANDLE deviceParameterHandle = NULL; // cdrom instance key
2569 HANDLE driverParameterHandle = NULL; // cdrom specific key
2570 RTL_QUERY_REGISTRY_TABLE queryTable[3];
2571 OBJECT_ATTRIBUTES objectAttributes;
2572 UNICODE_STRING subkeyName;
2573 NTSTATUS status = STATUS_UNSUCCESSFUL;
2574 ULONG alwaysEnable = FALSE;
2575 ULONG alwaysDisable = FALSE;
2576 ULONG i;
2577
2578 PAGED_CODE();
2579
2580 TRY {
2581
2582 status = IoOpenDeviceRegistryKey( FdoExtension->LowerPdo,
2583 PLUGPLAY_REGKEY_DEVICE,
2584 KEY_ALL_ACCESS,
2585 &deviceParameterHandle
2586 );
2587 if (!NT_SUCCESS(status)) {
2588
2589 //
2590 // this can occur when a new device is added to the system
2591 // this is due to cdrom.sys being an 'essential' driver
2592 //
2593 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2594 "ClassMediaChangeDeviceInstanceDisabled: "
2595 "Could not open device registry key [%lx]\n", status));
2596 LEAVE;
2597 }
2598
2599 RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME);
2600 InitializeObjectAttributes(&objectAttributes,
2601 &subkeyName,
2602 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2603 deviceParameterHandle,
2604 (PSECURITY_DESCRIPTOR) NULL);
2605
2606 status = ZwCreateKey(&driverParameterHandle,
2607 KEY_READ,
2608 &objectAttributes,
2609 0,
2610 (PUNICODE_STRING) NULL,
2611 REG_OPTION_NON_VOLATILE,
2612 NULL);
2613
2614 if (!NT_SUCCESS(status)) {
2615 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2616 "ClassMediaChangeDeviceInstanceDisabled: "
2617 "subkey could not be created. %lx\n", status));
2618 LEAVE;
2619 }
2620
2621 //
2622 // Default to not changing autorun behavior, based upon setting
2623 // registryValue to zero.
2624 //
2625
2626 for (i=0;i<2;i++) {
2627
2628 RtlZeroMemory(&queryTable[0], sizeof(queryTable));
2629
2630 queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
2631 queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
2632 queryTable[0].DefaultLength = 0;
2633
2634 if (i==0) {
2635 queryTable[0].Name = MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME;
2636 queryTable[0].EntryContext = &alwaysDisable;
2637 queryTable[0].DefaultData = &alwaysDisable;
2638 } else {
2639 queryTable[0].Name = MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME;
2640 queryTable[0].EntryContext = &alwaysEnable;
2641 queryTable[0].DefaultData = &alwaysEnable;
2642 }
2643
2644 //
2645 // don't care if it succeeds, since we set defaults above
2646 //
2647
2648 RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
2649 (PWSTR)driverParameterHandle,
2650 queryTable,
2651 NULL,
2652 NULL);
2653 }
2654
2655 } FINALLY {
2656
2657 if (driverParameterHandle) ZwClose(driverParameterHandle);
2658 if (deviceParameterHandle) ZwClose(deviceParameterHandle);
2659
2660 }
2661
2662 if (alwaysEnable && alwaysDisable) {
2663
2664 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2665 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2666 "Both Enable and Disable set -- DISABLE"));
2667 NT_ASSERT(NT_SUCCESS(status));
2668 status = STATUS_SUCCESS;
2669 *Enabled = FALSE;
2670
2671 } else if (alwaysDisable) {
2672
2673 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2674 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2675 "DISABLE"));
2676 NT_ASSERT(NT_SUCCESS(status));
2677 status = STATUS_SUCCESS;
2678 *Enabled = FALSE;
2679
2680 } else if (alwaysEnable) {
2681
2682 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2683 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2684 "ENABLE"));
2685 NT_ASSERT(NT_SUCCESS(status));
2686 status = STATUS_SUCCESS;
2687 *Enabled = TRUE;
2688
2689 } else {
2690
2691 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2692 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2693 "DEFAULT"));
2694 status = STATUS_UNSUCCESSFUL;
2695
2696 }
2697
2698 return status;
2699
2700 } // end ClasspMediaChangeDeviceInstanceOverride()
2701
2702 /*++////////////////////////////////////////////////////////////////////////////
2703
2704 ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2705
2706 Routine Description:
2707
2708 The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
2709 which to never enable MediaChangeNotification.
2710
2711 The user can override the global setting to enable or disable Autorun on a
2712 specific cdrom device via the control panel.
2713
2714 Arguments:
2715
2716 FdoExtension -
2717 RegistryPath - pointer to the unicode string inside
2718 ...\CurrentControlSet\Services\Cdrom
2719
2720 Return Value:
2721
2722 TRUE - no autorun.
2723 FALSE - Autorun may be enabled
2724
2725 --*/
2726 BOOLEAN
ClasspIsMediaChangeDisabledDueToHardwareLimitation(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN PUNICODE_STRING RegistryPath)2727 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2728 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
2729 IN PUNICODE_STRING RegistryPath
2730 )
2731 {
2732 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
2733 OBJECT_ATTRIBUTES objectAttributes = {0};
2734 HANDLE serviceKey = NULL;
2735 RTL_QUERY_REGISTRY_TABLE parameters[2] = {0};
2736
2737 UNICODE_STRING deviceUnicodeString;
2738 ANSI_STRING deviceString;
2739 ULONG mediaChangeNotificationDisabled = FALSE;
2740
2741 NTSTATUS status;
2742
2743
2744 PAGED_CODE();
2745
2746 //
2747 // open the service key.
2748 //
2749
2750 InitializeObjectAttributes(&objectAttributes,
2751 RegistryPath,
2752 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2753 NULL,
2754 NULL);
2755
2756 status = ZwOpenKey(&serviceKey,
2757 KEY_READ,
2758 &objectAttributes);
2759
2760 NT_ASSERT(NT_SUCCESS(status));
2761
2762
2763 if(!NT_SUCCESS(status)) {
2764
2765 //
2766 // always take the safe path. if we can't open the service key,
2767 // disable autorun
2768 //
2769
2770 return TRUE;
2771
2772 }
2773
2774 TRY {
2775 //
2776 // Determine if drive is in a list of those requiring
2777 // autorun to be disabled. this is stored in a REG_MULTI_SZ
2778 // named AutoRunAlwaysDisable. this is required as some autochangers
2779 // must load the disc to reply to ChkVerify request, causing them
2780 // to cycle discs continuously.
2781 //
2782
2783 PWSTR nullMultiSz;
2784 PUCHAR vendorId;
2785 PUCHAR productId;
2786 PUCHAR revisionId;
2787 ULONG length;
2788 ULONG offset;
2789
2790 deviceString.Buffer = NULL;
2791 deviceUnicodeString.Buffer = NULL;
2792
2793 //
2794 // there may be nothing to check against
2795 //
2796
2797 if ((deviceDescriptor->VendorIdOffset == 0) &&
2798 (deviceDescriptor->ProductIdOffset == 0)) {
2799 LEAVE;
2800 }
2801
2802 length = 0;
2803
2804 if (deviceDescriptor->VendorIdOffset == 0) {
2805 vendorId = NULL;
2806 } else {
2807 vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset;
2808 length = (ULONG)strlen((PCSZ)vendorId);
2809 }
2810
2811 if ( deviceDescriptor->ProductIdOffset == 0 ) {
2812 productId = NULL;
2813 } else {
2814 productId = (PUCHAR)deviceDescriptor + deviceDescriptor->ProductIdOffset;
2815 length += (ULONG)strlen((PCSZ)productId);
2816 }
2817
2818 if ( deviceDescriptor->ProductRevisionOffset == 0 ) {
2819 revisionId = NULL;
2820 } else {
2821 revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset;
2822 length += (ULONG)strlen((PCSZ)revisionId);
2823 }
2824
2825 //
2826 // allocate a buffer for the string
2827 //
2828
2829 deviceString.Length = (USHORT)( length );
2830 deviceString.MaximumLength = deviceString.Length + 1;
2831 deviceString.Buffer = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolNx,
2832 deviceString.MaximumLength,
2833 CLASS_TAG_AUTORUN_DISABLE
2834 );
2835 if (deviceString.Buffer == NULL) {
2836 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2837 "ClassMediaChangeDisabledForHardware: Unable to alloc "
2838 "string buffer\n" ));
2839 LEAVE;
2840 }
2841
2842 //
2843 // copy strings to the buffer
2844 //
2845 offset = 0;
2846
2847 if (vendorId != NULL) {
2848 RtlCopyMemory(deviceString.Buffer + offset,
2849 vendorId,
2850 strlen((PCSZ)vendorId));
2851 offset += (ULONG)strlen((PCSZ)vendorId);
2852 }
2853
2854 if ( productId != NULL ) {
2855 RtlCopyMemory(deviceString.Buffer + offset,
2856 productId,
2857 strlen((PCSZ)productId));
2858 offset += (ULONG)strlen((PCSZ)productId);
2859 }
2860 if ( revisionId != NULL ) {
2861 RtlCopyMemory(deviceString.Buffer + offset,
2862 revisionId,
2863 strlen((PCSZ)revisionId));
2864 offset += (ULONG)strlen((PCSZ)revisionId);
2865 }
2866
2867 NT_ASSERT(offset == deviceString.Length);
2868
2869 #ifdef _MSC_VER
2870 #pragma warning(suppress:6386) // Not an issue as deviceString.Buffer is of size deviceString.MaximumLength, which is equal to (deviceString.Length + 1)
2871 #endif
2872 deviceString.Buffer[deviceString.Length] = '\0'; // Null-terminated
2873
2874 //
2875 // convert to unicode as registry deals with unicode strings
2876 //
2877
2878 status = RtlAnsiStringToUnicodeString( &deviceUnicodeString,
2879 &deviceString,
2880 TRUE
2881 );
2882 if (!NT_SUCCESS(status)) {
2883 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2884 "ClassMediaChangeDisabledForHardware: cannot convert "
2885 "to unicode %lx\n", status));
2886 LEAVE;
2887 }
2888
2889 //
2890 // query the value, setting valueFound to true if found
2891 //
2892 nullMultiSz = L"\0";
2893 parameters[0].QueryRoutine = ClasspMediaChangeRegistryCallBack;
2894 parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
2895 parameters[0].Name = L"AutoRunAlwaysDisable";
2896 parameters[0].EntryContext = &mediaChangeNotificationDisabled;
2897 parameters[0].DefaultType = REG_MULTI_SZ;
2898 parameters[0].DefaultData = nullMultiSz;
2899 parameters[0].DefaultLength = 0;
2900
2901 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
2902 serviceKey,
2903 parameters,
2904 &deviceUnicodeString,
2905 NULL);
2906
2907 if ( !NT_SUCCESS(status) ) {
2908 LEAVE;
2909 }
2910
2911 } FINALLY {
2912
2913 FREE_POOL( deviceString.Buffer );
2914 if (deviceUnicodeString.Buffer != NULL) {
2915 RtlFreeUnicodeString( &deviceUnicodeString );
2916 }
2917
2918 ZwClose(serviceKey);
2919 }
2920
2921 if (mediaChangeNotificationDisabled) {
2922 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassMediaChangeDisabledForHardware: "
2923 "Device is on disable list\n"));
2924 return TRUE;
2925 }
2926 return FALSE;
2927
2928 } // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2929
2930 /*++////////////////////////////////////////////////////////////////////////////
2931
2932 ClasspIsMediaChangeDisabledForClass()
2933
2934 Routine Description:
2935
2936 The user must specify that AutoPlay is to run on the platform
2937 by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
2938 Services\<SERVICE>\Autorun:REG_DWORD:1.
2939
2940 The user can override the global setting to enable or disable Autorun on a
2941 specific cdrom device via the control panel.
2942
2943 Arguments:
2944
2945 FdoExtension -
2946 RegistryPath - pointer to the unicode string inside
2947 ...\CurrentControlSet\Services\Cdrom
2948
2949 Return Value:
2950
2951 TRUE - Autorun is disabled for this class
2952 FALSE - Autorun is enabled for this class
2953
2954 --*/
2955 BOOLEAN
ClasspIsMediaChangeDisabledForClass(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN PUNICODE_STRING RegistryPath)2956 ClasspIsMediaChangeDisabledForClass(
2957 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
2958 IN PUNICODE_STRING RegistryPath
2959 )
2960 {
2961 OBJECT_ATTRIBUTES objectAttributes = {0};
2962 HANDLE serviceKey = NULL;
2963 HANDLE parametersKey = NULL;
2964 RTL_QUERY_REGISTRY_TABLE parameters[3] = {0};
2965
2966 UNICODE_STRING paramStr;
2967
2968 //
2969 // Default to ENABLING MediaChangeNotification (!)
2970 //
2971
2972 ULONG mcnRegistryValue = 1;
2973
2974 NTSTATUS status;
2975
2976
2977 PAGED_CODE();
2978
2979 //
2980 // open the service key.
2981 //
2982
2983 InitializeObjectAttributes(&objectAttributes,
2984 RegistryPath,
2985 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2986 NULL,
2987 NULL);
2988
2989 status = ZwOpenKey(&serviceKey,
2990 KEY_READ,
2991 &objectAttributes);
2992
2993 NT_ASSERT(NT_SUCCESS(status));
2994
2995 if(!NT_SUCCESS(status)) {
2996
2997 //
2998 // return the default value, which is the
2999 // inverse of the registry setting default
3000 // since this routine asks if it's disabled
3001 //
3002
3003 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: Defaulting to %s\n",
3004 (mcnRegistryValue ? "Enabled" : "Disabled")));
3005 return (BOOLEAN)(!mcnRegistryValue);
3006
3007 }
3008
3009 //
3010 // Open the parameters key (if any) beneath the services key.
3011 //
3012
3013 RtlInitUnicodeString(¶mStr, L"Parameters");
3014
3015 InitializeObjectAttributes(&objectAttributes,
3016 ¶mStr,
3017 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3018 serviceKey,
3019 NULL);
3020
3021 status = ZwOpenKey(¶metersKey,
3022 KEY_READ,
3023 &objectAttributes);
3024
3025 if (!NT_SUCCESS(status)) {
3026 parametersKey = NULL;
3027 }
3028
3029
3030
3031 //
3032 // Check for the Autorun value.
3033 //
3034
3035 parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
3036 parameters[0].Name = L"Autorun";
3037 parameters[0].EntryContext = &mcnRegistryValue;
3038 parameters[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD;
3039 parameters[0].DefaultData = &mcnRegistryValue;
3040 parameters[0].DefaultLength = sizeof(ULONG);
3041
3042 // ignore failures
3043 RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
3044 serviceKey,
3045 parameters,
3046 NULL,
3047 NULL);
3048
3049 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
3050 "<Service>/Autorun flag = %d\n", mcnRegistryValue));
3051
3052 if(parametersKey != NULL) {
3053
3054 // ignore failures
3055 RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
3056 parametersKey,
3057 parameters,
3058 NULL,
3059 NULL);
3060 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
3061 "<Service>/Parameters/Autorun flag = %d\n",
3062 mcnRegistryValue));
3063 ZwClose(parametersKey);
3064
3065 }
3066 ZwClose(serviceKey);
3067
3068 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
3069 "Autoplay for device %p is %s\n",
3070 FdoExtension->DeviceObject,
3071 (mcnRegistryValue ? "on" : "off")
3072 ));
3073
3074 //
3075 // return if it is _disabled_, which is the
3076 // inverse of the registry setting
3077 //
3078
3079 return (BOOLEAN)(!mcnRegistryValue);
3080 } // end ClasspIsMediaChangeDisabledForClass()
3081
3082 /*++////////////////////////////////////////////////////////////////////////////
3083
3084 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
3085 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
3086
3087 Routine Description:
3088
3089 This routine
3090
3091 Arguments:
3092
3093 DeviceObject -
3094 Irp -
3095
3096 Return Value:
3097
3098 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)3099 _IRQL_requires_max_(PASSIVE_LEVEL)
3100 VOID
3101 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
3102 ClassEnableMediaChangeDetection(
3103 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
3104 )
3105 {
3106 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
3107 LONG oldCount;
3108
3109 PAGED_CODE();
3110
3111 if(info == NULL) {
3112 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
3113 "ClassEnableMediaChangeDetection: not initialized\n"));
3114 return;
3115 }
3116
3117 (VOID)KeWaitForMutexObject(&info->MediaChangeMutex,
3118 UserRequest,
3119 KernelMode,
3120 FALSE,
3121 NULL);
3122
3123 oldCount = --info->MediaChangeDetectionDisableCount;
3124
3125 NT_ASSERT(oldCount >= 0);
3126
3127 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassEnableMediaChangeDetection: Disable count "
3128 "reduced to %d - ",
3129 info->MediaChangeDetectionDisableCount));
3130
3131 if(oldCount == 0) {
3132
3133 //
3134 // We don't know what state the media is in anymore.
3135 //
3136
3137 ClasspInternalSetMediaChangeState(FdoExtension,
3138 MediaUnknown,
3139 FALSE
3140 );
3141
3142 //
3143 // Reset the MCN timer.
3144 //
3145
3146 ClassResetMediaChangeTimer(FdoExtension);
3147
3148 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCD is enabled\n"));
3149
3150 } else {
3151
3152 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCD still disabled\n"));
3153
3154 }
3155
3156
3157 //
3158 // Let something else run.
3159 //
3160
3161 KeReleaseMutex(&info->MediaChangeMutex, FALSE);
3162
3163 return;
3164 } // end ClassEnableMediaChangeDetection()
3165
3166 /*++////////////////////////////////////////////////////////////////////////////
3167
3168 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
3169 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
3170
3171 Routine Description:
3172
3173 This routine
3174
3175 Arguments:
3176
3177 DeviceObject -
3178 Irp -
3179
3180 Return Value:
3181
3182 --*/
3183 ULONG BreakOnMcnDisable = FALSE;
3184
_IRQL_requires_max_(PASSIVE_LEVEL)3185 _IRQL_requires_max_(PASSIVE_LEVEL)
3186 VOID
3187 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
3188 ClassDisableMediaChangeDetection(
3189 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
3190 )
3191 {
3192 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
3193
3194 PAGED_CODE();
3195
3196 if(info == NULL) {
3197 return;
3198 }
3199
3200 (VOID)KeWaitForMutexObject(&info->MediaChangeMutex,
3201 UserRequest,
3202 KernelMode,
3203 FALSE,
3204 NULL);
3205
3206 info->MediaChangeDetectionDisableCount++;
3207
3208 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassDisableMediaChangeDetection: "
3209 "disable count is %d\n",
3210 info->MediaChangeDetectionDisableCount));
3211
3212 KeReleaseMutex(&info->MediaChangeMutex, FALSE);
3213
3214 return;
3215 } // end ClassDisableMediaChangeDetection()
3216
3217 /*++////////////////////////////////////////////////////////////////////////////
3218
3219 ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!
3220
3221 Routine Description:
3222
3223 This routine will cleanup any resources allocated for MCN. It is called
3224 by classpnp during remove device, and therefore is not typically required
3225 by external drivers.
3226
3227 Arguments:
3228
3229 Return Value:
3230
3231 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)3232 _IRQL_requires_max_(PASSIVE_LEVEL)
3233 VOID
3234 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
3235 ClassCleanupMediaChangeDetection(
3236 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
3237 )
3238 {
3239 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
3240
3241 PAGED_CODE()
3242
3243 if(info == NULL) {
3244 return;
3245 }
3246
3247 FdoExtension->MediaChangeDetectionInfo = NULL;
3248
3249 if (info->Gesn.Mdl) {
3250 IoFreeMdl(info->Gesn.Mdl);
3251 }
3252 FREE_POOL(info->Gesn.Buffer);
3253 IoFreeIrp(info->MediaChangeIrp);
3254 FREE_POOL(info->SenseBuffer);
3255 FREE_POOL(info);
3256 return;
3257 } // end ClassCleanupMediaChangeDetection()
3258
3259 /*++////////////////////////////////////////////////////////////////////////////
3260
3261 ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented
3262
3263 Routine Description:
3264
3265 This routine
3266
3267 Arguments:
3268
3269 DeviceObject -
3270 Irp -
3271
3272 Return Value:
3273
3274 --*/
3275 NTSTATUS
ClasspMcnControl(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN PIRP Irp,IN PSCSI_REQUEST_BLOCK Srb)3276 ClasspMcnControl(
3277 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
3278 IN PIRP Irp,
3279 IN PSCSI_REQUEST_BLOCK Srb
3280 )
3281 {
3282 PCOMMON_DEVICE_EXTENSION commonExtension =
3283 (PCOMMON_DEVICE_EXTENSION) FdoExtension;
3284
3285 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
3286 PPREVENT_MEDIA_REMOVAL request = Irp->AssociatedIrp.SystemBuffer;
3287
3288 PFILE_OBJECT fileObject = irpStack->FileObject;
3289 PFILE_OBJECT_EXTENSION fsContext = NULL;
3290
3291 NTSTATUS status = STATUS_SUCCESS;
3292
3293 PAGED_CODE();
3294
3295 //
3296 // Check to make sure we have a file object extension to keep track of this
3297 // request. If not we'll fail it before synchronizing.
3298 //
3299
3300 TRY {
3301
3302 if(fileObject != NULL) {
3303 fsContext = ClassGetFsContext(commonExtension, fileObject);
3304 }else if(Irp->RequestorMode == KernelMode) { // && fileObject == NULL
3305 fsContext = &FdoExtension->KernelModeMcnContext;
3306 }
3307
3308 if (fsContext == NULL) {
3309
3310 //
3311 // This handle isn't setup correctly. We can't let the
3312 // operation go.
3313 //
3314
3315 status = STATUS_INVALID_PARAMETER;
3316 LEAVE;
3317 }
3318
3319 if(request->PreventMediaRemoval) {
3320
3321 //
3322 // This is a lock command. Reissue the command in case bus or
3323 // device was reset and the lock was cleared.
3324 //
3325
3326 ClassDisableMediaChangeDetection(FdoExtension);
3327 InterlockedIncrement((volatile LONG *)&(fsContext->McnDisableCount));
3328
3329 } else {
3330
3331 if(fsContext->McnDisableCount == 0) {
3332 status = STATUS_INVALID_DEVICE_STATE;
3333 LEAVE;
3334 }
3335
3336 InterlockedDecrement((volatile LONG *)&(fsContext->McnDisableCount));
3337 ClassEnableMediaChangeDetection(FdoExtension);
3338 }
3339
3340 } FINALLY {
3341
3342 Irp->IoStatus.Status = status;
3343
3344 FREE_POOL(Srb);
3345
3346 ClassReleaseRemoveLock(FdoExtension->DeviceObject, Irp);
3347 ClassCompleteRequest(FdoExtension->DeviceObject,
3348 Irp,
3349 IO_NO_INCREMENT);
3350 }
3351 return status;
3352 } // end ClasspMcnControl(
3353
3354 /*++////////////////////////////////////////////////////////////////////////////
3355
3356 ClasspMediaChangeRegistryCallBack()
3357
3358 Routine Description:
3359
3360 This callback for a registry SZ or MULTI_SZ is called once for each
3361 SZ in the value. It will attempt to match the data with the
3362 UNICODE_STRING passed in as Context, and modify EntryContext if a
3363 match is found. Written for ClasspCheckRegistryForMediaChangeCompletion
3364
3365 Arguments:
3366
3367 ValueName - name of the key that was opened
3368 ValueType - type of data stored in the value (REG_SZ for this routine)
3369 ValueData - data in the registry, in this case a wide string
3370 ValueLength - length of the data including the terminating null
3371 Context - unicode string to compare against ValueData
3372 EntryContext - should be initialized to 0, will be set to 1 if match found
3373
3374 Return Value:
3375
3376 STATUS_SUCCESS
3377 EntryContext will be 1 if found
3378
3379 --*/
3380 _Function_class_(RTL_QUERY_REGISTRY_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)3381 _IRQL_requires_max_(PASSIVE_LEVEL)
3382 _IRQL_requires_same_
3383 NTSTATUS
3384 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
3385 ClasspMediaChangeRegistryCallBack(
3386 _In_z_ PWSTR ValueName,
3387 _In_ ULONG ValueType,
3388 _In_reads_bytes_opt_(ValueLength) PVOID ValueData,
3389 _In_ ULONG ValueLength,
3390 _In_opt_ PVOID Context,
3391 _In_opt_ PVOID EntryContext
3392 )
3393 {
3394 PULONG valueFound;
3395 PUNICODE_STRING deviceString;
3396 PWSTR keyValue;
3397
3398 PAGED_CODE();
3399 UNREFERENCED_PARAMETER(ValueName);
3400
3401 if (ValueData == NULL ||
3402 Context == NULL ||
3403 EntryContext == NULL) {
3404 return STATUS_INVALID_PARAMETER;
3405 }
3406
3407 //
3408 // if we have already set the value to true, exit
3409 //
3410
3411 valueFound = EntryContext;
3412 if ((*valueFound) != 0) {
3413 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMcnRegCB: already set to true\n"));
3414 return STATUS_SUCCESS;
3415 }
3416
3417 if (ValueLength == sizeof(WCHAR)) {
3418 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspMcnRegCB: NULL string should "
3419 "never be passed to registry call-back!\n"));
3420 return STATUS_SUCCESS;
3421 }
3422
3423
3424 //
3425 // if the data is not a terminated string, exit
3426 //
3427
3428 if (ValueType != REG_SZ) {
3429 return STATUS_SUCCESS;
3430 }
3431
3432 deviceString = Context;
3433 keyValue = ValueData;
3434 ValueLength -= sizeof(WCHAR); // ignore the null character
3435
3436 //
3437 // do not compare more memory than is in deviceString
3438 //
3439
3440 if (ValueLength > deviceString->Length) {
3441 ValueLength = deviceString->Length;
3442 }
3443
3444 //
3445 // if the strings match, disable autorun
3446 //
3447
3448 if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) {
3449 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: Match found\n"));
3450 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: DeviceString at %p\n",
3451 deviceString->Buffer));
3452 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: KeyValue at %p\n",
3453 keyValue));
3454 (*valueFound) = TRUE;
3455 }
3456
3457 return STATUS_SUCCESS;
3458 } // end ClasspMediaChangeRegistryCallBack()
3459
3460 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
3461 VOID
ClasspTimerTickEx(_In_ PEX_TIMER Timer,_In_opt_ PVOID Context)3462 ClasspTimerTickEx(
3463 _In_ PEX_TIMER Timer,
3464 _In_opt_ PVOID Context
3465 )
3466 {
3467 KDPC dummyDpc = { 0 };
3468
3469 UNREFERENCED_PARAMETER(Timer);
3470 //
3471 // This is just a wrapper around ClasspTimerTick that allows us to make
3472 // the TickTimer a no-wake EX_TIMER.
3473 // We pass in a dummy DPC b/c ClasspTimerTick expects a non-NULL parameter
3474 // for the DPC. However, ClasspTimerTick does not actually reference it.
3475 //
3476 ClasspTimerTick(&dummyDpc, Context, NULL, NULL);
3477 }
3478 #endif
3479
3480 /*++////////////////////////////////////////////////////////////////////////////
3481
3482 ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented
3483
3484 Routine Description:
3485
3486 This routine
3487
3488 Arguments:
3489
3490 DeviceObject -
3491 Irp -
3492
3493 Return Value:
3494
3495 --*/
3496 _Function_class_(KDEFERRED_ROUTINE)
_IRQL_requires_max_(DISPATCH_LEVEL)3497 _IRQL_requires_max_(DISPATCH_LEVEL)
3498 _IRQL_requires_min_(DISPATCH_LEVEL)
3499 _IRQL_requires_(DISPATCH_LEVEL)
3500 _IRQL_requires_same_
3501 VOID
3502 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
3503 ClasspTimerTick(
3504 _In_ PKDPC Dpc,
3505 _In_opt_ PVOID DeferredContext,
3506 _In_opt_ PVOID SystemArgument1,
3507 _In_opt_ PVOID SystemArgument2
3508 )
3509 {
3510 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeferredContext;
3511 PCOMMON_DEVICE_EXTENSION commonExtension;
3512 PDEVICE_OBJECT DeviceObject;
3513 ULONG isRemoved;
3514
3515 UNREFERENCED_PARAMETER(Dpc);
3516 UNREFERENCED_PARAMETER(SystemArgument1);
3517 UNREFERENCED_PARAMETER(SystemArgument2);
3518
3519 NT_ASSERT(fdoExtension != NULL);
3520 _Analysis_assume_(fdoExtension != NULL);
3521
3522 commonExtension = &fdoExtension->CommonExtension;
3523 DeviceObject = fdoExtension->DeviceObject;
3524 NT_ASSERT(commonExtension->IsFdo);
3525
3526 //
3527 // Do any media change work
3528 //
3529 #ifdef _MSC_VER
3530 #pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
3531 #endif
3532 isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
3533
3534 //
3535 // We stop the timer before deleting the device. It's safe to keep going
3536 // if the flag value is REMOVE_PENDING because the removal thread will be
3537 // blocked trying to stop the timer.
3538 //
3539
3540 NT_ASSERT(isRemoved != REMOVE_COMPLETE);
3541
3542 //
3543 // This routine is reasonably safe even if the device object has a pending
3544 // remove
3545
3546 if (!isRemoved) {
3547
3548 PFAILURE_PREDICTION_INFO info = fdoExtension->FailurePredictionInfo;
3549
3550 //
3551 // Do any media change detection work
3552 //
3553
3554 if ((fdoExtension->MediaChangeDetectionInfo != NULL) &&
3555 (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) {
3556
3557 ClassCheckMediaState(fdoExtension);
3558
3559 }
3560
3561 //
3562 // Do any failure prediction work
3563 //
3564 if ((info != NULL) && (info->Method != FailurePredictionNone)) {
3565
3566 ULONG countDown;
3567
3568 if (ClasspCanSendPollingIrp(fdoExtension)) {
3569
3570 //
3571 // Synchronization is not required here since the Interlocked
3572 // locked instruction guarantees atomicity. Other code that
3573 // resets CountDown uses InterlockedExchange which is also
3574 // atomic.
3575 //
3576 countDown = InterlockedDecrement((volatile LONG *)&info->CountDown);
3577 if (countDown == 0) {
3578
3579 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspTimerTick: Send FP irp for %p\n",
3580 DeviceObject));
3581
3582 if(info->WorkQueueItem == NULL) {
3583
3584 info->WorkQueueItem =
3585 IoAllocateWorkItem(fdoExtension->DeviceObject);
3586
3587 if(info->WorkQueueItem == NULL) {
3588
3589 //
3590 // Set the countdown to one minute in the future.
3591 // we'll try again then in the hopes there's more
3592 // free memory.
3593 //
3594
3595 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClassTimerTick: Couldn't allocate "
3596 "item - try again in one minute\n"));
3597 InterlockedExchange((volatile LONG *)&info->CountDown, 60);
3598
3599 } else {
3600
3601 //
3602 // Grab the remove lock so that removal will block
3603 // until the work item is done.
3604 //
3605
3606 ClassAcquireRemoveLock(fdoExtension->DeviceObject,
3607 info->WorkQueueItem);
3608
3609 IoQueueWorkItem(info->WorkQueueItem,
3610 ClasspFailurePredict,
3611 DelayedWorkQueue,
3612 info);
3613 }
3614
3615 } else {
3616
3617 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspTimerTick: Failure "
3618 "Prediction work item is "
3619 "already active for device %p\n",
3620 DeviceObject));
3621
3622 }
3623 } // end (countdown == 0)
3624
3625 } else {
3626 //
3627 // If device is sleeping then just rearm polling timer
3628 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
3629 DeviceObject));
3630 }
3631
3632 } // end failure prediction polling
3633
3634 //
3635 // Give driver a chance to do its own specific work
3636 //
3637
3638 if (commonExtension->DriverExtension->InitData.ClassTick != NULL) {
3639
3640 commonExtension->DriverExtension->InitData.ClassTick(DeviceObject);
3641
3642 } // end device specific tick handler
3643 } // end check for removed
3644
3645 #ifdef _MSC_VER
3646 #pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
3647 #endif
3648 ClassReleaseRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
3649 } // end ClasspTimerTick()
3650
3651 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
3652 BOOLEAN
ClasspUpdateTimerNoWakeTolerance(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)3653 ClasspUpdateTimerNoWakeTolerance(
3654 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
3655 )
3656 /*
3657 Routine Description:
3658
3659 Updates the no-wake timer's tolerance based on system state.
3660
3661 If the timer is not allocated, initialized, or enabled then this function
3662 does nothing.
3663
3664 If the timer is enabled but the no-wake tolerance has *not* changed from
3665 its previous value then this function does nothing.
3666
3667 If the timer is enabled and the no-wake tolerance has changed from its
3668 previous value then this function *will* set/reset the tick timer.
3669
3670 Arguments:
3671
3672 FdoExtension for the device that has the timer whose tolerance needs updating.
3673
3674 Returns:
3675
3676 TRUE if the timer was set/reset.
3677 FALSE if the timer was not set/reset.
3678
3679 */
3680 {
3681 PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
3682
3683 if (FdoExtension->CommonExtension.IsFdo) {
3684 fdoData = FdoExtension->PrivateFdoData;
3685 }
3686
3687 if (fdoData != NULL &&
3688 fdoData->TickTimer != NULL &&
3689 fdoData->TimerInitialized &&
3690 fdoData->TickTimerEnabled) {
3691
3692 LONGLONG noWakeTolerance = TICK_TIMER_DELAY_IN_MSEC * (10 * 1000);
3693
3694 //
3695 // Set the no-wake tolerance to "unlimited" if the conditions below
3696 // are met. An "unlimited" no-wake tolerance means that the timer
3697 // will *never* wake the processor if the processor is in a
3698 // low-power state.
3699 // 1. The screen is off.
3700 // 2. The class driver is *not* a consumer of the tick timer (ClassTick is NULL).
3701 // 3. This is a disk device.
3702 // Otherwise the tolerance is set to the normal, default tolerable delay.
3703 //
3704 if (ClasspScreenOff &&
3705 FdoExtension->CommonExtension.DriverExtension->InitData.ClassTick == NULL &&
3706 FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_DISK) {
3707 noWakeTolerance = EX_TIMER_UNLIMITED_TOLERANCE;
3708 }
3709
3710 //
3711 // The new tolerance is different from the current tolerance so we need
3712 // to set/reset the timer with the new tolerance value.
3713 //
3714 if (fdoData->CurrentNoWakeTolerance != noWakeTolerance) {
3715 EXT_SET_PARAMETERS parameters;
3716 LONGLONG period = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000); // Convert to units of 100ns.
3717 LONGLONG dueTime = period * (-1); // Negative sign indicates dueTime is relative.
3718
3719 ExInitializeSetTimerParameters(¶meters);
3720 parameters.NoWakeTolerance = noWakeTolerance;
3721 fdoData->CurrentNoWakeTolerance = noWakeTolerance;
3722
3723 ExSetTimer(fdoData->TickTimer,
3724 dueTime,
3725 period,
3726 ¶meters);
3727
3728 return TRUE;
3729 }
3730 }
3731
3732 return FALSE;
3733 }
3734 #endif
3735
3736 NTSTATUS
ClasspInitializeTimer(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)3737 ClasspInitializeTimer(
3738 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
3739 )
3740 /*
3741 Routine Description:
3742
3743 This routine will attempt to initialize the tick timer.
3744 The caller should call ClasspEnableTmer() to actually start the timer.
3745
3746 If the caller just needs to check if the timer is initialized, the caller
3747 should simply check FdoExtension->PrivateFdoData->TimerInitialized rather
3748 than call this function.
3749
3750 The caller should subsequently call ClasspDeleteTimer() when they are done
3751 with the timer.
3752
3753 Arguments:
3754
3755 FdoExtension
3756
3757 Return Value:
3758
3759 STATUS_SUCCESS if the timer is initialized (the timer may already have been
3760 initialized by a previous call).
3761 A non-success status if the timer is not initialized.
3762
3763 */
3764 {
3765 PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
3766
3767 if (FdoExtension->CommonExtension.IsFdo) {
3768 fdoData = FdoExtension->PrivateFdoData;
3769 }
3770
3771 if (fdoData == NULL) {
3772 return STATUS_UNSUCCESSFUL;
3773 }
3774
3775 if (fdoData->TimerInitialized == FALSE) {
3776 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
3777 NT_ASSERT(fdoData->TickTimer == NULL);
3778 //
3779 // The tick timer is a no-wake timer, which means it will not wake
3780 // the processor while the processor is in a low power state until
3781 // the timer's no-wake tolerance is reached.
3782 //
3783 fdoData->TickTimer = ExAllocateTimer(ClasspTimerTickEx, FdoExtension, EX_TIMER_NO_WAKE);
3784 if (fdoData->TickTimer == NULL) {
3785 return STATUS_INSUFFICIENT_RESOURCES;
3786 }
3787 #else
3788 KeInitializeDpc(&fdoData->TickTimerDpc, ClasspTimerTick, FdoExtension);
3789 KeInitializeTimer(&fdoData->TickTimer);
3790 #endif
3791 fdoData->TimerInitialized = TRUE;
3792 }
3793
3794 return STATUS_SUCCESS;
3795 }
3796
3797 VOID
ClasspDeleteTimer(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)3798 ClasspDeleteTimer(
3799 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
3800 )
3801 /*
3802 Routine Description:
3803
3804 This routine will attempt to de-initialize and free the tick timer.
3805 This routine should only be called after a successful call to
3806 ClasspInitializeTimer().
3807
3808 Arguments:
3809
3810 FdoExtension
3811
3812 Return Value:
3813
3814 None.
3815
3816 */
3817 {
3818 PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
3819
3820 if (FdoExtension->CommonExtension.IsFdo) {
3821 fdoData = FdoExtension->PrivateFdoData;
3822 if (fdoData != NULL) {
3823 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
3824 if (fdoData->TickTimer != NULL) {
3825 EXT_DELETE_PARAMETERS parameters;
3826 ExInitializeDeleteTimerParameters(¶meters);
3827 ExDeleteTimer(fdoData->TickTimer, TRUE, FALSE, ¶meters);
3828 fdoData->TickTimer = NULL;
3829 }
3830 #endif
3831 fdoData->TimerInitialized = FALSE;
3832 fdoData->TickTimerEnabled = FALSE;
3833 }
3834 }
3835 }
3836
3837 /*++////////////////////////////////////////////////////////////////////////////
3838
3839 ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented
3840
3841 Routine Description:
3842
3843 This routine will enable the tick timer. ClasspInitializeTimer() should
3844 first be called to initialize the timer. Use ClasspDisableTimer() to
3845 disable the timer and then call this function to re-enable it.
3846
3847 Arguments:
3848
3849 FdoExtension
3850
3851 Return Value:
3852
3853 None.
3854
3855 --*/
3856 VOID
ClasspEnableTimer(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)3857 ClasspEnableTimer(
3858 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
3859 )
3860 {
3861 PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
3862
3863 if (FdoExtension->CommonExtension.IsFdo) {
3864 fdoData = FdoExtension->PrivateFdoData;
3865 }
3866
3867 if (fdoData != NULL) {
3868 //
3869 // The timer should have already been initialized, but if that's not
3870 // the case it's not the end of the world. We can attempt to
3871 // initialize it now.
3872 //
3873 NT_ASSERT(fdoData->TimerInitialized);
3874 if (fdoData->TimerInitialized == FALSE) {
3875 NTSTATUS status;
3876 status = ClasspInitializeTimer(FdoExtension);
3877 if (NT_SUCCESS(status) == FALSE) {
3878 return;
3879 }
3880 }
3881
3882 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
3883 if (fdoData->TickTimer != NULL) {
3884 EXT_SET_PARAMETERS parameters;
3885 LONGLONG period = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000); // Convert to units of 100ns.
3886 LONGLONG dueTime = period * (-1); // Negative sign indicates dueTime is relative.
3887
3888 ExInitializeSetTimerParameters(¶meters);
3889
3890 //
3891 // Set the no-wake tolerance to "unlimited" if the conditions below
3892 // are met. An "unlimited" no-wake tolerance means that the timer
3893 // will *never* wake the processor if the processor is in a
3894 // low-power state.
3895 // 1. The screen is off.
3896 // 2. The class driver is *not* a consumer of the tick timer (ClassTick is NULL).
3897 // 3. This is a disk device.
3898 // Otherwise the tolerance is set to the normal tolerable delay.
3899 //
3900 if (ClasspScreenOff &&
3901 FdoExtension->CommonExtension.DriverExtension->InitData.ClassTick == NULL &&
3902 FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_DISK) {
3903 parameters.NoWakeTolerance = EX_TIMER_UNLIMITED_TOLERANCE;
3904 } else {
3905 parameters.NoWakeTolerance = TICK_TIMER_DELAY_IN_MSEC * (10 * 1000);
3906 }
3907
3908 fdoData->CurrentNoWakeTolerance = parameters.NoWakeTolerance;
3909
3910 ExSetTimer(fdoData->TickTimer,
3911 dueTime,
3912 period,
3913 ¶meters);
3914
3915 fdoData->TickTimerEnabled = TRUE;
3916 } else {
3917 NT_ASSERT(fdoData->TickTimer != NULL);
3918 }
3919 #else
3920 //
3921 // Start the periodic tick timer using a coalescable timer with some delay
3922 //
3923 {
3924 LARGE_INTEGER timeout;
3925 timeout.QuadPart = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000) * (-1);
3926 KeSetCoalescableTimer(&fdoData->TickTimer,
3927 timeout, TICK_TIMER_PERIOD_IN_MSEC, TICK_TIMER_DELAY_IN_MSEC,
3928 &fdoData->TickTimerDpc);
3929 fdoData->TickTimerEnabled = TRUE;
3930 }
3931 #endif
3932
3933 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspEnableTimer: Periodic tick timer enabled "
3934 "for device %p\n", FdoExtension->DeviceObject));
3935
3936 }
3937
3938 } // end ClasspEnableTimer()
3939
3940 /*++////////////////////////////////////////////////////////////////////////////
3941
3942 ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented
3943
3944 Routine Description:
3945
3946 This routine
3947
3948 Arguments:
3949
3950 FdoExtension
3951
3952 Return Value:
3953
3954 --*/
3955 VOID
ClasspDisableTimer(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)3956 ClasspDisableTimer(
3957 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
3958 )
3959 {
3960 PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
3961
3962 if (FdoExtension->CommonExtension.IsFdo) {
3963 fdoData = FdoExtension->PrivateFdoData;
3964 }
3965
3966 if (fdoData && fdoData->TimerInitialized == TRUE) {
3967
3968 //
3969 // we are only going to stop the actual timer in remove device routine
3970 // or when done transitioning to D3 (timer will be started again when
3971 // done transitioning to D0).
3972 //
3973 // it is the responsibility of the code within the timer routine to
3974 // check if the device is removed and not processing io for the final
3975 // call.
3976 // this keeps the code clean and prevents lots of bugs.
3977 //
3978 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
3979 NT_ASSERT(fdoData->TickTimer != NULL);
3980 ExCancelTimer(fdoData->TickTimer, NULL);
3981 #else
3982 KeCancelTimer(&fdoData->TickTimer);
3983 #endif
3984 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspDisableTimer: Periodic tick timer disabled "
3985 "for device %p\n", FdoExtension->DeviceObject));
3986 fdoData->TickTimerEnabled = FALSE;
3987
3988 } else {
3989
3990 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspDisableTimer: Timer never initialized\n"));
3991
3992 }
3993
3994 return;
3995 } // end ClasspDisableTimer()
3996
3997 /*++////////////////////////////////////////////////////////////////////////////
3998
3999 ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented
4000
4001 Routine Description:
4002
4003 This routine
4004
4005 Arguments:
4006
4007 DeviceObject - Device object
4008 Context - Context (PFAILURE_PREDICTION_INFO)
4009
4010 Return Value:
4011
4012 Note: this function can be called (via the workitem callback) after the paging device is shut down,
4013 so it must be PAGE LOCKED.
4014 --*/
4015 VOID
4016 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClasspFailurePredict(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)4017 ClasspFailurePredict(
4018 IN PDEVICE_OBJECT DeviceObject,
4019 IN PVOID Context
4020 )
4021 {
4022 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
4023 PIO_WORKITEM workItem;
4024 STORAGE_PREDICT_FAILURE checkFailure = {0};
4025 SCSI_ADDRESS scsiAddress = {0};
4026 PFAILURE_PREDICTION_INFO Info = (PFAILURE_PREDICTION_INFO)Context;
4027
4028 NTSTATUS status;
4029
4030 if (Info == NULL) {
4031 NT_ASSERT(Info != NULL);
4032 return;
4033 }
4034
4035 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClasspFailurePredict: Polling for failure\n"));
4036
4037 //
4038 // Mark the work item as inactive and reset the countdown timer. we
4039 // can't risk freeing the work item until we've released the remove-lock
4040 // though - if we do it might get reused as a tag before we can release
4041 // the lock.
4042 //
4043
4044 InterlockedExchange((volatile LONG *)&Info->CountDown, Info->Period);
4045 workItem = InterlockedExchangePointer((volatile PVOID *)&(Info->WorkQueueItem), NULL);
4046
4047 if (ClasspCanSendPollingIrp(fdoExtension)) {
4048
4049 KEVENT event;
4050 PDEVICE_OBJECT topOfStack;
4051 PIRP irp = NULL;
4052 IO_STATUS_BLOCK ioStatus;
4053 NTSTATUS activateStatus = STATUS_UNSUCCESSFUL;
4054
4055 //
4056 // Take an active reference on the device to ensure it is powered up
4057 // while we do the failure prediction query.
4058 //
4059 if (fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled) {
4060 activateStatus = ClasspPowerActivateDevice(DeviceObject);
4061 }
4062
4063 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
4064
4065 topOfStack = IoGetAttachedDeviceReference(DeviceObject);
4066
4067 //
4068 // Send down irp to see if drive is predicting failure
4069 //
4070
4071 irp = IoBuildDeviceIoControlRequest(
4072 IOCTL_STORAGE_PREDICT_FAILURE,
4073 topOfStack,
4074 NULL,
4075 0,
4076 &checkFailure,
4077 sizeof(STORAGE_PREDICT_FAILURE),
4078 FALSE,
4079 &event,
4080 &ioStatus);
4081
4082
4083 if (irp != NULL) {
4084
4085
4086 status = IoCallDriver(topOfStack, irp);
4087 if (status == STATUS_PENDING) {
4088 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
4089 status = ioStatus.Status;
4090 }
4091
4092
4093 } else {
4094 status = STATUS_INSUFFICIENT_RESOURCES;
4095 }
4096
4097 if (NT_SUCCESS(status) && (checkFailure.PredictFailure)) {
4098
4099 checkFailure.PredictFailure = 512;
4100
4101 //
4102 // Send down irp to get scsi address
4103 //
4104 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
4105
4106 RtlZeroMemory(&scsiAddress, sizeof(SCSI_ADDRESS));
4107 irp = IoBuildDeviceIoControlRequest(
4108 IOCTL_SCSI_GET_ADDRESS,
4109 topOfStack,
4110 NULL,
4111 0,
4112 &scsiAddress,
4113 sizeof(SCSI_ADDRESS),
4114 FALSE,
4115 &event,
4116 &ioStatus);
4117
4118 if (irp != NULL) {
4119
4120
4121 status = IoCallDriver(topOfStack, irp);
4122 if (status == STATUS_PENDING) {
4123 (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
4124 }
4125
4126 }
4127
4128 ClassNotifyFailurePredicted(fdoExtension,
4129 (PUCHAR)&checkFailure,
4130 sizeof(checkFailure),
4131 (BOOLEAN)(fdoExtension->FailurePredicted == FALSE),
4132 2,
4133 scsiAddress.PathId,
4134 scsiAddress.TargetId,
4135 scsiAddress.Lun);
4136
4137 fdoExtension->FailurePredicted = TRUE;
4138
4139 }
4140
4141 ObDereferenceObject(topOfStack);
4142
4143 //
4144 // Update the failure prediction query time and release the active
4145 // reference.
4146 //
4147
4148 KeQuerySystemTime(&(Info->LastFailurePredictionQueryTime));
4149
4150 if (NT_SUCCESS(activateStatus)) {
4151 ClasspPowerIdleDevice(DeviceObject);
4152 }
4153 }
4154
4155 ClassReleaseRemoveLock(DeviceObject, (PIRP) workItem);
4156 IoFreeWorkItem(workItem);
4157 return;
4158 } // end ClasspFailurePredict()
4159
4160 /*++////////////////////////////////////////////////////////////////////////////
4161
4162 ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented
4163
4164 Routine Description:
4165
4166 Arguments:
4167
4168 Return Value:
4169
4170 --*/
_IRQL_requires_max_(DISPATCH_LEVEL)4171 _IRQL_requires_max_(DISPATCH_LEVEL)
4172 VOID
4173 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
4174 ClassNotifyFailurePredicted(
4175 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
4176 _In_reads_bytes_(BufferSize) PUCHAR Buffer,
4177 _In_ ULONG BufferSize,
4178 _In_ BOOLEAN LogError,
4179 _In_ ULONG UniqueErrorValue,
4180 _In_ UCHAR PathId,
4181 _In_ UCHAR TargetId,
4182 _In_ UCHAR Lun
4183 )
4184 {
4185 PIO_ERROR_LOG_PACKET logEntry;
4186 EVENT_DESCRIPTOR eventDescriptor;
4187 PCLASS_DRIVER_EXTENSION driverExtension;
4188
4189 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension->DeviceObject));
4190
4191 //
4192 // Fire off a WMI event
4193 //
4194 ClassWmiFireEvent(FdoExtension->DeviceObject,
4195 (LPGUID)&StoragePredictFailureEventGuid,
4196 0,
4197 BufferSize,
4198 Buffer);
4199 //
4200 // Log an error into the eventlog
4201 //
4202
4203 if (LogError)
4204 {
4205 logEntry = IoAllocateErrorLogEntry(
4206 FdoExtension->DeviceObject,
4207 sizeof(IO_ERROR_LOG_PACKET) + (3 * sizeof(ULONG)));
4208
4209 if (logEntry != NULL)
4210 {
4211
4212 logEntry->FinalStatus = STATUS_SUCCESS;
4213 logEntry->ErrorCode = IO_WRN_FAILURE_PREDICTED;
4214 logEntry->SequenceNumber = 0;
4215 logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL;
4216 logEntry->IoControlCode = IOCTL_STORAGE_PREDICT_FAILURE;
4217 logEntry->RetryCount = 0;
4218 logEntry->UniqueErrorValue = UniqueErrorValue;
4219 logEntry->DumpDataSize = 3;
4220
4221 logEntry->DumpData[0] = PathId;
4222 logEntry->DumpData[1] = TargetId;
4223 logEntry->DumpData[2] = Lun;
4224
4225 //
4226 // Write the error log packet.
4227 //
4228
4229 IoWriteErrorLogEntry(logEntry);
4230 }
4231 }
4232
4233 //
4234 // Send ETW event if LogError is TRUE. ClassInterpretSenseInfo sets this
4235 // to FALSE. So if failure is predicted for the first time and UniqueErrorValue
4236 // is 4 (used by ClassInterpretSenseInfo) then send ETW event.
4237 //
4238
4239 if ((LogError == TRUE) ||
4240 ((FdoExtension->FailurePredicted == FALSE) && (UniqueErrorValue == 4))) {
4241
4242 #ifdef _MSC_VER
4243 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
4244 #endif
4245 driverExtension = IoGetDriverObjectExtension(FdoExtension->DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
4246
4247 if ((driverExtension != NULL) && (driverExtension->EtwHandle != 0)) {
4248 EventDescCreate(&eventDescriptor,
4249 1, // Id
4250 0, // Version
4251 0, // Channel
4252 0, // Level
4253 0, // Task
4254 0, // OpCode
4255 0); // Keyword
4256
4257 EtwWrite(driverExtension->EtwHandle,
4258 &eventDescriptor,
4259 NULL,
4260 0,
4261 NULL);
4262 }
4263 }
4264
4265 } // end ClassNotifyFailurePredicted()
4266
4267 /*++////////////////////////////////////////////////////////////////////////////
4268
4269 ClassSetFailurePredictionPoll()
4270
4271 Routine Description:
4272
4273 This routine enables polling for failure prediction, setting the timer
4274 to fire every N seconds as specified by the PollingPeriod.
4275
4276 Arguments:
4277
4278 FdoExtension - the device to setup failure prediction for.
4279
4280 FailurePredictionMethod - specific failure prediction method to use
4281 if set to FailurePredictionNone, will disable failure detection
4282
4283 PollingPeriod - if 0 then no change to current polling timer
4284
4285 Return Value:
4286
4287 NT Status
4288
4289 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)4290 _IRQL_requires_max_(PASSIVE_LEVEL)
4291 NTSTATUS
4292 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
4293 ClassSetFailurePredictionPoll(
4294 _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
4295 _In_ FAILURE_PREDICTION_METHOD FailurePredictionMethod,
4296 _In_ ULONG PollingPeriod
4297 )
4298 {
4299 PFAILURE_PREDICTION_INFO info;
4300 NTSTATUS status;
4301
4302 PAGED_CODE();
4303
4304 if (FdoExtension->FailurePredictionInfo == NULL) {
4305
4306 if (FailurePredictionMethod != FailurePredictionNone) {
4307
4308 info = ExAllocatePoolWithTag(NonPagedPoolNx,
4309 sizeof(FAILURE_PREDICTION_INFO),
4310 CLASS_TAG_FAILURE_PREDICT);
4311
4312 if (info == NULL) {
4313
4314 return STATUS_INSUFFICIENT_RESOURCES;
4315
4316 }
4317
4318 KeInitializeEvent(&info->Event, SynchronizationEvent, TRUE);
4319
4320 info->WorkQueueItem = NULL;
4321 info->Period = DEFAULT_FAILURE_PREDICTION_PERIOD;
4322
4323 KeQuerySystemTime(&(info->LastFailurePredictionQueryTime));
4324
4325 } else {
4326
4327 //
4328 // FaultPrediction has not been previously initialized, nor
4329 // is it being initialized now. No need to do anything.
4330 //
4331 return STATUS_SUCCESS;
4332
4333 }
4334
4335 FdoExtension->FailurePredictionInfo = info;
4336
4337 } else {
4338
4339 info = FdoExtension->FailurePredictionInfo;
4340
4341 }
4342
4343 /*
4344 * Make sure the user-mode thread is not suspended while we hold the synchronization event.
4345 */
4346 KeEnterCriticalRegion();
4347
4348 (VOID)KeWaitForSingleObject(&info->Event,
4349 UserRequest,
4350 KernelMode,
4351 FALSE,
4352 NULL);
4353
4354
4355 //
4356 // Reset polling period and counter. Setup failure detection type
4357 //
4358
4359 if (PollingPeriod != 0) {
4360
4361 InterlockedExchange((volatile LONG *)&info->Period, PollingPeriod);
4362 }
4363
4364 InterlockedExchange((volatile LONG *)&info->CountDown, info->Period);
4365
4366 info->Method = FailurePredictionMethod;
4367 if (FailurePredictionMethod != FailurePredictionNone) {
4368
4369 ClasspEnableTimer(FdoExtension);
4370
4371 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClassEnableFailurePredictPoll: Enabled for "
4372 "device %p\n", FdoExtension->DeviceObject));
4373
4374 } else {
4375
4376 ClasspDisableTimer(FdoExtension);
4377 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClassEnableFailurePredictPoll: Disabled for "
4378 "device %p\n", FdoExtension->DeviceObject));
4379 }
4380 status = STATUS_SUCCESS;
4381
4382
4383 KeSetEvent(&info->Event, IO_NO_INCREMENT, FALSE);
4384
4385 KeLeaveCriticalRegion();
4386
4387 return status;
4388 } // end ClassSetFailurePredictionPoll()
4389
4390 BOOLEAN
ClasspFailurePredictionPeriodMissed(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)4391 ClasspFailurePredictionPeriodMissed(
4392 _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
4393 )
4394 /*
4395 Routine Description:
4396 This routine can be used to determine if a failure prediction polling
4397 period has been missed. That is, the time since the last failure
4398 prediction IOCTL has been sent is greater than the failure prediction
4399 polling period. This can happen if failure prediction polling was
4400 disabled, such as when the device is in D3 or when the screen is off.
4401
4402 Parameters:
4403 FdoExtension - FDO extension. The caller should make sure the FDO
4404 extension is valid. The FailurePredictionInfo structure should also
4405 be valid and the failure prediction method should not be "none".
4406
4407 Returns:
4408 TRUE if there was one or more failure prediction polling periods that was
4409 missed.
4410 FALSE otherwise.
4411 */
4412 {
4413 LARGE_INTEGER currentTime;
4414 LARGE_INTEGER timeDifference;
4415 BOOLEAN missedPeriod = FALSE;
4416
4417 NT_ASSERT(FdoExtension);
4418 NT_ASSERT(FdoExtension->FailurePredictionInfo);
4419 NT_ASSERT(FdoExtension->FailurePredictionInfo->Method != FailurePredictionNone);
4420
4421 //
4422 // Find the difference between the last failure prediction
4423 // query and the current time and convert it to seconds.
4424 //
4425 KeQuerySystemTime(¤tTime);
4426 timeDifference.QuadPart = currentTime.QuadPart - FdoExtension->FailurePredictionInfo->LastFailurePredictionQueryTime.QuadPart;
4427 timeDifference.QuadPart /= (10LL * 1000LL * 1000LL);
4428
4429 if (timeDifference.QuadPart >= FdoExtension->FailurePredictionInfo->Period) {
4430 missedPeriod = TRUE;
4431 }
4432
4433 return missedPeriod;
4434 }
4435
4436
4437