The
SCSIDRV programming interface
Building
SCSI (and IDE) support into your code by
Roger Burrows
When Atari
designed its first 32-bit systems, it included
a proprietary interface, ACSI, for connecting
peripherals such as hard disk drives. ACSI
is a stripped-down (and thus less expensive)
version of the SCSI interface, but the much
greater volume of SCSI peripherals together
with the greater competition in this area
made it cheaper to use an ACSI-to-SCSI convertor
(usually known as a host adaptor) to connect
off-the-shelf SCSI peripherals to Ataris.
Later Atari systems, such as the TT030 and
Falcon, included a SCSI interface as standard,
and so SCSI devices have dominated the Atari
peripheral market.
But this
is not without its downside. SCSI protocol
has generally been complex, there was no
built-in programming support for SCSI in
TOS (unlike, for example, the MacÆs SCSI
Manager), the instructions required for
low-level access to the SCSI hardware differ
between systems, and the possibility of
running your code under MagiC Mac or MagiC
PC adds yet another complexity. The use
of ATAPI (IDE) interfaces on later systems
(such as the Falcon and the Milan) has added
yet more work for anyone wishing to support
peripheral devices in their software.
The SCSIDRV
programming interface, however, offers a
straightforward way to build SCSI and ATAPI
capability into your code. In this article,
I'll examine the SCSIDRV standard and discuss
how to use it.
The problem
with SCSI/ATAPI The SCSI and ATAPI standards
define a communications protocol and a command
set and, fortunately for programmers wishing
to support the most devices with the least
work, the command sets are by-and-large
exactly the same. However, the missing link
for the Atari programmer is the interface
to the SCSI/ATAPI buses. If you tackle the
problem through low-level hardware programming,
you must transmit the appropriate commands
through one of multiple different buses.
And of course, all programs that need to
send commands directly to a SCSI or ATAPI
peripheral must have the same capability,
requiring duplication of efforts. A further
issue is that certain actions on the bus
(such as a reset) can affect the state of
other devices, which can lead to interference
between programs, each of which behaves
as though it "owns" the bus.
The SCSIDRV
programming interface, proposed by Steffen
Engel in 1994-5, defines a set of high-level
functions for communicating with devices
attached to a SCSI (or ATAPI) bus. It protects
you from the pitfalls of hardware-level
programming and provides a standard way
of transmitting commands across a bus to
a peripheral.
The SCSIDRV
programming interface The original SCSIDRV
driver was written by Steffen Engel for
the ST and TT030. This was extended and
eventually incorporated into the CBHD disk
driver (originally by Claus Brod, now maintained
by Steffen Engel). It supports ACSI, SCSI,
and ATAPI buses on the ST, TT030, and Falcon,
as well as devices under MagiC Mac and MagiC
PC. Another, independently-developed driver
is built into HD-Driver (by Dr Uwe Seimet),
starting in v7.00. This provides support
for the ACSI, SCSI, and ATAPI buses on the
ST, TT030, Falcon, and Milan (the Hades
is not officially supported, but normally
works just fine).
SCSIDRV offers
many advantages to the SCSI/ATAPI programmer.
Because it is available on all Atari-compatible
systems, it provides hardware and platform
independence. Furthermore, SCSIDRV support
is available for TOS, MagiC and MiNT operating
systems. Best of all, SCSIDRV provides a
high-level function set that's easy to use
and can dramatically shorten your development
time. SCSIDRV hides the inner workings of
the bus protocol from the programmer. The
SCSIDRV-compatible driver handles the details
of arbitration, selection, and message passing,
returning status codes and sense data when
appropriate. You still need a basic knowledge
of SCSI/ATAPI structures and protocol to
use SCSIDRV effectively, but it can shorten
your learning curve by making experimentation
easy.
Using SCSIDRV
does have its shortcomings. For example,
if the SCSIDRV driver does not support a
particular device type, you will not be
able to use it.
How SCSIDRV
works The
SCSIDRV driver is either built into your
hard disk driver (and therefore loaded during
the boot process), or it is loaded as a
separate program via the AUTO folder. At
startup, the SCSIDRV driver installs a cookie
("SCSI") which points to a structure
containing the version of the SCSIDRV specification
that is supported, together with the entry
addresses of the functions supported (see
diagram 1). It then polls the system buses,
looking for attached devices, and saves
the information to internal tables. A device
must be turned on before start-up in order
for SCSIDRV to recognise it. To use a SCSIDRV
function, you put the appropriate parameters
on the stack and then issue a sub-routine
call to the appropriate address, obtained
from the SCSIDRV structure. Note that the
entry points must be called from supervisor
mode; since the driver is frequently used
by other drivers (such as our own product,
ExtenDOS Gold) which are already in supervisor
mode, this is usually a benefit.
Offset |
Contents |
+0 |
version |
+2 |
pointer
to "In" routine |
+6 |
pointer
to "Out" routine |
+10 |
pointer
to "InquireSCSI" routine |
+14 |
pointer
to "InquireBus" routine |
+18 |
pointer
to "CheckDev" routine |
+22 |
pointer
to "RescanBus" routine |
+26 |
pointer
to "Open" routine |
+30 |
pointer
to "Close" routine |
+34 |
pointer
to "Error" routine |
|
Diagram
1: structure pointed to by "SCSI"
cookie. |
Using
SCSIDRV requires knowledge of the SCSI/ATAPI
command set and capabilities for the device
you wish to support. Keep in mind that SCSIDRV
provides interface functions, not high-level
SCSI functions. The SCSIDRV driver merely
passes data through to the SCSI device without
modifying or inspecting it.
The basic
SCSIDRV function set is small, but powerful.
Some of the functions return information
about the system and devices, while others
actually communicate with SCSI/ATAPI devices.
The base SCSIDRV specification defines the
functions in table 1. The essential functions
are In and Out; all other functions
are essentially support functions. The In and Out functions send a
SCSI CDB to the device, handle data transfer,
and return messages and status codes. If
the request results in a Check Condition
status, SCSIDRV requests sense data from
the device, making it available to the calling
program.
Function
name |
Description |
In |
Perform
SCSI input command |
Out |
Perform
SCSI output command |
InquireSCSI |
Determine
available buses in system |
InquireBus |
Determine
available devices on a bus |
CheckDev |
Get information
for a specific bus/device |
RescanBus |
Requests
driver to rescan bus for available
devices |
Open |
Get a
device handle |
Close |
Release
device handle |
Error |
Set/query
error status for a device |
|
Table
1: SCSIDRV basic functions. |
For
completeness, you should know that the SCSIDRV
specification also defines a set of functions
that are only used for "target handling"
(see table 2). These allow the computer
system to be used as a peripheral, for example
by another computer on the same SCSI bus.
We wonÆt cover these functions in this article.
Function
name |
Description |
Install |
Install
target handler routine |
Deinstall |
De-install
target handler routine |
GetCmd |
Get SCSI
command from initiator |
SendData |
Send
data to initiator |
GetData |
Get data
from initiator |
SendStatus |
Send
SCSI status to initiator |
SendMsg |
Send
SCSI message to initiator |
GetMsg |
Get SCSI
message from initiator |
|
Table
2: SCSIDRV target-handling functions. |
Listing
1 (SCSIDEMO.C) shows the use of the basic
SCSIDRV functions to determine which devices
are currently available via SCSIDRV. In
order to find out details about each device,
we issue the Inquiry command. This SCSI
command is mandatory for all devices and
is often used by driver programs to dynamically
identify available devices. WeÆll examine
each routine in turn and see how the SCSIDRV
functions are used.
The routine init_scsi()
locates the "SCSI" cookie and
save its value, which is a pointer to the
SCSIDRV structure, in the variable scsicall.
The routine scan_busses()
then manages the rest of the job. It uses
the SCSIDRV function InquireSCSI
to determine available busses. For those
who are familiar with the GEMDOS Fsfirst
and Fsnext functions, InquireSCSI
behaves in a similar manner. You call it
with the argument cInqFirst
to obtain information about the first available
bus, and then call it repeatedly with the
argument cInqNext
to retrieve information on the remaining
buses (if any).
The list_devices()
routine displays information about all the
devices on a specific bus. It uses the SCSIDRV
function InquireBus
to determine available devices, in the same
way as init_scsi()
uses InquireSCSI,
using cInqFirst and cInqNext. For each device
that exists, it gets a device handle via
a SCSIDRV Open,
and then calls scsi_inquiry()
to print information
about the device.
The scsi_inquiry()
function demonstrates the In
function to get data from the device. Like
the Out
function (not shown here), it uses a SCSIDRV
Command Block to contain all the information
needed to perform a SCSI I/O. This includes
the device handle, a pointer to the SCSI
CDB (command) to be passed to the device,
the length of the SCSI CDB, a pointer to
the data buffer that will receive the data,
and the length of data to be read. The SCSIDRV
Command Block also contains a pointer to
an 18-byte buffer that SCSIDRV will use
for Request Sense data if a Check Condition
occurs, as well as a flag field and a time-out
value (in system ticks).
The time-out
value specifies the maximum length of time
that the SCSIDRV driver should wait for
the command to complete; if the command
takes longer than this, the driver will
terminate the command and return control
to the caller of In, with an error code indicating
a time-out. The purpose of this is to avoid
the system hanging due to a problem in a
peripheral device. The time-out value should
be chosen with care: you must avoid terminating
commands that are progressing normally,
but not wait so long that, if a problem
does occur, the user thinks that the system
has crashed.
Conclusion
For
those wishing to experiment further, the
source for the program, including header
files and .PRJ files for Lattice C (SCSIDEMO.PRJ)
and Pure C (PC_SCSID.PRJ) is here. If you want to
read more about SCSIDRV, the documentation
is available here. This includes the
specification (in both English and German),
C-language headers with useful structure
and constant definitions, and sample SCSIDRV
programs. The current version of CBHD (version
5.02) is available here; this also includes
documentation, but in German only. |