From 8e07ffd3047362423a3ba4e5b240031996a26aac Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 16 Mar 2009 19:00:37 +0000 Subject: 12-byte SAT support for libatasmart Hi Lennart, My USB enclosure (a Maxtor basics of some description) supports SAT, but only the 12-byte version. I've included a patch that implements it. RIght now it tries the 16 byte one and then falls back, which is possibly not ideal - there's a significant pause before the 16 byte one finishes and the 12 byte one is tried. I'd reverse the order, but I've no idea if that would confuse other devices. Are we able to check for which commands the firmware claims to support? Signed-off-by: Lennart Poettering --- atasmart.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file 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); -- cgit