ASPI for Win16 Specification

Read this specification to find out about
    *   An overview of the steps involved in programming ASPI for Win16
    *   ASPI for Win16 commands
    *   Polling and posting of ASPI for Win16 SCSI requests
    *   Important information for ASPI for Win16 developers
    *   The scanscsi.exe application, which scans the SCSI bus and displays the
        device name of each target
    *   A summary of the error codes and messages returned by ASPI routines

-------------------------------------------------------------------------------

Using ASPI for Win16 - An Overview

ASPI for Win16 is implemented as a Dynamic Link Library (DLL). The name of
this file is winaspi.dll. ASPI function calls are used to return information
about the ASPI manager, host adapter, and SCSI devices, but are mainly used to
execute SCSI I/O requests. The ASPI for Win16 layer is fully multitasking and
can accept function calls before previous calls have completed. Two functions
must be imported from winaspi.dll into your Windows application:

    Function            Description
    ------------------  -------------------------------------------------------
    GetASPISupportInfo  This function returns the number of host adapters
                        installed and other miscellaneous information. You
                        should call this function to make sure that ASPI
                        is properly initialized before calling the
                        SendASPICommand function.
    SendASPICommand     This function allows you to send an ASPI for Win16
                        command.
    ------------------  -------------------------------------------------------

All SRBs and data buffers must be in locked memory before they are passed to
ASPI.  This is covered in greater detail in the Miscellaneous section at the
end of the document.

-------------------------------------------------------------------------------

ASPI Managers For Windows

This document does not define the protocol between winaspi.dll and any DOS
ASPI managers that may be loaded. There are many reasons for this,
including:

    *   Some hardware companies may write ASPI for Win16 managers that do not
        include concurrent ASPI for DOS support.
    *   Some hardware companies may decide to have winaspi.dll communicate with
        a Windows 386 Enhanced Mode Virtual Device Driver (VxD).
    *   Some hardware companies may decide to only support Windows 3.1, which
        may or may not have improved hardware support.

It is also not the intent of this specification to define which modes of
Windows should be supported. We anticipate that most hardware companies will
support ASPI for Win16 in Standard and 386 Enhanced Modes and will not support
Real Mode.

-------------------------------------------------------------------------------

GetASPISupportInfo Function

Prototype: WORD GetASPISupportInfo( VOID );

The GetASPISupportInfo function returns the number of host adapters installed
and other miscellaneous information. This function should be called first
before issuing an ASPI command to ensure that ASPI has been properly
initialized. This function call does not perform any initialization itself;
instead, it confirms that everything is ready for processing.

The return value specifies the outcome of the function. The LOBYTE returns the
number of host adapters installed if the HIBYTE value equals SS_COMP. The
HIBYTE returns whether ASPI for Win16 is ready to accept ASPI commands. Refer
to the example code. The HIBYTE is defined as follows:


    Table 1: Return Values from GetASPISupportInfo Function

    Status            Value  Description
    ----------------  -----  --------------------------------------------------
    SS_COMP            01h   SCSI Request Completed Without Error
    SS_OLD_MANAGER     E1h   One or more ASPI for DOS managers are loaded which
                             do not support ASPI for Win16.
    SS_ILLEGAL_MODE    E2h   This ASPI manager does not support this mode of
                             Windows. You typically see this error code when
                             running Windows in Real Mode.
    SS_NO_ASPI         E3h   No ASPI managers are loaded. This typically occurs
                             when a DOS ASPI manager is not resident in memory.
    SS_FAILED_INIT     E4h   For some reason, other than SS_OLD_MANAGER,
                             SS_ILLEGAL_MODE or SS_NO_ASPI, ASPI for Win16
                             could not properly initialize itself. This may be
                             caused by a lack of system resources.
    ----------------  -----  --------------------------------------------------


Example

The following example returns the current status of ASPI for Win16:

    WORD ASPIStatus;
    BYTE NumAdapters;
    HWND hwnd;

                .
                .

    ASPIStatus = GetASPISupportInfo();
    switch ( HIBYTE(ASPIStatus) )
        {
        case SS_COMP:
            //ASPI for Windows is properly initialized
            NumAdapters = LOBYTE(ASPIStatus);
            break;
        case SS_NO_ASPI:
            MessageBox( hwnd,
                         "No ASPI managers were found!!",
                         NULL,
                         MB_ICONSTOP );
            return 0;
        case SS_ILLEGAL_MODE:
            MessageBox( hwnd,
                         "ASPI for Windows does not support this mode!!",
                         NULL,
                         MB_ICONSTOP );
            return 0;
        case SS_OLD_MANAGER:
            MessageBox( hwnd,
                         "An ASPI manager which does not support Windows"
                         "is resident!!",
                         NULL,
                         MB_ICONSTOP );
            return 0;
        default:
            MessageBox( hwnd,
                    "    ASPI for Windows is not initialized!!",
                         NULL,
                         MB_ICONSTOP );
            return 0;
        }
                    .
                    .

-------------------------------------------------------------------------------

Host Adapter Inquiry Command

Prototype: WORD SendASPICommand( LPSRB lpSRB );

