X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=extras%2Fata_id%2Fata_id.c;h=64df86c23ad429a53c951209ff4205aed7dc5ca7;hb=009cc08c7b85bc6d69145a4b1efd7be534da1b45;hp=41544e922e9ac1c0c1b883253fb2dcb8d10ff6b0;hpb=6992637e3165d433353c996aad16c8d141b00845;p=elogind.git diff --git a/extras/ata_id/ata_id.c b/extras/ata_id/ata_id.c index 41544e922..64df86c23 100644 --- a/extras/ata_id/ata_id.c +++ b/extras/ata_id/ata_id.c @@ -52,7 +52,7 @@ static int disk_scsi_inquiry_command(int fd, size_t buf_len) { struct sg_io_v4 io_v4; - uint8_t cdb[12]; + uint8_t cdb[6]; uint8_t sense[32]; int ret; @@ -202,6 +202,89 @@ static int disk_identify_command(int fd, return ret; } +static int disk_identify_packet_device_command(int fd, + void *buf, + size_t buf_len) +{ + struct sg_io_v4 io_v4; + uint8_t cdb[16]; + uint8_t sense[32]; + uint8_t *desc = sense+8; + int ret; + + /* + * ATA Pass-Through 16 byte command, as described in + * + * T10 04-262r8 ATA Command Pass-Through + * + * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf + */ + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */ + 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 */ + cdb[3] = 0; /* FEATURES */ + cdb[4] = 0; /* FEATURES */ + cdb[5] = 0; /* SECTORS */ + cdb[6] = 1; /* SECTORS */ + cdb[7] = 0; /* LBA LOW */ + cdb[8] = 0; /* LBA LOW */ + cdb[9] = 0; /* LBA MID */ + cdb[10] = 0; /* LBA MID */ + cdb[11] = 0; /* LBA HIGH */ + cdb[12] = 0; /* LBA HIGH */ + cdb[13] = 0; /* DEVICE */ + cdb[14] = 0xA1; /* Command: ATA IDENTIFY PACKET DEVICE */; + cdb[15] = 0; /* CONTROL */ + memset(sense, 0, sizeof(sense)); + + memset(&io_v4, 0, sizeof(struct sg_io_v4)); + io_v4.guard = 'Q'; + io_v4.protocol = BSG_PROTOCOL_SCSI; + io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + io_v4.request_len = sizeof (cdb); + io_v4.request = (uintptr_t) cdb; + io_v4.max_response_len = sizeof (sense); + io_v4.response = (uintptr_t) sense; + io_v4.din_xfer_len = buf_len; + io_v4.din_xferp = (uintptr_t) buf; + io_v4.timeout = COMMAND_TIMEOUT_MSEC; + + ret = ioctl(fd, SG_IO, &io_v4); + if (ret != 0) { + /* could be that the driver doesn't do version 4, try version 3 */ + if (errno == EINVAL) { + struct sg_io_hdr io_hdr; + + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmdp = (unsigned char*) cdb; + io_hdr.cmd_len = sizeof (cdb); + io_hdr.dxferp = buf; + io_hdr.dxfer_len = buf_len; + io_hdr.sbp = sense; + io_hdr.mx_sb_len = sizeof (sense); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.timeout = COMMAND_TIMEOUT_MSEC; + + ret = ioctl(fd, SG_IO, &io_hdr); + if (ret != 0) + goto out; + } else { + goto out; + } + } + + if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { + errno = EIO; + ret = -1; + goto out; + } + + out: + return ret; +} + /** * disk_identify_get_string: * @identify: A block of IDENTIFY data @@ -256,31 +339,36 @@ static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offs * @udev: The libudev context. * @fd: File descriptor for the block device. * @out_identify: Return location for IDENTIFY data. + * @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE. * - * Sends the IDENTIFY DEVICE command to the device represented by - * @fd. If successful, then the result will be copied into - * @out_identify. + * Sends the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command to the + * device represented by @fd. If successful, then the result will be + * copied into @out_identify and @out_is_packet_device. * * This routine is based on code from libatasmart, Copyright 2008 * Lennart Poettering, LGPL v2.1. * - * Returns: 0 if the IDENTIFY data was successfully obtained, - * otherwise non-zero with errno set. + * Returns: 0 if the data was successfully obtained, otherwise + * non-zero with errno set. */ static int disk_identify (struct udev *udev, int fd, - uint8_t out_identify[512]) + uint8_t out_identify[512], + int *out_is_packet_device) { int ret; uint8_t inquiry_buf[36]; int peripheral_device_type; int all_nul_bytes; int n; + int is_packet_device; assert (out_identify != NULL); + /* init results */ ret = -1; memset (out_identify, '\0', 512); + is_packet_device = 0; /* If we were to use ATA PASS_THROUGH (12) on an ATAPI device * we could accidentally blank media. This is because MMC's BLANK @@ -309,6 +397,12 @@ static int disk_identify (struct udev *udev, /* SPC-4, section 6.4.2: Standard INQUIRY data */ peripheral_device_type = inquiry_buf[0] & 0x1f; + if (peripheral_device_type == 0x05) + { + is_packet_device = 1; + ret = disk_identify_packet_device_command(fd, out_identify, 512); + goto check_nul_bytes; + } if (peripheral_device_type != 0x00) { ret = -1; errno = EIO; @@ -320,6 +414,7 @@ static int disk_identify (struct udev *udev, if (ret != 0) goto out; + check_nul_bytes: /* Check if IDENTIFY data is all NUL bytes - if so, bail */ all_nul_bytes = 1; for (n = 0; n < 512; n++) { @@ -336,6 +431,8 @@ static int disk_identify (struct udev *udev, } out: + if (out_is_packet_device != NULL) + *out_is_packet_device = is_packet_device; return ret; } @@ -350,7 +447,8 @@ int main(int argc, char *argv[]) { struct udev *udev; struct hd_driveid id; - uint8_t identify[512]; + uint8_t identify[512]; + uint16_t *identify_words; char model[41]; char model_enc[256]; char serial[21]; @@ -358,8 +456,9 @@ int main(int argc, char *argv[]) const char *node = NULL; int export = 0; int fd; - uint16_t word; + uint16_t word; int rc = 0; + int is_packet_device = 0; static const struct option options[] = { { "export", no_argument, NULL, 'x' }, { "help", no_argument, NULL, 'h' }, @@ -388,8 +487,6 @@ int main(int argc, char *argv[]) printf("Usage: ata_id [--export] [--help] \n" " --export print values as environment keys\n" " --help print this help text\n\n"); - default: - rc = 1; goto exit; } } @@ -408,7 +505,7 @@ int main(int argc, char *argv[]) goto exit; } - if (disk_identify(udev, fd, identify) == 0) { + if (disk_identify(udev, fd, identify, &is_packet_device) == 0) { /* * fix up only the fields from the IDENTIFY data that we are going to * use and copy it into the hd_driveid struct for convenience @@ -416,7 +513,7 @@ int main(int argc, char *argv[]) disk_identify_fixup_string (identify, 10, 20); /* serial */ disk_identify_fixup_string (identify, 23, 6); /* fwrev */ disk_identify_fixup_string (identify, 27, 40); /* model */ - disk_identify_fixup_uint16 (identify, 0); /* configuration */ + disk_identify_fixup_uint16 (identify, 0); /* configuration */ disk_identify_fixup_uint16 (identify, 75); /* queue depth */ disk_identify_fixup_uint16 (identify, 75); /* SATA capabilities */ disk_identify_fixup_uint16 (identify, 82); /* command set supported */ @@ -439,26 +536,27 @@ int main(int argc, char *argv[]) info(udev, "HDIO_GET_IDENTITY unsupported for '%s'\n", node); rc = 2; } else { - err(udev, "HDIO_GET_IDENTITY failed for '%s'\n", node); + err(udev, "HDIO_GET_IDENTITY failed for '%s': %m\n", node); rc = 3; } goto close; } } + identify_words = (uint16_t *) identify; memcpy (model, id.model, 40); model[40] = '\0'; udev_util_encode_string(model, model_enc, sizeof(model_enc)); - udev_util_replace_whitespace((char *) id.model, model, 40); - udev_util_replace_chars(model, NULL); - udev_util_replace_whitespace((char *) id.serial_no, serial, 20); - udev_util_replace_chars(serial, NULL); - udev_util_replace_whitespace((char *) id.fw_rev, revision, 8); - udev_util_replace_chars(revision, NULL); + util_replace_whitespace((char *) id.model, model, 40); + util_replace_chars(model, NULL); + util_replace_whitespace((char *) id.serial_no, serial, 20); + util_replace_chars(serial, NULL); + util_replace_whitespace((char *) id.fw_rev, revision, 8); + util_replace_chars(revision, NULL); if (export) { - /* Set this to convey the disk speaks the ATA protocol */ - printf("ID_ATA=1\n"); + /* Set this to convey the disk speaks the ATA protocol */ + printf("ID_ATA=1\n"); if ((id.config >> 8) & 0x80) { /* This is an ATAPI device */ @@ -486,8 +584,12 @@ int main(int argc, char *argv[]) printf("ID_MODEL=%s\n", model); printf("ID_MODEL_ENC=%s\n", model_enc); printf("ID_REVISION=%s\n", revision); - printf("ID_SERIAL=%s_%s\n", model, serial); - printf("ID_SERIAL_SHORT=%s\n", serial); + if (serial[0] != '\0') { + printf("ID_SERIAL=%s_%s\n", model, serial); + printf("ID_SERIAL_SHORT=%s\n", serial); + } else { + printf("ID_SERIAL=%s\n", model); + } if (id.command_set_1 & (1<<5)) { printf ("ID_ATA_WRITE_CACHE=1\n"); @@ -600,6 +702,15 @@ int main(int argc, char *argv[]) /* ATA devices have no vendor extension */ printf("ID_WWN_WITH_EXTENSION=0x%llx\n", (unsigned long long int) wwwn); } + + /* from Linux's include/linux/ata.h */ + if (identify_words[0] == 0x848a || identify_words[0] == 0x844a) { + printf("ID_ATA_CFA=1\n"); + } else { + if ((identify_words[83] & 0xc004) == 0x4004) { + printf("ID_ATA_CFA=1\n"); + } + } } else { if (serial[0] != '\0') printf("%s_%s\n", model, serial);