linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* VPD in sysfs
@ 2004-08-14 18:29 Matthew Wilcox
  2004-08-16  6:14 ` Martin Schwenke
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Matthew Wilcox @ 2004-08-14 18:29 UTC (permalink / raw)
  To: Greg KH, martins; +Cc: linux-kernel, linux-scsi


I've been sent a patch that reads some VPD from the Symbios NVRAM and
displays it in sysfs.  I'm not sure whether the way the author chose
to present it is the best.  They put it in 0000:80:01.0/host0/vpd_name
which seems a bit too scsi-specific and insufficiently forward-looking
(I bet we want to expose more VPD data than that in the future, so we
should probably use a directory).

I actually have a feeling (and please don't kill me for saying this), that
we should add a struct vpd * to the struct device.  Then we need something
like the drivers/base/power/sysfs.c file (probably drivers/base/vpd.c)
that takes care of adding vpd to each device that wants it.

Thoughts?  Since there's at least four and probably more ways of getting
at VPD, we either need to fill in some VPD structs at initialisation or
have some kind of vpd_ops that a driver can fill in so the core can get
at the data.

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: VPD in sysfs
  2004-08-14 18:29 VPD in sysfs Matthew Wilcox
@ 2004-08-16  6:14 ` Martin Schwenke
  2004-08-16 14:11 ` Luben Tuikov
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Martin Schwenke @ 2004-08-16  6:14 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: Greg KH, linux-kernel, linux-scsi

[I'll reply to this quickly even though I'm 1/2 asleep...  travelling
 and not sure when I'll get to email again...]

>>>>> "Willy" == Matthew Wilcox <willy@debian.org> writes:

    Willy> I've been sent a patch that reads some VPD from the Symbios
    Willy> NVRAM and displays it in sysfs.  I'm not sure whether the
    Willy> way the author chose to present it is the best.  They put
    Willy> it in 0000:80:01.0/host0/vpd_name which seems a bit too
    Willy> scsi-specific and insufficiently forward-looking (I bet we
    Willy> want to expose more VPD data than that in the future, so we
    Willy> should probably use a directory).

Does the patch expose the VPD as text?  Either way, what's the format?

    Willy> I actually have a feeling (and please don't kill me for
    Willy> saying this), that we should add a struct vpd * to the
    Willy> struct device.  Then we need something like the
    Willy> drivers/base/power/sysfs.c file (probably
    Willy> drivers/base/vpd.c) that takes care of adding vpd to each
    Willy> device that wants it.

Sounds good to me...  I like the idea of a consistent way of getting
at VPD.  Perhaps then HAL would get at it too.

    Willy> Thoughts?  Since there's at least four and probably more
    Willy> ways of getting at VPD, we either need to fill in some VPD
    Willy> structs at initialisation or have some kind of vpd_ops that
    Willy> a driver can fill in so the core can get at the data.

For PCI 2.0/2.1 VPD in expansion ROMs the recent patch to expose the
ROMs in sysfs is probably enough.  The likely brokenness of ROMs and
the VPD means that trying to special-case around brokenness should
probably be done outside the kernel.

However, I could be wrong: there is something to be said for
consistency...

peace & happiness,
martin


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: VPD in sysfs
  2004-08-14 18:29 VPD in sysfs Matthew Wilcox
  2004-08-16  6:14 ` Martin Schwenke
