brintos

brintos / linux-shallow public Read only

0
0
Text · 15.3 KiB · 3961fc4 Raw
621 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * drivers/acpi/device_sysfs.c - ACPI device sysfs attributes and modalias.4 *5 * Copyright (C) 2015, Intel Corp.6 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>7 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>8 *9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~10 *11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~12 */13 14#include <linux/acpi.h>15#include <linux/device.h>16#include <linux/export.h>17#include <linux/nls.h>18 19#include "internal.h"20 21static ssize_t acpi_object_path(acpi_handle handle, char *buf)22{23	struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};24	int result;25 26	result = acpi_get_name(handle, ACPI_FULL_PATHNAME, &path);27	if (result)28		return result;29 30	result = sprintf(buf, "%s\n", (char *)path.pointer);31	kfree(path.pointer);32	return result;33}34 35struct acpi_data_node_attr {36	struct attribute attr;37	ssize_t (*show)(struct acpi_data_node *, char *);38	ssize_t (*store)(struct acpi_data_node *, const char *, size_t count);39};40 41#define DATA_NODE_ATTR(_name)			\42	static struct acpi_data_node_attr data_node_##_name =	\43		__ATTR(_name, 0444, data_node_show_##_name, NULL)44 45static ssize_t data_node_show_path(struct acpi_data_node *dn, char *buf)46{47	return dn->handle ? acpi_object_path(dn->handle, buf) : 0;48}49 50DATA_NODE_ATTR(path);51 52static struct attribute *acpi_data_node_default_attrs[] = {53	&data_node_path.attr,54	NULL55};56ATTRIBUTE_GROUPS(acpi_data_node_default);57 58#define to_data_node(k) container_of(k, struct acpi_data_node, kobj)59#define to_attr(a) container_of(a, struct acpi_data_node_attr, attr)60 61static ssize_t acpi_data_node_attr_show(struct kobject *kobj,62					struct attribute *attr, char *buf)63{64	struct acpi_data_node *dn = to_data_node(kobj);65	struct acpi_data_node_attr *dn_attr = to_attr(attr);66 67	return dn_attr->show ? dn_attr->show(dn, buf) : -ENXIO;68}69 70static const struct sysfs_ops acpi_data_node_sysfs_ops = {71	.show	= acpi_data_node_attr_show,72};73 74static void acpi_data_node_release(struct kobject *kobj)75{76	struct acpi_data_node *dn = to_data_node(kobj);77 78	complete(&dn->kobj_done);79}80 81static const struct kobj_type acpi_data_node_ktype = {82	.sysfs_ops = &acpi_data_node_sysfs_ops,83	.default_groups = acpi_data_node_default_groups,84	.release = acpi_data_node_release,85};86 87static void acpi_expose_nondev_subnodes(struct kobject *kobj,88					struct acpi_device_data *data)89{90	struct list_head *list = &data->subnodes;91	struct acpi_data_node *dn;92 93	if (list_empty(list))94		return;95 96	list_for_each_entry(dn, list, sibling) {97		int ret;98 99		init_completion(&dn->kobj_done);100		ret = kobject_init_and_add(&dn->kobj, &acpi_data_node_ktype,101					   kobj, "%s", dn->name);102		if (!ret)103			acpi_expose_nondev_subnodes(&dn->kobj, &dn->data);104		else if (dn->handle)105			acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret);106	}107}108 109static void acpi_hide_nondev_subnodes(struct acpi_device_data *data)110{111	struct list_head *list = &data->subnodes;112	struct acpi_data_node *dn;113 114	if (list_empty(list))115		return;116 117	list_for_each_entry_reverse(dn, list, sibling) {118		acpi_hide_nondev_subnodes(&dn->data);119		kobject_put(&dn->kobj);120	}121}122 123/**124 * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent125 * @acpi_dev: ACPI device object.126 * @modalias: Buffer to print into.127 * @size: Size of the buffer.128 *129 * Creates hid/cid(s) string needed for modalias and uevent130 * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:131 * char *modalias: "acpi:IBM0001:ACPI0001"132 * Return: 0: no _HID and no _CID133 *         -EINVAL: output error134 *         -ENOMEM: output is truncated135 */136static int create_pnp_modalias(const struct acpi_device *acpi_dev, char *modalias,137			       int size)138{139	int len;140	int count;141	struct acpi_hardware_id *id;142 143	/* Avoid unnecessarily loading modules for non present devices. */144	if (!acpi_device_is_present(acpi_dev))145		return 0;146 147	/*148	 * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should149	 * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the150	 * device's list.151	 */152	count = 0;153	list_for_each_entry(id, &acpi_dev->pnp.ids, list)154		if (strcmp(id->id, ACPI_DT_NAMESPACE_HID))155			count++;156 157	if (!count)158		return 0;159 160	len = snprintf(modalias, size, "acpi:");161	if (len >= size)162		return -ENOMEM;163 164	size -= len;165 166	list_for_each_entry(id, &acpi_dev->pnp.ids, list) {167		if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID))168			continue;169 170		count = snprintf(&modalias[len], size, "%s:", id->id);171 172		if (count >= size)173			return -ENOMEM;174 175		len += count;176		size -= count;177	}178 179	return len;180}181 182/**183 * create_of_modalias - Creates DT compatible string for modalias and uevent184 * @acpi_dev: ACPI device object.185 * @modalias: Buffer to print into.186 * @size: Size of the buffer.187 *188 * Expose DT compatible modalias as of:NnameTCcompatible.  This function should189 * only be called for devices having ACPI_DT_NAMESPACE_HID in their list of190 * ACPI/PNP IDs.191 */192static int create_of_modalias(const struct acpi_device *acpi_dev, char *modalias,193			      int size)194{195	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };196	const union acpi_object *of_compatible, *obj;197	acpi_status status;198	int len, count;199	int i, nval;200	char *c;201 202	status = acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);203	if (ACPI_FAILURE(status))204		return -ENODEV;205 206	/* DT strings are all in lower case */207	for (c = buf.pointer; *c != '\0'; c++)208		*c = tolower(*c);209 210	len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);211	ACPI_FREE(buf.pointer);212 213	if (len >= size)214		return -ENOMEM;215 216	size -= len;217 218	of_compatible = acpi_dev->data.of_compatible;219	if (of_compatible->type == ACPI_TYPE_PACKAGE) {220		nval = of_compatible->package.count;221		obj = of_compatible->package.elements;222	} else { /* Must be ACPI_TYPE_STRING. */223		nval = 1;224		obj = of_compatible;225	}226	for (i = 0; i < nval; i++, obj++) {227		count = snprintf(&modalias[len], size, "C%s",228				 obj->string.pointer);229 230		if (count >= size)231			return -ENOMEM;232 233		len += count;234		size -= count;235	}236 237	return len;238}239 240int __acpi_device_uevent_modalias(const struct acpi_device *adev,241				  struct kobj_uevent_env *env)242{243	int len;244 245	if (!adev)246		return -ENODEV;247 248	if (list_empty(&adev->pnp.ids))249		return 0;250 251	if (add_uevent_var(env, "MODALIAS="))252		return -ENOMEM;253 254	if (adev->data.of_compatible)255		len = create_of_modalias(adev, &env->buf[env->buflen - 1],256					 sizeof(env->buf) - env->buflen);257	else258		len = create_pnp_modalias(adev, &env->buf[env->buflen - 1],259					  sizeof(env->buf) - env->buflen);260	if (len < 0)261		return len;262 263	env->buflen += len;264 265	return 0;266}267 268/**269 * acpi_device_uevent_modalias - uevent modalias for ACPI-enumerated devices.270 * @dev: Struct device to get ACPI device node.271 * @env: Environment variables of the kobject uevent.272 *273 * Create the uevent modalias field for ACPI-enumerated devices.274 *275 * Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with276 * hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001".277 */278int acpi_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *env)279{280	return __acpi_device_uevent_modalias(acpi_companion_match(dev), env);281}282EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);283 284static int __acpi_device_modalias(const struct acpi_device *adev, char *buf, int size)285{286	int len, count;287 288	if (!adev)289		return -ENODEV;290 291	if (list_empty(&adev->pnp.ids))292		return 0;293 294	len = create_pnp_modalias(adev, buf, size - 1);295	if (len < 0) {296		return len;297	} else if (len > 0) {298		buf[len++] = '\n';299		size -= len;300	}301	if (!adev->data.of_compatible)302		return len;303 304	count = create_of_modalias(adev, buf + len, size - 1);305	if (count < 0) {306		return count;307	} else if (count > 0) {308		len += count;309		buf[len++] = '\n';310	}311 312	return len;313}314 315/**316 * acpi_device_modalias - modalias sysfs attribute for ACPI-enumerated devices.317 * @dev: Struct device to get ACPI device node.318 * @buf: The buffer to save pnp_modalias and of_modalias.319 * @size: Size of buffer.320 *321 * Create the modalias sysfs attribute for ACPI-enumerated devices.322 *323 * Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with324 * hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001".325 */326int acpi_device_modalias(struct device *dev, char *buf, int size)327{328	return __acpi_device_modalias(acpi_companion_match(dev), buf, size);329}330EXPORT_SYMBOL_GPL(acpi_device_modalias);331 332static ssize_t333modalias_show(struct device *dev, struct device_attribute *attr, char *buf)334{335	return __acpi_device_modalias(to_acpi_device(dev), buf, 1024);336}337static DEVICE_ATTR_RO(modalias);338 339static ssize_t real_power_state_show(struct device *dev,340				     struct device_attribute *attr, char *buf)341{342	struct acpi_device *adev = to_acpi_device(dev);343	int state;344	int ret;345 346	ret = acpi_device_get_power(adev, &state);347	if (ret)348		return ret;349 350	return sprintf(buf, "%s\n", acpi_power_state_string(state));351}352 353static DEVICE_ATTR_RO(real_power_state);354 355static ssize_t power_state_show(struct device *dev,356				struct device_attribute *attr, char *buf)357{358	struct acpi_device *adev = to_acpi_device(dev);359 360	return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state));361}362 363static DEVICE_ATTR_RO(power_state);364 365static ssize_t366eject_store(struct device *d, struct device_attribute *attr,367	    const char *buf, size_t count)368{369	struct acpi_device *acpi_device = to_acpi_device(d);370	acpi_object_type not_used;371	acpi_status status;372 373	if (!count || buf[0] != '1')374		return -EINVAL;375 376	if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)377	    && !d->driver)378		return -ENODEV;379 380	status = acpi_get_type(acpi_device->handle, &not_used);381	if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)382		return -ENODEV;383 384	acpi_dev_get(acpi_device);385	status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT);386	if (ACPI_SUCCESS(status))387		return count;388 389	acpi_dev_put(acpi_device);390	acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,391			  ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);392	return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;393}394 395static DEVICE_ATTR_WO(eject);396 397static ssize_t398hid_show(struct device *dev, struct device_attribute *attr, char *buf)399{400	struct acpi_device *acpi_dev = to_acpi_device(dev);401 402	return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev));403}404static DEVICE_ATTR_RO(hid);405 406static ssize_t uid_show(struct device *dev,407			struct device_attribute *attr, char *buf)408{409	struct acpi_device *acpi_dev = to_acpi_device(dev);410 411	return sprintf(buf, "%s\n", acpi_device_uid(acpi_dev));412}413static DEVICE_ATTR_RO(uid);414 415static ssize_t adr_show(struct device *dev,416			struct device_attribute *attr, char *buf)417{418	struct acpi_device *acpi_dev = to_acpi_device(dev);419 420	if (acpi_dev->pnp.bus_address > U32_MAX)421		return sprintf(buf, "0x%016llx\n", acpi_dev->pnp.bus_address);422	else423		return sprintf(buf, "0x%08llx\n", acpi_dev->pnp.bus_address);424}425static DEVICE_ATTR_RO(adr);426 427static ssize_t path_show(struct device *dev,428			 struct device_attribute *attr, char *buf)429{430	struct acpi_device *acpi_dev = to_acpi_device(dev);431 432	return acpi_object_path(acpi_dev->handle, buf);433}434static DEVICE_ATTR_RO(path);435 436/* sysfs file that shows description text from the ACPI _STR method */437static ssize_t description_show(struct device *dev,438				struct device_attribute *attr,439				char *buf)440{441	struct acpi_device *acpi_dev = to_acpi_device(dev);442	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};443	union acpi_object *str_obj;444	acpi_status status;445	int result;446 447	status = acpi_evaluate_object_typed(acpi_dev->handle, "_STR",448					    NULL, &buffer,449					    ACPI_TYPE_BUFFER);450	if (ACPI_FAILURE(status))451		return -EIO;452 453	str_obj = buffer.pointer;454 455	/*456	 * The _STR object contains a Unicode identifier for a device.457	 * We need to convert to utf-8 so it can be displayed.458	 */459	result = utf16s_to_utf8s(460		(wchar_t *)str_obj->buffer.pointer,461		str_obj->buffer.length,462		UTF16_LITTLE_ENDIAN, buf,463		PAGE_SIZE - 1);464 465	buf[result++] = '\n';466 467	kfree(str_obj);468 469	return result;470}471static DEVICE_ATTR_RO(description);472 473static ssize_t474sun_show(struct device *dev, struct device_attribute *attr,475	 char *buf)476{477	struct acpi_device *acpi_dev = to_acpi_device(dev);478	acpi_status status;479	unsigned long long sun;480 481	status = acpi_evaluate_integer(acpi_dev->handle, "_SUN", NULL, &sun);482	if (ACPI_FAILURE(status))483		return -EIO;484 485	return sprintf(buf, "%llu\n", sun);486}487static DEVICE_ATTR_RO(sun);488 489static ssize_t490hrv_show(struct device *dev, struct device_attribute *attr,491	 char *buf)492{493	struct acpi_device *acpi_dev = to_acpi_device(dev);494	acpi_status status;495	unsigned long long hrv;496 497	status = acpi_evaluate_integer(acpi_dev->handle, "_HRV", NULL, &hrv);498	if (ACPI_FAILURE(status))499		return -EIO;500 501	return sprintf(buf, "%llu\n", hrv);502}503static DEVICE_ATTR_RO(hrv);504 505static ssize_t status_show(struct device *dev, struct device_attribute *attr,506				char *buf)507{508	struct acpi_device *acpi_dev = to_acpi_device(dev);509	acpi_status status;510	unsigned long long sta;511 512	status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta);513	if (ACPI_FAILURE(status))514		return -EIO;515 516	return sprintf(buf, "%llu\n", sta);517}518static DEVICE_ATTR_RO(status);519 520static struct attribute *acpi_attrs[] = {521	&dev_attr_path.attr,522	&dev_attr_hid.attr,523	&dev_attr_modalias.attr,524	&dev_attr_description.attr,525	&dev_attr_adr.attr,526	&dev_attr_uid.attr,527	&dev_attr_sun.attr,528	&dev_attr_hrv.attr,529	&dev_attr_status.attr,530	&dev_attr_eject.attr,531	&dev_attr_power_state.attr,532	&dev_attr_real_power_state.attr,533	NULL534};535 536static bool acpi_show_attr(struct acpi_device *dev, const struct device_attribute *attr)537{538	/*539	 * Devices gotten from FADT don't have a "path" attribute540	 */541	if (attr == &dev_attr_path)542		return dev->handle;543 544	if (attr == &dev_attr_hid || attr == &dev_attr_modalias)545		return !list_empty(&dev->pnp.ids);546 547	if (attr == &dev_attr_description)548		return acpi_has_method(dev->handle, "_STR");549 550	if (attr == &dev_attr_adr)551		return dev->pnp.type.bus_address;552 553	if (attr == &dev_attr_uid)554		return acpi_device_uid(dev);555 556	if (attr == &dev_attr_sun)557		return acpi_has_method(dev->handle, "_SUN");558 559	if (attr == &dev_attr_hrv)560		return acpi_has_method(dev->handle, "_HRV");561 562	if (attr == &dev_attr_status)563		return acpi_has_method(dev->handle, "_STA");564 565	/*566	 * If device has _EJ0, 'eject' file is created that is used to trigger567	 * hot-removal function from userland.568	 */569	if (attr == &dev_attr_eject)570		return acpi_has_method(dev->handle, "_EJ0");571 572	if (attr == &dev_attr_power_state)573		return dev->flags.power_manageable;574 575	if (attr == &dev_attr_real_power_state)576		return dev->flags.power_manageable && dev->power.flags.power_resources;577 578	dev_warn_once(&dev->dev, "Unexpected attribute: %s\n", attr->attr.name);579	return false;580}581 582static umode_t acpi_attr_is_visible(struct kobject *kobj,583				    struct attribute *attr,584				    int attrno)585{586	struct acpi_device *dev = to_acpi_device(kobj_to_dev(kobj));587 588	if (acpi_show_attr(dev, container_of(attr, struct device_attribute, attr)))589		return attr->mode;590	else591		return 0;592}593 594static const struct attribute_group acpi_group = {595	.attrs = acpi_attrs,596	.is_visible = acpi_attr_is_visible,597};598 599const struct attribute_group *acpi_groups[] = {600	&acpi_group,601	NULL602};603 604/**605 * acpi_device_setup_files - Create sysfs attributes of an ACPI device.606 * @dev: ACPI device object.607 */608void acpi_device_setup_files(struct acpi_device *dev)609{610	acpi_expose_nondev_subnodes(&dev->dev.kobj, &dev->data);611}612 613/**614 * acpi_device_remove_files - Remove sysfs attributes of an ACPI device.615 * @dev: ACPI device object.616 */617void acpi_device_remove_files(struct acpi_device *dev)618{619	acpi_hide_nondev_subnodes(&dev->data);620}621