atacmds.cpp 27.4 KB
Newer Older
ballen4705's avatar
ballen4705 committed
1
2
/*
 * atacmds.c
3
4
 * 
 * Home page of code is: http://smartmontools.sourceforge.net
ballen4705's avatar
ballen4705 committed
5
 *
ballen4705's avatar
ballen4705 committed
6
 * Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
ballen4705's avatar
ballen4705 committed
7
8
9
10
11
12
13
14
15
16
17
 * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
 * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * You should have received a copy of the GNU General Public License
 * (for example COPYING); if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
ballen4705's avatar
ballen4705 committed
18
19
20
21
22
23
 *
 * This code was originally developed as a Senior Thesis by Michael Cornwell
 * at the Concurrent Systems Laboratory (now part of the Storage Systems
 * Research Center), Jack Baskin School of Engineering, University of
 * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 * 
ballen4705's avatar
ballen4705 committed
24
25
26
27
 */

#include <stdio.h>
#include <string.h>
28
#include <errno.h>
29
#include <stdlib.h>
ballen4705's avatar
ballen4705 committed
30
#include "atacmds.h"
31
#include "utility.h"
32

33
const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.53 2003/01/17 12:20:24 ballen4705 Exp $" ATACMDS_H_CVSID UTILITY_H_CVSID;
ballen4705's avatar
ballen4705 committed
34

35
36
37
38
39
40
// These Drive Identity tables are taken from hdparm 5.2, and are also
// given in the ATA/ATAPI specs for the IDENTIFY DEVICE command.  Note
// that SMART was first added into the ATA/ATAPI-3 Standard with
// Revision 3 of the document, July 25, 1995.  Look at the "Document
// Status" revision commands at the beginning of
// http://www.t13.org/project/d2008r6.pdf to see this.
41
42
43
44
45
46
47
48
49
50
51
#define NOVAL_0			0x0000
#define NOVAL_1			0xffff
/* word 81: minor version number */
#define MINOR_MAX 0x1C
const char *minor_str[] = {			/* word 81 value: */
  "Device does not report version",		/* 0x0000	*/
  "ATA-1 X3T9.2 781D prior to revision 4",	/* 0x0001	*/
  "ATA-1 published, ANSI X3.221-1994",		/* 0x0002	*/
  "ATA-1 X3T9.2 781D revision 4",		/* 0x0003	*/
  "ATA-2 published, ANSI X3.279-1996",		/* 0x0004	*/
  "ATA-2 X3T10 948D prior to revision 2k",	/* 0x0005	*/
52
  "ATA-3 X3T10 2008D revision 1",		/* 0x0006	*/ /* SMART NOT INCLUDED */
53
  "ATA-2 X3T10 948D revision 2k",		/* 0x0007	*/
54
  "ATA-3 X3T10 2008D revision 0",		/* 0x0008	*/ 
55
56
  "ATA-2 X3T10 948D revision 3",		/* 0x0009	*/
  "ATA-3 published, ANSI X3.298-199x",		/* 0x000a	*/
57
  "ATA-3 X3T10 2008D revision 6",		/* 0x000b	*/ /* 1st VERSION WITH SMART */
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  "ATA-3 X3T13 2008D revision 7 and 7a",	/* 0x000c	*/
  "ATA/ATAPI-4 X3T13 1153D revision 6",		/* 0x000d	*/
  "ATA/ATAPI-4 T13 1153D revision 13",		/* 0x000e	*/
  "ATA/ATAPI-4 X3T13 1153D revision 7",		/* 0x000f	*/
  "ATA/ATAPI-4 T13 1153D revision 18",		/* 0x0010	*/
  "ATA/ATAPI-4 T13 1153D revision 15",		/* 0x0011	*/
  "ATA/ATAPI-4 published, ANSI NCITS 317-1998",	/* 0x0012	*/
  "ATA/ATAPI-5 T13 1321D revision 3",	        /* 0x0013	*/
  "ATA/ATAPI-4 T13 1153D revision 14",		/* 0x0014	*/
  "ATA/ATAPI-5 T13 1321D revision 1",		/* 0x0015	*/
  "ATA/ATAPI-5 published, ANSI NCITS 340-2000",	/* 0x0016	*/
  "ATA/ATAPI-4 T13 1153D revision 17",		/* 0x0017	*/
  "ATA/ATAPI-6 T13 1410D revision 0",		/* 0x0018	*/
  "ATA/ATAPI-6 T13 1410D revision 3a",		/* 0x0019	*/
  "Reserved",					/* 0x001a	*/
  "ATA/ATAPI-6 T13 1410D revision 2",		/* 0x001b	*/
  "ATA/ATAPI-6 T13 1410D revision 1",		/* 0x001c	*/
  "reserved"					/* 0x001d	*/
  "reserved"					/* 0x001e	*/
  "reserved"					/* 0x001f-0xfffe*/
};

