18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/acpi/device_sysfs.c - ACPI device sysfs attributes and modalias. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015, Intel Corp. 68c2ecf20Sopenharmony_ci * Author: Mika Westerberg <mika.westerberg@linux.intel.com> 78c2ecf20Sopenharmony_ci * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/acpi.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/export.h> 178c2ecf20Sopenharmony_ci#include <linux/nls.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "internal.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic ssize_t acpi_object_path(acpi_handle handle, char *buf) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; 248c2ecf20Sopenharmony_ci int result; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci result = acpi_get_name(handle, ACPI_FULL_PATHNAME, &path); 278c2ecf20Sopenharmony_ci if (result) 288c2ecf20Sopenharmony_ci return result; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci result = sprintf(buf, "%s\n", (char *)path.pointer); 318c2ecf20Sopenharmony_ci kfree(path.pointer); 328c2ecf20Sopenharmony_ci return result; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct acpi_data_node_attr { 368c2ecf20Sopenharmony_ci struct attribute attr; 378c2ecf20Sopenharmony_ci ssize_t (*show)(struct acpi_data_node *, char *); 388c2ecf20Sopenharmony_ci ssize_t (*store)(struct acpi_data_node *, const char *, size_t count); 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define DATA_NODE_ATTR(_name) \ 428c2ecf20Sopenharmony_ci static struct acpi_data_node_attr data_node_##_name = \ 438c2ecf20Sopenharmony_ci __ATTR(_name, 0444, data_node_show_##_name, NULL) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic ssize_t data_node_show_path(struct acpi_data_node *dn, char *buf) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci return dn->handle ? acpi_object_path(dn->handle, buf) : 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciDATA_NODE_ATTR(path); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct attribute *acpi_data_node_default_attrs[] = { 538c2ecf20Sopenharmony_ci &data_node_path.attr, 548c2ecf20Sopenharmony_ci NULL 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define to_data_node(k) container_of(k, struct acpi_data_node, kobj) 588c2ecf20Sopenharmony_ci#define to_attr(a) container_of(a, struct acpi_data_node_attr, attr) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic ssize_t acpi_data_node_attr_show(struct kobject *kobj, 618c2ecf20Sopenharmony_ci struct attribute *attr, char *buf) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct acpi_data_node *dn = to_data_node(kobj); 648c2ecf20Sopenharmony_ci struct acpi_data_node_attr *dn_attr = to_attr(attr); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return dn_attr->show ? dn_attr->show(dn, buf) : -ENXIO; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic const struct sysfs_ops acpi_data_node_sysfs_ops = { 708c2ecf20Sopenharmony_ci .show = acpi_data_node_attr_show, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void acpi_data_node_release(struct kobject *kobj) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct acpi_data_node *dn = to_data_node(kobj); 768c2ecf20Sopenharmony_ci complete(&dn->kobj_done); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic struct kobj_type acpi_data_node_ktype = { 808c2ecf20Sopenharmony_ci .sysfs_ops = &acpi_data_node_sysfs_ops, 818c2ecf20Sopenharmony_ci .default_attrs = acpi_data_node_default_attrs, 828c2ecf20Sopenharmony_ci .release = acpi_data_node_release, 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void acpi_expose_nondev_subnodes(struct kobject *kobj, 868c2ecf20Sopenharmony_ci struct acpi_device_data *data) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct list_head *list = &data->subnodes; 898c2ecf20Sopenharmony_ci struct acpi_data_node *dn; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (list_empty(list)) 928c2ecf20Sopenharmony_ci return; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci list_for_each_entry(dn, list, sibling) { 958c2ecf20Sopenharmony_ci int ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci init_completion(&dn->kobj_done); 988c2ecf20Sopenharmony_ci ret = kobject_init_and_add(&dn->kobj, &acpi_data_node_ktype, 998c2ecf20Sopenharmony_ci kobj, "%s", dn->name); 1008c2ecf20Sopenharmony_ci if (!ret) 1018c2ecf20Sopenharmony_ci acpi_expose_nondev_subnodes(&dn->kobj, &dn->data); 1028c2ecf20Sopenharmony_ci else if (dn->handle) 1038c2ecf20Sopenharmony_ci acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void acpi_hide_nondev_subnodes(struct acpi_device_data *data) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct list_head *list = &data->subnodes; 1108c2ecf20Sopenharmony_ci struct acpi_data_node *dn; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (list_empty(list)) 1138c2ecf20Sopenharmony_ci return; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci list_for_each_entry_reverse(dn, list, sibling) { 1168c2ecf20Sopenharmony_ci acpi_hide_nondev_subnodes(&dn->data); 1178c2ecf20Sopenharmony_ci kobject_put(&dn->kobj); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent 1238c2ecf20Sopenharmony_ci * @acpi_dev: ACPI device object. 1248c2ecf20Sopenharmony_ci * @modalias: Buffer to print into. 1258c2ecf20Sopenharmony_ci * @size: Size of the buffer. 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * Creates hid/cid(s) string needed for modalias and uevent 1288c2ecf20Sopenharmony_ci * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: 1298c2ecf20Sopenharmony_ci * char *modalias: "acpi:IBM0001:ACPI0001" 1308c2ecf20Sopenharmony_ci * Return: 0: no _HID and no _CID 1318c2ecf20Sopenharmony_ci * -EINVAL: output error 1328c2ecf20Sopenharmony_ci * -ENOMEM: output is truncated 1338c2ecf20Sopenharmony_ci*/ 1348c2ecf20Sopenharmony_cistatic int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, 1358c2ecf20Sopenharmony_ci int size) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci int len; 1388c2ecf20Sopenharmony_ci int count; 1398c2ecf20Sopenharmony_ci struct acpi_hardware_id *id; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Avoid unnecessarily loading modules for non present devices. */ 1428c2ecf20Sopenharmony_ci if (!acpi_device_is_present(acpi_dev)) 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should 1478c2ecf20Sopenharmony_ci * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the 1488c2ecf20Sopenharmony_ci * device's list. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci count = 0; 1518c2ecf20Sopenharmony_ci list_for_each_entry(id, &acpi_dev->pnp.ids, list) 1528c2ecf20Sopenharmony_ci if (strcmp(id->id, ACPI_DT_NAMESPACE_HID)) 1538c2ecf20Sopenharmony_ci count++; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (!count) 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci len = snprintf(modalias, size, "acpi:"); 1598c2ecf20Sopenharmony_ci if (len >= size) 1608c2ecf20Sopenharmony_ci return -ENOMEM; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci size -= len; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci list_for_each_entry(id, &acpi_dev->pnp.ids, list) { 1658c2ecf20Sopenharmony_ci if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID)) 1668c2ecf20Sopenharmony_ci continue; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci count = snprintf(&modalias[len], size, "%s:", id->id); 1698c2ecf20Sopenharmony_ci if (count < 0) 1708c2ecf20Sopenharmony_ci return -EINVAL; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (count >= size) 1738c2ecf20Sopenharmony_ci return -ENOMEM; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci len += count; 1768c2ecf20Sopenharmony_ci size -= count; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci modalias[len] = '\0'; 1798c2ecf20Sopenharmony_ci return len; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/** 1838c2ecf20Sopenharmony_ci * create_of_modalias - Creates DT compatible string for modalias and uevent 1848c2ecf20Sopenharmony_ci * @acpi_dev: ACPI device object. 1858c2ecf20Sopenharmony_ci * @modalias: Buffer to print into. 1868c2ecf20Sopenharmony_ci * @size: Size of the buffer. 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * Expose DT compatible modalias as of:NnameTCcompatible. This function should 1898c2ecf20Sopenharmony_ci * only be called for devices having ACPI_DT_NAMESPACE_HID in their list of 1908c2ecf20Sopenharmony_ci * ACPI/PNP IDs. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, 1938c2ecf20Sopenharmony_ci int size) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; 1968c2ecf20Sopenharmony_ci const union acpi_object *of_compatible, *obj; 1978c2ecf20Sopenharmony_ci acpi_status status; 1988c2ecf20Sopenharmony_ci int len, count; 1998c2ecf20Sopenharmony_ci int i, nval; 2008c2ecf20Sopenharmony_ci char *c; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci status = acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); 2038c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 2048c2ecf20Sopenharmony_ci return -ENODEV; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* DT strings are all in lower case */ 2078c2ecf20Sopenharmony_ci for (c = buf.pointer; *c != '\0'; c++) 2088c2ecf20Sopenharmony_ci *c = tolower(*c); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); 2118c2ecf20Sopenharmony_ci ACPI_FREE(buf.pointer); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (len >= size) 2148c2ecf20Sopenharmony_ci return -ENOMEM; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci size -= len; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci of_compatible = acpi_dev->data.of_compatible; 2198c2ecf20Sopenharmony_ci if (of_compatible->type == ACPI_TYPE_PACKAGE) { 2208c2ecf20Sopenharmony_ci nval = of_compatible->package.count; 2218c2ecf20Sopenharmony_ci obj = of_compatible->package.elements; 2228c2ecf20Sopenharmony_ci } else { /* Must be ACPI_TYPE_STRING. */ 2238c2ecf20Sopenharmony_ci nval = 1; 2248c2ecf20Sopenharmony_ci obj = of_compatible; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci for (i = 0; i < nval; i++, obj++) { 2278c2ecf20Sopenharmony_ci count = snprintf(&modalias[len], size, "C%s", 2288c2ecf20Sopenharmony_ci obj->string.pointer); 2298c2ecf20Sopenharmony_ci if (count < 0) 2308c2ecf20Sopenharmony_ci return -EINVAL; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (count >= size) 2338c2ecf20Sopenharmony_ci return -ENOMEM; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci len += count; 2368c2ecf20Sopenharmony_ci size -= count; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci modalias[len] = '\0'; 2398c2ecf20Sopenharmony_ci return len; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciint __acpi_device_uevent_modalias(struct acpi_device *adev, 2438c2ecf20Sopenharmony_ci struct kobj_uevent_env *env) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci int len; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (!adev) 2488c2ecf20Sopenharmony_ci return -ENODEV; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (list_empty(&adev->pnp.ids)) 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (add_uevent_var(env, "MODALIAS=")) 2548c2ecf20Sopenharmony_ci return -ENOMEM; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (adev->data.of_compatible) 2578c2ecf20Sopenharmony_ci len = create_of_modalias(adev, &env->buf[env->buflen - 1], 2588c2ecf20Sopenharmony_ci sizeof(env->buf) - env->buflen); 2598c2ecf20Sopenharmony_ci else 2608c2ecf20Sopenharmony_ci len = create_pnp_modalias(adev, &env->buf[env->buflen - 1], 2618c2ecf20Sopenharmony_ci sizeof(env->buf) - env->buflen); 2628c2ecf20Sopenharmony_ci if (len < 0) 2638c2ecf20Sopenharmony_ci return len; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci env->buflen += len; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/** 2718c2ecf20Sopenharmony_ci * acpi_device_uevent_modalias - uevent modalias for ACPI-enumerated devices. 2728c2ecf20Sopenharmony_ci * 2738c2ecf20Sopenharmony_ci * Create the uevent modalias field for ACPI-enumerated devices. 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with 2768c2ecf20Sopenharmony_ci * hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001". 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ciint acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci return __acpi_device_uevent_modalias(acpi_companion_match(dev), env); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci int len, count; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!adev) 2898c2ecf20Sopenharmony_ci return -ENODEV; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (list_empty(&adev->pnp.ids)) 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci len = create_pnp_modalias(adev, buf, size - 1); 2958c2ecf20Sopenharmony_ci if (len < 0) { 2968c2ecf20Sopenharmony_ci return len; 2978c2ecf20Sopenharmony_ci } else if (len > 0) { 2988c2ecf20Sopenharmony_ci buf[len++] = '\n'; 2998c2ecf20Sopenharmony_ci size -= len; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci if (!adev->data.of_compatible) 3028c2ecf20Sopenharmony_ci return len; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci count = create_of_modalias(adev, buf + len, size - 1); 3058c2ecf20Sopenharmony_ci if (count < 0) { 3068c2ecf20Sopenharmony_ci return count; 3078c2ecf20Sopenharmony_ci } else if (count > 0) { 3088c2ecf20Sopenharmony_ci len += count; 3098c2ecf20Sopenharmony_ci buf[len++] = '\n'; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return len; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/** 3168c2ecf20Sopenharmony_ci * acpi_device_modalias - modalias sysfs attribute for ACPI-enumerated devices. 3178c2ecf20Sopenharmony_ci * 3188c2ecf20Sopenharmony_ci * Create the modalias sysfs attribute for ACPI-enumerated devices. 3198c2ecf20Sopenharmony_ci * 3208c2ecf20Sopenharmony_ci * Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with 3218c2ecf20Sopenharmony_ci * hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001". 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ciint acpi_device_modalias(struct device *dev, char *buf, int size) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci return __acpi_device_modalias(acpi_companion_match(dev), buf, size); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_device_modalias); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic ssize_t 3308c2ecf20Sopenharmony_cimodalias_show(struct device *dev, struct device_attribute *attr, char *buf) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci return __acpi_device_modalias(to_acpi_device(dev), buf, 1024); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic ssize_t real_power_state_show(struct device *dev, 3378c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct acpi_device *adev = to_acpi_device(dev); 3408c2ecf20Sopenharmony_ci int state; 3418c2ecf20Sopenharmony_ci int ret; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ret = acpi_device_get_power(adev, &state); 3448c2ecf20Sopenharmony_ci if (ret) 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", acpi_power_state_string(state)); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(real_power_state); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic ssize_t power_state_show(struct device *dev, 3538c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct acpi_device *adev = to_acpi_device(dev); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state)); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power_state); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic ssize_t 3638c2ecf20Sopenharmony_cieject_store(struct device *d, struct device_attribute *attr, 3648c2ecf20Sopenharmony_ci const char *buf, size_t count) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct acpi_device *acpi_device = to_acpi_device(d); 3678c2ecf20Sopenharmony_ci acpi_object_type not_used; 3688c2ecf20Sopenharmony_ci acpi_status status; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!count || buf[0] != '1') 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) 3748c2ecf20Sopenharmony_ci && !acpi_device->driver) 3758c2ecf20Sopenharmony_ci return -ENODEV; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci status = acpi_get_type(acpi_device->handle, ¬_used); 3788c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) 3798c2ecf20Sopenharmony_ci return -ENODEV; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci get_device(&acpi_device->dev); 3828c2ecf20Sopenharmony_ci status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); 3838c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) 3848c2ecf20Sopenharmony_ci return count; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci put_device(&acpi_device->dev); 3878c2ecf20Sopenharmony_ci acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, 3888c2ecf20Sopenharmony_ci ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); 3898c2ecf20Sopenharmony_ci return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(eject); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic ssize_t 3958c2ecf20Sopenharmony_cihid_show(struct device *dev, struct device_attribute *attr, char *buf) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = to_acpi_device(dev); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev)); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(hid); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic ssize_t uid_show(struct device *dev, 4048c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = to_acpi_device(dev); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", acpi_dev->pnp.unique_id); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(uid); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic ssize_t adr_show(struct device *dev, 4138c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = to_acpi_device(dev); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (acpi_dev->pnp.bus_address > U32_MAX) 4188c2ecf20Sopenharmony_ci return sprintf(buf, "0x%016llx\n", acpi_dev->pnp.bus_address); 4198c2ecf20Sopenharmony_ci else 4208c2ecf20Sopenharmony_ci return sprintf(buf, "0x%08llx\n", acpi_dev->pnp.bus_address); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(adr); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic ssize_t path_show(struct device *dev, 4258c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = to_acpi_device(dev); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return acpi_object_path(acpi_dev->handle, buf); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(path); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* sysfs file that shows description text from the ACPI _STR method */ 4348c2ecf20Sopenharmony_cistatic ssize_t description_show(struct device *dev, 4358c2ecf20Sopenharmony_ci struct device_attribute *attr, 4368c2ecf20Sopenharmony_ci char *buf) { 4378c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = to_acpi_device(dev); 4388c2ecf20Sopenharmony_ci int result; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (acpi_dev->pnp.str_obj == NULL) 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* 4448c2ecf20Sopenharmony_ci * The _STR object contains a Unicode identifier for a device. 4458c2ecf20Sopenharmony_ci * We need to convert to utf-8 so it can be displayed. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci result = utf16s_to_utf8s( 4488c2ecf20Sopenharmony_ci (wchar_t *)acpi_dev->pnp.str_obj->buffer.pointer, 4498c2ecf20Sopenharmony_ci acpi_dev->pnp.str_obj->buffer.length, 4508c2ecf20Sopenharmony_ci UTF16_LITTLE_ENDIAN, buf, 4518c2ecf20Sopenharmony_ci PAGE_SIZE - 1); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci buf[result++] = '\n'; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return result; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(description); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic ssize_t 4608c2ecf20Sopenharmony_cisun_show(struct device *dev, struct device_attribute *attr, 4618c2ecf20Sopenharmony_ci char *buf) { 4628c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = to_acpi_device(dev); 4638c2ecf20Sopenharmony_ci acpi_status status; 4648c2ecf20Sopenharmony_ci unsigned long long sun; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(acpi_dev->handle, "_SUN", NULL, &sun); 4678c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 4688c2ecf20Sopenharmony_ci return -EIO; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", sun); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(sun); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic ssize_t 4758c2ecf20Sopenharmony_cihrv_show(struct device *dev, struct device_attribute *attr, 4768c2ecf20Sopenharmony_ci char *buf) { 4778c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = to_acpi_device(dev); 4788c2ecf20Sopenharmony_ci acpi_status status; 4798c2ecf20Sopenharmony_ci unsigned long long hrv; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(acpi_dev->handle, "_HRV", NULL, &hrv); 4828c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 4838c2ecf20Sopenharmony_ci return -EIO; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", hrv); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(hrv); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic ssize_t status_show(struct device *dev, struct device_attribute *attr, 4908c2ecf20Sopenharmony_ci char *buf) { 4918c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = to_acpi_device(dev); 4928c2ecf20Sopenharmony_ci acpi_status status; 4938c2ecf20Sopenharmony_ci unsigned long long sta; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta); 4968c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 4978c2ecf20Sopenharmony_ci return -EIO; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", sta); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(status); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/** 5048c2ecf20Sopenharmony_ci * acpi_device_setup_files - Create sysfs attributes of an ACPI device. 5058c2ecf20Sopenharmony_ci * @dev: ACPI device object. 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ciint acpi_device_setup_files(struct acpi_device *dev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 5108c2ecf20Sopenharmony_ci acpi_status status; 5118c2ecf20Sopenharmony_ci int result = 0; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * Devices gotten from FADT don't have a "path" attribute 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_ci if (dev->handle) { 5178c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_path); 5188c2ecf20Sopenharmony_ci if (result) 5198c2ecf20Sopenharmony_ci goto end; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (!list_empty(&dev->pnp.ids)) { 5238c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_hid); 5248c2ecf20Sopenharmony_ci if (result) 5258c2ecf20Sopenharmony_ci goto end; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_modalias); 5288c2ecf20Sopenharmony_ci if (result) 5298c2ecf20Sopenharmony_ci goto end; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * If device has _STR, 'description' file is created 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_STR")) { 5368c2ecf20Sopenharmony_ci status = acpi_evaluate_object(dev->handle, "_STR", 5378c2ecf20Sopenharmony_ci NULL, &buffer); 5388c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 5398c2ecf20Sopenharmony_ci buffer.pointer = NULL; 5408c2ecf20Sopenharmony_ci dev->pnp.str_obj = buffer.pointer; 5418c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_description); 5428c2ecf20Sopenharmony_ci if (result) 5438c2ecf20Sopenharmony_ci goto end; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (dev->pnp.type.bus_address) 5478c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_adr); 5488c2ecf20Sopenharmony_ci if (dev->pnp.unique_id) 5498c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_uid); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_SUN")) { 5528c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_sun); 5538c2ecf20Sopenharmony_ci if (result) 5548c2ecf20Sopenharmony_ci goto end; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_HRV")) { 5588c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_hrv); 5598c2ecf20Sopenharmony_ci if (result) 5608c2ecf20Sopenharmony_ci goto end; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_STA")) { 5648c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_status); 5658c2ecf20Sopenharmony_ci if (result) 5668c2ecf20Sopenharmony_ci goto end; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* 5708c2ecf20Sopenharmony_ci * If device has _EJ0, 'eject' file is created that is used to trigger 5718c2ecf20Sopenharmony_ci * hot-removal function from userland. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_EJ0")) { 5748c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_eject); 5758c2ecf20Sopenharmony_ci if (result) 5768c2ecf20Sopenharmony_ci return result; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (dev->flags.power_manageable) { 5808c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, &dev_attr_power_state); 5818c2ecf20Sopenharmony_ci if (result) 5828c2ecf20Sopenharmony_ci return result; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (dev->power.flags.power_resources) 5858c2ecf20Sopenharmony_ci result = device_create_file(&dev->dev, 5868c2ecf20Sopenharmony_ci &dev_attr_real_power_state); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci acpi_expose_nondev_subnodes(&dev->dev.kobj, &dev->data); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ciend: 5928c2ecf20Sopenharmony_ci return result; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci/** 5968c2ecf20Sopenharmony_ci * acpi_device_remove_files - Remove sysfs attributes of an ACPI device. 5978c2ecf20Sopenharmony_ci * @dev: ACPI device object. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_civoid acpi_device_remove_files(struct acpi_device *dev) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci acpi_hide_nondev_subnodes(&dev->data); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (dev->flags.power_manageable) { 6048c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_power_state); 6058c2ecf20Sopenharmony_ci if (dev->power.flags.power_resources) 6068c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, 6078c2ecf20Sopenharmony_ci &dev_attr_real_power_state); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* 6118c2ecf20Sopenharmony_ci * If device has _STR, remove 'description' file 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_STR")) { 6148c2ecf20Sopenharmony_ci kfree(dev->pnp.str_obj); 6158c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_description); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci /* 6188c2ecf20Sopenharmony_ci * If device has _EJ0, remove 'eject' file. 6198c2ecf20Sopenharmony_ci */ 6208c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_EJ0")) 6218c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_eject); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_SUN")) 6248c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_sun); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_HRV")) 6278c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_hrv); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (dev->pnp.unique_id) 6308c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_uid); 6318c2ecf20Sopenharmony_ci if (dev->pnp.type.bus_address) 6328c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_adr); 6338c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_modalias); 6348c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_hid); 6358c2ecf20Sopenharmony_ci if (acpi_has_method(dev->handle, "_STA")) 6368c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_status); 6378c2ecf20Sopenharmony_ci if (dev->handle) 6388c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_path); 6398c2ecf20Sopenharmony_ci} 640