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