From patchwork Fri May 28 11:55:21 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Narendra K X-Patchwork-Id: 102898 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o4SE8HFs009697 for ; Fri, 28 May 2010 14:08:17 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757096Ab0E1OIQ (ORCPT ); Fri, 28 May 2010 10:08:16 -0400 Received: from ausxippc101.us.dell.com ([143.166.85.207]:28821 "EHLO ausxippc101.us.dell.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756056Ab0E1OIP convert rfc822-to-8bit (ORCPT ); Fri, 28 May 2010 10:08:15 -0400 X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 28 May 2010 14:08:18 +0000 (UTC) X-Greylist: delayed 575 seconds by postgrey-1.27 at vger.kernel.org; Fri, 28 May 2010 10:08:14 EDT X-LoopCount1: from 10.9.160.253 From: "K, Narendra" To: "netdev@vger.kernel.org" , "linux-hotplug@vger.kernel.org" , "linux-pci@vger.kernel.org" CC: "Domsch, Matt" , "Hargrave, Jordan" , "Rose, Charles" , "Nijhawan, Vijay" Date: Fri, 28 May 2010 06:55:21 -0500 Subject: [PATCH 1/2] Export firmware assigned labels of network devices to sysfs Thread-Topic: [PATCH 1/2] Export firmware assigned labels of network devices to sysfs Thread-Index: Acr+XKclN9Ccf6DeThWZyxFmEvvp8A== Message-ID: <20100528115520.GA24114@littleblue.us.dell.com> Accept-Language: en-US Content-Language: en-US X-MS-Exchange-Organization-AuthAs: Internal X-MS-Exchange-Organization-AuthMechanism: 0b X-MS-Exchange-Organization-AuthSource: AUSX7HPS303.AMER.DELL.COM X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originalarrivaltime: 28 May 2010 11:55:21.0897 (UTC) FILETIME=[A698F990:01CAFE5C] x-loopcount0: from 10.9.160.253 user-agent: Mutt/1.4.2.2i MIME-Version: 1.0 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index d464672..7d8439b 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -277,6 +277,28 @@ static void __init dmi_save_ipmi_device(const struct dmi_header *dm) list_add_tail(&dev->list, &dmi_devices); } +static void __init dmi_save_devslot(int id, int seg, int bus, int devfn, const char *name) +{ + struct dmi_devslot *slot; + + slot = dmi_alloc(sizeof(*slot) + strlen(name) + 1); + if (!slot) { + printk(KERN_ERR "dmi_save_devslot: out of memory.\n"); + return; + } + slot->id = id; + slot->seg = seg; + slot->bus = bus; + slot->devfn = devfn; + + strcpy((char *)&slot[1], name); + slot->dev.type = DMI_DEV_TYPE_DEVSLOT; + slot->dev.name = (char *)&slot[1]; + slot->dev.device_data = slot; + + list_add(&slot->dev.list, &dmi_devices); +} + static void __init dmi_save_extended_devices(const struct dmi_header *dm) { const u8 *d = (u8*) dm + 5; @@ -285,6 +307,7 @@ static void __init dmi_save_extended_devices(const struct dmi_header *dm) if ((*d & 0x80) == 0) return; + dmi_save_devslot(-1, *(u16 *)(d+2), *(d+4), *(d+5), dmi_string_nosave(dm, *(d-1))); dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d - 1))); } @@ -333,6 +356,7 @@ static void __init dmi_decode(const struct dmi_header *dm, void *dummy) break; case 41: /* Onboard Devices Extended Information */ dmi_save_extended_devices(dm); + break; } } diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 0b51857..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 diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c new file mode 100644 index 0000000..f3f4c37 --- /dev/null +++ b/drivers/pci/pci-label.c @@ -0,0 +1,242 @@ +/* + * File: drivers/pci/pci-label.c + * Purpose: Export the firmware label associated with a pci network interface + * device to sysfs + * Copyright (C) 2010 Dell Inc. + * by Narendra K , Jordan Hargrave + * + * This code checks if the pci network device has a related ACPI _DSM. If + * available, the code calls the _DSM to retrieve the index and string and + * exports them to sysfs. If the ACPI _DSM is not available, it falls back on + * SMBIOS. SMBIOS defines type 41 for onboard pci devices. This code retrieves + * strings associated with the type 41 and exports it to sysfs. + * + * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more + * information. + */ + +#include + +static ssize_t +smbiosname_string_exists(struct device *dev, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + const struct dmi_device *dmi; + struct dmi_devslot *dslot; + int bus; + int devfn; + + bus = pdev->bus->number; + devfn = pdev->devfn; + + dmi = NULL; + while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEVSLOT, NULL, dmi)) != NULL) { + dslot = dmi->device_data; + if (dslot && dslot->bus == bus && dslot->devfn == devfn) { + if (buf) + return scnprintf(buf, PAGE_SIZE, "%s\n", dmi->name); + return strlen(dmi->name); + } + } + + return 0; +} + +static ssize_t +smbiosname_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return smbiosname_string_exists(dev, buf); +} + +struct smbios_attribute smbios_attr_label = { + .attr = {.name = __stringify(label), .mode = 0444, .owner = THIS_MODULE}, + .show = smbiosname_show, + .test = smbiosname_string_exists, +}; + +static int +pci_create_smbiosname_file(struct pci_dev *pdev) +{ + if (smbios_attr_label.test && smbios_attr_label.test(&pdev->dev, NULL)) { + sysfs_create_file(&pdev->dev.kobj, &smbios_attr_label.attr); + return 0; + } + return -1; +} + +static int +pci_remove_smbiosname_file(struct pci_dev *pdev) +{ + if (smbios_attr_label.test && smbios_attr_label.test(&pdev->dev, NULL)) { + sysfs_remove_file(&pdev->dev.kobj, &smbios_attr_label.attr); + return 0; + } + return -1; +} + +static const char dell_dsm_uuid[] = { + 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, + 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D +}; + + +static int +dsm_get_label(acpi_handle handle, int func, + struct acpi_buffer *output, + char *buf, char *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(dell_dsm_uuid); + params[0].buffer.pointer = (char *)dell_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) { + printk(KERN_INFO "failed to evaulate _DSM\n"); + return -1; + } + + obj = (union acpi_object *)output->pointer; + + switch (obj->type) { + case ACPI_TYPE_PACKAGE: + if (obj->package.count == 2) { + len = obj->package.elements[0].integer.value; + if (buf) { + if (!strncmp(attribute, "index", strlen(attribute))) + scnprintf(buf, PAGE_SIZE, "%lu\n", + obj->package.elements[0].integer.value); + else + scnprintf(buf, PAGE_SIZE, "%s\n", + obj->package.elements[1].string.pointer); + kfree(output->pointer); + return strlen(buf); + } + } + kfree(output->pointer); + return len; + break; + default: + return -1; + } +} + +static ssize_t +acpi_index_string_exist(struct device *dev, char *buf, char *attribute) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_handle handle; + int length; + int is_addin_card = 0; + + if ((pdev->class >> 16) != PCI_BASE_CLASS_NETWORK) + return -1; + + handle = DEVICE_ACPI_HANDLE(dev); + + if (!handle) { + /* + * The device is an add-in network controller and does have + * a valid handle. Try until we get the handle for the parent + * bridge + */ + struct pci_bus *pbus; + for (pbus = pdev->bus; pbus; pbus = pbus->parent) { + handle = DEVICE_ACPI_HANDLE(&(pbus->self->dev)); + if (handle) + break; + + } + } + + if ((length = dsm_get_label(handle, DELL_DSM_NETWORK, + &output, buf, attribute)) < 0) + return -1; + + return length; +} + +static ssize_t +acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return acpi_index_string_exist(dev, buf, "label"); +} + +static ssize_t +acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return acpi_index_string_exist(dev, buf, "index"); +} + +struct acpi_attribute acpi_attr_label = { + .attr = {.name = __stringify(label), .mode = 0444, .owner = THIS_MODULE}, + .show = acpilabel_show, + .test = acpi_index_string_exist, +}; + +struct acpi_attribute acpi_attr_index = { + .attr = {.name = __stringify(index), .mode = 0444, .owner = THIS_MODULE}, + .show = acpiindex_show, + .test = acpi_index_string_exist, +}; + +static int +pci_create_acpi_index_label_files(struct pci_dev *pdev) +{ + if (acpi_attr_label.test && acpi_attr_label.test(&pdev->dev, NULL) > 0) { + sysfs_create_file(&pdev->dev.kobj, &acpi_attr_label.attr); + sysfs_create_file(&pdev->dev.kobj, &acpi_attr_index.attr); + return 0; + } + return -1; +} + +static int +pci_remove_acpi_index_label_files(struct pci_dev *pdev) +{ + if (acpi_attr_label.test && acpi_attr_label.test(&pdev->dev, NULL) > 0) { + sysfs_remove_file(&pdev->dev.kobj, &acpi_attr_label.attr); + sysfs_remove_file(&pdev->dev.kobj, &acpi_attr_index.attr); + return 0; + } + return -1; +} + +int pci_create_acpi_attr_files(struct pci_dev *pdev) +{ + if (!pci_create_acpi_index_label_files(pdev)) + return 0; + if (!pci_create_smbiosname_file(pdev)) + return 0; + return -ENODEV; +} +EXPORT_SYMBOL(pci_create_acpi_attr_files); + +int pci_remove_acpi_attr_files(struct pci_dev *pdev) +{ + if (!pci_remove_acpi_index_label_files(pdev)) + return 0; + if (!pci_remove_smbiosname_file(pdev)) + return 0; + return -ENODEV; + +} +EXPORT_SYMBOL(pci_remove_acpi_attr_files); + diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index fad9398..30fa62b 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "pci.h" @@ -1073,6 +1074,8 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) if (retval) goto err_vga_file; + pci_create_acpi_attr_files(pdev); + return 0; err_vga_file: @@ -1140,6 +1143,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_acpi_attr_files(pdev); + } static int __init pci_sysfs_init(void) diff --git a/include/linux/dmi.h b/include/linux/dmi.h index a8a3e1a..cc57c3a 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -20,6 +20,7 @@ enum dmi_device_type { DMI_DEV_TYPE_SAS, DMI_DEV_TYPE_IPMI = -1, DMI_DEV_TYPE_OEM_STRING = -2, + DMI_DEV_TYPE_DEVSLOT = -3, }; struct dmi_header { @@ -37,6 +38,14 @@ struct dmi_device { #ifdef CONFIG_DMI +struct dmi_devslot { + struct dmi_device dev; + int id; + int seg; + int bus; + int devfn; +}; + extern int dmi_check_system(const struct dmi_system_id *list); const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list); extern const char * dmi_get_system_info(int field); diff --git a/include/linux/pci-label.h b/include/linux/pci-label.h new file mode 100644 index 0000000..e9a4dfb --- /dev/null +++ b/include/linux/pci-label.h @@ -0,0 +1,38 @@ +/* + * File include/linux/pci-label.h + * Copyright (C) 2010 Dell Inc. + * by Narendra K , Jordan Hargrave + */ + +#ifndef _PCI_LABEL_H_ +#define _PCI_LABEL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct smbios_attribute { + struct attribute attr; + ssize_t (*show) (struct device *dev, char *buf); + ssize_t (*test) (struct device *dev, char *buf); +}; + +struct acpi_attribute { + struct attribute attr; + ssize_t (*show) (struct device *dev, char *buf); + ssize_t (*test) (struct device *dev, char *buf); +}; + +#define DELL_DSM_NETWORK 0x07 + +extern int pci_create_acpi_attr_files(struct pci_dev *pdev); +extern int pci_remove_acpi_attr_files(struct pci_dev *pdev); + +#endif /* _PCI_LABEL_H_ */ +