linux下的C USB驱动器的序列号+ [英] USB-drive serial number under linux C++

查看:484
本文介绍了linux下的C USB驱动器的序列号+的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法使用C ++来确定S / N在linux的USB驱动器?

Is there any way to determine s/n of usb-drive in linux using C++ ?

如果不是C ++是有任何其他方式从不同的 HWINFO -disk hdparm的-i

If not C++ is there any other way different from hwinfo -disk and hdparm -i ?

推荐答案

我会试着总结一下我的关于Linux上的存储驱动器的序列号获取的经验。结果
我假设你想在存储设备标识的序列号(按照SCSI规范)没有在 USB设备的序列号(根据USB规范下的设备描述符),这两个是不同的实体。

I'll try to summarize my experience regarding storage drive serial number retrieval on linux.
I assume you want the serial number of the storage device identity (as per SCSI specification) not the serial number of the USB device (as per USB specification under Device Descriptor ), these two are different entities.

注意!结果
  大多数设备往往实现在USB-控制器一个序列号和离开内的SCSI磁盘未实现的序列号。结果,
  所以,如果你想唯一识别USB设备的最佳方式是创建从字符串设备描述符(USB规格)喜欢的 VENDORID-产品编号-HardwareRevision-的SerialNumber 的结果
  下面我将介绍如何检索存储驱动器的SN作为问道。

NOTICE!
Most devices tend to implement a serial number in the USB-Controller and leave the serial number of the inner SCSI-disk unimplemented.
So if you want to uniquely identify an USB-device the best way is to create a string from the Device Descriptor (USB specification) like VendorId-ProductId-HardwareRevision-SerialNumber
In the following I shall describe how to retrieve the SN of the storage drive, as asked.

驱动器落在2类(实际上更多,但让我们简化):ATA样(HDA,HDB ...)和SCSI样(SDA SDB ...)。
USB驱动器落在第二类,他们被称为的 SCSI连接的磁盘的。
在这两种情况下的ioctl 调用可用于检索所需信息(本例中的序列号)

Drives fall in 2 categories (actually more, but let's simplify): ATA-like (hda, hdb ...) and SCSI-like (sda sdb ...). USB drives fall in the second category, they are called SCSI attached disks. In both situation ioctl calls can be used to retrieve the required information (in our case the serial number).

有关的 SCSI设备(这其中就包括USB驱动器)的Linux的通用驱动程序,它的API是在的 TLDP 。结果
在SCSI设备的序列号是重要产品数据使用(简称:VPD)和是可检索通过使用 SCSI查询命令
在Linux中commad行实用工具,可以获取该VPD是 sdparm

For SCSI devices (and these include USB drives) the Linux generic driver and it's API is documented at tldp.
The serial number on SCSI devices is available inside the Vital Product Data (short: VPD) and is retrievable by using the SCSI Inquiry Command. A commad line utility in linux that can fetch this VPD is sdparm:

> yum install sdparm
> sdparm --quiet --page=sn /dev/sda
    Unit serial number VPD page:
    3BT1ZQGR000081240XP7

请注意,并非所有的设备都有这个序列号,市场上充斥着仿冒吱吱,有的U盘返回奇怪的连续(例如我的SanDisk CRUZER回报只是字母U)。为了克服这一部分人选择创建由VPD同类产品ID,供应商ID和序列号混合不同的字符串的唯一标识符。

Note that not all devices have this serial number, the market is flooded with cheep knockoffs, and some usb flash disks return strange serials (for example my sandisk cruzer returns just the letter "u"). To overcome this some people choose to create a unique identifier by mixing different strings from VPD like Product ID, Vendor ID and Serial Number.

code在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);
}

有关completness的缘故,我还会提供code检索的 ATA设备的序列号(HDA,HDB ...)。这会不会对USB设备的正常工作。

For the sake of completness i'll also provide the code to retrieve the serial number for ATA devices (hda, hdb ...). This will NOT work for USB devices.

#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);
    }
}

这篇关于linux下的C USB驱动器的序列号+的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