xref: /reactos/drivers/storage/class/cdrom/sense.c (revision 3088717b)
1 /*--
2 
3 Copyright (C) Microsoft Corporation. All rights reserved.
4 
5 Module Name:
6 
7     sense.c
8 
9 Abstract:
10 
11     This file contains the methods needed to accurately
12     determine how to retry requests on CDROM device types.
13 
14 Environment:
15 
16     kernel mode only
17 
18 Revision History:
19 
20 --*/
21 
22 #include "stddef.h"
23 #include "string.h"
24 
25 #include "ntddk.h"
26 #include "ntddstor.h"
27 #include "cdrom.h"
28 #include "ntstrsafe.h"
29 
30 
31 #ifdef DEBUG_USE_WPP
32 #include "sense.tmh"
33 #endif
34 
35 // Forward declarations
36 VOID
37 SenseInfoInterpretRefineByIoControl(
38     _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
39     _In_      ULONG                     IoControlCode,
40     _In_      BOOLEAN                   OverrideVerifyVolume,
41     _Inout_   BOOLEAN*                  Retry,
42     _Inout_   NTSTATUS*                 Status
43     );
44 
45 
46 #ifdef ALLOC_PRAGMA
47 
48 #pragma alloc_text(PAGE, SenseInfoInterpretRefineByIoControl)
49 
50 #endif
51 
52 
53 //
54 // FROM CLASSPNP\CLASSP.H
55 //  Lots of retries of synchronized SCSI commands that devices may not
56 //  even support really slows down the system (especially while booting).
57 //  (Even GetDriveCapacity may be failed on purpose if an external disk is powered off).
58 //  If a disk cannot return a small initialization buffer at startup
59 //  in two attempts (with delay interval) then we cannot expect it to return
60 //  data consistently with four retries.
61 //  So don't set the retry counts as high here as for data SRBs.
62 //
63 //  If we find that these requests are failing consecutively,
64 //  despite the retry interval, on otherwise reliable media,
65 //  then we should either increase the retry interval for
66 //  that failure or (by all means) increase these retry counts as appropriate.
67 //
68 
69 #define TOTAL_COUNT_RETRY_DEFAULT       4
70 #define TOTAL_COUNT_RETRY_LOCK_MEDIA    1
71 #define TOTAL_COUNT_RETRY_MODESENSE     1
72 #define TOTAL_COUNT_RETRY_READ_CAPACITY 1
73 
74 
75 #define TOTAL_SECONDS_RETRY_TIME_WRITE          160
76 #define TOTAL_SECONDS_RETRY_TIME_MEDIUM_REMOVAL 120
77 
78 typedef struct _ERROR_LOG_CONTEXT {
79     BOOLEAN     LogError;
80     BOOLEAN     ErrorUnhandled;
81     NTSTATUS    ErrorCode;
82     ULONG       UniqueErrorValue;
83     ULONG       BadSector;
84 } ERROR_LOG_CONTEXT, *PERROR_LOG_CONTEXT;
85 
86 NTSTATUS
DeviceErrorHandlerForMmc(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ PSCSI_REQUEST_BLOCK Srb,_Inout_ PNTSTATUS Status,_Inout_ PBOOLEAN Retry)87 DeviceErrorHandlerForMmc(
88     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
89     _In_ PSCSI_REQUEST_BLOCK      Srb,
90     _Inout_ PNTSTATUS             Status,
91     _Inout_ PBOOLEAN              Retry
92     )
93 /*++
94 
95 Routine Description:
96 
97     this routine will be used for error handler for all MMC devices.
98     it's invoked by DeviceErrorHandler()that invoked by SenseInfoInterpret() or GESN
99 
100     This routine just checks for media change sense/asc/ascq and
101     also for other events, such as bus resets.  this is used to
102     determine if the device behaviour has changed, to allow for
103     read and write operations to be allowed and/or disallowed.
104 
105 Arguments:
106 
107     DeviceExtension - device context
108     Srb - SRB structure for analyze
109 
110 Return Value:
111 
112     NTSTATUS
113     Status -
114     Retry -
115 
116 --*/
117 {
118     BOOLEAN mediaChange = FALSE;
119     PCDB    cdb = (PCDB)Srb->Cdb;
120 
121     if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
122     {
123         PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
124 
125         // the following sense keys could indicate a change in capabilities.
126 
127         // we used to expect this to be serialized, and only hit from our
128         // own routine. we now allow some requests to continue during our
129         // processing of the capabilities update in order to allow
130         // IoReadPartitionTable() to succeed.
131         switch (senseBuffer->SenseKey & 0xf)
132         {
133 
134         case SCSI_SENSE_NOT_READY:
135         {
136             if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
137             {
138                 if (DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed)
139                 {
140                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
141                                "DeviceErrorHandlerForMmc: media removed, writes will be "
142                                "failed until new media detected\n"));
143                 }
144 
145                 // NOTE - REF #0002
146                 DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
147             }
148             else if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)
149             {
150                 if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_BECOMING_READY)
151                 {
152                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
153                                "DeviceErrorHandlerForMmc: media becoming ready, "
154                                "SHOULD notify shell of change time by sending "
155                                "GESN request immediately!\n"));
156                 }
157                 else if (((senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_OPERATION_IN_PROGRESS) ||
158                             (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS)
159                             ) &&
160                            ((Srb->Cdb[0] == SCSIOP_READ) ||
161                             (Srb->Cdb[0] == SCSIOP_READ6) ||
162                             (Srb->Cdb[0] == SCSIOP_READ_CAPACITY) ||
163                             (Srb->Cdb[0] == SCSIOP_READ_CD) ||
164                             (Srb->Cdb[0] == SCSIOP_READ_CD_MSF) ||
165                             (Srb->Cdb[0] == SCSIOP_READ_TOC) ||
166                             (Srb->Cdb[0] == SCSIOP_WRITE) ||
167                             (Srb->Cdb[0] == SCSIOP_WRITE6) ||
168                             (Srb->Cdb[0] == SCSIOP_READ_TRACK_INFORMATION) ||
169                             (Srb->Cdb[0] == SCSIOP_READ_DISK_INFORMATION)
170                             )
171                            )
172                 {
173                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
174                                "DeviceErrorHandlerForMmc: LONG_WRITE or "
175                                "OP_IN_PROGRESS for limited subset of cmds -- "
176                                "setting retry to TRUE\n"));
177                     *Retry = TRUE;
178                     *Status = STATUS_DEVICE_BUSY;
179                 }
180             }
181             break;
182         } // end SCSI_SENSE_NOT_READY
183 
184         case SCSI_SENSE_UNIT_ATTENTION:
185         {
186             switch (senseBuffer->AdditionalSenseCode)
187             {
188             case SCSI_ADSENSE_MEDIUM_CHANGED:
189             {
190                 // always update if the medium may have changed
191 
192                 // NOTE - REF #0002
193                 DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
194                 DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
195 
196                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
197                            "DeviceErrorHandlerForMmc: media change detected, need to "
198                            "update drive capabilities\n"));
199                 mediaChange = TRUE;
200                 break;
201 
202             } // end SCSI_ADSENSE_MEDIUM_CHANGED
203 
204             case SCSI_ADSENSE_BUS_RESET:
205             {
206                 // NOTE - REF #0002
207                 DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
208                 DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
209 
210                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
211                            "DeviceErrorHandlerForMmc: bus reset detected, need to "
212                            "update drive capabilities\n"));
213                 break;
214 
215             } // end SCSI_ADSENSE_BUS_RESET
216 
217             case SCSI_ADSENSE_OPERATOR_REQUEST:
218             {
219 
220                 BOOLEAN b = FALSE;
221 
222                 switch (senseBuffer->AdditionalSenseCodeQualifier)
223                 {
224                 case SCSI_SENSEQ_MEDIUM_REMOVAL:
225                 {
226                     // eject notification currently handled by classpnp
227                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
228                                "DeviceErrorHandlerForMmc: Eject requested by user\n"));
229                     *Retry = TRUE;
230                     *Status = STATUS_DEVICE_BUSY;
231                     break;
232                 }
233 
234                 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE:
235                     b = TRUE;
236                 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE:
237                 {
238                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
239                                "DeviceErrorHandlerForMmc: Write protect %s requested "
240                                "by user\n",
241                                (b ? "disable" : "enable")));
242                     *Retry = TRUE;
243                     *Status = STATUS_DEVICE_BUSY;
244                     // NOTE - REF #0002
245                     DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
246                     DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
247 
248                     break;
249                 }
250 
251                 } // end of AdditionalSenseCodeQualifier switch
252 
253 
254                 break;
255 
256             } // end SCSI_ADSENSE_OPERATOR_REQUEST
257 
258             default:
259             {
260                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
261                            "DeviceErrorHandlerForMmc: Unit attention %02x/%02x\n",
262                            senseBuffer->AdditionalSenseCode,
263                            senseBuffer->AdditionalSenseCodeQualifier));
264                 break;
265             }
266 
267             } // end of AdditionSenseCode switch
268             break;
269 
270         } // end SCSI_SENSE_UNIT_ATTENTION
271 
272         case SCSI_SENSE_ILLEGAL_REQUEST:
273         {
274             if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_WRITE_PROTECT)
275             {
276                 if (DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed)
277                 {
278                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
279                                "DeviceErrorHandlerForMmc: media was writable, but "
280                                "failed request with WRITE_PROTECT error...\n"));
281                 }
282                 // NOTE - REF #0002
283                 // do not update all the capabilities just because
284                 // we can't write to the disc.
285                 DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
286             }
287             break;
288         } // end SCSI_SENSE_ILLEGAL_REQUEST
289 
290         } // end of SenseKey switch
291 
292         // Check if we failed to set the DVD region key and send appropriate error
293         if (cdb->CDB16.OperationCode == SCSIOP_SEND_KEY)
294         {
295             if (cdb->SEND_KEY.KeyFormat == DvdSetRpcKey)
296             {
297                 if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
298                 {
299                     // media of appropriate region required
300                     *Status = STATUS_NO_MEDIA_IN_DEVICE;
301                     *Retry = FALSE;
302                 }
303                 else if ((senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
304                            (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_COPY_PROTECTION_FAILURE) &&
305                            (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT))
306                 {
307                     // media of appropriate region required
308                     *Status = STATUS_CSS_REGION_MISMATCH;
309                     *Retry = FALSE;
310                 }
311                 else if ((senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
312                            (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA) &&
313                            (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_INCOMPATIBLE_FORMAT))
314                 {
315                     // media of appropriate region required
316                     *Status = STATUS_CSS_REGION_MISMATCH;
317                     *Retry = FALSE;
318                 }
319             }
320         }
321     } // end of SRB_STATUS_AUTOSENSE_VALID
322 
323     // On media change, if device speed should be reset to default then
324     // queue a workitem to send the commands to the device. Do this on
325     // media arrival as some device will fail this command if no media
326     // is present. Ignore the fake media change from classpnp driver.
327     if ((mediaChange == TRUE) && (*Status != STATUS_MEDIA_CHANGED))
328     {
329         if (DeviceExtension->DeviceAdditionalData.RestoreDefaults == TRUE)
330         {
331             NTSTATUS                status = STATUS_SUCCESS;
332             WDF_OBJECT_ATTRIBUTES   attributes;
333             WDF_WORKITEM_CONFIG     workitemConfig;
334             WDFWORKITEM             workItem;
335 
336             WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
337             attributes.ParentObject = DeviceExtension->Device;
338 
339             WDF_WORKITEM_CONFIG_INIT(&workitemConfig,
340                                      DeviceRestoreDefaultSpeed);
341 
342             status = WdfWorkItemCreate(&workitemConfig,
343                                        &attributes,
344                                        &workItem);
345             if (!NT_SUCCESS(status))
346             {
347                 return STATUS_SUCCESS;
348             }
349 
350             WdfWorkItemEnqueue(workItem);
351 
352             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
353                         "DeviceErrorHandlerForMmc: Restore device default speed for %p\n",
354                         DeviceExtension->DeviceObject));
355         }
356     }
357     return STATUS_SUCCESS;
358 }
359 
360 NTSTATUS
DeviceErrorHandlerForHitachiGD2000(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ PSCSI_REQUEST_BLOCK Srb,_Inout_ PNTSTATUS Status,_Inout_ PBOOLEAN Retry)361 DeviceErrorHandlerForHitachiGD2000(
362     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
363     _In_ PSCSI_REQUEST_BLOCK      Srb,
364     _Inout_ PNTSTATUS             Status,
365     _Inout_ PBOOLEAN              Retry
366     )
367 /*++
368 
369 Routine Description:
370 
371    error handler for HITACHI CDR-1750S, CDR-3650/1650S
372 
373    This routine checks the type of error.  If the error suggests that the
374    drive has spun down and cannot reinitialize itself, send a
375    START_UNIT or READ to the device.  This will force the drive to spin
376    up.  This drive also loses the AGIDs it has granted when it spins down,
377    which may result in playback failure the first time around.
378 
379 Arguments:
380 
381     DeviceExtension - the device object.
382 
383     Srb - Supplies a pointer to the failing Srb.
384 
385     Status - return the final status for this command?
386 
387     Retry - return if the command should be retried.
388 
389 Return Value:
390 
391     None.
392 
393 --*/
394 {
395     PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
396 
397     if (!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
398     {
399         return STATUS_SUCCESS; //nobody cares about this return value yet.
400     }
401 
402     if (((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_HARDWARE_ERROR) &&
403         (senseBuffer->AdditionalSenseCode == 0x44))
404     {
405         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
406                     "DeviceErrorHandlerForHitachiGD2000 (%p) => Internal Target "
407                     "Failure Detected -- spinning up drive\n", DeviceExtension->Device));
408 
409         // the request should be retried because the device isn't ready
410         *Retry = TRUE;
411         *Status = STATUS_DEVICE_NOT_READY;
412 
413         // send a START_STOP unit to spin up the drive
414         // NOTE: this temporarily violates the StartIo serialization
415         //       mechanism, but the completion routine on this will NOT
416         //       call StartNextPacket(), so it's a temporary disruption
417         //       of the serialization only.
418         DeviceSendStartUnit(DeviceExtension->Device);
419     }
420 
421     return STATUS_SUCCESS;
422 }
423 
424 
425 VOID
SenseInfoRequestGetInformation(_In_ WDFREQUEST Request,_Out_ UCHAR * MajorFunctionCode,_Out_ ULONG * IoControlCode,_Out_ BOOLEAN * OverrideVerifyVolume,_Out_ ULONGLONG * Total100nsSinceFirstSend)426 SenseInfoRequestGetInformation(
427     _In_  WDFREQUEST  Request,
428     _Out_ UCHAR*      MajorFunctionCode,
429     _Out_ ULONG*      IoControlCode,
430     _Out_ BOOLEAN*    OverrideVerifyVolume,
431     _Out_ ULONGLONG*  Total100nsSinceFirstSend
432     )
433 {
434     PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
435 
436     *MajorFunctionCode = 0;
437     *IoControlCode = 0;
438     *OverrideVerifyVolume = FALSE;
439     *Total100nsSinceFirstSend = 0;
440 
441     if (requestContext->OriginalRequest != NULL)
442     {
443         PIO_STACK_LOCATION originalIrpStack = NULL;
444 
445         PIRP originalIrp = WdfRequestWdmGetIrp(requestContext->OriginalRequest);
446 
447         if (originalIrp != NULL)
448         {
449             originalIrpStack = IoGetCurrentIrpStackLocation(originalIrp);
450         }
451 
452         if (originalIrpStack != NULL)
453         {
454             *MajorFunctionCode = originalIrpStack->MajorFunction;
455 
456             if (*MajorFunctionCode == IRP_MJ_DEVICE_CONTROL)
457             {
458                 *IoControlCode = originalIrpStack->Parameters.DeviceIoControl.IoControlCode;
459             }
460 
461             *OverrideVerifyVolume = TEST_FLAG(originalIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
462         }
463     }
464 
465     // Calculate time past since the request was first time sent.
466     if (requestContext->TimeSentDownFirstTime.QuadPart > 0)
467     {
468         LARGE_INTEGER tmp;
469         KeQueryTickCount(&tmp);
470         tmp.QuadPart -= requestContext->TimeSentDownFirstTime.QuadPart;
471         tmp.QuadPart *= KeQueryTimeIncrement();
472         *Total100nsSinceFirstSend = tmp.QuadPart;
473     }
474     else
475     {
476         // set to -1 if field TimeSentDownFirstTime not set.
477         *Total100nsSinceFirstSend = (ULONGLONG) -1;
478     }
479 
480     return;
481 }
482 
483 BOOLEAN
484 SenseInfoInterpretByAdditionalSenseCode(
485     _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
486     _In_      PSCSI_REQUEST_BLOCK       Srb,
487     _In_      UCHAR                     AdditionalSenseCode,
488     _In_      UCHAR                     AdditionalSenseCodeQual,
489     _Inout_   NTSTATUS*                 Status,
490     _Inout_   BOOLEAN*                  Retry,
491     _Out_ _Deref_out_range_(0,100) ULONG*         RetryIntervalInSeconds,
492     _Inout_   PERROR_LOG_CONTEXT        LogContext
493     )
494 /*
495     This function will interpret error based on ASC/ASCQ.
496 
497     If the error code is not processed in this function, e.g. return value is TRUE,
498     caller needs to call SenseInfoInterpretBySenseKey() for further interpret.
499 */
500 {
501     BOOLEAN     needFurtherInterpret = TRUE;
502     PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
503 
504     // set default values for retry fields.
505     *Status = STATUS_IO_DEVICE_ERROR;
506     *Retry = TRUE;
507     *RetryIntervalInSeconds = 0;
508 
509     switch (AdditionalSenseCode)
510     {
511     case SCSI_ADSENSE_LUN_NOT_READY:
512         {
513             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
514                         "SenseInfoInterpretByAdditionalSenseCode: Lun not ready\n"));
515 
516             //
517             //  Many non-WHQL certified drives (mostly CD-RW) return
518             //  2/4/0 when they have no media instead of the obvious choice of:
519             //
520             //      SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
521             //
522             //  These drives should not pass WHQL certification due to this discrepency.
523             //
524             //  However, we have to retry on 2/4/0 (Not ready, LUN not ready, no info)
525             //  and also 3/2/0 (no seek complete).
526             //
527             //  These conditions occur when the shell tries to examine an
528             //  injected CD (e.g. for autoplay) before the CD is spun up.
529             //
530             //  The drive should be returning an ASCQ of SCSI_SENSEQ_BECOMING_READY
531             //  (0x01) in order to comply with WHQL standards.
532             //
533             //  The default retry timeout of one second is acceptable to balance
534             //  these discrepencies.  don't modify the status, though....
535             //
536 
537             switch (AdditionalSenseCodeQual)
538             {
539             case SCSI_SENSEQ_OPERATION_IN_PROGRESS:
540                 {
541                     DEVICE_EVENT_BECOMING_READY notReady = {0};
542 
543                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
544                                 "SenseInfoInterpretByAdditionalSenseCode: Operation In Progress\n"));
545 
546                     needFurtherInterpret = FALSE;
547 
548                     *Retry = TRUE;
549                     *RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
550                     *Status = STATUS_DEVICE_NOT_READY;
551 
552                     notReady.Version = 1;
553                     notReady.Reason = 2;
554                     notReady.Estimated100msToReady = *RetryIntervalInSeconds * 10;
555                     DeviceSendNotification(DeviceExtension,
556                                            &GUID_IO_DEVICE_BECOMING_READY,
557                                            sizeof(DEVICE_EVENT_BECOMING_READY),
558                                            &notReady);
559 
560                     break;
561                 }
562 
563             case SCSI_SENSEQ_BECOMING_READY:
564                 {
565                     DEVICE_EVENT_BECOMING_READY notReady = {0};
566 
567                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
568                                 "SenseInfoInterpretByAdditionalSenseCode: In process of becoming ready\n"));
569 
570                     needFurtherInterpret = FALSE;
571 
572                     *Retry = TRUE;
573                     *RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
574                     *Status = STATUS_DEVICE_NOT_READY;
575 
576                     notReady.Version = 1;
577                     notReady.Reason = 1;
578                     notReady.Estimated100msToReady = *RetryIntervalInSeconds * 10;
579                     DeviceSendNotification(DeviceExtension,
580                                            &GUID_IO_DEVICE_BECOMING_READY,
581                                            sizeof(DEVICE_EVENT_BECOMING_READY),
582                                            &notReady);
583 
584                     break;
585                 }
586 
587             case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS:
588                 {
589                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
590                                 "SenseInfoInterpretByAdditionalSenseCode: Long write in progress\n"));
591 
592                     needFurtherInterpret = FALSE;
593 
594                     // This has been seen as a transcient failure on some drives
595                     *Status = STATUS_DEVICE_NOT_READY;
596                     *Retry = TRUE;
597                     // Set retry interval to be 0 as the drive can be ready at anytime.
598                     *RetryIntervalInSeconds = 0;
599 
600                     break;
601                 }
602 
603             case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
604                 {
605                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
606                                 "SenseInfoInterpretByAdditionalSenseCode: Manual intervention required\n"));
607 
608                     needFurtherInterpret = FALSE;
609 
610                     *Status = STATUS_NO_MEDIA_IN_DEVICE;
611                     *Retry = FALSE;
612                     *RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
613 
614                     break;
615                 }
616 
617             case SCSI_SENSEQ_FORMAT_IN_PROGRESS:
618                 {
619                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
620                                 "SenseInfoInterpretByAdditionalSenseCode: Format in progress\n"));
621 
622                     needFurtherInterpret = FALSE;
623 
624                     *Status = STATUS_DEVICE_NOT_READY;
625                     *Retry = FALSE;
626                     *RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
627 
628                     break;
629                 }
630 
631             case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE:
632             case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
633             default:
634                 {
635                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
636                                 "SenseInfoInterpretByAdditionalSenseCode: Initializing command required\n"));
637 
638                     needFurtherInterpret = FALSE;
639 
640                     *Status = STATUS_DEVICE_NOT_READY;
641                     *Retry = TRUE;
642                     *RetryIntervalInSeconds = 0;
643 
644                     // This sense code/additional sense code combination may indicate
645                     // that the device needs to be started.
646                     if (TEST_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT) &&
647                         !TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY))
648                     {
649                         DeviceSendStartUnit(DeviceExtension->Device);
650                     }
651 
652                     break;
653                 }
654             } // end switch (AdditionalSenseCodeQual)
655             break;
656 
657         } // end case (SCSI_ADSENSE_LUN_NOT_READY)
658 
659     case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE:
660         {
661             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
662                         "SenseInfoInterpretByAdditionalSenseCode: No Media in device.\n"));
663 
664             needFurtherInterpret = FALSE;
665 
666             *Status = STATUS_NO_MEDIA_IN_DEVICE;
667             *Retry = FALSE;
668 
669             if (AdditionalSenseCodeQual == 0xCC)
670             {
671                 //  The IMAPIv1 filter returns this ASCQ value while it is burning CD media, and we want
672                 //  to preserve this functionality for compatibility reasons.
673                 //  We want to indicate that the media is not present to most applications;
674                 //  but RSM has to know that the media is still in the drive (i.e. the drive is not free).
675                 DeviceSetMediaChangeStateEx(DeviceExtension, MediaUnavailable, NULL);
676             }
677             else
678             {
679                 DeviceSetMediaChangeStateEx(DeviceExtension, MediaNotPresent, NULL);
680             }
681 
682             break;
683         } // end case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
684 
685     case SCSI_ADSENSE_INVALID_MEDIA:
686         {
687         switch (AdditionalSenseCodeQual)
688         {
689 
690         case SCSI_SENSEQ_UNKNOWN_FORMAT:
691             {
692                 needFurtherInterpret = FALSE;
693 
694                 // Log error only if this is a paging request
695                 *Status = STATUS_UNRECOGNIZED_MEDIA;
696                 *Retry = FALSE;
697 
698                 LogContext->LogError = TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING);
699                 LogContext->UniqueErrorValue = 256;
700                 LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
701 
702                 break;
703             }
704 
705         case SCSI_SENSEQ_INCOMPATIBLE_FORMAT:
706             {
707                 needFurtherInterpret = FALSE;
708 
709                 *Status = STATUS_UNRECOGNIZED_MEDIA;
710                 *Retry = FALSE;
711 
712                 LogContext->LogError = FALSE;
713 
714                 break;
715             }
716 
717         case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED:
718             {
719                 needFurtherInterpret = FALSE;
720 
721                 *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
722                 *Retry = FALSE;
723 
724                 LogContext->LogError = FALSE;
725                 LogContext->UniqueErrorValue = 256;
726                 LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
727                 break;
728             }
729 
730         default:
731             {
732                 needFurtherInterpret = TRUE;
733                 break;
734             }
735         } // end case AdditionalSenseCodeQual
736 
737         break;
738         } // end case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
739 
740     case SCSI_ADSENSE_NO_SEEK_COMPLETE:
741         {
742         switch (AdditionalSenseCodeQual)
743         {
744 
745         case 0x00:
746             {
747                 needFurtherInterpret = FALSE;
748 
749                 *Status = STATUS_DEVICE_DATA_ERROR;
750                 *Retry = TRUE;
751                 *RetryIntervalInSeconds = 0;
752                 LogContext->LogError = TRUE;
753                 LogContext->UniqueErrorValue = 256;
754                 LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
755                 break;
756             }
757 
758         default:
759             {
760                 needFurtherInterpret = TRUE;
761                 break;
762             }
763         }
764 
765         break;
766         } // end case SCSI_ADSENSE_NO_SEEK_COMPLETE
767 
768     case SCSI_ADSENSE_LUN_COMMUNICATION:
769         {
770         switch (AdditionalSenseCodeQual)
771         {
772 
773         case SCSI_SESNEQ_COMM_CRC_ERROR:
774             {
775                 needFurtherInterpret = FALSE;
776 
777                 *Status = STATUS_IO_DEVICE_ERROR;
778                 *Retry = TRUE;
779                 *RetryIntervalInSeconds = 1;
780                 LogContext->LogError = TRUE;
781                 LogContext->UniqueErrorValue = 257;
782                 LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
783                 break;
784             }
785 
786         default:
787             {
788                 needFurtherInterpret = TRUE;
789                 break;
790             }
791         }
792 
793         break;
794         } // end case SCSI_ADSENSE_LUN_COMMUNICATION
795 
796     case SCSI_ADSENSE_ILLEGAL_BLOCK:
797         {
798             needFurtherInterpret = FALSE;
799 
800             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
801                         "SenseInfoInterpretByAdditionalSenseCode: Illegal block address\n"));
802             *Status = STATUS_NONEXISTENT_SECTOR;
803             *Retry = FALSE;
804             break;
805         }
806 
807     case SCSI_ADSENSE_INVALID_LUN:
808         {
809             needFurtherInterpret = FALSE;
810 
811             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
812                         "SenseInfoInterpretByAdditionalSenseCode: Invalid LUN\n"));
813             *Status = STATUS_NO_SUCH_DEVICE;
814             *Retry = FALSE;
815             break;
816         }
817 
818     case SCSI_ADSENSE_COPY_PROTECTION_FAILURE:
819         {
820             needFurtherInterpret = FALSE;
821 
822             *Retry = FALSE;
823 
824             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
825                         "SenseInfoInterpretByAdditionalSenseCode: Key - Copy protection failure\n"));
826 
827             switch (AdditionalSenseCodeQual)
828             {
829             case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
830                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
831                             "SenseInfoInterpretByAdditionalSenseCode: Authentication failure\n"));
832                 *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
833                 break;
834             case SCSI_SENSEQ_KEY_NOT_PRESENT:
835                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
836                             "SenseInfoInterpretByAdditionalSenseCode: Key not present\n"));
837                 *Status = STATUS_CSS_KEY_NOT_PRESENT;
838                 break;
839             case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
840                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
841                             "SenseInfoInterpretByAdditionalSenseCode: Key not established\n"));
842                 *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
843                 break;
844             case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
845                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
846                             "SenseInfoInterpretByAdditionalSenseCode: Read of scrambled sector w/o authentication\n"));
847                 *Status = STATUS_CSS_SCRAMBLED_SECTOR;
848                 break;
849             case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
850                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
851                             "SenseInfoInterpretByAdditionalSenseCode: Media region does not logical unit region\n"));
852                 *Status = STATUS_CSS_REGION_MISMATCH;
853                 break;
854             case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
855                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
856                             "SenseInfoInterpretByAdditionalSenseCode: Region set error -- region may be permanent\n"));
857                 *Status = STATUS_CSS_RESETS_EXHAUSTED;
858                 break;
859 
860             default:
861                 *Status = STATUS_COPY_PROTECTION_FAILURE;
862                 break;
863             } // end switch of ASCQ for COPY_PROTECTION_FAILURE
864 
865             break;
866         }
867 
868     case SCSI_ADSENSE_INVALID_CDB:
869         {
870             needFurtherInterpret = FALSE;
871 
872             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
873                         "SenseInfoInterpretByAdditionalSenseCode: Key - Invalid CDB\n"));
874 
875             *Status = STATUS_INVALID_DEVICE_REQUEST;
876             *Retry = FALSE;
877 
878             // Note: the retry interval is not typically used.
879             // it is set here only because a ClassErrorHandler
880             // cannot set the RetryIntervalInSeconds, and the error may
881             // require a few commands to be sent to clear whatever
882             // caused this condition (i.e. disk clears the write
883             // cache, requiring at least two commands)
884             //
885             // hopefully, this shortcoming can be changed for blackcomb.
886             *RetryIntervalInSeconds = 3;
887 
888             break;
889         }
890 
891     case SCSI_ADSENSE_MEDIUM_CHANGED:
892         {
893             needFurtherInterpret = FALSE;
894             *RetryIntervalInSeconds = 0;
895 
896             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
897                         "SenseInfoInterpretByAdditionalSenseCode: Media changed\n"));
898 
899             DeviceSetMediaChangeStateEx(DeviceExtension, MediaPresent, NULL);
900 
901             // special process for Media Change
902             if (IsVolumeMounted(DeviceExtension->DeviceObject))
903             {
904                 // Set bit to indicate that media may have changed and volume needs verification.
905                 SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
906 
907                 *Status = STATUS_VERIFY_REQUIRED;
908                 *Retry = FALSE;
909             }
910             else
911             {
912                 *Status = STATUS_IO_DEVICE_ERROR;
913                 *Retry = TRUE;
914             }
915             break;
916         }
917 
918     case SCSI_ADSENSE_OPERATOR_REQUEST:
919         {
920             switch (AdditionalSenseCodeQual)
921             {
922             case SCSI_SENSEQ_MEDIUM_REMOVAL:
923                 {
924                     needFurtherInterpret = FALSE;
925                     *RetryIntervalInSeconds = 0;
926 
927                     InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
928 
929                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
930                         "SenseInfoInterpretByAdditionalSenseCode: Ejection request received!\n"));
931                     //Send eject notification.
932                     DeviceSendNotification(DeviceExtension,
933                                            &GUID_IO_MEDIA_EJECT_REQUEST,
934                                            0,
935                                            NULL);
936                     // special process for Media Change
937                     if (IsVolumeMounted(DeviceExtension->DeviceObject))
938                     {
939                         // Set bit to indicate that media may have changed and volume needs verification.
940                         SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
941 
942                         *Status = STATUS_VERIFY_REQUIRED;
943                         *Retry = FALSE;
944                     }
945                     else
946                     {
947                         *Status = STATUS_IO_DEVICE_ERROR;
948                         *Retry = TRUE;
949                     }
950                     break;
951                 }
952             default:
953                 {
954                     needFurtherInterpret = TRUE;
955                     break;
956                 }
957             }
958             break;
959         }
960 
961     case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED:
962         {
963             needFurtherInterpret = FALSE;
964             *RetryIntervalInSeconds = 5;
965 
966             InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
967 
968             // Device information has changed, we need to rescan the
969             // bus for changed information such as the capacity.
970             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
971                         "SenseInfoInterpretByAdditionalSenseCode: Device information changed. Invalidate the bus\n"));
972 
973             IoInvalidateDeviceRelations(DeviceExtension->LowerPdo, BusRelations);
974 
975             // special process for Media Change
976             if (IsVolumeMounted(DeviceExtension->DeviceObject))
977             {
978                 // Set bit to indicate that media may have changed and volume needs verification.
979                 SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
980 
981                 *Status = STATUS_VERIFY_REQUIRED;
982                 *Retry = FALSE;
983             }
984             else
985             {
986                 *Status = STATUS_IO_DEVICE_ERROR;
987                 *Retry = TRUE;
988             }
989             break;
990         } //end Case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED
991 
992 
993     case SCSI_ADSENSE_REC_DATA_NOECC:
994     case SCSI_ADSENSE_REC_DATA_ECC:
995         {
996             needFurtherInterpret = FALSE;
997 
998             *Status = STATUS_SUCCESS;
999             *Retry = FALSE;
1000             LogContext->LogError = TRUE;
1001             LogContext->UniqueErrorValue = 258;
1002             LogContext->ErrorCode = IO_RECOVERED_VIA_ECC;
1003 
1004             if (senseBuffer->IncorrectLength)
1005             {
1006                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
1007                             "SenseInfoInterpretByAdditionalSenseCode: Incorrect length detected.\n"));
1008                 *Status = STATUS_INVALID_BLOCK_LENGTH ;
1009             }
1010             break;
1011         }
1012 
1013     case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED:
1014         {
1015             UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
1016 
1017             *((PULONG)wmiEventData) = sizeof(UCHAR);
1018             wmiEventData[sizeof(ULONG)] = AdditionalSenseCodeQual;
1019 
1020             needFurtherInterpret = FALSE;
1021 
1022             // Don't log another eventlog if we have already logged once
1023             // NOTE: this should have been interlocked, but the structure
1024             //       was publicly defined to use a BOOLEAN (char).  Since
1025             //       media only reports these errors once per X minutes,
1026             //       the potential race condition is nearly non-existant.
1027             //       the worst case is duplicate log entries, so ignore.
1028 
1029             *Status = STATUS_SUCCESS;
1030             *Retry = FALSE;
1031             LogContext->UniqueErrorValue = 258;
1032             LogContext->LogError = TRUE;
1033             LogContext->ErrorCode = IO_WRN_FAILURE_PREDICTED;
1034 
1035             if (senseBuffer->IncorrectLength)
1036             {
1037                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
1038                             "SenseInfoInterpretByAdditionalSenseCode: Incorrect length detected.\n"));
1039                 *Status = STATUS_INVALID_BLOCK_LENGTH ;
1040             }
1041             break;
1042         }
1043 
1044     case 0x57:
1045         {
1046             // UNABLE_TO_RECOVER_TABLE_OF_CONTENTS
1047             // the Matshita CR-585 returns this for all read commands
1048             // on blank CD-R and CD-RW media, and we need to handle
1049             // this for READ_CD detection ability.
1050             switch (AdditionalSenseCodeQual)
1051             {
1052             case 0x00:
1053                 {
1054                     needFurtherInterpret = FALSE;
1055 
1056                     *Status = STATUS_UNRECOGNIZED_MEDIA;
1057                     *Retry = FALSE;
1058                     break;
1059                 }
1060             default:
1061                 {
1062                     needFurtherInterpret = TRUE;
1063                     break;
1064                 }
1065             }
1066             break;
1067         }   //end case Matshita specific error 0x57
1068 
1069     default:
1070         {
1071             needFurtherInterpret = TRUE;
1072             break;
1073         }
1074     }
1075 
1076     return needFurtherInterpret;
1077 }
1078 
1079 VOID
1080 SenseInfoInterpretBySenseKey(
1081     _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
1082     _In_      PSENSE_DATA               SenseData,
1083     _In_      UCHAR                     SenseKey,
1084     _Inout_   NTSTATUS*                 Status,
1085     _Inout_   BOOLEAN*                  Retry,
1086     _Out_ _Deref_out_range_(0,100) ULONG*         RetryIntervalInSeconds,
1087     _Inout_   PERROR_LOG_CONTEXT        LogContext
1088     )
1089 {
1090     // set default values for retry fields.
1091     *Status = STATUS_IO_DEVICE_ERROR;
1092     *Retry = TRUE;
1093     *RetryIntervalInSeconds = 0;
1094 
1095     switch (SenseKey)
1096     {
1097     case SCSI_SENSE_NOT_READY:
1098         {
1099             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1100                         "SenseInfoInterpretBySenseKey: Key - Not Ready (bad block)\n"));
1101 
1102             *Status = STATUS_DEVICE_NOT_READY;
1103             *Retry = TRUE;
1104 
1105             // for unprocessed "not ready" codes, retry the command immediately.
1106             *RetryIntervalInSeconds = 0;
1107             break;
1108         }
1109 
1110     case SCSI_SENSE_DATA_PROTECT:
1111         {
1112             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
1113                         "SenseInfoInterpretBySenseKey: Key - Media write protected\n"));
1114             *Status = STATUS_MEDIA_WRITE_PROTECTED;
1115             *Retry = FALSE;
1116             break;
1117         }
1118 
1119     case SCSI_SENSE_MEDIUM_ERROR:
1120         {
1121             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1122                         "SenseInfoInterpretBySenseKey: Key - Medium Error (bad block)\n"));
1123 
1124             *Status = STATUS_DEVICE_DATA_ERROR;
1125             *Retry = FALSE;
1126             LogContext->LogError = TRUE;
1127             LogContext->UniqueErrorValue = 256;
1128             LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
1129 
1130             break;
1131         } // end SCSI_SENSE_MEDIUM_ERROR
1132 
1133     case SCSI_SENSE_HARDWARE_ERROR:
1134         {
1135             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1136                         "SenseInfoInterpretBySenseKey: Key - Hardware error\n"));
1137 
1138             *Status = STATUS_IO_DEVICE_ERROR;
1139             *Retry = TRUE;
1140             LogContext->LogError = TRUE;
1141             LogContext->UniqueErrorValue = 257;
1142             LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
1143 
1144             break;
1145         } // end SCSI_SENSE_HARDWARE_ERROR
1146 
1147     case SCSI_SENSE_ILLEGAL_REQUEST:
1148         {
1149             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1150                         "SenseInfoInterpretBySenseKey: Key - Illegal SCSI request\n"));
1151             *Status = STATUS_INVALID_DEVICE_REQUEST;
1152             *Retry = FALSE;
1153 
1154             break;
1155         } // end SCSI_SENSE_ILLEGAL_REQUEST
1156 
1157     case SCSI_SENSE_UNIT_ATTENTION:
1158         {
1159             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1160                         "SenseInfoInterpretBySenseKey: Key - Unit Attention\n"));
1161 
1162             // A media change may have occured so increment the change
1163             // count for the physical device
1164             InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
1165 
1166             if (IsVolumeMounted(DeviceExtension->DeviceObject))
1167             {
1168                 // Set bit to indicate that media may have changed
1169                 // and volume needs verification.
1170                 SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
1171 
1172                 *Status = STATUS_VERIFY_REQUIRED;
1173                 *Retry = FALSE;
1174             }
1175             else
1176             {
1177                 *Status = STATUS_IO_DEVICE_ERROR;
1178                 *Retry = TRUE;
1179             }
1180 
1181             break;
1182 
1183         } // end SCSI_SENSE_UNIT_ATTENTION
1184 
1185     case SCSI_SENSE_ABORTED_COMMAND:
1186         {
1187             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1188                         "SenseInfoInterpretBySenseKey: Command aborted\n"));
1189             *Status = STATUS_IO_DEVICE_ERROR;
1190             *Retry = TRUE;
1191             *RetryIntervalInSeconds = 1;
1192             break;
1193         } // end SCSI_SENSE_ABORTED_COMMAND
1194 
1195     case SCSI_SENSE_BLANK_CHECK:
1196         {
1197             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
1198                         "SenseInfoInterpretBySenseKey: Media blank check\n"));
1199             *Retry = FALSE;
1200             *Status = STATUS_NO_DATA_DETECTED;
1201             break;
1202         } // end SCSI_SENSE_BLANK_CHECK
1203 
1204     case SCSI_SENSE_RECOVERED_ERROR:
1205         {
1206             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
1207                         "SenseInfoInterpretBySenseKey: Recovered error\n"));
1208             *Status = STATUS_SUCCESS;
1209             *Retry = FALSE;
1210             LogContext->LogError = TRUE;
1211             LogContext->UniqueErrorValue = 258;
1212             LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
1213 
1214             if (SenseData->IncorrectLength)
1215             {
1216                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
1217                             "SenseInfoInterpretBySenseKey: Incorrect length detected.\n"));
1218                 *Status = STATUS_INVALID_BLOCK_LENGTH ;
1219             }
1220 
1221             break;
1222         } // end SCSI_SENSE_RECOVERED_ERROR
1223 
1224     case SCSI_SENSE_NO_SENSE:
1225         {
1226             // Check other indicators.
1227             if (SenseData->IncorrectLength)
1228             {
1229                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
1230                             "SenseInfoInterpretBySenseKey: Incorrect length detected.\n"));
1231                 *Status = STATUS_INVALID_BLOCK_LENGTH ;
1232                 *Retry   = FALSE;
1233             }
1234             else
1235             {
1236                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
1237                             "SenseInfoInterpretBySenseKey: No specific sense key\n"));
1238                 *Status = STATUS_IO_DEVICE_ERROR;
1239                 *Retry = TRUE;
1240             }
1241 
1242             break;
1243         } // end SCSI_SENSE_NO_SENSE
1244 
1245     default:
1246         {
1247             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
1248                         "SenseInfoInterpretBySenseKey: Unrecognized sense code\n"));
1249             *Status = STATUS_IO_DEVICE_ERROR;
1250             *Retry = TRUE;
1251             *RetryIntervalInSeconds = 0;
1252 
1253             break;
1254         }
1255 
1256     } // end switch (SenseKey)
1257 
1258     return;
1259 }
1260 
1261 VOID
1262 SenseInfoInterpretBySrbStatus(
1263     _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
1264     _In_      PSCSI_REQUEST_BLOCK       Srb,
1265     _In_ ULONG                          RetriedCount,
1266     _Inout_   NTSTATUS*                 Status,
1267     _Inout_   BOOLEAN*                  Retry,
1268     _Out_ _Deref_out_range_(0,100) ULONG*         RetryIntervalInSeconds,
1269     _Inout_   PERROR_LOG_CONTEXT        LogContext
1270     )
1271 {
1272     BOOLEAN incrementErrorCount = FALSE;
1273 
1274     // set default values for retry fields.
1275     *Status = STATUS_IO_DEVICE_ERROR;
1276     *Retry = TRUE;
1277     *RetryIntervalInSeconds = 0;
1278 
1279     switch (SRB_STATUS(Srb->SrbStatus))
1280     {
1281     case SRB_STATUS_INVALID_LUN:
1282     case SRB_STATUS_INVALID_TARGET_ID:
1283     case SRB_STATUS_NO_DEVICE:
1284     case SRB_STATUS_NO_HBA:
1285     case SRB_STATUS_INVALID_PATH_ID:
1286     {
1287         *Status = STATUS_NO_SUCH_DEVICE;
1288         *Retry = FALSE;
1289         break;
1290     }
1291 
1292     case SRB_STATUS_COMMAND_TIMEOUT:
1293     case SRB_STATUS_TIMEOUT:
1294     {
1295         // Update the error count for the device.
1296         *Status = STATUS_IO_TIMEOUT;
1297         *Retry = TRUE;
1298         *RetryIntervalInSeconds = 0;
1299         incrementErrorCount = TRUE;
1300         break;
1301     }
1302 
1303     case SRB_STATUS_ABORTED:
1304     {
1305         // Update the error count for the device.
1306         *Status = STATUS_IO_TIMEOUT;
1307         *Retry = TRUE;
1308         *RetryIntervalInSeconds = 1;
1309         incrementErrorCount = TRUE;
1310         break;
1311     }
1312 
1313     case SRB_STATUS_SELECTION_TIMEOUT:
1314     {
1315         *Status = STATUS_DEVICE_NOT_CONNECTED;
1316         *Retry = FALSE;
1317         *RetryIntervalInSeconds = 2;
1318         LogContext->LogError = TRUE;
1319         LogContext->ErrorCode = IO_ERR_NOT_READY;
1320         LogContext->UniqueErrorValue = 260;
1321         break;
1322     }
1323 
1324     case SRB_STATUS_DATA_OVERRUN:
1325     {
1326         *Status = STATUS_DATA_OVERRUN;
1327         *Retry = FALSE;
1328         break;
1329     }
1330 
1331     case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
1332     {
1333         // Update the error count for the device.
1334         incrementErrorCount = TRUE;
1335         *Status = STATUS_IO_DEVICE_ERROR;
1336 
1337         // If there was phase sequence error then limit the number of retries.
1338         *Retry = (RetriedCount <= 1);
1339 
1340         break;
1341     }
1342 
1343     case SRB_STATUS_REQUEST_FLUSHED:
1344     {
1345         // If the status needs verification bit is set.  Then set
1346         // the status to need verification and no retry; otherwise,
1347         // just retry the request.
1348         if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME))
1349         {
1350             *Status = STATUS_VERIFY_REQUIRED;
1351             *Retry = FALSE;
1352         }
1353         else
1354         {
1355             *Status = STATUS_IO_DEVICE_ERROR;
1356             *Retry = TRUE;
1357         }
1358 
1359         break;
1360     }
1361 
1362     case SRB_STATUS_INVALID_REQUEST:
1363     {
1364         *Status = STATUS_INVALID_DEVICE_REQUEST;
1365         *Retry = FALSE;
1366         break;
1367     }
1368 
1369     case SRB_STATUS_UNEXPECTED_BUS_FREE:
1370     case SRB_STATUS_PARITY_ERROR:
1371         // Update the error count for the device and fall through to below
1372         incrementErrorCount = TRUE;
1373 
1374     case SRB_STATUS_BUS_RESET:
1375     {
1376         *Status = STATUS_IO_DEVICE_ERROR;
1377         *Retry = TRUE;
1378         break;
1379     }
1380 
1381     case SRB_STATUS_ERROR:
1382     {
1383         *Status = STATUS_IO_DEVICE_ERROR;
1384         *Retry = TRUE;
1385 
1386         if (Srb->ScsiStatus == 0)
1387         {
1388             // This is some strange return code.  Update the error
1389             // count for the device.
1390             incrementErrorCount = TRUE;
1391         }
1392 
1393         if (Srb->ScsiStatus == SCSISTAT_BUSY)
1394         {
1395             *Status = STATUS_DEVICE_NOT_READY;
1396         }
1397 
1398         break;
1399     }
1400 
1401     default:
1402     {
1403         *Status = STATUS_IO_DEVICE_ERROR;
1404         *Retry = TRUE;
1405         LogContext->LogError = TRUE;
1406         LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
1407         LogContext->UniqueErrorValue = 259;
1408         LogContext->ErrorUnhandled = TRUE;
1409         break;
1410     }
1411 
1412     } //end of (SRB_STATUS(Srb->SrbStatus))
1413 
1414     if (incrementErrorCount)
1415     {
1416         // if any error count occurred, delay the retry of this io by
1417         // at least one second, if caller supports it.
1418         if (*RetryIntervalInSeconds == 0)
1419         {
1420             *RetryIntervalInSeconds = 1;
1421         }
1422 
1423         DevicePerfIncrementErrorCount(DeviceExtension);
1424     }
1425 
1426     return;
1427 }
1428 
1429 VOID
SenseInfoLogError(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ PSCSI_REQUEST_BLOCK Srb,_In_ UCHAR MajorFunctionCode,_In_ ULONG IoControlCode,_In_ ULONG RetriedCount,_In_ NTSTATUS * Status,_In_ BOOLEAN * Retry,_Inout_ PERROR_LOG_CONTEXT LogContext)1430 SenseInfoLogError(
1431     _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
1432     _In_    PSCSI_REQUEST_BLOCK     Srb,
1433     _In_    UCHAR                   MajorFunctionCode,
1434     _In_    ULONG                   IoControlCode,
1435     _In_    ULONG                   RetriedCount,
1436     _In_    NTSTATUS*               Status,
1437     _In_    BOOLEAN*                Retry,
1438     _Inout_ PERROR_LOG_CONTEXT      LogContext
1439     )
1440 {
1441     //      Always log the error in our internal log.
1442     //      If logError is set, also log the error in the system log.
1443     PSENSE_DATA          senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
1444     ULONG                totalSize = 0;
1445     ULONG                senseBufferSize = 0;
1446     IO_ERROR_LOG_PACKET  staticErrLogEntry = {0};
1447     CDROM_ERROR_LOG_DATA staticErrLogData = {0};
1448 
1449     // Calculate the total size of the error log entry.
1450     // add to totalSize in the order that they are used.
1451     // the advantage to calculating all the sizes here is
1452     // that we don't have to do a bunch of extraneous checks
1453     // later on in this code path.
1454     totalSize = sizeof(IO_ERROR_LOG_PACKET)     // required
1455               + sizeof(CDROM_ERROR_LOG_DATA);   // struct for ease
1456 
1457     // also save any available extra sense data, up to the maximum errlog
1458     // packet size .  WMI should be used for real-time analysis.
1459     // the event log should only be used for post-mortem debugging.
1460     if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
1461     {
1462         ULONG       validSenseBytes;
1463         BOOLEAN     validSense;
1464 
1465         // make sure we can at least access the AdditionalSenseLength field
1466         validSense = RTL_CONTAINS_FIELD(senseBuffer,
1467                                         Srb->SenseInfoBufferLength,
1468                                         AdditionalSenseLength);
1469         if (validSense)
1470         {
1471             // if extra info exists, copy the maximum amount of available
1472             // sense data that is safe into the the errlog.
1473             validSenseBytes = senseBuffer->AdditionalSenseLength
1474                                 + offsetof(SENSE_DATA, AdditionalSenseLength);
1475 
1476             // this is invalid because it causes overflow!
1477             // whoever sent this type of request would cause
1478             // a system crash.
1479             NT_ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
1480 
1481             // set to save the most sense buffer possible
1482             senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
1483             senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
1484         }
1485         else
1486         {
1487             // it's smaller than required to read the total number of
1488             // valid bytes, so just use the SenseInfoBufferLength field.
1489             senseBufferSize = Srb->SenseInfoBufferLength;
1490         }
1491 
1492         //  Bump totalSize by the number of extra senseBuffer bytes
1493         //  (beyond the default sense buffer within CDROM_ERROR_LOG_DATA).
1494         //  Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
1495         if (senseBufferSize > sizeof(SENSE_DATA))
1496         {
1497             totalSize += senseBufferSize-sizeof(SENSE_DATA);
1498             if (totalSize > ERROR_LOG_MAXIMUM_SIZE)
1499             {
1500                 senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
1501                 totalSize = ERROR_LOG_MAXIMUM_SIZE;
1502             }
1503         }
1504     }
1505 
1506     // If we've used up all of our retry attempts, set the final status to
1507     // reflect the appropriate result.
1508     //
1509     // ISSUE: the test below should also check RetriedCount to determine if we will actually retry,
1510     //            but there is no easy test because we'd have to consider the original retry count
1511     //            for the op; besides, InterpretTransferPacketError sometimes ignores the retry
1512     //            decision returned by this function.  So just ErrorRetried to be true in the majority case.
1513     //
1514     if (*Retry)
1515     {
1516         staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
1517         staticErrLogData.ErrorRetried = TRUE;
1518     }
1519     else
1520     {
1521         staticErrLogEntry.FinalStatus = *Status;
1522     }
1523 
1524     // Don't log generic IO_WARNING_PAGING_FAILURE message if either the
1525     // I/O is retried, or it completed successfully.
1526     if ((LogContext->ErrorCode == IO_WARNING_PAGING_FAILURE) &&
1527         (*Retry || NT_SUCCESS(*Status)) )
1528     {
1529         LogContext->LogError = FALSE;
1530     }
1531 
1532     if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING))
1533     {
1534         staticErrLogData.ErrorPaging = TRUE;
1535     }
1536 
1537     staticErrLogData.ErrorUnhandled = LogContext->ErrorUnhandled;
1538 
1539     // Calculate the device offset if there is a geometry.
1540     staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)LogContext->BadSector;
1541     staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)DeviceExtension->DiskGeometry.BytesPerSector;
1542 
1543     if (LogContext->ErrorCode == -1)
1544     {
1545         staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
1546     }
1547     else
1548     {
1549         staticErrLogEntry.ErrorCode = LogContext->ErrorCode;
1550     }
1551 
1552     //  The dump data follows the IO_ERROR_LOG_PACKET
1553     staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET);
1554 
1555     staticErrLogEntry.SequenceNumber = 0;
1556     staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
1557     staticErrLogEntry.IoControlCode = IoControlCode;
1558     staticErrLogEntry.RetryCount = (UCHAR)RetriedCount;
1559     staticErrLogEntry.UniqueErrorValue = LogContext->UniqueErrorValue;
1560 
1561     KeQueryTickCount(&staticErrLogData.TickCount);
1562     staticErrLogData.PortNumber = (ULONG)-1;
1563 
1564     //  Save the entire contents of the SRB.
1565     staticErrLogData.Srb = *Srb;
1566 
1567     //  For our private log, save just the default length of the SENSE_DATA.
1568     if (senseBufferSize != 0)
1569     {
1570         RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
1571     }
1572 
1573     // Save the error log in our context.
1574     // We only save the default sense buffer length.
1575     {
1576         KIRQL                oldIrql;
1577         KeAcquireSpinLock(&DeviceExtension->PrivateFdoData->SpinLock, &oldIrql);
1578         DeviceExtension->PrivateFdoData->ErrorLogs[DeviceExtension->PrivateFdoData->ErrorLogNextIndex] = staticErrLogData;
1579         DeviceExtension->PrivateFdoData->ErrorLogNextIndex++;
1580         DeviceExtension->PrivateFdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
1581         KeReleaseSpinLock(&DeviceExtension->PrivateFdoData->SpinLock, oldIrql);
1582     }
1583 
1584     //  If logError is set, also save this log in the system's error log.
1585     //  But make sure we don't log TUR failures over and over
1586     //  (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
1587     if (LogContext->LogError)
1588     {
1589         // We do not want to log certain system events repetitively
1590         switch (((PCDB)Srb->Cdb)->CDB10.OperationCode)
1591         {
1592             case SCSIOP_TEST_UNIT_READY:
1593             {
1594                 if (DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO)
1595                 {
1596                     LogContext->LogError = FALSE;
1597                 }
1598                 else
1599                 {
1600                     DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO = TRUE;
1601                 }
1602 
1603                 break;
1604             }
1605 
1606             case SCSIOP_SYNCHRONIZE_CACHE:
1607             {
1608                 if (DeviceExtension->PrivateFdoData->LoggedSYNCFailure)
1609                 {
1610                     LogContext->LogError = FALSE;
1611                 }
1612                 else
1613                 {
1614                     DeviceExtension->PrivateFdoData->LoggedSYNCFailure = TRUE;
1615                 }
1616 
1617                 break;
1618             }
1619         }
1620 
1621         // Do not log 5/21/00 LOGICAL BLOCK ADDRESS OUT OF RANGE if the disc is blank,
1622         // it is known to litter the Event Log with repetitive errors
1623         // Do not log this error for READ, as it's known that File System mount process reads different sectors from media.
1624         if (senseBufferSize > RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier) &&
1625             senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST &&
1626             senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_ILLEGAL_BLOCK &&
1627             senseBuffer->AdditionalSenseCodeQualifier == 0 &&
1628             IS_SCSIOP_READ(((PCDB)Srb->Cdb)->CDB10.OperationCode))
1629         {
1630             LogContext->LogError = FALSE;
1631         }
1632     }
1633 
1634     //  Write the error log packet to the system error logging thread.
1635     if (LogContext->LogError)
1636     {
1637         PIO_ERROR_LOG_PACKET    errorLogEntry;
1638         PCDROM_ERROR_LOG_DATA   errlogData;
1639 
1640         errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceExtension->DeviceObject, (UCHAR)totalSize);
1641         if (errorLogEntry)
1642         {
1643             errlogData = (PCDROM_ERROR_LOG_DATA)errorLogEntry->DumpData;
1644 
1645             *errorLogEntry = staticErrLogEntry;
1646             *errlogData = staticErrLogData;
1647 
1648             //  For the system log, copy as much of the sense buffer as possible.
1649             if (senseBufferSize != 0)
1650             {
1651                 RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
1652             }
1653 
1654             //  errorLogEntry - It will be freed by the kernel.
1655             IoWriteErrorLogEntry(errorLogEntry);
1656         }
1657     }
1658 
1659     return;
1660 }
1661 
1662 VOID
1663 SenseInfoInterpretRefineByScsiCommand(
1664     _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
1665     _In_      PSCSI_REQUEST_BLOCK       Srb,
1666     _In_      ULONG                     RetriedCount,
1667     _In_      LONGLONG                  Total100nsSinceFirstSend,
1668     _In_      BOOLEAN                   OverrideVerifyVolume,
1669     _Inout_   BOOLEAN*                  Retry,
1670     _Inout_   NTSTATUS*                 Status,
1671     _Inout_   _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
1672               LONGLONG*                 RetryIntervalIn100ns
1673     )
1674 /*++
1675 
1676 Routine Description:
1677 
1678     Based on SCSI command, modify the interpretion result.
1679 
1680 Arguments:
1681 
1682     DeviceExtension - device extension.
1683     Srb - Supplies the scsi request block which failed.
1684     RetriedCount - retried count.
1685     Total100nsUnitsSinceFirstSend - time spent after the request was sent down first time.
1686     OverrideVerifyVolume - should override verify volume request.
1687 
1688 Return Value:
1689 
1690     Retry - the reques should be retried or not.
1691     Status - Returns the status for the request.
1692     RetryInterval - waiting time (in 100ns) before the request should be retried.
1693                     Zero indicates the request should be immediately retried.
1694 
1695 --*/
1696 {
1697     UCHAR const opCode = Srb->Cdb[0];
1698     CDB const*  cdb = (CDB const*)(Srb->Cdb);
1699     PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
1700 
1701     if (opCode == SCSIOP_MEDIUM_REMOVAL)
1702     {
1703         if (( cdb->AsByte[1]         == 0) &&
1704             ( cdb->AsByte[2]         == 0) &&
1705             ( cdb->AsByte[3]         == 0) &&
1706             ((cdb->AsByte[4] & 0xFC) == 0)
1707             )
1708         {
1709             // byte[4] == 0x3 or byte[4] == 0x1  ==  UNLOCK OF MEDIA
1710             if ((cdb->AsByte[4] & 0x01) == 0)
1711             {
1712                 if (RetriedCount < TOTAL_COUNT_RETRY_DEFAULT)
1713                 {
1714                     // keep retrying unlock operation for several times
1715                     *Retry = TRUE;
1716                 }
1717             }
1718             else // LOCK REQUEST
1719             {
1720                 // do not retry LOCK requests more than once (per CLASSPNP code)
1721                 if (RetriedCount > TOTAL_COUNT_RETRY_LOCK_MEDIA)
1722                 {
1723                     *Retry = FALSE;
1724                 }
1725             }
1726         }
1727 
1728         // want a minimum time to retry of 2 seconds
1729         if ((*Status == STATUS_DEVICE_NOT_READY) &&
1730             (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY))
1731         {
1732             *RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
1733         }
1734         else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)
1735         {
1736             *RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
1737         }
1738     }
1739     else if ((opCode == SCSIOP_MODE_SENSE) || (opCode == SCSIOP_MODE_SENSE10))
1740     {
1741         // want a minimum time to retry of 2 seconds
1742         if ((*Status == STATUS_DEVICE_NOT_READY) &&
1743             (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY))
1744         {
1745             *RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
1746         }
1747         else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)
1748         {
1749             *RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
1750         }
1751 
1752         // Want to ignore a STATUS_VERIFY_REQUIRED error because it either
1753         // doesn't make sense or is required to satisfy the VERIFY.
1754         if (*Status == STATUS_VERIFY_REQUIRED)
1755         {
1756             *Retry = TRUE;
1757         }
1758         else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)
1759         {
1760             /*
1761              *  This is a HACK.
1762              *  Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
1763              *  underrun (i.e. success, and the buffer is longer than needed).
1764              *  So treat this as a success.
1765              *  When the caller of this function sees that the status was changed to success,
1766              *  it will add the transferred length to the original irp.
1767              */
1768             *Status = STATUS_SUCCESS;
1769             *Retry = FALSE;
1770         }
1771 
1772         // limit the count of retries
1773         if (RetriedCount > TOTAL_COUNT_RETRY_MODESENSE)
1774         {
1775             *Retry = FALSE;
1776         }
1777     }
1778     else if ((opCode == SCSIOP_READ_CAPACITY) || (opCode == SCSIOP_READ_CAPACITY16))
1779     {
1780         // Want to ignore a STATUS_VERIFY_REQUIRED error because it either
1781         // doesn't make sense or is required to satisfy the VERIFY.
1782         if (*Status == STATUS_VERIFY_REQUIRED)
1783         {
1784             *Retry = TRUE;
1785         }
1786 
1787         if (RetriedCount > TOTAL_COUNT_RETRY_READ_CAPACITY)
1788         {
1789             *Retry = FALSE;
1790         }
1791     }
1792     else if ((opCode == SCSIOP_RESERVE_UNIT) || (opCode == SCSIOP_RELEASE_UNIT))
1793     {
1794         // The RESERVE(6) / RELEASE(6) commands are optional.
1795         // So if they aren't supported, try the 10-byte equivalents
1796         if (*Status == STATUS_INVALID_DEVICE_REQUEST)
1797         {
1798             PCDB tempCdb = (PCDB)Srb->Cdb;
1799 
1800             Srb->CdbLength = 10;
1801             tempCdb->CDB10.OperationCode = (tempCdb->CDB6GENERIC.OperationCode == SCSIOP_RESERVE_UNIT)
1802                                             ? SCSIOP_RESERVE_UNIT10
1803                                             : SCSIOP_RELEASE_UNIT10;
1804 
1805             SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6);
1806             *Retry = TRUE;
1807         }
1808     }
1809     else if (IS_SCSIOP_READWRITE(opCode))
1810     {
1811         // Retry if still verifying a (possibly) reloaded disk/cdrom.
1812         if (OverrideVerifyVolume && (*Status == STATUS_VERIFY_REQUIRED))
1813         {
1814             *Status = STATUS_IO_DEVICE_ERROR;
1815             *Retry = TRUE;
1816         }
1817 
1818         // Special case for streaming READ/WRITE commands
1819         if (((opCode == SCSIOP_READ12) && (cdb->READ12.Streaming == 1)) ||
1820             ((opCode == SCSIOP_WRITE12) && (cdb->WRITE12.Streaming == 1)))
1821         {
1822             // We've got a failure while performing a streaming operation and now need to guess if
1823             // it's likely to be a permanent error because the drive does not support streaming at all
1824             // (in which case we're going to fall back to normal reads/writes), or a transient error
1825             // (in which case we quickly fail the request but contrinue to use streaming).
1826             //
1827             // We analyze the sense information to make that decision. Bus resets and device timeouts
1828             // are treated as permanent errors, because some non-compliant devices may even hang when
1829             // they get a command that they do not expect.
1830 
1831             BOOLEAN     disableStreaming = FALSE;
1832 
1833             if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_TIMEOUT ||
1834                 SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_COMMAND_TIMEOUT ||
1835                 SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT ||
1836                 SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_BUS_RESET)
1837             {
1838                 disableStreaming = TRUE;
1839             }
1840             else if ((senseBuffer->SenseKey &0xf) == SCSI_SENSE_UNIT_ATTENTION)
1841             {
1842                 if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_BUS_RESET ||
1843                     senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INSUFFICIENT_TIME_FOR_OPERATION)
1844                 {
1845                     disableStreaming = TRUE;
1846                 }
1847             }
1848             else if ((senseBuffer->SenseKey &0xf) == SCSI_SENSE_ILLEGAL_REQUEST)
1849             {
1850                 // LBA Out of Range is an exception, as it's more likely to be caused by
1851                 // upper layers attempting to read/write a wrong LBA.
1852                 if (senseBuffer->AdditionalSenseCode != SCSI_ADSENSE_ILLEGAL_BLOCK)
1853                 {
1854                     disableStreaming = TRUE;
1855                 }
1856             }
1857 
1858             if (disableStreaming)
1859             {
1860                 // if the failure looks permanent, we disable streaming for all future reads/writes
1861                 // and retry the command immediately
1862                 SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_STREAMING);
1863                 *Retry = TRUE;
1864                 *RetryIntervalIn100ns = 0;
1865             }
1866             else
1867             {
1868                 // if the failure looks transient, we simply fail the current request without retries
1869                 // to minimize the time of processing
1870                 *Retry = FALSE;
1871             }
1872         }
1873 
1874         // Special-case handling of READ/WRITE commands.  These commands now have a 120 second timeout,
1875         // but the preferred behavior (and that taken by many drives) is to immediately report 2/4/x
1876         // on OPC and similar commands.  Thus, retries must occur for at least 160 seconds
1877         // (120 seconds + four 10 second retries) as a conservative guess.
1878         // Note: 160s retry time is also a result of discussion with OEMs for case of 2/4/7 and 2/4/8.
1879         if (*Retry)
1880         {
1881             if ((Total100nsSinceFirstSend < 0) ||
1882                 (((senseBuffer->SenseKey &0xf) == SCSI_SENSE_HARDWARE_ERROR) && (senseBuffer->AdditionalSenseCode == 0x09)))
1883             {
1884                 // time information is not valid. use default retry count.
1885                 // or if it's SERVO FAILURE, use retry count instead of 160s retry.
1886                 *Retry = (RetriedCount <= TOTAL_COUNT_RETRY_DEFAULT);
1887             }
1888             else if (Total100nsSinceFirstSend > SECONDS_TO_100NS_UNITS(TOTAL_SECONDS_RETRY_TIME_WRITE))
1889             {
1890                 *Retry = FALSE;
1891             }
1892 
1893             // How long should we request a delay for during writing?  This depends entirely on
1894             // the current write speed of the drive.  If we request retries too quickly,
1895             // we can overload the processor on the drive (resulting in garbage being written),
1896             // but too slowly results in lesser performance.
1897             //
1898             *RetryIntervalIn100ns = DeviceExtension->DeviceAdditionalData.ReadWriteRetryDelay100nsUnits;
1899 
1900         } // end retry for 160 seconds modification
1901     }
1902     else if (opCode == SCSIOP_GET_PERFORMANCE)
1903     {
1904         if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)
1905         {
1906             //  This is a HACK.
1907             //  Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
1908             //  underrun (i.e. success, and the buffer is longer than needed).
1909             //  So treat this as a success.
1910             //  When the caller of this function sees that the status was changed to success,
1911             //  it will add the transferred length to the original irp.
1912             *Status = STATUS_SUCCESS;
1913             *Retry = FALSE;
1914         }
1915 
1916         if ((Srb->SenseInfoBufferLength < RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA,AdditionalSenseCodeQualifier)) ||
1917             !TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
1918         {
1919             // If get configuration command is failing and if the request type is TYPE ONE
1920             // then most likely the device does not support this request type. Set the
1921             // flag so that the TYPE ONE requests will be tried as TYPE ALL requets.
1922             if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) &&
1923                 (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_DATA_OVERRUN) &&
1924                 (((PCDB)Srb->Cdb)->GET_CONFIGURATION.RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE))
1925             {
1926 
1927                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
1928                            "TYPE ONE GetConfiguration failed. Set hack flag and retry.\n"));
1929                 SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
1930                 *Retry = TRUE;
1931             }
1932         }
1933 
1934         // limit retries to GET_PERFORMANCE commands to default retry count
1935         if (RetriedCount > TOTAL_COUNT_RETRY_DEFAULT)
1936         {
1937             *Retry = FALSE;
1938         }
1939     }
1940     else // default handler -- checks for retry count only.
1941     {
1942         if (RetriedCount > TOTAL_COUNT_RETRY_DEFAULT)
1943         {
1944             *Retry = FALSE;
1945         }
1946     }
1947 
1948     return;
1949 }
1950 
1951 
1952 VOID
SenseInfoInterpretRefineByIoControl(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ ULONG IoControlCode,_In_ BOOLEAN OverrideVerifyVolume,_Inout_ BOOLEAN * Retry,_Inout_ NTSTATUS * Status)1953 SenseInfoInterpretRefineByIoControl(
1954     _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
1955     _In_      ULONG                     IoControlCode,
1956     _In_      BOOLEAN                   OverrideVerifyVolume,
1957     _Inout_   BOOLEAN*                  Retry,
1958     _Inout_   NTSTATUS*                 Status
1959     )
1960 /*++
1961 
1962 Routine Description:
1963 
1964     Based on IOCTL code, modify the interpretion result.
1965 
1966 Arguments:
1967 
1968     Device - Supplies the device object associated with this request.
1969     OriginalRequest - the irp that error occurs on.
1970     Srb - Supplies the scsi request block which failed.
1971     MajorFunctionCode - Supplies the function code to be used for logging.
1972     IoDeviceCode - Supplies the device code to be used for logging.
1973     PreviousRetryCount - retried count.
1974     RequestHistory_DoNotUse - the history list
1975 
1976 Return Value:
1977 
1978     BOOLEAN TRUE: Drivers should retry this request.
1979             FALSE: Drivers should not retry this request.
1980     Status - Returns the status for the request.
1981     RetryInterval - Number of seconds before the request should be retried.
1982                     Zero indicates the request should be immediately retried.
1983 
1984 --*/
1985 {
1986     PAGED_CODE();
1987 
1988     if ((IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) ||
1989         (IoControlCode == IOCTL_CDROM_READ_TOC)         ||
1990         (IoControlCode == IOCTL_CDROM_READ_TOC_EX)      ||
1991         (IoControlCode == IOCTL_CDROM_GET_CONFIGURATION)||
1992         (IoControlCode == IOCTL_CDROM_GET_VOLUME))
1993     {
1994         if (*Status == STATUS_DATA_OVERRUN)
1995         {
1996             *Status = STATUS_SUCCESS;
1997             *Retry = FALSE;
1998         }
1999     }
2000 
2001     if (IoControlCode == IOCTL_CDROM_READ_Q_CHANNEL)
2002     {
2003         PLAY_ACTIVE(DeviceExtension) = FALSE;
2004     }
2005 
2006     // If the status is verified required and the this request
2007     // should bypass verify required then retry the request.
2008     if (OverrideVerifyVolume && (*Status == STATUS_VERIFY_REQUIRED))
2009     {
2010         // note: status gets overwritten here
2011         *Status = STATUS_IO_DEVICE_ERROR;
2012         *Retry = TRUE;
2013 
2014         if ((IoControlCode == IOCTL_CDROM_CHECK_VERIFY) ||
2015             (IoControlCode == IOCTL_STORAGE_CHECK_VERIFY) ||
2016             (IoControlCode == IOCTL_STORAGE_CHECK_VERIFY2) ||
2017             (IoControlCode == IOCTL_DISK_CHECK_VERIFY)
2018            )
2019         {
2020             // Update the geometry information, as the media could have changed.
2021             (VOID) MediaReadCapacity(DeviceExtension->Device);
2022         } // end of ioctls to update capacity
2023     }
2024 
2025     if (!NT_SUCCESS(*Status) && (IoControlCode == IOCTL_CDROM_SET_SPEED))
2026     {
2027         // If set speed request fails then we should disable the restore speed option.
2028         // Otherwise we will try to restore to default speed on next media change,
2029         // if requested by the caller.
2030         DeviceExtension->DeviceAdditionalData.RestoreDefaults = FALSE;
2031         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "Disable restore default\n"));
2032     }
2033 
2034     return;
2035 }
2036 
2037 BOOLEAN
2038 SenseInfoInterpret(
2039     _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
2040     _In_    WDFREQUEST              Request,
2041     _In_    PSCSI_REQUEST_BLOCK     Srb,
2042     _In_    ULONG                   RetriedCount,
2043     _Out_   NTSTATUS*               Status,
2044     _Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
2045               LONGLONG*             RetryIntervalIn100ns
2046     )
2047 /*++
2048 
2049 SenseInfoInterpret()
2050 
2051 Routine Description:
2052 
2053     This routine interprets the data returned from the SCSI request sense.
2054     It determines the status to return in the IRP
2055     and whether this request can be retried.
2056 
2057 Arguments:
2058 
2059     Device - Supplies the device object associated with this request.
2060     Srb - Supplies the scsi request block which failed.
2061     MajorFunctionCode - Supplies the function code to be used for logging.
2062     IoDeviceCode - Supplies the device code to be used for logging.
2063 
2064 Return Value:
2065 
2066     BOOLEAN TRUE: Drivers should retry this request.
2067             FALSE: Drivers should not retry this request.
2068     Status - Returns the status for the request.
2069     RetryInterval - Number of seconds before the request should be retried.
2070                     Zero indicates the request should be immediately retried.
2071 
2072 --*/
2073 {
2074     ULONG               retryIntervalInSeconds = 0;
2075     BOOLEAN             retry = TRUE;
2076     PSENSE_DATA         senseBuffer = Srb->SenseInfoBuffer;
2077     ULONG               readSector = 0;
2078     ERROR_LOG_CONTEXT   logContext;
2079 
2080     UCHAR                   majorFunctionCode = 0;
2081     ULONG                   ioControlCode = 0;
2082     BOOLEAN                 overrideVerifyVolume = FALSE;
2083     ULONGLONG               total100nsSinceFirstSend = 0;
2084     PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
2085 
2086     //
2087     *Status = STATUS_IO_DEVICE_ERROR;
2088 
2089     RtlZeroMemory(&logContext, sizeof(ERROR_LOG_CONTEXT));
2090     logContext.ErrorCode = -1;
2091 
2092     // Get Original Request related information
2093     SenseInfoRequestGetInformation(Request,
2094                                    &majorFunctionCode,
2095                                    &ioControlCode,
2096                                    &overrideVerifyVolume,
2097                                    &total100nsSinceFirstSend);
2098 
2099     if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING))
2100     {
2101         // Log anything remotely incorrect about paging i/o
2102         logContext.LogError = TRUE;
2103         logContext.UniqueErrorValue = 301;
2104         logContext.ErrorCode = IO_WARNING_PAGING_FAILURE;
2105     }
2106 
2107     // must handle the SRB_STATUS_INTERNAL_ERROR case first,
2108     // as it has all the flags set.
2109     if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR)
2110     {
2111         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
2112                     "SenseInfoInterpret: Internal Error code is %x\n",
2113                     Srb->InternalStatus));
2114 
2115         retry = FALSE;
2116         *Status = Srb->InternalStatus;
2117     }
2118     else if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT)
2119     {
2120         retry = FALSE;
2121         *Status = STATUS_DEVICE_BUSY;
2122         logContext.LogError = FALSE;
2123     }
2124     else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
2125              (Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)))
2126     {
2127         UCHAR   senseKey = (UCHAR)(senseBuffer->SenseKey & 0x0f);
2128         UCHAR   additionalSenseCode = 0;
2129         UCHAR   additionalSenseCodeQual = 0;
2130 
2131         // Zero the additional sense code and additional sense code qualifier
2132         // if they were not returned by the device.
2133         readSector = senseBuffer->AdditionalSenseLength + offsetof(SENSE_DATA, AdditionalSenseLength);
2134         if (readSector > Srb->SenseInfoBufferLength)
2135         {
2136             readSector = Srb->SenseInfoBufferLength;
2137         }
2138 
2139         additionalSenseCode = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) ?
2140                                senseBuffer->AdditionalSenseCode : 0;
2141         additionalSenseCodeQual = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) ?
2142                                    senseBuffer->AdditionalSenseCodeQualifier : 0;
2143 
2144         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
2145                     "SCSI Error - \n"
2146                     "\tcdb: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
2147                     "\tsrb status: %X; sense: %02X/%02X/%02X; Retried count: %d\n\n",
2148                     Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5],
2149                     Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11],
2150                     Srb->Cdb[12], Srb->Cdb[13], Srb->Cdb[14], Srb->Cdb[15],
2151                     SRB_STATUS(Srb->SrbStatus),
2152                     senseKey,
2153                     additionalSenseCode,
2154                     additionalSenseCodeQual,
2155                     RetriedCount));
2156 
2157         if (senseKey == SCSI_SENSE_UNIT_ATTENTION)
2158         {
2159             ULONG   mediaChangeCount;
2160 
2161             // A media change may have occured so increment the change count for the physical device
2162             mediaChangeCount = InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
2163             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
2164                        "SenseInfoInterpret: Media change count for device %d incremented to %#lx\n",
2165                        DeviceExtension->DeviceNumber, mediaChangeCount));
2166         }
2167 
2168         if ((zpoddInfo != NULL) &&
2169             (((PCDB)Srb->Cdb)->CDB6GENERIC.OperationCode == SCSIOP_TEST_UNIT_READY))
2170         {
2171             // This sense code is in response to the Test Unit Ready sent during delayed power down
2172             // request. Copy the sense data into the zpoddInfo structure for later processing.
2173             zpoddInfo->SenseKey = senseKey;
2174             zpoddInfo->AdditionalSenseCode = additionalSenseCode;
2175             zpoddInfo->AdditionalSenseCodeQualifier = additionalSenseCodeQual;
2176         }
2177 
2178         // Interpret error by specific ASC & ASCQ first,
2179         // If the error is not handled, interpret using f
2180         {
2181             BOOLEAN notHandled = FALSE;
2182             notHandled = SenseInfoInterpretByAdditionalSenseCode(DeviceExtension,
2183                                                                  Srb,
2184                                                                  additionalSenseCode,
2185                                                                  additionalSenseCodeQual,
2186                                                                  Status,
2187                                                                  &retry,
2188                                                                  &retryIntervalInSeconds,
2189                                                                  &logContext);
2190 
2191             if (notHandled)
2192             {
2193                 SenseInfoInterpretBySenseKey(DeviceExtension,
2194                                              senseBuffer,
2195                                              senseKey,
2196                                              Status,
2197                                              &retry,
2198                                              &retryIntervalInSeconds,
2199                                              &logContext);
2200             }
2201         }
2202 
2203         // Try to determine the bad sector from the inquiry data.
2204         if ((IS_SCSIOP_READWRITE(((PCDB)Srb->Cdb)->CDB10.OperationCode)) ||
2205             (((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY)     ||
2206             (((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY16))
2207         {
2208             ULONG index;
2209             readSector = 0;
2210 
2211             for (index = 0; index < 4; index++)
2212             {
2213                 logContext.BadSector = (logContext.BadSector << 8) | senseBuffer->Information[index];
2214             }
2215 
2216             for (index = 0; index < 4; index++)
2217             {
2218                 readSector = (readSector << 8) | Srb->Cdb[index+2];
2219             }
2220 
2221             index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
2222                     ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
2223 
2224             // Make sure the bad sector is within the read sectors.
2225             if (!(logContext.BadSector >= readSector && logContext.BadSector < (readSector + index)))
2226             {
2227                 logContext.BadSector = readSector;
2228             }
2229         }
2230     }
2231     else
2232     {
2233         // Request sense buffer not valid. No sense information
2234         // to pinpoint the error. Return general request fail.
2235         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
2236                     "SCSI Error - \n"
2237                     "\tcdb: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
2238                     "\tsrb status: %X; sense info not valid; Retried count: %d\n\n",
2239                     Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5],
2240                     Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11],
2241                     Srb->Cdb[12], Srb->Cdb[13], Srb->Cdb[14], Srb->Cdb[15],
2242                     SRB_STATUS(Srb->SrbStatus),
2243                     RetriedCount));
2244 
2245         SenseInfoInterpretBySrbStatus(DeviceExtension,
2246                                       Srb,
2247                                       RetriedCount,
2248                                       Status,
2249                                       &retry,
2250                                       &retryIntervalInSeconds,
2251                                       &logContext);
2252     }
2253 
2254     // all functions using unit - seconds for retry Interval already be called.
2255     *RetryIntervalIn100ns = SECONDS_TO_100NS_UNITS(retryIntervalInSeconds);
2256 
2257     // call the device specific error handler if it has one.
2258     // DeviceErrorHandlerForMmmc() for all MMC devices
2259     // or DeviceErrorHandlerForHitachiGD2000() for HITACHI GD-2000, HITACHI DVD-ROM GD-2000
2260     if (DeviceExtension->DeviceAdditionalData.ErrorHandler)
2261     {
2262         DeviceExtension->DeviceAdditionalData.ErrorHandler(DeviceExtension, Srb, Status, &retry);
2263     }
2264 
2265     // Refine retry based on SCSI command
2266     SenseInfoInterpretRefineByScsiCommand(DeviceExtension,
2267                                           Srb,
2268                                           RetriedCount,
2269                                           total100nsSinceFirstSend,
2270                                           overrideVerifyVolume,
2271                                           &retry,
2272                                           Status,
2273                                           RetryIntervalIn100ns);
2274 
2275     // Refine retry based on IOCTL code.
2276     if (majorFunctionCode == IRP_MJ_DEVICE_CONTROL)
2277     {
2278         SenseInfoInterpretRefineByIoControl(DeviceExtension,
2279                                             ioControlCode,
2280                                             overrideVerifyVolume,
2281                                             &retry,
2282                                             Status);
2283     }
2284 
2285     // LOG the error:
2286     //  Always log the error in our internal log.
2287     //  If logError is set, also log the error in the system log.
2288     SenseInfoLogError(DeviceExtension,
2289                       Srb,
2290                       majorFunctionCode,
2291                       ioControlCode,
2292                       RetriedCount,
2293                       Status,
2294                       &retry,
2295                       &logContext);
2296 
2297     // all process about the error done. check if the irp was cancelled.
2298     if ((!NT_SUCCESS(*Status)) && retry)
2299     {
2300         PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
2301 
2302         if ((requestContext->OriginalRequest != NULL) &&
2303             WdfRequestIsCanceled(requestContext->OriginalRequest)
2304             )
2305         {
2306             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
2307                        "Request %p was cancelled when it would have been retried\n",
2308                        requestContext->OriginalRequest));
2309 
2310             *Status = STATUS_CANCELLED;
2311             retry = FALSE;
2312             *RetryIntervalIn100ns = 0;
2313         }
2314     }
2315 
2316     // now, all decisions are made. display trace information.
2317     if (retry)
2318     {
2319         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
2320                    "Command shall be retried in %2I64d.%03I64d seconds\n",
2321                    (*RetryIntervalIn100ns / UNIT_100NS_PER_SECOND),
2322                    (*RetryIntervalIn100ns / 10000) % 1000
2323                    ));
2324     }
2325     else
2326     {
2327         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
2328                    "Will not retry; Sense/ASC/ASCQ of %02x/%02x/%02x\n",
2329                    senseBuffer->SenseKey,
2330                    senseBuffer->AdditionalSenseCode,
2331                    senseBuffer->AdditionalSenseCodeQualifier
2332                    ));
2333     }
2334 
2335     return retry;
2336 
2337 } // end SenseInfoInterpret()
2338 
2339 
2340 BOOLEAN
2341 SenseInfoInterpretForZPODD(
2342     _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
2343     _In_    PSCSI_REQUEST_BLOCK     Srb,
2344     _Out_   NTSTATUS*               Status,
2345     _Out_ _Out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
2346               LONGLONG*             RetryIntervalIn100ns
2347     )
2348 /*++
2349 
2350 SenseInfoInterpretForZPODD()
2351 
2352 Routine Description:
2353 
2354     This routine interprets the data returned from the SCSI request sense.
2355     It determines the status to return in the IRP
2356     and whether this request can be retried.
2357 
2358 Arguments:
2359 
2360     Device - Supplies the device object associated with this request.
2361     Srb - Supplies the scsi request block which failed.
2362 
2363 Return Value:
2364 
2365     BOOLEAN TRUE: Drivers should retry this request.
2366             FALSE: Drivers should not retry this request.
2367     Status - Returns the status for the request.
2368     RetryInterval - Number of seconds before the request should be retried.
2369                     Zero indicates the request should be immediately retried.
2370 
2371 --*/
2372 {
2373     BOOLEAN                 retry = FALSE;
2374     PSENSE_DATA             senseBuffer = Srb->SenseInfoBuffer;
2375     ULONG                   readSector = 0;
2376     PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
2377 
2378     *Status = STATUS_IO_DEVICE_ERROR;
2379     *RetryIntervalIn100ns = 0;
2380 
2381     if (zpoddInfo->RetryFirstCommand != FALSE)
2382     {
2383         // The first command to the logical unit after power resumed will be terminated
2384         // with CHECK CONDITION Status, 6/29/00 POWER ON, RESET, OR BUS DEVICE RESET OCCURRED
2385 
2386         // We have observed some devices return a different sense code, and thus as long as
2387         // the first command after power resume fails, we just retry one more time.
2388         zpoddInfo->RetryFirstCommand = FALSE;
2389 
2390         retry = TRUE;
2391     }
2392     else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
2393              (Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)))
2394     {
2395         UCHAR   senseKey = (UCHAR)(senseBuffer->SenseKey & 0x0f);
2396         UCHAR   additionalSenseCode = 0;
2397         UCHAR   additionalSenseCodeQual = 0;
2398 
2399         // Zero the additional sense code and additional sense code qualifier
2400         // if they were not returned by the device.
2401         readSector = senseBuffer->AdditionalSenseLength + offsetof(SENSE_DATA, AdditionalSenseLength);
2402         if (readSector > Srb->SenseInfoBufferLength)
2403         {
2404             readSector = Srb->SenseInfoBufferLength;
2405         }
2406 
2407         additionalSenseCode = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) ?
2408                                senseBuffer->AdditionalSenseCode : 0;
2409         additionalSenseCodeQual = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) ?
2410                                    senseBuffer->AdditionalSenseCodeQualifier : 0;
2411 
2412         // If sense code is 2/4/1, device is becoming ready from ZPODD mode. According to Mt Fuji, device
2413         // could take up to 800msec to be fully operational.
2414         if ((senseKey == SCSI_SENSE_NOT_READY) &&
2415             (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
2416             (additionalSenseCodeQual == SCSI_SENSEQ_BECOMING_READY))
2417         {
2418             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
2419                         "SenseInfoInterpretForZPODD: In process of becoming ready\n"));
2420 
2421             zpoddInfo->BecomingReadyRetryCount--;
2422 
2423             if (zpoddInfo->BecomingReadyRetryCount > 0)
2424             {
2425                 DEVICE_EVENT_BECOMING_READY notReady = {0};
2426 
2427                 retry = TRUE;
2428                 *Status = STATUS_DEVICE_NOT_READY;
2429                 *RetryIntervalIn100ns = BECOMING_READY_RETRY_INTERNVAL_IN_100NS;
2430 
2431                 notReady.Version = 1;
2432                 notReady.Reason = 1;
2433                 notReady.Estimated100msToReady = (ULONG) *RetryIntervalIn100ns / (1000 * 1000);
2434                 DeviceSendNotification(DeviceExtension,
2435                                        &GUID_IO_DEVICE_BECOMING_READY,
2436                                        sizeof(DEVICE_EVENT_BECOMING_READY),
2437                                        &notReady);
2438             }
2439         }
2440     }
2441 
2442     // now, all decisions are made. display trace information.
2443     if (retry)
2444     {
2445         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
2446                    "Command shall be retried in %2I64d.%03I64d seconds\n",
2447                    (*RetryIntervalIn100ns / UNIT_100NS_PER_SECOND),
2448                    (*RetryIntervalIn100ns / 10000) % 1000
2449                    ));
2450     }
2451     else
2452     {
2453         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
2454                    "Will not retry; Sense/ASC/ASCQ of %02x/%02x/%02x\n",
2455                    senseBuffer->SenseKey,
2456                    senseBuffer->AdditionalSenseCode,
2457                    senseBuffer->AdditionalSenseCodeQualifier
2458                    ));
2459     }
2460 
2461     return retry;
2462 
2463 } // end SenseInfoInterpret()
2464 
2465 
2466 BOOLEAN
2467 RequestSenseInfoInterpret(
2468     _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
2469     _In_      WDFREQUEST                Request,
2470     _In_      PSCSI_REQUEST_BLOCK       Srb,
2471     _In_      ULONG                     RetriedCount,
2472     _Out_     NTSTATUS*                 Status,
2473     _Out_opt_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
2474               LONGLONG*                 RetryIntervalIn100ns
2475     )
2476 /*++
2477 
2478 Routine Description:
2479 
2480 Interpret the error, process it.
2481     1. Release device queue if it's frozen.
2482     2. Interpret and process the error.
2483 
2484 Arguments:
2485 
2486     DeviceExtension - Supplies the device object associated with this request.
2487     Request - the Request that error occurs on.
2488     Srb - Supplies the scsi request block which failed.
2489     RetriedCount - retried count.
2490 
2491 Return Value:
2492 
2493     BOOLEAN TRUE:  Drivers should retry this request.
2494             FALSE: Drivers should not retry this request.
2495     Status - Returns the status for the request.
2496     RetryIntervalIn100nsUnits - Number of 100ns before the request should be retried.
2497                                 Zero indicates the request should be immediately retried.
2498 
2499 --*/
2500 {
2501     BOOLEAN                 retry = FALSE;
2502     LONGLONG                retryIntervalIn100ns = 0;
2503     PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
2504 
2505     if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
2506     {
2507         // request succeeded.
2508         if ((zpoddInfo != NULL) &&
2509             (zpoddInfo->BecomingReadyRetryCount > 0))
2510         {
2511             zpoddInfo->BecomingReadyRetryCount = 0;
2512         }
2513 
2514         *Status = STATUS_SUCCESS;
2515         retry = FALSE;
2516     }
2517     else
2518     {
2519         // request failed. We need to process the error.
2520 
2521         // 1. Release the queue if it is frozen.
2522         if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN)
2523         {
2524             DeviceReleaseQueue(DeviceExtension->Device);
2525         }
2526 
2527         if ((zpoddInfo != NULL) &&
2528             ((zpoddInfo->RetryFirstCommand != FALSE) || (zpoddInfo->BecomingReadyRetryCount > 0)))
2529         {
2530             retry = SenseInfoInterpretForZPODD(DeviceExtension,
2531                                                Srb,
2532                                                Status,
2533                                                &retryIntervalIn100ns);
2534         }
2535 
2536         if (retry == FALSE)
2537         {
2538             // 2. Error Processing
2539             if ((zpoddInfo != NULL) &&
2540                 (zpoddInfo->BecomingReadyRetryCount > 0))
2541             {
2542                 zpoddInfo->BecomingReadyRetryCount = 0;
2543             }
2544 
2545             retry = SenseInfoInterpret(DeviceExtension,
2546                                        Request,
2547                                        Srb,
2548                                        RetriedCount,
2549                                        Status,
2550                                        &retryIntervalIn100ns);
2551         }
2552     }
2553 
2554     if (RetryIntervalIn100ns != NULL)
2555     {
2556         *RetryIntervalIn100ns = retryIntervalIn100ns;
2557     }
2558 
2559     return retry;
2560 }
2561 
2562 
2563 BOOLEAN
2564 RequestSenseInfoInterpretForScratchBuffer(
2565     _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
2566     _In_    ULONG                   RetriedCount,
2567     _Out_   NTSTATUS*               Status,
2568     _Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
2569             LONGLONG*               RetryIntervalIn100ns
2570     )
2571 /*++
2572 
2573 Routine Description:
2574 
2575     to analyze the error occurred and set the status, retry interval and decide to retry or not.
2576 
2577 Arguments:
2578 
2579     DeviceExtension - device extension
2580     RetriedCount - already retried count.
2581 
2582 Return Value:
2583 
2584     BOOLEAN - TRUE (should retry)
2585     Status - NTSTATUS
2586     RetryIntervalIn100nsUnits - retry interval
2587 
2588 --*/
2589 {
2590     NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse != 0);
2591 
2592     return RequestSenseInfoInterpret(DeviceExtension,
2593                                      DeviceExtension->ScratchContext.ScratchRequest,
2594                                      DeviceExtension->ScratchContext.ScratchSrb,
2595                                      RetriedCount,
2596                                      Status,
2597                                      RetryIntervalIn100ns);
2598 }
2599 
2600 
2601