diff -ruN /usr/src/sbin/atacontrol/Makefile atacontrol/Makefile --- /usr/src/sbin/atacontrol/Makefile 2009-08-03 01:13:06.000000000 -0700 +++ atacontrol/Makefile 2010-01-25 20:03:41.644497000 -0800 @@ -2,6 +2,7 @@ PROG= atacontrol MAN= atacontrol.8 +SRCS= atacontrol.c smart.c WARNS?= 6 .include diff -ruN /usr/src/sbin/atacontrol/atacontrol.c atacontrol/atacontrol.c --- /usr/src/sbin/atacontrol/atacontrol.c 2009-12-12 08:53:03.000000000 -0800 +++ atacontrol/atacontrol.c 2010-01-25 20:03:41.644497000 -0800 @@ -39,6 +39,8 @@ #include #include +#include "smart.h" + static const char * mode2str(int mode) { @@ -123,6 +125,7 @@ " atacontrol status array\n" " atacontrol mode device [mode]\n" " atacontrol cap device\n" + " atacontrol smart device\n" " atacontrol spindown device [seconds]\n" ); exit(EX_USAGE); @@ -403,6 +406,12 @@ exit(EX_OK); } + if (!strcmp(argv[1], "smart") && argc == 3) { + fd = open_dev(argv[2], O_RDONLY); + ata_smart_print(fd); + exit(EX_OK); + } + if (!strcmp(argv[1], "spindown") && (argc == 3 || argc == 4)) { fd = open_dev(argv[2], O_RDONLY); ata_spindown(fd, argv[2], argv[3]); diff -ruN /usr/src/sbin/atacontrol/smart.c atacontrol/smart.c --- /usr/src/sbin/atacontrol/smart.c 1969-12-31 16:00:00.000000000 -0800 +++ atacontrol/smart.c 2010-01-25 20:03:41.644497000 -0800 @@ -0,0 +1,349 @@ +/*- + * Copyright (C) 2010 Jeremy Chadwick. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smart.h" + +/* Specifications referenced in this code: + * + * ATA8-ACS + * http://www.t13.org/Documents/UploadedDocuments/docs2007/D1699r4a-ATA8-ACS.pdf + * + * SFF-8035i rev. 2.0 + * http://www.linux-mips.org/pub/linux/mips/people/macro/S.M.A.R.T./8035R2_0.PDF + * + * ATA SMART request (see ATA8-ACS, section 7.53.6): + * + * The official ATA command for SMART is 0xb0. Feature codes 0xd0, + * (SMART_READ_ATTRIBUTES) and 0xd1 (SMART_READ_THRESHOLDS) do the work. + * All SMART commands must specify the magic word 0xc24f, residing in + * bits 8-23 of the LBA (hence the << 8). + */ +#define SMART_READ_ATTRIBUTES 0xd0 +#define SMART_READ_THRESHOLDS 0xd1 +#define SMART_MAGIC_LBA (0xc24f << 8) +#define MAX_ATTRIBUTES 30 + +uint8_t +smart_checksum(const char *data) +{ + uint8_t sum = 0; + uint16_t i; + + for (i = 0; i < 512; i++) { + sum += (uint8_t) data[i]; + } + return sum; +} + + +const char * +smart_id_to_name(struct smart_attribute *a) +{ + switch (a->id) + { + case 1: return "Raw Read Error Rate"; + case 3: return "Spin Up Time"; + case 4: return "Start/Stop Count"; + case 5: return "Reallocated Sector Count"; + case 7: return "Seek Error Rate"; + case 9: return "Power On Hours Count"; + case 10: return "Spin Retry Count"; + case 11: return "Calibration Retry Count"; + case 12: return "Power Cycle Count"; + case 192: return "Power Off Retract Count"; + case 193: return "Load Cycle Count"; + case 194: return "Temperature"; + case 196: return "Reallocated Event Count"; + case 197: return "Current Pending Sectors"; + case 198: return "Uncorrected Sector Count"; + case 199: return "UltraDMA CRC Error Count"; + case 200: return "Write Error Rate"; + } + return ""; +} + +void +smart_make_hex_string(struct smart_attribute *a, char *buf) +{ + sprintf(buf, "%02x %02x %02x %02x %02x %02x", + a->data[0], a->data[1], a->data[2], + a->data[3], a->data[4], a->data[5]); + return; +} + +/* + * Obtain SMART attributes and their values + * + * Feature 0xd0 result (see ATA8-ACS, section 7.53.6.2, table 49): + * + * The 512-byte result of SMART READ DATA is documented per ATA8-ACS + * specification, section 7.53.6.2, table 49. However, you'll find + * bytes 0-361 marked "Vendor specific"; these are (mostly) the + * actual SMART attributes themselves. Example: + * + * Offset Size (B) Description + * -------- --------- --------------------------------------------------- + * 0 2 SMART attribute revision (16-bit, big endian) + * 2 12 SMART attribute data entry #0 + * 14 12 SMART attribute data entry #1 + * ..... + * 348 12 SMART attribute data entry #29 + * ..... + * -------- --------- --------------------------------------------------- + * + * The SMART attribute data format is completely undocumented. It + * consists of 12 bytes per attribute in the following format: + * + * Offset Size (B) Description + * -------- --------- --------------------------------------------------- + * 0 1 Attribute ID number + * 1 2 Attribute flags/status + * 3 1 Attribute CURRENT value (adjusted) + * 4 1 Attribute WORST value (adjusted) + * 5 6 Attribute data + * 11 1 + * -------- --------- --------------------------------------------------- + */ +int16_t +smart_read_attributes(int fd, struct smart_attribute *attributes) +{ + struct ata_ioc_request request; + char *data; + int retval; + uint16_t offset; + int16_t idx; + + data = (char *) calloc(1, 512); + memset(&request, 0, sizeof(struct ata_ioc_request)); + + request.u.ata.command = ATA_SMART_CMD; + request.u.ata.feature = SMART_READ_ATTRIBUTES; + request.u.ata.lba = SMART_MAGIC_LBA; + request.timeout = 300; + request.flags = ATA_CMD_READ; + request.data = data; + request.count = 512; + + retval = ioctl(fd, IOCATAREQUEST, &request); + + if ((retval == -1) || request.error) { + err(1, "smart_read_attributes(): ioctl(IOCATAREQUEST)"); + } + + if (smart_checksum(data) != 0) { + printf("SMART attribute structure checksum failed\n"); + } + + idx = 0; + for (offset = 2; offset < 362; offset += 12) + { + struct smart_attribute *a = &attributes[idx]; + + /* Attribute ID 0 (zero) means there aren't any more + * attributes that the drive provides. + */ + if (data[offset+0] == 0) { + break; + } + + a->id = (uint8_t) data[offset+0]; + a->flags = (uint16_t) data[offset+1]; + a->current = (uint8_t) data[offset+3]; + a->worst = (uint8_t) data[offset+4]; + memmove(&a->data, &data[offset+5], 6); + + idx++; + } + + free(data); + + return idx; +} + + +/* + * Obtain SMART attributes thresholds + * + * Feature 0xd1 result: + * + * The 512-byte result is undocumented. The below format was based on + * reading the source code to smarmtontools. + * + * Offset Size (B) Description + * -------- --------- --------------------------------------------------- + * 0 2 SMART threshold data revision (16-bit, big endian) + * 2 12 SMART threshold data entry #0 + * 14 12 SMART threshold data entry #1 + * ..... + * 348 12 SMART threshold data entry #29 + * ..... + * -------- --------- --------------------------------------------------- + * + * The SMART threshold data format is also undocumented. It consists + * of 12 bytes per attribute in the following format: + * + * Offset Size (B) Description + * -------- --------- --------------------------------------------------- + * 0 1 Attribute ID number + * 1 2 Attribute threshold + * 2 10 + * -------- --------- --------------------------------------------------- + */ +int16_t +smart_read_thresholds(int fd, struct smart_attribute *attributes) +{ + struct ata_ioc_request request; + char *data; + int retval; + uint16_t offset; + int16_t idx; + + data = (char *) calloc(1, 512); + memset(&request, 0, sizeof(struct ata_ioc_request)); + + request.u.ata.command = ATA_SMART_CMD; + request.u.ata.feature = SMART_READ_THRESHOLDS; + request.u.ata.lba = SMART_MAGIC_LBA; + request.timeout = 300; + request.flags = ATA_CMD_READ; + request.data = data; + request.count = 512; + + retval = ioctl(fd, IOCATAREQUEST, &request); + + if ((retval == -1) || request.error) { + err(1, "smart_read_thresholds(): ioctl(IOCATAREQUEST)"); + } + + if (smart_checksum(data) != 0) { + printf("SMART threshold data checksum failed\n"); + } + + idx = 0; + for (offset = 2; offset < 362; offset += 12) + { + struct smart_attribute *a = &attributes[idx]; + + /* Attribute ID 0 (zero) means there aren't any more + * attributes that the drive provides. + */ + if (data[offset+0] == 0) { + break; + } + + a->thresh = (uint8_t) data[offset+1]; + idx++; + } + + free(data); + + return idx; +} + +void +smart_print(struct smart_attribute *attributes, int16_t acount) +{ + int16_t i; + + printf("%3s %-25s %5s %5s %5s %s\n", + "ID#", "Attribute Name", + "Curr", "Worst", "Thrsh", "Bytes" + ); + printf("%3s %25s %5s %5s %5s %s\n", + "---", "-------------------------", "-----", "-----", + "-----", "-----------------"); + +// printf("id# attribute flags value worst fail\n"); + + for (i = 0; i < acount; i++) + { + struct smart_attribute *a = &attributes[i]; + char *hex; + + hex = (char *) calloc(1, 32); + smart_make_hex_string(a, hex); + + printf("%3u %1s %-25s %5u %5u %5u %s\n", + a->id, + ((a->id == 198 || a->id == 200) ? "*" : ""), + smart_id_to_name(a), + a->current, + a->worst, + a->thresh, + hex + ); + + free(hex); + } + + printf("%3s %25s %5s %5s %5s %s\n", + "---", "-------------------------", "-----", "-----", + "-----", "-----------------"); + printf(" * = values only updated after a short/long/offline test\n"); + + return; +} + +void +ata_smart_print(int fd) +{ + struct ata_params params; + struct smart_attribute attributes[MAX_ATTRIBUTES]; + int16_t acount; + int16_t tcount; + + if (ioctl(fd, IOCATAGPARM, ¶ms) < 0) + err(1, "ioctl(IOCATAGPARM)"); + + if ((params.support.command1 & ATA_SUPPORT_SMART) == 0) { + err(1, "SMART not supported on this device"); + } + + memset(&attributes, 0, (sizeof(struct smart_attribute) * MAX_ATTRIBUTES)); + + acount = smart_read_attributes(fd, attributes); + tcount = smart_read_thresholds(fd, attributes); + + smart_print(attributes, acount); + + return; +} + diff -ruN /usr/src/sbin/atacontrol/smart.h atacontrol/smart.h --- /usr/src/sbin/atacontrol/smart.h 1969-12-31 16:00:00.000000000 -0800 +++ atacontrol/smart.h 2010-01-25 20:03:41.644497000 -0800 @@ -0,0 +1,45 @@ +/*- + * Copyright (C) 2010 Jeremy Chadwick. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: + */ + +struct smart_attribute +{ + uint8_t id; + uint16_t flags; + uint8_t current; + uint8_t worst; + unsigned char data[6]; + uint8_t thresh; +}; + +uint8_t smart_checksum(const char *); +const char * smart_id_to_name(struct smart_attribute *); +void smart_make_hex_string(struct smart_attribute *, char *); +int16_t smart_read_attributes(int, struct smart_attribute *); +int16_t smart_read_thresholds(int, struct smart_attribute *); +void smart_print(struct smart_attribute *, int16_t); +void ata_smart_print(int); +