Chapter 16. SCSI Device Drivers
Prev | Part V. SCSI Device Drivers | Next

## Chapter 16. SCSI Device Drivers

All SGI systems support the small computer systems interface (SCSI) bus for the attachment of disks, tapes, and other devices. This chapter details the kernel-level support for SCSI device drivers.

If your aim is to control a SCSI device from a user-level process, this chapter contains some useful background information to supplement Chapter 5, “User-Level Access to SCSI Devices”. If you are designing a kernel-level SCSI driver, this chapter contains essential information on kernel support. The major topics in this chapter are as follows:

- “SCSI Support in SGI Systems” gives an overview of the hardware and software support for SCSI.

- “Host Adapter Facilities” documents the use of the host adapter driver to access a SCSI device.

- “Designing a SCSI Driver” provides information on how to design a third party SCSI device drivers.

- “SCSI Reference Data” tabulates SCSI codes and messages for reference.

- “A Note on FibreChannel Drivers” correlates writing SCSI device Drivers with writing FibreChannel device drivers.

In addition, you may want to review the following additional sources:

`intro(7)` reference page | Documents the naming conventions for disk and tape device special files.
`dksc(7)` reference page | Documents the SGI disk volume partition layout and the `ioctl` support in the base-level SCSI drivers.
ANSI X3.131-1986 and X3T9.2/85-52 Rev 4B. | SCSI standards documents.
http://scitexdv.com:8080/SCSI2/ | Web page containing the complete SCSI-2 standard in HTML form.
master.d/scsi | Driver registration tables used for registering third party drivers.

## SCSI Support in SGI Systems

All current SGI systems rely on the SCSI bus as the primary attachment for disks and tapes. The IRIX kernel also provides support for OEM drivers for SCSI devices.

As used here, the term “adapter” means a SCSI controller such as the Western Digital W93 chip, which attaches a unique chain of SCSI devices. In this sense, a SCSI adapter and a SCSI bus are the same. “Adapter number” is used instead of “bus number.”

### SCSI Hardware Support

The SGI computer systems supported by IRIX 6.5 can attach multiple SCSI adapters, as follows:

- The Origin 2000 and Onyx2 systems have two SCSI controllers on each Base I/O module. Several additional SCSI controllers can be added to each module.

- The Origin 200 system has two SCSI controllers per chassis and the possibility of optional SCSI controllers using PCI and MSCSI interfaces.

- The Octane workstation has two SCSI controllers, one for the internal disks and one for the external chain. In addition, PCI and MSCSI controllers can be added.

- The Indy workstation has at least one SCSI adapter on its motherboard, and can have up to two additional adapters on a GIO option board.

- The Indigo2 series supports two SCSI adapters on the motherboard.

- The Challenge S system has two SCSI adapters on the motherboard, and can have one or two additional on each of one or two additional GIO option boards, for a maximum of six adapters.

- The Challenge M system supports one SCSI adapter on the CPU board and can have up to two additional adapters on a GIO option board.

- The Power Channel-2 (IO4) boards used in the Challenge and Onyx series support two SCSI adapters, plus many as six additional SCSI adapters on mezzanine cards, for a maximum of eight adapters per IO4.

In all systems, DMA mapping hardware allows a SCSI adapter to treat discontiguous memory locations as if they were a contiguous buffer, providing scatter and gather support.

### IRIX Kernel SCSI Support

The IRIX kernel contains two levels of SCSI support. An inner SCSI driver, the host adapter driver, manages all communication with a SCSI hardware adapter. The kernel-level SCSI device driver for a particular device prepares SCSI commands and calls on the host adapter driver to execute them. This design centralizes the management of SCSI adapters. Centralization is necessary because the use of the SCSI bus is shared by many devices, while recovery and error-handling are handled at the adapter level. In addition, use of the host adapter driver makes it simpler to write a SCSI device driver.

#### Host Adapter Drivers

Different host adapter drivers are loaded, depending on the hardware in the system. Some examples of host adapter drivers are wd93 and ql.

The host adapter drivers support all levels of the SCSI standard: SCSI-1, the Common Command Set (CCS, superceded by SCSI-2), and SCSI-2. Not all optional features of the standard are supported. Different systems support different feature combinations (such as synchronous, fast, and wide SCSI), depending on the available hardware.

The host adapter drivers handle the low-level communication over the SCSI interface, such as programming the SCSI interface chip or board, negotiating synchronous or wide mode, and handling disconnect and reconnect.

A host adapter driver is not, strictly speaking, a proper device driver because it does not support all the entry points documented in Chapter 7, “Structure of a Kernel-Level Driver”. You can think of it as a specialized library module for SCSI-bus management or as a device driver, whichever you prefer. The software interface to the host adapter driver is documented under “Host Adapter Facilities”.

### SCSI Devices in the hwgraph

When planning a SCSI device driver, it is informative to spend some time exploring the rather complex network of hwgraph vertexes that is set up by the existing SCSI drivers. Your tools for this are the `find` and `grep` commands. For example:

    find /hw -print | grep scsi | grep -v disk

The result is voluminous even on a relatively small system and reveals that there are many vertexes created for each logical unit (LUN) on each controller. Here is a sample with repetitions edited out:

    houston 30% find /hw -print | grep scsi | grep -v disk
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/bus
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/1
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/1/lun
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/1/lun/0
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/1/lun/0/scsi
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/1/lun/1
    ...
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/2
    ...
    /hw/module/1/slot/io1/baseio/pci/1/scsi_ctlr
    ...
    /hw/scsi_ctlr
    /hw/scsi_ctlr/0
    /hw/scsi_ctlr/1
    /hw/scsi
    /hw/scsi/sc0d1l0
    /hw/scsi/sc0d2l0

#### Controller Vertexes

Paths of the form `/hw/.../scsi_ctlr/...` are hwgraph vertexes that represent SCSI host adapters (controllers) discovered during boot time. Each of these is presented to a host adapter at its pfx `attach()` entry point (see “Entry Point attach()” in Chapter 7). All the other hwgraph paths that contain the word `scsi` were created by the host adapter driver or by SCSI device drivers (see “Extending the hwgraph” in Chapter 8).

#### Target Vertexes and LUN Vertexes

Paths that contain `scsi_ctlr/``N``/target/``N` were created by the host adapter driver to represent each target that it discovered while probing its bus at attach time. Attached to these are paths containing `lun/``N`, for example:

    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/1/lun/0/scsi

Whenever a target is found to have logical units, a vertex is created for each, and when the LUN responds, a character device vertex is created for it.

    ls -l /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/1/lun/0
    total 0
    drwxr-xr-x    2 root     sys            0 Mar 12 14:30 disk
    crw-------    1 root     sys        0,116 Mar 12 14:30 scsi

The character device `scsi` represents the addressable LUN. The vertex disk was installed by the disk device driver. It is the attachment vertex for a number of device vertexes that represent the parts of a disk volume, such as `disk/partition/1/block` and `disk/partition/1/char`, character and block access to a disk (see “Block and Character Device Access” in Chapter 2).

#### Convenience Vertexes

In addition to the lengthy pathnames, there are shortcut names:

    % ls -l /hw/scsi
    total 0
    crw-------    1 root     sys        0,116 Mar 12 14:41 sc0d1l0
    crw-------    1 root     sys        0,133 Mar 12 14:41 sc0d2l0
    crw-------    1 root     sys        0,150 Mar 12 14:41 sc0d3l0
    crw-------    1 root     sys        0,167 Mar 12 14:41 sc0d4l0
    crw-------    1 root     sys        0,184 Mar 12 14:41 sc0d5l0

These were created by the SCSI driver as shortcut links to the `lun/``N``/scsi` vertexes, as you can verify with the `-S` option of `ls`:

    % ls -S /hw/scsi/sc0d1l0
    /hw/scsi/sc0d1l0 ->
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0/target/1/lun/0/scsi

In the system used to create this example, a second SCSI controller exists but has no LUNs. If it had LUNs, there would be shortcut names `sc1d1l0` and so forth in `/hw/scsi`, as well.

At boot time, the host adapter driver creates a vertex and adds an edge labelled “scsi” from the root vertex to the new vertex. The `ioconfig` command (see “Using ioconfig for Global Controller Numbers” in Chapter 2) then adds edges from it labelled “sc0d1l0,” “sc0d2l0,” and so forth, each ending at one of the`lun/N/scsi` vertexes.

Although it is created dynamically, the shortcut name `/hw/scsi` is the target of a symbolic link in `/dev`. Thus all convenience links such as `/hw/scsi/sc0d1l0` can also be addressed as `/dev/scsi/sc0d1l0` and so on.

    ls -l /dev/scsi
    lrwxr-xr-x    1 root     sys            8 Jan 10 15:33 /dev/scsi->/hw/scsi
    % ls -l /dev/scsi/sc0d1l0
    crw-------    1 root     sys        0,116 Mar 12 15:21 /dev/scsi/sc0d1l0

Additional convenience vertexes are created to point to the controllers themselves. These can be used by `scsiha` to pass requests to the scsi host adapter drivers to perform activities that aren't related to the SCSI commands (see `scsiha(7M)` and `scsiha(1M)` .

    % ls -l /hw/scsi_ctlr
    total 0
    lrw-------    1 root     sys           47 Mar 12 14:42 0 ->
    /hw/module/1/slot/io1/baseio/pci/0/scsi_ctlr/0
    lrw-------    1 root     sys           47 Mar 12 14:42 1 ->
    /hw/module/1/slot/io1/baseio/pci/1/scsi_ctlr/0

These were also created by `ioconfig` for each of the controller vertexes (controller 0 in PCI slot 0, later controller 0 in PCI slot 1). When they were created, the driver:

1. Created a vertex.

2. Added an edge from the root vertex with the label “scsi_ctlr,” ending at the new vertex.

3. Added an edge labelled “0” from that vertex ending at the controller vertex.

The next time the driver found the `/hw/scsi_ctlr` edge already existed, and only added the new edge “1” pointing to its controller vertex.

#### Disk Driver Vertexes

