ACPI / scan: Move sysfs-related device code to a separate file
[deliverable/linux.git] / drivers / acpi / device_sysfs.c
CommitLineData
c2efefb3
RW
1/*
2 * drivers/acpi/device_sysfs.c - ACPI device sysfs attributes and modalias.
3 *
4 * Copyright (C) 2015, Intel Corp.
5 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
6 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
7 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as published
12 * by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 */
21
22#include <linux/acpi.h>
23#include <linux/device.h>
24#include <linux/export.h>
25#include <linux/nls.h>
26
27#include "internal.h"
28
29/**
30 * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent
31 * @acpi_dev: ACPI device object.
32 * @modalias: Buffer to print into.
33 * @size: Size of the buffer.
34 *
35 * Creates hid/cid(s) string needed for modalias and uevent
36 * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
37 * char *modalias: "acpi:IBM0001:ACPI0001"
38 * Return: 0: no _HID and no _CID
39 * -EINVAL: output error
40 * -ENOMEM: output is truncated
41*/
42static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
43 int size)
44{
45 int len;
46 int count;
47 struct acpi_hardware_id *id;
48
49 /*
50 * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should
51 * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the
52 * device's list.
53 */
54 count = 0;
55 list_for_each_entry(id, &acpi_dev->pnp.ids, list)
56 if (strcmp(id->id, ACPI_DT_NAMESPACE_HID))
57 count++;
58
59 if (!count)
60 return 0;
61
62 len = snprintf(modalias, size, "acpi:");
63 if (len <= 0)
64 return len;
65
66 size -= len;
67
68 list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
69 if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID))
70 continue;
71
72 count = snprintf(&modalias[len], size, "%s:", id->id);
73 if (count < 0)
74 return -EINVAL;
75
76 if (count >= size)
77 return -ENOMEM;
78
79 len += count;
80 size -= count;
81 }
82 modalias[len] = '\0';
83 return len;
84}
85
86/**
87 * create_of_modalias - Creates DT compatible string for modalias and uevent
88 * @acpi_dev: ACPI device object.
89 * @modalias: Buffer to print into.
90 * @size: Size of the buffer.
91 *
92 * Expose DT compatible modalias as of:NnameTCcompatible. This function should
93 * only be called for devices having ACPI_DT_NAMESPACE_HID in their list of
94 * ACPI/PNP IDs.
95 */
96static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
97 int size)
98{
99 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
100 const union acpi_object *of_compatible, *obj;
101 int len, count;
102 int i, nval;
103 char *c;
104
105 acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
106 /* DT strings are all in lower case */
107 for (c = buf.pointer; *c != '\0'; c++)
108 *c = tolower(*c);
109
110 len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);
111 ACPI_FREE(buf.pointer);
112
113 if (len <= 0)
114 return len;
115
116 of_compatible = acpi_dev->data.of_compatible;
117 if (of_compatible->type == ACPI_TYPE_PACKAGE) {
118 nval = of_compatible->package.count;
119 obj = of_compatible->package.elements;
120 } else { /* Must be ACPI_TYPE_STRING. */
121 nval = 1;
122 obj = of_compatible;
123 }
124 for (i = 0; i < nval; i++, obj++) {
125 count = snprintf(&modalias[len], size, "C%s",
126 obj->string.pointer);
127 if (count < 0)
128 return -EINVAL;
129
130 if (count >= size)
131 return -ENOMEM;
132
133 len += count;
134 size -= count;
135 }
136 modalias[len] = '\0';
137 return len;
138}
139
140int __acpi_device_uevent_modalias(struct acpi_device *adev,
141 struct kobj_uevent_env *env)
142{
143 int len;
144
145 if (!adev)
146 return -ENODEV;
147
148 if (list_empty(&adev->pnp.ids))
149 return 0;
150
151 if (add_uevent_var(env, "MODALIAS="))
152 return -ENOMEM;
153
154 len = create_pnp_modalias(adev, &env->buf[env->buflen - 1],
155 sizeof(env->buf) - env->buflen);
156 if (len < 0)
157 return len;
158
159 env->buflen += len;
160 if (!adev->data.of_compatible)
161 return 0;
162
163 if (len > 0 && add_uevent_var(env, "MODALIAS="))
164 return -ENOMEM;
165
166 len = create_of_modalias(adev, &env->buf[env->buflen - 1],
167 sizeof(env->buf) - env->buflen);
168 if (len < 0)
169 return len;
170
171 env->buflen += len;
172
173 return 0;
174}
175
176/**
177 * acpi_device_uevent_modalias - uevent modalias for ACPI-enumerated devices.
178 *
179 * Create the uevent modalias field for ACPI-enumerated devices.
180 *
181 * Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with
182 * hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001".
183 */
184int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
185{
186 return __acpi_device_uevent_modalias(acpi_companion_match(dev), env);
187}
188EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);
189
190static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size)
191{
192 int len, count;
193
194 if (!adev)
195 return -ENODEV;
196
197 if (list_empty(&adev->pnp.ids))
198 return 0;
199
200 len = create_pnp_modalias(adev, buf, size - 1);
201 if (len < 0) {
202 return len;
203 } else if (len > 0) {
204 buf[len++] = '\n';
205 size -= len;
206 }
207 if (!adev->data.of_compatible)
208 return len;
209
210 count = create_of_modalias(adev, buf + len, size - 1);
211 if (count < 0) {
212 return count;
213 } else if (count > 0) {
214 len += count;
215 buf[len++] = '\n';
216 }
217
218 return len;
219}
220
221/**
222 * acpi_device_modalias - modalias sysfs attribute for ACPI-enumerated devices.
223 *
224 * Create the modalias sysfs attribute for ACPI-enumerated devices.
225 *
226 * Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with
227 * hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001".
228 */
229int acpi_device_modalias(struct device *dev, char *buf, int size)
230{
231 return __acpi_device_modalias(acpi_companion_match(dev), buf, size);
232}
233EXPORT_SYMBOL_GPL(acpi_device_modalias);
234
235static ssize_t
236acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) {
237 return __acpi_device_modalias(to_acpi_device(dev), buf, 1024);
238}
239static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
240
241static ssize_t real_power_state_show(struct device *dev,
242 struct device_attribute *attr, char *buf)
243{
244 struct acpi_device *adev = to_acpi_device(dev);
245 int state;
246 int ret;
247
248 ret = acpi_device_get_power(adev, &state);
249 if (ret)
250 return ret;
251
252 return sprintf(buf, "%s\n", acpi_power_state_string(state));
253}
254
255static DEVICE_ATTR(real_power_state, 0444, real_power_state_show, NULL);
256
257static ssize_t power_state_show(struct device *dev,
258 struct device_attribute *attr, char *buf)
259{
260 struct acpi_device *adev = to_acpi_device(dev);
261
262 return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state));
263}
264
265static DEVICE_ATTR(power_state, 0444, power_state_show, NULL);
266
267static ssize_t
268acpi_eject_store(struct device *d, struct device_attribute *attr,
269 const char *buf, size_t count)
270{
271 struct acpi_device *acpi_device = to_acpi_device(d);
272 acpi_object_type not_used;
273 acpi_status status;
274
275 if (!count || buf[0] != '1')
276 return -EINVAL;
277
278 if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)
279 && !acpi_device->driver)
280 return -ENODEV;
281
282 status = acpi_get_type(acpi_device->handle, &not_used);
283 if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
284 return -ENODEV;
285
286 get_device(&acpi_device->dev);
287 status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT);
288 if (ACPI_SUCCESS(status))
289 return count;
290
291 put_device(&acpi_device->dev);
292 acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
293 ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
294 return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;
295}
296
297static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store);
298
299static ssize_t
300acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) {
301 struct acpi_device *acpi_dev = to_acpi_device(dev);
302
303 return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev));
304}
305static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL);
306
307static ssize_t acpi_device_uid_show(struct device *dev,
308 struct device_attribute *attr, char *buf)
309{
310 struct acpi_device *acpi_dev = to_acpi_device(dev);
311
312 return sprintf(buf, "%s\n", acpi_dev->pnp.unique_id);
313}
314static DEVICE_ATTR(uid, 0444, acpi_device_uid_show, NULL);
315
316static ssize_t acpi_device_adr_show(struct device *dev,
317 struct device_attribute *attr, char *buf)
318{
319 struct acpi_device *acpi_dev = to_acpi_device(dev);
320
321 return sprintf(buf, "0x%08x\n",
322 (unsigned int)(acpi_dev->pnp.bus_address));
323}
324static DEVICE_ATTR(adr, 0444, acpi_device_adr_show, NULL);
325
326static ssize_t
327acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) {
328 struct acpi_device *acpi_dev = to_acpi_device(dev);
329 struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};
330 int result;
331
332 result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path);
333 if (result)
334 goto end;
335
336 result = sprintf(buf, "%s\n", (char*)path.pointer);
337 kfree(path.pointer);
338end:
339 return result;
340}
341static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL);
342
343/* sysfs file that shows description text from the ACPI _STR method */
344static ssize_t description_show(struct device *dev,
345 struct device_attribute *attr,
346 char *buf) {
347 struct acpi_device *acpi_dev = to_acpi_device(dev);
348 int result;
349
350 if (acpi_dev->pnp.str_obj == NULL)
351 return 0;
352
353 /*
354 * The _STR object contains a Unicode identifier for a device.
355 * We need to convert to utf-8 so it can be displayed.
356 */
357 result = utf16s_to_utf8s(
358 (wchar_t *)acpi_dev->pnp.str_obj->buffer.pointer,
359 acpi_dev->pnp.str_obj->buffer.length,
360 UTF16_LITTLE_ENDIAN, buf,
361 PAGE_SIZE);
362
363 buf[result++] = '\n';
364
365 return result;
366}
367static DEVICE_ATTR(description, 0444, description_show, NULL);
368
369static ssize_t
370acpi_device_sun_show(struct device *dev, struct device_attribute *attr,
371 char *buf) {
372 struct acpi_device *acpi_dev = to_acpi_device(dev);
373 acpi_status status;
374 unsigned long long sun;
375
376 status = acpi_evaluate_integer(acpi_dev->handle, "_SUN", NULL, &sun);
377 if (ACPI_FAILURE(status))
378 return -ENODEV;
379
380 return sprintf(buf, "%llu\n", sun);
381}
382static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL);
383
384static ssize_t status_show(struct device *dev, struct device_attribute *attr,
385 char *buf) {
386 struct acpi_device *acpi_dev = to_acpi_device(dev);
387 acpi_status status;
388 unsigned long long sta;
389
390 status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta);
391 if (ACPI_FAILURE(status))
392 return -ENODEV;
393
394 return sprintf(buf, "%llu\n", sta);
395}
396static DEVICE_ATTR_RO(status);
397
398/**
399 * acpi_device_setup_files - Create sysfs attributes of an ACPI device.
400 * @dev: ACPI device object.
401 */
402int acpi_device_setup_files(struct acpi_device *dev)
403{
404 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
405 acpi_status status;
406 int result = 0;
407
408 /*
409 * Devices gotten from FADT don't have a "path" attribute
410 */
411 if (dev->handle) {
412 result = device_create_file(&dev->dev, &dev_attr_path);
413 if (result)
414 goto end;
415 }
416
417 if (!list_empty(&dev->pnp.ids)) {
418 result = device_create_file(&dev->dev, &dev_attr_hid);
419 if (result)
420 goto end;
421
422 result = device_create_file(&dev->dev, &dev_attr_modalias);
423 if (result)
424 goto end;
425 }
426
427 /*
428 * If device has _STR, 'description' file is created
429 */
430 if (acpi_has_method(dev->handle, "_STR")) {
431 status = acpi_evaluate_object(dev->handle, "_STR",
432 NULL, &buffer);
433 if (ACPI_FAILURE(status))
434 buffer.pointer = NULL;
435 dev->pnp.str_obj = buffer.pointer;
436 result = device_create_file(&dev->dev, &dev_attr_description);
437 if (result)
438 goto end;
439 }
440
441 if (dev->pnp.type.bus_address)
442 result = device_create_file(&dev->dev, &dev_attr_adr);
443 if (dev->pnp.unique_id)
444 result = device_create_file(&dev->dev, &dev_attr_uid);
445
446 if (acpi_has_method(dev->handle, "_SUN")) {
447 result = device_create_file(&dev->dev, &dev_attr_sun);
448 if (result)
449 goto end;
450 }
451
452 if (acpi_has_method(dev->handle, "_STA")) {
453 result = device_create_file(&dev->dev, &dev_attr_status);
454 if (result)
455 goto end;
456 }
457
458 /*
459 * If device has _EJ0, 'eject' file is created that is used to trigger
460 * hot-removal function from userland.
461 */
462 if (acpi_has_method(dev->handle, "_EJ0")) {
463 result = device_create_file(&dev->dev, &dev_attr_eject);
464 if (result)
465 return result;
466 }
467
468 if (dev->flags.power_manageable) {
469 result = device_create_file(&dev->dev, &dev_attr_power_state);
470 if (result)
471 return result;
472
473 if (dev->power.flags.power_resources)
474 result = device_create_file(&dev->dev,
475 &dev_attr_real_power_state);
476 }
477
478end:
479 return result;
480}
481
482/**
483 * acpi_device_remove_files - Remove sysfs attributes of an ACPI device.
484 * @dev: ACPI device object.
485 */
486void acpi_device_remove_files(struct acpi_device *dev)
487{
488 if (dev->flags.power_manageable) {
489 device_remove_file(&dev->dev, &dev_attr_power_state);
490 if (dev->power.flags.power_resources)
491 device_remove_file(&dev->dev,
492 &dev_attr_real_power_state);
493 }
494
495 /*
496 * If device has _STR, remove 'description' file
497 */
498 if (acpi_has_method(dev->handle, "_STR")) {
499 kfree(dev->pnp.str_obj);
500 device_remove_file(&dev->dev, &dev_attr_description);
501 }
502 /*
503 * If device has _EJ0, remove 'eject' file.
504 */
505 if (acpi_has_method(dev->handle, "_EJ0"))
506 device_remove_file(&dev->dev, &dev_attr_eject);
507
508 if (acpi_has_method(dev->handle, "_SUN"))
509 device_remove_file(&dev->dev, &dev_attr_sun);
510
511 if (dev->pnp.unique_id)
512 device_remove_file(&dev->dev, &dev_attr_uid);
513 if (dev->pnp.type.bus_address)
514 device_remove_file(&dev->dev, &dev_attr_adr);
515 device_remove_file(&dev->dev, &dev_attr_modalias);
516 device_remove_file(&dev->dev, &dev_attr_hid);
517 if (acpi_has_method(dev->handle, "_STA"))
518 device_remove_file(&dev->dev, &dev_attr_status);
519 if (dev->handle)
520 device_remove_file(&dev->dev, &dev_attr_path);
521}
This page took 0.043881 seconds and 5 git commands to generate.