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, &not_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