Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
|
prog:lazarus:cas:disks:deviceiocontrol [25/03/2020 18:59] thierry [A etudier] |
prog:lazarus:cas:disks:deviceiocontrol [11/04/2023 16:42] (Version actuelle) thierry [Sources] |
||
|---|---|---|---|
| Ligne 2: | Ligne 2: | ||
| ===== Commandes ===== | ===== Commandes ===== | ||
| ==== SMART_SEND_DRIVE_COMMAND ==== | ==== SMART_SEND_DRIVE_COMMAND ==== | ||
| + | <note important>Pour pouvoir utiliser SMART il faut que le fichier soit ouvert avec GENERIC_WRITE | ||
| + | <code pascal> | ||
| + | CreateFile(PChar(AFile), GENERIC_READ or GENERIC_WRITE, | ||
| + | FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0) | ||
| + | </code> | ||
| + | </note> | ||
| === Types utiles === | === Types utiles === | ||
| == TSendCmdInParams == | == TSendCmdInParams == | ||
| Ligne 76: | Ligne 82: | ||
| === A etudier === | === 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]]) | ||
| <code c++> | <code c++> | ||
| BOOL CSmartReader::IsSmartEnabled(HANDLE hDevice,UCHAR ucDriveIndex) | BOOL CSmartReader::IsSmartEnabled(HANDLE hDevice,UCHAR ucDriveIndex) | ||
| Ligne 102: | Ligne 109: | ||
| } | } | ||
| </code> | </code> | ||
| + | [[http://mojolabs.nz/posts.php?topic=105995|Source de ci-dessous]] | ||
| + | <code c++> | ||
| + | /* | ||
| + | * 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; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ---- | ||
| + | <code c++> | ||
| + | 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"); | ||
| + | |||
| + | </code> | ||
| + | |||
| + | Projet Delphi : [[https://www.delphipraxis.net/97172-s-m-r-t-figured-out.html]] | ||
| === Sources === | === Sources === | ||
| Ligne 108: | Ligne 323: | ||
| - | ===== Sources ===== | + | ====== Sources & Ressources====== |
| * [[https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol|DeviceIOControl chez Doc Microsoft]] | * [[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]] | ||