linux/drivers/pci/pci-label.c
<<
>>
Prefs
   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 * PCI Firmware Specification Revision 3.1 section 4.6.7 (DSM for Naming a
   9 * PCI or PCI Express Device Under Operating Systems) defines an instance
  10 * number and string name. This code retrieves them and exports them to sysfs.
  11 * If the system firmware does not provide the ACPI _DSM (Device Specific
  12 * Method), then the SMBIOS type 41 instance number and string is exported to
  13 * sysfs.
  14 *
  15 * SMBIOS defines type 41 for onboard pci devices. This code retrieves
  16 * the instance number and string from the type 41 record and exports
  17 * it to sysfs.
  18 *
  19 * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more
  20 * information.
  21 */
  22
  23#include <linux/dmi.h>
  24#include <linux/sysfs.h>
  25#include <linux/pci.h>
  26#include <linux/pci_ids.h>
  27#include <linux/module.h>
  28#include <linux/device.h>
  29#include <linux/nls.h>
  30#include <linux/acpi.h>
  31#include <linux/pci-acpi.h>
  32#include "pci.h"
  33
  34#define DEVICE_LABEL_DSM        0x07
  35
  36#ifdef CONFIG_DMI
  37enum smbios_attr_enum {
  38        SMBIOS_ATTR_NONE = 0,
  39        SMBIOS_ATTR_LABEL_SHOW,
  40        SMBIOS_ATTR_INSTANCE_SHOW,
  41};
  42
  43static size_t
  44find_smbios_instance_string(struct pci_dev *pdev, char *buf,
  45                            enum smbios_attr_enum attribute)
  46{
  47        const struct dmi_device *dmi;
  48        struct dmi_dev_onboard *donboard;
  49        int bus;
  50        int devfn;
  51
  52        bus = pdev->bus->number;
  53        devfn = pdev->devfn;
  54
  55        dmi = NULL;
  56        while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD,
  57                                      NULL, dmi)) != NULL) {
  58                donboard = dmi->device_data;
  59                if (donboard && donboard->bus == bus &&
  60                                        donboard->devfn == devfn) {
  61                        if (buf) {
  62                                if (attribute == SMBIOS_ATTR_INSTANCE_SHOW)
  63                                        return scnprintf(buf, PAGE_SIZE,
  64                                                         "%d\n",
  65                                                         donboard->instance);
  66                                else if (attribute == SMBIOS_ATTR_LABEL_SHOW)
  67                                        return scnprintf(buf, PAGE_SIZE,
  68                                                         "%s\n",
  69                                                         dmi->name);
  70                        }
  71                        return strlen(dmi->name);
  72                }
  73        }
  74        return 0;
  75}
  76
  77static umode_t
  78smbios_instance_string_exist(struct kobject *kobj, struct attribute *attr,
  79                             int n)
  80{
  81        struct device *dev;
  82        struct pci_dev *pdev;
  83
  84        dev = container_of(kobj, struct device, kobj);
  85        pdev = to_pci_dev(dev);
  86
  87        return find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE) ?
  88                                           S_IRUGO : 0;
  89}
  90
  91static ssize_t
  92smbioslabel_show(struct device *dev, struct device_attribute *attr, char *buf)
  93{
  94        struct pci_dev *pdev;
  95        pdev = to_pci_dev(dev);
  96
  97        return find_smbios_instance_string(pdev, buf,
  98                                           SMBIOS_ATTR_LABEL_SHOW);
  99}
 100
 101static ssize_t
 102smbiosinstance_show(struct device *dev,
 103                    struct device_attribute *attr, char *buf)
 104{
 105        struct pci_dev *pdev;
 106        pdev = to_pci_dev(dev);
 107
 108        return find_smbios_instance_string(pdev, buf,
 109                                           SMBIOS_ATTR_INSTANCE_SHOW);
 110}
 111
 112static struct device_attribute smbios_attr_label = {
 113        .attr = {.name = "label", .mode = 0444},
 114        .show = smbioslabel_show,
 115};
 116
 117static struct device_attribute smbios_attr_instance = {
 118        .attr = {.name = "index", .mode = 0444},
 119        .show = smbiosinstance_show,
 120};
 121
 122static struct attribute *smbios_attributes[] = {
 123        &smbios_attr_label.attr,
 124        &smbios_attr_instance.attr,
 125        NULL,
 126};
 127
 128static struct attribute_group smbios_attr_group = {
 129        .attrs = smbios_attributes,
 130        .is_visible = smbios_instance_string_exist,
 131};
 132
 133static int
 134pci_create_smbiosname_file(struct pci_dev *pdev)
 135{
 136        return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group);
 137}
 138
 139static void
 140pci_remove_smbiosname_file(struct pci_dev *pdev)
 141{
 142        sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group);
 143}
 144#else
 145static inline int
 146pci_create_smbiosname_file(struct pci_dev *pdev)
 147{
 148        return -1;
 149}
 150
 151static inline void
 152pci_remove_smbiosname_file(struct pci_dev *pdev)
 153{
 154}
 155#endif
 156
 157#ifdef CONFIG_ACPI
 158static const char device_label_dsm_uuid[] = {
 159        0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D,
 160        0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D
 161};
 162
 163enum acpi_attr_enum {
 164        ACPI_ATTR_LABEL_SHOW,
 165        ACPI_ATTR_INDEX_SHOW,
 166};
 167
 168static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
 169{
 170        int len;
 171        len = utf16s_to_utf8s((const wchar_t *)obj->string.pointer,
 172                              obj->string.length,
 173                              UTF16_LITTLE_ENDIAN,
 174                              buf, PAGE_SIZE);
 175        buf[len] = '\n';
 176}
 177
 178static int
 179dsm_get_label(struct device *dev, char *buf, enum acpi_attr_enum attr)
 180{
 181        acpi_handle handle;
 182        union acpi_object *obj, *tmp;
 183        int len = -1;
 184
 185        handle = ACPI_HANDLE(dev);
 186        if (!handle)
 187                return -1;
 188
 189        obj = acpi_evaluate_dsm(handle, device_label_dsm_uuid, 0x2,
 190                                DEVICE_LABEL_DSM, NULL);
 191        if (!obj)
 192                return -1;
 193
 194        tmp = obj->package.elements;
 195        if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 2 &&
 196            tmp[0].type == ACPI_TYPE_INTEGER &&
 197            tmp[1].type == ACPI_TYPE_STRING) {
 198                /*
 199                 * The second string element is optional even when
 200                 * this _DSM is implemented; when not implemented,
 201                 * this entry must return a null string.
 202                 */
 203                if (attr == ACPI_ATTR_INDEX_SHOW)
 204                        scnprintf(buf, PAGE_SIZE, "%llu\n", tmp->integer.value);
 205                else if (attr == ACPI_ATTR_LABEL_SHOW)
 206                        dsm_label_utf16s_to_utf8s(tmp + 1, buf);
 207                len = strlen(buf) > 0 ? strlen(buf) : -1;
 208        }
 209
 210        ACPI_FREE(obj);
 211
 212        return len;
 213}
 214
 215static bool
 216device_has_dsm(struct device *dev)
 217{
 218        acpi_handle handle;
 219
 220        handle = ACPI_HANDLE(dev);
 221        if (!handle)
 222                return false;
 223
 224        return !!acpi_check_dsm(handle, device_label_dsm_uuid, 0x2,
 225                                1 << DEVICE_LABEL_DSM);
 226}
 227
 228static umode_t
 229acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n)
 230{
 231        struct device *dev;
 232
 233        dev = container_of(kobj, struct device, kobj);
 234
 235        if (device_has_dsm(dev))
 236                return S_IRUGO;
 237
 238        return 0;
 239}
 240
 241static ssize_t
 242acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf)
 243{
 244        return dsm_get_label(dev, buf, ACPI_ATTR_LABEL_SHOW);
 245}
 246
 247static ssize_t
 248acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf)
 249{
 250        return dsm_get_label(dev, buf, ACPI_ATTR_INDEX_SHOW);
 251}
 252
 253static struct device_attribute acpi_attr_label = {
 254        .attr = {.name = "label", .mode = 0444},
 255        .show = acpilabel_show,
 256};
 257
 258static struct device_attribute acpi_attr_index = {
 259        .attr = {.name = "acpi_index", .mode = 0444},
 260        .show = acpiindex_show,
 261};
 262
 263static struct attribute *acpi_attributes[] = {
 264        &acpi_attr_label.attr,
 265        &acpi_attr_index.attr,
 266        NULL,
 267};
 268
 269static struct attribute_group acpi_attr_group = {
 270        .attrs = acpi_attributes,
 271        .is_visible = acpi_index_string_exist,
 272};
 273
 274static int
 275pci_create_acpi_index_label_files(struct pci_dev *pdev)
 276{
 277        return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group);
 278}
 279
 280static int
 281pci_remove_acpi_index_label_files(struct pci_dev *pdev)
 282{
 283        sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group);
 284        return 0;
 285}
 286#else
 287static inline int
 288pci_create_acpi_index_label_files(struct pci_dev *pdev)
 289{
 290        return -1;
 291}
 292
 293static inline int
 294pci_remove_acpi_index_label_files(struct pci_dev *pdev)
 295{
 296        return -1;
 297}
 298
 299static inline bool
 300device_has_dsm(struct device *dev)
 301{
 302        return false;
 303}
 304#endif
 305
 306void pci_create_firmware_label_files(struct pci_dev *pdev)
 307{
 308        if (device_has_dsm(&pdev->dev))
 309                pci_create_acpi_index_label_files(pdev);
 310        else
 311                pci_create_smbiosname_file(pdev);
 312}
 313
 314void pci_remove_firmware_label_files(struct pci_dev *pdev)
 315{
 316        if (device_has_dsm(&pdev->dev))
 317                pci_remove_acpi_index_label_files(pdev);
 318        else
 319                pci_remove_smbiosname_file(pdev);
 320}
 321