Get Unique Serial Number of Usb Device Mounted to /Dev Folder

how to get the serial number from the USB device on Unix-like systems

I have managed to retrieve the serial number by getting the /dev file using df command

Than used this code and modified it a little bit :

USB-drive serial number under linux C++

3rd answer (Orwellophile's)

I'm not sure this will work on every Unix systems, but it is fine for now.

How to identify UNIQUELY a USB Device?

The closest equivalent of a "unique number" available in any USB device is the VID and PID, the vendor and product IDs. It is used to identify the device and install the proper driver for it. However, it is the same set of numbers for the same product, it is not good enough to identify the particular device you have in your hand. And thus useless to implement a license verification procedure, presumably what you are after.

Only certain kind of USB devices implement a serial number. You get it by accident from a flash drive due to the drive formatter generating a volume serial number. Useless as well, it can easily be changed. A USB wifi adapter could work, the manufacturer must generate a unique MAC address.

There's one class of USB devices that are perfect for this goal. The generic name is "dongle", they are made for this. When you buy one you also get software that you can link into your program that provides a tamper-proof way to verify the license number, another important part of a license verification procedure and usually the weak link. More about dongles in this Wikipedia article.

How to find /dev/ name of USB Device for Serial Reading on Mac OS?

So, I actually found the answer.

To find out what the device name is, I did an ls of the /dev/ directory with the device plugged in and then with it disconnected.

ls -lha /dev/tty* > plugged.txt
ls -lha /dev/tty* > np.txt

Then I compared the files using

vimdiff plugged.txt np.txt

And saw the line

crw-rw-rw- 1 root wheel 19, 30 Jan 16 15:24 /dev/tty.usbmodem145222

Sure enough, the device is named tty.usbmodem145222!

USB-drive serial number under linux C++

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.

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.

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).

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

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

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


Related Topics



Leave a reply



Submit