Bring more command-line utilities to bear on the task of displaying the vertexes built by the disk device driver:

    % find /hw -print | grep scsi | grep disk | \
    sed `s/hw.*_ctlr/.../' | more
    /.../0/target/1/lun/0/disk
    /.../0/target/1/lun/0/disk/volume
    /.../0/target/1/lun/0/disk/volume/char
    /.../0/target/1/lun/0/disk/volume_header
    /.../0/target/1/lun/0/disk/volume_header/block
    /.../0/target/1/lun/0/disk/volume_header/char
    /.../0/target/1/lun/0/disk/partition
    /.../0/target/1/lun/0/disk/partition/0
    /.../0/target/1/lun/0/disk/partition/0/block
    /.../0/target/1/lun/0/disk/partition/0/char
    /.../0/target/1/lun/0/disk/partition/1
    /.../0/target/1/lun/0/disk/partition/1/block
    /.../0/target/1/lun/0/disk/partition/1/char
    /.../0/target/2/lun/0/disk
    ...

These names are created dynamically, but at a slightly different time. The names reflect the actual layout of the disk volume. For example, a disk could be reformatted to have more or fewer partitions. The disk device driver removes and rebuilds all the names that depend on the volume format (such as ...`/lun/0/disk/partition/0/block`) each time the disk volume header is read into memory. That normally occurs only the first time the disk is opened—which is usually done by `ioconfig`.

Note that similar to the creation and removal of hwgraph entries by the host adapter driver, a third-party SCSI device driver must also create and remove hwgraph entries as described in “Designing a SCSI Driver”.

### Hardware Administration

Some bus protocol features such as connect and disconnect are controlled by configuration files that are used by the host adapter drivers. For example, the `wd93` driver has a number of configurable options coded in the descriptive file `/var/sysgen/master.d` (for the format of descriptive files, see “Describing the Driver in /var/sysgen/master.d” in Chapter 9).

The QLogic driver `ql` takes its options by the more modern route of the DEVICE_ADMIN statement (see “Storing Device and Driver Attributes” in Chapter 2 and “Retrieving Administrator Attributes” in Chapter 8). You can peruse `/var/sysgen/system/irix.sm` to see DEVICE_ADMIN statements addressed to “ql_” and associated comments.

| Note: The connect/disconnect strategy is enabled on any SCSI bus by default (the option is controlled by a constant defined in the host adapter driver descriptive file in `/var/sysgen/master.d`). When disconnect is enabled on a bus, and a device on that bus refuses to disconnect, it can cause timeouts on other devices.

## Host Adapter Facilities

The principal difference between a SCSI driver and other kernel-level drivers is that, while other kinds of drivers are expected to control devices directly using PIO and DMA, a SCSI driver operates its devices indirectly, by making function calls to the host adapter driver. This section documents the functional interface to the host adapter driver.

### Purpose of the Host Adapter Driver

IRIX uses host adapter drivers because the SCSI bus is shared among multiple devices of different types, each type controlled by a different driver. A disk, a tape, a CDROM, and a scanner could all be cabled from the same SCSI adapter. Each device has a different driver, but each driver needs to use the adapter, a single chip-set, to communicate with its device.

If IRIX allowed multiple drivers to operate the host adapter, there would be confusion and errors from the conflicting uses. IRIX puts the management of each host adapter under the control of a host adapter driver, whose job is to issue commands on its bus and report the results. The host adapter driver is tailored to the hardware of the particular host adapter and to the architecture of the host system.

The interface to the host adapter driver is the same no matter what type of hardware the adapter uses. This insulates the individual device drivers from details of the adapter hardware.

The driver for each type of device is responsible for preparing the SCSI command bytes for its device, for passing the command requests to the correct host adapter driver, and for interpreting sense and status data as it comes back.

### Host Adapter Concepts

The host adapter driver is the driver that is called by the kernel to attach the SCSI controller at boot time while the kernel is exploring the hardware and building the hwgraph (see “Entry Point attach()” in Chapter 7).

The host adapter driver places information in the hwgraph vertex that represents the controller, and extends the hwgraph with subordinate vertexes that represent targets and LUNs (see “SCSI Devices in the hwgraph”).

A SCSI driver is called to manage one or more SCSI target devices. Each target is physically connected to a SCSI adapter. The hwgraph echoes this connectivity: the SCSI target vertex is connected to the SCSI adapter vertex. Thus when the SCSI driver knows its target device vertex, it can access the corresponding host adapter vertex, and through this vertex, can invoke the host adapter driver.

#### Target Numbers

The purpose of a host adapter driver is to carry communications between a SCSI driver and a target. A target is a device on the SCSI chain that responds to SCSI commands. A target can be a single device, or it can be a controller that in turn manages other devices.

A target is identified by a number between 0 and 15. Normally this number is configured into the device with switches or jumpers. The SCSI controller itself has a target number (usually number 0), but it cannot be used as a target.

The SCSI device driver needs to know the number of its target in order to format a request structure. The target number is accessible from the target vertex, as shown under “Using the Function Vector Macros”.

#### Logical Unit Numbers (LUNs)

When the target is a controller, it manages one or more subdevices, each one a logical unit of that target. The logical unit being addressed is identified by a logical unit number (LUN). When the target is a single device, its LUN is 0.

A SCSI device driver needs the unit number of a device when it formats a request structure. The unit number is accessible from the vertex for the LUN, as shown under “Using the Function Vector Macros”.

### Overview of Host Adapter Functions

Each host adapter driver provides the same functional interface and supports the four functions listed in Table 16-1.

Table 16-1. Host Adapter Function Summary

Function | Header Files | Purpose
--- | --- | ---
`scsi_info(D3)` | scsi.h | Issue the SCSI Inquiry command and return the results.
`scsi_alloc(D3)` | scsi.h | Open a connection between a driver and a target device.
`scsi_free(D3)` | scsi.h | Release connection to a target device.
`scsi_command(D3)` | scsi.h | Transmit a SCSI command on the bus and return results.
scsi_abort() | scsi.h | Transmit a SCSI ABORT command (no reference page).
scsi_ioctl() | scsi.h | Implement arbitrary control operations.
scsi_dump() | scsi.h | Called by the kernel to notify the host adapter driver that the kernel is shutting down for a panic dump, and that subsequent operations will be for writing the dump and other diagnostic files, and should be performed synchronously.

| Note: The `scsi_reset()` function that formerly existed has been removed.

### How the Host Adapter Functions Are Found

A SCSI device driver can be asked to manage devices on different adapters. Different adapters may use the same or different hardware, and be managed by the same or different host adapter drivers. How does the driver locate the correct host adapter function for a given device?

The answer is that each host adapter driver places a set of vectors to its functions in the hwgraph vertex for the controller. Using macros defined in `sys/scsi.h`, the driver invokes the function it needs indirectly, by way of pointers stored in the controller vertex.

#### Vertex Information Structures

The host adapter driver constructs the arrangement of hwgraph vertexes and data structures illustrated in Figure 16-1.

Figure 16-1. SCSI Vertexes and Data Structures

The main features of this arrangement are as follows:

- The vertex for the controller anchors a scsi_ctlr_info_t, which contains the vectors to the host adapter functions.

- The vertex for any target anchors a scsi_targ_info_t, which contains the target number and a pointer to the scsi_ctlr_info_t for that target's controller.

- The vertex for any LUN anchors a scsi_lun_info_t, which contains the unit number and a pointer to the scsi_targ_info_t for that LUN's target.

#### Using the Function Vector Macros

A device driver, given a handle to a vertex for a LUN, a target, or a controller, can always access the vectors to the host adapter functions. These connections are used by macros defined in `sys/scsi.h`, as listed in Table 16-2.

Table 16-2. Macro Access to SCSI Information

Desired Datum | scsi_ctlr_info_t *pci | scsi_targ_info_t *pti | scsi_lun_info_t *pli
--- | --- | --- | ---
adapter number | SCI_ADAP(pci) | SCI_ADAP( STI_CTLR_INFO(pti)) | SLI_ADAP(pli)
adapter vertex | SCI_CTLR_VHDL(pci) | SCI_CTLR_VHDL( STI_CTLR_INFO(pti)) | SLI_CTLR_VHDL(pli)
target number |  | STI_TARG(pti) | SLI_TARG(pli)
target vertex |  | STI_TARG_VHDL(pti) | STI_TARG_VHDL( SLI_TARG_INFO(pli))
unit number |  |  | SLI_LUN(pli)
LUN vertex |  |  | SLI_LUN_VHDL(pli)
scsi_abort() | SCI_ABORT(pci) | SCI_ABORT( STI_CTLR_INFO(pti)) | SCI_ABORT( SLI_CTLR_INFO(pli))
scsi_alloc() | SCI_ALLOC(pci)() | SCI_ALLOC( STI_CTLR_INFO(pti)) | SCI_ALLOC( SLI_CTLR_INFO(pli))
scsi_command() | SCI_COMMAND(pci)() | SCI_COMMAND( STI_CTLR_INFO(pti)) | SCI_COMMAND( SLI_CTLR_INFO(pli))
scsi_free() | SCI_FREE(pci)() | SCI_FREE( STI_CTLR_INFO(pti)) | SCI_FREE( SLI_CTLR_INFO(pli))
scsi_icoctl | SCI_IOCTL(pci) | SCI_IOCTL( STI_CTLR_INFO(pti)) | SCI_IOCTL( SLI_CTLR_INFO(pli))
scsi_info() | SCI_INQ(pci)() | SCI_INQ( STI_CTLR_INFO(pti)) | SCI_INQ( SLI_CTRL_INFO(pli)

Study the macro definitions in `sys/scsi.h`—for example, the definition of SLI_ALLOC, with reference to the arrangement shown in Figure 16-1—to see the pattern. Additional macros can be defined using the same pattern.

### Using scsi_info()

Before a SCSI driver tries to access a device, it must call the host adapter `scsi_info()` function, passing the vertex handle for the LUN. This function issues an Inquiry command to the adapter, target, and logical unit. If the Inquiry is not successful—or if the adapter, target, or LUN are not valid—the return value is NULL. Otherwise, the return value is a pointer to a scsi_target_info structure.

The SCSI driver can learn the following things from a call to `scsi_info()`:

- If the return is NULL, there is a serious problem with the device or the information about it. Write a descriptive log message with `cmn_err()` and return ENODEV.

- The si_inq field points to the Inquiry bytes returned by the device. Examine them for device-dependent information.

- The value in si_maxq is the default limit on pending SCSI commands that can be queued to this host adapter driver. (You can specify a higher limit to `scsi_alloc()`.)

- Test the bits in si_ha_status for information about the capabilities and error status of the host adapter itself. The possible bits are declared in `sys/scsi.h`. For example, SRH_NOADAPSYNC indicates that the specified target, or possibly the host adapter itself, does not support synchronous transfer. Not all bits are supported by all host adapter drivers.

You can also call `scsi_info()` at other times; some of the returned information can be useful in error recovery. However, be aware that `scsi_info()` for some host adapters is slow, and can use serialized access to hardware. (See also reference page `scsi_info(d3x)` .)

### Using scsi_alloc()

Depending on its particular design, the host adapter driver may need to allocate memory for data structures, DMA maps, or buffers for sense and inquiry data, before it is ready to execute commands to a particular target. The call to `scsi_alloc()` gives the host adapter driver the opportunity to prepare in these ways. (See also reference page `scsi_alloc(d3x)` .)

Because the host adapter driver may allocate virtual memory, it may sleep. Some host adapter drivers allocate all the resources they need on the first call to `scsi_alloc()` and do little or nothing on subsequent calls.

A call to `scsi_alloc()` specifies these parameters:

lun_vhdl | Vertex handle of the LUN, from which the target and controller can be identified.
option | An integer comprising two parameters, a flag, and a count.
callback | Address of a function to be called whenever sense data is gotten from the device.

The option parameter may include the SCSIALLOC_EXCLUSIVE flag to request exclusive use of the target. If another driver has allocated a path to the same device, `scsi_alloc()` returns EBUSY. For example, a tape device driver might require exclusive access, while a disk device driver would not.

The option parameter may include SCSIALLOC_NOSYNC to specify that this device should not, or cannot, use synchronous transfer mode. That setting can be overridden for single commands by a flag to `scsi_command()` (see Table 16-4).

The option parameter can also include a small integer value indicating the maximum queue depth (the number of SCSI commands the driver would like to start before any have completed). The call to `scsi_info()` returns the default queue depth that will be used if you do not pass a nonzero value here (typically the default is 1).

The callback function address can be specified as NULL. The specified callback function is called only when sense data is gotten from the allocated device (regardless of which driver initiated the command that resulted in sense data). Only one driver that allocates a path to a device can specify a callback function. If the path is not held exclusively, any other drivers must specify a null address for their callback functions.

### Using scsi_free()

A SCSI driver typically calls `scsi_free()` from the pfx `close()` entry point. That is the time when the driver knows that no processes have the device open, so the host adapter should be allowed to release any resources it is holding just for this device.

In addition, `scsi_free()` releases the device for use by other drivers, if the driver had allocated it for exclusive use.

### Using scsi_command()

A SCSI device driver sends SCSI commands to its device by storing information in a scsi_request structure and passing the structure to the `scsi_command()` function for the adapter. The host adapter driver schedules the command on the SCSI bus that it manages, and returns to the caller. When the command completes, a notify function is invoked. (See also reference page `scsi_command(d3x)` .)

| Tip: When debugging a driver using a debugging kernel (see “Preparing the System for Debugging” in Chapter 10), you can display the contents of a scsi_request structure using `symmon` or `idbg` (see “Commands to Display I/O Status” in Chapter 10).

#### Input to scsi_command()

The device driver prepares the scsi_request fields shown in Table 16-3.

Table 16-3. Input Fields of the scsi_request Structure

Field Name | Contents
--- | ---
sr_dev_vhdl | The vertex handle of the LUN vertex. This field is required.
sr_ctlr | The adapter number.
sr_target | The target number.
sr_lun | The logical unit number.
sr_tag | If this target supports the SCSI-2 tagged-queue feature, and this command is directed to a queue, this field contains the queue tag message. Constant names for queue messages are in `sys/scsi.h`: SC_TAG_SIMPLE and two others.
sr_command | Address of the bytes of the SCSI command to issue.
sr_cmdlen | The length of the string at *sr_command. Constants for the common lengths are in `sys/scsi.h`: SC_CLASS0_SZ (6), SC_CLASS1_SZ (10), and SC_CLASS2_SZ (12).
sr_flags | Flags for data direction and DMA mapping; see Table 16-4 .
sr_timeout | Number of ticks (HZ units) to wait for a response before timing out. The host adapter driver supplies a minimum value if this field is zero or too small.
sr_buffer | Address of first byte of data. Must be zero when sr_bp is supplied and SRF_MAPBP is specified in sr_flags.
sr_buflen | Length of data or buffer space.
sr_sense | Address of space for sense data, in case the command ends in a check condition.
sr_senselen | Length of the sense area.
sr_notify | Address of the callback function, called when the command is complete. A callback address is required on all commands (this is a change in IRIX 6.4).
sr_bp | Address of a buf_t object, when the command is called from a block driver's pfx```strategy``()` entry point and buffer mapping is requested in sr_flags.
sr_dev | Address of additional information that could be useful in the callback routine *sr_notify.

Although the unit, target, and controller numbers can be discovered from the handle in sr_dev_vhdl, this would be time-consuming. Therefore the driver is still required to provide all three numbers in addition to the handle.

The callback function address in sr_notify must be specified. (Device drivers for versions of IRIX previous to 6.4 may set a NULL in this field; that is no longer permitted.)

The possible flag bits that can be set in sr_flags are listed in Table 16-4.

Table 16-4. Values for the sr_flags Field of a scsi_request

Flag Constant | Purpose
--- | ---
SRF_DIR_IN | Data will be received in memory. If this flag is absent, the command sends data from memory to the device.
SRF_FLUSH | The data cache for the buffer area should be flushed (for output) or marked invalid (for input) prior to the command. This flag should be used whenever the buffer is local to the driver, not mapped by a buf_t object. It causes no extra overhead in systems that do not require cache flushing.
SRF_MAPUSER | Set this flag when doing I/O based on a buf_t and B_MAPUSER is set in b_flags.
SRF_MAP | Set this flag when doing I/O based on a buf_t and the BP_ISMAPPED macro returns nonzero.
SRF_MAPBP | The sr_bp field points to a buf_t in which BP_ISMAPPED returns false. The host adapter driver maps in the buffer.
SRF_CONTINGENT_ ALLEGIANCE_CLEAR | Indicates that the driver wishes to clear a contingent allegiance condition with the host adapter driver. After a host adapter driver has returned sense data to the device driver, all future requests are immediately returned with SC_ATTN until this flag is set. SRF_AEN_ACK is a synonym that may appear in older code.
SRF_NEG_SYNC | Attempt to negotiate synchronous transfer mode for this command. Ignored by some host adapter drivers. Overrides SCSIALLOC_NOSYNC (see “Using scsi_alloc()” ).
SRF_NEG_ASYNC | Attempt to negotiate asynchronous mode for this command. Ignored unless the device is currently using synchronous mode.
SRF_ALENLIST | Set this flag and then set b_private to use the alenlist created—use `uvaddr_to_alenlist` to create the alenlist (see `alenlist_ops(D3)` ).
SRF_PRIORITY_REQUEST | Set this flag for a “priority” SCSI request (see `scsi.h`).

When none of the three flag values beginning SRF_MAP is supplied, the sr_buffer address must be a physical memory address. The SRF_MAPUSER and SRF_MAPBP flags are normally used when the command is issued from a pfx `strategy()` entry point in order to read or write a buffer controlled from a buf_t object.

#### Command Execution

The host adapter driver validates the contents of the scsi_request structure. If the contents are valid, it queues the command for transmission on the adapter and returns. If the contents are invalid, it sets a status flag (see Table 16-6), calls the sr_notify function, and returns.

In any event, the sr_notify function is called when the command is complete. This function can be called from the host adapter interrupt handler, so it can be entered asynchronously and concurrent with any part of the device driver.

The device driver should wait for the notify function to be called. The usual way is to share a semaphore (see “Semaphores” in Chapter 8), as follows:

- Before calling `scsi_command()`, initialize the semaphore to 0 (the semaphore is being used to wait for an event).

- Immediately after the call to `scsi_command()`, call `psema()` for the semaphore.

- In the notify function, call `vsema()` for the semaphore.

If the request is valid, the device driver will sleep in the `psema()` call until the command completes. If the request is invalid, the semaphore may already have been posted when the call to `psema()` is reached.

In the event that the device driver holds an exclusive lock before issuing the command and wants to release the lock while it waits and then regain the lock, a synchronization variable provides the appropriate mechanism (see “Using Synchronization Variables” in Chapter 8).

#### Values Returned in a scsi_request Structure

The host adapter driver sets the results of the request in the scsi_request structure. The sr_notify function is the first to inspect the values summarized in Table 16-5.

Table 16-5. Values Returned From a SCSI Command

Field Name | Purpose
--- | ---
sr_status | Software status flags, see Table 16-6 .
sr_scsi_status | SCSI status byte, see Table 16-7 .
sr_ha_flags | Host adapter status flags, see Table 16-8 .
sr_sensegotten | When no sense command was issued, 0. When a sense command was issued following an error, the number of bytes of sense data received. When an error occurred during a sense command, -1.
sr_resid | The difference between sr_buflen and the number of bytes actually transferred.

The sr_status field should be tested first. It contains an integer value; the possible values are summarized in Table 16-6.

Table 16-6. Software Status Values From a SCSI Request

Constant Name | Meaning
--- | ---
SC_GOOD | The request was valid and the command was executed. The command might still have failed; see sr_scsi_status.
SC_REQUEST | An error was detected in the input values; the command was not attempted. The error could be that `scsi_alloc()` has not been called, or it could be due to missing or incorrect values.
SC_TIMEOUT | The device did not respond to selection within 250 milliseconds.
SC_HARDERR | A hardware error occurred. (You can try inspecting sr_senselen to see if sense data was received, but typically it will not have sense data associated with it.)
SC_PARITY | SCSI bus parity error detected.
SC_MEMERR | System memory parity or ECC error detected.
SC_CMDTIME | The device responded to selection but the command did not complete before sr_timeout expired.
SC_ALIGN | The buffer address was not aligned as required by the adapter hardware. Most SGI adapters require word (4-byte) alignment.
SC_ATTN | The command could not be completed due to circumstances not related to the command, and not due to an error in the command.

SC_ATTN status is returned when a command is aborted by some event not directly related to the command, such as:

- SCSI bus reset, which aborts all outstanding commands.

- A contingent allegiance condition when QERR is 1, in which all outstanding commands to a LUN are aborted.

- The command follows the return of sense data but SRF_CONTINGENT_ALLEGIANCE_CLEAR is not set in the request (see Table 16-4).

One or more bits are set in the sc_scsi_status field. This field represents the status following the requested command, when the requested command executes correctly. When the requested command ends with Check Condition status, a sense command is issued and the SCSI status following the sense is placed in sc_scsi_status. In other words, the true indication of successful execution of the requested command is a zero in sr_sensegotten, because this indicates that no sense command was attempted.

Possible values of sc_scsi_status are summarized in Table 16-7.

Table 16-7. SCSI Status Bytes

Constant Name | Meaning
--- | ---
ST_GOOD | The target has successfully completed the SCSI command. If a check condition was returned, a sense command was issued. The sr_sensegotten field is nonzero when this was the case.
ST_CHECK | This bit is set only for the special case when a check condition occurred on a sense command following a check condition on the requested command. The sr_sensegotten field contains -1.
ST_COND_MET | Search condition was met.
ST_BUSY | The target is busy. The driver will normally delay and then request the command again.
ST_INT_GOOD | This status is reported for every command in a series of linked commands. Linked commands are not supported by SGI host adapters.
ST_INT_COND_MET | The sum of ST_COND_MET and ST_INT_COND_MET.
ST_RES_CONF | A conflict with a reserved logical unit or reserved extent.

One or more bits may be set in sr_ha_flags to document a host adapter state or problem (but not all host adapter drivers do this). These flags are summarized in Table 16-8.

Table 16-8. Host Adapter Status After a SCSI Request

Constant Name | Meaning
--- | ---
SRH_SYNCXFR | Synchronous mode was used. If not set, asynchronous mode was used.
SRH_TRIEDSYNC | Synchronous mode negotiation was attempted; see the SHR_CANTSYNC bit for the result.
SRH_CANTSYNC | Unable to negotiate synchronous mode. See also SRH_BADSYNC.
SRH_BADSYNC | When SRH_CANTSYNC is set, indicates that the negotiation failed because the device cannot negotiate.
SRH_NOADAPSYNC | When SRH_CANTSYNC is set, this bit indicates that the host adapter does not support synchronous negotiation, or that the system has been configured not to use synchronous mode for this device.
SRH_WIDE | This adapter supports Wide mode.
SRH_DISC | This adapter supports Disconnect mode and is configured to use it.
SRH_TAGQ | This adapter supports tagged queueing and is configured to use it.
SRH_MAPUSER | This host adapter driver can map user addresses.
SRH_QERR0, SRH_QERR1 | This host adapter supports one or the other of the queuing error recovery policies. The device reports its QERR bit on the Control mode page. If the device policy differs from the host adapter policy, the device driver should avoid the use of queued commands.

### Using scsi_abort()

The purpose of the `scsi_abort()` function is to abort all pending or executing commands on a device. The prototype of the function is:

    SCI_ABORT(SLI_CTLR_INFO(scsi_lun_info_get(lun_vhdl))
                   (struct scsi_request *req);

The only fields of the scsi_request that are input to this function are those that identify the device: sr_dev_vhdl (always!), sr_ctlr, sr_target, and sr_lun. The ABORT command is issued on the bus as soon as possible but there could be a delay if the bus is busy. Status is returned in sr_status. The function returns a nonzero value when the ABORT command is issued successfully, and a zero when the ABORT command fails (which probably indicates a serious bus problem).

| Note: Not all devices and not all host adapters support this operation. Error recovery of queued commands is up to the driver.

## Designing a SCSI Driver

As of IRIX 6.5, support is provided for you to write your own kernel-level SCSI device driver using the software interfaces and hardware devices supported by SGI. A SCSI driver can be loadable or it can be linked with the kernel. In general it is configured into IRIX as described in Chapter 9, “Building and Installing a Driver”. However, a SCSI driver uses additional services, including those of the host adapter driver, and its configuration is slightly different from other drivers.

IRIX support for the SCSI bus is designed to allow support for dynamic reconfiguration. A SCSI driver can be designed to allow devices to be attached and detached at any time. The general sequence of operations related to a functioning SCSI driver is as follows:

1. The driver is placed for kernel inclusion (or to be loaded later) and all appropriate system support files are properly configured (see Chapter 9, “Building and Installing a Driver”).

2. The (optional) `pfxinit()` entry point is called early in the boot sequence so the driver can perform initialization procedures.

3. In the (required) pfx `reg()` entry point, the driver registers itself as a SCSI driver, specifying the SCSI device type it supports and the driver prefix. See “About Registration”.

4. The host adapter driver discovers attached SCSI devices that return a device type and vendor and product identification strings. The kernel searches a table list for each discovered device for matching vendor ID and product ID strings for that device type. If it discovers a matching entry, it calls the (required) pfx `attach()` entry point of the registered driver, supplying the hwgraph vertex handle. See “About Attaching a Device”.

5. The driver uses the supplied vertex handle to construct the proper hwgraph space for its device(s). See “Building hwgraph Entries”. The driver may also create convenient aliases for hwgraph entries (see “Creating Device Aliases”).

6. The driver interacts with the device(s) using the SCSI host adapter interface as described under “Host Adapter Facilities”.

7. If the kernel learns that the device is being detached, the kernel calls the driver's pfx `detach()` entry point. The driver then undoes the work done in pfx `attach()`.

These steps are described in more detail in the following sections:

- “Configuring a SCSI Driver”

- “About Registration”

- “About Attaching a Device”

- “Opening a SCSI Device”

- “About Detaching a Device”

- “About Unloading a SCSI Driver”

- “Creating Device Aliases”

### Configuring a SCSI Driver

A SCSI driver can be either a block or a character driver, or it can support both interfaces. When preparing the descriptive file for `/var/sysgen/master.d`, you must use the `s` flag, specifying a software-only driver, and list `scsi` as a dependency in the description line. See “Describing the Driver in /var/sysgen/master.d” in Chapter 9.

### About Registration

Registration is a step that tells the kernel how to associate a device with a driver. The driver must register with the kernel or it will not be able to access a device.

At boot time, the host adapter driver discovers the complement of devices by probing the bus. A SCSI device is identified by its device type, a number defined as shown in Table 16-9

Table 16-9. SCSI Device Type Numbers

Number | Type
--- | ---
0 | Direct-access device (for example, magnetic disk)
1 | Sequential-access device (for example, magnetic tape)
2 | Printer device
3 | Processor device
4 | Write-once device (for example, some optical disks)
5 | CD-ROM device
6 | Scanner device
7 | Optical memory device (for example, some optical disks)
8 | Medium changer device (for example, jukeboxes)

In addition to the device type, SCSI devices supply vendor ID and product ID strings. When the kernel finds a device, it needs to associate it with a driver. For SCSI devices, the kernel looks through a list of drivers that have registered as supporting SCSI devices of the particular type. If a driver of that type has registered, and the kernel finds an entry for a driver of that type with vendor ID and product ID strings that match the ones found at device discovery, it calls pfx `attach()` (see “About Attaching a Device”).

Registration tables for driver types are defined in `master.d/scsi.h`. The entries in `scsi_drivers[]` list the device types supported (by default, only the type 1 table is defined, but you may define other types):

    scsi_type_reg_s scsi_drivers[] = {
    { 0, NULL                    },      /* Type 0 driver reg table */
    { 0, scsi_drivers_type1      },      /* Type 1 driver reg table */
    { 0, NULL                    },      /* Type 2 driver reg table */
    { 0, NULL                    },      /* Type 3 driver reg table */
    { 0, NULL                    },      /* Type 4 driver reg table */
    { 0, NULL                    },      /* Type 5 driver reg table */
    { 0, NULL                    },      /* Type 6 driver reg table */
    { 0, NULL                    },      /* Type 7 driver reg table */
    { 0, NULL                    },      /* Type 8 driver reg table */
    { 1, NULL                    },      /* Terminator - don't remove */
    };

A driver is associated with a device type in the `master.d/scsi` file with a four part entry for the specific table type. The four fields are strings that contain the SCSI vendor ID, the SCSI product ID, the driver prefix, and a hwgraph pathname component.

For example, consider the following entry for a type 1 (tape) driver:

    scsi_driver_reg_s scsi_drivers_type1[] = {
        // Type 1 - sequential access devices, tapes //
        { “Fujitsu”, “Diana-1”, “fuj”, “fujitsu-tape” },
        { NULL, NULL, NULL, NULL }
    };

The vendor ID is `Fujitsu`, the product ID is `Diana-1`, the prefix is `fuj`, and the hwgraph pathname component is `fujitsu-tape`. So, if a SCSI Inquiry on the device at device discovery time returns a type 1, and the vendor and product ID returned are `Fujitsu` and `Diana-1`, the kernel will address the driver entry points with the prefix `fuj`, passing the hwgraph pathname suffix fujitsu-tape.

Your driver registers by calling the `scsi_driver_register()` function (see `master.d/scsi`):

    int scsi_driver_register(int unit_type, char *driver_prefix)

This call specifies the SCSI device type int (see Table 16-9) and the driver's prefix character string that you define. The prefix string is configured in the driver's descriptive file (see “Describing the Driver in /var/sysgen/master.d” in Chapter 9). The kernel uses this string to find the addresses of driver entry points. Note that you may call this function multiple times if your driver supports more than one SCSI device type.

You should call `scsi_driver_register()` from the pfx `reg()` entry point. Be aware that, if there is an available device of the specified type, pfx `attach()` can be called immediately, before the `scsi_driver_register()` function returns.

The order in which drivers are called to attach a device is not defined.

### About Attaching a Device

At device discovery during the boot sequence, the kernel identifies SCSI devices by device type and by the vendor ID and product ID strings. It then searches the device type table for matching strings (see “About Registration”). When it finds a match, it uses the associated prefix string in the table entry to call pfx `attach()` and passes the hwgraph vertex handle, which represents the “connection point” of the device—typically the LUN vertex handle (for example `.../scsi_ctlr/0/target/0/lun/0`). The driver adds more vertexes connected to this one to represent the logical devices. The handle of the connection point is needed in several kernel functions, so you should save it as part of the device information.

#### Device and Inventory Information

You should allocate and initialize a device information structure for each device. You should also put inventory information on one created vertex, for example, /`hw/.../xyz/disk/volume/char`. Refer to “Attaching Information to Vertexes” in Chapter 8. Note that you should create the devices representing your actual hardware configuration, and not all possible devices as used to be the case with the old `/dev` file scheme populated by MKNOD. In this way, the `/hw` structure represents the actual system configuration. (Consequently, when detaching, you should remove any created nodes as described in “About Detaching a Device”.)

#### Building hwgraph Entries

Use `hwgraph_char_device_add` or `hwgraph_block_device_add` (possibly both, depending on your device), to add vertexes to the hardware graph. You pass the vertex handle received at pfx `attach` along with the additional edges or path to describe each logical device. For example, if the vertex handle received was `/hw/.../xyz`, an entry you create with `hwgraph_block_device_add` might be /`hw/.../xyz/partition/0/block`. Refer to “SCSI Devices in the hwgraph” for information on how the SCSI host adapter performs these same functions.

#### Returning from pfxattach

The return code from pfx `attach()` is tested by the kernel. The driver can reject an attachment. When your driver fails due to some problem, it should:

- Use `cmn_err()` to document the problem (see “Using cmn_err” in Chapter 10)

- Release any space allocated to the device such as a device information structure

- Return an informative return code

The pfx `detach()` entry point can be called only if the pfx `attach()` entry point returns success (0).

Whenever the new vertex is opened, pfx `open` is called.

### Opening a SCSI Device

When the pfx `open()` entry point is called, the SCSI driver uses the appropriate `scsi_info()` function to verify the device and get hardware dependent Inquiry data from it. If the device is not operational, the driver can return ENODEV. If the device is operational, the driver calls `scsi_alloc()` to open a communications path to it.

The pfx `open` entry point is passed the edge vertex, which you can use with `device_info_get` to access the device info pointer (see “Hardware Graph Management” in Chapter 8).

Refer to “Host Adapter Facilities” for information on how your driver can interact with the device.

### Accessing a SCSI Device

In general, it is simplest to put all access to a device within a pfx `strategy()` entry point, even in a character device driver. When the pfx `read()`, pfx `write()`, or pfx `ioctl()` entry point needs to read or write data, it can prepare a uio_t to describe the data, and call `uiophysio()` to direct the operation through the single pfx `strategy()` entry point (see “Calling Entry Point strategy() From Entry Point read() or write()” in Chapter 7).

The notify routine passed in the sr_notify field plays the same role as the pfx `intr()` entry point in other device drivers. It is called asynchronously, when the SCSI command completes. It may not call a kernel function that can sleep. However, it does not have to be named pfx `intr()`, and a SCSI driver does not have to provide a pfx `intr()` entry point.

### About Detaching a Device

Your pfx `detach` entry point is where you remove hwgraph vertexes added with pfx `attach`. Note that if you create aliases with an ioctl (see “Creating Device Aliases”), you should remove them in your pfx `detach` routine as well. As a result of this practice, the hwgraph will represent the actual available devices.

### About Unloading a SCSI Driver

When a loadable SCSI driver is called at its pfx `unload()` entry point, indicating that the kernel would like to unload it, the driver must take pains not to leave any dangling pointers (as discussed under “Entry Point unload()” in Chapter 7). A driver should not unload when it has any registered interrupt or error handlers.

A driver does not have to unregister itself as a SCSI driver before unloading. Nor does it have to detach any devices it has attached. However, if any devices are open or memory mapped, the driver should not unload.

If the driver has been autoregistered (see “Registration” in Chapter 9), stub functions are placed in the switch tables for the attach and open functions. When the kernel discovers a new device and wants this driver to attach it, or when a process attempts to open a device for which this driver created the vertex, the kernel reloads the driver.

### Creating Device Aliases

A device alias is a convenient shorthand path which refers to the same device as the full hwgraph entry. If you want your driver to create aliases for hwgraph entries, create a file in `/var/sysgen/ioconfig`, for example, `/var/sysgen/ioconfig/xyz`. This file allows you to choose a stable controller number for your device alias, and to specify an ioctl number used by `ioconfig` and your driver to create the alias. (See `/var/sysgen/ioconfig/README` and `ioconfig(1M)` for details of the file syntax.)

Create a pfx `ioctl` entry point that is responsible for creating device aliases, for example, a path under `/hw/disk` and `/hw/rdisk` corresponding to the actual hwgraph entry. The pfx `ioctl` entry point might be called with XYZ_ALIAS, for example, which is a numerical value specified in the last entry in `/var/sysgen/ioconfig/xyz`.

Use `device_controller_num_get(`dev`)` (see `sys/invent.h`), where dev is the hwgraph vertex on which your driver added inventory information, to get the controller number that has been assigned by `ioconfig`. The controller number supplied will be the starting one claimed in `/var/sysgen/ioconfig`, so your aliases will remain associated with the actual hwgraph entries.

For example, if you have specified that controller numbers should start at 10, you can be assured that you will always use 10 as your first controller number and, by picking a relatively high number, your driver should have no effect on ioconfig's default controller number assignments for other controllers of the class.

Refer to “Convenience Vertexes” for information on how the SCSI host adapter driver creates the hwgraph aliases.

## SCSI Reference Data

This section contains reference material in the following categories:

- “SCSI Error Messages” describes the general form of messages written by host adapter drivers into the system log.

- “Adapter Error Codes (Table scsi_adaperrs_tab)” lists the possible adapter error codes and their message strings.

- “SCSI Sense Codes (Table scsi_key_msgtab) ” lists the primary sense codes and the corresponding message strings.

- “Additional Sense Codes (Table scsi_addit_msgtab)” lists the possible additional sense codes (ASCs) and their message strings.

### SCSI Error Messages

The host adapter drivers send error messages to the system log using the `cmn_err()` function (see “Producing Diagnostic Displays” in Chapter 10).

These messages almost always contain the adapter number (sometimes called the bus number or controller number). They sometimes contain the number of the target device, and sometimes add the number of the logical unit that was addressed.

Messages from the wd93 driver specify the adapter number as `Bus=`n. The target device is shown as `ID=`n and the logical unit as `LUN=`n.

Messages from the wd95 and jag drivers contain one, two, or three or more decimal numbers. In all cases, the first number is the adapter number, the second is the target ID, and the third (when present) is the logical unit number.

When error messages list a sense code, refer to “SCSI Sense Codes (Table scsi_key_msgtab) ” and to “Additional Sense Codes (Table scsi_addit_msgtab)”.

When the error message reports an error from the adapter itself, refer to “Adapter Error Codes (Table scsi_adaperrs_tab)”.

### SCSI Error Message Tables

The scsi module contains a set of error message tables that you can use to generate error messages based on SCSI sense codes and other data. The contents of these tables is documented here for reference, and to assist in decoding messages from SCSI drivers.

Each table is an array of pointers to strings; for example, the definition of the scsi_key_msgtab table begins as follows:

    char *scsi_key_msgtab[SC_NUMSENSE] = {
       "No sense",          /* 0x0 */
       "Recovered Error",   /* 0x1 */
    ...};

Each of the tables is declared as extern in `sys/scsi.h`.

#### Adapter Error Codes (Table scsi_adaperrs_tab)

The table with the external name scsi_adaperrs_tab contains message strings to document the adapter error codes that can be returned in the scsirequest.sr_status field (see Table 16-6). The scsi_adaperrs_tab table contains NUM_ADAP_ERRS entries (9, defined in `sys/scsi.h`). The first entry (index 0x0) contains a pointer to a null string. The other entries are documented in Table 16-10.

Table 16-10. Adapter Error Codes

Adapter Error Code | Constant Name | Message Text
--- | --- | ---
0x1 | SC_TIMEOUT | Device does not respond to selection.
0x2 | SC_HARDERR | Controller protocol error or SCSI bus reset.
0x3 | SC_PARITY | SCSI bus parity error.
0x4 | SC_MEMERR | Parity/ECC error in system memory during DMA.
0x5 | SC_CMDTIME | Command timed out.
0x6 | SC_ALIGN | Buffer not correctly aligned in memory.
0x7 | SC_ATTN | Unit attention received on another command causes retry.
0x8 | SC_REQUEST | Driver protocol error.

#### SCSI Sense Codes (Table scsi_key_msgtab)

The table with the external name scsi_key_msgtab is indexed by the primary sense code. Its contents are listed in Table 16-11. The table contains SC_NUMADDSENSE entries (16, defined in `sys/scsi.h`), of which the last two should not occur.

Table 16-11. Primary Sense Key Error Table

Sense Key | Message | Most Common Cause
--- | --- | ---
0x0 | `No sense` | No error information available.
0x1 | `Recovered error` | The device recovered by itself.
0x2 | `Device not ready` | No media, or drive not spun up.
0x3 | `Media error` | An actual media problem.
0x4 | `Device hardware error` | Usually a device hardware error.
0x5 | `Illegal request` | Invalid command or data issued.
0x6 | `Unit attention` | Device was reset or power-cycled.
0x7 | `Data protect error` | Usually device is write protected.
0x8 | `Unexpected blank media` | Tried to read at end of a tape.
0x9 | `Vendor unique error` | Varies.
0xA | `Copy aborted` | Copy command aborted by host (not used).
0xB | `Aborted command` | Target device aborted command.
0xC | `Search data successful` | Search data command OK (not used).
0xD | `Volume overflow` | Tried to write past EOT on tape.
0xE | `Reserved (0xE)` | 0xE should not be seen.
0xF | `Reserved (0xF)` | 0xF should not be seen.

#### Additional Sense Codes (Table scsi_addit_msgtab)

The table with the external name scsi_addit_msgtab is indexed by the Additional Sense Code (ASC) value, when one is present. The table contains SC_NUMADDSENSE entries (0x71, defined in `sys/scsi.h`). Some values have no standard definition; for these, the table contains a NULL value. Therefore you should always test the table value for a valid pointer before using it to format a message. Table 16-12 lists the contents of this message table. Undefined (NULL) table entries are omitted.

Table 16-12. Additional Sense Code Table

ASC Value | Corresponding Message String
--- | ---
0x01 | `No index/sector signal`
0x02 | `No seek complete`
0x03 | `Write fault`
0x04 | `Not ready to perform command`
0x05 | `Unit does not respond to selection`
0x06 | `No reference position`
0x07 | `Multiple drives selected`
0x08 | `LUN communication error`
0x09 | `Track error`
0x0a | `Error log overflow`
0x0c | `Write error`
0x10 | `ID CRC or ECC error`
0x11 | `Unrecovered data block read error`
0x12 | `No address mark found in ID field`
0x13 | `No address mark found in Data field`
0x14 | `No record found`
0x15 | `Seek position error`
0x16 | `Data sync mark error`
0x17 | `Read data recovered with retries`
0x18 | `Read data recovered with ECC`
0x19 | `Defect list error`
0x1a | `Parameter overrun`
0x1b | `Synchronous transfer error`
0x1c | `Defect list not found`
0x1d | `Compare error`
0x1e | `Recovered ID with ECC`
0x20 | `Invalid command code`
0x21 | `Illegal logical block address`
0x22 | `Illegal function`
0x24 | `Illegal field in CDB`
0x25 | `Invalid LUN`
0x26 | `Invalid field in parameter list`
0x27 | `Media write protected`
0x28 | `Media change`
0x29 | `Device reset`
0x2a | `Log parameters changed`
0x2b | `Copy requires disconnect`
0x2c | `Command sequence error`
0x2d | `Update in place error`
0x2f | `Tagged commands cleared`
0x30 | `Incompatible media`
0x31 | `Media format corrupted`
0x32 | `No defect spare location available`
0x33[a] | `Media length error`
0x36 | `Toner/ink error`
0x37 | `Parameter rounded`
0x39 | `Saved parameters not supported`
0x3a | `Medium not present`
0x3b | `Forms error`
0x3d | `Invalid ID msg`
0x3e | `Self config in progress`
0x3f | `Device config has changed`
0x40 | `RAM failure`
0x41 | `Data path diagnostic failure`
0x42 | `Power on diagnostic failure`
0x43 | `Message reject error`
0x44 | `Internal controller error`
0x45 | `Select/reselect failed`
0x46 | `Soft reset failure`
0x47 | `SCSI interface parity error`
0x48 | `Initiator detected error`
0x49 | `Inappropriate/illegal message`
0x4a | `Command phase error`
0x4b | `Data phase error`
0x4c | `Failed self configuration`
0x4e | `Overlapped commands attempted`
0x53 | `Media load/unload failure`
0x57 | `Unable to read table of contents`
0x58 | `Generation (optical device) bad`
0x59 | `Updated block read (optical device)`
0x5a | `Operator request or state change`
0x5b | `Logging exception`
0x5c | `RPL status change`
0x5d | `Self diagnostics predict unit will fail soon`
0x60 | `Lamp failure`
0x61 | `Video acquisition error/focus problem`
0x62 | `Scan head positioning error`
0x63 | `End of user area on track`
0x64 | `Illegal mode for this track`
0x70[b] | `Decompression error`
[a] Specified as tape only. [b] DAT only; may be in SCSI3.

## A Note on FibreChannel Drivers

The FibreChannel adapter is accessed just like a SCSI adapter. It is a peer to drivers such as ql, adp78, and wd95.

Note that there is one difference in that all commands are tagged, whether or not the sr_tag member of the scsi_request structure is set.

Prev | Table of Contents | Next
Part V. SCSI Device Drivers |  | Part VI. Network Drivers