2 * quickstart.c - ACPI Direct App Launch driver
5 * Copyright (C) 2007 Angelo Arrifano <miknix@gmail.com>
7 * Information gathered from disassebled dsdt and from here:
8 * "http://download.microsoft.com/download/9/c/5/
9 * 9c5b2167-8017-4bae-9fde-d599bac8184a/DirAppLaunch_Vista.doc"
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #define QUICKSTART_VERSION "1.00"
29 #include <linux/kernel.h>
30 #include <linux/module.h>
31 #include <linux/init.h>
32 #include <linux/types.h>
33 #include <acpi/acpi_drivers.h>
34 #include <linux/platform_device.h>
36 MODULE_AUTHOR("Angelo Arrifano");
37 MODULE_DESCRIPTION("ACPI Direct App Launch driver");
38 MODULE_LICENSE("GPL");
40 static int enable_runtime_btns;
41 module_param(enable_runtime_btns, int, 0);
42 MODULE_PARM_DESC(enable_runtime_btns,
43 "Enable reporting of runtime button presses (hotkeys).\n");
45 #define QUICKSTART_ACPI_DEVICE_NAME "quickstart"
46 #define QUICKSTART_ACPI_CLASS "quickstart"
47 #define QUICKSTART_ACPI_HID "PNP0C32"
49 #define QUICKSTART_PF_DRIVER_NAME "quickstart"
50 #define QUICKSTART_PF_DEVICE_NAME "quickstart"
51 #define QUICKSTART_PF_DEVATTR_NAME "pressed_button"
53 #define QUICKSTART_MAX_BTN_NAME_LEN 16
55 struct quickstart_btn_list {
57 struct quickstart_btn_list *next;
60 static struct quickstart_driver_data {
61 struct quickstart_btn_list *btn_lst;
62 struct quickstart_btn_list *pressed;
72 struct quickstart_acpi {
73 struct acpi_device *device;
74 struct quickstart_btn_list *btn;
76 static int quickstart_acpi_add(struct acpi_device *device);
77 static int quickstart_acpi_remove(struct acpi_device *device, int type);
78 static const struct acpi_device_id quickstart_device_ids[] = {
79 {QUICKSTART_ACPI_HID, 0},
83 static struct acpi_driver quickstart_acpi_driver = {
85 .class = QUICKSTART_ACPI_CLASS,
86 .ids = quickstart_device_ids,
88 .add = quickstart_acpi_add,
89 .remove = quickstart_acpi_remove,
94 * Platform driver structs
96 static ssize_t buttons_show(struct device *dev,
97 struct device_attribute *attr,
99 static ssize_t pressed_button_show(struct device *dev,
100 struct device_attribute *attr,
102 static ssize_t pressed_button_store(struct device *dev,
103 struct device_attribute *attr,
106 static DEVICE_ATTR(pressed_button, 0666, pressed_button_show,
107 pressed_button_store);
108 static DEVICE_ATTR(buttons, 0444, buttons_show, NULL);
109 static struct platform_device *pf_device;
110 static struct platform_driver pf_driver = {
112 .name = QUICKSTART_PF_DRIVER_NAME,
113 .owner = THIS_MODULE,
118 * Platform driver functions
120 static ssize_t buttons_show(struct device *dev,
121 struct device_attribute *attr,
125 char tmpbuf[QUICKSTART_MAX_BTN_NAME_LEN + 2];
126 struct quickstart_btn_list *ptr = quickstart_data.btn_lst;
129 return snprintf(buf, PAGE_SIZE, "none");
131 while(ptr && (count < PAGE_SIZE)){
133 strncpy(tmpbuf, ptr->name, QUICKSTART_MAX_BTN_NAME_LEN);
134 strcat(tmpbuf, "\n");
135 count += snprintf(buf + count,
145 static ssize_t pressed_button_show(struct device *dev,
146 struct device_attribute *attr,
149 return snprintf(buf, PAGE_SIZE, "%s\n",
150 (quickstart_data.pressed?quickstart_data.pressed->name:"none"));
154 static ssize_t pressed_button_store(struct device *dev,
155 struct device_attribute *attr,
156 const char *buf, size_t count)
161 if(strncasecmp(buf, "none", 4) != 0)
164 quickstart_data.pressed = NULL;
168 /* Hotstart Helper functions */
169 static int quickstart_btnlst_add(struct quickstart_btn_list **data)
171 struct quickstart_btn_list **ptr = &quickstart_data.btn_lst;
174 ptr = &((*ptr)->next);
176 *ptr = kzalloc(sizeof(struct quickstart_btn_list), GFP_KERNEL);
186 static void quickstart_btnlst_free(void)
188 struct quickstart_btn_list *ptr = quickstart_data.btn_lst;
189 struct quickstart_btn_list *lptr = NULL;
205 /* ACPI Driver functions */
206 static void quickstart_acpi_notify(acpi_handle handle, u32 event, void *data)
208 struct quickstart_acpi *quickstart = data;
213 /* There will be two events:
214 * 0x02 - A quickstart button was pressed while device was off/sleeping.
215 * 0x08 - A quickstart button was pressed while device was up. */
216 if((event|0x02) == event){
217 quickstart_data.pressed = quickstart->btn;
218 printk("quickstart: Quickbutton %s pressed.\n",
219 acpi_device_bid(quickstart->device));
222 if(enable_runtime_btns && (event|0x08) == event){
223 printk("quickstart: Hotkey %s pressed.\n",
224 acpi_device_bid(quickstart->device));
230 static void quickstart_acpi_raise_notify(struct quickstart_acpi *quickstart)
235 * We should receive a buffer telling the kind of button,
236 * eg: dvd, printer, www, ...
237 * but we dont care about it now. The important is to trigger
238 * pending notify events (The ones before booting). */
239 status = acpi_evaluate_object(quickstart->device->handle,
241 if(ACPI_FAILURE(status))
242 printk("quickstart: Warning: GHID method failed, events of pressed buttons may have been lost.\n");
247 static int quickstart_acpi_config(struct quickstart_acpi *quickstart, char *bid)
249 int len = strlen(bid);
252 /* Add button to list */
253 ret = quickstart_btnlst_add(&quickstart->btn);
257 quickstart->btn->name = kzalloc(len + 1, GFP_KERNEL);
258 if(!quickstart->btn->name){
259 quickstart_btnlst_free();
262 strcpy(quickstart->btn->name, bid);
267 static int quickstart_acpi_add(struct acpi_device *device)
270 acpi_status status = AE_OK;
271 struct quickstart_acpi *quickstart = NULL;
276 quickstart = kzalloc(sizeof(struct quickstart_acpi), GFP_KERNEL);
280 quickstart->device = device;
281 strcpy(acpi_device_name(device), QUICKSTART_ACPI_DEVICE_NAME);
282 strcpy(acpi_device_class(device), QUICKSTART_ACPI_CLASS);
283 acpi_driver_data(device) = quickstart;
285 /* Add button to list and initialize some stuff */
286 ret = quickstart_acpi_config(quickstart, acpi_device_bid(device));
290 status = acpi_install_notify_handler(device->handle,
292 quickstart_acpi_notify, quickstart);
293 if(ACPI_FAILURE(status)){
294 printk("quickstart: Error installing notify handler\n");
296 goto fail_installnotify;
299 /* Call device GHID method. This is a workaround to get a notify
300 * event when hot button is pressed while laptop is powered off.
301 * This works, even if the method fails. */
302 quickstart_acpi_raise_notify(quickstart);
314 static int quickstart_acpi_remove(struct acpi_device *device, int type)
316 acpi_status status = 0;
317 struct quickstart_acpi *quickstart = NULL;
319 if (!device || !acpi_driver_data(device))
322 quickstart = acpi_driver_data(device);
324 status = acpi_remove_notify_handler(device->handle,
326 quickstart_acpi_notify);
327 if (ACPI_FAILURE(status))
328 printk("quickstart: Error removing notify handler\n");
336 /* Module functions */
338 static void quickstart_exit(void)
340 device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
341 device_remove_file(&pf_device->dev, &dev_attr_buttons);
343 platform_device_unregister(pf_device);
345 platform_driver_unregister(&pf_driver);
347 acpi_bus_unregister_driver(&quickstart_acpi_driver);
349 quickstart_btnlst_free();
354 static int __init quickstart_init(void)
357 acpi_status status = 0;
363 /* ACPI driver register */
364 status = acpi_bus_register_driver(&quickstart_acpi_driver);
368 /* Platform driver register */
369 ret = platform_driver_register(&pf_driver);
373 /* Platform device register */
374 pf_device = platform_device_alloc(QUICKSTART_PF_DEVICE_NAME, -1);
377 goto fail_pfdev_alloc;
380 ret = platform_device_register(pf_device);
384 /* Create device sysfs file */
385 ret = device_create_file(&pf_device->dev, &dev_attr_pressed_button);
389 ret = device_create_file(&pf_device->dev, &dev_attr_buttons);
393 printk("quickstart: ACPI Direct App Launch ver %s\n", QUICKSTART_VERSION);
394 if(enable_runtime_btns)
395 printk("quickstart: Runtime button reporting is enabled.");
400 device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
403 platform_device_del(pf_device);
406 platform_device_put(pf_device);
409 platform_driver_unregister(&pf_driver);
412 acpi_bus_unregister_driver(&quickstart_acpi_driver);
417 module_init(quickstart_init);
418 module_exit(quickstart_exit);