80
81
82
83
84
// NOTE ATA/ATAPI-4 REV 4 was the LAST revision where the device
// attribute structures were NOT completely vendor specific.  So any
// disk that is ATA/ATAPI-4 or above can not be trusted to show the
// vendor values in sensible format.

85
// Negative values below are because it doesn't support SMART
86
const int actual_ver[] = { 
87
88
89
90
91
92
93
  /* word 81 value: */
  0,		/* 0x0000	WARNING: 	*/
  1,		/* 0x0001	WARNING: 	*/
  1,		/* 0x0002	WARNING: 	*/
  1,		/* 0x0003	WARNING: 	*/
  2,		/* 0x0004	WARNING:   This array 		*/
  2,		/* 0x0005	WARNING:   corresponds 		*/
94
  -3, /*<== */	/* 0x0006	WARNING:   *exactly*		*/
95
  2,		/* 0x0007	WARNING:   to the ATA/		*/
96
  -3, /*<== */	/* 0x0008	WARNING:   ATAPI version	*/
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  2,		/* 0x0009	WARNING:   listed in	 	*/
  3,		/* 0x000a	WARNING:   the 		 	*/
  3,		/* 0x000b	WARNING:   minor_str 		*/
  3,		/* 0x000c	WARNING:   array		*/
  4,		/* 0x000d	WARNING:   above.		*/
  4,		/* 0x000e	WARNING:  			*/
  4,		/* 0x000f	WARNING:   If you change 	*/
  4,		/* 0x0010	WARNING:   that one,      	*/
  4,		/* 0x0011	WARNING:   change this one	*/
  4,		/* 0x0012	WARNING:   too!!!        	*/
  5,		/* 0x0013	WARNING:	*/
  4,		/* 0x0014	WARNING:	*/
  5,		/* 0x0015	WARNING:	*/
  5,		/* 0x0016	WARNING:	*/
  4,		/* 0x0017	WARNING:	*/
  6,		/* 0x0018	WARNING:	*/
  6,		/* 0x0019	WARNING:	*/
  0,		/* 0x001a	WARNING:	*/
  6,		/* 0x001b	WARNING:	*/
  6,		/* 0x001c	WARNING:	*/
  0		/* 0x001d-0xfffe    		*/
};

120

121
const char *vendorattributeargs[] = {
122
  // 0
123
  "9,minutes",
124
125
126
127
128
  // 1
  "220,temp",
  // 2
  "9,temp",
  // NULL should always terminate the array
129
130
131
  NULL
};

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// This is a utility function for parsing pairs like "9,minutes" or
// "220,temp", and putting the correct flag into the attributedefs
// array.  Returns 1 if problem, 0 if pair has been recongized.
int parse_attribute_def(char *pair, unsigned char *defs){
  int i;

  // look along list and see if we find the pair
  for (i=0; vendorattributeargs[i] && strcmp(pair, vendorattributeargs[i]); i++);

  switch (i) {
  case 0:
    // power on time stored in minutes
    defs[9]=1;
    return 0;
  case 1:
    // attribute 220 is temperature in celsius
    defs[220]=1;
    return 0;
  case 2:
    // attribute 9 is temperature in celsius
    defs[9]=2;
    return 0;
  default:
    // pair not found
    return 1;
  }
}

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Function to return a string containing a list of the arguments in 
// vendorattributeargs[] separated by commas.  The strings themselves
// contain commas, so surrounding double quotes are added for clarity.
// This function allocates the required memory for the string and the
// caller must use free() to free it.  Returns NULL if the required
// memory can't be allocated.
char *create_vendor_attribute_arg_list(void){
  const char **ps;
  char *s;
  int len;

  // Calculate the required number of characters
  len = 1;                // At least one char ('\0')
  for (ps = vendorattributeargs; *ps != NULL; ps++) {
    len += strlen(*ps);   // For the actual argument string
    len += 2;             // For the surrounding double quotes
    if (*(ps+1))
      len += 2;           // For the ", " delimiter if required
  }

  // Attempt to allocate memory for the string
  if (!(s = (char *)malloc(len)))
    return NULL;

  // Construct the string
  *s = '\0';
  for (ps = vendorattributeargs; *ps != NULL; ps++) {
    strcat(s, "\"");
    strcat(s, *ps);
    strcat(s, "\"");
    if (*(ps+1))
      strcat(s, ", ");
  }

  // Return a pointer to the string
  return s;
}
197

ballen4705's avatar
ballen4705 committed
198

199
200
201
202
203
204
// We no longer use this function, because the IOCTL appears to return
// only the drive identity at the time that the system was booted
// (perhaps from the BIOS.  It doesn't correctly reflect the current
// state information, and for example the checksum is usually
// wrong. The replacement function follows afterwards
#if (0)
205
int ataReadHDIdentity (int device, struct hd_driveid *buf){
206
  if (ioctl(device, HDIO_GET_IDENTITY, buf)){ 
207
    perror ("Error ATA GET HD Identity Failed");
208
    return -1;
209
210
  }
  return 0;
ballen4705's avatar
ballen4705 committed
211
}
212
213
#endif