@ 2004-08-16 14:11 ` Luben Tuikov
  2004-08-17  4:12 ` Douglas Gilbert
  2004-08-20 14:21 ` Matthew Wilcox
  3 siblings, 0 replies; 7+ messages in thread
From: Luben Tuikov @ 2004-08-16 14:11 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: Greg KH, martins, linux-kernel, linux-scsi

Matthew Wilcox wrote:
> 
> I've been sent a patch that reads some VPD from the Symbios NVRAM and
> displays it in sysfs.  I'm not sure whether the way the author chose
> to present it is the best.  They put it in 0000:80:01.0/host0/vpd_name
> which seems a bit too scsi-specific and insufficiently forward-looking
> (I bet we want to expose more VPD data than that in the future, so we
> should probably use a directory).
> 
> I actually have a feeling (and please don't kill me for saying this), that
> we should add a struct vpd * to the struct device.  Then we need something
> like the drivers/base/power/sysfs.c file (probably drivers/base/vpd.c)
> that takes care of adding vpd to each device that wants it.
> 
> Thoughts?  Since there's at least four and probably more ways of getting
> at VPD, we either need to fill in some VPD structs at initialisation or
> have some kind of vpd_ops that a driver can fill in so the core can get
> at the data.

I like this idea.  Certain VPD parameters are standard across SCSI devices,
so they could be shown in a standard way.

Not sure on the format of vpd_ops, _but_ getting the VPD data via
the EVPD bit in INQUIRY, could be pretty standard method, so that
if the LLDD doesn't set vpd_ops, they could be set to point to
the (this) generic way*. (The LLDD can snoop the INQUIRY data if
it wishes to, for further control.)

*I.e. the vpd_ops method(s) would be generic enough to abstract out
the actual method of getting the VPD...

Certainly, providing a vpd node in sysfs would help user level
apps id devices more easily.  This could also be used by multipathing
sofware and other apps.  I think it's a good idea.

			Luben



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: VPD in sysfs
  2004-08-14 18:29 VPD in sysfs Matthew Wilcox
  2004-08-16  6:14 ` Martin Schwenke
  2004-08-16 14:11 ` Luben Tuikov
@ 2004-08-17  4:12 ` Douglas Gilbert
  2004-08-20 14:21 ` Matthew Wilcox
  3 siblings, 0 replies; 7+ messages in thread
From: Douglas Gilbert @ 2004-08-17  4:12 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: Greg KH, martins, linux-kernel, linux-scsi

Matthew Wilcox wrote:
> I've been sent a patch that reads some VPD from the Symbios NVRAM and
> displays it in sysfs.  I'm not sure whether the way the author chose
> to present it is the best.  They put it in 0000:80:01.0/host0/vpd_name
> which seems a bit too scsi-specific and insufficiently forward-looking
> (I bet we want to expose more VPD data than that in the future, so we
> should probably use a directory).
> 
> I actually have a feeling (and please don't kill me for saying this), that
> we should add a struct vpd * to the struct device.  Then we need something
> like the drivers/base/power/sysfs.c file (probably drivers/base/vpd.c)
> that takes care of adding vpd to each device that wants it.
> 
> Thoughts?  Since there's at least four and probably more ways of getting
> at VPD, we either need to fill in some VPD structs at initialisation or
> have some kind of vpd_ops that a driver can fill in so the core can get
> at the data.

Vital Product Data (VPD) seems to be an ever increasing
area for all sorts of data. Here is a list (from sg_inq
in sg3_utils) of existing and proposed VPD pages for SCSI
targets and LUs:

struct vpd_name {
     int number;
     int peri_type;
     char * name;
};

static struct vpd_name vpd_name_arr[] = {
     {0x0, 0, "Supported VPD pages"},
     {0x80, 0, "Unit serial number"},
     {0x82, 0, "ASCII implemented operating definition"},
     {0x83, 0, "Device identification"},
     {0x84, 0, "Software interface identification"},
     {0x85, 0, "Management network addresses"},
     {0x86, 0, "Extended INQUIRY data"},
     {0x87, 0, "Mode page policy"},
     {0x88, 0, "SCSI ports"},
     {0x89, 0, "ATA information"},
     {0xb0, 0, "Block limits (sbc2)"},
     {0xb0, 0x1, "SSC device capabilities (ssc3)"},
     {0xb0, 0x11, "OSD information (osd)"},
     {0xb1, 0x11, "Security token (osd)"},
};

The most interesting one in the above list is the
"Device identification" VPD page (0x83) which may contain
multiple descriptors each identifying either a:
   - SCSI port (of a target)
   - SCSI target
   - SCSI logical unit

And here is a selection of possible identifier formats:
    - naa (2,5 or 6)
    - eui-64
    - t10 vendor
    - SCSI name string (iSCSI here)
    - MD5 logical unit identifier
    - vendor specific
amongst others.

["The great thing about standards is that there are so many
to choose from."]

And your patch is about attaching VPD data to a SCSI HBA (host)
via sysfs.
"struct vpd" should be interesting ...

Doug Gilbert




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: VPD in sysfs
  2004-08-14 18:29 VPD in sysfs Matthew Wilcox
                   ` (2 preceding siblings ...)
  2004-08-17  4:12 ` Douglas Gilbert
@ 2004-08-20 14:21 ` Matthew Wilcox
  2004-08-20 19:45   ` Dave C Boutcher
  3 siblings, 1 reply; 7+ messages in thread
From: Matthew Wilcox @ 2004-08-20 14:21 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: Greg KH, Patrick Mochel, martins, linux-kernel, linux-scsi

On Sat, Aug 14, 2004 at 07:29:32PM +0100, Matthew Wilcox wrote:
> Thoughts?  Since there's at least four and probably more ways of getting
> at VPD, we either need to fill in some VPD structs at initialisation or
> have some kind of vpd_ops that a driver can fill in so the core can get
> at the data.

I've tried the first option -- creating a large block of sysfs entries for
all the VPD entries that are present.  However, I've come upon a problem
with sysfs that prevents me from doing so.

Basically, the problem is that sysfs doesn't pass the attribute that's
being invoked to the attribute ->show method.  So I can't determine
which one is being read.  This isn't a problem for any other sysfs attribute
because they're all static, but for dynamically created attributes, it's
not possible to work this way.

What I wanted to have was:

/sys/devices/pci0000:00/0000:00:00.0/vpd/
/sys/devices/pci0000:00/0000:00:00.0/vpd/PN (contents "6181682A")
/sys/devices/pci0000:00/0000:00:00.0/vpd/EC (contents "4950262536")
/sys/devices/pci0000:00/0000:00:00.0/vpd/SN (contents "00000194")
/sys/devices/pci0000:00/0000:00:00.0/vpd/FN (contents "135722")
/sys/devices/pci0000:00/0000:00:00.0/vpd/MN (contents "1037")
...

(note, this example is taken from table 6-8 in PCI 2.1)

So I have code that iterates over each element in a block of raw VPD,
extracts each name and value and creates sysfs entries for them ... but
I can't write the show method.  Here's the VPD file (compiles, haven't
hooked any code up to it yet):


/*
 * drivers/base/vpd.c
 *
 * Expose Vital Product Data through sysfs
 *
 * Copyright (c) Matthew Wilcox 2004
 */

#include <linux/device.h>

/* This should really be in a common file shared with drivers/pnp/isapnp */

enum isapnp_tag {
	ISA_TAG_COMPAT = 0x03,
	ISA_TAG_VEND_S = 0x0e,
	ISA_TAG_END = 0x0f,
	ISA_TAG_IDSTR = 0x82,
	ISA_TAG_VEND_L = 0x84,
	ISA_TAG_VPD = 0x90,
};

static inline int vpd_get_len(char *data, int *idx, enum isapnp_tag *tag)
{
	int i, x, len;

	i = *idx;
	x = data[i++];

	if (x & 0x80) {
		len = data[i] | (data[i + 1] << 8);
		i += 2;
	} else {
		len = x & 7;
		x >>= 3;
	}

	*idx = i;
	*tag = x;
	return len;
}

static int vpd_count_items(char *data)
{
	int i = 0;
	int count = 0;

	for (;;) {
		int j;
		enum isapnp_tag tag;
		int len = vpd_get_len(data, &i, &tag);
		if (tag == ISA_TAG_END)
			break;
		if (tag != ISA_TAG_VPD)
			continue;
		j = i;
		while (j < i + len) {
			count++;
			j += 2;
			j += data[j] + 1;
		}
	}
	return count;
}

static ssize_t vpd_show(struct device *dev, char *buf)
{
	return 0;
}

static struct attribute * vpd_create_attr(unsigned char *data)
{
	struct device_attribute *attr;
	char *buf;
	unsigned int len = data[2];

	attr = kmalloc(sizeof(*attr) + 4 + len, GFP_KERNEL);
	if (!attr)
		return NULL;
	buf = (char *)(attr + 1);

	buf[0] = data[0];
	buf[1] = data[1];
	buf[2] = '\0';
	memcpy(buf, data + 3, len);

	attr->attr.name = buf;
	attr->attr.mode = 0644;
	attr->show = vpd_show;
	attr->store = NULL;

	return &attr->attr;
}

static int vpd_create_attrs(struct attribute **attrs, int count, unsigned char *data)
{
	int i = 0;
	int k = 0;

	for (;;) {
		int j;
		enum isapnp_tag tag;
		int len = vpd_get_len(data, &i, &tag);
		if (tag == ISA_TAG_END)
			break;
		if (tag != ISA_TAG_VPD)
			continue;
		j = i;
		while (j < i + len) {
			attrs[k] = vpd_create_attr(data + j);
			if (!attrs[k])
				return -ENOMEM;
			k++;
			j += 2;
			j += data[j] + 1;
		}
	}

	BUG_ON(k != count);
	attrs[count] = NULL;
	return 0;
}

/**
 * device_add_vpd - Add VPD files to a device
 * @dev: The device that this VPD pertains to
 * @vpd: A memory region containing VPD data
 */
int device_add_vpd(struct device *dev, unsigned char *data)
{
	int err, i;
	int count = vpd_count_items(data);
	if (count < 1)
		return count;

	dev->vpd = kmalloc(sizeof(*dev->vpd) + (count + 1) * sizeof(void *),
			GFP_KERNEL);
	if (!dev->vpd)
		return -ENOMEM;
	memset(dev->vpd, 0, sizeof(*dev->vpd) + (count + 1) * sizeof(void *));

	dev->vpd->name = "vpd";
	dev->vpd->attrs = (struct attribute **)(dev->vpd + 1);

	err = vpd_create_attrs(dev->vpd->attrs, count, data);
	if (err)
		goto fail;

	err = sysfs_create_group(&dev->kobj, dev->vpd);
	if (err)
		goto fail;

	return 0;

 fail:
	for (i = 0; i < count; i++) {
		kfree(dev->vpd->attrs[i]);
	}
	kfree(dev->vpd);
	return err;
}

void device_remove_vpd(struct device *dev)
{
	int i = 0;
	sysfs_remove_group(&dev->kobj, dev->vpd);
	while (dev->vpd->attrs[i] != NULL) {
		kfree(dev->vpd->attrs[i]);
		i++;
	}
	kfree(dev->vpd->attrs);
	kfree(dev->vpd);
	dev->vpd = NULL;
}

EXPORT_SYMBOL(device_add_vpd);
EXPORT_SYMBOL(device_remove_vpd);

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: VPD in sysfs
  2004-08-20 14:21 ` Matthew Wilcox
@ 2004-08-20 19:45   ` Dave C Boutcher
  2004-08-24 19:09     ` Matthew Wilcox
  0 siblings, 1 reply; 7+ messages in thread
From: Dave C Boutcher @ 2004-08-20 19:45 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: Greg KH, Patrick Mochel, martins, linux-kernel, linux-scsi

On Fri, Aug 20, 2004 at 03:21:43PM +0100, Matthew Wilcox wrote:
> On Sat, Aug 14, 2004 at 07:29:32PM +0100, Matthew Wilcox wrote:
> > Thoughts?  Since there's at least four and probably more ways of getting
> > at VPD, we either need to fill in some VPD structs at initialisation or
> > have some kind of vpd_ops that a driver can fill in so the core can get
> > at the data.
> 
> I've tried the first option -- creating a large block of sysfs entries for
> all the VPD entries that are present.  However, I've come upon a problem
> with sysfs that prevents me from doing so.
> 
> Basically, the problem is that sysfs doesn't pass the attribute that's
> being invoked to the attribute ->show method.  So I can't determine
> which one is being read.  This isn't a problem for any other sysfs attribute
> because they're all static, but for dynamically created attributes, it's
> not possible to work this way.

Ya, I ran into some similar restrictions with a driver I was
writing...after talking to gregkh, you are going to have to go down a
level and use kobjects directly...each piece of data will have a kobject, 
and that's what you dereference in the show method. 

