The purpose of this document is to cover the second biggest hurdle a developer encounters when tasked with using DTB – this hurdle can be summarized by the question “Can you get me started on some of the most common API, i.e. the APIs that ‘everyone uses’?”. (By the way, the first hurdle you have to overcome is ‘How do I link DTB to my development environment?’, and our document “Getting Started” covers those details).
As you can imagine, the very first task every developer is hit with is “What devices are there on my system?”, and the most common APIs are those that help you determine exactly what devices are on your system. So let’s jump right into the API.
API #1: VCSCSIGetDLLVersion
The first thing we recommend is that you make sure you are using the library version you are expecting. The prototype for the API to get the version is
int VCSCSIGetDLLVersion();
The 32-bit integer that is returned is broken down into 3 parts: 2-digit year, followed by a 2-digit month, followed by a 2-digit day. For example, the version of our 9.2.0 library is 150811 (so the year is 2015, the month is August, and the Day is 11). It is important to have the version you are expecting as many times later versions have added functionality.
API #2: VCSCSIHostAdapterCount
As part of the device discovery process, you will need to know how many host adapters your system has. Many systems come with either two or four built in adapters, so do not be surprised by how large the number of adapters your system might have. For example, you may install a SAS adapter on your system, and VCSCSIHostAdapterCount might tell you that there are 5 host adapters on your system – this is most likely because there are four built in adapters plus the one SAS card you installed. The prototype for the API to retrieve the number of host adapters is
int VCSCSIHostAdapterCount();
The 32-bit integer that is returned is the number of host adaptors on your system – this number will NEVER be zero! If you should get a return value of zero, the most likely cause of this error condition is that the STB drivers have not been installed correctly on your system.
We will be building up the code to determine devices are on your system – the strategy is to poll all possible host adaptors, followed by all possible targets, followed by all possible luns.
Our “polling” code at this point looks like:
int nNumHBA = 0;
nNumHBA = VCSCSIHostAdapterCount();
for (int nHA = 0;nHA < nNumHBA;nHA++)
{
//We will fill this in as we go along
}
API #3: VCSCSITargetCount
The general strategy for doing device discovery is basically to “poll” every host adapter, for every target, and for every lun. But an important issue arises – how many potential targets does my host adaptor have? Should I “poll” targets 0 – thru – 7, or possibly 0 – thru – 15, or even 0 – thru – 127? What is the right number? This is why your call VCSCSITargetCount – it returns the maximum number of targets that you should “poll”. It is important to realize that this “target count” is NOT how many actual targets are connected to the host adaptor – it is merely an upper bound to the target number that a device can have that is connected to the host adaptor. The prototype for the API to retrieve the upper bound for the target count is
int VCSCSITargetCount(int nHostAdaptorNumber);
Notice you pass in an integer value nHostAdaptorNumber, and for this host adaptor, the API returns the target count for the specified host adaptor. Common values returned by this API are 8, 16, 128, 1024, and 2048 (but you may get a value other than these, depending on your host adaptor).
Our “polling” code at this point looks like:
int nNumHBA = 0;
int nNumTargets = 0;
nNumHBA = VCSCSIHostAdapterCount();
for (int nHA = 0;nHA < nNumHBA;nHA++)
{
nNumTargets = VCSCSITargetCount(nHA);
for (int nTid = 0;nTid < nNumTargets;nTid++)
{
for (int nLun = 0;nLun < 8;nLun++)
{
//We will fill this in as we go along
}
}
}
NOTE: In the above code, we are polling from lun 0 to lun 7. In some cases, you may have to poll on the lun values up to say 255.
At this point we have our “polling code” written, which is basically the “for loops” to determine if there is in fact a device at address (nHA:nTid:nLun). The best API to call to figure out whether or not a device is at address (nHA:nTid:nLun) is to call API VCSCSIGetDeviceType. We cover that next.
API #4: VCSCSIGetDeviceType
The return value from VCSCSIGetDeviceType is the standard SCSI Device Type values. The most common values are
0 – Disk Drive
1 – Tape Drive
5 – CD-ROM
8 – Library
And there are many others. The prototype for the API that returns the SCSI Device Type value is
int VCSCSIGetDeviceType(int nHA, int nTID, int nLun);
The return value is the SCSI Device Type value. There are two cases that you must consider – one is that there is an actual device at the address (nHA:nTid:nLun), while the second is that there is NOT a device at that address. In the event there is NO device at that address, the return value from VCSCSIGetDeviceType is -1. Another thing to be aware of is the SCSI Device Type is a 5-bit value, so it’s maximum value can only be 31. Please note that the below code checks for this condition
Our “polling” code at this point looks like:
int nNumHBA = 0;
int nNumTargets = 0;
int nDeviceType = -1;
nNumHBA = VCSCSIHostAdapterCount();
for (int nHA = 0;nHA < nNumHBA;nHA++)
{
nNumTargets = VCSCSITargetCount(nHA);
for (int nTid = 0;nTid < nNumTargets;nTid++)
{
for (int nLun = 0;nLun < 8;nLun++)
{
nDeviceType = VCSCSIGetDeviceType(nHA,nTid,nLun);
if (nDeviceType >= 0 && nDeviceType <= 31)
{
//We have found a device! We will fill this in as we go along
}
}
}
}
API #5: VCSCSIGetVendor
Use API VCSCSIGetVendor to retrieve the 8-byte VENDOR from the inquiry data.
The prototype for the API to retrieve the VENDOR information is
int VCSCSIGetVendor(int nHA, int nTid, int nLun, char * p_sVendorString);
Notice the API takes the address of the device (i.e. the host adaptor number, the target number, and the lun number). The parameter p_sVendorString is a pointer to a character array. It is important that it is a character array, and that it have atleast 9 characters to the array (the 9th character is needed to null terminate the string). For those working in MFC, do NOT pass in a pointer to a CString object.
The return value is a 32-bit integer with
Return value of 1 is success
Any other return value means a failure occurred, with the most likely failure being the device is not there.
API #6: VCSCSIGetProduct
Use API VCSCSIGetProduct to retrieve the 16-byte PRODUCT from the inquiry data.
The prototype for the API to retrieve the PRODUCT information is
int VCSCSIGetProduct(int nHA, int nTid, int nLun, char * p_sProductString);
Notice the API takes the address of the device (i.e. the host adaptor number, the target number, and the lun number). The parameter p_sProductString is a pointer to a character array. It is important that it is a character array, and that it have atleast 17 characters to the array (the 17th character is needed to null terminate the string). For those working in MFC, do NOT pass in a pointer to a CString object.
The return value is a 32-bit integer with
Return value of 1 is success
Any other return value means a failure occurred, with the most likely failure being the device is not there.
API #7: VCSCSIGetVersion
Use API VCSCSIGetVersion to retrieve the 4-byte VERSION from the inquiry data. This 4-byte version is the firmware version.
The prototype for the API to retrieve the VERSION information is
int VCSCSIGetVersion(int nHA, int nTid, int nLun, char * p_sVersionString);
Notice the API takes the address of the device (i.e. the host adaptor number, the target number, and the lun number). The parameter p_sVersionString is a pointer to a character array. It is important that it is a character array, and that it have atleast 5 characters to the array (the 5th character is needed to null terminate the string). For those working in MFC, do NOT pass in a pointer to a CString object.
The return value is a 32-bit integer with
Return value of 1 is success
Any other return value means a failure occurred, with the most likely failure being the device is not there.
Let’s now return back to our “polling” code sample and put in calls to VCSCSIGetVendor, VCSCSIGetProduct, and VCSCSIGetVersion.
Our “polling” code at this point looks like:
int nNumHBA = 0;
int nNumTargets = 0;
int nDeviceType = -1;
int nRetValue = 0;
char cVendor[9];
char cProduct[17];
char cVersion[5];
nNumHBA = VCSCSIHostAdapterCount();
for (int nHA = 0;nHA < nNumHBA;nHA++)
{
nNumTargets = VCSCSITargetCount(nHA);
for (int nTid = 0;nTid < nNumTargets;nTid++)
{
for (int nLun = 0;nLun < 8;nLun++)
{
nDeviceType = VCSCSIGetDeviceType(nHA,nTid,nLun);
if (nDeviceType >= 0 && nDeviceType <= 31)
{
//We have found a device!
//Get Vendor
memset(&cVendor[0],0,sizeof(cVendor));
nRetValue = VCSCSIGetVendor(nHA,nTid,nLun,&cVendor[0]);
if (nRetValue != 1)
{
continue;
}
//Get Product
memset(&cProduct[0],0,sizeof(cProduct));
nRetValue = VCSCSIGetProduct(nHA,nTid,nLun,&cProduct[0]);
if (nRetValue != 1)
{
continue;
}
//Get Version
memset(&cVersion[0],0,sizeof(cVersion));
nRetValue = VCSCSIGetVersion(nHA,nTid,nLun,&cVersion[0]);
if (nRetValue != 1)
{
continue;
}
}
}
}
}
API #8: VCSCSIInquiry
The API VCSCSIInquiry retrieves the entire inquiry data from the device. This inquiry data has a lot of information, including the device type, the VENDOR, the PRODUCT, and the VERSION.
The prototype for the API to retrieve the entire inquiry data is
int VCSCSIInquriy(int nHA,int nTid,int nLun,BYTE * p_cInquiryData);
Notice the API takes the address of the device (i.e. the host adaptor number, the target number, and the lun number). The parameter p_cInquiryData is a pointer to a BYTE array. It is important that it is a BYTE array, and that it have atleast 64 characters to the array. For those working in MFC, do NOT pass in a pointer to a CString object.
NOTE: Eventhough not all devices return 64 BYTEs of inquiry data, it is important that your array be 64 BYTEs long!
NOTE: The inquiry data must NOT be treated as a string! There will be many BYTEs that have a value of 0
The return value is a 32-bit integer with
Return value of 1 is success
Any other return value means a failure occurred, with the most likely failure being the device is not there.
Here is an example:
int nHA =4,nTid = 5,nLun = 0;
BYTE cInquiryData[64] = {0};
int nRetValue = 0;
nRetValue = VCSCSIInquiry(nHA,nTid,nLun,&cInquiryData[0]);
API #9: VCSCSITUR
The API VCSCSITUR issues the “Test Unit Ready” command.
The prototype for the API that issues the “Test Unit Ready” command is
int VCSCSITUR(int nHA,int nTid,int nLun);
Notice the API takes the address of the device (i.e. the host adaptor number, the target number, and the lun number).
The return value is a 32-bit integer with
Return value of 0 is success
Any other return value means a failure occurred, with the most likely failure being the device is not there OR the device responded with a check-condition.
Here is an example:
int nHA =4,nTid = 5,nLun = 0;
int nRetValue = 0;
nRetValue = VCSCSITUR(nHA,nTid,nLun);
//You MUST check the return value!
If (nRetValue == 0)
{
//The device is online
}
API #10: VCSCSIUserCdb
The API VCSCSIUserCdb is one of our most used and powerful API – it gives you full control on issuing any SCSI CDB you want. You have to be careful with this API since with this control also comes the capability to issue a CDB that modifies the functionality of your drive, or even worst, renders the drive inoperable!
The prototype for the API that allows you to issue any SCSI CDB is
int VCSCSIUserCdb int nHA,int nTid,int nLun, BYTE * p_cCDB, int nCDBLength, int nDataDir, long lDataLength, int nWhichBuffer);
Let’s cover these parameters:
nHA, nTid, nLun:
The address (host bus adapter, target, and LUN) of the device you want to send the CDB to.
p_cCDB:
A pointer to an array of BYTES which contain your CDB bytes
nCDBLength:
The length of the CDB – this value must be either 6, 10, 12, or 16.
nDataDir:
0 = data goes out from the HBA to the target
1 = data comes in from the target to the HBA
nDataLength:
The size (if any) of any data that will be transferred to/from the device. By way of an example, if you are writing one block of data (with a block size of 512) then nDataLength should be set to 512 (since you will be transferring 512 bytes of data to the device. On the other hand, if you are requesting 64-bytes of inquiry data from the drive, you should set nDataLength to 64. The following is a technical point, but if your CDB has embedded an Allocation Length value, then nDataLength must match this Allocation Length. Please see the example below.
NOTE: If no data will be transferred to/from the device, then you should set nDataLength to 0.
nWhichBuffer:
The API uses two different buffers to transfer data to/from the device. nWhichBuffer simply tells the API which buffer you wish the API to use.
0=use DTB buffer 0
1=use DTB buffer 1
IMPORTANT: If data is being transferred to the device, you MUST fill in the appropriate buffer (either 0 or 1) by first calling API VCSCSILoadBuffer. If data is being received from the device, you MUST retrieve the data from the buffer by calling API VCSCSIGetBuffer – in our example below we will be calling VCSCSIGetBuffer.
Returns:
1 on success,
Anything other value than 1 is a failure
In our example below, we will be retrieving Log Sense data from the drive. Follow the example very closely, paying close attention to things like the CDB Length is exactly 10, that since we are retrieving data from the drive we set nDataDir to 1, and that we are telling the API to store all the Log Sense data into buffer 0, and that AFTER calling the API, we then retrieve the data from buffer 0 by calling VCSCSIGetBuffer.
Here’s the code for our example:
int nRetValue = 0;
int nHA = 4,nTid = 5,nLun = 0; //CDB goes to device 4:5:0
BYTE cLogSenseCmd[10] = {0x4D, 0, 0x40, 0, 0, 0, 0, 0xFF, 0xFF, 0};
int nCdbLength = 10;
long lDataLength = 0xFFFF;
//IMPORTANT: Embedded in bytes 7 and 8 of the CDB is the
//“Allocation Length” of 0xFFFF so we must set lDataLength to 0xFFFF
int nDataDir = 1; //0 = data goes from user out to drive,
//1 = data goes from drive to user
int nWhichBuffer = 0; //You have two buffers,
//buffer 0 and buffer 1
nRetValue = VCSCSIUserCdb(nHA,nTid,nLun,
&cLogSenseCmd[0],nCdbLength,
nDataDir,lDataLength,nWhichBuffer);
//You MUST check the return code from the API!
if (nRetValue == 1)
{
//NOW the data is in buffer 0 – so now have to retrieve
//the data from the buffer. We are only going to get the
//first 4096 bytes.
BYTE cMyLogSenseData[4096];
VCSCSIGetBuffer(nWhichBuffer,4096,&cMyLogSenseData[0]);
}