Re: [PATCH] ACPI: Add GPIO-signaled event emulator.

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

 



On Wed, Aug 20, 2014 at 04:58:20PM +0200, Tomasz Nowicki wrote:
> GPIO-signaled events is quite new thing in Linux kernel.
> There are not many board which can take advantage of it.
> However, GPIO events are very useful feature during work on ACPI
> subsystems.
> 
> This commit emulates GPIO h/w behaviour and consists on write
> operation to debugfs file. GPIO device instance is still required in DSDT
> table along with _AEI resources and event methods.
> 
> Please, see Kconfig help and driver head section for more details
> regarding tool usage.
> 
> Signed-off-by: Tomasz Nowicki <tomasz.nowicki@xxxxxxxxxx>
> ---
>  drivers/acpi/Kconfig             |  10 ++
>  drivers/acpi/Makefile            |   1 +
>  drivers/acpi/gpio-acpi-evt-emu.c | 195 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 206 insertions(+)
>  create mode 100644 drivers/acpi/gpio-acpi-evt-emu.c
> 
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index fd54a74..8b9b74d 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -122,6 +122,16 @@ config ACPI_BUTTON
>  	  To compile this driver as a module, choose M here:
>  	  the module will be called button.
>  
> +config ACPI_GPIO_EVT_EMULATE
> +        bool "ACPI GPIO-signaled Events Emulation Support"

Is there anything preventing building this as a module?

> +        depends on DEBUG_FS

Spaces -> Tab

> +	default n

n is the default already, no need to specify it here.

> +	help
> +	  This will enable your system to emulate GPIO-signaled event through
> +	  proc file system. User needs to trigger event method by
> +	  echo 1 >  /sys/kernel/debug/acpi/events/<GPIO DEVICE>/<PIN>
> +	  (where, GPIO DEVICE is a GPIO device name and PIN is a pin number).

We should probably mention that this is dangerous and should be used for
debugging purposes only.

> +
>  config ACPI_VIDEO
>  	tristate "Video"
>  	depends on X86 && BACKLIGHT_CLASS_DEVICE
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 9fa20ff..24f9d8f 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -55,6 +55,7 @@ acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
>  ifdef CONFIG_ACPI_VIDEO
>  acpi-y				+= video_detect.o
>  endif
> +acpi-$(CONFIG_ACPI_GPIO_EVT_EMULATE)	+= gpio-acpi-evt-emu.o
>  
>  # These are (potentially) separate modules
>  
> diff --git a/drivers/acpi/gpio-acpi-evt-emu.c b/drivers/acpi/gpio-acpi-evt-emu.c
> new file mode 100644
> index 0000000..c39f501
> --- /dev/null
> +++ b/drivers/acpi/gpio-acpi-evt-emu.c
> @@ -0,0 +1,195 @@
> +/*
> + * Code to emulate GPIO-signaled events.
> + *
> + * The sole purpose of this module is to help with GPIO event triggering.
> + * Usage:
> + * 1. See the list of available GPIO devices and associated pins under:
> + *    /sys/kernel/debug/acpi/events/<GPIO DEVICE>/<PIN>. Only pins which are to
> + *    be used as GPIO-signaled events will be listed (_AEI resources).
> + *
> + * 2. Trigger method corresponding to device pin number:
> + *    $ echo 1 > /sys/kernel/debug/acpi/events/<GPIO DEVICE>/<PIN>
> + */
> +
> +/*
> + * Copyright (C) 2014, Linaro Ltd.
> + * Author: Tomasz Nowicki <tomasz.nowicki@xxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

You don't need to specify the FSF address here.

> + */
> +
> +#include <linux/module.h>
> +#include <linux/uaccess.h>
> +#include <linux/debugfs.h>
> +#include <linux/acpi.h>
> +
> +#include "acpica/accommon.h"
> +#include "acpica/acnamesp.h"

Are these actually needed?

