aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pci-label.c
diff options
context:
space:
mode:
authorNarendra_K@Dell.com <Narendra_K@Dell.com>2011-03-02 12:04:17 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2011-03-04 13:41:56 -0500
commit6058989bad05b82e78baacce69ec14f27a11b5fd (patch)
tree26353578e7e9a95d2ea101cd9a8e656d85ddd829 /drivers/pci/pci-label.c
parentcdb9755849fbaf2bb9c0a009ba5baa817a0f152d (diff)
PCI: Export ACPI _DSM provided firmware instance number and string name to sysfs
This patch exports ACPI _DSM (Device Specific Method) provided firmware instance number and string name of PCI devices as defined by 'PCI Firmware Specification Revision 3.1' section 4.6.7.( DSM for Naming a PCI or PCI Express Device Under Operating Systems) to sysfs. New files created are: /sys/bus/pci/devices/.../label which contains the firmware name for the device in question, and /sys/bus/pci/devices/.../acpi_index which contains the firmware device type instance for the given device. cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/acpi_index 1 cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/label Embedded Broadcom 5709C NIC 1 cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/acpi_index 2 cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/label Embedded Broadcom 5709C NIC 2 The ACPI _DSM provided firmware 'instance number' and 'string name' will be given priority if the firmware also provides 'SMBIOS type 41 device type instance and string'. Signed-off-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Jordan Hargrave <jordan_hargrave@dell.com> Signed-off-by: Narendra K <narendra_k@dell.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pci-label.c')
-rw-r--r--drivers/pci/pci-label.c247
1 files changed, 241 insertions, 6 deletions
diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c
index 90c0a729cd3a..824e247edc58 100644
--- a/drivers/pci/pci-label.c
+++ b/drivers/pci/pci-label.c
@@ -5,6 +5,13 @@
5 * by Narendra K <Narendra_K@dell.com>, 5 * by Narendra K <Narendra_K@dell.com>,
6 * Jordan Hargrave <Jordan_Hargrave@dell.com> 6 * Jordan Hargrave <Jordan_Hargrave@dell.com>
7 * 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 *
8 * SMBIOS defines type 41 for onboard pci devices. This code retrieves 15 * SMBIOS defines type 41 for onboard pci devices. This code retrieves
9 * the instance number and string from the type 41 record and exports 16 * the instance number and string from the type 41 record and exports
10 * it to sysfs. 17 * it to sysfs.
@@ -19,8 +26,30 @@
19#include <linux/pci_ids.h> 26#include <linux/pci_ids.h>
20#include <linux/module.h> 27#include <linux/module.h>
21#include <linux/device.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_drivers.h>
33#include <acpi/acpi_bus.h>
22#include "pci.h" 34#include "pci.h"
23 35
36#define DEVICE_LABEL_DSM 0x07
37
38#ifndef CONFIG_DMI
39
40static inline int
41pci_create_smbiosname_file(struct pci_dev *pdev)
42{
43 return -1;
44}
45
46static inline void
47pci_remove_smbiosname_file(struct pci_dev *pdev)
48{
49}
50
51#else
52
24enum smbios_attr_enum { 53enum smbios_attr_enum {
25 SMBIOS_ATTR_NONE = 0, 54 SMBIOS_ATTR_NONE = 0,
26 SMBIOS_ATTR_LABEL_SHOW, 55 SMBIOS_ATTR_LABEL_SHOW,
@@ -120,9 +149,7 @@ static struct attribute_group smbios_attr_group = {
120static int 149static int
121pci_create_smbiosname_file(struct pci_dev *pdev) 150pci_create_smbiosname_file(struct pci_dev *pdev)
122{ 151{
123 if (!sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group)) 152 return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group);
124 return 0;
125 return -ENODEV;
126} 153}
127 154
128static void 155static void
@@ -131,13 +158,221 @@ pci_remove_smbiosname_file(struct pci_dev *pdev)
131 sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); 158 sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group);
132} 159}
133 160
161#endif
162
163#ifndef CONFIG_ACPI
164
165static inline int
166pci_create_acpi_index_label_files(struct pci_dev *pdev)
167{
168 return -1;
169}
170
171static inline int
172pci_remove_acpi_index_label_files(struct pci_dev *pdev)
173{
174 return -1;
175}
176
177#else
178
179static const char device_label_dsm_uuid[] = {
180 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D,
181 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D
182};
183
184enum acpi_attr_enum {
185 ACPI_ATTR_NONE = 0,
186 ACPI_ATTR_LABEL_SHOW,
187 ACPI_ATTR_INDEX_SHOW,
188};
189
190static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
191{
192 int len;
193 len = utf16s_to_utf8s((const wchar_t *)obj->
194 package.elements[1].string.pointer,
195 obj->package.elements[1].string.length,
196 UTF16_LITTLE_ENDIAN,
197 buf, PAGE_SIZE);
198 buf[len] = '\n';
199}
200
201static int
202dsm_get_label(acpi_handle handle, int func,
203 struct acpi_buffer *output,
204 char *buf, enum acpi_attr_enum attribute)
205{
206 struct acpi_object_list input;
207 union acpi_object params[4];
208 union acpi_object *obj;
209 int len = 0;
210
211 int err;
212
213 input.count = 4;
214 input.pointer = params;
215 params[0].type = ACPI_TYPE_BUFFER;
216 params[0].buffer.length = sizeof(device_label_dsm_uuid);
217 params[0].buffer.pointer = (char *)device_label_dsm_uuid;
218 params[1].type = ACPI_TYPE_INTEGER;
219 params[1].integer.value = 0x02;
220 params[2].type = ACPI_TYPE_INTEGER;
221 params[2].integer.value = func;
222 params[3].type = ACPI_TYPE_PACKAGE;
223 params[3].package.count = 0;
224 params[3].package.elements = NULL;
225
226 err = acpi_evaluate_object(handle, "_DSM", &input, output);
227 if (err)
228 return -1;
229
230 obj = (union acpi_object *)output->pointer;
231
232 switch (obj->type) {
233 case ACPI_TYPE_PACKAGE:
234 if (obj->package.count != 2)
235 break;
236 len = obj->package.elements[0].integer.value;
237 if (buf) {
238 if (attribute == ACPI_ATTR_INDEX_SHOW)
239 scnprintf(buf, PAGE_SIZE, "%llu\n",
240 obj->package.elements[0].integer.value);
241 else if (attribute == ACPI_ATTR_LABEL_SHOW)
242 dsm_label_utf16s_to_utf8s(obj, buf);
243 kfree(output->pointer);
244 return strlen(buf);
245 }
246 kfree(output->pointer);
247 return len;
248 break;
249 default:
250 kfree(output->pointer);
251 }
252 return -1;
253}
254
255static bool
256device_has_dsm(struct device *dev)
257{
258 acpi_handle handle;
259 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
260
261 handle = DEVICE_ACPI_HANDLE(dev);
262
263 if (!handle)
264 return FALSE;
265
266 if (dsm_get_label(handle, DEVICE_LABEL_DSM, &output, NULL,
267 ACPI_ATTR_NONE) > 0)
268 return TRUE;
269
270 return FALSE;
271}
272
273static mode_t
274acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n)
275{
276 struct device *dev;
277
278 dev = container_of(kobj, struct device, kobj);
279
280 if (device_has_dsm(dev))
281 return S_IRUGO;
282
283 return 0;
284}
285
286static ssize_t
287acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf)
288{
289 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
290 acpi_handle handle;
291 int length;
292
293 handle = DEVICE_ACPI_HANDLE(dev);
294
295 if (!handle)
296 return -1;
297
298 length = dsm_get_label(handle, DEVICE_LABEL_DSM,
299 &output, buf, ACPI_ATTR_LABEL_SHOW);
300
301 if (length < 1)
302 return -1;
303
304 return length;
305}
306
307static ssize_t
308acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf)
309{
310 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
311 acpi_handle handle;
312 int length;
313
314 handle = DEVICE_ACPI_HANDLE(dev);
315
316 if (!handle)
317 return -1;
318
319 length = dsm_get_label(handle, DEVICE_LABEL_DSM,
320 &output, buf, ACPI_ATTR_INDEX_SHOW);
321
322 if (length < 0)
323 return -1;
324
325 return length;
326
327}
328
329static struct device_attribute acpi_attr_label = {
330 .attr = {.name = "label", .mode = 0444},
331 .show = acpilabel_show,
332};
333
334static struct device_attribute acpi_attr_index = {
335 .attr = {.name = "acpi_index", .mode = 0444},
336 .show = acpiindex_show,
337};
338
339static struct attribute *acpi_attributes[] = {
340 &acpi_attr_label.attr,
341 &acpi_attr_index.attr,
342 NULL,
343};
344
345static struct attribute_group acpi_attr_group = {
346 .attrs = acpi_attributes,
347 .is_visible = acpi_index_string_exist,
348};
349
350static int
351pci_create_acpi_index_label_files(struct pci_dev *pdev)
352{
353 return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group);
354}
355
356static int
357pci_remove_acpi_index_label_files(struct pci_dev *pdev)
358{
359 sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group);
360 return 0;
361}
362#endif
363
134void pci_create_firmware_label_files(struct pci_dev *pdev) 364void pci_create_firmware_label_files(struct pci_dev *pdev)
135{ 365{
136 if (!pci_create_smbiosname_file(pdev)) 366 if (device_has_dsm(&pdev->dev))
137 ; 367 pci_create_acpi_index_label_files(pdev);
368 else
369 pci_create_smbiosname_file(pdev);
138} 370}
139 371
140void pci_remove_firmware_label_files(struct pci_dev *pdev) 372void pci_remove_firmware_label_files(struct pci_dev *pdev)
141{ 373{
142 pci_remove_smbiosname_file(pdev); 374 if (device_has_dsm(&pdev->dev))
375 pci_remove_acpi_index_label_files(pdev);
376 else
377 pci_remove_smbiosname_file(pdev);
143} 378}