From: "Ângelo Miguel Arrifano" <miknix@gmail.com>
To: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: andi@firstfloor.org, Zhang Rui <rui.zhang@intel.com>,
Len Brown <lenb@kernel.org>, "Li, Shaohua" <shaohua.li@intel.com>,
linux-acpi@vger.kernel.org
Subject: Re: [PATCH] ACPI: Platform driver to support App Hot Startup (PNP0C32)
Date: Mon, 1 Sep 2008 00:38:56 +0200 [thread overview]
Message-ID: <20080901003856.2d855880.miknix@gmail.com> (raw)
In-Reply-To: <20080828134055.GA27158@srcf.ucam.org>
On Thu, 28 Aug 2008 14:40:55 +0100
Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> On Thu, Aug 28, 2008 at 02:37:08PM +0200, Ângelo Miguel Arrifano wrote:
>
> > +/* 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. */
>
> Are you sure? The spec seems to say 0x80, not 0x08.
>
> > +#define QUICKSTART_EVENT_RUNTIME 0x80
>
> As you have here :)
>
> Could you add an input device for runtime button notification? Use the
> GHID value as a scancode, then provide functions to do scancode->keycode
> mappings (see wistron_btns.c or hp_wmi.c for examples of this). That
> way, hal or whatever can be used to map them to machine-specific values.
>
> --
> Matthew Garrett | mjg59@srcf.ucam.org
Patch below includes proposed changes for runtime button notification.
I could not fully test it since my laptop doesn't report runtime key presses.
NOTE: There are some inline comments in the patch below.
-- Thanks, Angelo Arrifano
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index c52fca8..ed48a56 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -199,6 +199,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 40b0fca..df74d57 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) += sbshc.o
obj-$(CONFIG_ACPI_SBS) += sbs.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..1e6d928
--- /dev/null
+++ b/drivers/acpi/quickstart.c
@@ -0,0 +1,474 @@
+/*
+ * 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>
+#include <linux/input.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.
+ * 0x80 - A hot button was pressed while device was up. */
+#define QUICKSTART_EVENT_WAKE 0x02
+#define QUICKSTART_EVENT_RUNTIME 0x80
+
+struct quickstart_btn {
+ char *name;
+ unsigned int id;
+ 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,
+ },
+};
+
+/* Input device structs */
+struct input_dev *quickstart_input;
+
+/* 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,
+ "%d\t%s\n", ptr->id, ptr->name);
Added the button usage ID to the button list.
The button usage ID is returned by the GHID method.
Ex:
cat /sys/ . . . /buttons
1 QBTN
2 DBTN
3 MUBN
6 PIBN
4 WEBN
8 LVBN
7 VOBN
+ }
+ 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;
+ else if (event == QUICKSTART_EVENT_RUNTIME) {
+ input_report_key(quickstart_input, quickstart->btn->id, 1);
+ input_sync(quickstart_input);
+ input_report_key(quickstart_input, quickstart->btn->id, 0);
+ input_sync(quickstart_input);
The button usage ID is used as key code.
+ }
+ return;
+}
+
+static void quickstart_acpi_ghid(struct quickstart_acpi *quickstart)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ unsigned int usageid = 0;
+
+ if (!quickstart)
+ return;
+
+ /* This returns a buffer telling the button usage ID,
+ * and triggers 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;
+ }
This _GHID method can return a BYTE, WORD or DWORD. I guess each vendor chooses which they like most.
On my laptop, the returned buffer.length is 32.
Buffer dump:
QBTN: 03 00 00 00 01 00 00 00 58 78 6c 4c 00 81 ff ff 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
DBTN: 03 00 00 00 01 00 00 00 58 78 6c 4c 00 81 ff ff 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00
MUBN: 03 00 00 00 01 00 00 00 58 78 6c 4c 00 81 ff ff 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00
PIBN: 03 00 00 00 01 00 00 00 58 78 6c 4c 00 81 ff ff 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00
WEBN: 03 00 00 00 01 00 00 00 58 78 6c 4c 00 81 ff ff 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00
LVBN: 03 00 00 00 01 00 00 00 58 78 6c 4c 00 81 ff ff 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00
VOBN: 03 00 00 00 01 00 00 00 58 78 6c 4c 00 81 ff ff 00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00
+---------------------+
Does it return a QWORD?? Why is it at the end of the buffer?
+
+ if (buffer.length < 8)
+ return;
+
+ /* <<The GHID method can return a BYTE, WORD, or DWORD.
+ * The value must be encoded in little-endian byte
+ * order (least significant byte first).>> */
+ ACPI_MOVE_32_TO_32(&usageid, buffer.pointer + (buffer.length - 8));
+ quickstart->btn->id = usageid;
I need your help in here, I doubt this code runs for anyone but me.
+
+ kfree(buffer.pointer);
+}
+
+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_ghid(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)
+{
+ input_unregister_device(quickstart_input);
+ input_free_device(quickstart_input);
+
+ 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_input(void)
+{
+ struct quickstart_btn **ptr = &quickstart_data.btn_lst;
+ int count;
+
+ quickstart_input = input_allocate_device();
+
+ if (!quickstart_input)
+ return -ENOMEM;
+
+ quickstart_input->name = "Quickstart ACPI Buttons";
+ quickstart_input->id.bustype = BUS_HOST;
+
+ while (*ptr) {
+ count++;
+ set_bit(EV_KEY, quickstart_input->evbit);
+ set_bit((*ptr)->id, quickstart_input->keybit);
+ ptr = &((*ptr)->next);
+ }
+
+ return input_register_device(quickstart_input);
+}
+
+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;
+
+
+ /* Input device */
+ ret = quickstart_init_input();
+ if (ret)
+ goto fail_input;
+
+ printk(KERN_INFO "quickstart: ACPI Direct App Launch ver %s\n",
+ QUICKSTART_VERSION);
+
+ return 0;
+fail_input:
+ device_remove_file(&pf_device->dev, &dev_attr_buttons);
+
+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);
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2008-08-31 21:39 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-03-14 15:17 [PATCH] ACPI: Platform driver to support App Hot Startup (PNP0C32) Ângelo Miguel Arrifano
2008-03-25 6:59 ` Zhang, Rui
2008-03-26 17:51 ` Ângelo Miguel Arrifano
2008-03-27 2:30 ` Shaohua Li
2008-03-27 16:03 ` Ângelo Miguel Arrifano
2008-03-28 1:36 ` Shaohua Li
2008-03-28 19:39 ` Ângelo Miguel Arrifano
2008-08-24 16:39 ` Ângelo Miguel Arrifano
2008-08-25 1:29 ` Zhang Rui
2008-08-26 18:43 ` Andi Kleen
2008-08-28 12:37 ` Ângelo Miguel Arrifano
2008-08-28 13:08 ` Andi Kleen
2008-08-28 13:47 ` Ângelo Miguel Arrifano
2008-08-28 15:36 ` Andi Kleen
2008-08-28 13:40 ` Matthew Garrett
2008-08-31 22:38 ` Ângelo Miguel Arrifano [this message]
2008-08-31 22:00 ` Matthew Garrett
2008-08-31 23:21 ` Ângelo Miguel Arrifano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080901003856.2d855880.miknix@gmail.com \
--to=miknix@gmail.com \
--cc=andi@firstfloor.org \
--cc=lenb@kernel.org \
--cc=linux-acpi@vger.kernel.org \
--cc=mjg59@srcf.ucam.org \
--cc=rui.zhang@intel.com \
--cc=shaohua.li@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.