 drivers/firmware/dmi_scan.c |   24 ++++++
 drivers/pci/Makefile        |    2 +-
 drivers/pci/pci-label.c     |  163 +++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci-sysfs.c     |    7 ++
 include/linux/dmi.h         |    9 +++
 include/linux/pci-label.h   |   33 +++++++++
 6 files changed, 237 insertions(+), 1 deletions(-)

diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 31b983d..ebdfc43 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -278,6 +278,28 @@ static void __init dmi_save_ipmi_device(const struct dmi_header *dm)
 	list_add_tail(&dev->list, &dmi_devices);
 }
 
+static void __init dmi_save_devslot(int id, int seg, int bus, int devfn, const char *name)
+{
+	struct dmi_devslot *slot;
+
+	slot = dmi_alloc(sizeof(*slot) + strlen(name) + 1);
+	if (!slot) {
+		printk(KERN_ERR "dmi_save_devslot: out of memory.\n");
+		return;
+	}	
+	slot->id = id;
+	slot->seg = seg;
+	slot->bus = bus;
+	slot->devfn = devfn;
+
+	strcpy((char *)&slot[1], name);
+	slot->dev.type = DMI_DEV_TYPE_DEVSLOT;
+	slot->dev.name = (char *)&slot[1];
+	slot->dev.device_data = slot;
+
+	list_add(&slot->dev.list, &dmi_devices);
+}
+
 static void __init dmi_save_extended_devices(const struct dmi_header *dm)
 {
 	const u8 *d = (u8*) dm + 5;
@@ -286,6 +308,7 @@ static void __init dmi_save_extended_devices(const struct dmi_header *dm)
 	if ((*d & 0x80) == 0)
 		return;
 
+	dmi_save_devslot(-1, *(u16 *)(d+2), *(d+4), *(d+5), dmi_string_nosave(dm, *(d-1)));
 	dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d - 1)));
 }
 
@@ -334,6 +357,7 @@ static void __init dmi_decode(const struct dmi_header *dm, void *dummy)
 		break;
 	case 41:	/* Onboard Devices Extended Information */
 		dmi_save_extended_devices(dm);
+		break;
 	}
 }
 
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 0b51857..69c503a 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -4,7 +4,7 @@
 
 obj-y		+= access.o bus.o probe.o remove.o pci.o \
 			pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
-			irq.o vpd.o
+			irq.o vpd.o pci-label.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSFS) += slot.o
 
diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c
new file mode 100644
index 0000000..8e929f0
--- /dev/null
+++ b/drivers/pci/pci-label.c
@@ -0,0 +1,163 @@
+/*
+ * File:	drivers/pci/pci-label.c
+ * Purpose:	Export the label associated with a pci device to sysfs
+ * Copyright (C) 2010 Dell Inc.
+ * by Narendra K <Narendra_K@dell.com>, Jordan Hargrave <Jordan_Hargrave@dell.com>
+ *
+ * This code checks if the pci device has a related ACPI _DSM. If available,
+ * the code calls the _DSM to retrieve the string and exports the string to 
+ * sysfs. If the ACPI _DSM is not available, it falls back on SMBIOS.
+ * SMBIOS defines type 41 for onboard pci devices. This code retrieves strings
+ * associated with the type 41 and exports it to sysfs. 
+ *
+ * For ex: 
+ *
+ * cat /sys/bus/pci/devices/0000\:0a\:00.0/label
+ * Embedded NIC 1
+ *
+ * cat /sys/class/net/eth0/device/label
+ * Embedded NIC 1
+ *
+ * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more
+ * information.
+ */
+
+#include <linux/pci-label.h>
+
+static ssize_t
+smbiosname_string_is_valid(struct device *dev, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+  	const struct dmi_device *dmi;
+  	struct dmi_devslot *dslot;
+  	int bus;
+  	int devfn;
+
+  	bus = pdev->bus->number;
+  	devfn = pdev->devfn;
+
+  	dmi = NULL;
+  	while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEVSLOT, NULL, dmi)) != NULL) {
+    		dslot = dmi->device_data;
+    		if (dslot && dslot->bus == bus && dslot->devfn == devfn) {
+			if (buf)
+      				return scnprintf(buf, PAGE_SIZE, "%s\n", dmi->name);
+			return strlen(dmi->name);
+		}
+	}
+	
+	return 0;
+}
+
+static ssize_t
+smbiosname_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return smbiosname_string_is_valid(dev, buf);
+}
+
+struct smbios_attribute smbios_attr_label = {
+	.attr = {.name = __stringify(label), .mode = 0444},
+	.show = smbiosname_show,
+	.test = smbiosname_string_is_valid,
+};
+
+static bool 
+pci_create_smbiosname_file(struct pci_dev *pdev)
+{
+	/* if the device does not have an associated smbios string in the smbios table, do not create this attribute */ 
+	if (smbios_attr_label.test && smbios_attr_label.test(&pdev->dev, NULL)) {
+		sysfs_create_file(&pdev->dev.kobj, &smbios_attr_label.attr);
+		return true;
+	}
+	return false;	
+}
+
+static bool 
+pci_remove_smbiosname_file(struct pci_dev *pdev)
+{
+	/* if the device does not have an associated smbios string in the smbios table, do not create this attribute */ 
+	if (smbios_attr_label.test && smbios_attr_label.test(&pdev->dev, NULL)) {
+		sysfs_remove_file(&pdev->dev.kobj, &smbios_attr_label.attr);
+		return true;
+	}
+	return false;
+}
+
+static ssize_t
+acpiname_string_is_valid(struct device *dev, char *buf)
+{
+/* Place holder for the actual ACPI _DSM analysis */
+
+/*	struct pci_dev *pdev = to_pci_dev(dev);
+  	int bus;
+  	int devfn;
+
+  	bus = pdev->bus->number;
+  	devfn = pdev->devfn;
+*/
+	return 0;
+
+}
+
+static ssize_t
+acpiname_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return acpiname_string_is_valid(dev, buf);
+}
+
+struct acpi_attribute acpi_attr_label = {
+	.attr = {.name = __stringify(label), .mode = 0444},
+	.show = acpiname_show,
+	.test = acpiname_string_is_valid,
+};
+
+static bool 
+pci_create_acpiname_file(struct pci_dev *pdev)
+{
+	/* For testing purpose */
+	return false;
+	/* if the device does not have an associated string in the ACPI table, do not create this attribute */ 
+	if (acpi_attr_label.test && acpi_attr_label.test(&pdev->dev, NULL)) {
+		sysfs_create_file(&pdev->dev.kobj, &acpi_attr_label.attr);
+		return true;
+	}
+	return false;
+}
+
+static bool 
+pci_remove_acpiname_file(struct pci_dev *pdev)
+{
+	/* For testing purpose */
+	return false;
+	/* if the device does not have an associated string in the ACPI table, do not create this attribute */ 
+	if (acpi_attr_label.test && acpi_attr_label.test(&pdev->dev, NULL)) {
+		sysfs_remove_file(&pdev->dev.kobj, &acpi_attr_label.attr);
+		return true;
+	}
+	return false;
+}
+
+int pci_create_label_file(struct pci_dev *pdev)
+{
+	/* if the the pci device has an ACPI _DSM provided string use it as the device label */
+	if (pci_create_acpiname_file(pdev))
+		return 0;
+	/* if the above method is not available, fallback to SMBIOS string */
+	if (pci_create_smbiosname_file(pdev))
+		return 0;
+	/* if SMBIOS is not available, export no string */
+	return -ENOLABEL;
+}
+EXPORT_SYMBOL(pci_create_label_file);
+
+int pci_remove_label_file(struct pci_dev *pdev)
+{
+	if (pci_remove_acpiname_file(pdev))
+		return 0;
+	if (pci_remove_smbiosname_file(pdev))
+		return 0;
+	return -ENOLABEL;
+
+}
+EXPORT_SYMBOL(pci_remove_label_file);
+
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 9976685..82cd02c 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -23,6 +23,7 @@
 #include <linux/mm.h>
 #include <linux/capability.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-label.h>
 #include "pci.h"
 
 static int sysfs_initialized;	/* = 0 */
