/* * This code is copyright 2007 by Mark Lord, * and is made available to all under the terms * of the GNU General Public License v2. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef unsigned long long u64; enum { ATA_CMD_PIO_IDENTIFY = 0xec, ATA_CMD_PIO_PIDENTIFY = 0xa1, /* normal sector size (bytes) for PIO/DMA */ ATA_SECT_SIZE = 512, ATA_16 = 0x85, ATA_16_LEN = 16, ATA_DEV_REG_LBA = (1 << 6), ATA_LBA48 = 1, /* data transfer protocols; only basic PIO and DMA actually work */ ATA_PROTO_NON_DATA = ( 3 << 1), ATA_PROTO_PIO_IN = ( 4 << 1), ATA_PROTO_PIO_OUT = ( 5 << 1), ATA_PROTO_DMA = ( 6 << 1), ATA_PROTO_UDMA_IN = (11 << 1), /* unsupported */ ATA_PROTO_UDMA_OUT = (12 << 1), /* unsupported */ }; /* * Taskfile layout for ATA_16 cdb (LBA28/LBA48): * * cdb[ 4] = feature * cdb[ 6] = nsect * cdb[ 8] = lbal * cdb[10] = lbam * cdb[12] = lbah * cdb[13] = device * cdb[14] = command * * "high order byte" (hob) fields for LBA48 commands: * * cdb[ 3] = hob_feature * cdb[ 5] = hob_nsect * cdb[ 7] = hob_lbal * cdb[ 9] = hob_lbam * cdb[11] = hob_lbah * * dxfer_direction choices: * * SG_DXFER_TO_DEV (writing to drive) * SG_DXFER_FROM_DEV (reading from drive) * SG_DXFER_NONE (non-data commands) */ static int sg_issue (int fd, unsigned char ata_op, void *buf) { unsigned char cdb[ATA_16_LEN] = { ATA_16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char sense[32]; unsigned int nsects = 1; struct sg_io_hdr hdr; cdb[ 1] = ATA_PROTO_PIO_IN; cdb[ 6] = nsects; cdb[14] = ata_op; memset(&hdr, 0, sizeof(struct sg_io_hdr)); hdr.interface_id = 'S'; hdr.cmd_len = ATA_16_LEN; hdr.mx_sb_len = sizeof(sense); hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.dxfer_len = nsects * ATA_SECT_SIZE; hdr.dxferp = buf; hdr.cmdp = cdb; hdr.sbp = sense; hdr.timeout = 5000; /* milliseconds */ memset(sense, 0, sizeof(sense)); if (ioctl(fd, SG_IO, &hdr) < 0) { perror("ioctl(SG_IO)"); return (-1); } if (hdr.status == 0 && hdr.host_status == 0 && hdr.driver_status == 0) return 0; /* success */ if (hdr.status > 0) { unsigned char *d = sense + 8; /* SCSI status is non-zero */ fprintf(stderr, "SG_IO error: SCSI sense=0x%x/%02x/%02x, ATA=0x%02x/%02x\n", sense[1] & 0xf, sense[2], sense[3], d[13], d[3]); return -1; } /* some other error we don't know about yet */ fprintf(stderr, "SG_IO returned: SCSI status=0x%x, host_status=0x%x, driver_status=0x%x\n", hdr.status, hdr.host_status, hdr.driver_status); return -1; } int main (int argc, char *argv[]) { const char *devpath; int i, rc, fd; #if 0 unsigned short id[ATA_SECT_SIZE / 2]; memset(id, 0, sizeof(id)); #else unsigned short *id; id = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (id == MAP_FAILED) { perror("mmap"); exit(1); } #endif if (argc != 2) { fprintf(stderr, "%s: bad/missing parm: expected \n", argv[0]); exit(1); } devpath = argv[1]; fd = open(devpath, O_RDWR|O_NONBLOCK); if (fd == -1) { perror(devpath); exit(1); } rc = sg_issue(fd, ATA_CMD_PIO_IDENTIFY, id); if (rc != 0) rc = sg_issue(fd, ATA_CMD_PIO_PIDENTIFY, id); if (rc == 0) { unsigned short *d = id; for (i = 0; i < (256/8); ++i) { printf("%04x %04x %04x %04x %04x %04x %04x %04x\n", d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]); d += 8; } exit(0); } exit(1); }