Using SendASPI32Command()

First off, I'll include some code snippits about how to query for Host Adapter Information.


First, we need the proper SRB. Looking through wnaspi32.h, we'll notice that SRB_HAInquiry appears to be the proper one. Once we declare it, we'll need to populate its members with the proper data, and finally, send it.

Of course, before we can do any of this, we'll have needed to initialize the ASPI and make sure Windows has it loaded. Let's do that first:

// MSVC++

/* Global Variables */
HINSTANCE hWNASPI32; // Handle to ASPI for Win32

/* Function Prototypes for WNASPI32.DLL */
DWORD (*GetASPI32SupportInfo)(void);
DWORD (*SendASPI32Command)(LPSRB);
BOOL (*FreeASPI32Buffer)( PASPI32BUFF );
BOOL (*TranslateASPI32Address)( PDWORD, PDWORD );

// Then, inside your Main/WinMain function:

/* Load Real ASPI Layer, WNASPI32.DLL */
hWNASPI32 = LoadLibrary("WNASPI32.DLL");

if(!hWNASPI32) /* If Load Failed */
MessageBox(NULL, TEXT("LoadLibrary(\"WNASPI32.DLL\") Failed."), TEXT("MYSCSIAPP Failure!"), MB_ICONERROR);
return FALSE;

GetASPI32SupportInfo = (DWORD (*)(void)) GetProcAddress(hWNASPI32,"GetASPI32SupportInfo");
SendASPI32Command = (DWORD (*)(void *)) GetProcAddress(hWNASPI32,"SendASPI32Command");
GetASPI32Buffer = (BOOL (*)(void *)) GetProcAddress(hWNASPI32,"GetASPI32Buffer");
FreeASPI32Buffer = (BOOL (*)(void *)) GetProcAddress(hWNASPI32,"FreeASPI32Buffer");
TranslateASPI32Address = (BOOL (*)(void *)) GetProcAddress(hWNASPI32,"TranslateASPI32Address");

if( !GetASPI32SupportInfo || !SendASPI32Command )
MessageBox(NULL, TEXT("Cannot find critical WNASPI32.DLL Functions!"), TEXT("Fatal BSASPI32 Error!"), MB_ICONERROR);
return FALSE;


The 2 functions we absolutely need are GetASPI32SupportInfo() and SendASPI32Command(). If they're not found, we can't go any further. Older ASPIs only had these 2 functions. Newer ones have all 5.

So, now we have access to the functions. We've now got the tools to interact with SCSI devices!

Now, let's use GetASPI32SupportInfo() to figure out what our SCSI subsystem looks like.

DWORD SupportInfo;
SupportInfo = GetASPI32SupportInfo();
HACount = HIBYTE(LOWORD(SupportInfo));
ASPIStatus = LOBYTE(LOWORD(SupportInfo));
// TODO:
// If ASPIStatus != SS_COMP and != SS_NO_ADAPTERS, you must handle the error.

That's it! We now know how many SCSI Host Adapters are in our system, and we know the Status of the ASPI API. We're all ready to query a Host Adapter to find out more about what's connected to that system.

To do that, we need the use of the SRB_HAInquiry struct from wnaspi32.h

typedef struct // Offset
{ // HX/DEC
BYTE SRB_Cmd; // 00/000 ASPI command code = SC_HA_INQUIRY
BYTE SRB_Status; // 01/001 ASPI command status byte
BYTE SRB_HaId; // 02/002 ASPI host adapter number
BYTE SRB_Flags; // 03/003 ASPI request flags
DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0
BYTE HA_Count; // 08/008 Number of host adapters present
BYTE HA_SCSI_ID; // 09/009 SCSI ID of host adapter
BYTE HA_ManagerId[16]; // 0A/010 String describing the manager
BYTE HA_Identifier[16]; // 1A/026 String describing the host adapter
BYTE HA_Unique[16]; // 2A/042 Host Adapter Unique parameters
WORD HA_Rsvd1; // 3A/058 Reserved, MUST = 0
SRB_HAInquiry, *PSRB_HAInquiry, FAR *LPSRB_HAInquiry;

Let's consider for a moment that HACount from the SupportInfo section of our code returned 2. That means it's safe to say that we can query Host Adapter #1. We'll do that now.

SRB_HAInquiry HAInquiry; // Create a data struct for our command.
memset( &HAInquiry, 0, sizeof(SRB_HAInquiry) ); // Zero out the SRB
HAInquiry.SRB_HaId = 1;
SendASPI32Command( (LPSRB)&HAInquiry ); // Give it a pointer to our packet

Now, the ASPI manager will query Host Adapter 1 with the packet we constructed. Next we'll check the SRB_Status byte to determine if it succeeded or not. ASPI will set it to SS_COMP if it succeeded.

if( HAInquiry.SRB_Status != SS_COMP )
{ //Handle errors here

We've now got access to all kinds of information about our host adapter, such as its SCSI ID (usually 7), the manager ID("ASPI for Win32"), the Host Identifier(Adaptec, Tekram, etc.), and some unique parameters. Feel free to experiment and view these strings and values. For information about the important Unique parameters, consult Adaptec's ASPI Technical Reference (aspi32.pdf).

So far all we've been talking to is the ASPI interface itself and the Host Adapter. Now we'll fiddle around with sending a packet to a particular ID on the Host Adapter.

We'll need to send a SC_GET_DEV_TYPE to all SCSI IDs on the bus to determine exactly what's connected. The header file scsidefs.h and the ASPI Technical Reference will give you a listing of those.

I happend to know that my Plextor UltraPlex 40x CD-ROM is hooked up to SCSI ID 6. Normally you'll want to query every ID inside every adapter, but since this is just an example, I'll leave it up to you to modify the code for it.

typedef struct // Offset
{ // HX/DEC
BYTE SRB_Cmd; // 00/000 ASPI command code = SC_GET_DEV_TYPE
BYTE SRB_Status; // 01/001 ASPI command status byte
BYTE SRB_HaId; // 02/002 ASPI host adapter number
BYTE SRB_Flags; // 03/003 Reserved, MUST = 0
DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0
BYTE SRB_Target; // 08/008 Target's SCSI ID
BYTE SRB_Lun; // 09/009 Target's LUN number
BYTE SRB_DeviceType; // 0A/010 Target's peripheral device type
BYTE SRB_Rsvd1; // 0B/011 Reserved, MUST = 0

Here's our SRB for SC_GET_DEV_TYPE. Notice we have to set the command, HA number, the ID, and the LUN (Logical Unit Number). Let's do that.

memset( &GDEVBlock, 0, sizeof( SRB_GDEVBlock ); // Zero out all fields
GDEVBlock.SRB_HaId = 1; // Host Adapter
GDEVBlock.SRB_Target = 6; // SCSI ID

SendASPI32Command( (LPSRB)&GDEVBlock );

// Now, check the SRB_Status field to see if it completed properly with a SS_COMP.
if( GDEVBlock.SRB_Status != SS_COMP )
{ // Handle Error Code
else // No error
if( GDEVBlock.SRB_DeviceType == DTYPE_CDROM )
{ // It found it!
printf("I found a CD-ROM on Host Adapter %d, at SCSI ID %d",
GDEVBlock.SRB_HaId, GDEVBlock.SRB_Target );


Good stuff, huh? Well, next time, we'll take a look at what it takes to create an ASPI 'spy'. We'll be making a DLL that other programs such as FireBurner can hook into and log their commands. Our ASPI 'spy' will, of course, pass the commands to the real ASPI Layer once we've logged the information, so it'll be seamless. This will give us a better idea about how to command a SCSI bus and the devices it connects.

Until then, play around with some code that will initialize ASPI, and scan all host adapters and all IDs, and print out devices as it finds it. Have fun!

<< Prev Next