USCSI Ioctl: Application-level
Access to SCSI Device Capabilities
An unsupported, undocumented
feature of Sun SCSI device drivers allows users to issue SCSI
requests directly to SCSI devices. This capability is intended
for driver prototyping and diagnostics; it allows the developer
to try out sequences of SCSI commands without recompiling and
reloading a device driver. Users of this feature must be
forewarned that potential side effects include system panics and
putting devices into unusable states.
Documentation for most of the
USCSI features is available at the end of this White Paper in
manual page format. The rest of the White Paper provides a simple
example of using USCSI as well as several caveats about its
dangers.
The USCSICMD request, issued via
the ioctl(2) call, passes a user-constructed SCSI
command block directly to a SCSI device. This feature enables the
user to utilize the full SCSI command set appropriate for a
particular device.
The following code fragment
demonstrates the use of USCSI. With a simple example of rewinding
a tape, we'll compare how this is accomplished with the standard
mtio(7) and then with the USCSI ioctl commands. mtio(7) describes
the general magnetic tape interface supported by drivers for tape
devices (e.g. st(7)) and used by commands such as mt(1).Here's a
short routine using the mtio
ioctl:
- #include
<sys/types.h>
- #include
<sys/mtio.h>
- int mt_rewind(fd)
- int fd;
- {
- struct mtop
mtop;
- mtop.mt_op =
(short) MTREW;
- mtop.mt_op =
(short) MTREW;
- mtop.mt_count =
1;
-
- if (ioctl(fd,
MTIOCTOP, &mtop) == -1) {
- printf("ERROR:
mt_rewind\n");
- return(-1);
- }
- return(0);
}
The two fields of the mtop
structure are set to the desired operation (MTREW) and a count of how
many to do (just 1). ioctl is called with the le descriptor for the
device, the MTIOCTOP (do a mag tape op) again, and a pointer to
the mtop structure. Within a SCSI driver, the command is
translated into a SCSI command which is issued to the device. The
USCSI ioctl lets the user pass his or her own SCSI rewind command to
the device. First, the command must be built. The uscsi_cmd structure (scsi/impl/uscsi.h<
/font>) and command description block, scsi_cdb union
(scsi/general/comands.h), are zeroed using memset(3c). The scc_cmd field in the command block is set to SCMD_REWIND.
The uscsi_cmd struct is pointed
at the command block and its size is entered in the uscsi_cdblen field.
uscsi_flags is set to USCSI_SILENT to turn off error messages (for demonstration
purposes).
- #include
<sys/scsi/scsi.h>
- int uscsi_rewind(fd)
- int fd;
- {
- struct uscsi_cmd
ucmd;
- union scsi_cdb cdb;
- int status;
- (void) memset((char
*)&ucmd, 0, sizeof(ucmd));
- (void) memset((char
*)&cdb, 0, sizeof(union scsi_cdb));
- cdb.scc_cmd =
SCMD_REWIND;
- ucmd.uscsi_cdb =
(caddr_t) &cdb;
- ucmd.uscsi_cdblen =
CDB_GROUP0;
- ucmd.uscsi_flags |=
USCSI_SILENT;
-
- status = ioctl(fd,
USCSICMD, &ucmd);
-
- if (status == 0
&& ucmd.uscsi_status == 0)
- return (0);
- else {
- printf(ERROR:
uscsi_rewind\n);
- return(-1);
- }
- }
Obviously, the USCSI version
requires more effort, but is much more flexible. Any appropriate
SCSI command can be built and issued (see the SCSI include files
in the /usr/include/sys/scsi). For our tape example, quite a
variety of commands are available. For test unit ready we would
simply set cdb.scc_cmd = SCMD_TEST_UNIT_READY in the above code.
Likewise for erase (SCMD_ERASE). A SCSI inquiry command (
scsi/general/inquiry.h) requires a buffer to receive information.
So, we add:
- FORMG0COUNT(&cdb,
sizeof (buf));
- ucmd.uscsi_bufaddr =
(caddr_t) buf;
- ucmd.uscsi_buflen = sizeof
(buf);
which specifies the buffers size
and address. FORMG0COUNT is a macro (scsi/general/commands.h)
which sets the g0 count in the cdb structure. Mode sense (SCMD_MODE_SENSE) is implemented this way, too. Reads and writes (SCMD_READ/SCMD_WRITE) to the device are similar, but they also use the
tag (t_code) field of the cdb structure. Quite a few
other tape specific commands can be issued including mode select,
mode sense, request sense, and write file mark
The flexibility of the USCSI ioctl allows
the device driver writer to fully exercise a SCSI device. The
feature is useful for rapid prototyping of algorithms for SCSI
devices and for diagnostic programs. The associated risks (system
panics, unusable devices, future obsolescence of the USCSI
feature) make the feature inappropriate for other uses. In
particular, users should be aware of the following:
Issuing a command to a device
simultaneously in use by a system driver may put that device in a
state such that the system driver may no longer work with that
device correctly (e.g., mode-selecting a disk to 1k sector size).
Third-party devices will not
necessarily respond correctly to any SCSI-2 command.
An arbitrary SCSI-2 command issued
to a device may cause that device to misbehave on the SCSI bus in
some fashion that may interfere with the correct operation of
other devices on the same bus in use by the system.
USCSI Man page.
Name
uscsi user SCSI command
interface
SYNOPSIS
- #include
<sys/scsi/impl/uscsi.h>
- ioctl(int fildes,int
request, struct uscsi_cmd *cmd);
DESCRIPTION
Drivers supporting this ioctl
provide a general interface allowing user-level applications
to cause individual SCSI commands to be directed to a
particular SCSI device under control of that driver. The SCSI
command may include a data transfer to or from that device,
if appropriate for that command. Upon completion of the
command, the user application can determine how many bytes
were transferred and the status returned by the device, and,
optionally, the sense data returned by the device if the
status returned is Check Condition.
The uscsi_cmd structure is
defined in <sys/scsi/impl/uscsi.h>
; and includes the
following members:
- int uscsi_flags; /*
read, write, etc. see below */
- short uscsi_status; /*
resulting status */
- short uscsi_timeout; /*
Command Timeout */
- caddr_tuscsi_cdb ; /*
cdb to send to target */
- caddr_tuscsi_bufaddr;
/* i/o source/destination */
- u_int uscsi_buflen ; /*
size of i/o to take place */
- u_int uscsi_resid ; /*
resid from i/o operation */
- u_char uscsi_cdblen; /*
# of valid cdb bytes */
- u_char uscsi_rqlen; /*
size of uscsi_rqbuf */
- u_char uscsi_rqstatus ;
/* status of request sense cmd */
- u_char uscsi_rqresid;
/* resid of request sense cmd */
- caddr_tuscsi_rqbuf; /*
request sense buffer */
- void *uscsi_reserved_5;
/* Reserved for future use */
The fields of the uscsi_cmd structure have the following meaning:
- uscsi_flags This field defines the I/O
direction, if any, and other SCSI bus operations. The
possible values are described below.
- uscsi_status The SCSI status byte returned by
the device is returned by the driver to the user
application here.
- uscsi_timeout The driver
will fail the command if the command does not complete
with the specified timeout period, in seconds.
- uscsi_cdb This field must point to the SCSI
cdb to be transferred to the device in command phase.
- uscsi_bufaddr If a data
transfer is to occur, this field must point to the user
buffer to be transferred to the device, or to which the
data sent by the device is to be written.
- uscsi_buflen If a data transfer is to occur,
this field should specify the maximum length of transfe
possible.
- uscsi_resid If a data transfer occurs as part
of executing a uscsi command, the driver returns the
residue of the transfer.
- uscsi_cdblen This field
must contain the length of the SCSI cdb to be transferred to the device in
command phase.
- uscsi_rqlen If the user application wishes to
receive any Request Sense data returned by the device in
the event of an error, this field should be set to the
length of the applications Request Sense buffer.
- uscsi_rqstatus If the user
application is prepared to receive Request Sense data,
and the driver executes a Request Sense command in
response to the user command terminating with Check
Condition, the driver will return the status of the
Request Sense command itself here.
- uscsi_rqresid If the user
application is prepared to receive Request Sense data,
and the driver executes a Request Sense command in
response to the user command terminating with Check
Condition, the driver will return the residue of the
Request Sense data transfer here.
- uscsi_rqbuf If the user application wishes to
receive any Request Sense data returned by the device in
the event of an error, this should point to a buffer in
the applica tions address space to which this data will
be written.
- uscsi_reserved_5 Not available
The uscsi_flags field
defines the following:
- USCSI_WRITE /* send
data to device */
- USCSI_SILENT /* no
error messages */
- USCSI_DIAGNOSE /* fail
if any error occurs */
- USCSI_ISOLATE /*
isolate from normal commands */
- USCSI_READ /* get data
from device */
- USCSI_ASYNC /* set bus
to asynchronous mode */
- USCSI_SYNC /* return
bus to sync mode if possible */
- USCSI_RESET /* reset
target */
- USCSI_RESET_ALL /*
reset all targets */
- USCSI_RQENABLE /*
enable request sense extensions */
The uscsi_flags bits have the
following interpretation:
- USCSI_WRITE If a
data transfer is to occur, the transfer is to occur
as a write from the initiator to the target.
- USCSI_SILENT The
driver should not print any console error messages or
warnings regarding failures associated with this SCSI
command.
- USCSI_DIAGNOSE The
driver should not attempt any retries or other
recovery mechanisms, should this SCSI command
terminate abnormally in any way.
- USCSI_ISOLATE This SCSI command should not be
executed with other commands.
- USCSI_READ If a
data transfer is to occur, the transfer is to occur
as a read from the target to the initiator.
- USCSI_ASYNC Set the SCSI bus to
asynchronous mode before running this command.
- USCSI_SYNC Set the
SCSI bus to synchronous mode before runing this
command.
- USCSI_RESET Send a
SCSI Reset Message to this target.
- USCSI_RESET_ALL Cause a SCSI Bus Reset on the
bus associated with this target.
- USCSI_RQENABLE Enable
Request Sense extensions. If the user application is
prepared to receive sense data, this bit must be set,
and the fields uscsi_rqbuf and uscsi_rqbuflen
must be non-zero,
and the uscsi_rqbuf must point to memory
writable by the application.
ERRORS
- EINVAL some parameter has an incorrect, or
unsupported, value.
- EIO an error occurred during the execution of
the command.
- EFAULT the uscsi_cmd itself, the uscsi_cdb, the uscsi_buf, or the uscsi_rqbuf point to an invalid address.
IOCTLS
The ioctl supported by drivers
providing the uscsi interface is:
USCSICMD The argument is a
pointer to a uscsi_cmd structure. The SCSI device addressed
by that driver is selected, and given the SCSI command
addressed by uscsi_cdb. If this command requires a data
phase, the uscsi_buflen and uscsi_bufaddr fields must be set appropriately; if
data phase occurs, the uscsi_resid is returned as
the number of bytes not transferred. The status of the
command, as returned by the device, is returned in the uscsi_status field. If the command terminates with Check
Condition status, and Request Sense is enabled, the sense
data itself is returned in usc-scsi_rqbuf. The uscsi_rqresid provides the residue of the Request
Sense data transfer.
SEE ALSO
sd(7),st(7),ioctl(2),ANSI Small Computer System Interface-2
(SCSI-2)
|