hal/hald/linux/drive_id drive_id.c,NONE,1.1 drive_id.h,NONE,1.1

David Zeuthen david at freedesktop.org
Sun Aug 1 12:14:05 PDT 2004


Update of /cvs/hal/hal/hald/linux/drive_id
In directory pdx:/tmp/cvs-serv22767/hald/linux/drive_id

Added Files:
	drive_id.c drive_id.h 
Log Message:
2004-08-01  David Zeuthen  <david at fubar.dk>

	Patch from Kay Sievers <kay.sievers at vrfy.org>

	* hald/Makefile.am:
	* hald/linux/block_class_device.c: (block_class_pre_process),
	(block_class_compute_udi):
	* hald/linux/drive_id/drive_id.c: (dump), (set_str), (scsi_inq),
	(probe_scsi), (probe_ata), (drive_id_probe), (drive_id_open_fd),
	(drive_id_open_node), (drive_id_open_dev_t), (drive_id_close):
	* hald/linux/drive_id/drive_id.h:
	Here is the code, that reads the serial number of a drive,
	directly connected to an ATA or SCSI bus. HAL probes for the
	values and adds: storage.serial, storage.firmware_version,
        storage.revision if available.
	Note: This will only work on native interfaces. For devices behind
	USB, it's not possible to get any of these values. There are some
	bridges out there, which are capable to set the USB-values itself,
	to the values from a ATA Inquiry, but unfortunally, most of the
	bridges out there, don't do it.  "USB storage compliance" mandates
	a unique serial number, but most of the vendors seems not to care
	about it.
	I changed the compute_udi for block devices. If we get a serial
	number or a uuid we use it to compute the udi.



--- NEW FILE: drive_id.c ---
/*
 * drive_id - reads drive model and serial number
 *
 * Copyright (C) 2004 Kay Sievers <kay.sievers at vrfy.org>
 *
 * 	Try to get vendor, model, serial number, firmware version
 *	of ATA or SCSI devices.
 *	
 *	Note:	Native interface access is needed. There is no way to get
 *		these kind from a device behind e.g. a USB adapter. A good
 *		bridge reads these values from the disk and provides them
 *		as USB config strings.
 *
 *	This library is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation; either
 *	version 2.1 of the License, or (at your option) any later version.
 *
 *	This library is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *	Lesser General Public License for more details.
 *
 *	You should have received a copy of the GNU Lesser General Public
 *	License along with this library; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <scsi/sg.h>
#include <linux/hdreg.h>
#include <asm/types.h>

#include "drive_id.h"

#define DEBUG
#ifdef DEBUG
#define dbg(format, arg...)						\
	do {								\
		printf("%s: " format "\n", __FUNCTION__ , ## arg);	\
	} while (0)
#else
#define dbg(format, arg...)	do {} while (0)
#endif /* DEBUG */

static inline void dump(unsigned char *buf, int len)
{
	int i;
	char *c = (char *) buf;
	for (i = 0; i < len; i++)
		printf("%c", c[i] );
}

static void set_str(char *to, const unsigned char *from, int count)
{
	int first;
	int last;

	/* remove leading whitespace */
	for (first = 0; isspace(from[first]) && (first < count); first++);

	memcpy(to, &from[first], count - first);

	/* remove trailing whitespace */
	last = strnlen(to, count);
	while (last--) {
		if (! isspace(to[last]))
			break;
	}
	to[last+1] = '\0';


}

#define INQUIRY_CMD_LEN		6
#define SENSE_BUF_LEN		32
#define INQUIRY			0x12
#define TIMEOUT			3000
static int scsi_inq(int fd,
		    unsigned char evpd, unsigned char page,
		    unsigned char *buf, int buf_len)
{
	unsigned char cmd[INQUIRY_CMD_LEN];
	unsigned char sense[SENSE_BUF_LEN];
	struct sg_io_hdr io_hdr;
	int rc;

	cmd[0] = INQUIRY;
	cmd[1] = evpd;
	cmd[2] = page;
	cmd[3] = 0;
	cmd[4] = buf_len;
	cmd[5] = 0;

	memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
	io_hdr.interface_id = 'S';
	io_hdr.cmdp = cmd;
	io_hdr.cmd_len = sizeof(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 = TIMEOUT;

	rc = ioctl(fd, SG_IO, &io_hdr);
	if (rc != 0) {
		dbg("ioctl failed");
		return -1;
	}

	if (io_hdr.status != 0 ||
	    io_hdr.msg_status != 0 ||
	    io_hdr.host_status != 0 ||
	    io_hdr.driver_status != 0) {
		dbg("scsi status error");
		return -1;
	}

	if (buf[1] != page) {
		dbg("asked for page 0x%x, got page 0x%x", page, buf[1]);
		return -1;
	}

	return 0;
}

#define BUFFER_SIZE		256
static int probe_scsi(struct drive_id *id)
{
	unsigned char buf[BUFFER_SIZE];
	int len;
	int rc;

	/* get vendor product */
	memset(buf, 0, BUFFER_SIZE);
	rc = scsi_inq(id->fd, 0, 0x00, buf, 255);
	if (rc != 0) {
		dbg("inquiry page 0x00 failed");
		return -1;
	}

	set_str(id->vendor, &buf[8], 8);
	set_str(id->model, &buf[16], 16);
	set_str(id->revision, &buf[32], 12);

	/* get serial number from page 0x80 */
	memset(buf, 0, BUFFER_SIZE);
	rc = scsi_inq(id->fd, 1, 0x80, buf, 255);
	if (rc != 0) {
		dbg("inquiry page 0x80 failed");
		return -1;
	}
	len = buf[3];
	dbg("page 0x80 serial number length %i", len);
	if (len > DRIVE_ID_SERIAL_SIZE)
		len = DRIVE_ID_SERIAL_SIZE;
	set_str(id->serial, &buf[4], len);

	return 0;
}

static int probe_ata(struct drive_id *id)
{
	struct hd_driveid ata_id;

	if (ioctl(id->fd, HDIO_GET_IDENTITY, &ata_id) != 0)
		return -1;

	set_str(id->model, ata_id.model, 40);
	set_str(id->serial, ata_id.serial_no, 20);
	set_str(id->firmware, ata_id.fw_rev, 8);

	return 0;
}

/* probe drive for model/serial number */
int drive_id_probe(struct drive_id *id, enum drive_type type)
{
	int rc;

	if (id == NULL)
		return -1;

	switch(type) {
	case DID_ATA:
		rc = probe_ata(id);
		break;
	case DID_SCSI:
		rc = probe_scsi(id);
		break;
	case DID_ALL:
	default:
		rc = probe_ata(id);
		if (rc == 0)
			break;
		rc = probe_scsi(id);
		if (rc == 0)
			break;
	}

	return rc;
}

/* open drive by already open file descriptor */
struct drive_id *drive_id_open_fd(int fd)
{
	struct drive_id *id;

	id = malloc(sizeof(struct drive_id));
	if (id == NULL)
		return NULL;
	memset(id, 0x00, sizeof(struct drive_id));

	id->fd = fd;

	return id;
}

/* open drive by device node */
struct drive_id *drive_id_open_node(const char *path)
{
	struct drive_id *id;
	int fd;

	fd = open(path, O_RDONLY | O_NONBLOCK);
	if (fd < 0) {
		dbg("unable to open '%s'", path);
		return NULL;
	}

	id = drive_id_open_fd(fd);
	if (id == NULL)
		return NULL;

	/* close fd on device close */
	id->fd_close = 1;

	return id;
}

/* open drive by major/minor */
struct drive_id *drive_id_open_dev_t(dev_t devt)
{
	struct drive_id *id;
	__u8 tmp_node[DRIVE_ID_PATH_MAX];

	snprintf(tmp_node, DRIVE_ID_PATH_MAX,
		 "/tmp/drive-%u-%u-%u", getpid(), major(devt), minor(devt));
	tmp_node[DRIVE_ID_PATH_MAX] = '\0';

	/* create tempory node to open the block device */
	unlink(tmp_node);
	if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
		return NULL;

	id = drive_id_open_node(tmp_node);

	unlink(tmp_node);

	return id;
}

/* free allocated drive info */
void drive_id_close(struct drive_id *id)
{
	if (id == NULL)
		return;

	if (id->fd_close != 0)
		close(id->fd);

	free(id);
}

--- NEW FILE: drive_id.h ---
/*
 * drive_id - reads drive model and serial number
 *
 * Copyright (C) 2004 Kay Sievers <kay.sievers at vrfy.org>
 *
 *	This library is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation; either
 *	version 2.1 of the License, or (at your option) any later version.
 *
 *	This library is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *	Lesser General Public License for more details.
 *
 *	You should have received a copy of the GNU Lesser General Public
 *	License along with this library; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef _DRIVE_ID_H_
#define _DRIVE_ID_H_

#define DRIVE_ID_VENDOR_SIZE		8
#define DRIVE_ID_MODEL_SIZE		40
#define DRIVE_ID_SERIAL_SIZE		20
#define DRIVE_ID_FW_SIZE		9
#define DRIVE_ID_REV_SIZE		12
#define DRIVE_ID_PATH_MAX		255


enum drive_type {
	DID_ALL,
	DID_SCSI,
	DID_ATA
};

struct drive_id {
	enum drive_type type;
	unsigned char	vendor[DRIVE_ID_VENDOR_SIZE+1];
	unsigned char	model[DRIVE_ID_MODEL_SIZE+1];
	unsigned char	serial[DRIVE_ID_SERIAL_SIZE+1];
	unsigned char	firmware[DRIVE_ID_FW_SIZE+1];
	unsigned char	revision[DRIVE_ID_REV_SIZE+1];
	int		fd;
	int		fd_close;
};

/* open drive by already open file descriptor */
extern struct drive_id *drive_id_open_fd(int fd);

/* open drive by device node */
extern struct drive_id *drive_id_open_node(const char *path);

/* open drive by major/minor */
extern struct drive_id *drive_id_open_dev_t(dev_t devt);

/* probe drive for model/serial number */
extern int drive_id_probe(struct drive_id *id, enum drive_type type);

/* free allocated device info */
extern void drive_id_close(struct drive_id *id);

#endif /* _DRIVE_ID_H_ */




More information about the hal-commit mailing list