214
215
216
217
218
219
220
221
222
223
224
225
226
227

// This function computes the checksum of a single disk sector (512
// bytes).  Returns zero if checksum is OK, nonzero if the checksum is
// incorrect.  The size (512) is correct for all SMART structures.
unsigned char checksum(unsigned char *buffer){
  unsigned char sum=0;
  int i;
  
  for (i=0; i<512; i++)
    sum+=buffer[i];

  return sum;
}

228
229
// Reads current Device Identity info (512 bytes) into buf
int ataReadHDIdentity (int device, struct hd_driveid *buf){
230
  unsigned short driveidchecksum;
231
  unsigned char parms[HDIO_DRIVE_CMD_HDR_SIZE+sizeof(*buf)]=
232
233
  {WIN_IDENTIFY, 0, 0, 1,};
  
234
235
  if (ioctl(device ,HDIO_DRIVE_CMD,parms)){
    // See if device responds to packet command...
236
237
    parms[0]=WIN_PIDENTIFY;
    if (ioctl(device ,HDIO_DRIVE_CMD,parms)){
238
      syserror("Error ATA GET HD Identity Failed");
239
240
      return -1; 
    }
241
242
243
244
  }
  // copy data into driveid structure
  memcpy(buf,parms+HDIO_DRIVE_CMD_HDR_SIZE,sizeof(*buf));
  
245
#if 0
246
247
248
249
250
  // The following ifdef is a HACK to distinguish different versions
  // of the header file defining hd_driveid
#ifdef CFA_REQ_EXT_ERROR_CODE
  driveidchecksum=buf->integrity_word;
#else
251
252
253
254
255
  // Note -- the declaration that appears in
  // /usr/include/linux/hdreg.h: short words160_255[95], is WRONG.
  // It should say: short words160_255[96]. I have written to Andre
  // Hedrick about this on Oct 17 2002.  Please remove this comment
  // once the fix has made it into the stock kernel tree.
256
257
  driveidchecksum=buf->words160_255[95];
#endif
258
259
260
261
#else
  // This way is ugly and you may feel ill -- but it always works...
  {
    unsigned short *rawstructure=
262
      (unsigned short *)buf;
263
264
265
    driveidchecksum=rawstructure[255];
  }
#endif
266
  
267
268
269
270
  if ((driveidchecksum & 0x00ff) == 0x00a5 && checksum((unsigned char *)buf))
    checksumwarning("Drive Identity Structure");
  
  return 0;
271
}
ballen4705's avatar
ballen4705 committed
272

273
274
275
276
// Returns ATA version as an integer, and a pointer to a string
// describing which revision.  Note that Revision 0 of ATA-3 does NOT
// support SMART.  For this one case we return -3 rather than +3 as
// the version number.  See notes above.
ballen4705's avatar
ballen4705 committed
277
int ataVersionInfo (const char** description, struct hd_driveid *drive, unsigned short *minor){
278
  unsigned short major;
279
  int i;
280
281
  
  // get major and minor ATA revision numbers
ballen4705's avatar
ballen4705 committed
282
#ifdef __NEW_HD_DRIVE_ID
ballen4705's avatar
ballen4705 committed
283
284
  major=drive->major_rev_num;
  *minor=drive->minor_rev_num;
ballen4705's avatar
ballen4705 committed
285
#else
ballen4705's avatar
ballen4705 committed
286
287
  major=drive->word80;
  *minor=drive->word81;
ballen4705's avatar
ballen4705 committed
288
#endif
289
290
291
292
293
294
295
296
  
  // First check if device has ANY ATA version information in it
  if (major==NOVAL_0 || major==NOVAL_1) {
    *description=NULL;
    return -1;
  }
  
  // The minor revision number has more information - try there first
297
298
  if (*minor && (*minor<=MINOR_MAX)){
    int std = actual_ver[*minor];
299
    if (std) {
300
      *description=minor_str[*minor];
301
302
303
304
305
306
307
      return std;
    }
  }
  
  // HDPARM has a very complicated algorithm from here on. Since SMART only
  // exists on ATA-3 and later standards, let's punt on this.  If you don't
  // like it, please fix it.  The code's in CVS.
308
  for (i=15; i>0; i--)
309
    if (major & (0x1<<i))
310
311
312
313
314
315
316
      break;
  
  *description=NULL; 
  if (i==0)
    return 1;
  else
    return i;;
ballen4705's avatar
ballen4705 committed
317
318
}

319
// returns 1 if SMART supported, 0 if not supported or can't tell
ballen4705's avatar
ballen4705 committed
320
int ataSmartSupport(struct hd_driveid *drive){
321
  unsigned short word82,word83;
ballen4705's avatar
ballen4705 committed
322

323
  // get correct bits of IDENTIFY DEVICE structure
ballen4705's avatar
ballen4705 committed
324
#ifdef __NEW_HD_DRIVE_ID
ballen4705's avatar
ballen4705 committed
325
326
  word82=drive->command_set_1;
  word83=drive->command_set_2;
ballen4705's avatar
ballen4705 committed
327
#else
ballen4705's avatar
ballen4705 committed
328
329
  word82=drive->command_sets;
  word83=drive->word83;
ballen4705's avatar
ballen4705 committed
330
#endif
331
332
333
334

  // Note this does not work for ATA3 < Revision 6, when word82 and word83 were added
  // we should check for ATA3 Rev 0 in minor identity code...  
  return (word83 & 0x0001<<14) && !(word83 & 0x0001<<15) && (word82 & 0x0001);
ballen4705's avatar
ballen4705 committed
335
336
}

337
// returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell
ballen4705's avatar
ballen4705 committed
338
int ataIsSmartEnabled(struct hd_driveid *drive){
339
340
341
342
    unsigned short word85,word87;

  // Get correct bits of IDENTIFY DRIVE structure
#ifdef __NEW_HD_DRIVE_ID
ballen4705's avatar
ballen4705 committed
343
344
  word85=drive->cfs_enable_1;
  word87=drive->csf_default;
345
#else
ballen4705's avatar
ballen4705 committed
346
347
  word85=drive->word85;
  word87=drive->word87;
348
349
350
351
352
353
354
355
356
357
358
359
360
#endif
  
  if ((word87 & 0x0001<<14) && !(word87 & 0x0001<<15))
    // word85 contains valid information, so
    return word85 & 0x0001;
  
  // Since we can't rely word85, we don't know if SMART is enabled.
  return -1;
}


// Reads SMART attributes into *data
int ataReadSmartValues(int device, struct ata_smart_values *data){	
ballen4705's avatar
ballen4705 committed
361
  unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE]= 
362
    {WIN_SMART, 0, SMART_READ_VALUES, 1, };
ballen4705's avatar
ballen4705 committed
363
364
  
  if (ioctl(device,HDIO_DRIVE_CMD,buf)){
ballen4705's avatar
ballen4705 committed
365
    syserror("Error SMART Values Read failed");
ballen4705's avatar
ballen4705 committed
366
367
    return -1;
  }
368
369
370
371

  // copy data
  memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE,ATA_SMART_SEC_SIZE);

ballen4705's avatar
ballen4705 committed
372
  // compute checksum
373
  if (checksum((unsigned char *)data))
ballen4705's avatar
ballen4705 committed
374
    checksumwarning("SMART Attribute Data Structure");
ballen4705's avatar
ballen4705 committed
375
376
  
  return 0;
ballen4705's avatar
ballen4705 committed
377
378
379
}


380
381
382
383
384
385
386
// Reads the Self Test Log (log #6)
int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){	
  unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = 
    {WIN_SMART, 0x06, SMART_READ_LOG_SECTOR, 1,};
  
  // get data from device
  if (ioctl(device, HDIO_DRIVE_CMD, buf)){
ballen4705's avatar
ballen4705 committed
387
    syserror("Error SMART Error Self-Test Log Read failed");
388
389
    return -1;
  }
390
391
392
393

  // copy data back to the user
  memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); 

394
  // compute its checksum, and issue a warning if needed
395
  if (checksum((unsigned char *)data))
ballen4705's avatar
ballen4705 committed
396
    checksumwarning("SMART Self-Test Log Structure");
397
398
  
  return 0;
ballen4705's avatar
ballen4705 committed
399
400
}

401
402
403
404
405
406
// Reads the Error Log (log #1)
int ataReadErrorLog (int device, struct ata_smart_errorlog *data){	
  unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = 
    {WIN_SMART, 0x01, SMART_READ_LOG_SECTOR, 1,};
  
  // get data from device
407
  if (ioctl(device,HDIO_DRIVE_CMD,buf)) {
ballen4705's avatar
ballen4705 committed
408
    syserror("Error SMART Error Log Read failed");
409
410
411
    return -1;
  }
  
412
413
414
415
416
  //copy data back to user
  memcpy(data, buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE);
  
  // compute its checksum, and issue a warning if needed
  if (checksum((unsigned char *)data))
ballen4705's avatar
ballen4705 committed
417
    checksumwarning("SMART ATA Error Log Structure");
418
419
  
  return 0;
ballen4705's avatar
ballen4705 committed
420
421
422
}


423
int ataReadSmartThresholds (int device, struct ata_smart_thresholds *data){
424
425
426
427
428
  unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = 
    {WIN_SMART, 1, SMART_READ_THRESHOLDS, 1,};
  
  // get data from device
  if (ioctl(device ,HDIO_DRIVE_CMD, buf)){
ballen4705's avatar
ballen4705 committed
429
    syserror("Error SMART Thresholds Read failed");
430
431
    return -1;
  }
432
433
434

  // copy data back to user
  memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE);
