Re: [PATCH v2] platform: x86: Add ACPI driver for ChromeOS

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, 2020-03-22 at 10:43 +0100, Enric Balletbo i Serra wrote:
> This driver attaches to the ChromeOS ACPI device and then exports the
> values
> reported by the ACPI in a sysfs directory. The ACPI values are
> presented in
> the string form (numbers as decimal values) or binary blobs, and can
> be
> accessed as the contents of the appropriate read only files in the
> sysfs
> directory tree originating in /sys/devices/platform/chromeos_acpi.
> 
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@xxxxxxxxxxxxx>
> ---
> 

[...]

>  /sys/devices/platform/chromeos_acpi/BINF.2 : 1
Just wondering why don't you create additional attributes under ACPI
device itself, like in your case:
/sys/bus/acpi/devices/GGL0001/*

Additional attributes are added for ACPI device objects at such
location in other cases also for some PNP* and INT3* objects.

This will make your driver simple with one attr group.

Thanks,
Srinivas 


>  /sys/devices/platform/chromeos_acpi/FMAP : -2031616
>  /sys/devices/platform/chromeos_acpi/HWID : SAMUS E25-G7R-W35
>  /sys/devices/platform/chromeos_acpi/BINF.0 : 0
>  /sys/devices/platform/chromeos_acpi/GPIO.0/GPIO.2 : -1
>  /sys/devices/platform/chromeos_acpi/GPIO.0/GPIO.0 : 1
>  /sys/devices/platform/chromeos_acpi/GPIO.0/GPIO.3 : INT3437:00
>  /sys/devices/platform/chromeos_acpi/GPIO.0/GPIO.1 : 0
>  /sys/devices/platform/chromeos_acpi/FRID : Google_Samus.6300.102.0
>  /sys/devices/platform/chromeos_acpi/VBNV.0 : 38
>  /sys/devices/platform/chromeos_acpi/BINF.3 : 2
>  /sys/devices/platform/chromeos_acpi/BINF.1 : 1
>  /sys/devices/platform/chromeos_acpi/GPIO.1/GPIO.2 : 16
>  /sys/devices/platform/chromeos_acpi/GPIO.1/GPIO.0 : 3
>  /sys/devices/platform/chromeos_acpi/GPIO.1/GPIO.3 : INT3437:00
>  /sys/devices/platform/chromeos_acpi/GPIO.1/GPIO.1 : 1
>  /sys/devices/platform/chromeos_acpi/CHSW : 0
>  /sys/devices/platform/chromeos_acpi/FWID : Google_Samus.6300.330.0
>  /sys/devices/platform/chromeos_acpi/VBNV.1 : 16
>  /sys/devices/platform/chromeos_acpi/BINF.4 : 0
> 
> And for binary packages:
> 
> cat /sys/devices/platform/chromeos_acpi/MECK | hexdump
>  0000000 02fb 8e72 a025 0a73 0f13 095e 9e07 41e6
>  0000010 f9e6 bb4e 76cc bef9 cca7 70e2 8f6d 863d
>  0000020
> 
> cat /sys/devices/platform/chromeos_acpi/VDAT | hexdump
>  0000000 6256 4453 0002 0000 0448 0000 0000 0000
>  0000010 0c00 0000 0000 0000 0850 0000 0000 0000
>  0000020 7c54 0003 0000 0000 0420 0000 0000 0000
>  0000030 0408 0000 0000 0000 0007 0000 0000 0000
>  0000040 0003 0000 0000 0000 0448 0000 0000 0000
>  0000050 0408 0000 0000 0000 9335 1f80 0000 0000
>  0000060 69a8 21f3 0000 0000 1d02 21f9 0000 0000
>  0000070 ba55 371b 0000 0000 0000 0000 0000 0000
>  0000080 bcae 001d 0000 0000 0003 0001 0001 0003
>  0000090 000c 0000 0003 0001 0003 0001 0001 0000
>  00000a0 0001 0000 0000 0000 cc00 01da 0000 0000
>  00000b0 0200 0000 0204 0000 0001 0000 0000 0000
>  00000c0 0800 0000 0000 0000 0000 0001 0000 0000
>  00000d0 0001 0001 1301 0000 0000 0000 0000 0000
>  00000e0 0000 0000 0000 0000 0000 0000 0000 0000
>  *
> 
> Thanks,
>  Enric
> 
> [1] https://lkml.org/lkml/2017/7/31/378
> 
> Changes in v2:
> - Note that this version is a total rework, with those major changes:
>   - Use lists to track dinamically allocated attributes and groups.
>   - Use sysfs binary attributes to store the ACPI contents.
>   - Remove all the functionalities except the one that creates the
> sysfs files.
> 
>  drivers/platform/x86/Kconfig         |  12 +
>  drivers/platform/x86/Makefile        |   1 +
>  drivers/platform/x86/chromeos_acpi.c | 489
> +++++++++++++++++++++++++++
>  3 files changed, 502 insertions(+)
>  create mode 100644 drivers/platform/x86/chromeos_acpi.c
> 
> diff --git a/drivers/platform/x86/Kconfig
> b/drivers/platform/x86/Kconfig
> index 587403c44598..917a1c1a0758 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -72,6 +72,18 @@ config ACERHDF
>  	  If you have an Acer Aspire One netbook, say Y or M
>  	  here.
>  
> +config ACPI_CHROMEOS
> +	tristate "ChromeOS specific ACPI extensions"
> +	depends on ACPI
> +	depends on CHROME_PLATFORMS
> +	help
> +	  This driver provides the firmware interface for the services
> +	  exported through the ChromeOS interfaces when using ChromeOS
> +	  ACPI firmware.
> +
> +	  If you have an ACPI-compatible Chromebook, say Y or M
> +	  here.
> +
>  config ALIENWARE_WMI
>  	tristate "Alienware Special feature control"
>  	depends on ACPI
> diff --git a/drivers/platform/x86/Makefile
> b/drivers/platform/x86/Makefile
> index 3747b1f07cf1..222e2e88ccb8 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -83,6 +83,7 @@ obj-$(CONFIG_SAMSUNG_Q10)	+= samsung-q10.o
>  obj-$(CONFIG_APPLE_GMUX)	+= apple-gmux.o
>  obj-$(CONFIG_INTEL_RST)		+= intel-rst.o
>  obj-$(CONFIG_INTEL_SMARTCONNECT)	+= intel-smartconnect.o
> +obj-$(CONFIG_ACPI_CHROMEOS)	+= chromeos_acpi.o
>  
>  obj-$(CONFIG_ALIENWARE_WMI)	+= alienware-wmi.o
>  obj-$(CONFIG_INTEL_PMC_IPC)	+= intel_pmc_ipc.o
> diff --git a/drivers/platform/x86/chromeos_acpi.c
> b/drivers/platform/x86/chromeos_acpi.c
> new file mode 100644
> index 000000000000..4d9addee2473
> --- /dev/null
> +++ b/drivers/platform/x86/chromeos_acpi.c
> @@ -0,0 +1,489 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * ChromeOS specific ACPI extensions
> + *
> + * Copyright 2011 Google, Inc.
> + * Copyright 2020 Google LLC
> + *
> + * This file is a rework and part of the code is ported from
> + * drivers/platform/x86/chromeos_acpi.c of the chromeos-3.18 kernel
> and
> + * was originally written by Vadim Bendebury <vbendeb@xxxxxxxxxxxx>.
> + *
> + * This driver attaches to the ChromeOS ACPI device and then exports
> the
> + * values reported by the ACPI in a sysfs directory. All values are
> + * presented in the string form (numbers as decimal values) and can
> be
> + * accessed as the contents of the appropriate read only files in
> the
> + * sysfs directory tree originating in
> /sys/devices/platform/chromeos_acpi.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +/*
> + * ACPI method name for MLST; the response for this method is a
> package of
> + * strings listing the methods which should be reflected in sysfs.
> + */
> +#define MLST "MLST"
> +
> +/*
> + * The default list of methods the ChromeOS ACPI device is supposed
> to export,
> + * if the MLST method is not present or is poorly formed.  The MLST
> method
> + * itself is included, to aid in debugging.
> + */
> +static char *chromeos_acpi_default_methods[] = {
> +	"CHSW", "HWID", "BINF", "GPIO", "CHNV", "FWID", "FRID", MLST
> +};
> +
> +/*
> + * Representation of a single sysfs attribute. In addition to the
> standard
> + * bin_attribute structure has a list of these structures (to keep
> track for
> + * de-allocation when removing the driver) and a pointer to the
> actual
> + * attribute name and value, reported when accessing the appropriate
> sysfs
> + * file.
> + */
> +struct chromeos_acpi_attribute {
> +	struct bin_attribute bin_attr;
> +	struct list_head list;
> +	char *name;
> +	char *data;
> +};
> +
> +/*
> + * Representation of a sysfs attribute group (a sub directory in the
> device's
> + * sysfs directory). In addition to the standard structure has lists
> to allow
> + * to keep track of the allocated structures.
> + */
> +struct chromeos_acpi_attribute_group {
> +	struct list_head attribs;
> +	struct list_head list;
> +	struct kobject *kobj;	/* chromeos_acpi/name directory */
> +	char *name;
> +};
> +
> +/*
> + * This is the main structure, we use it to store data and adds
> links pointing
> + * at lists of allocated attributes and attribute groups.
> + */
> +struct chromeos_acpi_dev {
> +	struct platform_device *pdev;
> +
> +	struct chromeos_acpi_attribute_group root;
> +	struct list_head groups;
> +};
> +
> +static struct chromeos_acpi_dev chromeos_acpi;
> +
> +static ssize_t chromeos_acpi_read_bin_attribute(struct file *filp,
> +						struct kobject *kobj,
> +						struct bin_attribute
> *bin_attr,
> +						char *buffer, loff_t
> pos,
> +						size_t count)
> +{
> +	struct chromeos_acpi_attribute *info = bin_attr->private;
> +
> +	return memory_read_from_buffer(buffer, count, &pos, info->data,
> +				       info->bin_attr.size);
> +}
> +
> +static char *chromeos_acpi_alloc_name(char *name, int count, int
> index)
> +{
> +	char *str;
> +
> +	if (count == 1)
> +		str = kstrdup(name, GFP_KERNEL);
> +	else
> +		str = kasprintf(GFP_KERNEL, "%s.%d", name, index);
> +
> +	return str;
> +}
> +
> +static int
> +chromeos_acpi_add_attr(struct chromeos_acpi_attribute_group *aag,
> +		       union acpi_object *element, char *name,
> +		       int count, int index)
> +{
> +	struct chromeos_acpi_attribute *info;
> +	char buffer[24]; /* enough to store a u64 and "\n\0" */
> +	int length;
> +	int ret;
> +
> +	info = kzalloc(sizeof(*info), GFP_KERNEL);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	info->name = chromeos_acpi_alloc_name(name, count, index);
> +	if (!info->name) {
> +		ret = -ENOMEM;
> +		goto free_attribute;
> +	}
> +
> +	sysfs_bin_attr_init(&info->bin_attr);
> +	info->bin_attr.attr.name = info->name;
> +	info->bin_attr.attr.mode = 0444;
> +
> +	switch (element->type) {
> +	case ACPI_TYPE_BUFFER:
> +		length = element->buffer.length;
> +		info->data = kmemdup(element->buffer.pointer,
> +				     length, GFP_KERNEL);
> +		break;
> +	case ACPI_TYPE_INTEGER:
> +		length = snprintf(buffer, sizeof(buffer), "%d",
> +				  (int)element->integer.value);
> +		info->data = kmemdup(buffer, length, GFP_KERNEL);
> +		break;
> +	case ACPI_TYPE_STRING:
> +		length = element->string.length + 1;
> +		info->data = kstrdup(element->string.pointer,
> GFP_KERNEL);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		goto free_attr_name;
> +	}
> +
> +	if (!info->data) {
> +		ret = -ENOMEM;
> +		goto free_attr_name;
> +	}
> +
> +	info->bin_attr.size = length;
> +	info->bin_attr.read = chromeos_acpi_read_bin_attribute;
> +	info->bin_attr.private = info;
> +
> +	INIT_LIST_HEAD(&info->list);
> +
> +	ret = sysfs_create_bin_file(aag->kobj, &info->bin_attr);
> +	if (ret)
> +		goto free_attr_data;
> +
> +	list_add(&info->list, &aag->attribs);
> +
> +	return 0;
> +
> +free_attr_data:
> +	kfree(info->data);
> +free_attr_name:
> +	kfree(info->name);
> +free_attribute:
> +	kfree(info);
> +	return ret;
> +}
> +
> +static void
> +chromeos_acpi_remove_attribs(struct chromeos_acpi_attribute_group
> *aag)
> +{
> +	struct chromeos_acpi_attribute *attr, *tmp_attr;
> +
> +	list_for_each_entry_safe(attr, tmp_attr, &aag->attribs, list) {
> +		sysfs_remove_bin_file(aag->kobj, &attr->bin_attr);
> +		kfree(attr->name);
> +		kfree(attr->data);
> +		kfree(attr);
> +	}
> +}
> +
> +/**
> + * chromeos_acpi_add_group() - Create a sysfs group including
> attributes
> + *			       representing a nested ACPI package.
> + *
> + * @obj: Package contents as returned by ACPI.
> + * @name: Name of the group.
> + * @num_attrs: Number of attributes of this package.
> + * @index: Index number of this particular group.
> + *
> + * The created group is called @name in case there is a single
> instance, or
> + * @name.@index otherwise.
> + *
> + * All group and attribute storage allocations are included in the
> lists for
> + * tracking of allocated memory.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +static int chromeos_acpi_add_group(union acpi_object *obj, char
> *name,
> +				   int num_attrs, int index)
> +{
> +	struct device *dev = &chromeos_acpi.pdev->dev;
> +	struct chromeos_acpi_attribute_group *aag;
> +	union acpi_object *element;
> +	int i, count, ret;
> +
> +	aag = kzalloc(sizeof(*aag), GFP_KERNEL);
> +	if (!aag)
> +		return -ENOMEM;
> +
> +	aag->name = chromeos_acpi_alloc_name(name, num_attrs, index);
> +	if (!aag->name) {
> +		ret = -ENOMEM;
> +		goto free_group;
> +	}
> +
> +	aag->kobj = kobject_create_and_add(aag->name, &dev->kobj);
> +	if (!aag->kobj) {
> +		ret = -EINVAL;
> +		goto free_group_name;
> +	}
> +
> +	INIT_LIST_HEAD(&aag->attribs);
> +	INIT_LIST_HEAD(&aag->list);
> +
> +	count = obj->package.count;
> +	element = obj->package.elements;
> +	for (i = 0; i < count; i++, element++) {
> +		ret = chromeos_acpi_add_attr(aag, element, name, count,
> i);
> +		if (ret)
> +			goto free_group_attr;
> +	}
> +
> +	list_add(&aag->list, &chromeos_acpi.groups);
> +
> +	return 0;
> +
> +free_group_attr:
> +	chromeos_acpi_remove_attribs(aag);
> +	kobject_put(aag->kobj);
> +free_group_name:
> +	kfree(aag->name);
> +free_group:
> +	kfree(aag);
> +	return ret;
> +}
> +
> +static void chromeos_acpi_remove_groups(void)
> +{
> +	struct chromeos_acpi_attribute_group *aag, *tmp_aag;
> +
> +	list_for_each_entry_safe(aag, tmp_aag, &chromeos_acpi.groups,
> list) {
> +		chromeos_acpi_remove_attribs(aag);
> +		kfree(aag->name);
> +		kobject_put(aag->kobj);
> +		kfree(aag);
> +	}
> +}
> +
> +/**
> + * chromeos_acpi_handle_package() - Create sysfs group including
> attributes
> + *				    representing an ACPI package.
> + *
> + * @obj: Package contents as returned by ACPI.
> + * @name: Name of the group.
> + *
> + * Scalar objects included in the package get sysfs attributes
> created for
> + * them. Nested packages are passed to a function creating a sysfs
> group per
> + * package.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +static int chromeos_acpi_handle_package(union acpi_object *obj, char
> *name)
> +{
> +	struct device *dev = &chromeos_acpi.pdev->dev;
> +	int count = obj->package.count;
> +	union acpi_object *element;
> +	int i, ret = 0;
> +
> +	element = obj->package.elements;
> +	for (i = 0; i < count; i++, element++) {
> +		if (element->type == ACPI_TYPE_BUFFER ||
> +		    element->type == ACPI_TYPE_STRING ||
> +		    element->type == ACPI_TYPE_INTEGER)
> +			/* Create a single attribute in the root
> directory */
> +			ret =
> chromeos_acpi_add_attr(&chromeos_acpi.root,
> +						     element, name,
> +						     count, i);
> +		else if (element->type == ACPI_TYPE_PACKAGE)
> +			/* Create a group of attributes */
> +			ret = chromeos_acpi_add_group(element, name,
> +						      count, i);
> +		else
> +			ret = -EINVAL;
> +		if (ret)
> +			dev_err(dev,
> +				"failed to create group attributes
> (%d)\n",
> +				ret);
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * chromeos_acpi_add_method() - Evaluate an ACPI method and create
> sysfs
> + *				attributes.
> + *
> + * @adev: ACPI device
> + * @name: Name of the method to evaluate
> + *
> + * Return: 0 on success, non-zero on failure
> + */
> +static int chromeos_acpi_add_method(struct acpi_device *adev, char
> *name)
> +{
> +	struct device *dev = &chromeos_acpi.pdev->dev;
> +	struct acpi_buffer output;
> +	union acpi_object *obj;
> +	acpi_status status;
> +	int ret = 0;
> +
> +	output.length = ACPI_ALLOCATE_BUFFER;
> +
> +	status = acpi_evaluate_object(adev->handle, name, NULL,
> &output);
> +	if (ACPI_FAILURE(status)) {
> +		dev_err(dev, "failed to retrieve %s (%d)\n", name,
> status);
> +		return status;
> +	}
> +
> +	obj = output.pointer;
> +	if (obj->type == ACPI_TYPE_PACKAGE)
> +		ret = chromeos_acpi_handle_package(obj, name);
> +
> +	kfree(output.pointer);
> +
> +	return ret;
> +}
> +
> +/**
> + * chromeos_acpi_process_mlst() - Evaluate the MLST method and add
> methods
> + *				  listed in the response.
> + *
> + * @adev: ACPI device
> + *
> + * Returns: 0 if successful, non-zero if error.
> + */
> +static int chromeos_acpi_process_mlst(struct acpi_device *adev)
> +{
> +	char name[ACPI_NAMESEG_SIZE + 1];
> +	union acpi_object *element, *obj;
> +	struct acpi_buffer output;
> +	acpi_status status;
> +	int ret = 0;
> +	int size;
> +	int i;
> +
> +	output.length = ACPI_ALLOCATE_BUFFER;
> +	status = acpi_evaluate_object(adev->handle, MLST, NULL,
> +				      &output);
> +	if (ACPI_FAILURE(status))
> +		return status;
> +
> +	obj = output.pointer;
> +	if (obj->type != ACPI_TYPE_PACKAGE) {
> +		ret = -EINVAL;
> +		goto free_acpi_buffer;
> +	}
> +
> +	element = obj->package.elements;
> +	for (i = 0; i < obj->package.count; i++, element++) {
> +		if (element->type == ACPI_TYPE_STRING) {
> +			size = min(element->string.length + 1,
> +				   (u32)ACPI_NAMESEG_SIZE + 1);
> +			strlcpy(name, element->string.pointer, size);
> +			ret = chromeos_acpi_add_method(adev, name);
> +			if (ret) {
> +				chromeos_acpi_remove_groups();
> +				break;
> +			}
> +		}
> +	}
> +
> +free_acpi_buffer:
> +	kfree(output.pointer);
> +
> +	return ret;
> +}
> +
> +static int chromeos_acpi_device_add(struct acpi_device *adev)
> +{
> +	struct chromeos_acpi_attribute_group *aag =
> &chromeos_acpi.root;
> +	struct device *dev = &chromeos_acpi.pdev->dev;
> +	int i, ret;
> +
> +	INIT_LIST_HEAD(&aag->attribs);
> +	INIT_LIST_HEAD(&aag->list);
> +
> +	aag->kobj = &dev->kobj;
> +
> +	/*
> +	 * Attempt to add methods by querying the device's MLST method
> +	 * for the list of methods.
> +	 */
> +	if (!chromeos_acpi_process_mlst(adev))
> +		return 0;
> +
> +	dev_info(dev, "falling back to default list of methods\n");
> +
> +	for (i = 0; i < ARRAY_SIZE(chromeos_acpi_default_methods); i++)
> {
> +		ret = chromeos_acpi_add_method(adev,
> +					     chromeos_acpi_default_meth
> ods[i]);
> +		if (ret) {
> +			dev_err(dev, "failed to add default methods
> (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int chromeos_acpi_device_remove(struct acpi_device *adev)
> +{
> +	/* Remove dinamically allocated sysfs groups and attributes */
> +	chromeos_acpi_remove_groups();
> +	/* Remove attributes from the root group */
> +	chromeos_acpi_remove_attribs(&chromeos_acpi.root);
> +
> +	return 0;
> +}
> +
> +static const struct acpi_device_id chromeos_device_ids[] = {
> +	{ "GGL0001", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(acpi, chromeos_device_ids);
> +
> +static struct acpi_driver chromeos_acpi_driver = {
> +	.name = "ChromeOS ACPI driver",
> +	.class = "chromeos-acpi",
> +	.ids = chromeos_device_ids,
> +	.ops = {
> +		.add = chromeos_acpi_device_add,
> +		.remove = chromeos_acpi_device_remove,
> +	},
> +	.owner = THIS_MODULE,
> +};
> +
> +static int __init chromeos_acpi_init(void)
> +{
> +	int ret;
> +
> +	chromeos_acpi.pdev =
> platform_device_register_simple("chromeos_acpi",
> +						PLATFORM_DEVID_NONE,
> NULL, 0);
> +	if (IS_ERR(chromeos_acpi.pdev)) {
> +		pr_err("unable to register chromeos_acpi platform
> device\n");
> +		return PTR_ERR(chromeos_acpi.pdev);
> +	}
> +
> +	INIT_LIST_HEAD(&chromeos_acpi.groups);
> +
> +	ret = acpi_bus_register_driver(&chromeos_acpi_driver);
> +	if (ret < 0) {
> +		pr_err("failed to register chromeos_acpi driver
> (%d)\n", ret);
> +		platform_device_unregister(chromeos_acpi.pdev);
> +		chromeos_acpi.pdev = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void __exit chromeos_acpi_exit(void)
> +{
> +	acpi_bus_unregister_driver(&chromeos_acpi_driver);
> +	platform_device_unregister(chromeos_acpi.pdev);
> +}
> +
> +module_init(chromeos_acpi_init);
> +module_exit(chromeos_acpi_exit);
> +
> +MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@xxxxxxxxxxxxx>
> ");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("ChromeOS specific ACPI extensions");




[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux