0

I have recently been playing around with some hard drive stuff. Now what I want to do is print out the model and serial number of harddisk. Sata drives are very easy with ioctl. scsi on the other hand I have to send an inquiry command. I found a very helpful site which explains everything and even has a example program: http://tldp.org/HOWTO/archived/SCSI-Programming-HOWTO/SCSI-Programming-HOWTO-24.html

but I only get nothing or gibberish as a result if I print it out. I even had to fix the program as stdlib wasn't included and the function Inquiry returned a local variable. But I have no idea how to fix it...

#define DEVICE "/dev/sdb"
/* Example program to demonstrate the generic SCSI interface */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/sg.h>

#define SCSI_OFF sizeof(struct sg_header)
static unsigned char cmd[SCSI_OFF + 18];      /* SCSI command buffer */
int fd;                               /* SCSI device/file descriptor */

/* process a complete scsi cmd. Use the generic scsi interface. */
static int handle_scsi_cmd(unsigned cmd_len,         /* command length */
                           unsigned in_size,         /* input data size */
                           unsigned char *i_buff,    /* input buffer */
                           unsigned out_size,        /* output data size */
                           unsigned char *o_buff     /* output buffer */
                           )
{
int status = 0;
struct sg_header *sg_hd;

/* safety checks */
if (!cmd_len) return -1;            /* need a cmd_len != 0 */
if (!i_buff) return -1;             /* need an input buffer != NULL */
#ifdef SG_BIG_BUFF
if (SCSI_OFF + cmd_len + in_size > SG_BIG_BUFF) return -1;
if (SCSI_OFF + out_size > SG_BIG_BUFF) return -1;
#else
if (SCSI_OFF + cmd_len + in_size > 4096) return -1;
if (SCSI_OFF + out_size > 4096) return -1;
#endif

if (!o_buff) out_size = 0;

/* generic scsi device header construction */
sg_hd = (struct sg_header *) i_buff;
sg_hd->reply_len   = SCSI_OFF + out_size;
sg_hd->twelve_byte = cmd_len == 12;
sg_hd->result = 0;
#if     0
sg_hd->pack_len    = SCSI_OFF + cmd_len + in_size; /* not necessary */
sg_hd->pack_id;     /* not used */
sg_hd->other_flags; /* not used */
#endif

/* send command */
status = write( fd, i_buff, SCSI_OFF + cmd_len + in_size );
if ( status < 0 || status != SCSI_OFF + cmd_len + in_size ||
                   sg_hd->result ) {
    /* some error happened */
    fprintf( stderr, "write(generic) result = 0x%x cmd = 0x%x\n", 
                sg_hd->result, i_buff[SCSI_OFF] );
    perror("");
    return status;
}

if (!o_buff) o_buff = i_buff;       /* buffer pointer check */

/* retrieve result */
status = read( fd, o_buff, SCSI_OFF + out_size);
if ( status < 0 || status != SCSI_OFF + out_size || sg_hd->result ) {
    /* some error happened */
    fprintf( stderr, "read(generic) result = 0x%x cmd = 0x%x\n", 
            sg_hd->result, o_buff[SCSI_OFF] );
    fprintf( stderr, "read(generic) sense "
            "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", 
            sg_hd->sense_buffer[0],         sg_hd->sense_buffer[1],
            sg_hd->sense_buffer[2],         sg_hd->sense_buffer[3],
            sg_hd->sense_buffer[4],         sg_hd->sense_buffer[5],
            sg_hd->sense_buffer[6],         sg_hd->sense_buffer[7],
            sg_hd->sense_buffer[8],         sg_hd->sense_buffer[9],
            sg_hd->sense_buffer[10],        sg_hd->sense_buffer[11],
            sg_hd->sense_buffer[12],        sg_hd->sense_buffer[13],
            sg_hd->sense_buffer[14],        sg_hd->sense_buffer[15]);
    if (status < 0)
        perror("");
}
/* Look if we got what we expected to get */
if (status == SCSI_OFF + out_size) status = 0; /* got them all */

return status;  /* 0 means no error */
}

#define INQUIRY_CMD     0x12
#define INQUIRY_CMDLEN  6
#define INQUIRY_REPLY_LEN 96
#define INQUIRY_VENDOR  8       /* Offset in reply data to vendor name */

/* request vendor brand and model */
static unsigned char *Inquiry ( void )
{
unsigned char Inqbuffer[ SCSI_OFF + INQUIRY_REPLY_LEN ];
unsigned char cmdblk [ INQUIRY_CMDLEN ] = 
  { INQUIRY_CMD,  /* command */
                0,  /* lun/reserved */
                0,  /* page code */
                0,  /* reserved */
INQUIRY_REPLY_LEN,  /* allocation length */
                0 };/* reserved/flag/link */

memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );

/*
 * +------------------+
 * | struct sg_header | <- cmd
 * +------------------+
 * | copy of cmdblk   | <- cmd + SCSI_OFF
 * +------------------+
*/

if (handle_scsi_cmd(sizeof(cmdblk), 0, cmd, 
                  sizeof(Inqbuffer) - SCSI_OFF, Inqbuffer )) {
  fprintf( stderr, "Inquiry failed\n" );
  exit(2);
}
return (Inqbuffer + SCSI_OFF);
}

void main( void )
{
  fd = open(DEVICE, O_RDWR);
  if (fd < 0) {
    fprintf( stderr, "Need read/write permissions for "DEVICE".\n" );
    exit(1);
  }

/* print some fields of the Inquiry result */
printf( "||%s||", Inquiry() + INQUIRY_VENDOR );

}

Joe
  • 62,789
  • 6
  • 49
  • 67
  • Removing [tag:SAS] as that is about the programming language, not Server Attached Storage. – Joe Jan 16 '14 at 17:08
  • sas in this context means serial attached scsi and not server attached storage. I added it because it needs to work with SAS drives –  Jan 16 '14 at 17:12
  • Understood, but that is not what the [tag:sas] tag means on [SO]. – Joe Jan 16 '14 at 17:14
  • ah didn't know these tags had meaning beyond human interpretation –  Jan 16 '14 at 17:23
  • No problem. Hover over the tag briefly and you'll get an explanation of what it is used for. In some cases there will be two conflicting meanings, and one (or both) will get a more specific (spelled out) tag, like "serial-atached-scsi" or something (don't know if that exists, but perhaps). – Joe Jan 16 '14 at 17:24

0 Answers0