diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/Makefile | 3 | ||||
-rw-r--r-- | drivers/pci/pci-label.c | 143 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 5 | ||||
-rw-r--r-- | drivers/pci/pci.h | 9 |
4 files changed, 160 insertions, 0 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 0b51857fbaf..dc1aa092286 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -55,6 +55,9 @@ obj-$(CONFIG_MICROBLAZE) += setup-bus.o # obj-$(CONFIG_ACPI) += pci-acpi.o +# SMBIOS provided firmware instance and labels +obj-$(CONFIG_DMI) += pci-label.o + # Cardbus & CompactPCI use setup-bus obj-$(CONFIG_HOTPLUG) += setup-bus.o diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c new file mode 100644 index 00000000000..111500e86f9 --- /dev/null +++ b/drivers/pci/pci-label.c @@ -0,0 +1,143 @@ +/* + * Purpose: Export the firmware instance and 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> + * + * SMBIOS defines type 41 for onboard pci devices. This code retrieves + * the instance number and string from the type 41 record and exports + * it to sysfs. + * + * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more + * information. + */ + +#include <linux/dmi.h> +#include <linux/sysfs.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/module.h> +#include <linux/device.h> +#include "pci.h" + +enum smbios_attr_enum { + SMBIOS_ATTR_NONE = 0, + SMBIOS_ATTR_LABEL_SHOW, + SMBIOS_ATTR_INSTANCE_SHOW, +}; + +static mode_t +find_smbios_instance_string(struct pci_dev *pdev, char *buf, + enum smbios_attr_enum attribute) +{ + const struct dmi_device *dmi; + struct dmi_dev_onboard *donboard; + int bus; + int devfn; + + bus = pdev->bus->number; + devfn = pdev->devfn; + + dmi = NULL; + while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, + NULL, dmi)) != NULL) { + donboard = dmi->device_data; + if (donboard && donboard->bus == bus && + donboard->devfn == devfn) { + if (buf) { + if (attribute == SMBIOS_ATTR_INSTANCE_SHOW) + return scnprintf(buf, PAGE_SIZE, + "%d\n", + donboard->instance); + else if (attribute == SMBIOS_ATTR_LABEL_SHOW) + return scnprintf(buf, PAGE_SIZE, + "%s\n", + dmi->name); + } + return strlen(dmi->name); + } + } + return 0; +} + +static mode_t +smbios_instance_string_exist(struct kobject *kobj, struct attribute *attr, + int n) +{ + struct device *dev; + struct pci_dev *pdev; + + dev = container_of(kobj, struct device, kobj); + pdev = to_pci_dev(dev); + + return find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE) ? + S_IRUGO : 0; +} + +static ssize_t +smbioslabel_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev; + pdev = to_pci_dev(dev); + + return find_smbios_instance_string(pdev, buf, + SMBIOS_ATTR_LABEL_SHOW); +} + +static ssize_t +smbiosinstance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev; + pdev = to_pci_dev(dev); + + return find_smbios_instance_string(pdev, buf, + SMBIOS_ATTR_INSTANCE_SHOW); +} + +static struct device_attribute smbios_attr_label = { + .attr = {.name = "label", .mode = 0444, .owner = THIS_MODULE}, + .show = smbioslabel_show, +}; + +static struct device_attribute smbios_attr_instance = { + .attr = {.name = "index", .mode = 0444, .owner = THIS_MODULE}, + .show = smbiosinstance_show, +}; + +static struct attribute *smbios_attributes[] = { + &smbios_attr_label.attr, + &smbios_attr_instance.attr, + NULL, +}; + +static struct attribute_group smbios_attr_group = { + .attrs = smbios_attributes, + .is_visible = smbios_instance_string_exist, +}; + +static int +pci_create_smbiosname_file(struct pci_dev *pdev) +{ + if (!sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group)) + return 0; + return -ENODEV; +} + +static void +pci_remove_smbiosname_file(struct pci_dev *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); +} + +void pci_create_firmware_label_files(struct pci_dev *pdev) +{ + if (!pci_create_smbiosname_file(pdev)) + ; +} + +void pci_remove_firmware_label_files(struct pci_dev *pdev) +{ + pci_remove_smbiosname_file(pdev); +} diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index f7692dc531e..b5a7d9bfcb2 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1165,6 +1165,8 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) if (retval) goto err_vga_file; + pci_create_firmware_label_files(pdev); + return 0; err_vga_file: @@ -1232,6 +1234,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_firmware_label_files(pdev); + } static int __init pci_sysfs_init(void) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f8077b3c8c8..d930338e092 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -11,6 +11,15 @@ extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env); extern int pci_create_sysfs_dev_files(struct pci_dev *pdev); extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev); +#ifndef CONFIG_DMI +static inline void pci_create_firmware_label_files(struct pci_dev *pdev) +{ return 0; } +static inline void pci_remove_firmware_label_files(struct pci_dev *pdev) +{ return 0; } +#else +extern void pci_create_firmware_label_files(struct pci_dev *pdev); +extern void pci_remove_firmware_label_files(struct pci_dev *pdev); +#endif extern void pci_cleanup_rom(struct pci_dev *dev); #ifdef HAVE_PCI_MMAP extern int pci_mmap_fits(struct pci_dev *pdev, int resno, |