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
jwawinioctl.pas
  _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;

_IDEREGS @ Microsoft

TSendCmdOutParams
jwawinioctl.pas
  _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
jwawinioctl.pas
// 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;

Explications @ Microsoft

Autres constantes
jwawinioctl.pas
  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 (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;
}

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: <a href="https://en.wikipedia.org/wiki/S.M.A.R.T." target="_blank">https://en.wikipedia.org/wiki/S.M.A.R.T.</a>
  */
#include <windows.h>
#include <ntdddisk.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
 
#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; i<argc; i++) {
		if(!strcmp( argv[i], "--raw"))
			showraw = true;
		else {
			drive = strtol( argv[1], NULL, 10);
			if(drive < 0) {
				fprintf( stderr, "error: invalid drive id (%d)\n", drive);
				return 1;
			}
		}
	}
	if(drive == -1) {
		fprintf( stderr, "error: no drive id specified\n");
		return 1;		
	}
 
	SENDCMDOUTPARAMS* adata = read_smart_data( drive, READ_ATTRIBUTES);
	SENDCMDOUTPARAMS* tdata = read_smart_data( drive, READ_THRESHOLDS);
	if(!adata) {
		fprintf( stderr, "error: unable to read S.M.A.R.T ATTRIBUTES of drive %d\n", drive);
		return 1;
	}
	if(!check_smart( "ATTRIBUTES", adata) || (tdata && !check_smart( "THRESHOLDS", tdata))) {
		free(adata);
		free(tdata);
		return 1;
	}	
 
	if(showraw) {
		print_raw( "ATTRIBUTE", adata->bBuffer);
		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="<<dwRet<<std::endl;
	//printf("\n");

Projet Delphi : https://www.delphipraxis.net/97172-s-m-r-t-figured-out.html

Sources

Sources & Ressources

Vous pourriez laisser un commentaire si vous étiez connecté.