I'm trying to get the serial number of a storage device. I've tried
sg3_utils and sg_inq does this. I tried calling the functions within
the sg3 source files, but I can't seem to build it even after
including the relevant header files.
I've also tried forming my own SCSI inquiry CDB and sending it to the
device, but I only seem to get the vendor ID and product ID, no serial
number and other information.
Does anyone know how to use the functions within sg3_utils, or how to
obtain the serial number?
Thanks!
You have to link against "libsgutils". Copy the following files:
- .libs/libsgutils.a
- sg_cmds.h
- sg_include.h
- sg_lib.h
- sg_inq.c
... and build a binary like this:
gcc -o sg_inq sg_inq.c -lsgutils
Now you can strip down "sg_inq.c" to your needs.
> I've also tried forming my own SCSI inquiry CDB and sending it to the
> device, but I only seem to get the vendor ID and product ID, no serial
> number and other information.
The Standard Inquiry Data don't contain the serial number, you have to
readout the "Vital Product Data" page 0x80. For details look at the SPC
document.
> Does anyone know how to use the functions within sg3_utils, or how to
> obtain the serial number?
Example:
----------------------------------------------------------------------
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <scsi/sg.h>
int main(void)
{
const char devicefile[] = "/dev/sg1";
const char* file_name = NULL;
int sg_fd;
unsigned char reply_buffer[96];
unsigned char sense_buffer[32];
sg_io_hdr_t io_hdr;
unsigned char inquiry[6]
= {0x12, 0x01, 0x80, 0x00, sizeof(reply_buffer), 0x00};
int i;
/* Open device file */
file_name = devicefile;
printf("\nGet Type & SN from device file: %s\n\n", file_name);
if ((sg_fd = open(file_name, O_RDWR | O_EXCL)) < 0)
{
fprintf(stderr, "Cannot open devicefile!\n\n");
exit(1);
}
/* Send INQUIRY command */
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.mx_sb_len = sizeof(sense_buffer);
io_hdr.sbp = sense_buffer;
io_hdr.dxfer_len = sizeof(reply_buffer);
io_hdr.dxferp = reply_buffer;
io_hdr.cmd_len = sizeof(inquiry);
io_hdr.cmdp = inquiry;
io_hdr.timeout = 1000; /* Miliseconds */
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
{
fprintf(stderr, "Cannot send INQUIRY command!\n\n");
exit(1);
}
if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
{
fprintf(stderr, "INQUIRY command failed!\n");
if (io_hdr.sb_len_wr > 0)
{
printf("Sense data: ");
for (i = 0; i < io_hdr.sb_len_wr; i++)
{
printf("0x%02X ", sense_buffer[i]);
}
printf("\n");
}
exit(1);
}
/* Extract SN */
if (reply_buffer[1] != 0x80)
{
fprintf(stderr, "Unit serial number page invalid!\n\n");
exit(1);
}
printf("Device type: 0x%02X\n", reply_buffer[1] & 0x1F);
printf("Serial number: ");
for (i = 4; i < reply_buffer[3] + 4; i++)
{
printf("%c", reply_buffer[i]);
}
printf("\n\n");
exit(0);
}
----------------------------------------------------------------------
You want to tune the error handling. ILLEGAL REQUEST may occur and it
must not be a real error because VPD page support was not mandatory (or
not specified at all) in older SCSI revisions.
The output from my example should look like this for a disk [1]:
----------------------------------------------------------------------
$ ./getsn
Get Type & SN from device file: /dev/sg1
Device type: 0x00
Serial number: AJF61666
----------------------------------------------------------------------
Micha
[1] http://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type
Thanks! That worked...
I take it that my DiskID32 http://www.winsim.com/diskid32/diskid32.html
program fails also ?
Lynn