diff options
author | Matthew Garrett <mjg59@srcf.ucam.org> | 2009-03-16 19:00:37 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2009-03-18 21:04:48 +0100 |
commit | 8e07ffd3047362423a3ba4e5b240031996a26aac (patch) | |
tree | ac2272bcb68f057f1994e1eff81cf94050e8092d | |
parent | f6c77e667797c121e76121ed80e957325ca0a9f2 (diff) |
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 <lennart@poettering.net>
-rw-r--r-- | atasmart.c | 86 |
1 files changed, 79 insertions, 7 deletions
@@ -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); |