diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2008-03-05 16:52:39 (GMT) |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-04-21 04:47:07 (GMT) |
commit | 94e6108803469a37ee1e3c92dafdd1d59298602f (patch) | |
tree | 7f3ee30721411cca238f8eea5971d5bebbb70a55 /drivers/pci/pci-sysfs.c | |
parent | 5e0d2a6fc094a9b5047998deefeb1254c66856ee (diff) | |
download | linux-94e6108803469a37ee1e3c92dafdd1d59298602f.tar.xz |
PCI: Expose PCI VPD through sysfs
Vital Product Data (VPD) may be exposed by PCI devices in several
ways. It is generally unsafe to read this information through the
existing interfaces to user-land because of stateful interfaces.
This adds:
- abstract operations for VPD access (struct pci_vpd_ops)
- VPD state information in struct pci_dev (struct pci_vpd)
- an implementation of the VPD access method specified in PCI 2.2
(in access.c)
- a 'vpd' binary file in sysfs directories for PCI devices with VPD
operations defined
It adds a probe for PCI 2.2 VPD in pci_scan_device() and release of
VPD state in pci_release_dev().
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci/pci-sysfs.c')
-rw-r--r-- | drivers/pci/pci-sysfs.c | 109 |
1 files changed, 95 insertions, 14 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index f5b0b62..ae9a769 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -343,6 +343,58 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr, return count; } +static ssize_t +pci_read_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct pci_dev *dev = + to_pci_dev(container_of(kobj, struct device, kobj)); + int end; + int ret; + + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + end = off + count; + + while (off < end) { + ret = dev->vpd->ops->read(dev, off, end - off, buf); + if (ret < 0) + return ret; + buf += ret; + off += ret; + } + + return count; +} + +static ssize_t +pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct pci_dev *dev = + to_pci_dev(container_of(kobj, struct device, kobj)); + int end; + int ret; + + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + end = off + count; + + while (off < end) { + ret = dev->vpd->ops->write(dev, off, end - off, buf); + if (ret < 0) + return ret; + buf += ret; + off += ret; + } + + return count; +} + #ifdef HAVE_PCI_LEGACY /** * pci_read_legacy_io - read byte(s) from legacy I/O port space @@ -611,7 +663,7 @@ int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev) int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) { - struct bin_attribute *rom_attr = NULL; + struct bin_attribute *attr = NULL; int retval; if (!sysfs_initialized) @@ -624,22 +676,41 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) if (retval) goto err; + /* If the device has VPD, try to expose it in sysfs. */ + if (pdev->vpd) { + attr = kzalloc(sizeof(*attr), GFP_ATOMIC); + if (attr) { + pdev->vpd->attr = attr; + attr->size = pdev->vpd->ops->get_size(pdev); + attr->attr.name = "vpd"; + attr->attr.mode = S_IRUGO | S_IWUSR; + attr->read = pci_read_vpd; + attr->write = pci_write_vpd; + retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); + if (retval) + goto err_vpd; + } else { + retval = -ENOMEM; + goto err_config_file; + } + } + retval = pci_create_resource_files(pdev); if (retval) - goto err_bin_file; + goto err_vpd_file; /* If the device has a ROM, try to expose it in sysfs. */ if (pci_resource_len(pdev, PCI_ROM_RESOURCE) || (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)) { - rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); - if (rom_attr) { - pdev->rom_attr = rom_attr; - rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE); - rom_attr->attr.name = "rom"; - rom_attr->attr.mode = S_IRUSR; - rom_attr->read = pci_read_rom; - rom_attr->write = pci_write_rom; - retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); + attr = kzalloc(sizeof(*attr), GFP_ATOMIC); + if (attr) { + pdev->rom_attr = attr; + attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE); + attr->attr.name = "rom"; + attr->attr.mode = S_IRUSR; + attr->read = pci_read_rom; + attr->write = pci_write_rom; + retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); if (retval) goto err_rom; } else { @@ -657,12 +728,18 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) err_rom_file: if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) - sysfs_remove_bin_file(&pdev->dev.kobj, rom_attr); + sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); err_rom: - kfree(rom_attr); + kfree(pdev->rom_attr); err_resource_files: pci_remove_resource_files(pdev); -err_bin_file: +err_vpd_file: + if (pdev->vpd) { + sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr); +err_vpd: + kfree(pdev->vpd->attr); + } +err_config_file: if (pdev->cfg_size < 4096) sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); else @@ -684,6 +761,10 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) pcie_aspm_remove_sysfs_dev_files(pdev); + if (pdev->vpd) { + sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr); + kfree(pdev->vpd->attr); + } if (pdev->cfg_size < 4096) sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); else |