public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Matthew Wilcox <willy@debian.org>
To: boutcher@cs.umn.edu
Cc: Matthew Wilcox <willy@debian.org>, Greg KH <greg@kroah.com>,
	Patrick Mochel <mochel@osdl.org>,
	martins@au.ibm.com, linux-kernel@vger.kernel.org,
	linux-scsi@vger.kernel.org
Subject: Re: VPD in sysfs
Date: Tue, 24 Aug 2004 20:09:31 +0100	[thread overview]
Message-ID: <20040824190931.GB16196@parcelfarce.linux.theplanet.co.uk> (raw)
In-Reply-To: <20040820194525.GA13970@cs.umn.edu>

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

      reply	other threads:[~2004-08-24 19:10 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20040824190931.GB16196@parcelfarce.linux.theplanet.co.uk \
    --to=willy@debian.org \
    --cc=boutcher@cs.umn.edu \
    --cc=greg@kroah.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=martins@au.ibm.com \
    --cc=mochel@osdl.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox