搜尋此網誌

2014年9月14日 星期日

Linux ioctl command for bypassing ATA command(Identify) - Sample Code

//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(&timeseconds);

     

      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;

}

沒有留言:

張貼留言