Linux C++下的USB驱动器序列号

uni*_*uni 9 c c++ linux usb-drive

有没有办法用C++确定linux中usb-drive的s/n?

如果不是C++是否有任何其他方式,从不同的hwinfo -diskhdparm -i

cly*_*yfe 22

我将尝试总结一下我在linux上存储驱动器序列号检索的经验.
我假设你想要存储设备标识的序列号(根据SCSI规范)而不是USB设备的序列号(根据设备描述符下的USB规范),这两个是不同的实体.

注意!
大多数设备倾向于在USB控制器中实现序列号,并保留内部SCSI磁盘的序列号未实现.
因此,如果您想唯一地识别USB设备,最好的方法是从Device Descriptor(USB规范)创建一个字符串,如VendorId-ProductId-HardwareRevision-SerialNumber
.下面我将描述如何检索存储驱动器的SN ,问道.

驱动器分为两类(实际上更多,但让我们简化):类似ATA(hda,hdb ......)和类似SCSI(sda sdb ......).USB驱动器属于第二类,它们被称为SCSI连接磁盘.在这两种情况下,ioctl调用都可用于检索所需的信息(在我们的例子中是序列号).

对于SCSI设备(包括USB驱动器),Linux通用驱动程序及其API在tldp中有记录.
SCSI设备上的序列号可在重要产品数据(简称:VPD)中找到,并可使用SCSI查询命令检索.linux中可以获取此VPD的commad行实用程序是sdparm:

> yum install sdparm
> sdparm --quiet --page=sn /dev/sda
    Unit serial number VPD page:
    3BT1ZQGR000081240XP7
Run Code Online (Sandbox Code Playgroud)

请注意,并非所有设备都具有此序列号,市场充斥着吱吱声仿冒品,并且一些USB闪存盘返回奇怪的连续剧(例如我的sandisk cruzer返回字母"u").为了克服这个问题,一些人选择通过混合VPD中的不同字符串来创建唯一标识符,如产品ID,供应商ID和序列号.

c中的代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>

int scsi_get_serial(int fd, void *buf, size_t buf_len) {
    // we shall retrieve page 0x80 as per http://en.wikipedia.org/wiki/SCSI_Inquiry_Command
    unsigned char inq_cmd[] = {INQUIRY, 1, 0x80, 0, buf_len, 0};
    unsigned char sense[32];
    struct sg_io_hdr io_hdr;
            int result;

    memset(&io_hdr, 0, sizeof (io_hdr));
    io_hdr.interface_id = 'S';
    io_hdr.cmdp = inq_cmd;
    io_hdr.cmd_len = sizeof (inq_cmd);
    io_hdr.dxferp = buf;
    io_hdr.dxfer_len = buf_len;
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
    io_hdr.sbp = sense;
    io_hdr.mx_sb_len = sizeof (sense);
    io_hdr.timeout = 5000;

    result = ioctl(fd, SG_IO, &io_hdr);
    if (result < 0)
        return result;

    if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
        return 1;

    return 0;
}

int main(int argc, char** argv) {
    char *dev = "/dev/sda";
    char scsi_serial[255];
    int rc;
    int fd;

    fd = open(dev, O_RDONLY | O_NONBLOCK);
    if (fd < 0) {
        perror(dev);
    }

    memset(scsi_serial, 0, sizeof (scsi_serial));
    rc = scsi_get_serial(fd, scsi_serial, 255);
    // scsi_serial[3] is the length of the serial number
    // scsi_serial[4] is serial number (raw, NOT null terminated)
    if (rc < 0) {
        printf("FAIL, rc=%d, errno=%d\n", rc, errno);
    } else
    if (rc == 1) {
        printf("FAIL, rc=%d, drive doesn't report serial number\n", rc);
    } else {
        if (!scsi_serial[3]) {
            printf("Failed to retrieve serial for %s\n", dev);
            return -1;
        }
        printf("Serial Number: %.*s\n", (size_t) scsi_serial[3], (char *) & scsi_serial[4]);
    }
    close(fd);

    return (EXIT_SUCCESS);
}
Run Code Online (Sandbox Code Playgroud)

为了完整性,我还将提供代码来检索ATA设备的序列号(hda,hdb ...).这不适用于USB设备.

#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <cctype>
#include <unistd.h>

int main(){
    struct hd_driveid *id;
    char *dev = "/dev/hda";
    int fd;

    fd = open(dev, O_RDONLY|O_NONBLOCK);
    if(fd < 0) {
        perror("cannot open");
    }
    if (ioctl(fd, HDIO_GET_IDENTITY, id) < 0) {
        close(fd);
        perror("ioctl error");
    } else {
        // if we want to retrieve only for removable drives use this branching
        if ((id->config & (1 << 7)) || (id->command_set_1 & 4)) {
            close(fd);
            printf("Serial Number: %s\n", id->serial_no);
        } else {
            perror("support not removable");
        }
        close(fd);
    }
}
Run Code Online (Sandbox Code Playgroud)