435
  
436
437
  // compute its checksum, and issue a warning if needed
  if (checksum((unsigned char *)data))
ballen4705's avatar
ballen4705 committed
438
    checksumwarning("SMART Attribute Thresholds Structure");
439
440
  
  return 0;
ballen4705's avatar
ballen4705 committed
441
442
443
}


444
445
// This routine is not currently in use, and it's been marked as
// "Obsolete" in the ANSI ATA-5 spec.  So it should probably be left
446
447
// alone and unused.  If you do modify the thresholds, be sure to set
// the checksum correctly before putting the structure back!
448
449
450
451
452
453
454
int ataSetSmartThresholds ( int device, struct ata_smart_thresholds *data){	
  unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = 
    {WIN_SMART, 1, 0xD7, 1,};
  
  memcpy(buf+HDIO_DRIVE_CMD_HDR_SIZE, data, ATA_SMART_SEC_SIZE);
  
  if (ioctl(device, HDIO_DRIVE_CMD, buf)){
ballen4705's avatar
ballen4705 committed
455
    syserror("Error SMART Thresholds Write failed");
456
457
458
    return -1;
  }
  return 0;
ballen4705's avatar
ballen4705 committed
459
460
}

461
462
463
int ataEnableSmart (int device ){	
  unsigned char parms[4] = {WIN_SMART, 1, SMART_ENABLE, 0};
  
464
  if (ioctl (device, HDIO_DRIVE_CMD, parms)){
ballen4705's avatar
ballen4705 committed
465
    syserror("Error SMART Enable failed");
466
467
468
469
    return -1;
  }
  return 0;
}
ballen4705's avatar
ballen4705 committed
470

471
472
473
int ataDisableSmart (int device ){	
  unsigned char parms[4] = {WIN_SMART, 1, SMART_DISABLE, 0};
  
474
  if (ioctl(device, HDIO_DRIVE_CMD, parms)){
ballen4705's avatar
ballen4705 committed
475
    syserror("Error SMART Disable failed");
476
    return -1;
477
  }  
478
  return 0;
ballen4705's avatar
ballen4705 committed
479
480
481
}

int ataEnableAutoSave(int device){
482
483
  unsigned char parms[4] = {WIN_SMART, 241, SMART_AUTOSAVE, 0};
  
484
  if (ioctl(device, HDIO_DRIVE_CMD, parms)){
ballen4705's avatar
ballen4705 committed
485
    syserror("Error SMART Enable Auto-save failed");
486
487
488
489
    return -1;
  }
  return 0;
}
ballen4705's avatar
ballen4705 committed
490
491

int ataDisableAutoSave(int device){
492
493
494
  unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTOSAVE, 0};
  
  if (ioctl(device, HDIO_DRIVE_CMD, parms)){
ballen4705's avatar
ballen4705 committed
495
    syserror("Error SMART Disable Auto-save failed");
496
497
498
    return -1;
  }
  return 0;
ballen4705's avatar
ballen4705 committed
499
500
}

501
502
503
504
505
506
507
508
509
510
// Note that in the ATA-5 standard the Enable/Disable AutoOffline
// command is marked "OBSOLETE".  Curiously, I could not find it
// documented in ANY of the ATA specifications.  In other words, it's
// been obsolete forever. However some vendors (eg, IBM) seem to be
// using this command anyway.  For example see the IBM Travelstar
// 40GNX hard disk drive specifications page 164 Revision 1.1 22 Apr
// 2002.  This gives a detailed description of the command, although
// the drive claims to comply with the ATA/ATAPI-5 Revision 3
// standard!  The latter document makes no mention of this command at
// all, other than to say that it is "obsolete".
511
512
513
514
515
516
int ataEnableAutoOffline (int device ){	
  
  /* timer hard coded to 4 hours */
  unsigned char parms[4] = {WIN_SMART, 248, SMART_AUTO_OFFLINE, 0};
  
  if (ioctl(device , HDIO_DRIVE_CMD, parms)){
ballen4705's avatar
ballen4705 committed
517
    syserror("Error SMART Enable Automatic Offline failed");
518
519
520
521
    return -1;
  }
  return 0;
}
ballen4705's avatar
ballen4705 committed
522

523
524
// Another Obsolete Command.  See comments directly above, associated
// with the corresponding Enable command.
525
526
527
528
int ataDisableAutoOffline (int device ){	
  unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTO_OFFLINE, 0};
  
  if (ioctl(device , HDIO_DRIVE_CMD, parms)){
ballen4705's avatar
ballen4705 committed
529
    syserror("Error SMART Disable Automatic Offline failed");
530
531
532
    return -1;
  }
  return 0;
ballen4705's avatar
ballen4705 committed
533
534
535
}


