#include <stdio.h>
#include <fcntl.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <asm/types.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <net/if.h>
#include<sys/ioctl.h>
#define SG_CHECK_CONDITION 0x02
#define SG_DRIVER_SENSE 0x08
#define SG_ATA_16 0x85
#define SG_ATA_16_LEN 16
#define SG_ATA_LBA48 1
#define SG_ATA_PROTO_NON_DATA ( 3 << 1)
#define SG_ATA_PROTO_PIO_IN ( 4 << 1)
#define SG_ATA_PROTO_PIO_OUT ( 5 << 1)
#define SG_ATA_PROTO_DMA ( 6 << 1)
#define SG_ATA_PROTO_UDMA_IN (11 << 1) /* not yet supported in libata */
#define SG_ATA_PROTO_UDMA_OUT (12 << 1) /* not yet supported in libata */
#define ATA_USING_LBA (1 << 6)
enum {
ATA_OP_IDENTIFY = 0xec,
//SG_CDB2_TLEN_NODATA = 0 << 0,
//SG_CDB2_TLEN_FEAT = 1 << 0,
SG_CDB2_TLEN_NSECT = 2 << 0,
//SG_CDB2_TLEN_BYTES = 0 << 2,
SG_CDB2_TLEN_SECTORS = 1 << 2,
//SG_CDB2_TDIR_TO_DEV = 0 << 3,
SG_CDB2_TDIR_FROM_DEV = 1 << 3,
SG_CDB2_CHECK_COND = 1 << 5,
};
#define START_SERIAL 10 /* ASCII serial number */
#define LENGTH_SERIAL 10 /* 10 words (20 bytes or characters) */
void print_ascii(unsigned short *p, unsigned char length, char *SZID) {
unsigned char ii;
char cl;
int len=0;
for (ii = 0; ii< length; ii++)
{
if(((char) 0x00ff&((*p)>>8)) != ' ') break;
if((cl = (char) 0x00ff&(*p)) != ' ')
{
if(cl != '\0')
{
SZID[len]=cl;
len++;
printf("%c",cl);
}
p++; ii++;
break;
}
p++;
}
for (; ii < length; ii++) {
__u8 c;
c = (*p) >> 8;
if (c)
{
SZID[len]=c;
len++;
}
c = (*p);
if (c)
{
SZID[len]=c;
len++;
}
p++;
}
SZID[len]='\n';
}
//获取硬盘ID
int get_sata_serial(char *szDevice, char *szid)
{
int fd = 0;
static __u8 args[512] = { 0 };
__u16 *id = (__u16 *)(args);
void *data = (void *)(args);
unsigned int data_bytes = 512;
unsigned char cdb[SG_ATA_16_LEN] = { 0 };
unsigned char sb[32], *desc;
unsigned char ata_status, ata_error;
struct sg_io_hdr io_hdr;
fd = open(szDevice,O_RDONLY);
if(fd<0)
{
printf("open sda error.\n");
return -1;
}
cdb[0] = SG_ATA_16;
cdb[1] = SG_ATA_PROTO_PIO_IN;
cdb[2] = SG_CDB2_CHECK_COND;
cdb[2] |= SG_CDB2_TLEN_NSECT | SG_CDB2_TLEN_SECTORS;
cdb[2] |= SG_CDB2_TDIR_FROM_DEV;
cdb[13] = ATA_USING_LBA;
cdb[14] = ATA_OP_IDENTIFY;
memset(&(sb[0]), 0, sizeof(sb));
//设置sg_io_hdr结构体
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S'; //必须是大"S",表示Scsi Generic.
io_hdr.cmd_len = SG_ATA_16_LEN; //表示该scsi命令的长度,它必须<=16
io_hdr.mx_sb_len = sizeof(sb);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; //表示数据传输方向,比如对于写命令该变量可取值为SG_DXFER_TO_DEV,对于读命令则取SG_DXFER_FROM_DEV
io_hdr.dxfer_len = data_bytes; //表示数据传输阶段会传输多少字节
io_hdr.dxferp = data;
io_hdr.cmdp = cdb;
io_hdr.sbp = sb;
io_hdr.timeout = 10000; /* msecs */
if (ioctl(fd, SG_IO, &io_hdr) == -1) {
fprintf(stderr, "SG_IO ioctl not supported\n");
return -1; /* SG_IO not supported */
}
if (io_hdr.host_status || io_hdr.driver_status != SG_DRIVER_SENSE
|| (io_hdr.status && io_hdr.status != SG_CHECK_CONDITION))
{
errno = EIO;
return -2;
}
if (sb[0] != 0x72 || sb[7] < 14) {
errno = EIO;
return -3;
}
desc = sb + 8;
if (desc[0] != 9 || desc[1] < 12){
errno = EIO;
return -4;
}
ata_error = desc[3];
ata_status = desc[13];
if (ata_status & 0x01) { /* ERR_STAT */
errno = EIO;
return -5;
}
print_ascii( &id[START_SERIAL], LENGTH_SERIAL, szid);
return 0;
}
int main(void)
{
char szid[64] = { 0 };
get_sata_serial((char*)"/dev/sda", szid);
printf("硬盘系列号为:%s\n",szid);
}
记录一下UOS系统下的硬件信息获取方式,测试可用。系统:UOSx86_x64桌面专业版
以g++ xxx.cpp -o xxx 编译,执行时需要以权限的方式运行:sudo ./xxx