@@ -1072,6 +1073,9 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
 	if (retval)
 		goto err_vga_file;
 
+	/* add the label to sysfs if the pci device has a valid label */
+	pci_create_label_file(pdev);
+
 	return 0;
 
 err_vga_file:
@@ -1139,6 +1143,9 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
 		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
 		kfree(pdev->rom_attr);
 	}
+
+	pci_remove_label_file(pdev);
+
 }
 
 static int __init pci_sysfs_init(void)
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
index a8a3e1a..cc57c3a 100644
--- a/include/linux/dmi.h
+++ b/include/linux/dmi.h
@@ -20,6 +20,7 @@ enum dmi_device_type {
 	DMI_DEV_TYPE_SAS,
 	DMI_DEV_TYPE_IPMI = -1,
 	DMI_DEV_TYPE_OEM_STRING = -2,
+	DMI_DEV_TYPE_DEVSLOT = -3,
 };
 
 struct dmi_header {
@@ -37,6 +38,14 @@ struct dmi_device {
 
 #ifdef CONFIG_DMI
 
+struct dmi_devslot {
+	struct dmi_device dev;
+	int id;
+	int seg;
+	int bus;
+	int devfn;
+};
+
 extern int dmi_check_system(const struct dmi_system_id *list);
 const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list);
 extern const char * dmi_get_system_info(int field);
diff --git a/include/linux/pci-label.h b/include/linux/pci-label.h
new file mode 100644
index 0000000..172060f
--- /dev/null
+++ b/include/linux/pci-label.h
@@ -0,0 +1,33 @@
+/*
+ * File		include/linux/pci-label.h
+ * Copyright (C) 2010 Dell Inc.
+ * by Narendra K <Narendra_K@dell.com>, Jordan Hargrave <Jordan_Hargrave@dell.com>
+ */
+
+#ifndef _PCI_LABEL_H_
+#define _PCI_LABEL_H_
+
+#include <linux/dmi.h>
+#include <linux/sysfs.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+
+#define ENOLABEL 	1
+
+struct smbios_attribute {
+	struct attribute attr;
+	ssize_t (*show) (struct device *dev, char *buf);
+	ssize_t (*test) (struct device *dev, char *buf);
+};
+
+struct acpi_attribute {
+	struct attribute attr;
+	ssize_t (*show) (struct device *dev, char *buf);
+	ssize_t (*test) (struct device *dev, char *buf);
+};
+
+extern int pci_create_label_file(struct pci_dev *pdev);
+extern int pci_remove_label_file(struct pci_dev *pdev);
+
+#endif  /* _PCI_LABEL_H_ */
+
