@@ -537,4 +537,15 @@ config INTEL_SCU_IPC
some embedded Intel x86 platforms. This is not needed for PC-type
machines.
+config ACPI_QUICKSTART
+ tristate "ACPI QUICKSTART quick launch button support"
+ depends on ACPI
+ select INPUT
+ default n
+ help
+ Some media and other buttons could get driven by this driver.
+ It could get identified which button woke the machine up after
+ a suspend and appropriate action, e.g. launch an application
+ could be taken by userspace.
+
endif # X86_PLATFORM_DEVICES
@@ -25,4 +25,5 @@ obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
+obj-$(CONFIG_ACPI_QUICKSTART) += quickstart.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
new file mode 100644
@@ -0,0 +1,374 @@
+/*
+ * quickstart.c - ACPI Direct App Launch driver
+ *
+ *
+ * Copyright (C) 2007-2010 Angelo Arrifano <miknix@gmail.com>
+ * Copyright (C) 2010 Thomas Renninger <trenn@suse.de>
+ *
+ * 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.04"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <acpi/acpi_drivers.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_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
+
+static unsigned int max_defined_keycode = BTN_TRIGGER_HAPPY;
+
+/* ACPI driver Structs */
+struct quickstart_acpi {
+ struct acpi_device *device;
+ struct input_dev *input;
+ unsigned int id;
+ unsigned int keycode;
+};
+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,
+ },
+};
+
+/* ACPI Driver functions */
+static void quickstart_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct quickstart_acpi *quickstart = data;
+
+ printk(KERN_INFO "XXX Notify: Event: %d\n", event);
+
+ if (!quickstart)
+ return;
+
+ printk(KERN_INFO "Notify: Event: %d - keycode: %d\n", event,
+ quickstart->keycode);
+ switch(event) {
+ case QUICKSTART_EVENT_WAKE:
+ case QUICKSTART_EVENT_RUNTIME:
+ printk(KERN_INFO "Send input key %d\n", quickstart->keycode);
+ input_report_key(quickstart->input, quickstart->keycode, 1);
+ input_sync(quickstart->input);
+ input_report_key(quickstart->input, quickstart->keycode, 0);
+ input_sync(quickstart->input);
+ }
+ acpi_bus_generate_netlink_event(quickstart->device->pnp.device_class,
+ dev_name(&quickstart->device->dev), event,
+ quickstart->id);
+ return;
+}
+
+static int quickstart_acpi_ghid(struct quickstart_acpi *quickstart)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ uint32_t usageid = 0;
+ union acpi_object *obj;
+
+ if (!quickstart)
+ return -ENODEV;
+
+ /* 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)) {
+ printk(KERN_ERR "quickstart: %s GHID method failed\n",
+ quickstart->device->pnp.bus_id);
+ /*
+ * In which case do we have to free buffer.pointer?
+ * ACPI_TYPE_BUFFER, ACPI_TYPE_STRING, ACPI_TYPE_PACKAGE
+ * case?
+ * A generic acpi_free_buffer(struct acpi_buffer) function
+ * would be nice...
+ */
+ return -EINVAL;
+ }
+
+ obj = (union acpi_object *)buffer.pointer;
+ if (!obj || obj->type != ACPI_TYPE_BUFFER)
+ return -EINVAL;
+
+ /*
+ * <<The GHID method can return a BYTE, WORD, or DWORD.
+ * The value must be encoded in little-endian byte
+ * order (least significant byte first).>>
+ *
+ * Also handle 64 bit case, could be that a BIOS uses
+ * an integer accidently.
+ */
+ printk("Obj length: %d\n", obj->buffer.length);
+ switch(obj->buffer.length) {
+ case 1:
+ usageid = *((uint8_t *)(obj->buffer.pointer));
+ break;
+ case 2:
+ usageid = *((uint16_t *)(obj->buffer.pointer));
+ break;
+ case 4:
+ usageid = *((uint32_t *)(obj->buffer.pointer));
+ break;
+ case 8:
+ usageid = *((uint64_t *)(obj->buffer.pointer));
+ break;
+ default:
+ break;
+ kfree(obj->buffer.pointer);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "Obj length: %d - UsageId: %d\n",
+ obj->buffer.length, usageid);
+
+ kfree(obj->buffer.pointer);
+
+ quickstart->id = usageid;
+
+ return 0;
+}
+
+static ssize_t quickstart_usageid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct quickstart_acpi *quickstart = acpi_dev->driver_data;
+
+ if (quickstart)
+ return sprintf(buf, "%d\n", quickstart->id);
+ return -ENODEV;
+}
+
+static ssize_t quickstart_keycode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct quickstart_acpi *quickstart = acpi_dev->driver_data;
+
+ if (quickstart)
+ return sprintf(buf, "%d\n", quickstart->keycode);
+ return -ENODEV;
+}
+
+static ssize_t quickstart_keycode_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct quickstart_acpi *quickstart = acpi_dev->driver_data;
+ unsigned long temp;
+ int res;
+ char input_name[32];
+
+ if (!quickstart)
+ return -ENODEV;
+
+ res = strict_strtoul(buf, 10, &temp);
+ if (res)
+ return res;
+
+ quickstart->keycode = temp;
+ /*
+ * Reregister input device with new capabilities
+ * This is somewhat ugly, eventually this can be
+ * solved somewhat more elegant.
+ */
+ input_unregister_device(quickstart->input);
+ quickstart->input = input_allocate_device();
+ if (!quickstart->input)
+ return -ENOMEM;
+
+ sprintf(input_name, "Quickstart ACPI Button %s",
+ quickstart->device->pnp.bus_id);
+ quickstart->input->name = input_name;
+ quickstart->input->id.bustype = BUS_HOST;
+
+ input_set_capability(quickstart->input, EV_KEY,
+ quickstart->keycode);
+ res = input_register_device(quickstart->input);
+ if (res) {
+ input_free_device(quickstart->input);
+ return -ENOMEM;
+ }
+ return count;
+}
+
+DEVICE_ATTR (keycode, 0644, quickstart_keycode_show, quickstart_keycode_store);
+DEVICE_ATTR (usageid, 0444, quickstart_usageid_show, NULL);
+
+static int quickstart_acpi_add(struct acpi_device *device)
+{
+ int ret = 0;
+ struct quickstart_acpi *quickstart = NULL;
+ acpi_status status;
+ char input_name[32];
+
+ if (max_defined_keycode >= BTN_TRIGGER_HAPPY40)
+ return -ENODEV;
+
+ if (!device)
+ return -EINVAL;
+
+ quickstart = kzalloc(sizeof(struct quickstart_acpi), GFP_KERNEL);
+ if (!quickstart)
+ return -ENOMEM;
+
+ quickstart->device = device;
+ strcpy(device->pnp.device_name, QUICKSTART_ACPI_DEVICE_NAME);
+ strcpy(device->pnp.device_class, QUICKSTART_ACPI_CLASS);
+ device->driver_data = quickstart;
+
+ ret = quickstart_acpi_ghid(quickstart);
+ printk("%s %d\n", __FUNCTION__, ret);
+ if (ret)
+ goto free_obj;
+
+ status = acpi_install_notify_handler(device->handle,
+ ACPI_ALL_NOTIFY,
+ quickstart_acpi_notify,
+ quickstart);
+ if (ACPI_FAILURE(status))
+ goto free_obj;
+
+ ret = device_create_file(&device->dev, &dev_attr_keycode);
+ if (ret)
+ goto un_notify;
+
+ ret = device_create_file(&device->dev, &dev_attr_usageid);
+ if (ret)
+ goto file2;
+
+ quickstart->keycode = max_defined_keycode;
+ max_defined_keycode++;
+
+ /* Input device */
+ quickstart->input = input_allocate_device();
+ if (!quickstart->input)
+ goto file1;
+
+ sprintf(input_name, "Quickstart ACPI Button %s",
+ quickstart->device->pnp.bus_id);
+ quickstart->input->name = input_name;
+ quickstart->input->id.bustype = BUS_HOST;
+
+ input_set_capability(quickstart->input, EV_KEY,
+ quickstart->keycode);
+ ret = input_register_device(quickstart->input);
+ if (ret)
+ goto free_input;
+
+ return 0;
+
+ free_input:
+ input_free_device(quickstart->input);
+
+ file1:
+ device_remove_file(&device->dev, &dev_attr_usageid);
+ file2:
+ device_remove_file(&device->dev, &dev_attr_keycode);
+
+ un_notify:
+ acpi_remove_notify_handler(device->handle,
+ ACPI_ALL_NOTIFY,
+ quickstart_acpi_notify);
+ free_obj:
+ kfree(quickstart);
+
+ return ret;
+}
+
+static int quickstart_acpi_remove(struct acpi_device *device, int type)
+{
+ struct quickstart_acpi *quickstart;
+
+ if (!device || !device->driver_data)
+ return -EINVAL;
+
+ quickstart = device->driver_data;
+
+ input_unregister_device(quickstart->input);
+
+ acpi_remove_notify_handler(device->handle,
+ ACPI_ALL_NOTIFY,
+ quickstart_acpi_notify);
+
+ device_remove_file(&device->dev, &dev_attr_usageid);
+ device_remove_file(&device->dev, &dev_attr_keycode);
+
+ kfree(quickstart);
+
+ return 0;
+}
+
+/* Module functions */
+
+static void quickstart_exit(void)
+{
+ acpi_bus_unregister_driver(&quickstart_acpi_driver);
+}
+
+static int __init quickstart_init(void)
+{
+ acpi_status status = 0;
+
+ /* ACPI Check */
+ if (acpi_disabled)
+ return -ENODEV;
+
+ /* ACPI driver register */
+ status = acpi_bus_register_driver(&quickstart_acpi_driver);
+ printk("Status: %d\n", status);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ printk(KERN_INFO "quickstart: ACPI Direct App Launch ver %s\n",
+ QUICKSTART_VERSION);
+ return 0;
+}
+
+module_init(quickstart_init);
+module_exit(quickstart_exit);