-- 
Dave Boutcher

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: VPD in sysfs
  2004-08-20 19:45   ` Dave C Boutcher
@ 2004-08-24 19:09     ` Matthew Wilcox
  0 siblings, 0 replies; 7+ messages in thread
From: Matthew Wilcox @ 2004-08-24 19:09 UTC (permalink / raw)
  To: boutcher
  Cc: Matthew Wilcox, Greg KH, Patrick Mochel, martins, linux-kernel,
	linux-scsi

On Fri, Aug 20, 2004 at 02:45:25PM -0500, Dave C Boutcher wrote:
> Ya, I ran into some similar restrictions with a driver I was
> writing...after talking to gregkh, you are going to have to go down a
> level and use kobjects directly...each piece of data will have a kobject, 
> and that's what you dereference in the show method. 

I was a little more sneaky than that ...

Clearly this is not ready for merging yet, but it shows where I'm going
atm.

Todo:

 - Automatically set up VPD for devices with it in their capability list
 - Write code for devices with PCI 2.1 VPD
 - Write code for sym2 that doesn't quite conform to PCI 2.1
 - Allow writable attributes for PCI 2.2 VPD
 - Teardown code for when the device goes away
 - Figure out why checksum doesn't work (I think I know)
 - Write a proper autoexpanding array data type

Index: vpd-2.6/drivers/net/tg3.c
===================================================================
RCS file: /var/cvs/linux-2.6/drivers/net/tg3.c,v
retrieving revision 1.20
diff -u -p -r1.20 tg3.c
--- vpd-2.6/drivers/net/tg3.c	13 Aug 2004 14:29:46 -0000	1.20
+++ vpd-2.6/drivers/net/tg3.c	24 Aug 2004 18:59:50 -0000
@@ -8272,6 +8272,8 @@ static int __devinit tg3_init_one(struct
 	 */
 	pci_save_state(tp->pdev, tp->pci_cfg_state);
 
+	pci_setup_vpd(pdev);
+
 	printk(KERN_INFO "%s: Tigon3 [partno(%s) rev %04x PHY(%s)] (PCI%s:%s:%s) %sBaseT Ethernet ",
 	       dev->name,
 	       tp->board_part_number,
Index: vpd-2.6/drivers/pci/Makefile
===================================================================
RCS file: /var/cvs/linux-2.6/drivers/pci/Makefile,v
retrieving revision 1.7
diff -u -p -r1.7 Makefile
--- vpd-2.6/drivers/pci/Makefile	13 Aug 2004 14:29:50 -0000	1.7
+++ vpd-2.6/drivers/pci/Makefile	24 Aug 2004 18:59:50 -0000
@@ -5,6 +5,7 @@
 obj-y		+= access.o bus.o probe.o remove.o pci.o quirks.o \
 			names.o pci-driver.o search.o pci-sysfs.o
 obj-$(CONFIG_PROC_FS) += proc.o
+obj-$(CONFIG_VPD) += vpd.o
 
 ifndef CONFIG_SPARC64
 obj-y += setup-res.o
Index: vpd-2.6/drivers/pci/vpd.c
===================================================================
RCS file: vpd-2.6/drivers/pci/vpd.c
diff -N vpd-2.6/drivers/pci/vpd.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ vpd-2.6/drivers/pci/vpd.c	24 Aug 2004 18:59:50 -0000
@@ -0,0 +1,284 @@
+/*
+ * drivers/pci/vpd.c
+ *
+ * Retrieves VPD from a PCI device and exposes it through sysfs
+ *
+ * Copyright (c) Matthew Wilcox 2004
+ */
+
+#include <linux/isapnp.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+
+/* XXX: sysfs should expose these properly */
+
+extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *));
+extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);
+extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
+
+
+/* XXX: krealloc should be moved to mm/slab.c */
+
+static void * krealloc(void *ptr, size_t newsize, int flags)
+{
+	int size = ksize(ptr);
+	void *newptr = kmalloc(newsize, flags);
+	if (!newptr)
+		return NULL;
+	if (size > newsize)
+		size = newsize;
+	memcpy(newptr, ptr, size);
+	kfree(ptr);
+	return newptr;
+}
+
+static inline int isapnp_get_len(unsigned char *data, int *idx, enum isapnp_tag *tag)
+{
+	int i, x, len;
+
+	i = *idx;
+	x = data[i++];
+
+	if (x & 0x80) {
+		len = data[i] | (data[i + 1] << 8);
+		i += 2;
+	} else {
+		len = x & 7;
+		x >>= 3;
+	}
+
+	printk("VPD: idx %d tag %x len %d\n", i, x, len);
+	*idx = i;
+	*tag = x;
+	return len;
+}
+
+static ssize_t vpd_read(struct file *file, char __user *buf, size_t count,
+		loff_t *ppos)
+{
+	int err;
+	char *data = file->f_dentry->d_fsdata;
+	int len = strlen(data);
+	loff_t pos = *ppos;
+
+	if (pos > len)
+		return 0;
+	if (pos + count > len)
+		count = len - pos;
+	err = copy_to_user(buf, data + pos, count);
+	if (err)
+		return -EFAULT;
+	*ppos += count;
+	return count;
+}
+
+static ssize_t vpd_write(struct file *file, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static struct file_operations vpd_file_operations = {
+	.read = vpd_read,
+	.write = vpd_write,
+	.llseek = generic_file_llseek,
+};
+
+static int vpd_create_file(struct dentry *dir, char *name, char *contents, int mode)
+{
+	struct dentry *dentry;
+	int err;
+
+	printk("VPD: Creating %s %s\n", name, contents);
+
+	dget(dir);
+
+	down(&dir->d_inode->i_sem);
+	dentry = sysfs_get_dentry(dir, name);
+	err = PTR_ERR(dentry);
+	if (IS_ERR(dentry))
+		goto out;
+	err = sysfs_create(dentry, mode, NULL);
+	if (err)
+		goto dput;
+	dentry->d_inode->i_size = strlen(contents);
+	dentry->d_inode->i_fop = &vpd_file_operations;
+	dentry->d_fsdata = contents;
+
+ dput:
+	dput(dentry);
+ out:
+	up(&dir->d_inode->i_sem);
+	dput(dir);
+	return err;
+}
+
+static int vpd_create_name(struct dentry *dir, unsigned char *data, int len)
+{
+	char *content = kmalloc(len + 1, GFP_KERNEL);
+	memcpy(content, data, len);
+	content[len] = '\0';
+	return vpd_create_file(dir, "name", content, 0444);
+}
+
+static int vpd_create_tags(struct dentry *dir, unsigned char *data, int len,
+		int mode)
+{
+	int error, i = 0;
+	while (i < len) {
+		int l = data[i + 2];
+		char *tagcont = kmalloc(l + 4, GFP_KERNEL);
+		tagcont[0] = data[i];
+		tagcont[1] = data[i + 1];
+		tagcont[2] = '\0';
+		memcpy(tagcont + 3, data + i + 3, l);
+		tagcont[l + 3] = '\0';
+		error = vpd_create_file(dir, tagcont, tagcont + 3, mode);
+		if (error)
+			return error;
+		i += l + 3;
+		printk("i = %d, len = %d\n", i, len);
+	}
+
+	return 0;
+}
+
+static int vpd_checksum_data(unsigned char *data, int len)
+{
+	int i;
+	unsigned char accum = 0;
+	for (i = 0; i < len; i++) {
+		accum += data[i];
+		printk("pos 0x%x byte 0x%x, cxsum = 0x%x\n", i, data[i], accum);
+	}
+	return (accum == 0);
+}
+
+static int pci_dev_add_vpd(struct device *dev, unsigned char *data, int len)
+{
+	int err, i = 0;
+	struct dentry *dir;
+
+	vpd_checksum_data(data, len);
+//	if (!vpd_checksum_data(data, len))
+//		return -EINVAL;
+
+	err = sysfs_create_subdir(&dev->kobj, "vpd", &dir);
+	if (err)
+		return err;
+
+	for (;;) {
+		enum isapnp_tag tag;
+		int len = isapnp_get_len(data, &i, &tag);
+		if (tag == ISA_TAG_END) {
+			break;
+		} else if (tag == ISA_TAG_ANSIIDSTR) {
+			err = vpd_create_name(dir, data + i, len);
+		} else if (tag == ISA_TAG_VPD_R) {
+			err = vpd_create_tags(dir, data + i, len, 0444);
+		} else if (tag == ISA_TAG_VPD_W) {
+			err = vpd_create_tags(dir, data + i, len, 0644);
+		}
+		if (err)
+			break;
+		i += len;
+	}
+
+	return err;
+}
+
+static int pci_vpd_read_cap(struct pci_dev *dev, int addr, char *data, int off)
+{
+	u16 areg;
+	u32 dreg;
+
+	if (off > PCI_VPD_ADDR_MASK)
+		return -ENXIO;
+
+	pci_write_config_word(dev, addr + PCI_VPD_ADDR, off);
+	do {
+		pci_read_config_word(dev, addr + PCI_VPD_ADDR, &areg);
+	} while ((areg & PCI_VPD_ADDR_F) == 0);
+	pci_read_config_dword(dev, addr + PCI_VPD_DATA, &dreg);
+
+	data[off]	= (dreg >> 0)  & 0xff;
+	data[off + 1]	= (dreg >> 8)  & 0xff;
+	data[off + 2]	= (dreg >> 16) & 0xff;
+	data[off + 3]	= (dreg >> 24) & 0xff;
+
+	return 0;
+}
+
+static int pci_setup_vpd_cap(struct pci_dev *dev, int addr)
+{
+	int err;
+	unsigned int i = 0, vpd_size = 32;
+	unsigned char *vpd = kmalloc(vpd_size, GFP_KERNEL);
+	unsigned int tag, next_tag = 0;
+
+	for (;;) {
+		if (i == vpd_size) {
+			vpd_size *= 2;
+			char *new_vpd = krealloc(vpd, vpd_size, GFP_KERNEL);
+			if (!new_vpd) {
+				kfree(vpd);
+				return -ENOMEM;
+			}
+			vpd = new_vpd;
+		}
+
+		err = pci_vpd_read_cap(dev, addr, vpd, i);
+		if (err)
+			break;
+		i += 4;
+		if (next_tag >= i)
+			continue;
+		tag = vpd[next_tag];
+		if (tag & 0x80) {
+			if (next_tag + 2 >= i)
+				continue;
+			next_tag += 3 + vpd[next_tag + 1] +
+					(vpd[next_tag + 2] << 8);
+		} else {
+			next_tag += tag & 7;
+			tag >>= 3;
+		}
+
+		if (tag != ISA_TAG_END)
+			continue;
+
+		if (next_tag > i) {
+			err = pci_vpd_read_cap(dev, addr, vpd, i);
+			i += 4;
+		}
+		break;
+	}
+
+	if (err)
+		return err;
+
+	err = pci_dev_add_vpd(&dev->dev, vpd, i);
+
+	kfree(vpd);
+	return err;
+}
+
+/* PCI 2.1 specified a ROM-based method of finding the VPD. */
+static int pci_setup_vpd_old(struct pci_dev *dev)
+{
+	return -ENXIO;
+}
+
+int pci_setup_vpd(struct pci_dev *dev)
+{
+	int vpd_addr = pci_find_capability(dev, PCI_CAP_ID_VPD);
+
+	if (vpd_addr)
+		return pci_setup_vpd_cap(dev, vpd_addr);
+	return pci_setup_vpd_old(dev);
+}
+
+EXPORT_SYMBOL(pci_setup_vpd);
+EXPORT_SYMBOL(device_add_vpd);
+EXPORT_SYMBOL(device_remove_vpd);
Index: vpd-2.6/drivers/pnp/isapnp/core.c
===================================================================
RCS file: /var/cvs/linux-2.6/drivers/pnp/isapnp/core.c,v
retrieving revision 1.8
diff -u -p -r1.8 core.c
--- vpd-2.6/drivers/pnp/isapnp/core.c	20 Jul 2004 22:06:37 -0000	1.8
+++ vpd-2.6/drivers/pnp/isapnp/core.c	24 Aug 2004 18:59:50 -0000
@@ -72,24 +72,24 @@ MODULE_LICENSE("GPL");
 #define _PNPWRP		0xa79
 
 /* short tags */
-#define _STAG_PNPVERNO		0x01
-#define _STAG_LOGDEVID		0x02
-#define _STAG_COMPATDEVID	0x03
-#define _STAG_IRQ		0x04
-#define _STAG_DMA		0x05
-#define _STAG_STARTDEP		0x06
-#define _STAG_ENDDEP		0x07
-#define _STAG_IOPORT		0x08
-#define _STAG_FIXEDIO		0x09
-#define _STAG_VENDOR		0x0e
-#define _STAG_END		0x0f
+#define _STAG_PNPVERNO		ISA_TAG_PNPVERNO
+#define _STAG_LOGDEVID		ISA_TAG_LOGDEVID
+#define _STAG_COMPATDEVID	ISA_TAG_COMPATDEVID
+#define _STAG_IRQ		ISA_TAG_IRQ
+#define _STAG_DMA		ISA_TAG_DMA
+#define _STAG_STARTDEP		ISA_TAG_STARTDEP
+#define _STAG_ENDDEP		ISA_TAG_ENDDEP
+#define _STAG_IOPORT		ISA_TAG_IOPORT
+#define _STAG_FIXEDIO		ISA_TAG_FIXEDIO
+#define _STAG_VENDOR		ISA_TAG_VENDOR_S
+#define _STAG_END		ISA_TAG_END
 /* long tags */
-#define _LTAG_MEMRANGE		0x81
-#define _LTAG_ANSISTR		0x82
-#define _LTAG_UNICODESTR	0x83
-#define _LTAG_VENDOR		0x84
-#define _LTAG_MEM32RANGE	0x85
-#define _LTAG_FIXEDMEM32RANGE	0x86
+#define _LTAG_MEMRANGE		ISA_TAG_MEMRANGE
+#define _LTAG_ANSISTR		ISA_TAG_ANSIIDSTR
+#define _LTAG_UNICODESTR	ISA_TAG_UNICODEIDSTR
+#define _LTAG_VENDOR		ISA_TAG_VENDOR_L
+#define _LTAG_MEM32RANGE	ISA_TAG_MEM32RANGE
+#define _LTAG_FIXEDMEM32RANGE	ISA_TAG_FIXEDMEM32RANGE
 
 static unsigned char isapnp_checksum_value;
 static DECLARE_MUTEX(isapnp_cfg_mutex);
Index: vpd-2.6/fs/Kconfig
===================================================================
RCS file: /var/cvs/linux-2.6/fs/Kconfig,v
retrieving revision 1.17
diff -u -p -r1.17 Kconfig
--- vpd-2.6/fs/Kconfig	13 Aug 2004 14:30:02 -0000	1.17
+++ vpd-2.6/fs/Kconfig	24 Aug 2004 18:59:51 -0000
@@ -832,6 +832,10 @@ config SYSFS
 
 	Designers of embedded systems may wish to say N here to conserve space.
 
+config VPD
+	def_bool y
+	depends on SYSFS
+
 config DEVFS_FS
 	bool "/dev file system support (OBSOLETE)"
 	depends on EXPERIMENTAL
Index: vpd-2.6/include/linux/isapnp.h
===================================================================
RCS file: /var/cvs/linux-2.6/include/linux/isapnp.h,v
retrieving revision 1.1
diff -u -p -r1.1 isapnp.h
--- vpd-2.6/include/linux/isapnp.h	29 Jul 2003 17:02:13 -0000	1.1
+++ vpd-2.6/include/linux/isapnp.h	24 Aug 2004 18:59:55 -0000
@@ -91,6 +91,29 @@ struct isapnp_device_id {
 	unsigned long driver_data;	/* data private to the driver */
 };
 
+
+enum isapnp_tag {
+	ISA_TAG_PNPVERNO = 0x01,
+	ISA_TAG_LOGDEVID = 0x02,
+	ISA_TAG_COMPATDEVID = 0x03,
+	ISA_TAG_IRQ = 0x04,
+	ISA_TAG_DMA = 0x05,
+	ISA_TAG_STARTDEP = 0x06,
+	ISA_TAG_ENDDEP = 0x07,
+	ISA_TAG_IOPORT = 0x08,
+	ISA_TAG_FIXEDIO = 0x09,
+	ISA_TAG_VENDOR_S = 0x0e,
+	ISA_TAG_END = 0x0f,
+	ISA_TAG_MEMRANGE = 0x81,
+	ISA_TAG_ANSIIDSTR = 0x82,
+	ISA_TAG_UNICODEIDSTR = 0x83,
+	ISA_TAG_VENDOR_L = 0x84,
+	ISA_TAG_MEM32RANGE = 0x85,
+	ISA_TAG_FIXEDMEM32RANGE = 0x86,
+	ISA_TAG_VPD_R = 0x90,
+	ISA_TAG_VPD_W = 0x91,
+};
+
 #if defined(CONFIG_ISAPNP) || (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE))
 
 #define __ISAPNP__
Index: vpd-2.6/include/linux/pci.h
===================================================================
RCS file: /var/cvs/linux-2.6/include/linux/pci.h,v
retrieving revision 1.17
diff -u -p -r1.17 pci.h
--- vpd-2.6/include/linux/pci.h	13 Aug 2004 14:30:23 -0000	1.17
+++ vpd-2.6/include/linux/pci.h	24 Aug 2004 18:59:55 -0000
@@ -784,6 +784,8 @@ int pci_restore_state(struct pci_dev *de
 int pci_set_power_state(struct pci_dev *dev, int state);
 int pci_enable_wake(struct pci_dev *dev, u32 state, int enable);
 
+int pci_setup_vpd(struct pci_dev *dev);
+
 /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */
 void pci_bus_assign_resources(struct pci_bus *bus);
 void pci_bus_size_bridges(struct pci_bus *bus);

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2004-08-24 19:10 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-08-14 18:29 VPD in sysfs Matthew Wilcox
2004-08-16  6:14 ` Martin Schwenke
2004-08-16 14:11 ` Luben Tuikov
2004-08-17  4:12 ` Douglas Gilbert
2004-08-20 14:21 ` Matthew Wilcox
2004-08-20 19:45   ` Dave C Boutcher
2004-08-24 19:09     ` Matthew Wilcox

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).