> +
> +#include "internal.h"
> +
> +#define _COMPONENT		ACPI_SYSTEM_COMPONENT
> +ACPI_MODULE_NAME("gpio_acpi_evt_emu");
> +
> +struct gpio_pin_parent_data {
> +	acpi_handle handle;
> +	struct dentry *debugfs_dir;
> +	char *name;
> +	unsigned int evt_count;
> +};
> +
> +struct gpio_pin_data {
> +	struct list_head list;
> +	acpi_handle handle;
> +	unsigned int pin;
> +};
> +
> +static struct dentry *acpi_evt_debugfs_dir;
> +static LIST_HEAD(pin_data_list);
> +
> +static int gpio_evt_trigger(void *data, u64 val)
> +{
> +	struct gpio_pin_data *pin_data = (struct gpio_pin_data *)data;
> +	int pin = pin_data->pin;
> +
> +	if (ACPI_FAILURE(acpi_execute_simple_method(pin_data->handle, NULL,
> +						    pin <= 255 ? 0 : pin)))
> +		pr_err(PREFIX "evaluating event method failed\n");

acpi_execute_simple_method() passes one argument to the method. You
can't use it with _Lxx or _Exx which don't expect any arguments.
Otherwise you get this:

[  122.258191] ACPI: \_SB_.GPO2._E12: Excess arguments - Caller passed 1, method requires 0 (20140724/nsarguments-263)

Also where does "PREFIX" come from?

> +
> +	return 0;
> +}
> +
> +DEFINE_SIMPLE_ATTRIBUTE(gpio_evt_emu_fops, NULL, gpio_evt_trigger, "%llu\n");
> +
> +static acpi_status gpio_list_resource(struct acpi_resource *ares, void *context)
> +{
> +	struct acpi_resource_gpio *agpio = &ares->data.gpio;
> +	struct gpio_pin_parent_data *parent_data = context;
> +	struct dentry *pin_debugfs_dir;
> +	struct gpio_pin_data *pin_data;
> +	acpi_handle evt_handle;
> +	acpi_status status;
> +	char str_pin[5];
> +	char ev_name[5];
> +	int pin;
> +
> +	if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
> +		return AE_OK;
> +
> +	if (agpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT)
> +		return AE_OK;
> +
> +	pin_data = kmalloc(sizeof(*pin_data), GFP_KERNEL);
> +	if (!pin_data)
> +		return AE_NO_MEMORY;
> +
> +	pin = agpio->pin_table[0];
> +	snprintf(str_pin, 5, "%d", pin);
> +	pin_debugfs_dir = debugfs_create_file(str_pin, S_IWUSR,
> +					      parent_data->debugfs_dir,
> +					      pin_data,
> +					      &gpio_evt_emu_fops);
> +	if (!pin_debugfs_dir) {
> +		status = AE_NULL_ENTRY;
> +		goto fail;
> +	}
> +
> +	if (pin <= 255)
> +		sprintf(ev_name, "_%c%02X",
> +			agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L',
> +			pin);
> +	else
> +		sprintf(ev_name, "_EVT");
> +
> +	status = acpi_get_handle(parent_data->handle, ev_name, &evt_handle);
> +	if (ACPI_FAILURE(status)) {
> +		pr_err(PREFIX "getting handle to <%s> of <%s> failed, there is no method related to 0x%02X pin\n",
> +		       ev_name, parent_data->name, pin);
> +		goto fail;
> +	}
> +
> +	pin_data->handle = evt_handle;
> +	pin_data->pin = pin;
> +        list_add_tail(&pin_data->list, &pin_data_list);

Spaces -> tab

> +
> +	parent_data->evt_count++;
> +
> +	return AE_OK;
> +fail:
> +	kfree(pin_data);
> +	return status;
> +}
> +
> +static acpi_status gpio_find_resource(acpi_handle handle, u32 lvl,
> +					  void *context, void **rv)
> +{
> +	struct acpi_namespace_node *node;
> +	struct dentry *gpio_debugfs_dir;
> +	struct gpio_pin_parent_data parent_data;
> +	char gpio_name[5];
> +
> +	node = acpi_ns_validate_handle(handle);

Hmm, why is this needed? Is uppose acpi_get_devices() or already makes
sure you have a valid handle, no?

> +	if (!node) {
> +		pr_err(PREFIX "Mapping GPIO handle to node failed\n");
> +		return AE_BAD_PARAMETER;
> +	}
> +
> +	snprintf(gpio_name, 5, "%s", node->name.ascii);
> +	gpio_debugfs_dir = debugfs_create_dir(gpio_name, acpi_evt_debugfs_dir);
> +	if (gpio_debugfs_dir == NULL)
> +		return AE_OK;
> +
> +	parent_data.debugfs_dir = gpio_debugfs_dir;
> +	parent_data.handle = handle;
> +	parent_data.name = gpio_name;
> +	parent_data.evt_count = 0;
> +
> +	acpi_walk_resources(handle, METHOD_NAME__AEI, gpio_list_resource,
> +			    &parent_data);
> +
> +	if (!parent_data.evt_count)
> +		debugfs_remove(gpio_debugfs_dir);
> +
> +	return AE_OK;
> +}
> +
> +static int __init gpio_evt_emu_init(void)
> +{
> +	if (acpi_debugfs_dir == NULL)
> +		return -ENOENT;
> +
> +	acpi_evt_debugfs_dir = debugfs_create_dir("events", acpi_debugfs_dir);
> +	if (acpi_evt_debugfs_dir == NULL)
> +		return -ENOENT;
> +
> +	acpi_get_devices(NULL, gpio_find_resource, NULL, NULL);
> +
> +	return 0;
> +}
> +
> +static void __exit gpio_evt_emu_deinit(void)

call it gpio_evt_emu_exit() instead.

> +{
> +	struct gpio_pin_data *pin_data, *temp;
> +
> +	list_for_each_entry_safe(pin_data, temp, &pin_data_list, list)
> +		kfree(pin_data);

I suppose you want to first remove the directory entries and then the
pin data. Otherwise if you get pre-empted at this point and someone
tries to use your debugfs files, bad things might happen.

> +
> +	debugfs_remove_recursive(acpi_evt_debugfs_dir);

Since this already removes everything below this dentry, why do you need
to store the pointer in gpio_pin_parent_data?

> +}
> +
> +module_init(gpio_evt_emu_init);
> +module_exit(gpio_evt_emu_deinit);

These should follow their respective functions. E.g

static int __init gpio_evt_emu_init(void)
{
	...
}
module_init(gpio_evt_emu_init);

> +
> +MODULE_DESCRIPTION("GPIO-signaled events emulator");
> +MODULE_LICENSE("GPL");

GPL v2
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux