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 ¬Ready);
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 ¬Ready);
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 ¬Ready);
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