The SendASPICommand function with command code SC_HA_INQUIRY (00h) is used to
get information on the installed host adapter hardware, including number of
host adapters installed.  lpSRB is a pointer to the following structure:

    Table 2: Host Adapter Inquiry Command

    typedef struct
    {
        BYTE        SRB_Cmd;            // ASPI command code = SC_HA_INQUIRY
        BYTE        SRB_Status;         // ASPI command status byte
        BYTE        SRB_HaId;           // ASPI host adapter number
        BYTE        SRB_Flags;          // ASPI request flags
        WORD        SRB_55AASignature;  // Extended signature bytes
        WORD        SRB_ExtBufferSize;  // Extended buffer length
        BYTE        HA_Count;           // Number of host adapters present
        BYTE        HA_SCSI_ID;         // SCSI ID of host adapter
        BYTE        HA_ManagerId[16];   // String describing the manager
        BYTE        HA_Identifier[16];  // String describing the host adapter
        BYTE        HA_Unique[16];      // Host Adapter Unique parameters
        BYTE        HA_ExtBuffer[4];    // Extended buffer area
    } SRB_HAInquiry;

    Offset   # Bytes  Description                                          R/W
    -------- -------- ---------------------------------------------------- ---
    00h (00) 01h (01) Command Code = SC_HA_INQUIRY (00h)                    W
    01h (01) 01h (01) Command Status. This byte always returns with a       R
                      non-zero status. A SCSI Request Completed Without
                      Error (01h) status indicates that the remaining
                      fields are valid.  See the table below for other
                      return codes for this command.
    02h (02) 01h (01) Host Adapter Number. This field specifies which       W
                      installed host adapter the request is intended for.
                      Host adapter numbers are assigned by the SCSI
                      manager layer, beginning with 0. An Invalid Host
                      Adapter Number (81h) status indicates that the
                      specified host adapter is not installed.
    03h (03) 01h (01) SCSI Request Flags. This field is currently           -
                      undefined for this command and should be zeroed.     R/W
    04h (04) 02h (02) Extended Request Signature. This field is set to
                      AA55h (55 in byte 4) by the ASPI client. If the
                      extended request mechanism is supported by the
                      device driver, this signature is reversed to 55AAh
                      when returned. This word is reserved for older
                      drivers that do not support the extended request
                      and will be returned unchanged.
    06h (06) 02h (02) Extended Buffer Length. The ASPI client specifies    R/W
                      the number of extended-buffer bytes desired.
                      Currently, 4 bytes are defined. When returned, this
                      field contains the number of bytes that contain
                      valid information. In no case will the length
                      returned be greater than the length specified on
                      input.
    08h (08) 01h (01) Number of Host Adapters. The ASPI manager sets this   R
                      field with the number of host adapters installed
                      under ASPI. For example, a return value of 2
                      indicates that host adapters #0 and #1 are valid.
                      To determine the total number of host adapters in
                      the system the SRB_HaId field (offset 02h) should
                      be set to 0, or GetASPISupportInfo can be used.
    09h (09) 01h (01) Host Adapter SCSI ID. The ASPI manager sets this      R
                      field with the SCSI ID of the given host adapter.
    0Ah (10) 10h (16) SCSI Manager ID. This field contains a 16-byte        R
                      ASCII string describing the SCSI manager.
    1Ah (26) 10h (16) Host Adapter Identifier. This field contains a        R
                      16-byte ASCII string describing the SCSI host
                      adapter.
    2Ah (42) 10h (16) Host Adapter Unique Parameters. The definition of
                      this field is left to implementation notes
                      specific to a particular host adapter.
    3Ah (58) 02h (02) Supported Extensions.                                 R
                      Bit 15-4  Reserved
                      Bit 3     0 = Not a Wide SCSI 32-bit host adapter
                                1 = Wide SCSI 32-bit host adapter
                      Bit 2     0 = Not a Wide SCSI 16-bit host adapter
                                1 = Wide SCSI 16-bit host adapter
                      Bit 1     0 = Residual byte length not reported
                                1 = Residual byte length reported. See section
                                    on Residual Byte Length below.
                      Bit 0     Reserved
    -------- -------- ---------------------------------------------------- ---

The return value specifies the outcome of the function. One of the following
values is returned by ASPI for Win16:

    Table 3: Return Values from Host Adapter Inquiry Command

    Status            Value  Description
    ----------------  -----  --------------------------------------------------
    SS_COMP            01h   SCSI Request Completed Without Error
    SS_INVALID_HA      81h   Invalid Host Adapter Number
    SS_INVALID_SRB     E0h   One or more parameters in the SCSI Request Block
                             (SRB) are set incorrectly.
    ----------------  -----  --------------------------------------------------

Residual Byte Length

Residual byte length is the number of bytes not transferred to, or received
from, the target SCSI device. For example, if the ASPI buffer length for a
SCSI Inquiry command is set for 100 bytes, but the target only returns 36
bytes; the residual length is 64 bytes. Another example: if the ASPI buffer
length for a SCSI Write command is set for 514 bytes but the target only takes
512 bytes, the residual length is 2 bytes.

ASPI modules can determine if the loaded ASPI manager supports residual byte
length by issuing an Extended Host Adapter Inquiry command, as described in
the previous section.

Example

The following example retrieves host adapter hardware information from adapter
#0:

    SRB_HAInquiry MySRB;
    WORD ASPI_Status;
                .
                .
    MySRB.SRB_Cmd      = SC_HA_INQUIRY;
    MySRB.SRB_HaId     = 0;
    MySRB.SRB_Flags    = 0;
    MySRB.SRB_Hdr_Rsvd = 0;
    ASPI_Status = SendASPICommand ( (LPSRB) &MySRB );
                .
                .

-------------------------------------------------------------------------------

Get Device Type Command

Prototype: WORD SendASPICommand( LPSRB lpSRB );

The SendASPICommand function with command code SC_GET_DEV_TYPE (01h) is used by
Windows applications to identify the targets which they need to support. For
example, a Windows tape backup package can scan each Target/LUN on each
installed host adapter looking for the device type corresponding to sequential
access devices. This eliminates the need for each Windows application to
duplicate the effort of scanning the SCSI bus for devices.

    Note: Some Windows applications may scan the SCSI bus themselves instead
          of using this command. This allows them to detect SCSI devices that
          were powered up after ASPI initialization.

lpSRB is a pointer to the following structure:

    Table 4: Get Device Type Command

    typedef struct
    {
        BYTE        SRB_Cmd;            // ASPI command code = SC_GET_DEV_TYPE
        BYTE        SRB_Status;         // ASPI command status byte
        BYTE        SRB_HaId;           // ASPI host adapter number
        BYTE        SRB_Flags;          // ASPI request flags
        DWORD       SRB_Hdr_Rsvd;       // Reserved, MUST = 0
        BYTE        SRB_Target;         // Target's SCSI ID
        BYTE        SRB_Lun;            // Target's LUN number
        BYTE        SRB_DeviceType;     // Target's peripheral device type
    } SRB_GDEVBlock;

    Offset   # Bytes  Description                                          R/W
    -------- -------- ---------------------------------------------------- ---
    00h (00) 01h (01) Command Code = SC_GET_DEV_TYPE (01h)                  W
    01h (01) 01h (01) Command Status. This byte always returns with a       R
                      non-zero status. A SCSI Request Completed Without
                      Error (01h) status indicates that the specified
                      device is installed and the Peripheral Device Type
                      field is valid. A SCSI Device Not Installed Error
                      (82h) indicates that the Peripheral Device Type
                      field is not valid.
    02h (02) 01h (01) Host Adapter Number. This field specifies which       W
                      installed host adapter the request is intended for.
                      Host adapter numbers are assigned by the SCSI
                      manager layer, beginning with 0. An Invalid Host
                      Adapter Number (81h) status indicates that the
                      specified host adapter is not installed.
    03h (03) 01h (01) SCSI Request Flags. This field is currently           -
                      undefined for this command and should be zeroed.
    04h (04) 04h (04) Reserved for Expansion = 0                            -
    08h (08) 01h (01) Target ID. This field indicates the SCSI ID of the    W
                      target device.
    09h (09) 01h (01) LUN. This field indicates the Logical Unit Number     W
                      (LUN) of the device.
    0Ah (10) 01h (01) Peripheral Device Type of Target/LUN. The ASPI        R
                      manager fills this field with the peripheral device
                      type, as previously reported by the SCSI Inquiry
                      command. Refer to any SCSI specification to learn
                      more about the SCSI Inquiry command.
    -------- -------- ---------------------------------------------------- ---

The return value specifies the outcome of the function. One of the following
values is returned by ASPI for Win16:

    Table 5: Return Values from Get Device Type Command

    Status            Value  Description
    ----------------  -----  --------------------------------------------------
    SS_COMP            01h   SCSI Request Completed Without Error
    SS_INVALID_HA      81h   Invalid Host Adapter Number
    SS_NO_DEVICE       82h   SCSI Device Not Installed
    SS_INVALID_SRB     E0h   One or more parameters in the SCSI Request Block
                             (SRB) are set incorrectly.
    ----------------  -----  --------------------------------------------------

Example

The following example retrieves the peripheral device type from host adapter
#0, target ID #4, and LUN #0.


    SRB_GDEVBlock MySRB;
    WORD ASPI_Status;
                .
                .
    MySRB.SRB_Cmd      = SC_GET_DEV_TYPE;
    MySRB.SRB_HaId     = 0;
    MySRB.SRB_Flags    = 0;
    MySRB.SRB_Hdr_Rsvd = 0;
    MySRB.SRB_Target   = 4;
    MySRB.SRB_Lun      = 0;
    ASPI_Status = SendASPICommand ( (LPSRB) &MySRB );
                .
    /***************************************************/
    /* If ASPI_Status == SS_COMP, MySRB.SRB_DeviceType */
    /* will contain the peripheral device type.        */
    /***************************************************/
                .
                .

-------------------------------------------------------------------------------

Execute SCSI I/O Command

Prototype: WORD SendASPICommand( LPSRB lpSRB );

The SendASPICommand function with command code SC_EXEC_SCSI_CMD (02h) is used
to execute a SCSI command: for example, sending a SCSI Test Unit Ready command
to a tape drive.  lpSRB is a pointer to the following structure:

    Table 6: Execute SCSI I/O Command

    typedef struct
    {                                       // Structure for 6-byte CDBs
        BYTE        SRB_Cmd;                // ASPI command code = SC_EXEC_SCSI_CMD
        BYTE        SRB_Status;             // ASPI command status byte
        BYTE        SRB_HaId;               // ASPI host adapter number
        BYTE        SRB_Flags;              // ASPI request flags
        DWORD       SRB_Hdr_Rsvd;           // Reserved, MUST = 0
        BYTE        SRB_Target;             // Target's SCSI ID
        BYTE        SRB_Lun;                // Target's LUN number
        DWORD       SRB_BufLen;             // Data Allocation LengthPG
        BYTE        SRB_SenseLen;           // Sense Allocation Length
        BYTE        far *SRB_BufPointer;    // Data Buffer Pointer
        DWORD       SRB_Rsvd1;              // Reserved, MUST = 0
        BYTE        SRB_CDBLen;             // CDB Length = 6
        BYTE        SRB_HaStat;             // Host Adapter Status
        BYTE        SRB_TargStat;           // Target Status
        FARPROC SRB_PostProc;               // Post routine
        BYTE        SRB_Rsvd2[34];          // Reserved, MUST = 0
        BYTE        CDBByte[6];             // SCSI CDB
        BYTE        SenseArea6[SENSE_LEN];  // Request Sense buffer
    } SRB_ExecSCSICmd6;

    typedef struct
    {                                       // Structure for 10-byte CDBs
        BYTE        SRB_Cmd;                // ASPI command code = SC_EXEC_SCSI_CMD
        BYTE        SRB_Status;             // ASPI command status byte
        BYTE        SRB_HaId;               // ASPI host adapter number
        BYTE        SRB_Flags;              // ASPI request flags
        DWORD       SRB_Hdr_Rsvd;           // Reserved, MUST = 0
        BYTE        SRB_Target;             // Target's SCSI ID
        BYTE        SRB_Lun;                // Target's LUN number
        DWORD       SRB_BufLen;             // Data Allocation Length
        BYTE        SRB_SenseLen;           // Sense Allocation Length
        BYTE        far *SRB_BufPointer;    // Data Buffer Pointer
        DWORD       SRB_Rsvd1;              // Reserved, MUST = 0
        BYTE        SRB_CDBLen;             // CDB Length = 10
        BYTE        SRB_HaStat;             // Host Adapter Status
        BYTE        SRB_TargStat;           // Target Status
        FARPROC     SRB_PostProc;           // Post routine
        BYTE        SRB_Rsvd2[34];          // Reserved, MUST = 0
        BYTE        CDBByte[10];            // SCSI CDB
        BYTE        SenseArea10[SENSE_LEN]; // Request Sense buffer
    } SRB_ExecSCSICmd10;

    Note: You can easily create a new structure for non-standard CDB lengths.

    Offset   # Bytes  Description                                          R/W
    -------- -------- ---------------------------------------------------- ---
    00h (00) 01h (01) Command Code = SC_EXEC_SCSI_CMD (02h)                 W
    01h (01) 01h (01) Command Status.                                       R
    02h (02) 01h (01) Host Adapter Number. This field specifies which       W
                      installed host adapter the request is intended for.
                      Host adapter numbers are assigned by the SCSI
                      manager layer, beginning with 0. An Invalid Host
                      Adapter Number (81h) status indicates that the
                      specified host adapter is not installed.
    03h (03) 01h (01) SCSI Request Flags.                                   W
                      Bits 7-5 Reserved
                      Bits 4-3 00 = Transfer direction determined by SCSI
                                    command. Length not checked.
                               01 = Transfer direction from SCSI target
                                    to host. Length checked.
                               10 = Transfer direction from host to SCSI
                                    target. Length checked.
                               11 = No data transfer.
                      Bit 2    0 = Residual byte length reporting disabled
                               1 = Residual byte length reporting enabled
                                   (if the ASPI manager supports it)
                      Bit 1    0 = Linking disabled
                               1 = Linking enabled. This bit should be
                                   cleared if scatter/gather is enabled.
                      Bit 0    0 = Posting disabled
                               1 = Posting enabled
    04h (04) 04h (04) Reserved = 0                                          -
    08h (08) 01h (01) Target ID. This field specifies the target ID of      W
                      the peripheral device involved in the I/O
                      operation.
    09h (09) 01h (01) LUN. This field specifies the LUN of the peripheral   W
                      device involved in the I/O operation.
    0Ah (10) 04h (04) Data Allocation Length. This field indicates the      W
                      number of bytes to be transferred. If the SCSI
                      command to be executed does not transfer data
                      (i.e., Rewind, Start Unit, etc.) the Data
                      Allocation Length must be set to zero. If residual
                      byte length is supported and selected, this field
                      is returned with the residual number of bytes
                      (usually 0). See Residual Byte Length on page 2-7.
    0Eh (14) 01h (01) Sense Allocation Length (N). This field indicates     W
                      the number of bytes allocated at the end of the SRB
                      for sense data. A request sense is automatically
                      generated if a check condition is presented at the
                      end of a SCSI command.
    0Fh (15) 02h (02) Data Buffer Pointer (Offset). This field is a         W
                      pointer to the I/O data buffer. You place the
                      logical address here. ASPI converts it to the
                      physical address in the case of a bus master or DMA
                      transfer.
    11h (17) 02h (02) Data Buffer Pointer (Segment)                         W
    13h (19) 02h (02) SRB Link Pointer (Offset). This field is a pointer    W
                      to the next SRB in a chain. See SCSI Command
                      Linking on page 2-9 for more information.
    15h (21) 02h (02) SRB Link Pointer (Segment)                            W
    17h (23) 01h (01) SCSI CDB Length (M). This field establishes the       W
                      length, in bytes, of the SCSI Command Descriptor
                      Block (CDB).
    18h (24) 01h (01) Host Adapter Status.                                  R
                      00h - Host adapter did not detect any error
                      11h - Selection time-out
                      12h - Data overrun/underrun
                      13h - Unexpected Bus Free
                      14h - Target Bus phase sequence failure
    19h (25) 01h (01) Target Status.                                        R
                      00h - No target status
                      02h - Check status (sense data is in sense
                            allocation area)
                      08h - Specified target/LUN is busy
                      18h - Reservation conflict
    1Ah (26) 02h (02) Post Routine Address (Offset). This field, if         W
                      specified, is called when the I/O is completed. See
                      ASPI Command Posting on page 2-10 for more
                      information.
    1Ch (28) 02h (02) Post Routine Address (Segment)                        W
    1Eh (30) 22h (34) Reserved for ASPI Workspace                           -
    40h (64) M        SCSI Command Descriptor Block (CDB). This field       W
                      contains the CDB as defined by the target's SCSI
                      command set. The length of the SCSI CDB is
                      specified in the SCSI Command Length field.
    40h+M    N        Sense Allocation Area. This field is filled with      R
                      sense data on a check condition. The maximum length
                      of this field is specified in the Sense Allocation
                      Length field. The target can return fewer than the
                      number of sense bytes requested.
    -------- -------- ---------------------------------------------------- ---

The return value specifies the outcome of the function. ASPI for Win16 returns
one of the following values:

    Table 7: Return Values from Execute SCSI I/O Command

    Status            Value  Description
    ----------------  -----  --------------------------------------------------
    SS_PENDING         00h   SCSI request is in progress.
    SS_COMP            01h   SCSI/ASPI request has completed without error.
    SS_ABORTED         02h   SCSI command has been aborted.
    SS_ERR             04h   SCSI command has completed with an error.
    SS_INVALID_HA      81h   Invalid Host Adapter Number
    SS_INVALID_SRB     E0h   One or more parameters in the SCSI Request Block
                             (SRB) are set incorrectly.
    SS_ASPI_IS_BUSY    E5h   The ASPI manager cannot handle the request at this
                             time. This error generally occurs if the ASPI
                             manager is already using up all of his resources
                             to execute other requests. Try resending the
                             command later.
    SS_BUFFER_TO_BIG   E6h   The ASPI manager cannot handle the given transfer
                             size. Refer to Miscellaneous for more information.
    ----------------  -----  --------------------------------------------------

Example

The following example sends a SCSI Inquiry command to host adapter #0, target
#0, and LUN #0:

    SRB_ExecSCSICmd6 MySRB;
    char InquiryBuffer[32];
    FARPROC lpfnPostProcedure;
                .
                .
    lpfnPostProcedure    = MakeProcInstance( PostProcedure, hInstance );
                .
                .
    MySRB.SRB_Cmd        = SC_EXEC_SCSI_CMD;
    MySRB.SRB_HaId       = 0;
    MySRB.SRB_Flags      = SRB_DIR_SCSI | SRB_POSTING;
    MySRB.SRB_Hdr_Rsvd   = 0;
    MySRB.SRB_Target     = 0;
    MySRB.SRB_Lun        = 0;
    MySRB.SRB_BufLen     = 32;
    MySRB.SRB_SenseLen   = SENSE_LEN;
    MySRB.SRB_BufPointer = InquiryBuffer;
    MySRB.SRB_CDBLen     = 6;
    MySRB.SRB_PostProc   = lpfnPostProcedure;
    MySRB.CDBByte[0]     = SCSI_INQUIRY;
    MySRB.CDBByte[1]     = 0;
    MySRB.CDBByte[2]     = 0;
    MySRB.CDBByte[3]     = 0;
    MySRB.CDBByte[4]     = 32;
    MySRB.CDBByte[5]     = 0;
                .

    /**************************************************/
    /* Make sure all other reserved fields are zeroed */
    /* before passing the SRB to ASPI for Windows     */
    /**************************************************/
                .
    SendASPICommand ( (LPSRB) &MySRB );
                .
                .

-------------------------------------------------------------------------------

Abort SCSI I/O Command

Prototype: WORD SendASPICommand( LPSRB lpSRB );

The SendASPICommand function with command code SC_ABORT_SRB is used to
request that an SRB be aborted. It should be issued on any I/O request that
has not completed if the application wishes to time-out on that request.
Success of the Abort command is never assured.  lpSRB is a pointer to the
following structure:

    Table 8: Abort SCSI I/O Command

    typedef struct
    {
        BYTE        SRB_Cmd;            // ASPI command code = SC_ABORT_SRB
        BYTE        SRB_Status;         // ASPI command status byte
        BYTE        SRB_HaId;           // ASPI host adapter number
        BYTE        SRB_Flags;          // ASPI request flags
        DWORD       SRB_Hdr_Rsvd;       // Reserved, MUST = 0
        LPSRB       SRB_ToAbort;        // Pointer to SRB to abort
    } SRB_Abort;

    Offset   # Bytes  Description                                          R/W
    -------- -------- ---------------------------------------------------- ---
    00h (00) 01h (01) Command Code = SC_ARORT_SRB (03h)                     W
    01h (01) 01h (01) Command Status. This byte always returns with a       R
                      non-zero status. A SCSI Request Completed Without
                      Error (01h) status indicates that the remaining
                      fields are valid, and the abort will be attempted.
    02h (02) 01h (01) Host Adapter Number. This field specifies which       W
                      installed host adapter the request is intended for.
                      Host adapter numbers are assigned by the SCSI
                      manager layer, beginning with 0. An Invalid Host
                      Adapter Number (81h) status indicates that the
                      specified host adapter is not installed.
    03h (03) 01h (01) SCSI Request Flags. This field is currently           -
                      undefined for this command and should be zeroed.
    04h (04) 04h (04) Reserved for Expansion = 0                            -
    08h (08) 02h (02) SRB Pointer to Abort (Offset). This field contains    W
                      a pointer to the SRB which is to be aborted.
    0Ah (10) 02h (02) SRB Pointer to Abort (Segment)                        W
    -------- -------- ---------------------------------------------------- ---

The return value specifies the outcome of the function. One of the following
values is returned by ASPI for Win16:

    Table 9: Return Values from Abort SCSI I/O Command

    Status            Value  Description
    ----------------  -----  --------------------------------------------------
    SS_COMP            01h   SCSI/ASPI request has completed without error.
    SS_INVALID_HA      81h   Invalid Host Adapter Number
    SS_INVALID_SRB     E0h   One or more parameters in the SCSI Request Block
                             (SRB) are set incorrectly.
    ----------------  -----  --------------------------------------------------

Example

The following example shows how to abort a stuck SCSI I/O:

    SRB_ExecSCSICmd6 StuckSRB;
    SRB_Abort AbortSRB;
    WORD ASPI_Status;
                .
                .
    AbortSRB.SRB_Cmd      = SC_ABORT_SRB;
    AbortSRB.SRB_HaId     = 0;
    AbortSRB.SRB_Flags    = 0;
    AbortSRB.SRB_Hdr_Rsvd = 0;
    AbortSRB.SRB_ToAbort  = (LPSRB) &StuckSRB;
    ASPI_Status = SendASPICommand ( (LPSRB) &AbortSRB );
                .
                .
    while (StuckSRB.SRB_Status==SS_PENDING);
                .
                .
    /**************************************************/
    /* This sample code has no error handling, time-  */
    /* out code, nor does it free up the processor.   */
    /* Your application should be more robust.        */
    /**************************************************/

-------------------------------------------------------------------------------

Reset SCSI Device Command

Prototype: WORD SendASPICommand( LPSRB lpSRB );

The SendASPICommand function with command code SC_RESET_DEV is used to send a
SCSI Bus Device reset to the specified target.  lpSRB is a pointer to the
following structure:

    Table 10: Reset SCSI Device Command

    typedef struct
    {
        BYTE        SRB_Cmd;            // ASPI command code = SC_RESET_DEV
        BYTE        SRB_Status;         // ASPI command status byte
        BYTE        SRB_HaId;           // ASPI host adapter number
        BYTE        SRB_Flags;          // ASPI request flags
        DWORD       SRB_Hdr_Rsvd;       // Reserved, MUST = 0
        BYTE        SRB_Target;         // Target's SCSI ID
        BYTE        SRB_Lun;            // Target's LUN number
        BYTE        SRB_ResetRsvd1[14]; // Reserved, MUST = 0
        BYTE        SRB_HaStat;         // Host Adapter Status
        BYTE        SRB_TargStat;       // Target Status
        FARPROC SRB_PostProc;           // Post routine
        BYTE        SRB_ResetRsvd2[34]; // Reserved, MUST = 0
    } SRB_BusDeviceReset;

    Offset   # Bytes  Description                                          R/W
    -------- -------- ---------------------------------------------------- ---
    00h (00) 01h (01) Command Code = SC_RESET_DEV (04h)                     W
    01h (01) 01h (01) Command Status.                                       R
    02h (02) 01h (01) Host Adapter Number. This field specifies which       W
                      installed host adapter the request is intended for.
                      Host adapter numbers are assigned by the SCSI
                      manager layer, beginning with 0. An Invalid Host
                      Adapter Number (81h) status indicates that the
                      specified host adapter is not installed.
    03h (03) 01h (01) SCSI Request Flags                                    W
                      Bit 7-1   Reserved
                      Bit 0     1 = Posting enabled
                                0 = Posting disabled
    04h (04) 04h (04) Reserved = 0                                          -
    08h (08) 01h (01) Target ID                                             W
    09h (09) 01h (01) LUN                                                   W
    0Ah (10) 0Eh (14) Reserved = 0                                          -
    18h (24) 01h (01) Host Adapter Status                                   R
    19h (25) 01h (01) Target Status                                         R
    1Ah (26) 02h (02) Post Routine Address (Offset)                         W
    1Ch (28) 02h (02) Post Routine Address (Segment)                        W
    1Eh (30) 02h (02) Reserved for ASPI Workspace                           -
    -------- -------- ---------------------------------------------------- ---

Upon completion of the SCSI command, the ASPI manager sets this field with the
host adapter status as follows:

    Table 11: Reset SCSI Device Command Status, HA Status, Target Status

    Command Status    Value  Description
    ----------------  -----  --------------------------------------------------
    SS_COMP            01h   SCSI/ASPI request has completed without error.
    SS_INVALID_HA      81h   Invalid Host Adapter Number
    SS_INVALID_SRB     E0h   One or more parameters in the SCSI Request Block
                             (SRB) are set incorrectly.
    SS_ASPI_IS_BUSY    E5h   The ASPI manager cannot handle the request at this
                             time. This error generally occurs if the ASPI
                             manager is already using up all of his resources
                             to execute other requests. Try resending the
                             command later.
    ----------------  -----  --------------------------------------------------

    HA Status         Value  Description
    ----------------  -----  --------------------------------------------------
    HASTAT_OK          00h   Host adapter did not detect an error
    HASTAT_SEL_TO      11h   Selection time-out
    HASTAT_DO_DU       12h   Data overrun/underrun
    HASTAT_BUS_FREE    13h   Unexpected Bus Free
    HASTAT_PHASE_ERR   14h   Target Bus phase sequence failure
    ----------------  -----  --------------------------------------------------

    Target Status     Value  Description
    ----------------  -----  --------------------------------------------------
    STATUS_GOOD        00h   No target status
    STATUS_CHKCOND     02h   Check status (sense data is in SenseArea)
    STATUS_BUSY        08h   Specified Target/LUN is busy
    STATUS_RESCONF     18h   Reservation conflict
    ----------------  -----  --------------------------------------------------

Example

The following example issues a SCSI Bus Device reset to host adapter #0, target
#5:

    SRB_BusDeviceReset MySRB;
    WORD ASPI_Status;
                .
                .
    MySRB.SRB_Cmd      = SC_RESET_DEV;
    MySRB.SRB_HaId     = 0;
    MySRB.SRB_Flags    = 0;
    MySRB.SRB_Hdr_Rsvd = 0;
    MySRB.SRB_Target   = 5;
    MySRB.SRB_Lun      = 0;
    ASPI_Status = SendASPICommand ( (LPSRB) &MySRB );
                .
    /**************************************************/
    /* Make sure all other reserved fields are zeroed */
    /* before passing the SRB to ASPI for Windows     */
    /**************************************************/
                .
    while (MySRB.SRB_Status==SS_PENDING);
                .
                .
    /**************************************************/
    /* This sample code has no error handling, time-  */
    /* out code, nor does it free up the processor.   */
    /* Your application should be more robust.        */
    /**************************************************/

