aboutsummaryrefslogtreecommitdiff
path: root/en_US.ISO8859-1/books/arch-handbook/scsi/chapter.xml
blob: 7de627b5b9d1c6b655b7fd06e941e08292af0cba (plain) (blame)
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
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
     The FreeBSD Documentation Project

     $FreeBSD$
-->
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="scsi">
  <info><title>Common Access Method SCSI Controllers</title>
    <authorgroup>
      <author><personname><firstname>Sergey</firstname><surname>Babkin</surname></personname><contrib>Written by </contrib></author>
    </authorgroup>
    <authorgroup>
      <author><personname><firstname>Murray</firstname><surname>Stokely</surname></personname><contrib>Modifications for Handbook made by </contrib></author>
    </authorgroup>
  </info>

  

  <sect1 xml:id="scsi-synopsis">
    <title>Synopsis</title>

    <indexterm><primary>SCSI</primary></indexterm>
    <para>This document assumes that the reader has a general
      understanding of device drivers in FreeBSD and of the SCSI
      protocol.  Much of the information in this document was
      extracted from the drivers:</para>

    <itemizedlist>

      <listitem><para>ncr (<filename>/sys/pci/ncr.c</filename>) by
	Wolfgang Stanglmeier and Stefan Esser</para></listitem>

      <listitem>
	<para>sym (<filename>/sys/dev/sym/sym_hipd.c</filename>) by
	  Gerard Roudier</para>
      </listitem>

      <listitem>
	<para>aic7xxx
	  (<filename>/sys/dev/aic7xxx/aic7xxx.c</filename>) by Justin
	  T. Gibbs</para>
      </listitem>
    </itemizedlist>

    <para>and from the CAM code itself (by Justin T. Gibbs, see
      <filename>/sys/cam/*</filename>).  When some solution looked the
      most logical and was essentially verbatim extracted from the
      code by Justin T. Gibbs, I marked it as
      <quote>recommended</quote>.</para>

    <para>The document is illustrated with examples in
      pseudo-code.  Although sometimes the examples have many details
      and look like real code, it is still pseudo-code.  It was
      written to demonstrate the concepts in an understandable way.
      For a real driver other approaches may be more modular and
      efficient.  It also abstracts from the hardware details, as well
      as issues that would cloud the demonstrated concepts or that are
      supposed to be described in the other chapters of the developers
      handbook.  Such details are commonly shown as calls to functions
      with descriptive names, comments or pseudo-statements.
      Fortunately real life full-size examples with all the details
      can be found in the real drivers.</para>
  </sect1>

  <sect1 xml:id="scsi-general">
    <title>General Architecture</title>

    <indexterm>
      <primary>Common Access Method (CAM)</primary>
    </indexterm>

    <para>CAM stands for Common Access Method.  It is a generic way to
      address the I/O buses in a SCSI-like way.  This allows a
      separation of the generic device drivers from the drivers
      controlling the I/O bus: for example the disk driver becomes
      able to control disks on both SCSI, IDE, and/or any other bus so
      the disk driver portion does not have to be rewritten (or copied
      and modified) for every new I/O bus.  Thus the two most
      important active entities are:</para>

    <indexterm><primary>CD-ROM</primary></indexterm>
    <indexterm><primary>tape</primary></indexterm>
    <indexterm><primary>IDE</primary></indexterm>
    <itemizedlist>
      <listitem>
	<para><emphasis>Peripheral Modules</emphasis> - a
	  driver for peripheral devices (disk, tape, CD-ROM,
	  etc.)</para>
      </listitem>

      <listitem>
	<para><emphasis>SCSI Interface Modules </emphasis>(SIM) - a
	  Host Bus Adapter drivers for connecting to an I/O bus such
	  as SCSI or IDE.</para>
      </listitem>
    </itemizedlist>

    <para>A peripheral driver receives requests from the OS, converts
      them to a sequence of SCSI commands and passes these SCSI
      commands to a SCSI Interface Module.  The SCSI Interface Module
      is responsible for passing these commands to the actual hardware
      (or if the actual hardware is not SCSI but, for example, IDE
      then also converting the SCSI commands to the native commands of
      the hardware).</para>

    <para>As we are interested in writing a SCSI adapter driver
      here, from this point on we will consider everything from the
      SIM standpoint.</para>

    <para>A typical SIM driver needs to include the following
      CAM-related header files:</para>

    <programlisting>#include &lt;cam/cam.h&gt;
#include &lt;cam/cam_ccb.h&gt;
#include &lt;cam/cam_sim.h&gt;
#include &lt;cam/cam_xpt_sim.h&gt;
#include &lt;cam/cam_debug.h&gt;
#include &lt;cam/scsi/scsi_all.h&gt;</programlisting>

    <para>The first thing each SIM driver must do is register itself
      with the CAM subsystem.  This is done during the driver's
      <function>xxx_attach()</function> function (here and further
      xxx_ is used to denote the unique driver name prefix).  The
      <function>xxx_attach()</function> function itself is called by
      the system bus auto-configuration code which we do not describe
      here.</para>

    <para>This is achieved in multiple steps: first it is necessary to
      allocate the queue of requests associated with this SIM:</para>

    <programlisting>    struct cam_devq *devq;

    if(( devq = cam_simq_alloc(SIZE) )==NULL) {
        error; /* some code to handle the error */
    }</programlisting>

    <para>Here <literal>SIZE</literal> is the size of the queue to be
      allocated, maximal number of requests it could contain.  It is
      the number of requests that the SIM driver can handle in
      parallel on one SCSI card.  Commonly it can be calculated
      as:</para>

    <programlisting>SIZE = NUMBER_OF_SUPPORTED_TARGETS * MAX_SIMULTANEOUS_COMMANDS_PER_TARGET</programlisting>

    <para>Next we create a descriptor of our SIM:</para>

    <programlisting>    struct cam_sim *sim;

    if(( sim = cam_sim_alloc(action_func, poll_func, driver_name,
            softc, unit, mtx, max_dev_transactions,
            max_tagged_dev_transactions, devq) )==NULL) {
        cam_simq_free(devq);
        error; /* some code to handle the error */
    }</programlisting>

    <para>Note that if we are not able to create a SIM descriptor we
      free the <varname remap="structname">devq</varname> also because we can do
      nothing else with it and we want to conserve memory.</para>

    <para>If a SCSI card has multiple SCSI
      buses<indexterm><primary>SCSI</primary><secondary>bus</secondary></indexterm>
      on it then each bus requires its own
      <varname remap="structname">cam_sim</varname> structure.</para>

    <para>An interesting question is what to do if a SCSI card has
      more than one SCSI bus, do we need one
      <varname remap="structname">devq</varname> structure per card or per SCSI
      bus?  The answer given in the comments to the CAM code is:
      either way, as the driver's author prefers.</para>

    <para>The arguments are:</para>

    <itemizedlist>
      <listitem>
	<para><function>action_func</function> - pointer to
	  the driver's <function>xxx_action</function> function.
	  <funcsynopsis>
	    <funcprototype>
	      <funcdef>static void
		<function>xxx_action</function>
	      </funcdef>
	      <paramdef>
		<parameter>struct cam_sim *sim</parameter>,
		<parameter>union ccb *ccb</parameter>
	      </paramdef>
	    </funcprototype>
	  </funcsynopsis></para>
      </listitem>

      <listitem>
	<para><function>poll_func</function> - pointer to
	  the driver's <function>xxx_poll()</function>
	  <funcsynopsis>
	    <funcprototype>
	      <funcdef>static void
		<function>xxx_poll</function>
	      </funcdef>
	      <paramdef>
		<parameter>struct cam_sim *sim</parameter>
	      </paramdef>
	    </funcprototype>
	  </funcsynopsis></para>
      </listitem>

      <listitem>
	<para>driver_name - the name of the actual driver,
	  such as <quote>ncr</quote> or
	  <quote>wds</quote>.</para>
      </listitem>

      <listitem>
	<para><varname remap="structname">softc</varname> - pointer to the driver's
	  internal descriptor for this SCSI card.  This pointer will
	  be used by the driver in future to get private
	  data.</para>
      </listitem>

      <listitem>
	<para>unit - the controller unit number, for example
	  for controller <quote>mps0</quote> this number will be
	  0</para>
      </listitem>

      <listitem>
	<para>mtx - Lock associated with this SIM. For SIMs that don't
	know about locking, pass in Giant. For SIMs that do, pass in
	the lock used to guard this SIM's data structures. This lock
	will be held when xxx_action and xxx_poll are called.</para>
      </listitem>

      <listitem>
	<para>max_dev_transactions - maximal number of simultaneous
	  transactions per SCSI target in the non-tagged mode.  This
	  value will be almost universally equal to 1, with possible
	  exceptions only for the non-SCSI cards.  Also the drivers
	  that hope to take advantage by preparing one transaction
	  while another one is executed may set it to 2 but this does
	  not seem to be worth the complexity.</para>
      </listitem>

      <listitem>
	<para>max_tagged_dev_transactions - the same thing, but in the
	  tagged mode.  Tags are the SCSI way to initiate multiple
	  transactions on a device: each transaction is assigned a
	  unique tag and the transaction is sent to the device.  When
	  the device completes some transaction it sends back the
	  result together with the tag so that the SCSI adapter (and
	  the driver) can tell which transaction was completed.  This
	  argument is also known as the maximal tag depth.  It depends
	  on the abilities of the SCSI adapter.</para>
      </listitem>

    </itemizedlist>

    <para>Finally we register the SCSI buses associated with our SCSI
      adapter<indexterm><primary>SCSI</primary><secondary>adapter</secondary></indexterm>:</para>

    <programlisting>    if(xpt_bus_register(sim, softc, bus_number) != CAM_SUCCESS) {
        cam_sim_free(sim, /*free_devq*/ TRUE);
        error; /* some code to handle the error */
    }</programlisting>

    <para>If there is one <varname remap="structname">devq</varname> structure per
      SCSI bus (i.e., we consider a card with multiple buses as
      multiple cards with one bus each) then the bus number will
      always be 0, otherwise each bus on the SCSI card should be get a
      distinct number.  Each bus needs its own separate structure
      cam_sim.</para>

    <para>After that our controller is completely hooked to the CAM
      system.  The value of <varname remap="structname">devq</varname> can be
      discarded now: sim will be passed as an argument in all further
      calls from CAM and devq can be derived from it.</para>

    <para>CAM provides the framework for such asynchronous events.
      Some events originate from the lower levels (the SIM drivers),
      some events originate from the peripheral drivers, some events
      originate from the CAM subsystem itself.  Any driver can
      register callbacks for some types of the asynchronous events, so
      that it would be notified if these events occur.</para>

    <para>A typical example of such an event is a device reset.  Each
      transaction and event identifies the devices to which it applies
      by the means of <quote>path</quote>.  The target-specific events
      normally occur during a transaction with this device.  So the
      path from that transaction may be re-used to report this event
      (this is safe because the event path is copied in the event
      reporting routine but not deallocated nor passed anywhere
      further).  Also it is safe to allocate paths dynamically at any
      time including the interrupt routines, although that incurs
      certain overhead, and a possible problem with this approach is
      that there may be no free memory at that time.  For a bus reset
      event we need to define a wildcard path including all devices on
      the bus.  So we can create the path for the future bus reset
      events in advance and avoid problems with the future memory
      shortage:</para>

    <programlisting>    struct cam_path *path;

    if(xpt_create_path(&amp;path, /*periph*/NULL,
                cam_sim_path(sim), CAM_TARGET_WILDCARD,
                CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
        xpt_bus_deregister(cam_sim_path(sim));
        cam_sim_free(sim, /*free_devq*/TRUE);
        error; /* some code to handle the error */
    }

    softc-&gt;wpath = path;
    softc-&gt;sim = sim;</programlisting>

    <para>As you can see the path includes:</para>

    <itemizedlist>
      <listitem>
	<para>ID of the peripheral driver (NULL here because we have
	  none)</para>
      </listitem>

      <listitem>
	<para>ID of the SIM driver
	  (<function>cam_sim_path(sim)</function>)</para>
      </listitem>

      <listitem>
	<para>SCSI target number of the device (CAM_TARGET_WILDCARD
	  means <quote>all devices</quote>)</para>
      </listitem>

      <listitem>
	<para>SCSI LUN number of the subdevice (CAM_LUN_WILDCARD means
	  <quote>all LUNs</quote>)</para>
      </listitem>
    </itemizedlist>

    <para>If the driver can not allocate this path it will not be able
      to work normally, so in that case we dismantle that SCSI
      bus.</para>

    <para>And we save the path pointer in the
      <varname remap="structname">softc</varname> structure for future use.  After
      that we save the value of sim (or we can also discard it on the
      exit from <function>xxx_probe()</function> if we wish).</para>

    <para>That is all for a minimalistic initialization.  To do things
      right there is one more issue left.</para>

    <para>For a SIM driver there is one particularly interesting
      event: when a target device is considered lost.  In this case
      resetting the SCSI negotiations with this device may be a good
      idea.  So we register a callback for this event with CAM.  The
      request is passed to CAM by requesting CAM action on a CAM
      control block for this type of request:</para>

    <programlisting>    struct ccb_setasync csa;

    xpt_setup_ccb(&amp;csa.ccb_h, path, /*priority*/5);
    csa.ccb_h.func_code = XPT_SASYNC_CB;
    csa.event_enable = AC_LOST_DEVICE;
    csa.callback = xxx_async;
    csa.callback_arg = sim;
    xpt_action((union ccb *)&amp;csa);</programlisting>

    <para>Now we take a look at the <function>xxx_action()</function>
      and <function>xxx_poll()</function> driver entry points.</para>

    <para>
      <funcsynopsis>
	<funcprototype>
	  <funcdef>static void
	    <function>xxx_action</function>
	  </funcdef>
	  <paramdef>
	    <parameter>struct cam_sim *sim</parameter>,
	    <parameter>union ccb *ccb</parameter>
	  </paramdef>
	</funcprototype>
      </funcsynopsis></para>

    <para>Do some action on request of the CAM subsystem.  Sim
      describes the SIM for the request, CCB is the request itself.
      CCB stands for <quote>CAM Control Block</quote>.  It is a union
      of many specific instances, each describing arguments for some
      type of transactions.  All of these instances share the CCB
      header where the common part of arguments is stored.</para>

    <para>CAM supports the SCSI controllers working in both initiator
      (<quote>normal</quote>) mode and target (simulating a SCSI
      device) mode.  Here we only consider the part relevant to the
      initiator mode.</para>

    <para>There are a few function and macros (in other words,
      methods) defined to access the public data in the struct
      sim:</para>

    <itemizedlist>
      <listitem>
	<para><function>cam_sim_path(sim)</function> - the path ID
	  (see above)</para>
      </listitem>

      <listitem>
	<para><function>cam_sim_name(sim)</function> - the name of the
	  sim</para>
      </listitem>

      <listitem>
	<para><function>cam_sim_softc(sim)</function> - the pointer to
	  the softc (driver private data) structure</para>
      </listitem>

      <listitem>
	<para><function> cam_sim_unit(sim)</function> - the unit
	  number</para>
      </listitem>

      <listitem>
	<para><function> cam_sim_bus(sim)</function> - the bus
	  ID</para>
      </listitem>
    </itemizedlist>

    <para>To identify the device, <function>xxx_action()</function>
      can get the unit number and pointer to its structure softc using
      these functions.</para>

    <para>The type of request is stored in
      <varname remap="structfield">ccb-&gt;ccb_h.func_code</varname>.  So
      generally <function>xxx_action()</function> consists of a big
      switch:</para>

    <programlisting>    struct xxx_softc *softc = (struct xxx_softc *) cam_sim_softc(sim);
    struct ccb_hdr *ccb_h = &amp;ccb-&gt;ccb_h;
    int unit = cam_sim_unit(sim);
    int bus = cam_sim_bus(sim);

    switch(ccb_h-&gt;func_code) {
    case ...:
        ...
    default:
        ccb_h-&gt;status = CAM_REQ_INVALID;
        xpt_done(ccb);
        break;
    }</programlisting>

    <para>As can be seen from the default case (if an unknown command
      was received) the return code of the command is set into
      <varname remap="structfield">ccb-&gt;ccb_h.status</varname> and the
      completed CCB is returned back to CAM by calling
      <function>xpt_done(ccb)</function>.</para>

    <para><function>xpt_done()</function> does not have to be called
      from <function>xxx_action()</function>: For example an I/O
      request may be enqueued inside the SIM driver and/or its SCSI
      controller.  Then when the device would post an interrupt
      signaling that the processing of this request is complete
      <function>xpt_done()</function> may be called from the interrupt
      handling routine.</para>

    <para>Actually, the CCB status is not only assigned as a return
      code but a CCB has some status all the time.  Before CCB is
      passed to the <function>xxx_action()</function> routine it gets
      the status CCB_REQ_INPROG meaning that it is in progress.  There
      are a surprising number of status values defined in
      <filename>/sys/cam/cam.h</filename> which should be able to
      represent the status of a request in great detail.  More
      interesting yet, the status is in fact a <quote>bitwise
      or</quote> of an enumerated status value (the lower 6 bits) and
      possible additional flag-like bits (the upper bits).  The
      enumerated values will be discussed later in more detail.  The
      summary of them can be found in the Errors Summary section.  The
      possible status flags are:</para>

    <itemizedlist>
      <listitem>
	<para><emphasis>CAM_DEV_QFRZN</emphasis> - if the SIM driver
	  gets a serious error (for example, the device does not
	  respond to the selection or breaks the SCSI protocol) when
	  processing a CCB it should freeze the request queue by
	  calling <function>xpt_freeze_simq()</function>, return the
	  other enqueued but not processed yet CCBs for this device
	  back to the CAM queue, then set this flag for the
	  troublesome CCB and call <function>xpt_done()</function>.
	  This flag causes the CAM subsystem to unfreeze the queue
	  after it handles the error.</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_AUTOSNS_VALID</emphasis> - if the
	  device returned an error condition and the flag
	  CAM_DIS_AUTOSENSE is not set in CCB the SIM driver must
	  execute the REQUEST SENSE command automatically to extract
	  the sense (extended error information) data from the device.
	  If this attempt was successful the sense data should be
	  saved in the CCB and this flag set.</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_RELEASE_SIMQ</emphasis> - like
	  CAM_DEV_QFRZN but used in case there is some problem (or
	  resource shortage) with the SCSI controller itself.  Then
	  all the future requests to the controller should be stopped
	  by <function>xpt_freeze_simq()</function>.  The controller
	  queue will be restarted after the SIM driver overcomes the
	  shortage and informs CAM by returning some CCB with this
	  flag set.</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_SIM_QUEUED</emphasis> - when SIM puts a
	  CCB into its request queue this flag should be set (and
	  removed when this CCB gets dequeued before being returned
	  back to CAM).  This flag is not used anywhere in the CAM
	  code now, so its purpose is purely diagnostic.</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_QOS_VALID</emphasis> - The QOS data
	  is now valid.</para>
      </listitem>
    </itemizedlist>

    <para>The function <function>xxx_action()</function> is not
      allowed to sleep, so all the synchronization for resource access
      must be done using SIM or device queue freezing.  Besides the
      aforementioned flags the CAM subsystem provides functions
      <function>xpt_release_simq()</function> and
      <function>xpt_release_devq()</function> to unfreeze the queues
      directly, without passing a CCB to CAM.</para>

    <para>The CCB header contains the following fields:</para>

    <itemizedlist>
      <listitem>
	<para><emphasis>path</emphasis> - path ID for the
	  request</para>
      </listitem>

      <listitem>
	<para><emphasis>target_id</emphasis> - target device ID for
	  the request</para>
      </listitem>

      <listitem>
	<para><emphasis>target_lun</emphasis> - LUN ID of the target
	  device</para>
      </listitem>

      <listitem>
	<para><emphasis>timeout</emphasis> - timeout interval for this
	  command, in milliseconds</para>
      </listitem>

      <listitem>
	<para><emphasis>timeout_ch</emphasis> - a convenience place
	  for the SIM driver to store the timeout handle (the CAM
	  subsystem itself does not make any assumptions about
	  it)</para>
      </listitem>

      <listitem>
	<para><emphasis>flags</emphasis> - various bits of information
	  about the request spriv_ptr0, spriv_ptr1 - fields reserved
	  for private use by the SIM driver (such as linking to the
	  SIM queues or SIM private control blocks); actually, they
	  exist as unions: spriv_ptr0 and spriv_ptr1 have the type
	  (void *), spriv_field0 and spriv_field1 have the type
	  unsigned long, sim_priv.entries[0].bytes and
	  sim_priv.entries[1].bytes are byte arrays of the size
	  consistent with the other incarnations of the union and
	  sim_priv.bytes is one array, twice bigger.</para>
      </listitem>
    </itemizedlist>

    <para>The recommended way of using the SIM private fields of CCB
      is to define some meaningful names for them and use these
      meaningful names in the driver, like:</para>

    <programlisting>#define ccb_some_meaningful_name    sim_priv.entries[0].bytes
#define ccb_hcb spriv_ptr1 /* for hardware control block */</programlisting>

    <para>The most common initiator mode requests are:</para>

    <itemizedlist>
      <listitem>
	<para><emphasis>XPT_SCSI_IO</emphasis> - execute an I/O
	  transaction</para>

	<para>The instance <quote>struct ccb_scsiio csio</quote> of
	  the union ccb is used to transfer the arguments.  They
	  are:</para>

	<itemizedlist>
	  <listitem>
	    <para><emphasis>cdb_io</emphasis> - pointer to the SCSI
	      command buffer or the buffer itself</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>cdb_len</emphasis> - SCSI command
	      length</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>data_ptr</emphasis> - pointer to the data
	      buffer (gets a bit complicated if scatter/gather is
	      used)</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>dxfer_len</emphasis> - length of the data
	      to transfer</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>sglist_cnt</emphasis> - counter of the
	      scatter/gather segments</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>scsi_status</emphasis> - place to return
	      the SCSI status</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>sense_data</emphasis> - buffer for the
	      SCSI sense information if the command returns an error
	      (the SIM driver is supposed to run the REQUEST SENSE
	      command automatically in this case if the CCB flag
	      CAM_DIS_AUTOSENSE is not set)</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>sense_len</emphasis> - the length of that
	      buffer (if it happens to be higher than size of
	      sense_data the SIM driver must silently assume the
	      smaller value) resid, sense_resid - if the transfer of
	      data or SCSI sense returned an error these are the
	      returned counters of the residual (not transferred)
	      data.  They do not seem to be especially meaningful, so
	      in a case when they are difficult to compute (say,
	      counting bytes in the SCSI controller's FIFO buffer) an
	      approximate value will do as well.  For a successfully
	      completed transfer they must be set to
	      zero.</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>tag_action</emphasis> - the kind of tag to
	      use:</para>

	    <itemizedlist>
	      <listitem>
		<para>CAM_TAG_ACTION_NONE - do not use tags for this
		  transaction</para>
	      </listitem>

	      <listitem>
		<para>MSG_SIMPLE_Q_TAG, MSG_HEAD_OF_Q_TAG,
		  MSG_ORDERED_Q_TAG - value equal to the appropriate
		  tag message (see /sys/cam/scsi/scsi_message.h); this
		  gives only the tag type, the SIM driver must assign
		  the tag value itself</para>
	      </listitem>
	    </itemizedlist>
	  </listitem>
	</itemizedlist>

	<para>The general logic of handling this request is the
	  following:</para>

	<para>The first thing to do is to check for possible races, to
	  make sure that the command did not get aborted when it was
	  sitting in the queue:</para>

	<programlisting>    struct ccb_scsiio *csio = &amp;ccb-&gt;csio;

    if ((ccb_h-&gt;status &amp; CAM_STATUS_MASK) != CAM_REQ_INPROG) {
        xpt_done(ccb);
        return;
    }</programlisting>

	<para>Also we check that the device is supported at all by our
	  controller:</para>

	<programlisting>    if(ccb_h-&gt;target_id &gt; OUR_MAX_SUPPORTED_TARGET_ID
    || cch_h-&gt;target_id == OUR_SCSI_CONTROLLERS_OWN_ID) {
        ccb_h-&gt;status = CAM_TID_INVALID;
        xpt_done(ccb);
        return;
    }
    if(ccb_h-&gt;target_lun &gt; OUR_MAX_SUPPORTED_LUN) {
        ccb_h-&gt;status = CAM_LUN_INVALID;
        xpt_done(ccb);
        return;
    }</programlisting>

	<para>Then allocate whatever data structures (such as
	  card-dependent hardware control
	  block<indexterm><primary>hardware control
	    block</primary></indexterm>) we need to process this
	  request.  If we can not then freeze the SIM queue and
	  remember that we have a pending operation, return the CCB
	  back and ask CAM to re-queue it.  Later when the resources
	  become available the SIM queue must be unfrozen by returning
	  a ccb with the <literal>CAM_SIMQ_RELEASE</literal> bit set
	  in its status.  Otherwise, if all went well, link the CCB
	  with the hardware control block (HCB) and mark it as
	  queued.</para>

	<programlisting>    struct xxx_hcb *hcb = allocate_hcb(softc, unit, bus);

    if(hcb == NULL) {
        softc-&gt;flags |= RESOURCE_SHORTAGE;
        xpt_freeze_simq(sim, /*count*/1);
        ccb_h-&gt;status = CAM_REQUEUE_REQ;
        xpt_done(ccb);
        return;
    }

    hcb-&gt;ccb = ccb; ccb_h-&gt;ccb_hcb = (void *)hcb;
    ccb_h-&gt;status |= CAM_SIM_QUEUED;</programlisting>

	<para>Extract the target data from CCB into the hardware
	  control block.  Check if we are asked to assign a tag and if
	  yes then generate an unique tag and build the SCSI tag
	  messages.  The SIM driver is also responsible for
	  negotiations with the devices to set the maximal mutually
	  supported bus width, synchronous rate and offset.</para>

	<programlisting>    hcb-&gt;target = ccb_h-&gt;target_id; hcb-&gt;lun = ccb_h-&gt;target_lun;
    generate_identify_message(hcb);
    if( ccb_h-&gt;tag_action != CAM_TAG_ACTION_NONE )
        generate_unique_tag_message(hcb, ccb_h-&gt;tag_action);
    if( !target_negotiated(hcb) )
        generate_negotiation_messages(hcb);</programlisting>

	<para>Then set up the SCSI command.  The command storage may
	  be specified in the CCB in many interesting ways, specified
	  by the CCB flags.  The command buffer can be contained in
	  CCB or pointed to, in the latter case the pointer may be
	  physical or virtual.  Since the hardware commonly needs
	  physical address we always convert the address to the
	  physical one, typically using the busdma API.</para>

	<para>In case if a physical address is
	  requested it is OK to return the CCB with the status
	  <errorname>CAM_REQ_INVALID</errorname>, the current drivers
	  do that.  If necessary a physical address can be also
	  converted or mapped back to a virtual address but with
	  big pain, so we do not do that.</para>

	<programlisting>    if(ccb_h-&gt;flags &amp; CAM_CDB_POINTER) {
        /* CDB is a pointer */
        if(!(ccb_h-&gt;flags &amp; CAM_CDB_PHYS)) {
            /* CDB pointer is virtual */
            hcb-&gt;cmd = vtobus(csio-&gt;cdb_io.cdb_ptr);
        } else {
            /* CDB pointer is physical */
            hcb-&gt;cmd = csio-&gt;cdb_io.cdb_ptr ;
        }
    } else {
        /* CDB is in the ccb (buffer) */
        hcb-&gt;cmd = vtobus(csio-&gt;cdb_io.cdb_bytes);
    }
    hcb-&gt;cmdlen = csio-&gt;cdb_len;</programlisting>

	<para>Now it is time to set up the data.  Again, the data
	  storage may be specified in the CCB in many interesting
	  ways, specified by the CCB flags.  First we get the
	  direction of the data transfer.  The simplest case is if
	  there is no data to transfer:</para>

	<programlisting>    int dir = (ccb_h-&gt;flags &amp; CAM_DIR_MASK);

    if (dir == CAM_DIR_NONE)
        goto end_data;</programlisting>

	<para>Then we check if the data is in one chunk or in a
	  scatter-gather list, and the addresses are physical or
	  virtual.  The SCSI controller may be able to handle only a
	  limited number of chunks of limited length.  If the request
	  hits this limitation we return an error.  We use a special
	  function to return the CCB to handle in one place the HCB
	  resource shortages.  The functions to add chunks are
	  driver-dependent, and here we leave them without detailed
	  implementation.  See description of the SCSI command (CDB)
	  handling for the details on the address-translation issues.
	  If some variation is too difficult or impossible to
	  implement with a particular card it is OK to return the
	  status <errorname>CAM_REQ_INVALID</errorname>.  Actually, it
	  seems like the scatter-gather ability is not used anywhere
	  in the CAM code now.  But at least the case for a single
	  non-scattered virtual buffer must be implemented, it is
	  actively used by CAM.</para>

	<programlisting>    int rv;

    initialize_hcb_for_data(hcb);

    if((!(ccb_h-&gt;flags &amp; CAM_SCATTER_VALID)) {
        /* single buffer */
        if(!(ccb_h-&gt;flags &amp; CAM_DATA_PHYS)) {
            rv = add_virtual_chunk(hcb, csio-&gt;data_ptr, csio-&gt;dxfer_len, dir);
            }
        } else {
            rv = add_physical_chunk(hcb, csio-&gt;data_ptr, csio-&gt;dxfer_len, dir);
        }
    } else {
        int i;
        struct bus_dma_segment *segs;
        segs = (struct bus_dma_segment *)csio-&gt;data_ptr;

        if ((ccb_h-&gt;flags &amp; CAM_SG_LIST_PHYS) != 0) {
            /* The SG list pointer is physical */
            rv = setup_hcb_for_physical_sg_list(hcb, segs, csio-&gt;sglist_cnt);
        } else if (!(ccb_h-&gt;flags &amp; CAM_DATA_PHYS)) {
            /* SG buffer pointers are virtual */
            for (i = 0; i &lt; csio-&gt;sglist_cnt; i++) {
                rv = add_virtual_chunk(hcb, segs[i].ds_addr,
                    segs[i].ds_len, dir);
                if (rv != CAM_REQ_CMP)
                    break;
            }
        } else {
            /* SG buffer pointers are physical */
            for (i = 0; i &lt; csio-&gt;sglist_cnt; i++) {
                rv = add_physical_chunk(hcb, segs[i].ds_addr,
                    segs[i].ds_len, dir);
                if (rv != CAM_REQ_CMP)
                    break;
            }
        }
    }
    if(rv != CAM_REQ_CMP) {
        /* we expect that add_*_chunk() functions return CAM_REQ_CMP
         * if they added a chunk successfully, CAM_REQ_TOO_BIG if
         * the request is too big (too many bytes or too many chunks),
         * CAM_REQ_INVALID in case of other troubles
         */
        free_hcb_and_ccb_done(hcb, ccb, rv);
        return;
    }
    end_data:</programlisting>

	<para>If disconnection is disabled for this CCB we pass this
	  information to the hcb:</para>

	<programlisting>    if(ccb_h-&gt;flags &amp; CAM_DIS_DISCONNECT)
        hcb_disable_disconnect(hcb);</programlisting>

	<para>If the controller is able to run REQUEST SENSE command
	  all by itself then the value of the flag CAM_DIS_AUTOSENSE
	  should also be passed to it, to prevent automatic REQUEST
	  SENSE if the CAM subsystem does not want it.</para>

	<para>The only thing left is to set up the timeout, pass our
	  hcb to the hardware and return, the rest will be done by the
	  interrupt handler (or timeout handler).</para>

	<programlisting>    ccb_h-&gt;timeout_ch = timeout(xxx_timeout, (caddr_t) hcb,
        (ccb_h-&gt;timeout * hz) / 1000); /* convert milliseconds to ticks */
    put_hcb_into_hardware_queue(hcb);
    return;</programlisting>

	<para>And here is a possible implementation of the function
	  returning CCB:</para>

	<programlisting>    static void
    free_hcb_and_ccb_done(struct xxx_hcb *hcb, union ccb *ccb, u_int32_t status)
    {
        struct xxx_softc *softc = hcb-&gt;softc;

        ccb-&gt;ccb_h.ccb_hcb = 0;
        if(hcb != NULL) {
            untimeout(xxx_timeout, (caddr_t) hcb, ccb-&gt;ccb_h.timeout_ch);
            /* we're about to free a hcb, so the shortage has ended */
            if(softc-&gt;flags &amp; RESOURCE_SHORTAGE)  {
                softc-&gt;flags &amp;= ~RESOURCE_SHORTAGE;
                status |= CAM_RELEASE_SIMQ;
            }
            free_hcb(hcb); /* also removes hcb from any internal lists */
        }
        ccb-&gt;ccb_h.status = status |
            (ccb-&gt;ccb_h.status &amp; ~(CAM_STATUS_MASK|CAM_SIM_QUEUED));
        xpt_done(ccb);
    }</programlisting>
      </listitem>

      <listitem>
	<para><emphasis>XPT_RESET_DEV</emphasis> - send the SCSI
	  <quote>BUS DEVICE RESET</quote> message to a device</para>

	<para>There is no data transferred in CCB except the header
	  and the most interesting argument of it is target_id.
	  Depending on the controller hardware a hardware control
	  block just like for the XPT_SCSI_IO request may be
	  constructed (see XPT_SCSI_IO request description) and sent
	  to the controller or the SCSI controller may be immediately
	  programmed to send this RESET message to the device or this
	  request may be just not supported (and return the status
	  <errorname>CAM_REQ_INVALID</errorname>).  Also on completion
	  of the request all the disconnected transactions for this
	  target must be aborted (probably in the interrupt
	  routine).</para>

	<para>Also all the current negotiations for the target are
	  lost on reset, so they might be cleaned too.  Or they
	  clearing may be deferred, because anyway the target would
	  request re-negotiation on the next
	  transaction.</para>
      </listitem>

      <listitem>
	<para><emphasis>XPT_RESET_BUS</emphasis> - send the RESET
	  signal to the SCSI bus</para>

	<para>No arguments are passed in the CCB, the only interesting
	  argument is the SCSI bus indicated by the struct sim
	  pointer.</para>

	<para>A minimalistic implementation would forget the SCSI
	  negotiations for all the devices on the bus and return the
	  status CAM_REQ_CMP.</para>

	<para>The proper implementation would in addition actually
	  reset the SCSI bus (possible also reset the SCSI controller)
	  and mark all the CCBs being processed, both those in the
	  hardware queue and those being disconnected, as done with
	  the status CAM_SCSI_BUS_RESET. Like:</para>

	<programlisting>    int targ, lun;
    struct xxx_hcb *h, *hh;
    struct ccb_trans_settings neg;
    struct cam_path *path;

    /* The SCSI bus reset may take a long time, in this case its completion
     * should be checked by interrupt or timeout. But for simplicity
     * we assume here that it is really fast.
     */
    reset_scsi_bus(softc);

    /* drop all enqueued CCBs */
    for(h = softc-&gt;first_queued_hcb; h != NULL; h = hh) {
        hh = h-&gt;next;
        free_hcb_and_ccb_done(h, h-&gt;ccb, CAM_SCSI_BUS_RESET);
    }

    /* the clean values of negotiations to report */
    neg.bus_width = 8;
    neg.sync_period = neg.sync_offset = 0;
    neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
        | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);

    /* drop all disconnected CCBs and clean negotiations  */
    for(targ=0; targ &lt;= OUR_MAX_SUPPORTED_TARGET; targ++) {
        clean_negotiations(softc, targ);

        /* report the event if possible */
        if(xpt_create_path(&amp;path, /*periph*/NULL,
                cam_sim_path(sim), targ,
                CAM_LUN_WILDCARD) == CAM_REQ_CMP) {
            xpt_async(AC_TRANSFER_NEG, path, &amp;neg);
            xpt_free_path(path);
        }

        for(lun=0; lun &lt;= OUR_MAX_SUPPORTED_LUN; lun++)
            for(h = softc-&gt;first_discon_hcb[targ][lun]; h != NULL; h = hh) {
                hh=h-&gt;next;
                free_hcb_and_ccb_done(h, h-&gt;ccb, CAM_SCSI_BUS_RESET);
            }
    }

    ccb-&gt;ccb_h.status = CAM_REQ_CMP;
    xpt_done(ccb);

    /* report the event */
    xpt_async(AC_BUS_RESET, softc-&gt;wpath, NULL);
    return;</programlisting>

	<para>Implementing the SCSI bus reset as a function may be a
	  good idea because it would be re-used by the timeout
	  function as a last resort if the things go
	  wrong.</para>
      </listitem>

      <listitem>
	<para><emphasis>XPT_ABORT</emphasis> - abort the specified
	  CCB</para>

	<para>The arguments are transferred in the instance
	  <quote>struct ccb_abort cab</quote> of the union ccb.  The
	  only argument field in it is:</para>

	<para><emphasis>abort_ccb</emphasis> - pointer to the CCB to
	  be aborted</para>

	<para>If the abort is not supported just return the status
	  CAM_UA_ABORT.  This is also the easy way to minimally
	  implement this call, return CAM_UA_ABORT in any case.</para>

	<para>The hard way is to implement this request honestly.
	  First check that abort applies to a SCSI transaction:</para>

	<programlisting>    struct ccb *abort_ccb;
    abort_ccb = ccb-&gt;cab.abort_ccb;

    if(abort_ccb-&gt;ccb_h.func_code != XPT_SCSI_IO) {
        ccb-&gt;ccb_h.status = CAM_UA_ABORT;
        xpt_done(ccb);
        return;
    }</programlisting>

	<para>Then it is necessary to find this CCB in our queue.
	  This can be done by walking the list of all our hardware
	  control blocks in search for one associated with this
	  CCB:</para>

	<programlisting>    struct xxx_hcb *hcb, *h;

    hcb = NULL;

    /* We assume that softc-&gt;first_hcb is the head of the list of all
     * HCBs associated with this bus, including those enqueued for
     * processing, being processed by hardware and disconnected ones.
     */
    for(h = softc-&gt;first_hcb; h != NULL; h = h-&gt;next) {
        if(h-&gt;ccb == abort_ccb) {
            hcb = h;
            break;
        }
    }

    if(hcb == NULL) {
        /* no such CCB in our queue */
        ccb-&gt;ccb_h.status = CAM_PATH_INVALID;
        xpt_done(ccb);
        return;
    }

    hcb=found_hcb;</programlisting>

	<para>Now we look at the current processing status of the HCB.
	  It may be either sitting in the queue waiting to be sent to
	  the SCSI bus, being transferred right now, or disconnected
	  and waiting for the result of the command, or actually
	  completed by hardware but not yet marked as done by
	  software.  To make sure that we do not get in any races with
	  hardware we mark the HCB as being aborted, so that if this
	  HCB is about to be sent to the SCSI bus the SCSI controller
	  will see this flag and skip it.</para>

	<programlisting>    int hstatus;

    /* shown as a function, in case special action is needed to make
     * this flag visible to hardware
     */
    set_hcb_flags(hcb, HCB_BEING_ABORTED);

    abort_again:

    hstatus = get_hcb_status(hcb);
    switch(hstatus) {
    case HCB_SITTING_IN_QUEUE:
        remove_hcb_from_hardware_queue(hcb);
        /* FALLTHROUGH */
    case HCB_COMPLETED:
        /* this is an easy case */
        free_hcb_and_ccb_done(hcb, abort_ccb, CAM_REQ_ABORTED);
        break;</programlisting>

	<para>If the CCB is being transferred right now we would like
	  to signal to the SCSI controller in some hardware-dependent
	  way that we want to abort the current transfer.  The SCSI
	  controller would set the SCSI ATTENTION signal and when the
	  target responds to it send an ABORT message.  We also reset
	  the timeout to make sure that the target is not sleeping
	  forever.  If the command would not get aborted in some
	  reasonable time like 10 seconds the timeout routine would go
	  ahead and reset the whole SCSI bus.  Since the command
	  will be aborted in some reasonable time we can just return
	  the abort request now as successfully completed, and mark
	  the aborted CCB as aborted (but not mark it as done
	  yet).</para>

	<programlisting>    case HCB_BEING_TRANSFERRED:
        untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb-&gt;ccb_h.timeout_ch);
        abort_ccb-&gt;ccb_h.timeout_ch =
            timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);
        abort_ccb-&gt;ccb_h.status = CAM_REQ_ABORTED;
        /* ask the controller to abort that HCB, then generate
         * an interrupt and stop
         */
        if(signal_hardware_to_abort_hcb_and_stop(hcb) &lt; 0) {
            /* oops, we missed the race with hardware, this transaction
             * got off the bus before we aborted it, try again */
            goto abort_again;
        }

        break;</programlisting>

	<para>If the CCB is in the list of disconnected then set it up
	  as an abort request and re-queue it at the front of hardware
	  queue.  Reset the timeout and report the abort request to be
	  completed.</para>

	<programlisting>    case HCB_DISCONNECTED:
        untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb-&gt;ccb_h.timeout_ch);
        abort_ccb-&gt;ccb_h.timeout_ch =
            timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);
        put_abort_message_into_hcb(hcb);
        put_hcb_at_the_front_of_hardware_queue(hcb);
        break;
    }
    ccb-&gt;ccb_h.status = CAM_REQ_CMP;
    xpt_done(ccb);
    return;</programlisting>

	<para>That is all for the ABORT request, although there is one
	  more issue.  As the ABORT message cleans all the
	  ongoing transactions on a LUN we have to mark all the other
	  active transactions on this LUN as aborted.  That should be
	  done in the interrupt routine, after the transaction gets
	  aborted.</para>

	<para>Implementing the CCB abort as a function may be quite a
	  good idea, this function can be re-used if an I/O
	  transaction times out.  The only difference would be that
	  the timed out transaction would return the status
	  CAM_CMD_TIMEOUT for the timed out request.  Then the case
	  XPT_ABORT would be small, like that:</para>

	<programlisting>    case XPT_ABORT:
        struct ccb *abort_ccb;
        abort_ccb = ccb-&gt;cab.abort_ccb;

        if(abort_ccb-&gt;ccb_h.func_code != XPT_SCSI_IO) {
            ccb-&gt;ccb_h.status = CAM_UA_ABORT;
            xpt_done(ccb);
            return;
        }
        if(xxx_abort_ccb(abort_ccb, CAM_REQ_ABORTED) &lt; 0)
            /* no such CCB in our queue */
            ccb-&gt;ccb_h.status = CAM_PATH_INVALID;
        else
            ccb-&gt;ccb_h.status = CAM_REQ_CMP;
        xpt_done(ccb);
        return;</programlisting>
      </listitem>

      <listitem>
	<para><emphasis>XPT_SET_TRAN_SETTINGS</emphasis> - explicitly
	  set values of SCSI transfer settings</para>

	<para>The arguments are transferred in the instance
	  <quote>struct ccb_trans_setting cts</quote> of the union
	  ccb:</para>

	<itemizedlist>
	  <listitem>
	    <para><emphasis>valid</emphasis> - a bitmask showing which
	      settings should be updated:</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>CCB_TRANS_SYNC_RATE_VALID</emphasis> -
	      synchronous transfer rate</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>CCB_TRANS_SYNC_OFFSET_VALID</emphasis> -
	      synchronous offset</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>CCB_TRANS_BUS_WIDTH_VALID</emphasis> - bus
	      width</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>CCB_TRANS_DISC_VALID</emphasis> - set
	      enable/disable disconnection</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>CCB_TRANS_TQ_VALID</emphasis> - set
	      enable/disable tagged queuing</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>flags</emphasis> - consists of two parts,
	      binary arguments and identification of sub-operations.
	      The binary arguments are:</para>

	    <itemizedlist>
	      <listitem>
		<para><emphasis>CCB_TRANS_DISC_ENB</emphasis> - enable
		  disconnection</para>
	      </listitem>

	      <listitem>
		<para><emphasis>CCB_TRANS_TAG_ENB</emphasis> - enable
		  tagged queuing</para>
	      </listitem>
	    </itemizedlist>
	  </listitem>

	  <listitem>
	    <para>the sub-operations are:</para>

	    <itemizedlist>
	      <listitem>
		<para><emphasis>CCB_TRANS_CURRENT_SETTINGS</emphasis>
		  - change the current negotiations</para>
	      </listitem>

	      <listitem>
		<para><emphasis>CCB_TRANS_USER_SETTINGS</emphasis> -
		  remember the desired user values sync_period,
		  sync_offset - self-explanatory, if sync_offset==0
		  then the asynchronous mode is requested bus_width -
		  bus width, in bits (not bytes)</para>
	      </listitem>
	    </itemizedlist>
	  </listitem>
	</itemizedlist>

	<para>Two sets of negotiated parameters are supported, the
	  user settings and the current settings.  The user settings
	  are not really used much in the SIM drivers, this is mostly
	  just a piece of memory where the upper levels can store (and
	  later recall) its ideas about the parameters.  Setting the
	  user parameters does not cause re-negotiation of the
	  transfer rates.  But when the SCSI controller does a
	  negotiation it must never set the values higher than the
	  user parameters, so it is essentially the top
	  boundary.</para>

	<para>The current settings are, as the name says, current.
	  Changing them means that the parameters must be
	  re-negotiated on the next transfer.  Again, these
	  <quote>new current settings</quote> are not supposed to be
	  forced on the device, just they are used as the initial step
	  of negotiations.  Also they must be limited by actual
	  capabilities of the SCSI controller: for example, if the
	  SCSI controller has 8-bit bus and the request asks to set
	  16-bit wide transfers this parameter must be silently
	  truncated to 8-bit transfers before sending it to the
	  device.</para>

	<para>One caveat is that the bus width and synchronous
	  parameters are per target while the disconnection and tag
	  enabling parameters are per lun.</para>

	<para>The recommended implementation is to keep 3 sets of
	  negotiated (bus width and synchronous transfer)
	  parameters:</para>

	<itemizedlist>
	  <listitem>
	    <para><emphasis>user</emphasis> - the user set, as
	      above</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>current</emphasis> - those actually in
	      effect</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>goal</emphasis> - those requested by
	      setting of the <quote>current</quote>
	      parameters</para>
	  </listitem>
	</itemizedlist>

	<para>The code looks like:</para>

	<programlisting>    struct ccb_trans_settings *cts;
    int targ, lun;
    int flags;

    cts = &amp;ccb-&gt;cts;
    targ = ccb_h-&gt;target_id;
    lun = ccb_h-&gt;target_lun;
    flags = cts-&gt;flags;
    if(flags &amp; CCB_TRANS_USER_SETTINGS) {
        if(flags &amp; CCB_TRANS_SYNC_RATE_VALID)
            softc-&gt;user_sync_period[targ] = cts-&gt;sync_period;
        if(flags &amp; CCB_TRANS_SYNC_OFFSET_VALID)
            softc-&gt;user_sync_offset[targ] = cts-&gt;sync_offset;
        if(flags &amp; CCB_TRANS_BUS_WIDTH_VALID)
            softc-&gt;user_bus_width[targ] = cts-&gt;bus_width;

        if(flags &amp; CCB_TRANS_DISC_VALID) {
            softc-&gt;user_tflags[targ][lun] &amp;= ~CCB_TRANS_DISC_ENB;
            softc-&gt;user_tflags[targ][lun] |= flags &amp; CCB_TRANS_DISC_ENB;
        }
        if(flags &amp; CCB_TRANS_TQ_VALID) {
            softc-&gt;user_tflags[targ][lun] &amp;= ~CCB_TRANS_TQ_ENB;
            softc-&gt;user_tflags[targ][lun] |= flags &amp; CCB_TRANS_TQ_ENB;
        }
    }
    if(flags &amp; CCB_TRANS_CURRENT_SETTINGS) {
        if(flags &amp; CCB_TRANS_SYNC_RATE_VALID)
            softc-&gt;goal_sync_period[targ] =
                max(cts-&gt;sync_period, OUR_MIN_SUPPORTED_PERIOD);
        if(flags &amp; CCB_TRANS_SYNC_OFFSET_VALID)
            softc-&gt;goal_sync_offset[targ] =
                min(cts-&gt;sync_offset, OUR_MAX_SUPPORTED_OFFSET);
        if(flags &amp; CCB_TRANS_BUS_WIDTH_VALID)
            softc-&gt;goal_bus_width[targ] = min(cts-&gt;bus_width, OUR_BUS_WIDTH);

        if(flags &amp; CCB_TRANS_DISC_VALID) {
            softc-&gt;current_tflags[targ][lun] &amp;= ~CCB_TRANS_DISC_ENB;
            softc-&gt;current_tflags[targ][lun] |= flags &amp; CCB_TRANS_DISC_ENB;
        }
        if(flags &amp; CCB_TRANS_TQ_VALID) {
            softc-&gt;current_tflags[targ][lun] &amp;= ~CCB_TRANS_TQ_ENB;
            softc-&gt;current_tflags[targ][lun] |= flags &amp; CCB_TRANS_TQ_ENB;
        }
    }
    ccb-&gt;ccb_h.status = CAM_REQ_CMP;
    xpt_done(ccb);
    return;</programlisting>

	<para>Then when the next I/O request will be processed it will
	  check if it has to re-negotiate, for example by calling the
	  function target_negotiated(hcb).  It can be implemented like
	  this:</para>

	<programlisting>    int
    target_negotiated(struct xxx_hcb *hcb)
    {
        struct softc *softc = hcb-&gt;softc;
        int targ = hcb-&gt;targ;

        if( softc-&gt;current_sync_period[targ] != softc-&gt;goal_sync_period[targ]
        || softc-&gt;current_sync_offset[targ] != softc-&gt;goal_sync_offset[targ]
        || softc-&gt;current_bus_width[targ] != softc-&gt;goal_bus_width[targ] )
            return 0; /* FALSE */
        else
            return 1; /* TRUE */
    }</programlisting>

	<para>After the values are re-negotiated the resulting values
	  must be assigned to both current and goal parameters, so for
	  future I/O transactions the current and goal parameters
	  would be the same and
	  <function>target_negotiated()</function> would return TRUE.
	  When the card is initialized (in
	  <function>xxx_attach()</function>) the current negotiation
	  values must be initialized to narrow asynchronous mode, the
	  goal and current values must be initialized to the maximal
	  values supported by controller.</para>

	<para><emphasis>XPT_GET_TRAN_SETTINGS</emphasis> - get values
	  of SCSI transfer settings</para>

	<para>This operations is the reverse of XPT_SET_TRAN_SETTINGS.
	  Fill up the CCB instance
	  <quote>struct ccb_trans_setting cts</quote> with data as
	  requested by the flags CCB_TRANS_CURRENT_SETTINGS or
	  CCB_TRANS_USER_SETTINGS (if both are set then the existing
	  drivers return the current settings).  Set all the bits in
	  the valid field.</para>

	<para><emphasis>XPT_CALC_GEOMETRY</emphasis> - calculate
	  logical (BIOS)<indexterm><primary>BIOS</primary></indexterm>
	  geometry of the disk</para>

	<para>The arguments are transferred in the instance
	  <quote>struct ccb_calc_geometry ccg</quote> of the union
	  ccb:</para>

	<itemizedlist>

	  <listitem>
	    <para><emphasis>block_size</emphasis> - input, block
	      (A.K.A sector) size in bytes</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>volume_size</emphasis> - input, volume
	      size in bytes</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>cylinders</emphasis> - output, logical
	      cylinders</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>heads</emphasis> - output, logical
	      heads</para>
	  </listitem>

	  <listitem>
	    <para><emphasis>secs_per_track</emphasis> - output,
	      logical sectors per track</para>
	  </listitem>
	</itemizedlist>

	<para>If the returned geometry differs much enough from what
	  the SCSI controller BIOS<indexterm><primary>SCSI</primary>
	  <secondary>BIOS</secondary></indexterm> thinks and a disk on
	  this SCSI controller is used as bootable the system may not
	  be able to boot.  The typical calculation example taken from
	  the aic7xxx driver is:</para>

	<programlisting>    struct    ccb_calc_geometry *ccg;
    u_int32_t size_mb;
    u_int32_t secs_per_cylinder;
    int   extended;

    ccg = &amp;ccb-&gt;ccg;
    size_mb = ccg-&gt;volume_size
        / ((1024L * 1024L) / ccg-&gt;block_size);
    extended = check_cards_EEPROM_for_extended_geometry(softc);

    if (size_mb &gt; 1024 &amp;&amp; extended) {
        ccg-&gt;heads = 255;
        ccg-&gt;secs_per_track = 63;
    } else {
        ccg-&gt;heads = 64;
        ccg-&gt;secs_per_track = 32;
    }
    secs_per_cylinder = ccg-&gt;heads * ccg-&gt;secs_per_track;
    ccg-&gt;cylinders = ccg-&gt;volume_size / secs_per_cylinder;
    ccb-&gt;ccb_h.status = CAM_REQ_CMP;
    xpt_done(ccb);
    return;</programlisting>

	<para>This gives the general idea, the exact calculation
	  depends on the quirks of the particular BIOS.  If BIOS
	  provides no way set the <quote>extended translation</quote>
	  flag in EEPROM this flag should normally be assumed equal to
	  1. Other popular geometries are:</para>

	<programlisting>    128 heads, 63 sectors - Symbios controllers
    16 heads, 63 sectors - old controllers</programlisting>

	<para>Some system BIOSes and SCSI BIOSes fight with each other
	  with variable success, for example a combination of Symbios
	  875/895 SCSI and Phoenix BIOS can give geometry 128/63 after
	  power up and 255/63 after a hard reset or soft
	  reboot.</para>
      </listitem>

      <listitem>
	<para><emphasis>XPT_PATH_INQ</emphasis> - path inquiry, in
	  other words get the SIM driver and SCSI controller (also
	  known as HBA - Host Bus Adapter) properties</para>

	<para>The properties are returned in the instance
	  <quote>struct ccb_pathinq cpi</quote> of the union
	  ccb:</para>

	<itemizedlist>
	  <listitem>
	    <para>version_num - the SIM driver version number, now all
	      drivers use 1</para>
	  </listitem>

	  <listitem>
	    <para>hba_inquiry - bitmask of features supported by the
	      controller:</para>
	  </listitem>

	  <listitem>
	    <para>PI_MDP_ABLE - supports MDP message (something from
	      SCSI3?)</para>
	  </listitem>

	  <listitem>
	    <para>PI_WIDE_32 - supports 32 bit wide
	      SCSI</para>
	  </listitem>

	  <listitem>
	    <para>PI_WIDE_16 - supports 16 bit wide
	      SCSI</para>
	  </listitem>

	  <listitem>
	    <para>PI_SDTR_ABLE - can negotiate synchronous transfer
	      rate</para>
	  </listitem>

	  <listitem>
	    <para>PI_LINKED_CDB - supports linked
	      commands</para>
	  </listitem>

	  <listitem>
	    <para>PI_TAG_ABLE - supports tagged
	      commands</para>
	  </listitem>

	  <listitem>
	    <para>PI_SOFT_RST - supports soft reset alternative (hard
	      reset and soft reset are mutually exclusive within a
	      SCSI bus)</para>
	  </listitem>

	  <listitem>
	    <para>target_sprt - flags for target mode support, 0 if
	      unsupported</para>
	  </listitem>

	  <listitem>
	    <para>hba_misc - miscellaneous controller
	      features:</para>
	  </listitem>

	  <listitem>
	    <para>PIM_SCANHILO - bus scans from high ID to low
	      ID</para>
	  </listitem>

	  <listitem>
	    <para>PIM_NOREMOVE - removable devices not included in
	      scan</para>
	  </listitem>

	  <listitem>
	    <para>PIM_NOINITIATOR - initiator role not
	      supported</para>
	  </listitem>

	  <listitem>
	    <para>PIM_NOBUSRESET - user has disabled initial BUS
	      RESET</para>
	  </listitem>

	  <listitem>
	    <para>hba_eng_cnt - mysterious HBA engine count, something
	      related to compression, now is always set to 0</para>
	  </listitem>

	  <listitem>
	    <para>vuhba_flags - vendor-unique flags, unused now</para>
	  </listitem>

	  <listitem>
	    <para>max_target - maximal supported target ID (7 for
	      8-bit bus, 15 for 16-bit bus, 127 for Fibre
	      Channel)</para>
	  </listitem>

	  <listitem>
	    <para>max_lun - maximal supported LUN ID (7 for older SCSI
	      controllers, 63 for newer ones)</para>
	  </listitem>

	  <listitem>
	    <para>async_flags - bitmask of installed Async handler,
	      unused now</para>
	  </listitem>

	  <listitem>
	    <para>hpath_id - highest Path ID in the subsystem, unused
	      now</para>
	  </listitem>

	  <listitem>
	    <para>unit_number - the controller unit number,
	      cam_sim_unit(sim)</para>
	  </listitem>

	  <listitem>
	    <para>bus_id - the bus number, cam_sim_bus(sim)</para>
	  </listitem>

	  <listitem>
	    <para>initiator_id - the SCSI ID of the controller
	      itself</para>
	  </listitem>

	  <listitem>
	    <para>base_transfer_speed - nominal transfer speed in KB/s
	      for asynchronous narrow transfers, equals to 3300 for
	      SCSI</para>
	  </listitem>

	  <listitem>
	    <para>sim_vid - SIM driver's vendor id, a zero-terminated
	      string of maximal length SIM_IDLEN including the
	      terminating zero</para>
	  </listitem>

	  <listitem>
	    <para>hba_vid - SCSI controller's vendor id, a
	      zero-terminated string of maximal length HBA_IDLEN
	      including the terminating zero</para>
	  </listitem>

	  <listitem>
	    <para>dev_name - device driver name, a zero-terminated
	      string of maximal length DEV_IDLEN including the
	      terminating zero, equal to cam_sim_name(sim)</para>
	  </listitem>
	</itemizedlist>

	<para>The recommended way of setting the string fields is
	  using strncpy, like:</para>

	<programlisting>    strncpy(cpi-&gt;dev_name, cam_sim_name(sim), DEV_IDLEN);</programlisting>

	<para>After setting the values set the status to CAM_REQ_CMP
	  and mark the CCB as done.</para>
      </listitem>
    </itemizedlist>
  </sect1>

  <sect1 xml:id="scsi-polling">
    <title>Polling</title>

    <funcsynopsis>
      <funcprototype>
	<funcdef>static void
	  <function>xxx_poll</function>
	</funcdef>
	<paramdef>
	  <parameter>struct cam_sim *sim</parameter>
	</paramdef>
      </funcprototype>
    </funcsynopsis>

    <para>The poll function is used to simulate the interrupts when
      the interrupt subsystem is not functioning (for example, when
      the system has crashed and is creating the system dump).  The
      CAM subsystem sets the proper interrupt level before calling the
      poll routine.  So all it needs to do is to call the interrupt
      routine (or the other way around, the poll routine may be doing
      the real action and the interrupt routine would just call the
      poll routine).  Why bother about a separate function then?
      Due to different calling conventions.  The
      <function>xxx_poll</function> routine gets the struct cam_sim
      pointer as its argument when the PCI interrupt routine by common
      convention gets pointer to the struct
      <varname remap="structname">xxx_softc</varname> and the ISA interrupt routine
      gets just the device unit number.  So the poll routine would
      normally look as:</para>

    <programlisting>static void
