Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.8-rc2 383 lines 8.4 kB view raw
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 <acpi/acpi_bus.h> 33#include "pci.h" 34 35#define DEVICE_LABEL_DSM 0x07 36 37#ifndef CONFIG_DMI 38 39static inline int 40pci_create_smbiosname_file(struct pci_dev *pdev) 41{ 42 return -1; 43} 44 45static inline void 46pci_remove_smbiosname_file(struct pci_dev *pdev) 47{ 48} 49 50#else 51 52enum smbios_attr_enum { 53 SMBIOS_ATTR_NONE = 0, 54 SMBIOS_ATTR_LABEL_SHOW, 55 SMBIOS_ATTR_INSTANCE_SHOW, 56}; 57 58static size_t 59find_smbios_instance_string(struct pci_dev *pdev, char *buf, 60 enum smbios_attr_enum attribute) 61{ 62 const struct dmi_device *dmi; 63 struct dmi_dev_onboard *donboard; 64 int bus; 65 int devfn; 66 67 bus = pdev->bus->number; 68 devfn = pdev->devfn; 69 70 dmi = NULL; 71 while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, 72 NULL, dmi)) != NULL) { 73 donboard = dmi->device_data; 74 if (donboard && donboard->bus == bus && 75 donboard->devfn == devfn) { 76 if (buf) { 77 if (attribute == SMBIOS_ATTR_INSTANCE_SHOW) 78 return scnprintf(buf, PAGE_SIZE, 79 "%d\n", 80 donboard->instance); 81 else if (attribute == SMBIOS_ATTR_LABEL_SHOW) 82 return scnprintf(buf, PAGE_SIZE, 83 "%s\n", 84 dmi->name); 85 } 86 return strlen(dmi->name); 87 } 88 } 89 return 0; 90} 91 92static umode_t 93smbios_instance_string_exist(struct kobject *kobj, struct attribute *attr, 94 int n) 95{ 96 struct device *dev; 97 struct pci_dev *pdev; 98 99 dev = container_of(kobj, struct device, kobj); 100 pdev = to_pci_dev(dev); 101 102 return find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE) ? 103 S_IRUGO : 0; 104} 105 106static ssize_t 107smbioslabel_show(struct device *dev, struct device_attribute *attr, char *buf) 108{ 109 struct pci_dev *pdev; 110 pdev = to_pci_dev(dev); 111 112 return find_smbios_instance_string(pdev, buf, 113 SMBIOS_ATTR_LABEL_SHOW); 114} 115 116static ssize_t 117smbiosinstance_show(struct device *dev, 118 struct device_attribute *attr, char *buf) 119{ 120 struct pci_dev *pdev; 121 pdev = to_pci_dev(dev); 122 123 return find_smbios_instance_string(pdev, buf, 124 SMBIOS_ATTR_INSTANCE_SHOW); 125} 126 127static struct device_attribute smbios_attr_label = { 128 .attr = {.name = "label", .mode = 0444}, 129 .show = smbioslabel_show, 130}; 131 132static struct device_attribute smbios_attr_instance = { 133 .attr = {.name = "index", .mode = 0444}, 134 .show = smbiosinstance_show, 135}; 136 137static struct attribute *smbios_attributes[] = { 138 &smbios_attr_label.attr, 139 &smbios_attr_instance.attr, 140 NULL, 141}; 142 143static struct attribute_group smbios_attr_group = { 144 .attrs = smbios_attributes, 145 .is_visible = smbios_instance_string_exist, 146}; 147 148static int 149pci_create_smbiosname_file(struct pci_dev *pdev) 150{ 151 return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group); 152} 153 154static void 155pci_remove_smbiosname_file(struct pci_dev *pdev) 156{ 157 sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); 158} 159 160#endif 161 162#ifndef CONFIG_ACPI 163 164static inline int 165pci_create_acpi_index_label_files(struct pci_dev *pdev) 166{ 167 return -1; 168} 169 170static inline int 171pci_remove_acpi_index_label_files(struct pci_dev *pdev) 172{ 173 return -1; 174} 175 176static inline bool 177device_has_dsm(struct device *dev) 178{ 179 return false; 180} 181 182#else 183 184static const char device_label_dsm_uuid[] = { 185 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, 186 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D 187}; 188 189enum acpi_attr_enum { 190 ACPI_ATTR_NONE = 0, 191 ACPI_ATTR_LABEL_SHOW, 192 ACPI_ATTR_INDEX_SHOW, 193}; 194 195static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf) 196{ 197 int len; 198 len = utf16s_to_utf8s((const wchar_t *)obj-> 199 package.elements[1].string.pointer, 200 obj->package.elements[1].string.length, 201 UTF16_LITTLE_ENDIAN, 202 buf, PAGE_SIZE); 203 buf[len] = '\n'; 204} 205 206static int 207dsm_get_label(acpi_handle handle, int func, 208 struct acpi_buffer *output, 209 char *buf, enum acpi_attr_enum attribute) 210{ 211 struct acpi_object_list input; 212 union acpi_object params[4]; 213 union acpi_object *obj; 214 int len = 0; 215 216 int err; 217 218 input.count = 4; 219 input.pointer = params; 220 params[0].type = ACPI_TYPE_BUFFER; 221 params[0].buffer.length = sizeof(device_label_dsm_uuid); 222 params[0].buffer.pointer = (char *)device_label_dsm_uuid; 223 params[1].type = ACPI_TYPE_INTEGER; 224 params[1].integer.value = 0x02; 225 params[2].type = ACPI_TYPE_INTEGER; 226 params[2].integer.value = func; 227 params[3].type = ACPI_TYPE_PACKAGE; 228 params[3].package.count = 0; 229 params[3].package.elements = NULL; 230 231 err = acpi_evaluate_object(handle, "_DSM", &input, output); 232 if (err) 233 return -1; 234 235 obj = (union acpi_object *)output->pointer; 236 237 switch (obj->type) { 238 case ACPI_TYPE_PACKAGE: 239 if (obj->package.count != 2) 240 break; 241 len = obj->package.elements[0].integer.value; 242 if (buf) { 243 if (attribute == ACPI_ATTR_INDEX_SHOW) 244 scnprintf(buf, PAGE_SIZE, "%llu\n", 245 obj->package.elements[0].integer.value); 246 else if (attribute == ACPI_ATTR_LABEL_SHOW) 247 dsm_label_utf16s_to_utf8s(obj, buf); 248 kfree(output->pointer); 249 return strlen(buf); 250 } 251 kfree(output->pointer); 252 return len; 253 break; 254 default: 255 kfree(output->pointer); 256 } 257 return -1; 258} 259 260static bool 261device_has_dsm(struct device *dev) 262{ 263 acpi_handle handle; 264 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 265 266 handle = DEVICE_ACPI_HANDLE(dev); 267 268 if (!handle) 269 return FALSE; 270 271 if (dsm_get_label(handle, DEVICE_LABEL_DSM, &output, NULL, 272 ACPI_ATTR_NONE) > 0) 273 return TRUE; 274 275 return FALSE; 276} 277 278static umode_t 279acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n) 280{ 281 struct device *dev; 282 283 dev = container_of(kobj, struct device, kobj); 284 285 if (device_has_dsm(dev)) 286 return S_IRUGO; 287 288 return 0; 289} 290 291static ssize_t 292acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf) 293{ 294 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 295 acpi_handle handle; 296 int length; 297 298 handle = DEVICE_ACPI_HANDLE(dev); 299 300 if (!handle) 301 return -1; 302 303 length = dsm_get_label(handle, DEVICE_LABEL_DSM, 304 &output, buf, ACPI_ATTR_LABEL_SHOW); 305 306 if (length < 1) 307 return -1; 308 309 return length; 310} 311 312static ssize_t 313acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf) 314{ 315 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 316 acpi_handle handle; 317 int length; 318 319 handle = DEVICE_ACPI_HANDLE(dev); 320 321 if (!handle) 322 return -1; 323 324 length = dsm_get_label(handle, DEVICE_LABEL_DSM, 325 &output, buf, ACPI_ATTR_INDEX_SHOW); 326 327 if (length < 0) 328 return -1; 329 330 return length; 331 332} 333 334static struct device_attribute acpi_attr_label = { 335 .attr = {.name = "label", .mode = 0444}, 336 .show = acpilabel_show, 337}; 338 339static struct device_attribute acpi_attr_index = { 340 .attr = {.name = "acpi_index", .mode = 0444}, 341 .show = acpiindex_show, 342}; 343 344static struct attribute *acpi_attributes[] = { 345 &acpi_attr_label.attr, 346 &acpi_attr_index.attr, 347 NULL, 348}; 349 350static struct attribute_group acpi_attr_group = { 351 .attrs = acpi_attributes, 352 .is_visible = acpi_index_string_exist, 353}; 354 355static int 356pci_create_acpi_index_label_files(struct pci_dev *pdev) 357{ 358 return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group); 359} 360 361static int 362pci_remove_acpi_index_label_files(struct pci_dev *pdev) 363{ 364 sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group); 365 return 0; 366} 367#endif 368 369void pci_create_firmware_label_files(struct pci_dev *pdev) 370{ 371 if (device_has_dsm(&pdev->dev)) 372 pci_create_acpi_index_label_files(pdev); 373 else 374 pci_create_smbiosname_file(pdev); 375} 376 377void pci_remove_firmware_label_files(struct pci_dev *pdev) 378{ 379 if (device_has_dsm(&pdev->dev)) 380 pci_remove_acpi_index_label_files(pdev); 381 else 382 pci_remove_smbiosname_file(pdev); 383}