ASPI Polling

Once you send an ASPI for Win16 SCSI request, you have two ways of being
notified that the SCSI request has completed. The first and simplest method is
called polling. After the command is sent, and ASPI for Win16 returns control
to your program, you can poll the status byte waiting for the command to
complete. For example, the following code segment sends a SCSI Inquiry command
to target #2.

    SRB_ExecSCSICmd6 MySRB;
    char InquiryBuffer[32];
            .
            .
    /**************************************************/
    /* Code is entered with 'MySRB' zeroed.           */
    /**************************************************/

    MySRB.SRB_Cmd        = SC_EXEC_SCSI_CMD;
    MySRB.SRB_Flags      = SRB_DIR_SCSI;
    MySRB.SRB_Target     = 2;
    MySRB.SRB_BufLen     = 32;
    MySRB.SRB_SenseLen   = SENSE_LEN;
    MySRB.SRB_BufPointer = InquiryBuffer;
    MySRB.SRB_CDBLen     = 6;
    MySRB.CDBByte[0]     = SCSI_INQUIRY;
    MySRB.CDBByte[4]     = 32;
            .
            .
    SendASPICommand ( (LPSRB) &MySRB );         // Send Inquiry command
    while ( MySRB.SRB_Status == SS_PENDING );   // Wait till it's finished

    /**************************************************/
    /* At this point, the SCSI command has completed  */
    /* with or without an error.                      */
    /**************************************************/

    if ( MySRB.SRB_Status == SS_COMP )
      ; // Command completed without error
    else
      ; // Command completed with error


Since Windows is currently a non-preemptive multitasking operating system, you
should use polling with caution. The example above is not a good example of
freeing up the processor, nor does it have any time-out handler.

ASPI Posting

Most applications use posting, rather than polling, to be notified that a SCSI
request has completed. When posting is enabled, ASPI for Win16 posts
completion by calling your callback function. For example, the following code
segment sends a SCSI Inquiry command to target #2 during the WM_CREATE
message.

    long FAR PASCAL WndProc (HWND, WORD, WORD, LONG);
    void FAR PASCAL ASPIPostProc ( LPSRB );

    HWND        PostHWND;
    HANDLE      hInstance;
                                    .
                                    .
                                    .

    // ************************************************************************
    //
    // Function:    ASPIPostProc
    //
    // Description: If POSTING is enabled,  this function is called by ASPI
    //              for Windows when the SCSI request has completed.  This
    //              sample function simply posts a message to our Window
    //              handle to indicate that the SCSI request has completed.
    //
    //  DoneSRB     This parameter points to the ASPI SCSI Request Block (SRB)
    //              which has completed.
    //
    //  Returns     Nothing
    //
    // ************************************************************************

    #ifdef WIN32
        void ASPIPostProc( PSRB_ExecSCSICmd DoneSRB )
        {
            PostMessage( PostHWND, WM_ASPIPOST, 0, (LPARAM)DoneSRB );
            return;
        }
    #else
        void _loadds __far __pascal ASPIPostProc( LPSRB DoneSRB )
        {
            PostMessage(PostHWND,WM_ASPIPOST,
                        (WORD)((SRB_ExecSCSICmd6 far *)DoneSRB)->SRB_Status,
                        (DWORD)DoneSRB);
            return;
        }
    #endif

    // ************************************************************************
    //
    // Functioin: WndProc()
    //
    // ************************************************************************

    #ifdef WIN32
        LRESULT CALLBACK WndProc( HWND hwnd, UINT message,
                                  WPARAM wParam, LPARAM lParam )
    #else
        long __far __pascal _export WndProc( HWND hwnd, UINT message,
                                             WPARAM wParam, LPARAM lParam )
    #endif
    {
        HDC          hdc;
        PAINTSTRUCT  ps;
        TEXTMETRIC   tm;
        HMENU        hMenu;
        static short cxClient, cyClient;
        int          i=0;
        WORD         status;

        #ifdef WIN32
            SRB_ExecSCSICmd *SRBPtr;
        #else
            SRB_ExecSCSICmd6 far *SRBPtr;
        #endif

        switch (message)
        {
            case WM_CREATE:
                hdc = GetDC(hwnd);
                SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
                GetTextMetrics(hdc,&tm);
                cxChar = tm.tmAveCharWidth;
                cyChar = tm.tmHeight;
                ReleaseDC(hwnd,hdc);
                rect.top = 0;
                return 0;

            case WM_PAINT:
                InvalidateRect(hwnd,NULL,TRUE);
                hdc = BeginPaint(hwnd,&ps) ;
                EndPaint (hwnd,&ps) ;
                return 0 ;

            case WM_SIZE:
                cxClient = LOWORD(lParam);
                cyClient = HIWORD(lParam);
                rect.right  = LOWORD(lParam);
                rect.bottom = HIWORD(lParam);
                UpdateWindow(hwnd);
                return 0;

            case WM_COMMAND:
                hMenu = GetMenu(hwnd);
                switch (wParam)
                {
                    case ID_FILE_EXIT:
                        SendMessage(hwnd,WM_CLOSE,0,0L);
                        return 0;

                    default:
                        // 10 = target #0
                        // 11 = target #1
                        // 12 = target #2
                        // 13 = target #3
                        // 14 = target #4
                        // 15 = target #5
                        // 16 = target #6
                        // 17 = target #7

                        // Toggle the SCSI target scan status(ENABLED/DISABELD)
                        if ((wParam >= 10 && wParam <= 17) ||
                            (wParam >= 20 && wParam <= 27))
                        {
                            status = GetMenuState(hMenu,wParam,MF_BYCOMMAND) &
                                     MF_CHECKED;
                            CheckMenuItem(hMenu,wParam,MF_BYCOMMAND |
                                          ((status) ? MF_UNCHECKED:MF_CHECKED));
                        }
                        return 0;
                }

            case WM_ASPIPOST:
                #ifdef WIN32
                    SRBPtr = (PSRB_ExecSCSICmd)lParam;
                #else
                    SRBPtr = (SRB_ExecSCSICmd6 far *)lParam;
                #endif
                DisplaySCSIID(hwnd,SRBPtr);
                ScanNextTarget(hwnd);
                return 0;

            case WM_DESTROY:
                PostQuitMessage(0) ;
                return 0 ;
        }
        return DefWindowProc (hwnd, message, wParam, lParam) ;
    }

