Narendra K | 911e1c9 | 2010-07-26 05:56:50 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Purpose: Export the firmware instance and label associated with |
| 3 | * a pci device to sysfs |
| 4 | * Copyright (C) 2010 Dell Inc. |
| 5 | * by Narendra K <Narendra_K@dell.com>, |
| 6 | * Jordan Hargrave <Jordan_Hargrave@dell.com> |
| 7 | * |
| 8 | * SMBIOS defines type 41 for onboard pci devices. This code retrieves |
| 9 | * the instance number and string from the type 41 record and exports |
| 10 | * it to sysfs. |
| 11 | * |
| 12 | * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more |
| 13 | * information. |
| 14 | */ |
| 15 | |
| 16 | #include <linux/dmi.h> |
| 17 | #include <linux/sysfs.h> |
| 18 | #include <linux/pci.h> |
| 19 | #include <linux/pci_ids.h> |
| 20 | #include <linux/module.h> |
| 21 | #include <linux/device.h> |
| 22 | #include "pci.h" |
| 23 | |
| 24 | enum smbios_attr_enum { |
| 25 | SMBIOS_ATTR_NONE = 0, |
| 26 | SMBIOS_ATTR_LABEL_SHOW, |
| 27 | SMBIOS_ATTR_INSTANCE_SHOW, |
| 28 | }; |
| 29 | |
| 30 | static mode_t |
| 31 | find_smbios_instance_string(struct pci_dev *pdev, char *buf, |
| 32 | enum smbios_attr_enum attribute) |
| 33 | { |
| 34 | const struct dmi_device *dmi; |
| 35 | struct dmi_dev_onboard *donboard; |
| 36 | int bus; |
| 37 | int devfn; |
| 38 | |
| 39 | bus = pdev->bus->number; |
| 40 | devfn = pdev->devfn; |
| 41 | |
| 42 | dmi = NULL; |
| 43 | while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, |
| 44 | NULL, dmi)) != NULL) { |
| 45 | donboard = dmi->device_data; |
| 46 | if (donboard && donboard->bus == bus && |
| 47 | donboard->devfn == devfn) { |
| 48 | if (buf) { |
| 49 | if (attribute == SMBIOS_ATTR_INSTANCE_SHOW) |
| 50 | return scnprintf(buf, PAGE_SIZE, |
| 51 | "%d\n", |
| 52 | donboard->instance); |
| 53 | else if (attribute == SMBIOS_ATTR_LABEL_SHOW) |
| 54 | return scnprintf(buf, PAGE_SIZE, |
| 55 | "%s\n", |
| 56 | dmi->name); |
| 57 | } |
| 58 | return strlen(dmi->name); |
| 59 | } |
| 60 | } |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | static mode_t |
| 65 | smbios_instance_string_exist(struct kobject *kobj, struct attribute *attr, |
| 66 | int n) |
| 67 | { |
| 68 | struct device *dev; |
| 69 | struct pci_dev *pdev; |
| 70 | |
| 71 | dev = container_of(kobj, struct device, kobj); |
| 72 | pdev = to_pci_dev(dev); |
| 73 | |
| 74 | return find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE) ? |
| 75 | S_IRUGO : 0; |
| 76 | } |
| 77 | |
| 78 | static ssize_t |
| 79 | smbioslabel_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 80 | { |
| 81 | struct pci_dev *pdev; |
| 82 | pdev = to_pci_dev(dev); |
| 83 | |
| 84 | return find_smbios_instance_string(pdev, buf, |
| 85 | SMBIOS_ATTR_LABEL_SHOW); |
| 86 | } |
| 87 | |
| 88 | static ssize_t |
| 89 | smbiosinstance_show(struct device *dev, |
| 90 | struct device_attribute *attr, char *buf) |
| 91 | { |
| 92 | struct pci_dev *pdev; |
| 93 | pdev = to_pci_dev(dev); |
| 94 | |
| 95 | return find_smbios_instance_string(pdev, buf, |
| 96 | SMBIOS_ATTR_INSTANCE_SHOW); |
| 97 | } |
| 98 | |
| 99 | static struct device_attribute smbios_attr_label = { |
Stephen Rothwell | 763e9db | 2010-08-04 14:25:31 +1000 | [diff] [blame] | 100 | .attr = {.name = "label", .mode = 0444}, |
Narendra K | 911e1c9 | 2010-07-26 05:56:50 -0500 | [diff] [blame] | 101 | .show = smbioslabel_show, |
| 102 | }; |
| 103 | |
| 104 | static struct device_attribute smbios_attr_instance = { |
Stephen Rothwell | 763e9db | 2010-08-04 14:25:31 +1000 | [diff] [blame] | 105 | .attr = {.name = "index", .mode = 0444}, |
Narendra K | 911e1c9 | 2010-07-26 05:56:50 -0500 | [diff] [blame] | 106 | .show = smbiosinstance_show, |
| 107 | }; |
| 108 | |
| 109 | static struct attribute *smbios_attributes[] = { |
| 110 | &smbios_attr_label.attr, |
| 111 | &smbios_attr_instance.attr, |
| 112 | NULL, |
| 113 | }; |
| 114 | |
| 115 | static struct attribute_group smbios_attr_group = { |
| 116 | .attrs = smbios_attributes, |
| 117 | .is_visible = smbios_instance_string_exist, |
| 118 | }; |
| 119 | |
| 120 | static int |
| 121 | pci_create_smbiosname_file(struct pci_dev *pdev) |
| 122 | { |
| 123 | if (!sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group)) |
| 124 | return 0; |
| 125 | return -ENODEV; |
| 126 | } |
| 127 | |
| 128 | static void |
| 129 | pci_remove_smbiosname_file(struct pci_dev *pdev) |
| 130 | { |
| 131 | sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); |
| 132 | } |
| 133 | |
| 134 | void pci_create_firmware_label_files(struct pci_dev *pdev) |
| 135 | { |
| 136 | if (!pci_create_smbiosname_file(pdev)) |
| 137 | ; |
| 138 | } |
| 139 | |
| 140 | void pci_remove_firmware_label_files(struct pci_dev *pdev) |
| 141 | { |
| 142 | pci_remove_smbiosname_file(pdev); |
| 143 | } |