SMART functionality is implemented as a "pass through" mechanism whereby the application sets up the IDE registers in a structure and passes it to the driver through the DeviceIoControl API. The registers are checked for validity and passed to the IDE port driver for execution. Any error from the IDE interface is returned to the application.
SMART stands for Self-Monitoring Analysis and Reporting Technology. You can find more information on SMART in the document SFF8035 version 1.0.
DFP stands for Disk Fault Prediction, and was an early naming of SMART. As you can see, the acronym still occurs in command names.
The interface to the driver is via the Win32® DeviceIoControl API function. Before an application can call the DeviceIoControl API, a handle to the target driver must be obtained by a call to the CreateFile API. Under Windows 95, the driver will be automatically loaded if it is placed it in the \Windows\System\IOsubsys directory, so it will not be necessary for application software to load it.
The DeviceIoControl API is documented in the Win32 online reference guide as follows:
The DeviceIoControl function sends a control code directly to a device driver, causing the given device to perform the specified operation.
hDevice
Identifies the device. The CreateFile function returns this handle.
dwIoControlCode
Specifies the control code for the operation. This value identifies the specific operation to be performed and the type of device on which the operation is to be performed. The following values are defined for this driver.
Value | Meaning |
DFP_GET_VERSION (0x00074080) | Gets the version and revision number of the driver. |
DFP_SEND_DRIVE_COMMAND (0x0007c084) | Sends a command to a drive. Sends a command to a drive that sends data. If a command is sent to a drive that does not send data, no data is transferred. |
DFP_RECEIVE_DRIVE_DATA (0x0007c088) | Sends a command to the drive that returns data. |
Points to a buffer containing the data required to perform the operation. This parameter can be NULL if the dwIoControlCode parameter specifies an operation that does not require input data.
cbInBuffer
Specifies the size, in bytes, of the lpvInBuffer buffer.
lpvOutBuffer
Points to a buffer in which the operation's output data is returned. This parameter can be NULL if the dwIoControlCode parameter specifies an operation that does not produce output data.
cbOutBuffer
Specifies the size, in bytes, of the lpvOutBuffer buffer.
lpcbBytesReturned
Points to a 32-bit variable that receives the size, in bytes, of the data returned in the lpvOutBuffer buffer.
lpoOverlapped
Points to an OVERLAPPED structure. This parameter is ignored if the hDevice handle was opened without specifying the FILE_FLAG_OVERLAPPED flag. This parameter can be NULL if you do not want overlapped operation.
OVERLAPPED (asynchronous) I/O will not be used by this driver, so this parameter should be set to NULL.
Return Value
If the function succeeds, the return value is TRUE; otherwise, it is FALSE. To get extended error information, use the GetLastError function.
The following functions are supported.
Value | Meaning |
DFP_GET_VERSION | Gets the version and revision number of the driver. |
DFP_SEND_DRIVE_COMMAND | Sends a command to a drive Sends a command to a drive that sends data. If a command is sent to a drive that does not send data, no data is transferred. |
DFP_RECEIVE_DRIVE_DATA | Sends a command to the drive that returns data. |
This function returns the version of the driver and its capabilities.
The input parameters to DeviceIoControl for this function are as follows.
Parameter | Value |
hDevice | Handle returned from CreateFile function |
DwIoControlCode | DFP_GET_VERSION (see Data Structures section for value) |
lpvInBuffer | NULL |
cbInBuffer | 0 |
LpvOutBuffer | Pointer to GetVersionOutParams structure (see Data Structures section for definition) |
cbOutBuffer | Size of the GetVersionOutParams structure |
LpcbBytesReturned | Pointer to DWORD to receive number of bytes returned |
lpoOverlapped | NULL |
This Ioctl command code is used to send a DFP command to the disk controller when the command will either transfer data to the drive or when the command will not transfer any data.
The input parameters to DeviceIoControl for this function are as follows.
Parameter | Value |
hDevice | Handle returned from CreateFile function |
DwIoControlCode | DFP_SEND_DRIVE_COMMAND (see "Data Structures" section for value) |
lpvInBuffer | Pointer to SendCommandInParams (see "Data Structures" section for definition) |
cbInBuffer | Size of SendCommandInParams (accounting for buffer length) |
LpvOutBuffer | Pointer to SendCmdOutParams structure (see "Data Structures" section for definition) |
cbOutBuffer | Size of SendCmdOutParams structure (accounting for buffer length of zero) |
LpcbBytesReturned | Pointer to DWORD to receive number of bytes returned |
lpoOverlapped | NULL |
Uses same arguments as Send Command to Drive. The field cBufferSize in SENDCMDINPARAMS is the maximum size of the expected output from the command.
This Ioctl command code is used to send a DFP command that will transfer data from the drive, or to send an Identify command to the disk controller.
The input parameters to DeviceIoControl for this function are as follows.
Parameter | Value |
hDevice | Handle returned from CreateFile function |
DwIoControlCode | DFP_RECEIVE_DRIVE_DATA (see "Data Structures" section for value) |
lpvInBuffer | Pointer to SendCommandInParams (see "Data Structures" section for definition) |
cbInBuffer | Size of SendCommandInParams |
LpvOutBuffer | Pointer to SendCmdOutParams structure (see "Data Structures" section for definition) |
cbOutBuffer | Size of SendCmdOutParams structure (accounting for buffer length) |
LpcbBytesReturned | Pointer to DWORD to receive number of bytes returned |
lpoOverlapped | NULL |
DeviceIoControl returns FALSE when it fails and TRUE when it succeeds. There are many types of failure, both from the DeviceIoControl API and from the driver. In order to determine the exact cause of an error, the application must examine three values: the return code from DeviceIoControl, the value returned from the GetLastError API, and the status value returned by the driver. Unfortunately, the current documentation for DeviceIoControl does not specify the error values from the API. Therefore, the driver will not attempt to pass error information through the GetLastError function but will use the DriverStatusStructure to pass error information back to the caller.
Before calling the IOCTL APIs in the driver, set the bDriverError member of the DriverStatusStructure to zero. Then, if the return code from DeviceIoControl is FALSE, check the bDriverError member of DriverStatusStructure. If it is nonzero, the error is from the driver. If it is zero, the error is from DeviceIoControl and GetLastError must be called to determine the exact error.
IMPORTANT: If the driver cannot validate the DriverStatus structure, it will not attempt to place error codes in it. This might occur when the address is NULL or when the length passed is not valid.
There are three IDE commands supported in this driver, ID (0xEC), ATAPI ID (0xA1), and SMART (0xB0). The "subcommands" of the SMART commands (features register values) are limited to the currently defined values (0xD0 through 0xD6, 0xD8 through 0xEF). SMART subcommand 0xD7, write threshold value, is not allowed. Any other command or SMART subcommand will result in an error being returned from the driver. Any SMART command that is not currently implemented on the target drive will result in an ABORT error from the IDE interface.
The following data structures and constants are used in the API.
Note All reserved fields should be set to zero. All structures are aligned on DWORD boundaries.
This structure contains the data returned from the Get Driver Version function.
The bit positions of the fCapabilities member are defined as follows.
Bit | Name | Value |
0 | IDE IDENTIFY Cmd | 0 = not supported, 1 = supported |
1 | ATAPI IDENTIFY Cmd | 0 = not supported, 1 = supported |
2 | SMART command set | 0 = not supported, 1 = supported |
3-31 | Reserved for future | 0 |
Bit | Meaning |
0 | IDE device is attached to primary controller, drive 0. |
1 | IDE device is attached to primary controller, drive 1. |
2 | IDE device is attached to secondary controller, drive 0. |
3 | IDE device is attached to secondary controller, drive 1. |
4 | ATAPI device is attached to primary controller, drive 0. |
5 | ATAPI device is attached to primary controller, drive 1. |
6 | ATAPI device is attached to secondary controller, drive 0. |
7 | ATAPI device is attached to secondary controller, drive 1. |
This structure contains the input parameters for the Send Command To Drive functions that will transfer data to the drive:
Note The bDriveNumber parameter corresponds to the four possible IDE drives in an ATA system. 0 is the first drive on the first controller, 1 is the second drive on the first controller, 2 is the first drive on the second controller, and 3 is the third drive on the second controller. An application can determine drive mappings by examining the bIDEDeviceMap member of the GETVERSIONOUTPARAMS structure. If the command will not result in any data being transferred to the drive, cBufferSize should be set to zero.
This structure contains the result of any command to the SMART driver.
Note cBufferSize is filled in the driver to indicate how much data was returned in the bBuffer location.
This structure contains the values to be written to the IDE registers by the driver. It is used in the SendCmdInParams structure. Although most of these values do not need to be specified by the application, allowing the application to specify them increases the flexibility of the interface. This is subject to review and revision. See each individual command for information on the required register values.
typedef struct _IDEREGS {
This section summarizes the errors that can be returned from the device I/O control
The following errors are possible on the device I/O control.
Error | Meaning |
ERROR_ACCESS_DENIED | User is not allowed to make this request. |
ERROR_NOT_ENOUGH_MEMORY | System could not issue request due to resource requirements. |
ERROR_INVALID_FUNCTION | The IOCTL code passed was invalid. |
The following driver errors are defined.
Error | Value | Meaning |
NO_ERROR | 0 | No error. |
IDE_ERROR | 1 | IDE command resulted in an error from the drive/controller. Examine bIDEStatus to determine the error. bIDEStatus will contain the value read from the IDE error register. |
INVALID_FLAG | 2 | An invalid command flag was passed. |
INVALID_COMMAND | 3 | An invalid IDE drive command register was passed. In this version of the driver, only the IDENTIFY (0xEC), ATAPI IDENTIFY (0xA1), and DFP (0xB0) commands are valid. |
INVALID_BUFFER | 4 | A buffer pointer was not valid (bad pointer, bad length, etc.). This could be any pointer passed to the driver. |
INVALID_DRIVE | 5 | The drive number was invalid. Either the drive number or the value in the drive/head register was not valid. |
ERROR_NO_MEM | 7 | Could not lock user's buffer for I/O. |
INVALID_REGISTER | 8 | Some IDE register (besides the command and drive/head register) value not valid. Examples are invalid DFP "subcommands" in the features register. |
NOT_SUPPORTED | 9 | A bit has been set in the fCmdFlags that the driver does not support, or a reserved field is nonzero. |
NO_IDE_DEVICE | 10 | No device attached to the controller that is specified by the drive number passed in the command. |
RESERVED | 11-255 | Reserved for future. |
Under Windows 95, the name of the executable is Smartvsd.vxd. This file will reside in the \Windows\System\IOsubsys directory.
© 1997 Microsoft Corporation. All rights reserved. Legal Notices.