Re: [PATCH] ACPI: Platform driver to support App Hot Startup (PNP0C32)

Ângelo Miguel Arrifano
2008-03-27T15:50:45Z

On Thu, 27 Mar 2008 10:30:13 +0800
Shaohua Li <shaohua.li@intel.com> wrote:

>
> On Thu, 2008-03-27 at 01:51 +0800, Ângelo Miguel Arrifano wrote:
> > PATCH
> > - -----------------------------------------------------------------
> > diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> > index f688c21..fb096ee 100644
> > - --- a/drivers/acpi/Kconfig
> > +++ b/drivers/acpi/Kconfig
> > @@ -196,6 +196,14 @@ config ACPI_THERMAL
> > recommended that this option be enabled, as your
> > processor(s)
> > may be damaged without it.
> >
> > +config ACPI_QUICKSTART
> > + tristate "Quickstart"
> > + default y
> default m?
>
>
> > +
> > +static struct quickstart_driver_data {
> > + struct quickstart_btn *btn_lst;
> > + struct quickstart_btn *pressed;
> > +} quickstart_data = {
> > + .btn_lst = NULL,
> > + .pressed = NULL,
> > +};
> They are NULL, you don't need initialize them.
>
>
> > + * Platform driver structs
> > + */
> > +static ssize_t buttons_show(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf);
> > +static ssize_t pressed_button_show(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf);
> > +static ssize_t pressed_button_store(struct device *dev,
> > + struct device_attribute *attr,
> > + const char *buf,
> > + size_t count);
> > +static DEVICE_ATTR(pressed_button, 0666, pressed_button_show,
> > + pressed_button_store);
> this file can be changed by non-root?

The only thing that writing to this file does is clearing the
pressed button.
Is it a bad idea that can be changed by non-root?

>
>
> > +static ssize_t buttons_show(struct device *dev,
> > + struct device_attribute
> > *attr,
> > + char *buf)
> > +{
> > + int count = 0;
> > + char tmpbuf[QUICKSTART_MAX_BTN_NAME_LEN + 2];
> > + struct quickstart_btn *ptr = quickstart_data.btn_lst;
> > +
> > + if (!ptr)
> > + return snprintf(buf, PAGE_SIZE, "none");
> > +
> > + while (ptr && (count < PAGE_SIZE)) {
> > + if (ptr->name) {
> > + strncpy(tmpbuf, ptr->name,
> > QUICKSTART_MAX_BTN_NAME_LEN);
> > + strcat(tmpbuf, "\n");
> > + count += snprintf(buf + count,
> > + PAGE_SIZE - count,
> > + tmpbuf);
> we could simplify:
> snprintf(buf + count, PAGE_SIZE -count, "%s\n", ptr->name);

:)

> > + }
> > + ptr = ptr->next;
> > + }
> > +
> > + return count;
> > +}
>
> > +
>
> > +
> > +/* ACPI Driver functions */
> > +static void quickstart_acpi_notify(acpi_handle handle, u32 event,
> > void *data)
> > +{
> > + struct quickstart_acpi *quickstart = data;
> > +
> > + if (!quickstart)
> > + return;
> > +
> > + if (event == QUICKSTART_EVENT_WAKE) {
> > + quickstart_data.pressed = quickstart->btn;
> > + printk(KERN_ERR "quickstart: Quickbutton %s
> > pressed.\n",
> > +
> > acpi_device_bid(quickstart->device));
> > + } else if (event == QUICKSTART_EVENT_RUNTIME) {
> > + printk(KERN_ERR "quickstart: Runtime button %s
> > pressed.\n\t"
> > + "please report on linux-acpi@vger.kernel.org",
> > +
> > acpi_device_bid(quickstart->device));
> > + }
> Please remove above printks. they are misleading, which isn't an error.
> please send an ACPI event to userspace in the notify, we need it for
> runtime.

According to the Microsoft paper, runtime hot button reporting is optional.
My laptop doesn't report them and on the SF.net project page there was no
reports of people with hardware supporting this.
Sorry, I don't have a way to test this..

>
> > + return;
> > +}
> > +
> > +static void quickstart_acpi_raise_notify(struct quickstart_acpi
> > *quickstart)
> > +{
> > + acpi_status status;
> > + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> > +
> > + if (!quickstart)
> > + return;
> > +
> > + /* This returns a buffer telling the button usage ID
> > (useless),
> > + * we dont care about it now. The important is to trigger
> > + * pending notify events (The ones before booting). */
> > + status = acpi_evaluate_object(quickstart->device->handle,
> > + "GHID", NULL, &buffer);
> > + if (ACPI_FAILURE(status) || !buffer.pointer) {
> > + printk(KERN_ERR "quickstart: %s GHID method
> > failed.\n",
> > + quickstart->btn->name);
> > + return;
> > + }
> I'd suggest the GHID info should be exported to sysfs too. It's hard to
> judge what a button is just per its name. You know it but an
> inexperienced user doesn't.

I also would like to have a more descriptive information about the button but
the returned buffer is a BYTE/WORD/DWORD with a decimal number telling the button
usage ID.

Looking at my DSDT piece of code:

Method (GHID, 0, NotSerialized)
{
If (LEqual (HOTB, 0x05))
{
Notify (DBTN, 0x02)
Store (0x00, HOTB)
}

Return (Buffer (0x01)
{
/* 0000 */ 0x04
})
}

IMHO, a value like 0x04 for button usageID is totally useless..

>
> > + kfree(buffer.pointer);
> > + return;
> > +}
> > +
>
> > +
> > +static int quickstart_acpi_add(struct acpi_device *device)
> > +{
> > + int ret = 0;
> > + acpi_status status = AE_OK;
> > + struct quickstart_acpi *quickstart = NULL;
> > +
> > + if (!device)
> > + return -EINVAL;
> > +
> > + quickstart = kzalloc(sizeof(struct quickstart_acpi),
> > GFP_KERNEL);
> > + if (!quickstart)
> > + return -ENOMEM;
> > +
> > + quickstart->device = device;
> > + strcpy(acpi_device_name(device), QUICKSTART_ACPI_DEVICE_NAME);
> > + strcpy(acpi_device_class(device), QUICKSTART_ACPI_CLASS);
> > + acpi_driver_data(device) = quickstart;
> > +
> > + /* Add button to list and initialize some stuff */
> > + ret = quickstart_acpi_config(quickstart,
> > acpi_device_bid(device));
> > + if (ret)
> > + goto fail_config;
> > +
> > + status = acpi_install_notify_handler(device->handle,
> > + ACPI_ALL_NOTIFY,
> > +
> > quickstart_acpi_notify,
> > + quickstart);
> > + if (ACPI_FAILURE(status)) {
> > + printk(KERN_ERR "quickstart: Notify handler install
> > error\n");
> > + ret = -ENODEV;
> > + goto fail_installnotify;
> memory is freed but it's still in the list.

I got it now, big mistake here.
I hope to have this fixed on the patch below

>
> Thanks,
> Shaohua
>

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index f688c21..fd65e36 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -196,6 +196,14 @@ config ACPI_THERMAL
recommended that this option be enabled, as your processor(s)
may be damaged without it.

+config ACPI_QUICKSTART
+ tristate "Quickstart"
+ default m
+ help
+ This driver add support for ACPI Direct Application Launch
+ also known as Hotstart(TM). Say yes here to have a entry in
+ sysfs telling which button was used to turn on the system.
+
config ACPI_NUMA
bool "NUMA support"
depends on NUMA
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index f29812a..badb0a6 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -62,3 +62,4 @@ obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
obj-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
obj-$(CONFIG_ACPI_SBS) += sbs.o
obj-$(CONFIG_ACPI_SBS) += sbshc.o
+obj-$(CONFIG_ACPI_QUICKSTART) += quickstart.o
diff --git a/drivers/acpi/quickstart.c b/drivers/acpi/quickstart.c
new file mode 100644
index 0000000..b8ebb93
--- /dev/null
+++ b/drivers/acpi/quickstart.c
@@ -0,0 +1,427 @@
+/*
+ * quickstart.c - ACPI Direct App Launch driver
+ *
+ *
+ * Copyright (C) 2007 Angelo Arrifano <miknix@gmail.com>
+ *
+ * Information gathered from disassebled dsdt and from here:
+ * "http://download.microsoft.com/download/9/c/5/
+ * 9c5b2167-8017-4bae-9fde-d599bac8184a/DirAppLaunch_Vista.doc"
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ *
+ */
+
+#define QUICKSTART_VERSION "1.01"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/platform_device.h>
+
+MODULE_AUTHOR("Angelo Arrifano");
+MODULE_DESCRIPTION("ACPI Direct App Launch driver");
+MODULE_LICENSE("GPL");
+
+#define QUICKSTART_ACPI_DEVICE_NAME "quickstart"
+#define QUICKSTART_ACPI_CLASS "quickstart"
+#define QUICKSTART_ACPI_HID "PNP0C32"
+
+#define QUICKSTART_PF_DRIVER_NAME "quickstart"
+#define QUICKSTART_PF_DEVICE_NAME "quickstart"
+#define QUICKSTART_PF_DEVATTR_NAME "pressed_button"
+
+#define QUICKSTART_MAX_BTN_NAME_LEN 16
+
+/* There will be two events:
+ * 0x02 - A hot button was pressed while device was off/sleeping.
+ * 0x08 - A hot button was pressed while device was up. */
+#define QUICKSTART_EVENT_WAKE 0x02
+#define QUICKSTART_EVENT_RUNTIME 0x80
+
+struct quickstart_btn {
+ char *name;
+ struct quickstart_btn *next;
+};
+
+static struct quickstart_driver_data {
+ struct quickstart_btn *btn_lst;
+ struct quickstart_btn *pressed;
+} quickstart_data;
+
+/*
+ * ACPI driver Structs
+ */
+
+struct quickstart_acpi {
+ struct acpi_device *device;
+ struct quickstart_btn *btn;
+};
+static int quickstart_acpi_add(struct acpi_device *device);
+static int quickstart_acpi_remove(struct acpi_device *device, int type);
+static const struct acpi_device_id quickstart_device_ids[] = {
+ {QUICKSTART_ACPI_HID, 0},
+ {"", 0},
+};
+
+static struct acpi_driver quickstart_acpi_driver = {
+ .name = "quickstart",
+ .class = QUICKSTART_ACPI_CLASS,
+ .ids = quickstart_device_ids,
+ .ops = {
+ .add = quickstart_acpi_add,
+ .remove = quickstart_acpi_remove,
+ },
+};
+
+/*
+ * Platform driver structs
+ */
+static ssize_t buttons_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t pressed_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t pressed_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count);
+static DEVICE_ATTR(pressed_button, 0666, pressed_button_show,
+ pressed_button_store);
+static DEVICE_ATTR(buttons, 0444, buttons_show, NULL);
+static struct platform_device *pf_device;
+static struct platform_driver pf_driver = {
+ .driver = {
+ .name = QUICKSTART_PF_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+/*
+ * Platform driver functions
+ */
+static ssize_t buttons_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int count = 0;
+ struct quickstart_btn *ptr = quickstart_data.btn_lst;
+
+ if (!ptr)
+ return snprintf(buf, PAGE_SIZE, "none");
+
+ while (ptr && (count < PAGE_SIZE)) {
+ if (ptr->name) {
+ count += snprintf(buf + count,
+ PAGE_SIZE - count,
+ "%s\n", ptr->name);
+ }
+ ptr = ptr->next;
+ }
+
+ return count;
+}
+
+static ssize_t pressed_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ (quickstart_data.pressed?quickstart_data.pressed->name:"none"));
+}
+
+
+static ssize_t pressed_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ if (count < 2)
+ return -EINVAL;
+
+ if (strncasecmp(buf, "none", 4) != 0)
+ return -EINVAL;
+
+ quickstart_data.pressed = NULL;
+ return count;
+}
+
+/* Hotstart Helper functions */
+static int quickstart_btnlst_add(struct quickstart_btn **data)
+{
+ struct quickstart_btn **ptr = &quickstart_data.btn_lst;
+
+ while (*ptr)
+ ptr = &((*ptr)->next);
+
+ *ptr = kzalloc(sizeof(struct quickstart_btn), GFP_KERNEL);
+ if (!*ptr) {
+ *data = NULL;
+ return(-ENOMEM);
+ }
+ *data = *ptr;
+
+ return 0;
+}
+
+static void quickstart_btnlst_del(struct quickstart_btn *data)
+{
+ struct quickstart_btn **ptr = &quickstart_data.btn_lst;
+
+ if (!data)
+ return;
+
+ while (*ptr) {
+ if (*ptr == data) {
+ *ptr = (*ptr)->next;
+ kfree(data);
+ return;
+ }
+ ptr = &((*ptr)->next);
+ }
+
+ return;
+}
+
+static void quickstart_btnlst_free(void)
+{
+ struct quickstart_btn *ptr = quickstart_data.btn_lst;
+ struct quickstart_btn *lptr = NULL;
+
+ while (ptr) {
+ lptr = ptr;
+ ptr = ptr->next;
+ kfree(lptr->name);
+ kfree(lptr);
+ }
+
+ return;
+}
+
+/* ACPI Driver functions */
+static void quickstart_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct quickstart_acpi *quickstart = data;
+
+ if (!quickstart)
+ return;
+
+ if (event == QUICKSTART_EVENT_WAKE)
+ quickstart_data.pressed = quickstart->btn;
+
+ return;
+}
+
+static void quickstart_acpi_raise_notify(struct quickstart_acpi *quickstart)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ if (!quickstart)
+ return;
+
+ /* This returns a buffer telling the button usage ID (useless),
+ * we dont care about it now. The important is to trigger
+ * pending notify events (The ones before booting). */
+ status = acpi_evaluate_object(quickstart->device->handle,
+ "GHID", NULL, &buffer);
+ if (ACPI_FAILURE(status) || !buffer.pointer) {
+ printk(KERN_ERR "quickstart: %s GHID method failed.\n",
+ quickstart->btn->name);
+ return;
+ }
+
+ kfree(buffer.pointer);
+ return;
+}
+
+static int quickstart_acpi_config(struct quickstart_acpi *quickstart, char *bid)
+{
+ int len = strlen(bid);
+ int ret;
+
+ /* Add button to list */
+ ret = quickstart_btnlst_add(&quickstart->btn);
+ if (ret)
+ return ret;
+
+ quickstart->btn->name = kzalloc(len + 1, GFP_KERNEL);
+ if (!quickstart->btn->name) {
+ quickstart_btnlst_free();
+ return -ENOMEM;
+ }
+ strcpy(quickstart->btn->name, bid);
+
+ return 0;
+}
+
+static int quickstart_acpi_add(struct acpi_device *device)
+{
+ int ret = 0;
+ acpi_status status = AE_OK;
+ struct quickstart_acpi *quickstart = NULL;
+
+ if (!device)
+ return -EINVAL;
+
+ quickstart = kzalloc(sizeof(struct quickstart_acpi), GFP_KERNEL);
+ if (!quickstart)
+ return -ENOMEM;
+
+ quickstart->device = device;
+ strcpy(acpi_device_name(device), QUICKSTART_ACPI_DEVICE_NAME);
+ strcpy(acpi_device_class(device), QUICKSTART_ACPI_CLASS);
+ acpi_driver_data(device) = quickstart;
+
+ /* Add button to list and initialize some stuff */
+ ret = quickstart_acpi_config(quickstart, acpi_device_bid(device));
+ if (ret)
+ goto fail_config;
+
+ status = acpi_install_notify_handler(device->handle,
+ ACPI_ALL_NOTIFY,
+ quickstart_acpi_notify,
+ quickstart);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "quickstart: Notify handler install error\n");
+ ret = -ENODEV;
+ goto fail_installnotify;
+ }
+
+ quickstart_acpi_raise_notify(quickstart);
+
+ return 0;
+
+fail_installnotify:
+ quickstart_btnlst_del(quickstart->btn);
+
+fail_config:
+
+ kfree(quickstart);
+
+ return ret;
+}
+
+static int quickstart_acpi_remove(struct acpi_device *device, int type)
+{
+ acpi_status status = 0;
+ struct quickstart_acpi *quickstart = NULL;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+
+ quickstart = acpi_driver_data(device);
+
+ status = acpi_remove_notify_handler(device->handle,
+ ACPI_ALL_NOTIFY,
+ quickstart_acpi_notify);
+ if (ACPI_FAILURE(status))
+ printk(KERN_ERR "quickstart: Error removing notify handler\n");
+
+
+ kfree(quickstart);
+
+ return 0;
+}
+
+/* Module functions */
+
+static void quickstart_exit(void)
+{
+ device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
+ device_remove_file(&pf_device->dev, &dev_attr_buttons);
+
+ platform_device_unregister(pf_device);
+
+ platform_driver_unregister(&pf_driver);
+
+ acpi_bus_unregister_driver(&quickstart_acpi_driver);
+
+ quickstart_btnlst_free();
+
+ return;
+}
+
+static int __init quickstart_init(void)
+{
+ int ret;
+ acpi_status status = 0;
+
+ /* ACPI Check */
+ if (acpi_disabled)
+ return -ENODEV;
+
+ /* ACPI driver register */
+ status = acpi_bus_register_driver(&quickstart_acpi_driver);
+ if (status < 0)
+ return -ENODEV;
+
+ /* If existing bus with no devices */
+ if (!quickstart_data.btn_lst) {
+ ret = -ENODEV;
+ goto fail_pfdrv_reg;
+ }
+
+ /* Platform driver register */
+ ret = platform_driver_register(&pf_driver);
+ if (ret)
+ goto fail_pfdrv_reg;
+
+ /* Platform device register */
+ pf_device = platform_device_alloc(QUICKSTART_PF_DEVICE_NAME, -1);
+ if (!pf_device) {
+ ret = -ENOMEM;
+ goto fail_pfdev_alloc;
+ }
+ ret = platform_device_register(pf_device);
+ if (ret)
+ goto fail_pfdev_reg;
+
+ /* Create device sysfs file */
+ ret = device_create_file(&pf_device->dev, &dev_attr_pressed_button);
+ if (ret)
+ goto fail_dev_file;
+
+ ret = device_create_file(&pf_device->dev, &dev_attr_buttons);
+ if (ret)
+ goto fail_dev_file2;
+
+ printk(KERN_INFO "quickstart: ACPI Direct App Launch ver %s\n",
+ QUICKSTART_VERSION);
+
+ return 0;
+
+fail_dev_file2:
+ device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
+
+fail_dev_file:
+ platform_device_del(pf_device);
+
+fail_pfdev_reg:
+ platform_device_put(pf_device);
+
+fail_pfdev_alloc:
+ platform_driver_unregister(&pf_driver);
+
+fail_pfdrv_reg:
+ acpi_bus_unregister_driver(&quickstart_acpi_driver);
+
+ return ret;
+}
+
+module_init(quickstart_init);
+module_exit(quickstart_exit);

Thanks all,
--
Angelo Arrifano AKA MiKNiX
CSE Student at UBI, Portugal
Gentoo Linux AMD64 Arch Tester
Linwizard Developer
http://miknix.homelinux.com
PGP Pubkey 0x3D92BB0B