xxx_poll(struct cam_sim *sim)
{
    xxx_intr((struct xxx_softc *)cam_sim_softc(sim)); /* for PCI device */
}</programlisting>

    <para>or</para>

    <programlisting>static void
xxx_poll(struct cam_sim *sim)
{
    xxx_intr(cam_sim_unit(sim)); /* for ISA device */
}</programlisting>
  </sect1>

  <sect1 xml:id="scsi-async">
    <title>Asynchronous Events</title>

    <para>If an asynchronous event callback has been set up then the
      callback function should be defined.</para>

    <programlisting>static void
ahc_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)</programlisting>

    <itemizedlist>
      <listitem>
	<para>callback_arg - the value supplied when registering the
	  callback</para>
      </listitem>

      <listitem>
	<para>code - identifies the type of event</para>
      </listitem>

      <listitem>
	<para>path - identifies the devices to which the event
	  applies</para>
      </listitem>

      <listitem>
	<para>arg - event-specific argument</para>
      </listitem>
    </itemizedlist>

    <para>Implementation for a single type of event, AC_LOST_DEVICE,
      looks like:</para>

    <programlisting>    struct xxx_softc *softc;
    struct cam_sim *sim;
    int targ;
    struct ccb_trans_settings neg;

    sim = (struct cam_sim *)callback_arg;
    softc = (struct xxx_softc *)cam_sim_softc(sim);
    switch (code) {
    case AC_LOST_DEVICE:
        targ = xpt_path_target_id(path);
        if(targ &lt;= OUR_MAX_SUPPORTED_TARGET) {
            clean_negotiations(softc, targ);
            /* send indication to CAM */
            neg.bus_width = 8;
            neg.sync_period = neg.sync_offset = 0;
            neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
                | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);
            xpt_async(AC_TRANSFER_NEG, path, &amp;neg);
        }
        break;
    default:
        break;
    }</programlisting>
  </sect1>

  <sect1 xml:id="scsi-interrupts">
    <title>Interrupts</title>

    <indexterm><primary>SCSI</primary><secondary>interrupts</secondary></indexterm>

    <para>The exact type of the interrupt routine depends on the type
      of the peripheral bus (PCI, ISA and so on) to which the SCSI
      controller is connected.</para>

    <para>The interrupt routines of the SIM drivers run at the
      interrupt level splcam.  So <function>splcam()</function> should
      be used in the driver to synchronize activity between the
      interrupt routine and the rest of the driver (for a
      multiprocessor-aware driver things get yet more interesting but
      we ignore this case here).  The pseudo-code in this document
      happily ignores the problems of synchronization.  The real code
      must not ignore them.  A simple-minded approach is to set
      <function>splcam()</function> on the entry to the other routines
      and reset it on return thus protecting them by one big critical
      section.  To make sure that the interrupt level will be always
      restored a wrapper function can be defined, like:</para>

    <programlisting>    static void
    xxx_action(struct cam_sim *sim, union ccb *ccb)
    {
        int s;
        s = splcam();
        xxx_action1(sim, ccb);
        splx(s);
    }

    static void
    xxx_action1(struct cam_sim *sim, union ccb *ccb)
    {
        ... process the request ...
    }</programlisting>

    <para>This approach is simple and robust but the problem with it
      is that interrupts may get blocked for a relatively long time
      and this would negatively affect the system's performance.  On
      the other hand the functions of the <function>spl()</function>
      family have rather high overhead, so vast amount of tiny
      critical sections may not be good either.</para>

    <para>The conditions handled by the interrupt routine and the
      details depend very much on the hardware.  We consider the set
      of <quote>typical</quote> conditions.</para>

    <para>First, we check if a SCSI reset was encountered on the bus
      (probably caused by another SCSI controller on the same SCSI
      bus).  If so we drop all the enqueued and disconnected requests,
      report the events and re-initialize our SCSI controller.  It is
      important that during this initialization the controller will
      not issue another reset or else two controllers on the same SCSI
      bus could ping-pong resets forever.  The case of fatal
      controller error/hang could be handled in the same place, but it
      will probably need also sending RESET signal to the SCSI bus to
      reset the status of the connections with the SCSI
      devices.</para>

    <programlisting>    int fatal=0;
    struct ccb_trans_settings neg;
    struct cam_path *path;

    if( detected_scsi_reset(softc)
    || (fatal = detected_fatal_controller_error(softc)) ) {
        int targ, lun;
        struct xxx_hcb *h, *hh;

        /* drop all enqueued CCBs */
        for(h = softc-&gt;first_queued_hcb; h != NULL; h = hh) {
            hh = h-&gt;next;
            free_hcb_and_ccb_done(h, h-&gt;ccb, CAM_SCSI_BUS_RESET);
        }

        /* the clean values of negotiations to report */
        neg.bus_width = 8;
        neg.sync_period = neg.sync_offset = 0;
        neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
            | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);

        /* drop all disconnected CCBs and clean negotiations  */
        for(targ=0; targ &lt;= OUR_MAX_SUPPORTED_TARGET; targ++) {
            clean_negotiations(softc, targ);

            /* report the event if possible */
            if(xpt_create_path(&amp;path, /*periph*/NULL,
                    cam_sim_path(sim), targ,
                    CAM_LUN_WILDCARD) == CAM_REQ_CMP) {
                xpt_async(AC_TRANSFER_NEG, path, &amp;neg);
                xpt_free_path(path);
            }

            for(lun=0; lun &lt;= OUR_MAX_SUPPORTED_LUN; lun++)
                for(h = softc-&gt;first_discon_hcb[targ][lun]; h != NULL; h = hh) {
                    hh=h-&gt;next;
                    if(fatal)
                        free_hcb_and_ccb_done(h, h-&gt;ccb, CAM_UNREC_HBA_ERROR);
                    else
                        free_hcb_and_ccb_done(h, h-&gt;ccb, CAM_SCSI_BUS_RESET);
                }
        }

        /* report the event */
        xpt_async(AC_BUS_RESET, softc-&gt;wpath, NULL);

        /* re-initialization may take a lot of time, in such case
         * its completion should be signaled by another interrupt or
         * checked on timeout - but for simplicity we assume here that
         * it is really fast
         */
        if(!fatal) {
            reinitialize_controller_without_scsi_reset(softc);
        } else {
            reinitialize_controller_with_scsi_reset(softc);
        }
        schedule_next_hcb(softc);
        return;
    }</programlisting>

    <para>If interrupt is not caused by a controller-wide condition
      then probably something has happened to the current hardware
      control block.  Depending on the hardware there may be other
      non-HCB-related events, we just do not consider them here.  Then
      we analyze what happened to this HCB:</para>

    <programlisting>    struct xxx_hcb *hcb, *h, *hh;
    int hcb_status, scsi_status;
    int ccb_status;
    int targ;
    int lun_to_freeze;

    hcb = get_current_hcb(softc);
    if(hcb == NULL) {
        /* either stray interrupt or something went very wrong
         * or this is something hardware-dependent
         */
        handle as necessary;
        return;
    }

    targ = hcb-&gt;target;
    hcb_status = get_status_of_current_hcb(softc);</programlisting>

    <para>First we check if the HCB has completed and if so we check
      the returned SCSI status.</para>

    <programlisting>    if(hcb_status == COMPLETED) {
        scsi_status = get_completion_status(hcb);</programlisting>

    <para>Then look if this status is related to the REQUEST SENSE
      command and if so handle it in a simple way.</para>

    <programlisting>        if(hcb-&gt;flags &amp; DOING_AUTOSENSE) {
            if(scsi_status == GOOD) { /* autosense was successful */
                hcb-&gt;ccb-&gt;ccb_h.status |= CAM_AUTOSNS_VALID;
                free_hcb_and_ccb_done(hcb, hcb-&gt;ccb, CAM_SCSI_STATUS_ERROR);
            } else {
        autosense_failed:
                free_hcb_and_ccb_done(hcb, hcb-&gt;ccb, CAM_AUTOSENSE_FAIL);
            }
            schedule_next_hcb(softc);
            return;
        }</programlisting>

    <para>Else the command itself has completed, pay more attention to
      details.  If auto-sense is not disabled for this CCB and the
      command has failed with sense data then run REQUEST SENSE
      command to receive that data.</para>

    <programlisting>        hcb-&gt;ccb-&gt;csio.scsi_status = scsi_status;
        calculate_residue(hcb);

        if( (hcb-&gt;ccb-&gt;ccb_h.flags &amp; CAM_DIS_AUTOSENSE)==0
        &amp;&amp; ( scsi_status == CHECK_CONDITION
                || scsi_status == COMMAND_TERMINATED) ) {
            /* start auto-SENSE */
            hcb-&gt;flags |= DOING_AUTOSENSE;
            setup_autosense_command_in_hcb(hcb);
            restart_current_hcb(softc);
            return;
        }
        if(scsi_status == GOOD)
            free_hcb_and_ccb_done(hcb, hcb-&gt;ccb, CAM_REQ_CMP);
        else
            free_hcb_and_ccb_done(hcb, hcb-&gt;ccb, CAM_SCSI_STATUS_ERROR);
        schedule_next_hcb(softc);
        return;
    }</programlisting>

    <para>One typical thing would be negotiation events: negotiation
      messages received from a SCSI target (in answer to our
      negotiation attempt or by target's initiative) or the target is
      unable to negotiate (rejects our negotiation messages or does
      not answer them).</para>

    <programlisting>    switch(hcb_status) {
    case TARGET_REJECTED_WIDE_NEG:
        /* revert to 8-bit bus */
        softc-&gt;current_bus_width[targ] = softc-&gt;goal_bus_width[targ] = 8;
        /* report the event */
        neg.bus_width = 8;
        neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
        xpt_async(AC_TRANSFER_NEG, hcb-&gt;ccb.ccb_h.path_id, &amp;neg);
        continue_current_hcb(softc);
        return;
    case TARGET_ANSWERED_WIDE_NEG:
        {
            int wd;

            wd = get_target_bus_width_request(softc);
            if(wd &lt;= softc-&gt;goal_bus_width[targ]) {
                /* answer is acceptable */
                softc-&gt;current_bus_width[targ] =
                softc-&gt;goal_bus_width[targ] = neg.bus_width = wd;

                /* report the event */
                neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
                xpt_async(AC_TRANSFER_NEG, hcb-&gt;ccb.ccb_h.path_id, &amp;neg);
            } else {
                prepare_reject_message(hcb);
            }
        }
        continue_current_hcb(softc);
        return;
    case TARGET_REQUESTED_WIDE_NEG:
        {
            int wd;

            wd = get_target_bus_width_request(softc);
            wd = min (wd, OUR_BUS_WIDTH);
            wd = min (wd, softc-&gt;user_bus_width[targ]);

            if(wd != softc-&gt;current_bus_width[targ]) {
                /* the bus width has changed */
                softc-&gt;current_bus_width[targ] =
                softc-&gt;goal_bus_width[targ] = neg.bus_width = wd;

                /* report the event */
                neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
                xpt_async(AC_TRANSFER_NEG, hcb-&gt;ccb.ccb_h.path_id, &amp;neg);
            }
            prepare_width_nego_rsponse(hcb, wd);
        }
        continue_current_hcb(softc);
        return;
    }</programlisting>

    <para>Then we handle any errors that could have happened during
      auto-sense in the same simple-minded way as before.  Otherwise
      we look closer at the details again.</para>

    <programlisting>    if(hcb-&gt;flags &amp; DOING_AUTOSENSE)
        goto autosense_failed;

    switch(hcb_status) {</programlisting>

    <para>The next event we consider is unexpected disconnect.  Which
      is considered normal after an ABORT or BUS DEVICE RESET message
      and abnormal in other cases.</para>

    <programlisting>    case UNEXPECTED_DISCONNECT:
        if(requested_abort(hcb)) {
            /* abort affects all commands on that target+LUN, so
             * mark all disconnected HCBs on that target+LUN as aborted too
             */
            for(h = softc-&gt;first_discon_hcb[hcb-&gt;target][hcb-&gt;lun];
                    h != NULL; h = hh) {
                hh=h-&gt;next;
                free_hcb_and_ccb_done(h, h-&gt;ccb, CAM_REQ_ABORTED);
            }
            ccb_status = CAM_REQ_ABORTED;
        } else if(requested_bus_device_reset(hcb)) {
            int lun;

            /* reset affects all commands on that target, so
             * mark all disconnected HCBs on that target+LUN as reset
             */

            for(lun=0; lun &lt;= OUR_MAX_SUPPORTED_LUN; lun++)
                for(h = softc-&gt;first_discon_hcb[hcb-&gt;target][lun];
                        h != NULL; h = hh) {
                    hh=h-&gt;next;
                    free_hcb_and_ccb_done(h, h-&gt;ccb, CAM_SCSI_BUS_RESET);
                }

            /* send event */
            xpt_async(AC_SENT_BDR, hcb-&gt;ccb-&gt;ccb_h.path_id, NULL);

            /* this was the CAM_RESET_DEV request itself, it is completed */
            ccb_status = CAM_REQ_CMP;
        } else {
            calculate_residue(hcb);
            ccb_status = CAM_UNEXP_BUSFREE;
            /* request the further code to freeze the queue */
            hcb-&gt;ccb-&gt;ccb_h.status |= CAM_DEV_QFRZN;
            lun_to_freeze = hcb-&gt;lun;
        }
        break;</programlisting>

    <para>If the target refuses to accept tags we notify CAM about
      that and return back all commands for this LUN:</para>

    <programlisting>    case TAGS_REJECTED:
        /* report the event */
        neg.flags = 0 &amp; ~CCB_TRANS_TAG_ENB;
        neg.valid = CCB_TRANS_TQ_VALID;
        xpt_async(AC_TRANSFER_NEG, hcb-&gt;ccb.ccb_h.path_id, &amp;neg);

        ccb_status = CAM_MSG_REJECT_REC;
        /* request the further code to freeze the queue */
        hcb-&gt;ccb-&gt;ccb_h.status |= CAM_DEV_QFRZN;
        lun_to_freeze = hcb-&gt;lun;
        break;</programlisting>

    <para>Then we check a number of other conditions, with processing
      basically limited to setting the CCB status:</para>

    <programlisting>    case SELECTION_TIMEOUT:
        ccb_status = CAM_SEL_TIMEOUT;
        /* request the further code to freeze the queue */
        hcb-&gt;ccb-&gt;ccb_h.status |= CAM_DEV_QFRZN;
        lun_to_freeze = CAM_LUN_WILDCARD;
        break;
    case PARITY_ERROR:
        ccb_status = CAM_UNCOR_PARITY;
        break;
    case DATA_OVERRUN:
    case ODD_WIDE_TRANSFER:
        ccb_status = CAM_DATA_RUN_ERR;
        break;
    default:
        /* all other errors are handled in a generic way */
        ccb_status = CAM_REQ_CMP_ERR;
        /* request the further code to freeze the queue */
        hcb-&gt;ccb-&gt;ccb_h.status |= CAM_DEV_QFRZN;
        lun_to_freeze = CAM_LUN_WILDCARD;
        break;
    }</programlisting>

    <para>Then we check if the error was serious enough to freeze the
      input queue until it gets proceeded and do so if it is:</para>

    <programlisting>    if(hcb-&gt;ccb-&gt;ccb_h.status &amp; CAM_DEV_QFRZN) {
        /* freeze the queue */
        xpt_freeze_devq(ccb-&gt;ccb_h.path, /*count*/1);

        /* re-queue all commands for this target/LUN back to CAM */

        for(h = softc-&gt;first_queued_hcb; h != NULL; h = hh) {
            hh = h-&gt;next;

            if(targ == h-&gt;targ
            &amp;&amp; (lun_to_freeze == CAM_LUN_WILDCARD || lun_to_freeze == h-&gt;lun) )
                free_hcb_and_ccb_done(h, h-&gt;ccb, CAM_REQUEUE_REQ);
        }
    }
    free_hcb_and_ccb_done(hcb, hcb-&gt;ccb, ccb_status);
    schedule_next_hcb(softc);
    return;</programlisting>

    <para>This concludes the generic interrupt handling although
      specific controllers may require some additions.</para>
  </sect1>

  <sect1 xml:id="scsi-errors">
    <title>Errors Summary</title>

    <indexterm><primary>SCSI</primary><secondary>errors</secondary></indexterm>

    <para>When executing an I/O request many things may go wrong.  The
      reason of error can be reported in the CCB status with great
      detail.  Examples of use are spread throughout this document.
      For completeness here is the summary of recommended responses
      for the typical error conditions:</para>

    <itemizedlist>
      <listitem>
	<para><emphasis>CAM_RESRC_UNAVAIL</emphasis> - some resource
	  is temporarily unavailable and the SIM driver cannot
	  generate an event when it will become available.  An example
	  of this resource would be some intra-controller hardware
	  resource for which the controller does not generate an
	  interrupt when it becomes available.</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_UNCOR_PARITY</emphasis> - unrecovered
	  parity error occurred</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_DATA_RUN_ERR</emphasis> - data overrun or
	  unexpected data phase (going in other direction than
	  specified in CAM_DIR_MASK) or odd transfer length for wide
	  transfer</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_SEL_TIMEOUT</emphasis> - selection timeout
	  occurred (target does not respond)</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_CMD_TIMEOUT</emphasis> - command timeout
	  occurred (the timeout function ran)</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_SCSI_STATUS_ERROR</emphasis> - the device
	  returned error</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_AUTOSENSE_FAIL</emphasis> - the device
	  returned error and the REQUEST SENSE COMMAND failed</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_MSG_REJECT_REC</emphasis> - MESSAGE REJECT
	  message was received</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_SCSI_BUS_RESET</emphasis> - received SCSI
	  bus reset</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_REQ_CMP_ERR</emphasis> -
	  <quote>impossible</quote> SCSI phase occurred or something
	  else as weird or just a generic error if further detail is
	  not available</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_UNEXP_BUSFREE</emphasis> - unexpected
	  disconnect occurred</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_BDR_SENT</emphasis> - BUS DEVICE RESET
	  message was sent to the target</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_UNREC_HBA_ERROR</emphasis> - unrecoverable
	  Host Bus Adapter Error</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_REQ_TOO_BIG</emphasis> - the request was
	  too large for this controller</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_REQUEUE_REQ</emphasis> - this request
	  should be re-queued to preserve transaction ordering.  This
	  typically occurs when the SIM recognizes an error that
	  should freeze the queue and must place other queued requests
	  for the target at the sim level back into the XPT queue.
	  Typical cases of such errors are selection timeouts, command
	  timeouts and other like conditions.  In such cases the
	  troublesome command returns the status indicating the error,
	  the and the other commands which have not be sent to the bus
	  yet get re-queued.</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_LUN_INVALID</emphasis> - the LUN ID in the
	  request is not supported by the SCSI controller</para>
      </listitem>

      <listitem>
	<para><emphasis>CAM_TID_INVALID</emphasis> - the target ID in
	  the request is not supported by the SCSI controller</para>
      </listitem>
    </itemizedlist>
  </sect1>

  <sect1 xml:id="scsi-timeout">
    <title>Timeout Handling</title>

    <para>When the timeout for an HCB expires that request should be
      aborted, just like with an XPT_ABORT request.  The only
      difference is that the returned status of aborted request should
      be CAM_CMD_TIMEOUT instead of CAM_REQ_ABORTED (that is why
      implementation of the abort better be done as a function).  But
      there is one more possible problem: what if the abort request
      itself will get stuck? In this case the SCSI bus should be
      reset, just like with an XPT_RESET_BUS request (and the idea
      about implementing it as a function called from both places
      applies here too).  Also we should reset the whole SCSI bus if a
      device reset request got stuck.  So after all the timeout
      function would look like:</para>

    <programlisting>static void
xxx_timeout(void *arg)
{
    struct xxx_hcb *hcb = (struct xxx_hcb *)arg;
    struct xxx_softc *softc;
    struct ccb_hdr *ccb_h;

    softc = hcb-&gt;softc;
    ccb_h = &amp;hcb-&gt;ccb-&gt;ccb_h;

    if(hcb-&gt;flags &amp; HCB_BEING_ABORTED
    || ccb_h-&gt;func_code == XPT_RESET_DEV) {
        xxx_reset_bus(softc);
    } else {
        xxx_abort_ccb(hcb-&gt;ccb, CAM_CMD_TIMEOUT);
    }
}</programlisting>

    <para>When we abort a request all the other disconnected requests
      to the same target/LUN get aborted too.  So there appears a
      question, should we return them with status CAM_REQ_ABORTED or
      CAM_CMD_TIMEOUT?  The current drivers use CAM_CMD_TIMEOUT. This
      seems logical because if one request got timed out then probably
      something really bad is happening to the device, so if they
      would not be disturbed they would time out by themselves.</para>
  </sect1>
</chapter>