536
537
538
// This function does NOTHING except tell us if SMART is working &
// enabled on the device.  See ataSmartStatus2() for one that actually
// returns SMART status.
ballen4705's avatar
ballen4705 committed
539
int ataSmartStatus (int device ){	
540
   unsigned char parms[4] = {WIN_SMART, 0, SMART_STATUS, 0};
ballen4705's avatar
ballen4705 committed
541

542
   if (ioctl(device, HDIO_DRIVE_CMD, parms)){
ballen4705's avatar
ballen4705 committed
543
     syserror("Error Return SMART Status via HDIO_DRIVE_CMD failed");
544
     return -1;
ballen4705's avatar
ballen4705 committed
545
546
547
548
   }
   return 0;
}

549
550
551
552
553
554
// If SMART is enabled, supported, and working, then this call is
// guaranteed to return 1, else zero.  Silent inverse of
// ataSmartStatus()
int ataDoesSmartWork(int device){	
   unsigned char parms[4] = {WIN_SMART, 0, SMART_STATUS, 0};
   return !ioctl(device, HDIO_DRIVE_CMD, parms);
ballen4705's avatar
ballen4705 committed
555
556
557
}


558
#ifdef HDIO_DRIVE_TASK
559
560
// This function uses a different interface (DRIVE_TASK) than the
// other commands in this file.
561
562
563
int ataSmartStatus2(int device){
  unsigned char normal_cyl_lo=0x4f, normal_cyl_hi=0xc2;
  unsigned char failed_cyl_lo=0xf4, failed_cyl_hi=0x2c;
564
  
565
  unsigned char parms[HDIO_DRIVE_TASK_HDR_SIZE]=
566
567
    {WIN_SMART, SMART_STATUS, 0, 0, 0, 0, 0};
  
568
569
570
571
  // load CL and CH values
  parms[4]=normal_cyl_lo;
  parms[5]=normal_cyl_hi;

572
  if (ioctl(device,HDIO_DRIVE_TASK,parms)){
ballen4705's avatar
ballen4705 committed
573
    syserror("Error SMART Status command via HDIO_DRIVE_TASK failed");
574
575
576
577
578
579
    return -1;
  }
  
  // Cyl low and Cyl high unchanged means "Good SMART status"
  if (parms[4]==normal_cyl_lo && parms[5]==normal_cyl_hi)
    return 0;
580
  
581
582
583
584
585
  // These values mean "Bad SMART status"
  if (parms[4]==failed_cyl_lo && parms[5]==failed_cyl_hi)
    return 1;

  // We haven't gotten output that makes sense; print out some debugging info
ballen4705's avatar
ballen4705 committed
586
  syserror("Error SMART Status command failed");
587
588
  pout("Please get assistance from %s\n",PROJECTHOME);
  pout("Register values returned from SMART Status command are:\n");
ballen4705's avatar
ballen4705 committed
589
590
591
592
593
594
595
  pout("CMD=0x%02x\n",(int)parms[0]);
  pout("FR =0x%02x\n",(int)parms[1]);
  pout("NS =0x%02x\n",(int)parms[2]);
  pout("SC =0x%02x\n",(int)parms[3]);
  pout("CL =0x%02x\n",(int)parms[4]);
  pout("CH =0x%02x\n",(int)parms[5]);
  pout("SEL=0x%02x\n",(int)parms[6]);
596
  return -1;
ballen4705's avatar
ballen4705 committed
597
}
598
599
600
601
602
603
#else
// Just a hack so that the code compiles on 
// 2.2 kernels without HDIO_DRIVE TASK support.  
// Should be fixed by putting in a call to code 
// that compares smart data to thresholds.
int ataSmartStatus2(int device){
604
  return ataSmartStatus(device);
605
606
607
}
#endif

ballen4705's avatar
ballen4705 committed
608

609
610
611
612
613
// This is the way to execute ALL tests: offline, short self-test,
// extended self test, with and without captive mode, etc.
int ataSmartTest(int device, int testtype){	
  unsigned char parms[4] = {WIN_SMART, 0, SMART_IMMEDIATE_OFFLINE};
  char cmdmsg[128],*type,*captive;
614
  int errornum;
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635

  parms[1]=testtype;

  // Set up strings that describe the type of test
  if (testtype==SHORT_CAPTIVE_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST)
    captive="captive";
  else
    captive="off-line";

  if (testtype==OFFLINE_FULL_SCAN)
    type="off-line";
  else  if (testtype==SHORT_SELF_TEST || testtype==SHORT_CAPTIVE_SELF_TEST)
    type="Short self-test";
  else 
    type="Extended self-test";

  //  Print ouf message that we are sending the command to test
  if (testtype==ABORT_SELF_TEST)
    sprintf(cmdmsg,"Abort SMART off-line mode self-test routine");
  else
    sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive);
636
  pout("Sending command: \"%s\".\n",cmdmsg);
637
638

  // Now send the command to test
