====== DeviceIOControl en Pascal ======
===== Commandes =====
==== SMART_SEND_DRIVE_COMMAND ====
Pour pouvoir utiliser SMART il faut que le fichier soit ouvert avec GENERIC_WRITE
CreateFile(PChar(AFile), GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0)
=== Types utiles ===
== TSendCmdInParams ==
_SENDCMDINPARAMS = packed record
cBufferSize: DWORD; // Buffer size in bytes
irDriveRegs: IDEREGS; // Structure with drive register values.
bDriveNumber: BYTE; // Physical drive number to send
// command to (0,1,2,3).
bReserved: array [0..2] of BYTE; // Reserved for future expansion.
dwReserved: array [0..3] of DWORD; // For future use.
bBuffer: array [0..0] of BYTE; // Input buffer.
end;
...
_IDEREGS = packed record
bFeaturesReg: BYTE; // Used for specifying SMART "commands".
bSectorCountReg: BYTE; // IDE sector count register
bSectorNumberReg: BYTE; // IDE sector number register
bCylLowReg: BYTE; // IDE low order cylinder value
bCylHighReg: BYTE; // IDE high order cylinder value
bDriveHeadReg: BYTE; // IDE drive/head register
bCommandReg: BYTE; // Actual IDE command.
bReserved: BYTE; // reserved for future use. Must be zero.
end;
[[https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntdddisk/ns-ntdddisk-_ideregs|_IDEREGS @ Microsoft]]
== TSendCmdOutParams ==
_SENDCMDOUTPARAMS = packed record
cBufferSize: DWORD; // Size of bBuffer in bytes
DriverStatus: DRIVERSTATUS; // Driver status structure.
bBuffer: array [0..0] of BYTE; // Buffer of arbitrary length in which to store the data read from the drive.
end;
...
_DRIVERSTATUS = packed record
bDriverError: BYTE; // Error code from driver, or 0 if no error.
bIDEError: BYTE; // Contents of IDE Error register. Only valid when bDriverError is SMART_IDE_ERROR.
bReserved: array [0..1] of BYTE; // Reserved for future expansion.
dwReserved: array [0..1] of DWORD; // Reserved for future expansion.
end;
=== Constantes Utiles ===
== Commandes SMART ==
// Feature register defines for SMART "sub commands"
READ_ATTRIBUTES = $D0;
READ_THRESHOLDS = $D1;
ENABLE_DISABLE_AUTOSAVE = $D2;
SAVE_ATTRIBUTE_VALUES = $D3;
EXECUTE_OFFLINE_DIAGS = $D4;
SMART_READ_LOG = $D5;
SMART_WRITE_LOG = $d6;
ENABLE_SMART = $D8;
DISABLE_SMART = $D9;
RETURN_SMART_STATUS = $DA;
ENABLE_DISABLE_AUTO_OFFLINE = $DB;
[[https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntdddisk/ns-ntdddisk-_ideregs|Explications @ Microsoft]]
== Autres constantes ==
SMART_CMD = $B0; // Performs SMART cmd. Requires valid bFeaturesReg, bCylLowReg, and bCylHighReg
// Cylinder register defines for SMART command
SMART_CYL_LOW = $4F;
SMART_CYL_HI = $C2;
=== A etudier ===
Avec #define DRIVE_HEAD_REG 0xA0 ([[https://www.codeproject.com/script/Content/ViewAssociatedFile.aspx?rzp=%2FKB%2Fwinsdk%2FHard_drive_Information%2F%2FSMARTSRC.zip&zep=SMART%2FSmartReader.cpp&obid=16671&obtid=2&ovid=1|Trouvé ici]])
BOOL CSmartReader::IsSmartEnabled(HANDLE hDevice,UCHAR ucDriveIndex)
{
SENDCMDINPARAMS cmdInParams={0};
SENDCMDOUTPARAMS smdOutParams={0};
DWORD dwRet=0;
BOOL bRet=FALSE;
cmdInParams.cBufferSize=0;
cmdInParams.bDriveNumber =ucDriveIndex;
cmdInParams.irDriveRegs.bFeaturesReg=ENABLE_SMART;
cmdInParams.irDriveRegs.bSectorCountReg = 1;
cmdInParams.irDriveRegs.bSectorNumberReg = 1;
cmdInParams.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
cmdInParams.irDriveRegs.bCylHighReg = SMART_CYL_HI;
cmdInParams.irDriveRegs.bDriveHeadReg = DRIVE_HEAD_REG;
cmdInParams.irDriveRegs.bCommandReg = SMART_CMD;
bRet=DeviceIoControl(hDevice,SMART_SEND_DRIVE_COMMAND,&cmdInParams,sizeof(cmdInParams),&smdOutParams,sizeof(smdOutParams),&dwRet,NULL);
if(!bRet)
{
dwRet=GetLastError();
}
return bRet;
}
[[http://mojolabs.nz/posts.php?topic=105995|Source de ci-dessous]]
/*
* smart.c - reads S.M.A.R.T data from disks
*
* usage: smart driveid [--raw]
*
* !!! ADMIN privilidges is needed to read s.m.a.r.t data !!!
*
* for list of attributes and their ids: https://en.wikipedia.org/wiki/S.M.A.R.T.
*/
#include
#include
#include
#include
#include
#include
#include
#if READ_ATTRIBUTE_BUFFER_SIZE != READ_THRESHOLD_BUFFER_SIZE
#error "This code assumes READ_ATTRIBUTE_BUFFER_SIZE == READ_THRESHOLD_BUFFER_SIZE"
#endif
// only meant to read ATTRIBUTES and THRESHOLDS
static SENDCMDOUTPARAMS* read_smart_data( int drive, int smartcmd) {
char path[32];
sprintf( path, "\\\\.\\PhysicalDrive%d", drive);
HANDLE handle = CreateFile( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if(handle == INVALID_HANDLE_VALUE) return NULL;
SENDCMDINPARAMS cmd = {
.cBufferSize = sizeof(SENDCMDINPARAMS) - 1,
.irDriveRegs = {
.bFeaturesReg = smartcmd,
.bSectorCountReg = 1,
.bSectorNumberReg = 0,
.bCylLowReg = SMART_CYL_LOW,
.bCylHighReg = SMART_CYL_HI,
.bDriveHeadReg = 0xA0,
.bCommandReg = SMART_CMD,
},
.bDriveNumber = drive,
};
DWORD datasz = (sizeof(SENDCMDOUTPARAMS) - 1) + READ_ATTRIBUTE_BUFFER_SIZE;
SENDCMDOUTPARAMS* data = malloc(datasz);
DWORD writesz = 0;
if(!DeviceIoControl( handle, SMART_RCV_DRIVE_DATA, &cmd, sizeof(cmd) - 1, data, datasz, &writesz, NULL)) {
free(data);
CloseHandle(handle);
return NULL;
}
CloseHandle(handle);
return data;
}
static bool check_smart( const char* name, SENDCMDOUTPARAMS* data) {
if(data->DriverStatus.bDriverError || data->DriverStatus.bIDEError) {
printf( "Error while reading S.M.A.R.T. %s...\n", name);
printf( "DriverError = %d\n", data->DriverStatus.bDriverError);
printf( "IDEError = %d\n", data->DriverStatus.bIDEError);
return false;
}
return true;
}
static void print_raw( const char* name, void* attrs) {
printf("\n------------- RAW %9s DATA --------------\n", name);
for( uint8_t *p = attrs, *end = attrs + READ_ATTRIBUTE_BUFFER_SIZE; p < end; p += 16) {
printf( "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]
);
}
}
/*
* format of S.M.A.R.T attribute/threshold data:
*
* size of attribute/threshold data is normally 512 bytes (1 sector).
* READ_ATTRIBUTE_BUFFER_SIZE and READ_THRESHOLD_BUFFER_SIZE holds actual size if it ever changes.
*
*
* OFFS SIZE DESC
* 0 2 ?
* 2 12 attribute/threshold
* ...
* all attributes/thresholds from here
*
*
* format of S.M.A.R.T attribute:
*
* OFFS SIZE DESC
* 0 1 type
* 1 2 flags
* 3 1 value
* 4 1 worst
* 5 6 raw value (48-bits big-endian)
* 11 1 ?
*
* if type==0 no attribute in that slot
*
* the raw value is vendor specific, and for certain attributes that denote time or temperature further calculations are needed to display correctly.
*
*
* format of S.M.A.R.T threshold:
*
* OFFS SIZE DESC
* 0 1 type
* 1 1 threshold
* 2 10 ?
*
* if type==0 no threshold in that slot
*
*/
static void print_smart( void* attrs, void* thresh) {
printf( "\nID FLAG VALUE WORST THRESH RAW\n");
uint8_t* aend = attrs + READ_ATTRIBUTE_BUFFER_SIZE;
uint8_t* t = thresh;
for( uint8_t *a = attrs + 2; a < aend; a += 12) {
if(*a) {
uint8_t id = *a;
uint16_t flags = *(uint16_t*)(a+1);
uint8_t value = a[3];
uint8_t worst = a[4];
uint8_t threshold = t ? (t[a - (uint8_t*)attrs] ? t[(a - (uint8_t*)attrs) + 1] : 0) : 0;
uint8_t raw[8] = {0};
// reverse byte order
for( int i=5; i<11; i++) raw[i-5] = a[i];
printf( "%3d 0x%04x %03d %03d %03d %" PRIu64 "\n", id, flags, value, worst, threshold, *((uint64_t*)raw) & 0xFFFFFFFFFFFFL);
}
}
}
int main( int argc, const char* argv[argc]) {
if(argc == 1) {
printf( "usage: smart driveid [--raw]\n");
return 0;
}
int drive = -1;
bool showraw = false;
for( int i=1; ibBuffer);
if(tdata) print_raw( "THRESHOLD", tdata->bBuffer);
}
print_smart( adata->bBuffer, tdata ? tdata->bBuffer : NULL);
free(adata);
free(tdata);
return 0;
}
----
SENDCMDINPARAMS stCIP={0};
SENDCMDOUTPARAMS stCOP={0};
DWORD dwRet=0;
BOOL bRet=FALSE;
BYTE szAttributes[sizeof(SENDCMDOUTPARAMS) + READ_ATTRIBUTE_BUFFER_SIZE - 1];
stCIP.cBufferSize=0;
stCIP.bDriveNumber = drive;
stCIP.irDriveRegs.bFeaturesReg= 0xD0;
stCIP.irDriveRegs.bSectorCountReg = 1;
stCIP.irDriveRegs.bSectorNumberReg = 1;
stCIP.irDriveRegs.bCylLowReg = 0x4F;
stCIP.irDriveRegs.bCylHighReg = 0xC2;
stCIP.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
stCIP.irDriveRegs.bCommandReg = 0xB0;
bRet=DeviceIoControl(handle,SMART_RCV_DRIVE_DATA,&stCIP,sizeof(stCIP),szAttributes,sizeof(SENDCMDOUTPARAMS)+ READ_ATTRIBUTE_BUFFER_SIZE - 1,&dwRet,NULL);
//std::cout<<"output="<
Projet Delphi : [[https://www.delphipraxis.net/97172-s-m-r-t-figured-out.html]]
=== Sources ===
* [[https://docs.microsoft.com/en-us/previous-versions/windows/hardware/drivers/ff566206(v%3Dvs.85)]]
====== Sources & Ressources======
* [[https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol|DeviceIOControl chez Doc Microsoft]]
* [[https://www.smartmontools.org/static/doxygen/atacmds_8cpp_source.html]]
* [[https://www.delphipraxis.net/97172-s-m-r-t-figured-out.html]]