pkg://Linux-HOWTOs.tar.gz:1682658/SCSI-Programming-HOWTO
downloads
The Linux SCSI programming HOWTO
Heiko Eißfeldt heiko@colossus.escape.de
v1.5, 7 May 1996
This document deals with programming the Linux generic SCSI interface.
1. What's New?
Newer kernels have changed the interface a bit. This affects a section
formerly entitled 'rescanning the devices'. Now it is possible to
add/remove SCSI devices on the fly.
Since kernel 1.3.98 some important header files have been moved/split
(sg.h and scsi.h).
Some stupid bugs have been replaced by newer ones.
2. Introduction
This document is a guide to the installation and programming of the
Linux generic SCSI interface.
It covers kernel prerequisites, device mappings, and basic interaction
with devices. Some simple C programming examples are included.
General knowledge of the SCSI command set is required; for more
information on the SCSI standard and related information, see the
appendix to this document.
Note the plain text version of this document lacks cross references
(they show up as ``'').
3. What Is The Generic SCSI Interface?
The generic SCSI interface has been implemented to provide general
SCSI access to (possibly exotic) pieces of SCSI hardware. It was
developed by Lawrence Foard ( entropy@world.std.com) and sponsored by
Killy Corporation (see the comments in scsi/sg.h).
The interface makes special device handling possible from user level
applications (i.e. outside the kernel). Thus, kernel driver
development, which is more risky and difficult to debug, is not
necessary.
However, if you don't program the driver properly it is possible to
hang the SCSI bus, the driver, or the kernel. Therefore, it is
important to properly program the generic driver and to first back up
all files to avoid losing data. Another useful thing to do before
running your programs is to issue a sync command to ensure that any
buffers are flushed to disk, minimizing data loss if the system hangs.
Another advantage of the generic driver is that as long as the
interface itself does not change, all applications are independent of
new kernel development. In comparison, other low-level kernel drivers
have to be synchronized with other internal kernel changes.
Typically, the generic driver is used to communicate with new SCSI
hardware devices that require special user applications to be written
to take advantage of their features (e.g. scanners, printers, CD-ROM
jukeboxes). The generic interface allows these to be written quickly.
4. What Are The Requirements To Use It?
4.1. Kernel Configuration
You must have a supported SCSI controller, obviously. Furthermore,
your kernel must have controller support as well as generic support
compiled in. Configuring the Linux kernel (via make config under
/usr/src/linux) typically looks like the following:
...
*
* SCSI support
*
SCSI support? (CONFIG_SCSI) [n] y
*
* SCSI support type (disk, tape, CDrom)
*
...
Scsi generic support (CONFIG_CHR_DEV_SG) [n] y
*
* SCSI low-level drivers
*
...
If available, modules can of course be build instead.
4.2. Device Files
The generic SCSI driver uses its own device files, separate from those
used by the other SCSI device drivers. They can be generated using the
MAKEDEV script, typically found in the /dev directory. Running MAKEDEV
sg produces these files:
crw------- 1 root system 21, 0 Aug 20 20:09 /dev/sga
crw------- 1 root system 21, 1 Aug 20 20:09 /dev/sgb
crw------- 1 root system 21, 2 Aug 20 20:09 /dev/sgc
crw------- 1 root system 21, 3 Aug 20 20:09 /dev/sgd
crw------- 1 root system 21, 4 Aug 20 20:09 /dev/sge
crw------- 1 root system 21, 5 Aug 20 20:09 /dev/sgf
crw------- 1 root system 21, 6 Aug 20 20:09 /dev/sgg
crw------- 1 root system 21, 7 Aug 20 20:09 /dev/sgh
| |
major, minor device numbers
Note that these are character devices for raw access. On some systems
these devices may be called /dev/{sg0,sg1,...}, depending on your
installation, so adjust the following examples accordingly.
4.3. Device Mapping
These device files are dynamically mapped to SCSI id/LUNs on your SCSI
bus (LUN = logical unit). The mapping allocates devices consecutively
for each LUN of each device on each SCSI bus found at time of the SCSI
scan, beginning at the lower LUNs/ids/buses. It starts with the first
SCSI controller and continues without interruption with all following
controllers. This is currently done in the initialisation of the SCSI
driver.
For example, assuming you had three SCSI devices hooked up with ids 1,
3, and 5 on the first SCSI bus (each having one LUN), then the
following mapping would be in effect:
/dev/sga -> SCSI id 1
/dev/sgb -> SCSI id 3
/dev/sgc -> SCSI id 5
If you now add a new device with id 4, then the mapping (after the
next rescan) will be:
/dev/sga -> SCSI id 1
/dev/sgb -> SCSI id 3
/dev/sgc -> SCSI id 4
/dev/sgd -> SCSI id 5
Notice the change for id 5 -- the corresponding device is no longer
mapped to /dev/sgc but is now under /dev/sgd.
Luckily newer kernels allow for changing this order.
4.3.1. Dynamically insert and remove SCSI devices
If a newer kernel and the /proc file system is running, a non-busy
device can be removed and installed 'on the fly'.
To remove a SCSI device:
echo "scsi remove-single-device a b c d" > /proc/scsi/scsi
and similar, to add a SCSI device, do
echo "scsi add-single-device a b c d" > /proc/scsi/scsi
where
a == hostadapter id (first one being 0)
b == SCSI channel on hostadapter (first one being 0)
c == ID
d == LUN (first one being 0)
So in order to swap the /dev/sgc and /dev/sgd mappings from the
previous example, we could do
echo "scsi remove-single-device 0 0 4 0" > /proc/scsi/scsi
echo "scsi remove-single-device 0 0 5 0" > /proc/scsi/scsi
echo "scsi add-single-device 0 0 5 0" > /proc/scsi/scsi
echo "scsi add-single-device 0 0 4 0" > /proc/scsi/scsi
since generic devices are mapped in the order of their insertion.
When adding more devices to the scsi bus keep in mind there are
limited spare entries for new devices. The memory has been allocated
at boot time and has room for 2 more devices.
5. Programmers Guide
The following sections are for programmers who want to use the generic
SCSI interface in their own applications. An example will be given
showing how to access a SCSI device with the INQUIRY and the
TESTUNITREADY commands.
When using these code examples, note the following:
· the location of the header files sg.h and scsi.h has changed in
kernel version 1.3.98. Now these files are located at
/usr/src/linux/include/scsi, which is hopefully linked to
/usr/include/scsi. Previously they were in
/usr/src/linux/drivers/scsi. We assume a newer kernel in the
following text.
· the generic SCSI interface was extended in kernel version 1.1.68;
the examples require at least this version. But please avoid kernel
version 1.1.77 up to 1.1.89 and 1.3.52 upto 1.3.56 since they had a
broken generic scsi interface.
· the constant DEVICE in the header section describing the accessed
device should be set according to your available devices (see
section ``''.
6. Overview Of Device Programming
The header file include/scsi/sg.h contains a description of the
interface (this is based on kernel version 1.3.98):
struct sg_header
{
int pack_len;
/* length of incoming packet (including header) */
int reply_len; /* maximum length of expected reply */
int pack_id; /* id number of packet */
int result; /* 0==ok, otherwise refer to errno codes */
unsigned int twelve_byte:1;
/* Force 12 byte command length for group 6 & 7 commands */
unsigned int other_flags:31; /* for future use */
unsigned char sense_buffer[16]; /* used only by reads */
/* command follows then data for command */
};
This structure describes how a SCSI command is to be processed and has
room to hold the results of the execution of the command. The
individual structure components will be discussed later in section
``''.
The general way of exchanging data with the generic driver is as
follows: to send a command to an opened generic device, write() a
block containing these three parts to it:
struct sg_header
SCSI command
data to be sent with the command
To obtain the result of a command, read() a block with this (similar)
block structure:
struct sg_header
data coming from the device
This is a general overview of the process. The following sections
describe each of the steps in more detail.
NOTE: Up to recent kernel versions, it is necessary to block the
SIGINT signal between the write() and the corresponding read() call
(i.e. via sigprocmask()). A return after the write() part without any
read() to fetch the results will block on subsequent accesses. This
signal blocking has not yet been included in the example code. So
better do not issue SIGINT (a la ^C) when running these examples.
7. Opening The Device
A generic device has to be opened for read and write access:
int fd = open (device_name, O_RDWR);
(This is the case even for a read-only hardware device such as a cdrom
drive).
We have to perform a write to send the command and a read to get back
any results. In the case of an error the return code is negative (see
section ``'' for a complete list).
8. The Header Structure
The header structure struct sg_header serves as a controlling layer
between the application and the kernel driver. We now discuss its
components in detail.
int pack_len
defines the size of the block written to the driver. This is
defined within the kernel for internal use.
int reply_len
defines the size of the block to be accepted at reply. This is
defined from the application side.
int pack_id
This field helps to assign replies to requests. The application
can supply a unique id for each request. Suppose you have
written several commands (say 4) to one device. They may work in
parallel, one being the fastest. When getting replies via 4
reads, the replies do not have to have the order of the
requests. To identify the correct reply for a given request one
can use the pack_id field. Typically its value is incremented
after each request (and wraps eventually). The maximum amount of
outstanding requests is limited by the kernel to SG_MAX_QUEUE
(eg 4).
int result
the result code of a read or write call. This is (sometimes)
defined from the generic driver (kernel) side. It is safe to
set it to null before the write call. These codes are defined
in errno.h (0 meaning no error).
unsigned int twelve_byte:1
This field is necessary only when using non-standard vendor
specific commands (in the range 0xc0 - 0xff). When these
commands have a command length of 12 bytes instead of 10, this
field has to be set to one before the write call. Other command
lengths are not supported. This is defined from the application
side.
unsigned char sense_buffer[16]
This buffer is set after a command is completed (after a read()
call) and contains the SCSI sense code. Some command results
have to be read from here (e.g. for TESTUNITREADY). Usually it
contains just zero bytes. The value in this field is set by the
generic driver (kernel) side.
The following example function interfaces directly with the generic
kernel driver. It defines the header structure, sends the command via
write, gets the result via read and does some (limited) error
checking. The sense buffer data is available in the output buffer
(unless a NULL pointer has been given, in which case it's in the input
buffer). We will use it in the examples which follow.
Note: Set the value of DEVICE to your device descriptor.
#define DEVICE "/dev/sgc"
/* Example program to demonstrate the generic SCSI interface */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/sg.h>
#define SCSI_OFF sizeof(struct sg_header)
static unsigned char cmd[SCSI_OFF + 18]; /* SCSI command buffer */
int fd; /* SCSI device/file descriptor */
/* process a complete SCSI cmd. Use the generic SCSI interface. */
static int handle_SCSI_cmd(unsigned cmd_len, /* command length */
unsigned in_size, /* input data size */
unsigned char *i_buff, /* input buffer */
unsigned out_size, /* output data size */
unsigned char *o_buff /* output buffer */
)
{
int status = 0;
struct sg_header *sg_hd;
/* safety checks */
if (!cmd_len) return -1; /* need a cmd_len != 0 */
if (!i_buff) return -1; /* need an input buffer != NULL */
#ifdef SG_BIG_BUFF
if (SCSI_OFF + cmd_len + in_size > SG_BIG_BUFF) return -1;
if (SCSI_OFF + out_size > SG_BIG_BUFF) return -1;
#else
if (SCSI_OFF + cmd_len + in_size > 4096) return -1;
if (SCSI_OFF + out_size > 4096) return -1;
#endif
if (!o_buff) out_size = 0; /* no output buffer, no output size */
/* generic SCSI device header construction */
sg_hd = (struct sg_header *) i_buff;
sg_hd->reply_len = SCSI_OFF + out_size;
sg_hd->twelve_byte = cmd_len == 12;
sg_hd->result = 0;
#if 0
sg_hd->pack_len = SCSI_OFF + cmd_len + in_size; /* not necessary */
sg_hd->pack_id; /* not used */
sg_hd->other_flags; /* not used */
#endif
/* send command */
status = write( fd, i_buff, SCSI_OFF + cmd_len + in_size );
if ( status < 0 || status != SCSI_OFF + cmd_len + in_size ||
sg_hd->result ) {
/* some error happened */
fprintf( stderr, "write(generic) result = 0x%x cmd = 0x%x\n",
sg_hd->result, i_buff[SCSI_OFF] );
perror("");
return status;
}
if (!o_buff) o_buff = i_buff; /* buffer pointer check */
/* retrieve result */
status = read( fd, o_buff, SCSI_OFF + out_size);
if ( status < 0 || status != SCSI_OFF + out_size || sg_hd->result ) {
/* some error happened */
fprintf( stderr, "read(generic) status = 0x%x, result = 0x%x, "
"cmd = 0x%x\n",
status, sg_hd->result, o_buff[SCSI_OFF] );
fprintf( stderr, "read(generic) sense "
"%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
sg_hd->sense_buffer[0], sg_hd->sense_buffer[1],
sg_hd->sense_buffer[2], sg_hd->sense_buffer[3],
sg_hd->sense_buffer[4], sg_hd->sense_buffer[5],
sg_hd->sense_buffer[6], sg_hd->sense_buffer[7],
sg_hd->sense_buffer[8], sg_hd->sense_buffer[9],
sg_hd->sense_buffer[10], sg_hd->sense_buffer[11],
sg_hd->sense_buffer[12], sg_hd->sense_buffer[13],
sg_hd->sense_buffer[14], sg_hd->sense_buffer[15]);
if (status < 0)
perror("");
}
/* Look if we got what we expected to get */
if (status == SCSI_OFF + out_size) status = 0; /* got them all */
return status; /* 0 means no error */
}
While this may look somewhat complex at first appearance, most of the
code is for error checking and reporting (which is useful even after
the code is working).
Handle_SCSI_cmd has a generalized form for all SCSI commands types,
falling into each of these categories:
Data Mode | Example Command
===============================================
neither input nor output data | test unit ready
no input data, output data | inquiry, read
input data, no output data | mode select, write
input data, output data | mode sense
9. Inquiry Command Example
One of the most basic SCSI commands is the INQUIRY command, used to
identify the type and make of the device. Here is the definition from
the SCSI-2 specification (for details refer to the SCSI-2 standard).
Table 44: INQUIRY Command
+=====-========-========-========-========-========-========-========-========+
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|Byte | | | | | | | | |
|=====+=======================================================================|
| 0 | Operation Code (12h) |
|-----+-----------------------------------------------------------------------|
| 1 | Logical Unit Number | Reserved | EVPD |
|-----+-----------------------------------------------------------------------|
| 2 | Page Code |
|-----+-----------------------------------------------------------------------|
| 3 | Reserved |
|-----+-----------------------------------------------------------------------|
| 4 | Allocation Length |
|-----+-----------------------------------------------------------------------|
| 5 | Control |
+=============================================================================+
The output data are as follows:
Table 45: Standard INQUIRY Data Format
+=====-========-========-========-========-========-========-========-========+
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|Byte | | | | | | | | |
|=====+==========================+============================================|
| 0 | Peripheral Qualifier | Peripheral Device Type |
|-----+-----------------------------------------------------------------------|
| 1 | RMB | Device-Type Modifier |
|-----+-----------------------------------------------------------------------|
| 2 | ISO Version | ECMA Version | ANSI-Approved Version |
|-----+-----------------+-----------------------------------------------------|
| 3 | AENC | TrmIOP | Reserved | Response Data Format |
|-----+-----------------------------------------------------------------------|
| 4 | Additional Length (n-4) |
|-----+-----------------------------------------------------------------------|
| 5 | Reserved |
|-----+-----------------------------------------------------------------------|
| 6 | Reserved |
|-----+-----------------------------------------------------------------------|
| 7 | RelAdr | WBus32 | WBus16 | Sync | Linked |Reserved| CmdQue | SftRe |
|-----+-----------------------------------------------------------------------|
| 8 | (MSB) |
|- - -+--- Vendor Identification ---|
| 15 | (LSB) |
|-----+-----------------------------------------------------------------------|
| 16 | (MSB) |
|- - -+--- Product Identification ---|
| 31 | (LSB) |
|-----+-----------------------------------------------------------------------|
| 32 | (MSB) |
|- - -+--- Product Revision Level ---|
| 35 | (LSB) |
|-----+-----------------------------------------------------------------------|
| 36 | |
|- - -+--- Vendor Specific ---|
| 55 | |
|-----+-----------------------------------------------------------------------|
| 56 | |
|- - -+--- Reserved ---|
| 95 | |
|=====+=======================================================================|
| | Vendor-Specific Parameters |
|=====+=======================================================================|
| 96 | |
|- - -+--- Vendor Specific ---|
| n | |
+=============================================================================+
The next example uses the low-level function handle_SCSI_cmd to
perform the Inquiry SCSI command.
We first append the command block to the generic header, then call
handle_SCSI_cmd. Note that the output buffer size argument for the
handle_SCSI_cmd call excludes the generic header size. After command
completion the output buffer contains the requested data, unless an
error occurred.
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
#define INQUIRY_REPLY_LEN 96
#define INQUIRY_VENDOR 8 /* Offset in reply data to vendor name */
/* request vendor brand and model */
static unsigned char *Inquiry ( void )
{
unsigned char Inqbuffer[ SCSI_OFF + INQUIRY_REPLY_LEN ];
unsigned char cmdblk [ INQUIRY_CMDLEN ] =
{ INQUIRY_CMD, /* command */
0, /* lun/reserved */
0, /* page code */
0, /* reserved */
INQUIRY_REPLY_LEN, /* allocation length */
0 };/* reserved/flag/link */
memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
/*
* +------------------+
* | struct sg_header | <- cmd
* +------------------+
* | copy of cmdblk | <- cmd + SCSI_OFF
* +------------------+
*/
if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
sizeof(Inqbuffer) - SCSI_OFF, Inqbuffer )) {
fprintf( stderr, "Inquiry failed\n" );
exit(2);
}
return (Inqbuffer + SCSI_OFF);
}
The example above follows this structure. The Inquiry function copies
its command block behind the generic header (given by SCSI_OFF). Input
data is not present for this command. Handle_SCSI_cmd will define the
header structure. We can now implement the function main to complete
this working example program.
void main( void )
{
fd = open(DEVICE, O_RDWR);
if (fd < 0) {
fprintf( stderr, "Need read/write permissions for "DEVICE".\n" );
exit(1);
}
/* print some fields of the Inquiry result */
printf( "%s\n", Inquiry() + INQUIRY_VENDOR );
}
We first open the device, check for errors, and then call the higher
level subroutine. Then we print the results in human readable format
including the vendor, product, and revision.
Note: There is more information in the Inquiry result than this little
program gives. You may want to extend the program to give device type,
ANSI version etc. The device type is of special importance, since it
determines the mandatory and optional command sets for this device.
If you don't want to program it yourself, you may want to use the
scsiinfo program from Eric Youngdale, which requests nearly all
information about an SCSI device. Look at tsx-11.mit.edu in
pub/Linux/ALPHA/scsi.
10. The Sense Buffer
Commands with no output data can give status information via the sense
buffer (which is part of the header structure). Sense data is
available when the previous command has terminated with a CHECK
CONDITION status. In this case the kernel automatically retrieves the
sense data via a REQUEST SENSE command. Its structure is:
+=====-========-========-========-========-========-========-========-========+
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|Byte | | | | | | | | |
|=====+========+==============================================================|
| 0 | Valid | Error Code (70h or 71h) |
|-----+-----------------------------------------------------------------------|
| 1 | Segment Number |
|-----+-----------------------------------------------------------------------|
| 2 |Filemark| EOM | ILI |Reserved| Sense Key |
|-----+-----------------------------------------------------------------------|
| 3 | (MSB) |
|- - -+--- Information ---|
| 6 | (LSB) |
|-----+-----------------------------------------------------------------------|
| 7 | Additional Sense Length (n-7) |
|-----+-----------------------------------------------------------------------|
| 8 | (MSB) |
|- - -+--- Command-Specific Information ---|
| 11 | (LSB) |
|-----+-----------------------------------------------------------------------|
| 12 | Additional Sense Code |
|-----+-----------------------------------------------------------------------|
| 13 | Additional Sense Code Qualifier |
|-----+-----------------------------------------------------------------------|
| 14 | Field Replaceable Unit Code |
|-----+-----------------------------------------------------------------------|
| 15 | SKSV | |
|- - -+------------ Sense-Key Specific ---|
| 17 | |
|-----+-----------------------------------------------------------------------|
| 18 | |
|- - -+--- Additional Sense Bytes ---|
| n | |
+=============================================================================+
Note: The most useful fields are Sense Key (see section ``''),
Additional Sense Code and Additional Sense Code Qualifier (see section
``''). The latter two are used combined as a pair.
11. Example Using Sense Buffer
Here we will use the TEST UNIT READY command to check whether media is
loaded into our device. The header declarations and function
handle_SCSI_cmd from the inquiry example will be needed as well.
Table 73: TEST UNIT READY Command
+=====-========-========-========-========-========-========-========-========+
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|Byte | | | | | | | | |
|=====+=======================================================================|
| 0 | Operation Code (00h) |
|-----+-----------------------------------------------------------------------|
| 1 | Logical Unit Number | Reserved |
|-----+-----------------------------------------------------------------------|
| 2 | Reserved |
|-----+-----------------------------------------------------------------------|
| 3 | Reserved |
|-----+-----------------------------------------------------------------------|
| 4 | Reserved |
|-----+-----------------------------------------------------------------------|
| 5 | Control |
+=============================================================================+
Here is the function which implements it:
#define TESTUNITREADY_CMD 0
#define TESTUNITREADY_CMDLEN 6
#define ADD_SENSECODE 12
#define ADD_SC_QUALIFIER 13
#define NO_MEDIA_SC 0x3a
#define NO_MEDIA_SCQ 0x00
int TestForMedium ( void )
{
/* request READY status */
static unsigned char cmdblk [TESTUNITREADY_CMDLEN] = {
TESTUNITREADY_CMD, /* command */
0, /* lun/reserved */
0, /* reserved */
0, /* reserved */
0, /* reserved */
0};/* control */
memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
/*
* +------------------+
* | struct sg_header | <- cmd
* +------------------+
* | copy of cmdblk | <- cmd + SCSI_OFF
* +------------------+
*/
if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
0, NULL)) {
fprintf (stderr, "Test unit ready failed\n");
exit(2);
}
return
*(((struct sg_header*)cmd)->sense_buffer +ADD_SENSECODE) !=
NO_MEDIA_SC ||
*(((struct sg_header*)cmd)->sense_buffer +ADD_SC_QUALIFIER) !=
NO_MEDIA_SCQ;
}
Combined with this main function we can do the check.
void main( void )
{
fd = open(DEVICE, O_RDWR);
if (fd < 0) {
fprintf( stderr, "Need read/write permissions for "DEVICE".\n" );
exit(1);
}
/* look if medium is loaded */
if (!TestForMedium()) {
printf("device is unloaded\n");
} else {
printf("device is loaded\n");
}
}
The file generic_demo.c from the appendix contains both examples.
12. Ioctl Functions
There are two ioctl functions available:
· ioctl(fd, SG_SET_TIMEOUT, &Timeout); sets the timeout value to
Timeout * 10 milliseconds. Timeout has to be declared as int.
· ioctl(fd, SG_GET_TIMEOUT, &Timeout); gets the current timeout
value. Timeout has to be declared as int.
13. Driver Defaults
13.1. Transfer Lengths
Currently (at least up to kernel version 1.1.68) input and output
sizes have to be less than or equal than 4096 bytes unless the kernel
has been compiled with SG_BIG_BUFF defined, if which case it is
limited to SG_BIG_BUFF (e.g. 32768) bytes. These sizes include the
generic header as well as the command block on input. SG_BIG_BUFF can
be safely increased upto (131072 - 512). To take advantage of this, a
new kernel has to be compiled and booted, of course.
13.2. Timeout And Retry Values
The default timeout value is set to one minute (Timeout = 6000). It
can be changed through an ioctl call (see section ``''). The default
number of retries is one.
14. Obtaining The Scsi Specifications
There are standards entitled SCSI-1 and SCSI-2 (and possibly soon
SCSI-3). The standards are mostly upward compatible.
The SCSI-1 standard is (in the author's opinion) mostly obsolete, and
SCSI-2 is the most widely used. SCSI-3 is very new and very expensive.
These standardized command sets specify mandatory and optional
commands for SCSI manufacturers and should be preferred over the
vendor specific command extensions which are not standardized and for
which programming information is seldom available. Of course sometimes
there is no alternative to these extensions.
Electronic copies of the latest drafts are available via anonymous ftp
from:
· ftp.cs.tulane.edu:pub/scsi
· ftp.symbios.com:/pub/standards
· ftp.cs.uni-sb.de:/pub/misc/doc/scsi
(I got my SCSI specification from the Yggdrasil Linux CD-ROM in the
directory /usr/doc/scsi-2 and /usr/doc/scsi-1).
The SCSI FAQ also lists the following sources of printed information:
The SCSI specification: Available from:
Global Engineering Documents
15 Inverness Way East
Englewood Co 80112-5704
(800) 854-7179
SCSI-1: X3.131-1986
SCSI-2: X3.131-199x
SCSI-3 X3T9.2/91-010R4 Working Draft
(Global Engineering Documentation in Irvine, CA (714)261-1455??)
SCSI-1: Doc \# X3.131-1986 from ANSI, 1430 Broadway, NY, NY 10018
IN-DEPTH EXPLORATION OF SCSI can be obtained from
Solution Technology, Attn: SCSI Publications, POB 104, Boulder Creek,
CA 95006, (408)338-4285, FAX (408)338-4374
THE SCSI ENCYLOPEDIA and the SCSI BENCH REFERENCE can be obtained from
ENDL Publishing, 14426 Black Walnut Ct., Saratoga, CA 95090,
(408)867-6642, FAX (408)867-2115
SCSI: UNDERSTANDING THE SMALL COMPUTER SYSTEM INTERFACE was published
by Prentice-Hall, ISBN 0-13-796855-8
15. Related Information Sources
15.1. HOWTOs and FAQs
The Linux SCSI-HOWTO by Drew Eckhardt covers all supported SCSI
controllers as well as device specific questions. A lot of
troubleshooting hints are given. It is available from sunsite.unc.edu
in /pub/Linux/docs/LDP and its mirror sites.
General questions about SCSI are answered in the SCSI-FAQ from the
newsgroup Comp.Periphs.Scsi (available on tsx-11 in
pub/linux/ALPHA/scsi and mirror sites).
15.2. Mailing list
There is a mailing list for bug reports and questions regarding SCSI
development under Linux. To join, send email to
majordomo@vger.rutgers.edu with the line subscribe linux-scsi in the
body of the message. Messages should be posted to linux-
scsi@vger.rutgers.edu. Help text can be requested by sending the
message line "help" to majordomo@vger.rutgers.edu.
15.3. Example code
sunsite.unc.edu: apps/graphics/hpscanpbm-0.3a.tar.gz
This package handles a HP scanjet scanner through the generic
interface.
tsx-11.mit.edu: BETA/cdrom/private/mkisofs/cdwrite-1.3.tar.gz
The cdwrite package uses the generic interface to write a cd
image to a cd writer.
sunsite.unc.edu: apps/sound/cds/cdda2wav*.src.tar.gz
A shameless plug for my own application, which copies audio cd
tracks into wav files.
16. Other useful stuff
Things that may come in handy. I don't have no idea if there are newer
or better versions around. Feedback is welcome.
16.1. Device driver writer helpers
These documents can be found at the sunsite.unc.edu ftp server and its
mirrors.
/pub/Linux/docs/kernel/kernel-hackers-guide
The LDP kernel hackers guide. May be a bit outdated, but covers
the most fundamental things.
/pub/Linux/docs/kernel/drivers.doc.z
This document covers writing character drivers.
/pub/Linux/docs/kernel/tutorial.doc.z
Tutorial on writing a character device driver with code.
/pub/Linux/docs/kernel/scsi.paper.tar.gz
A Latex document describing howto write a SCSI driver.
/pub/Linux/docs/hardware/DEVICES
A list of device majors and minors used by Linux.
16.2. Utilities
tsx-11.mit.edu: ALPHA/scsi/scsiinfo*.tar.gz
Program to query a scsi device for operating parameters, defect
lists, etc. An X-based interface is available which requires
you have Tk/Tcl/wish installed. With the X-based interface you
can easily alter the settings on the drive.
tsx-11.mit.edu: ALPHA/kdebug
A gdb extension for kernel debugging.
17. Other SCSI Access Interfaces
In Linux there is also another SCSI access method via
SCSI_IOCTL_SEND_COMMAND ioctl calls, which is deprecated. Special
tools like 'scsiinfo' utilize it.
There are some other similar interfaces in use in the un*x world, but
not available for Linux:
1. CAM (Common Access Method) developed by Future Domain and other
SCSI vendors. Linux has little support for a SCSI CAM system yet
(mainly for booting from hard disk). CAM even supports target
mode, so one could disguise ones computer as a peripheral hardware
device (e.g. for a small SCSI net).
2. ASPI (Advanced SCSI Programming Interface) developed by Adaptec.
This is the de facto standard for MS-DOS machines.
There are other application interfaces from SCO(TM), NeXT(TM), Silicon
Graphics(TM) and SUN(TM) as well.
18. Final Comments
The generic SCSI interface bridges the gap between user applications
and specific devices. But rather than bloating a lot of programs with
similar sets of low-level functions, it would be more desirable to
have a shared library with a generalized set of low-level functions
for a particular purpose. The main goal should be to have independent
layers of interfaces. A good design would separate an application
into low-level and hardware independent routines. The low-level
routines could be put into a shared library and made available for all
applications. Here, standardized interfaces should be followed as much
as possible before making new ones.
By now you should know more than I do about the Linux generic SCSI
interface. So you can start developing powerful applications for the
benefit of the global Linux community now...
19. Acknowledgments
Special thanks go to Jeff Tranter for proofreading and enhancing the
text considerably as well as to Carlos Puchol for useful comments.
Drew Eckhardt's and Eric Youngdale's help on my first (dumb) questions
about the use of this interface has been appreciated.
T. Appendix
U. Error handling
The functions open, ioctl, write and read can report errors. In this
case their return value is -1 and the global variable errno is set to
the error number. The errno values are defined in
/usr/include/errno.h. Possible values are:
Function | Error | Description
=========|==============|=============================================
open | ENXIO | not a valid device
| EACCES | access mode is not read/write (O_RDWR)
| EBUSY | device was requested for nonblocking access,
| | but is busy now.
| ERESTARTSYS | this indicates an internal error. Try to
| | make it reproducible and inform the SCSI
| | channel (for details on bug reporting
| | see Drew Eckhardts SCSI-HOWTO).
ioctl | ENXIO | not a valid device
read | EAGAIN | the device would block. Try again later.
| ERESTARTSYS | this indicates an internal error. Try to
| | make it reproducible and inform the SCSI
| | channel (for details on bug reporting
| | see Drew Eckhardts SCSI-HOWTO).
write | EIO | the length is too small (smaller than the
| | generic header struct). Caution: Currently
| | there is no overlength checking.
| EAGAIN | the device would block. Try again later.
| ENOMEM | memory required for this request could not be
| | allocated. Try later again unless you
| | exceeded the maximum transfer size (see above)
select | | none
close | | none
For read/write positive return values indicate as usual the amount of
bytes that have been successfully transferred. This should equal the
amount you requested.
U.1. Error status decoding
Furthermore a detailed reporting is done via the kernels hd_status and
the devices sense_buffer (see section ``'') both from the generic
header structure.
The meaning of hd_status can be found in drivers/scsi/scsi.h: This
unsigned int is composed out of different parts:
lsb | ... | ... | msb
=======|===========|===========|============
status | sense key | host code | driver byte
These macros from drivers/scsi/scsi.h are available, but unfortunately
cannot be easily used due to weird header file interdependencies. This
has to be cleaned.
Macro | Description
=======================|=================================================
status_byte(hd_status) | The SCSI device status. See section Status codes
msg_byte(hd_status) | From the device. See section SCSI sense keys
host_byte(hd_status) | From the kernel. See section Hostcodes
driver_byte(hd_status) | From the kernel. See section midlevel codes
U.2. Status codes
The following status codes from the SCSI device (defined in
scsi/scsi.h) are available.
Value | Symbol
======|=====================
0x00 | GOOD
0x01 | CHECK_CONDITION
0x02 | CONDITION_GOOD
0x04 | BUSY
0x08 | INTERMEDIATE_GOOD
0x0a | INTERMEDIATE_C_GOOD
0x0c | RESERVATION_CONFLICT
Note that these symbol values have been shifted right once. When the
status is CHECK_CONDITION, the sense data in the sense buffer is valid
(check especially the additional sense code and additional sense code
qualifier).
These values carry the meaning from the SCSI-2 specification:
Table 27: Status Byte Code
+=================================-==============================+
| Bits of Status Byte | Status |
| 7 6 5 4 3 2 1 0 | |
|---------------------------------+------------------------------|
| R R 0 0 0 0 0 R | GOOD |
| R R 0 0 0 0 1 R | CHECK CONDITION |
| R R 0 0 0 1 0 R | CONDITION MET |
| R R 0 0 1 0 0 R | BUSY |
| R R 0 1 0 0 0 R | INTERMEDIATE |
| R R 0 1 0 1 0 R | INTERMEDIATE-CONDITION MET |
| R R 0 1 1 0 0 R | RESERVATION CONFLICT |
| R R 1 0 0 0 1 R | COMMAND TERMINATED |
| R R 1 0 1 0 0 R | QUEUE FULL |
| | |
| All Other Codes | Reserved |
|----------------------------------------------------------------|
| Key: R = Reserved bit |
+================================================================+
A definition of the status byte codes is given below.
GOOD. This status indicates that the target has successfully completed the
command.
CHECK CONDITION. This status indicates that a contingent allegiance condition
has occurred (see 6.6).
CONDITION MET. This status or INTERMEDIATE-CONDITION MET is returned whenever
the requested operation is satisfied (see the SEARCH DATA and PRE-FETCH
commands).
BUSY. This status indicates that the target is busy. This status shall be
returned whenever a target is unable to accept a command from an otherwise
acceptable initiator (i.e., no reservation conflicts). The recommended
initiator recovery action is to issue the command again at a later time.
INTERMEDIATE. This status or INTERMEDIATE-CONDITION MET shall be returned for
every successfully completed command in a series of linked commands (except
the last command), unless the command is terminated with CHECK CONDITION,
RESERVATION CONFLICT, or COMMAND TERMINATED status. If INTERMEDIATE or
INTERMEDIATE-CONDITION MET status is not returned, the series of linked
commands is terminated and the I/O process is ended.
INTERMEDIATE-CONDITION MET. This status is the combination of the CONDITION
MET and INTERMEDIATE statuses.
RESERVATION CONFLICT. This status shall be returned whenever an initiator
attempts to access a logical unit or an extent within a logical unit that is
reserved with a conflicting reservation type for another SCSI device (see the
RESERVE and RESERVE UNIT commands). The recommended initiator recovery action
is to issue the command again at a later time.
COMMAND TERMINATED. This status shall be returned whenever the target
terminates the current I/O process after receiving a TERMINATE I/O PROCESS
message (see 5.6.22). This status also indicates that a contingent allegiance
condition has occurred (see 6.6).
QUEUE FULL. This status shall be implemented if tagged queuing is
implemented. This status is returned when a SIMPLE QUEUE TAG, ORDERED QUEUE
TAG, or HEAD OF QUEUE TAG message is received and the command queue is full.
The I/O process is not placed in the command queue.
U.3. SCSI Sense Keys
These kernel symbols (from scsi/scsi.h) are predefined:
Value | Symbol
======|================
0x00 | NO_SENSE
0x01 | RECOVERED_ERROR
0x02 | NOT_READY
0x03 | MEDIUM_ERROR
0x04 | HARDWARE_ERROR
0x05 | ILLEGAL_REQUEST
0x06 | UNIT_ATTENTION
0x07 | DATA_PROTECT
0x08 | BLANK_CHECK
0x0a | COPY_ABORTED
0x0b | ABORTED_COMMAND
0x0d | VOLUME_OVERFLOW
0x0e | MISCOMPARE
A verbatim list from the SCSI-2 doc follows (from section 7.2.14.3):
Table 69: Sense Key (0h-7h) Descriptions
+========-====================================================================+
| Sense | Description |
| Key | |
|--------+--------------------------------------------------------------------|
| 0h | NO SENSE. Indicates that there is no specific sense key |
| | information to be reported for the designated logical unit. This |
| | would be the case for a successful command or a command that |
| | received CHECK CONDITION or COMMAND TERMINATED status because one |
| | of the filemark, EOM, or ILI bits is set to one. |
|--------+--------------------------------------------------------------------|
| 1h | RECOVERED ERROR. Indicates that the last command completed |
| | successfully with some recovery action performed by the target. |
| | Details may be determinable by examining the additional sense |
| | bytes and the information field. When multiple recovered errors |
| | occur during one command, the choice of which error to report |
| | (first, last, most severe, etc.) is device specific. |
|--------+--------------------------------------------------------------------|
| 2h | NOT READY. Indicates that the logical unit addressed cannot be |
| | accessed. Operator intervention may be required to correct this |
| | condition. |
|--------+--------------------------------------------------------------------|
| 3h | MEDIUM ERROR. Indicates that the command terminated with a non- |
| | recovered error condition that was probably caused by a flaw in |
| | the medium or an error in the recorded data. This sense key may |
| | also be returned if the target is unable to distinguish between a |
| | flaw in the medium and a specific hardware failure (sense key 4h).|
|--------+------------------