639
  errornum=ioctl(device, HDIO_DRIVE_CMD, parms);
640
  if (errornum && !((testtype=SHORT_CAPTIVE_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST) && errno==EIO)){
641
    char errormsg[128];
642
    sprintf(errormsg,"Command \"%s\" failed",cmdmsg); 
ballen4705's avatar
ballen4705 committed
643
    syserror(errormsg);
644
    fprintf(stderr,"\n");
645
646
647
648
649
    return -1;
  }
  
  // Since the command succeeded, tell user
  if (testtype==ABORT_SELF_TEST)
650
    pout("Self-testing aborted!\n");
651
  else
652
    pout("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg);
653
  return 0;
ballen4705's avatar
ballen4705 committed
654
655
656
}

/* Test Time Functions */
ballen4705's avatar
ballen4705 committed
657
int TestTime(struct ata_smart_values *data,int testtype){
658
659
  switch (testtype){
  case OFFLINE_FULL_SCAN:
ballen4705's avatar
ballen4705 committed
660
    return (int) data->total_time_to_complete_off_line;
661
662
  case SHORT_SELF_TEST:
  case SHORT_CAPTIVE_SELF_TEST:
ballen4705's avatar
ballen4705 committed
663
    return (int) data->short_test_completion_time;
664
665
  case EXTEND_SELF_TEST:
  case EXTEND_CAPTIVE_SELF_TEST:
ballen4705's avatar
ballen4705 committed
666
    return (int) data->extend_test_completion_time;
667
668
669
  default:
    return 0;
  }
ballen4705's avatar
ballen4705 committed
670
671
}

672
673
674
// This function tells you both about the ATA error log and the
// self-test error log capability.  The bit is poorly documented in
// the ATA/ATAPI standard.
ballen4705's avatar
ballen4705 committed
675
676
int isSmartErrorLogCapable ( struct ata_smart_values *data){
   return data->errorlog_capability & 0x01;
ballen4705's avatar
ballen4705 committed
677
}
ballen4705's avatar
ballen4705 committed
678
679
int isSupportExecuteOfflineImmediate ( struct ata_smart_values *data){
   return data->offline_data_collection_capability & 0x01;
ballen4705's avatar
ballen4705 committed
680
}
681
682
683
684
685
686

// Note in the ATA-5 standard, the following bit is listed as "Vendor
// Specific".  So it may not be reliable. The only use of this that I
// have found is in IBM drives, where it is well-documented.  See for
// example page 170, section 13.32.1.18 of the IBM Travelstar 40GNX
// hard disk drive specifications page 164 Revision 1.1 22 Apr 2002.
ballen4705's avatar
ballen4705 committed
687
688
int isSupportAutomaticTimer ( struct ata_smart_values *data){
   return data->offline_data_collection_capability & 0x02;
ballen4705's avatar
ballen4705 committed
689
}
ballen4705's avatar
ballen4705 committed
690
691
int isSupportOfflineAbort ( struct ata_smart_values *data){
   return data->offline_data_collection_capability & 0x04;
ballen4705's avatar
ballen4705 committed
692
}
ballen4705's avatar
ballen4705 committed
693
694
int isSupportOfflineSurfaceScan ( struct ata_smart_values *data){
   return data->offline_data_collection_capability & 0x08;
ballen4705's avatar
ballen4705 committed
695
}
ballen4705's avatar
ballen4705 committed
696
697
int isSupportSelfTest (struct ata_smart_values *data){
   return data->offline_data_collection_capability & 0x10;
ballen4705's avatar
ballen4705 committed
698
699
700
}


701
// Loop over all valid attributes.  If they are prefailure attributes
702
703
704
705
706
707
708
709
710
711
712
// and are at or below the threshold value, then return the ID of the
// first failing attribute found.  Return 0 if all prefailure
// attributes are in bounds.  The spec says "Bit 0
// -Pre-failure/advisory - If the value of this bit equals zero, an
// attribute value less than or equal to its corresponding attribute
// threshold indicates an advisory condition where the usage or age of
// the device has exceeded its intended design life period. If the
// value of this bit equals one, an atribute value less than or equal
// to its corresponding attribute threshold indicates a pre-failure
// condition where imminent loss of data is being predicted."

713

714
715
// onlyfailed=0 : are or were any age or prefailure attributes <= threshold
// onlyfailed=1:  are any prefailure attributes <= threshold now
ballen4705's avatar
ballen4705 committed
716
717
int ataCheckSmart(struct ata_smart_values *data,
		  struct ata_smart_thresholds *thresholds,
718
		  int onlyfailed){
719
  int i;
ballen4705's avatar
ballen4705 committed
720
  
721
722
723
724
  // loop over all attributes
  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){

    // pointers to disk's values and vendor's thresholds
ballen4705's avatar
ballen4705 committed
725
726
    struct ata_smart_attribute *disk=data->vendor_attributes+i;
    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
727
728
729
730
731
732
733
734
735
736
737
738
739
740
 
    // consider only valid attributes
    if (disk->id && thre->id){
      int failednow,failedever;
      
      failednow =disk->current <= thre->threshold;
      failedever=disk->worst   <= thre->threshold;
      
      if (!onlyfailed && failedever)
	return disk->id;
      
      if (onlyfailed && failednow && disk->status.flag.prefailure)
	return disk->id;      
    }
741
742
  }
  return 0;
ballen4705's avatar
ballen4705 committed
743
}
744
745


746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779

// This checks the n'th attribute in the attribute list, NOT the
// attribute with id==n.  If the attribute does not exist, or the
// attribute is > threshold, then returns zero.  If the attribute is
// <= threshold (failing) then we the attribute number if it is a
// prefail attribute.  Else we return minus the attribute number if it
// is a usage attribute.
int ataCheckAttribute(struct ata_smart_values *data,
		      struct ata_smart_thresholds *thresholds,
		      int n){
  struct ata_smart_attribute *disk;
  struct ata_smart_threshold_entry *thre;
  
  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds)
    return 0;
  
  // pointers to disk's values and vendor's thresholds
  disk=data->vendor_attributes+n;
  thre=thresholds->thres_entries+n;

  if (!disk || !thre)
    return 0;
  
  // consider only valid attributes, check for failure
  if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold)
    return 0;
  
  // We have found a failed attribute.  Return positive or negative? 
  if (disk->status.flag.prefailure)
    return disk->id;
  else
    return -1*(disk->id);
}

780
781
// Note some attribute names appear redundant because different
// manufacturers use different attribute IDs for an attribute with the
782
783
784
// same name.  The array defs[] contains non-zero values if particular
// attributes have non-default interpretations.
void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *defs){
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  char *name;
  switch (id){
    
  case 1:
    name="Raw_Read_Error_Rate";
    break;
  case 2:
    name="Throughput_Performance";
    break;
  case 3:
    name="Spin_Up_Time";
    break;
  case 4:
    name="Start_Stop_Count";
    break;
  case 5:
    name="Reallocated_Sector_Ct";
    break;
  case 6:
    name="Read_Channel_Margin";
    break;
  case 7:
    name="Seek_Error_Rate";
    break;
  case 8:
    name="Seek_Time_Performance";
    break;
  case 9:
813
814
815
816
817
818
819
820
821
822
823
    switch (defs[id]) {
    case 1:
      name="Power_On_Minutes";
      break;
    case 2:
      name="Temperature_Celsius";
      break;
    default:
      name="Power_On_Hours";
      break;
    }
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
    break;
  case 10:
    name="Spin_Retry_Count";
    break;
  case 11:
    name="Calibration_Retry_Count";
    break;
  case 12:
    name="Power_Cycle_Count";
    break;
  case 13:
    name="Read_Soft_Error_Rate";
    break;
  case 191:
    name="G-Sense_Error_Rate";
    break;
  case 192:
    name="Power-Off_Retract_Count";
    break;
  case 193:
    name="Load_Cycle_Count";
    break;
  case 194:
ballen4705's avatar
   
ballen4705 committed
847
    name="Temperature_Celsius";
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
    break;
  case 195:
    name="Hardware_ECC_Recovered";
    break;
  case 196:
    name="Reallocated_Event_Count";
    break;
  case 197:
    name="Current_Pending_Sector";
    break;
  case 198:
    name="Offline_Uncorrectable";
    break;
  case 199:
    name="UDMA_CRC_Error_Count";
    break;
ballen4705's avatar
ballen4705 committed
864
865
866
867
  case 200:
    // Western Digital
    name="Multi_Zone_Error_Rate";
    break;
868
  case 220:
869
870
871
872
873
874
875
876
    switch (defs[id]) {
    case 1:
      name="Temperature_Celsius";
      break;
    default:
      name="Disk_Shift";
      break;
    }
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
    break;
  case 221:
    name="G-Sense_Error_Rate";
    break;
  case 222:
    name="Loaded_Hours";
    break;
  case 223:
    name="Load_Retry_Count";
    break;
  case 224:
    name="Load_Friction";
    break;
  case 225:
    name="Load_Cycle_Count";
    break;
  case 226:
    name="Load-in_Time";
    break;
  case 227:
    name="Torq-amp_Count";
    break;
  case 228:
    name="Power-off_Retract_Count";
    break;
902
903
904
905
  case 230:
    // seen in IBM DTPA-353750
    name="Head Amplitude";
    break;
ballen4705's avatar
ballen4705 committed
906
  case 231:
ballen4705's avatar
   
ballen4705 committed
907
    name="Temperature_Celsius";
908
    break;
909
910
911
  case 240:
    name="Head flying hours";
    break;
912
913
914
915
  default:
    name="Unknown_Attribute";
    break;
  }
916
  sprintf(out,"%3hhu %s",id,name);
917
918
  return;
}