When the post routine is called, the sample post handler fills the wParam
field and contains the status of ASPI command (SRB_Status), while the lParam
field contains a far pointer to the SRB which has completed.

-------------------------------------------------------------------------------

Miscellaneous

This section contains important information about using ASPI for Win16.

*   All structures must be "packed" onto byte aligments.  To do this using
    Microsoft compilers use the "#pragma pack(1)" command within your source
    code or use the /Zp1 command line option.  Please check your documentation
    for the proper commands or pragmas for other compilers.

*   All of the ASPI functions use 'C' style name decoration.  If using a
    'C++' compiler then make sure to include your function prototypes within
    an "extern "C" {}" block.

*   Your ASPI for Win16 program should never exit with pending SCSI I/Os.
    Doing so could lead to system instability. Send an ASPI Abort command if
    necessary.

*   Your SRBs and data buffers must be in page-locked memory. Most SCSI host
    adapters are bus masters. This means that the data buffer must not move
    while the transfer is taking place. We recommend that you allocate your
    buffers using GlobalAlloc and lock it first with GlobalLock and then with
    GlobalPageLock. This technique can overcome some quirks that Windows 3.x
    seems to have with locking down buffers.

    Note: This is no longer a requirement if you are writing to the ASPI for
          Win32 interface.

*   At a minimum, all ASPI for Win16 managers must support transfers of 64
    KBytes or less. It is not possible for all SCSI host adapters to transfer
    data blocks larger than this. If the ASPI manager cannot support the
    requested transfer size, the SS_BUFFER_TOO_BIG error is returned from the
    SendASPICommand routine. No posting occurs. If this happens, break the
    transfer down into 64 KByte transfers or less. For maximum compatibility,
    we recommend that you do not request transfer sizes larger than 64 KBytes.

*   Do not forget to support the SS_ASPI_IS_BUSY return status when sending a
    SCSI command. Under extreme loads, some ASPI for Win16 managers may not
    have enough resources to service each request.

*   If you send an ASPI request with posting enabled, and the return value is
    not equal to SS_PENDING (in other words, the request is not in progress),
    then ASPI for Win16 does not post completion to your specified window
    handle. (Refer to the specific return value for more information about why
    the request is not in progress.)

*   ASPI for Win16 is fully multitasking. You can send a request to ASPI
    while another request is executing. Make sure you use a separate SRB for
    each ASPI request. We also recommend that you only send one SRB at a time
    per target.

*   If you use posting, your post routine will most likely be called during
    interrupt time. Since most Windows routines are non-reentrant, you should
    call Windows routines with caution. You can call PostMessage during
    interrupt time, however.

*   Make sure that you zero out all reserved fields before passing the SRB to
    ASPI for Win16.

-------------------------------------------------------------------------------

Error Codes and Messages

All ASPI for Win16 calls can fail. This specification has already defined
which error codes can be returned by each ASPI routine. The following table
summarizes all of the error codes returned by ASPI routines.

    Code              Value  Description
    ----------------  -----  --------------------------------------------------
    SS_PENDING         00h   SCSI request is in progress.
    SS_COMP            01h   SCSI/ASPI request has completed without error.
    SS_ABORTED         02h   SCSI command has been aborted.
    SS_ERR             04h   SCSI command has completed with an error.
    SS_INVALID_CMD     80h   Invalid ASPI command code.
    SS_INVALID_HA      81h   Invalid Host Adapter Number.
    SS_NO_DEVICE       82h   SCSI device not installed at passed address.
    SS_INVALID_SRB     E0h   One or more parameters in the SCSI Request Block
                             (SRB) are set incorrectly.
    SS_OLD_MANAGER     E1h   One or more ASPI for DOS managers are loaded which
                             do not support ASPI for Win16.
    SS_ILLEGAL_MODE    E2h   This ASPI manager does not support this mode of
                             Windows. You typically see this error code when
                             running Windows in Real Mode.
    SS_NO_ASPI         E3h   No ASPI managers are loaded. This typically occurs
                             when a DOS ASPI manager is not resident in memory.
    SS_FAILED_INIT     E4h   For some reason, other than SS_OLD_MANAGER,
                             SS_ILLEGAL_MODE or SS_NO_ASPI, ASPI for Win16
                             could not properly initialize itself. This may be
                             caused by a lack of system resources.
    SS_ASPI_IS_BUSY    E5h   The ASPI manager cannot handle the request at this
                             time. This error generally occurs if the ASPI
                             manager is already using up all of his resources
                             to execute other requests. Try resending the
                             command later.
    SS_BUFFER_TO_BIG   E6h   The ASPI manager cannot handle the given transfer
                             size. Refer to Miscellaneous for more information.
    ----------------  -----  --------------------------------------------------
