//Modified source from: omapalvelin.homedns.org/electr/ata-query.c
//gcc test.c -o test
////////////////////////////////////////////////////////////////////////////
//
// INCLUDES AND DEFINES
//
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/hdreg.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#define BUFLEN_ATA 4096 // len of own ATA command buffer
#define DUMP_STEP 8 // number of bytes to show per hex/binary dump row
////////////////////////////////////////////////////////////////////////////
//
// DECLARATIONS
//
void printbin(unsigned char* pbyte, unsigned int len);
unsigned char hextobyte(const char *hex, int* valid);
void fix_endian(unsigned char* pbuf, unsigned int len);
void strdump(const char* str, int maxlen);
/* ATA device commands, for reference */
enum {
ATA_CMD_CHK_POWER = 0xE5, /* check power mode */
ATA_CMD_STANDBY = 0xE2, /* place in standby power mode */
ATA_CMD_IDLE = 0xE3, /* place in idle power mode */
ATA_CMD_EDD = 0x90, /* execute device diagnostic */
ATA_CMD_FLUSH = 0xE7,
ATA_CMD_FLUSH_EXT = 0xEA,
ATA_CMD_ID_ATA = 0xEC,
ATA_CMD_ID_ATAPI = 0xA1,
ATA_CMD_READ = 0xC8,
ATA_CMD_READ_EXT = 0x25,
ATA_CMD_WRITE = 0xCA,
ATA_CMD_WRITE_EXT = 0x35,
ATA_CMD_WRITE_FUA_EXT = 0x3D,
ATA_CMD_PIO_READ = 0x20,
ATA_CMD_PIO_READ_EXT = 0x24,
ATA_CMD_PIO_WRITE = 0x30,
ATA_CMD_PIO_WRITE_EXT = 0x34,
ATA_CMD_READ_MULTI = 0xC4,
ATA_CMD_READ_MULTI_EXT = 0x29,
ATA_CMD_WRITE_MULTI = 0xC5,
ATA_CMD_WRITE_MULTI_EXT = 0x39,
ATA_CMD_WRITE_MULTI_FUA_EXT = 0xCE,
ATA_CMD_SET_FEATURES = 0xEF,
ATA_CMD_PACKET = 0xA0,
ATA_CMD_VERIFY = 0x40,
ATA_CMD_VERIFY_EXT = 0x42,
ATA_CMD_STANDBYNOW1 = 0xE0,
ATA_CMD_IDLEIMMEDIATE = 0xE1,
ATA_CMD_INIT_DEV_PARAMS = 0x91,
};
// Dump Hex value of Buffer
void hexdump(char *data, int size, char *caption)
{
int i; // index in data...
int j; // index in line...
char temp[8];
char buffer[128];
char *ascii;
memset(buffer, 0, 128);
printf("---------> %s <--------- (%d bytes from %p)\n", caption, size, data);
// Printing the ruler...
printf(" +0 +4 +8 +c 0 4 8 c \n");
// Hex portion of the line is 8 (the padding) + 3 * 16 = 52 chars long
// We add another four bytes padding and place the ASCII version...
ascii = buffer + 58;
memset(buffer, ' ', 58 + 16);
buffer[58 + 16] = '\n';
buffer[58 + 17] = '\0';
buffer[0] = '+';
buffer[1] = '0';
buffer[2] = '0';
buffer[3] = '0';
buffer[4] = '0';
for (i = 0, j = 0; i < size; i++, j++)
{
if (j == 16)
{
printf("%s", buffer);
memset(buffer, ' ', 58 + 16);
sprintf(temp, "+%04x", i);
memcpy(buffer, temp, 5);
j = 0;
}
sprintf(temp, "%02x", 0xff & data[i]);
memcpy(buffer + 8 + (j * 3), temp, 2);
if ((data[i] > 31) && (data[i] < 127))
ascii[j] = data[i];
else
ascii[j] = '.';
}
if (j != 0)
printf("%s", buffer);
}
////////////////////////////////////////////////////////////////////////////
//
// MAIN
//
int main(const int argc, const char **argv) {
char hostname[30];
time_t timeseconds;
struct tm *timeinfo;
int device, ret, i;
unsigned char ata_cmd;
unsigned char *buf = NULL;
if (argc <= 1) {
printf( "\n"
" Command: ata-query device [atacmd] [atacmd params]\n\n"
" device = /dev/hda or other(/dev/sda)\n"
" atacmd = command to send, in hex, e.g. EC\n"
" atacmd params = additional subcmd or data to add to cmd, in hex, e.g. 00\n\n"
" Example: ata-query /dev/sda EC 0 0 1 to send ATA CMD ID\n"
" For more ATA cmds see linux/ata.h\n\n");
} else {
ret = gethostname(hostname, 128);
assert(ret == 0);
timeseconds = time(NULL);
timeinfo = gmtime(×econds);
buf = calloc(BUFLEN_ATA, sizeof(char));
assert(buf);
// ATA command to send?
if (argc <= 2) {
// default to ATA Identify Drive
ata_cmd = ATA_CMD_ID_ATA;
buf[0] = ata_cmd;
buf[1] = 0;
buf[2] = 0;
buf[3] = 1;
} else {
// assemble user-specified command
for (i=2; i<=argc; ++i) {
buf[i-2] = hextobyte(argv[i], &ret);
if (ret!=1) {
printf("Error in hex argument %d '%s', wrong hex format (Valid formats: AB, 0xAB)?\n", i, argv[i]);
free(buf);
return 0;
}
}
ata_cmd = buf[0];
}
device = open(argv[1], O_RDWR);
if (device < 0) {
perror("Couldn't open device.");
free(buf);
exit(1);
}
printf("<0_0>\n");
ret = ioctl(device, HDIO_DRIVE_CMD, buf);
printf("<0_1>\n");
if (ret == -1) {
perror("ioctl");
free(buf);
exit(1);
}
ret = close(device);
assert(ret != -1);
printf("\nATA cmd %02Xh for %s at %s on %04d-%02d-%02dT%02d:%02d:%02d+0000\n\n",
ata_cmd,
argv[1],
hostname,
timeinfo->tm_year+1900,
timeinfo->tm_mon+1,
timeinfo->tm_mday,
timeinfo->tm_hour,
timeinfo->tm_min,
timeinfo->tm_sec);
#if 0
printf("<1>\n");
fix_endian(buf, BUFLEN_ATA);
printf("<2>\n");
for (i=0; i<(BUFLEN_ATA/DUMP_STEP); i+=DUMP_STEP) {
printf("wd %03d: ", i/2);
printbin(buf + i, DUMP_STEP);
putchar('\n');
}
printf("<3>\n");
#else
// hexdump((char *)buf, 512, "Identify Data:");
#endif
if (ATA_CMD_ID_ATA==ata_cmd) {
char *dump_buff = (char *)&buf[4];
char str_buf[40] = {0}, *input_str = NULL;
hexdump((char *)dump_buff, 512, "Identify Data:");
// FW rev word 23..26 (words start with word 0)
printf("Serial num (minus last character):");
input_str = dump_buff + 2 * 10;
memset(str_buf, 0, 40);
for (i = 0; i < (10 * 2); i += 2) {
str_buf[i] = input_str[i + 1];
str_buf[i + 1] = input_str[i];
}
//strdump(dump_buff+2*10, 2*(19-10)+2); putchar('\n');
strdump(str_buf, 10 * 2); putchar('\n');
// FW rev word 23..26 (words start with word 0)
printf("Firmware rev (minus last character):");
input_str = dump_buff + 2 * 23;
memset(str_buf, 0, 40);
for (i = 0; i < (4 * 2); i += 2) {
str_buf[i] = input_str[i + 1];
str_buf[i + 1] = input_str[i];
}
//strdump(dump_buff+2*23, 2*(26-23)+2); putchar('\n');
strdump(str_buf, 4 * 2); putchar('\n');
// Model nr 27..46
printf("Model nr (minus last character):");
input_str = dump_buff + 2 * 27;
memset(str_buf, 0, 40);
for (i = 0; i < (20 * 2); i += 2) {
str_buf[i] = input_str[i + 1];
str_buf[i + 1] = input_str[i];
}
//strdump(dump_buff+2*27, 2*(46-27)+2); putchar('\n');
strdump(str_buf, 20 * 2); putchar('\n');
// word Ident word 84
printf("Ident word 84: 0x%04X\n", *(short *)(dump_buff + 2*82) ); //*((int*)(buf+(2*86))));
printf("Size(Ident word 84): %lld G\n", *(unsigned long long *)(dump_buff + 2*100) / 2 / 1000 / 1000);
// word Ident word 217
printf("Medua Rotation Rate(Ident word 217): %04d rpm.\n", *(short *)(dump_buff + 2*217) ); //*((int*)(buf+(2*86))));
}
free(buf);
}
putchar('\n');
return 0;
}
////////////////////////////////////////////////////////////////////////////
//
// HELPER FUNCS
//
// printbin: prints out a row of binary, hex and ascii dump
// of the specified 'pbyte' byte array containing 'len' bytes
void printbin(unsigned char* pbyte, unsigned int len) {
int i, j;
unsigned char shift;
unsigned char *p = pbyte;
// binary
for (i=len; i>0; --i) {
shift = 128;
for(j=0; j<8; ++j) {
putchar((*pbyte & shift) ? '1' : '0');
shift = shift >> 1;
}
++pbyte;
printf("b ");
}
// hex
pbyte = p;
for (i=len; i>0; --i) {
printf("%02X", *pbyte);
++pbyte;
}
printf("h ");
// ascii
pbyte = p;
printf(" '");
for (i=len; i>0; --i) {
if (isprint(*pbyte)) {
putchar(*pbyte);
} else {
putchar(' ');
}
++pbyte;
}
printf("'");
return;
}
// hextobyte: converts specified byte hex string to decimal and
// returns the value, sets 'valid' to 1 if value is valid
unsigned char hextobyte(const char *hex, int* valid) {
long l;
if (0==hex || 0==valid) {
l = 0;
} else {
l = strtol(hex, 0, 16);
if (l>=256L || errno == ERANGE) {
*valid = 0;
l = 0;
} else {
*valid = 1;
l = (unsigned char)l;
}
}
return (unsigned char)l;
}
// swap bytes for MSB first / MSB last encoding differences
void fix_endian(unsigned char* pbuf, unsigned int len) {
int i;
unsigned char c;
for (i=0; i<len && pbuf!=0; i+=2) {
c = *(pbuf + i);
*(pbuf + i) = *(pbuf + i+1);
*(pbuf + i+1) = c;
}
return;
}
// somewhat safe string to screen dump
void strdump(const char* str, int maxlen) {
int i;
unsigned char c;
for (i=0; i<maxlen; ++i) {
c = *(str + i);
if (isprint(c)) {
putchar(c);
} else {
printf("<%02X>", c);
}
if ('\0'==c) break;
}
return;
}
沒有留言:
張貼留言