                      UDF SDK Multi-Surface Device Example
                      -------------------------------------

1.  INTRODUCTION

     The UDF SDK provides users with an example implementation of an
MSDevice (multi-surface device) object, the hpACDev class.  The hpACDev
class is an abstraction of a magneto-optical autochanger, and allows users 
to send I/O requests to surfaces in the autochanger without having to worry 
about the details of swapping cartridges into and out of drives.  

     The purpose of this documentation is to provide users of the UDF SDK
with an understanding of what functionality they must provide in their 
implementations of the MSDevice for use with both the MSLIB and SSLIB code
libraries, as well as the details of an example implementation.  The
documentation is organized into the following sections:

     Platform and Dependencies
     hpACDev Functional Interface
     Architecture and Algorithms
     hpACDev-specfic Public Method Interfaces

2.  PLATFORM AND DEPENDENCIES

     In its current implementation, the hpACDev is designed to be used
on Hewlett-Packard series 700 workstations running HP-UX version 9.0x.  It
requires the SCSI passthru driver (sctl) as well as the standard SCSI disk 
driver (sdisk).  Both of these drivers are by default configured into a
9.0x kernel on an S700 workstation.

     The hpACDev requires two different kinds of device files to operate.
Upon construction, user code must provide the hpACDev constructor with the
pathname of a SCSI passthru driver character special device file corresponding 
to the autochanger's controller (picker) device.  I/O to surfaces requires that 
the drives within the autochanger are associated with SCSI disk driver 
character special device files.  If the application program using the
hpACDev object is expected to be run by root processes, no special actions
need to be taken for associating drives with device files; the hpACDev contains
code which will automatically generate appropriate device files for the 
drives.  However, this code uses the mknod(2) system call, which requires
a root user id.  If the application will not be run by root in general,
then it must be written to use the addDriveMap() method (described below) so
that the user can provide device files for the drives for which the user
has read and write permission.

3.  hpACDev FUNCTION INTERFACE

     This section gives a step-by-step description of how application code
would construct and use the hpACDev object to perform I/O operations on
autochanger surfaces on a series 700 workstation running HP-UX 9.0x.  These
steps are give basic information.  For greater detail, please consult the
Architecture and Algorithms section, as well as the method interfaces.

     1.  Construction

          The hpACDev constructor has the following interface:

          hpACDev( const String& devfile )

          The devfile argument is a String containing the pathname of a 
          SCSI passthru driver character device file corresponding to the
          autochanger's controller.  After construction, users may wish
          to call the error() method to verify that construction was
          successful.

     2.  Map surfaces to sid_t handles

          Once the hpACDev object has been successfully constructed, the
          user must then map surfaces within the autochanger to sid_t handles,
          which are used to specify to the hpACDev which surface is desired
          for a given I/O request.  The addSurface() method has the following
          interface:

          sid_t addSurface( UINT32 slot,UINT32 side )

          The slot parameter specifies which storage slot in the autochanger,
          starting from 1 and increasing numerically, contains the desired
          surface.  The side parameter specifies which side of the cartridge.
          A value of 0 indicates side 'A', and any nonzero value specifies
          side 'B'.  The hpACDev verifies that the specified slot is in fact
          occupied by a cartridge, and if it is, allocates an sid_t handle
          and returns it.  The sid_t will be used henceforth to refer to
          that surface for all I/O requests.

          Note that it is not necessary to map all surfaces at once.  A
          surface only needs to be mapped before the user attempts to 
          access it with the hpACDev, as the sid_t handles returned by the
          addSurface() method are the only means by which the user can
          specify a surface to the other methods of the hpACDev.

     3.  Map drives to device files

          If the user process using the hpACDev is not a root process, the
          user must provide the pathnames of disk character special device
          files for all of the drives in the autochanger prior to the first
          I/O request to a surface.  The user must have read and write 
          permission to all of the device files specified.  To map a drive
          to a device file, the hpACDev provides the following method:

          INT32 addDriveMap( INT32 id,INT32 lun, const String& devfile )

          The id and lun parameters specify the SCSI target ID and SCSI 
          LUN ID of the drive being mapped to the device file given in
          devfile.  The hpACDev verifies that the given id and lun values
          correspond to a drive within the autochanger.

          Note that the addDriveMap() method does not attempt to verify
          that the specified device file is accessible.  

     4.  Perform I/O

          At this point, once surfaces and drives have been mapped, the
          user is ready to perform I/O to surfaces.  Any of the I/O-related
          methods may now be used.  The user simply must specify one of
          the sid_t handles returned from the addSurface() method.

     5.  Destruction

          The user does not need to do anything special to destruct the 
          hpACDev.  Simply allow the hpACDev object to go out of scope, or,
          if a pointer to an hpACDev is used, delete it.  

4.  ARCHITECTURE AND ALGORITHMS
     
     The hpACDev is built upon an HP-UX specific implementation of an 
SSDevice (single-surface device), the DriveDevice class.  The DriveDevice is 
an abstraction of a stand-alone magneto-optical disk drive.  It provides
I/O capabilities to a disk drive, including write-without erase and
write-with-verify modes.  The hpACDev object uses DriveDevice objects to
perform the actual I/O requests; the hpACDev itself contains no intelligence
about accessing MO drives.  The primary responsibility of the hpACDev is
to keep track of surfaces within the autochanger, allocate drives to
surfaces, and to move surfaces between their storage slots and drives as
necessary.

     The MSLIB code requires that a subclass implementation of a MSDevice
provide only seven mandatory methods.  They are listed below:

     virtual UINT32 numsectors(sid_t sid) = 0
     virtual UINT32 sectorSize(sid_t sid) = 0
     virtual INT32  read(sid_t sid,UINT32 start,UINT32 nbytes,BYTE *buf) = 0
     virtual INT32  write(sid_t sid,UINT32 start,UINT32 nbytes,BYTE *buf) = 0
     virtual Error  error(void) const = 0
     virtual void   flush(void) = 0
     virtual void   errorClear(void) = 0

     The hpACDev implements these seven virtual methods as well as several 
more.  The following virtual methods are not mandatory to the MSLIB code, but
if they are implemented, they must be implemented according to the virtual
interfaces specified in the MSDevice class.  These virtual methods must
be implemented if an MSDevice subclass is to be used with the SSLIB code:

    virtual          ~hpACDev(void);
    virtual INT32    writeWOE(sid_t sid,UINT32 start,UINT32 nbytes,
                              BYTE *barr);
    virtual INT32    erase(sid_t sid,UINT32 start,UINT32 nsectors);
    virtual INT32    verifyMode(sid_t sid,INT32 newmode);
    virtual INT32    verifyMode(sid_t sid);
    virtual SSDevice *allocDrive(sid_t sid);
    virtual INT32    freeDrive(SSDevice *drivep);
    virtual MediaType mediaType(sid_t sid);
    virtual Boolean  verifyBlank(sid_t sid,UINT32 start,UINT32 nsectors,
                                 UINT32 *where);

     Finally, the hpACDev implements several additional functions which are
not specified by the MSDevice virtual interface.  These functions provide
functionality specific to the HP-UX implementation, and in several cases
reflect the underlying device driver being used to control the autochanger:

                     hpACDev(const String& devfile);
            sid_t    addSurface(UINT32 slot,UINT32 side);
            INT32    removeSurface(sid_t sid);
            INT32    sleepMode(INT32 newmode);
            INT32    sleepMode(void) const;
            INT32    addDriveMap(INT32 id,INT32 lun,const String& devfile);
            INT32    elementAddresses(struct element_addresses& eltaddr);
            INT32    elementStatus(INT32 element,struct element_status& es);
            UINT32   senseData(struct sense_2_aligned& sense);
#ifdef DEBUG
            void     print(UINT32 indent,const String& str);
#endif

     The virtual methods follow the interface and semantics specification
given in UDF_devs.txt, under the MSDevice method description section.  The
additional functions which are not specified by the MSDevice virtual
interface will be discussed in greater detail below.

SWAP ALGORITHMS

     The hpACDev allows the user to perform I/O to surfaces within the
autochanger without having to be concerned about explicitly moving
cartridges between their storage slots and drives.  The hpACDev uses
the following algorithm during an I/O request

     If the desired surface is already in an allocated drive, simply
     perform the I/O.

     If the desired surface is in an unallocated drive, create a Drive
     Device object and use it to perform the desired I/O.

     Otherwise, find a free drive.  If there is none and sleep mode is 
     nonzero, enter a busy-wait loop until there is a free drive.  Otherwise 
     return and set the internal error to error_Drive_not_available.

     If the free drive is occupied, execute a SCSI exchange medium command
     to move the drive occupant to its home slot and the desired cartridge
     into the drive.

     If the free drive is empty, execute a SCSI move medium command to 
     move the desired cartridge into the drive.

     Create a DriveDevice object and use it to perform the desired I/O on
     the surface.

     Note that after the I/O request is complete, the surface is left in
the drive, though it is marked unallocated.  Media in an unallocated drive
may be swapped out whenever necessary; if the drive is allocated, the media
in it cannot be swapped out until the drive is freed.  Any of the I/O
methods, such as read(), write(), and erase(), unallocate the drive for the
surface after the I/O is complete.  Normally the only way a surface would
be left in an allocated driver across I/O calls is if the drive was allocated
explicitly by the user by the allocDrive() method.

DRIVE "SPARING" AND "CLEARING"

     Upon construction, the hpACDev checks the autochanger's drives and
determines whether any contain media.  If a drive contains media, has a
valid source element, and the source element is a storage slot, the hpACDev
will attempt to clear the drive by issueing a SCSI move medium command to
move the cartridge from the drive to its home slot.  If the drive contains
media but does not have a valid source element, or if the source element is
not a storage element, the hpACDev "spares" the drive and marks it as
unavailable for use.  

     When an hpACDev is destructed, it will issue SCSI move medium commands
to return any and all surfaces in non-spared drives to their home storage
slots.

SURFACE UNMAPPING

     The user may remove an sid_t handle from use by calling the removeSurface()
method.  If the specified surface is in an allocated drive, the call fails;
if not, the sid_t will no longer be valid and the surface, if it is in a
drive, will be returned to its home storage slot.

------------------------------------------------------------------------------
--                             METHOD DESCRIPTIONS                          --
------------------------------------------------------------------------------
                
     All of the virtual functions are described in a separate document,
UDF_devs.txt.  The hpACDev implementation of the functions corresponds to
the behavior specified in the UDF_devs.txt document.

     Following are method descriptions of the methods which are unique to
the hpACDev class.

------------------------------------------------------------------------------
METHOD:  hpACDev(const String& devfile)

     Constructor.  This method opens the specified device file for read/write,
and gets element addresses and element status information from the autochanger.

ARGUMENTS:

     const String& devfile  IN  String containing the pathname of the passthru
                                device file corresponding to the autochanger's
                                controller device.
                            
RETURNS:

     None.

PRECONDITIONS:

--The argument device file exists and has appropriate access permissions.

POSTCONDITIONS:

     See above.

------------------------------------------------------------------------------
METHOD:  addSurface(UINT32 slot,UINT32 side)

     This method maps the surface given by the (slot,side) parameters to an
sid_t handle.

ARGUMENTS:

     UINT32  slot  IN  Home slot of the surface.  
     UINT32  side  IN  Which side; 0 = side 'A', nonzero = side 'B'.

RETURNS:

     On success, returns the sid_t mapping for the surface; on failure,
returns SID_T_INVALID.

PRECONDITIONS:

--The hpACDev was successfully constructed.
--The specified surface is in the autochanger.

POSTCONDITIONS:

     See Return values.

------------------------------------------------------------------------------
METHOD:  removeSurface(sid_t sid)

     This method removes an sid_t mapping from the hpACDev's internal map.

ARGUMENTS:

     sid_t  sid  IN  Sid to remove from the internal map.

RETURNS:

     Returns 0 on success, -1 on failure (improperly constructed hpACDev
object, bad sid).

PRECONDITIONS:

--The hpACDev was successfully constructed.
--The argument sid was allocated by a previous call to addSurface().
--The surface corresponding to the argument sid is not in an allocated drive.

POSTCONDITIONS:

--The sid will be removed from the internal map.
--The surface, if in a drive, will be returned to its home slot. 

------------------------------------------------------------------------------
METHOD:  sleepMode(INT32 newmode)

     This method sets the sleep mode of the hpACDev.  See "Swap Algorithms"
above.

ARGUMENTS:

     INT32 newmode  IN  New sleep mode.  Non-zero value puts the hpACDev
                        into sleep mode; zero value puts the hpACDev into
                        non-sleep mode.

RETURNS:

     Returns the previous value of the sleep mode.

PRECONDITIONS:

--The hpACDev was successfully constructed.

POSTCONDITIONS:

     See above.

------------------------------------------------------------------------------
METHOD:  sleepMode(void)

     Returns the current sleep mode of the hpACDev.

ARGUMENTS:

     None.

RETURNS:  

     See above.

PRECONDITIONS:

--The hpACDev was successfully constructed.

POSTCONDITIONS:

     See Return values.   

------------------------------------------------------------------------------
METHOD:  addDriveMap(INT32 id,INT32 lun,const String& devfile)

     Maps a drive in the autochanger to a device file.

ARGUMENTS:

     INT32          id       IN  SCSI target id of the drive
     INT32          lun      IN  SCSI LUN id of the drive
     const String&  devfile  IN  String containing the pathname of the disk
                                 drive character special device file to 
                                 associate with the specified drive.

RETURNS:

     Returns 0 on success, -1 on failure.

PRECONDITIONS:

--The hpACDev was successfully constructed.
--The argument device file exists and has appropriate permissions.
--The drive specified by the (tgt,lun) is in the autochanger.

POSTCONDITIONS:

     See above.

------------------------------------------------------------------------------
METHOD:  elementAddresses(struct element_addresses& eltaddr)

     This method gets element addresses information from the autochanger.
This information includes the total number of drives and storage slots in
the autochanger.  The element_addresses struct is defined in <nsrscsi.h>,
which is a version of the system header file <sys/scsi.h> modified to allow
C++ compilation.

ARGUMENTS:

     struct element_addresses& eltaddr  OUT  Data structure for requested
                                             information.

RETURNS:

     Returns 0 on success, -1 on failure.

PRECONDITIONS:

--The hpACDev was successfully constructed.

POSTCONDITIONS:

     See above.

------------------------------------------------------------------------------
METHOD:  elementStatus(INT32 element,struct element_status& es)

     This method gets element status information.

ARGUMENTS:

     INT32                  element IN  Element to get status about
     struct element_status& es      OUT Data structure for requested data

RETURNS:

     Returns 0 on success, -1 on failure.

PRECONDITIONS:

--The hpACDev was successfully constructed.
--The element number is valid.

POSTCONDITIONS:

     See above.

------------------------------------------------------------------------------
METHOD:  senseData(struct sense_2_aligned& sense)

     This method returns sense data from the last failed autochanger command.
Typically this method would be used after a failed I/O operation that indicated
a move or exchange failure, for diagnosis purposes.

ARGUMENTS:
     struct sense_2_aligned& sense  OUT  Data structure for requested data.

RETURNS:

     Returns 0 on success, -1 on failure.

PRECONDITIONS:

--The hpACDev was successfully constructed.

POSTCONDITIONS:

     See above.

------------------------------------------------------------------------------
METHOD:  print(UINT32 indent,const String& str)

     This method prints to stdout information about the internal state of
the hpACDev.  This method is only available if the hpACDev code is compiled
with the DEBUG macro defined, and is intended solely for debugging purposes.

ARGUMENTS:

     UINT32        indent IN  Indent factor; not currently used     
     const String& str    IN  Header string to prepend to state information

RETURNS:

     None.

PRECONDITIONS:

     None.

POSTCONDITIONS:

     None.
------------------------------------------------------------------------------
