From 3c0f3e8c05dc174e98af266c3dcc2f8a260d68ca Mon Sep 17 00:00:00 2001 From: Narendra K Date: Tue, 27 Jul 2010 23:25:15 +0530 Subject: [PATCH 2/2] Export ACPI provided firmware index and label to sysfs This patch exports ACPI provided firmware index and label of pci devices to sysfs. This patch retrieves the index and label via the ACPI _DSM provided by the system firmware. Signed-off-by: Narendra K --- Documentation/ABI/testing/sysfs-bus-pci | 12 +- drivers/pci/Makefile | 5 +- drivers/pci/pci-label.c | 229 ++++++++++++++++++++++++++++++- drivers/pci/pci.h | 6 +- 4 files changed, 237 insertions(+), 15 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 11855ca..1e3e95d 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -185,9 +185,9 @@ Date: July 2010 Contact: Narendra K , linux-bugs@dell.com Description: Reading this attribute will provide the firmware - given name(SMBIOS type 41 string) of the PCI device. - The attribute will be created only if the firmware - has given a name to the PCI device. + given name(SMBIOS type 41 string or ACPI _DSM) of + the PCI device. The attribute will be created only + if the firmware has given a name to the PCI device. Users: Userspace applications interested in knowing the firmware assigned name of the PCI device. @@ -197,9 +197,9 @@ Date: July 2010 Contact: Narendra K , linux-bugs@dell.com Description: Reading this attribute will provide the firmware - given instance(SMBIOS type 41 device type instance) - of the PCI device. The attribute will be created - only if the firmware has given a device type instance + given instance/index (SMBIOS type 41 device type instance + or ACPI _DSM) of the PCI device. The attribute will be + created only if the firmware has given a device type instance to the PCI device. Users: Userspace applications interested in knowing the diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index dc1aa09..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 @@ -55,9 +55,6 @@ 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 index 111500e..2e3d91c 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -19,8 +19,30 @@ #include #include #include +#include +#include +#include +#include +#include #include "pci.h" +#define DEVICE_LABEL_DSM 0x07 + +#ifndef CONFIG_DMI + +static inline int +pci_create_smbiosname_file(struct pci_dev *pdev) +{ + return -1; +} + +static inline void +pci_remove_smbiosname_file(struct pci_dev *pdev) +{ +} + +#else + enum smbios_attr_enum { SMBIOS_ATTR_NONE = 0, SMBIOS_ATTR_LABEL_SHOW, @@ -131,13 +153,216 @@ pci_remove_smbiosname_file(struct pci_dev *pdev) sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); } +#endif + +#ifndef CONFIG_ACPI + +static inline int +pci_create_acpi_index_label_files(struct pci_dev *pdev) +{ + return -1; +} + +static inline int +pci_remove_acpi_index_label_files(struct pci_dev *pdev) +{ + return -1; +} + +#else + +static const char device_label_dsm_uuid[] = { + 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, + 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D +}; + +enum acpi_attr_enum { + ACPI_ATTR_NONE = 0, + ACPI_ATTR_LABEL_SHOW, + ACPI_ATTR_INDEX_SHOW, +}; + +static int dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf) +{ + int err; + err = utf16s_to_utf8s((const wchar_t *)obj-> + package.elements[1].string.pointer, + obj->package.elements[1].string.length, + UTF16_LITTLE_ENDIAN, + buf, PAGE_SIZE); + printk(KERN_ERR "%s: len=%d\n", __func__, err); + buf[err] = '\n'; + return err; +} + +static int +dsm_get_label(acpi_handle handle, int func, + struct acpi_buffer *output, + char *buf, enum acpi_attr_enum attribute) +{ + struct acpi_object_list input; + union acpi_object params[4]; + union acpi_object *obj; + int len = 0; + + int err; + + input.count = 4; + input.pointer = params; + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = sizeof(device_label_dsm_uuid); + params[0].buffer.pointer = (char *)device_label_dsm_uuid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = 0x02; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = func; + params[3].type = ACPI_TYPE_PACKAGE; + params[3].package.count = 0; + params[3].package.elements = NULL; + + err = acpi_evaluate_object(handle, "_DSM", &input, output); + if (err) + return -1; + + obj = (union acpi_object *)output->pointer; + + switch (obj->type) { + case ACPI_TYPE_PACKAGE: + if (obj->package.count != 2) + break; + len = obj->package.elements[0].integer.value; + if (buf) { + if (attribute == ACPI_ATTR_INDEX_SHOW) + scnprintf(buf, PAGE_SIZE, "%llu\n", + obj->package.elements[0].integer.value); + else if (attribute == ACPI_ATTR_LABEL_SHOW) { + if (dsm_label_utf16s_to_utf8s(obj, buf) < 0) + return -1; + } + kfree(output->pointer); + return strlen(buf); + } + kfree(output->pointer); + return len; + break; + default: + kfree(output->pointer); + } + return -1; +} + +static mode_t +acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n) +{ + struct device *dev; + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_handle handle; + int length; + + dev = container_of(kobj, struct device, kobj); + handle = DEVICE_ACPI_HANDLE(dev); + + if (!handle) + return 0; + + length = dsm_get_label(handle, DEVICE_LABEL_DSM, + &output, NULL, ACPI_ATTR_NONE); + + return (length > 0) ? S_IRUGO : 0; +} + +static ssize_t +acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_handle handle; + int length; + + handle = DEVICE_ACPI_HANDLE(dev); + + if (!handle) + return -1; + + length = dsm_get_label(handle, DEVICE_LABEL_DSM, + &output, buf, ACPI_ATTR_LABEL_SHOW); + + if (length < 1) + return -1; + + return length; +} + +static ssize_t +acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_handle handle; + int length; + + handle = DEVICE_ACPI_HANDLE(dev); + + if (!handle) + return -1; + + length = dsm_get_label(handle, DEVICE_LABEL_DSM, + &output, buf, ACPI_ATTR_INDEX_SHOW); + + if (length < 0) + return -1; + + return length; + +} + +static struct device_attribute acpi_attr_label = { + .attr = {.name = "label", .mode = 0444, .owner = THIS_MODULE}, + .show = acpilabel_show, +}; + +static struct device_attribute acpi_attr_index = { + .attr = {.name = "index", .mode = 0444, .owner = THIS_MODULE}, + .show = acpiindex_show, +}; + +static struct attribute *acpi_attributes[] = { + &acpi_attr_label.attr, + &acpi_attr_index.attr, + NULL, +}; + +static struct attribute_group acpi_attr_group = { + .attrs = acpi_attributes, + .is_visible = acpi_index_string_exist, +}; + +static int +pci_create_acpi_index_label_files(struct pci_dev *pdev) +{ + if (!sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group)) + return 0; + return -ENODEV; +} + +static int +pci_remove_acpi_index_label_files(struct pci_dev *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group); + return 0; +} +#endif + void pci_create_firmware_label_files(struct pci_dev *pdev) { - if (!pci_create_smbiosname_file(pdev)) + if (!pci_create_acpi_index_label_files(pdev)) + ; + else if (!pci_create_smbiosname_file(pdev)) ; } void pci_remove_firmware_label_files(struct pci_dev *pdev) { - pci_remove_smbiosname_file(pdev); + if (!pci_remove_acpi_index_label_files(pdev)) + ; + else + pci_remove_smbiosname_file(pdev); } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d930338..1b6fc13 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -11,11 +11,11 @@ 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 +#if !defined(CONFIG_DMI) && !defined(CONFIG_ACPI) 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); -- 1.6.5.2