summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--atasmart.c86
1 files changed, 79 insertions, 7 deletions
diff --git a/atasmart.c b/atasmart.c
index e0d140c..c66b088 100644
--- a/atasmart.c
+++ b/atasmart.c
@@ -59,7 +59,8 @@ typedef enum SkDirection {
} SkDirection;
typedef enum SkDiskType {
- SK_DISK_TYPE_ATA_PASSTHROUGH, /* ATA passthrough over SCSI transport */
+ SK_DISK_TYPE_ATA_PASSTHROUGH_12, /* ATA passthrough over SCSI, 12-byte version */
+ SK_DISK_TYPE_ATA_PASSTHROUGH_16, /* ATA passthrough over SCSI transport */
SK_DISK_TYPE_ATA,
SK_DISK_TYPE_UNKNOWN,
_SK_DISK_TYPE_MAX
@@ -241,7 +242,7 @@ static int sg_io(int fd, int direction,
return ioctl(fd, SG_IO, &io_hdr);
}
-static int disk_passthrough_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
+static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
uint8_t *bytes = cmd_data;
uint8_t cdb[16];
uint8_t sense[32];
@@ -254,7 +255,7 @@ static int disk_passthrough_command(SkDisk *d, SkAtaCommand command, SkDirection
[SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
};
- assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH);
+ assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
/* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
* ATA Command Pass-Through":
@@ -314,11 +315,82 @@ static int disk_passthrough_command(SkDisk *d, SkAtaCommand command, SkDirection
return ret;
}
+static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
+ uint8_t *bytes = cmd_data;
+ uint8_t cdb[12];
+ uint8_t sense[32];
+ uint8_t *desc = sense+8;
+ int ret;
+
+ static const int direction_map[] = {
+ [SK_DIRECTION_NONE] = SG_DXFER_NONE,
+ [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
+ [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
+ };
+
+ assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
+
+ /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
+ * ATA Command Pass-Through":
+ * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
+
+ memset(cdb, 0, sizeof(cdb));
+
+ cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
+
+ if (direction == SK_DIRECTION_NONE) {
+ cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
+ cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
+
+ } else if (direction == SK_DIRECTION_IN) {
+ cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
+ cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
+
+ } else if (direction == SK_DIRECTION_OUT) {
+ cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
+ cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
+ }
+
+ cdb[3] = bytes[1]; /* FEATURES */
+ cdb[4] = bytes[3]; /* SECTORS */
+
+ cdb[5] = bytes[9]; /* LBA LOW */
+ cdb[6] = bytes[8]; /* LBA MID */
+ cdb[7] = bytes[7]; /* LBA HIGH */
+
+ cdb[8] = bytes[10] & 0x4F; /* SELECT */
+ cdb[9] = (uint8_t) command;
+
+ memset(sense, 0, sizeof(sense));
+
+ if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, (size_t) cdb[4] * 512, sense, sizeof(sense))) < 0)
+ return ret;
+
+ if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
+ errno = EIO;
+ return -1;
+ }
+
+ memset(bytes, 0, 12);
+
+ bytes[1] = desc[3];
+ bytes[2] = desc[4];
+ bytes[3] = desc[5];
+ bytes[9] = desc[7];
+ bytes[8] = desc[9];
+ bytes[7] = desc[11];
+ bytes[10] = desc[12];
+ bytes[11] = desc[13];
+
+ return ret;
+}
+
static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
[SK_DISK_TYPE_ATA] = disk_ata_command,
- [SK_DISK_TYPE_ATA_PASSTHROUGH] = disk_passthrough_command,
+ [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
+ [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command
};
assert(d);
@@ -470,7 +542,7 @@ int sk_disk_smart_status(SkDisk *d, SkBool *good) {
cmd[4] == htons(0x4F00U))
*good = TRUE;
else if (cmd[3] == htons(0x002CU) &&
- cmd[4] == htons(0xF400U))
+ cmd[4] == htons(0xF400U))
*good = FALSE;
else {
errno = EIO;
@@ -1420,7 +1492,7 @@ int sk_disk_dump(SkDisk *d) {
return ret;
printf("Disk Health Good: %s\n",
- yes_no(good));
+ yes_no(good));
if ((ret = sk_disk_smart_read_data(d)) < 0)
return ret;
@@ -1447,7 +1519,7 @@ int sk_disk_dump(SkDisk *d) {
yes_no(spd->short_and_extended_test_available),
yes_no(spd->start_test_available),
yes_no(spd->abort_test_available),
- spd->short_test_polling_minutes,
+ spd->short_test_polling_minutes,
spd->extended_test_polling_minutes,
spd->conveyance_test_polling_minutes);