[quickstart] Import v1.00r1 released on 2008/03/06
[quickstart] / quickstart / quickstart.c
1 /*
2  *  quickstart.c - ACPI Direct App Launch driver
3  *
4  *
5  *  Copyright (C) 2007 Angelo Arrifano <miknix@gmail.com>
6  *
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"
10  *
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.
15  *
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.
20  *
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
24  *
25  */
26
27 #define QUICKSTART_VERSION "1.00"
28
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>
35
36 MODULE_AUTHOR("Angelo Arrifano");
37 MODULE_DESCRIPTION("ACPI Direct App Launch driver");
38 MODULE_LICENSE("GPL");
39
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");
44
45 #define QUICKSTART_ACPI_DEVICE_NAME   "quickstart"
46 #define QUICKSTART_ACPI_CLASS         "quickstart"
47 #define QUICKSTART_ACPI_HID           "PNP0C32"
48
49 #define QUICKSTART_PF_DRIVER_NAME     "quickstart"
50 #define QUICKSTART_PF_DEVICE_NAME     "quickstart"
51 #define QUICKSTART_PF_DEVATTR_NAME    "pressed_button"
52
53 #define QUICKSTART_MAX_BTN_NAME_LEN   16
54
55 struct quickstart_btn_list {
56         char *name;
57         struct quickstart_btn_list *next;
58 };
59
60 static struct quickstart_driver_data {
61         struct quickstart_btn_list *btn_lst;
62         struct quickstart_btn_list *pressed;
63 } quickstart_data = {
64         .btn_lst = NULL,
65         .pressed = NULL,
66 };
67
68 /*
69  * ACPI driver Structs
70  */
71
72 struct quickstart_acpi {
73         struct acpi_device *device;
74         struct quickstart_btn_list *btn;
75 };
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},
80         {"", 0},
81 };
82
83 static struct acpi_driver quickstart_acpi_driver = {
84         .name = "quickstart",
85         .class = QUICKSTART_ACPI_CLASS,
86         .ids = quickstart_device_ids,
87         .ops = {
88                         .add = quickstart_acpi_add,
89                         .remove = quickstart_acpi_remove,
90                 },
91 };
92
93 /*
94  * Platform driver structs
95  */
96 static ssize_t buttons_show(struct device *dev, 
97                                         struct device_attribute *attr,
98                                         char *buf);
99 static ssize_t pressed_button_show(struct device *dev,
100                                         struct device_attribute *attr,
101                                         char *buf);
102 static ssize_t pressed_button_store(struct device *dev,
103                                         struct device_attribute *attr,
104                                          const char *buf,
105                                          size_t count);
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 = {
111         .driver = {
112                 .name = QUICKSTART_PF_DRIVER_NAME,
113                 .owner = THIS_MODULE,
114         }
115 };
116
117 /*
118  * Platform driver functions
119  */
120 static ssize_t buttons_show(struct device *dev,
121                                          struct device_attribute *attr,
122                                          char *buf)
123 {
124         int count = 0;
125         char tmpbuf[QUICKSTART_MAX_BTN_NAME_LEN + 2];
126         struct quickstart_btn_list *ptr = quickstart_data.btn_lst;
127         
128         if(!ptr)
129                 return snprintf(buf, PAGE_SIZE, "none");
130
131         while(ptr && (count < PAGE_SIZE)){
132                 if(ptr->name){
133                         strncpy(tmpbuf, ptr->name, QUICKSTART_MAX_BTN_NAME_LEN);
134                         strcat(tmpbuf, "\n");
135                         count += snprintf(buf + count,
136                                         PAGE_SIZE - count,
137                                         tmpbuf);
138                 }
139                 ptr = ptr->next;
140         }
141         
142         return count;
143 }
144
145 static ssize_t pressed_button_show(struct device *dev,
146                                         struct device_attribute *attr,
147                                         char *buf)
148 {
149         return snprintf(buf, PAGE_SIZE, "%s\n", 
150                 (quickstart_data.pressed?quickstart_data.pressed->name:"none"));
151 }
152
153
154 static ssize_t pressed_button_store(struct device *dev,
155                                          struct device_attribute *attr,
156                                          const char *buf, size_t count)
157 {
158         if(count < 2)
159                 return -EINVAL;
160
161         if(strncasecmp(buf, "none", 4) != 0)
162                 return -EINVAL;
163
164         quickstart_data.pressed = NULL;
165         return count;
166 }
167
168 /* Hotstart Helper functions */
169 static int quickstart_btnlst_add(struct quickstart_btn_list **data)
170 {
171         struct quickstart_btn_list **ptr = &quickstart_data.btn_lst;
172         
173         while(*ptr)
174                 ptr = &((*ptr)->next);
175                 
176         *ptr = kzalloc(sizeof(struct quickstart_btn_list), GFP_KERNEL);
177         if(!*ptr){
178                 *data = NULL;
179                 return(-ENOMEM);
180         }
181         *data = *ptr;
182
183         return 0;
184 }
185
186 static void quickstart_btnlst_free(void)
187 {
188         struct quickstart_btn_list *ptr = quickstart_data.btn_lst;
189         struct quickstart_btn_list *lptr = NULL;
190         
191         if(!ptr)
192                 return;
193
194         while(ptr){
195                 lptr = ptr;
196                 ptr = ptr->next;
197                 if(lptr->name)
198                         kfree(lptr->name);
199                 kfree(lptr);
200         }
201         
202         return;
203 }
204
205 /* ACPI Driver functions */
206 static void quickstart_acpi_notify(acpi_handle handle, u32 event, void *data)
207 {
208         struct quickstart_acpi *quickstart = data;
209
210         if (!quickstart)
211                 return;
212
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));
220         }
221
222         if(enable_runtime_btns && (event|0x08) == event){
223                 printk("quickstart: Hotkey %s pressed.\n",
224                                         acpi_device_bid(quickstart->device));
225         }
226         
227         return;
228 }
229
230 static void quickstart_acpi_raise_notify(struct quickstart_acpi *quickstart)
231 {
232         acpi_status status;
233
234         /* TODO:
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,
240                                         "GHID", NULL, NULL);
241         if(ACPI_FAILURE(status))
242                 printk("quickstart: Warning: GHID method failed, events of pressed buttons may have been lost.\n");
243
244         return;
245 }
246
247 static int quickstart_acpi_config(struct quickstart_acpi *quickstart, char *bid)
248 {
249         int len = strlen(bid);
250         int ret;
251
252         /* Add button to list */
253         ret = quickstart_btnlst_add(&quickstart->btn);
254         if(ret)
255                 return ret;
256         
257         quickstart->btn->name = kzalloc(len + 1, GFP_KERNEL);
258         if(!quickstart->btn->name){
259                 quickstart_btnlst_free();
260                 return -ENOMEM;
261         }
262         strcpy(quickstart->btn->name, bid);
263
264         return 0;
265 }
266
267 static int quickstart_acpi_add(struct acpi_device *device)
268 {
269         int ret = 0;
270         acpi_status status = AE_OK;
271         struct quickstart_acpi *quickstart = NULL;      
272
273         if (!device)
274                 return -EINVAL;
275
276         quickstart = kzalloc(sizeof(struct quickstart_acpi), GFP_KERNEL);
277         if (!quickstart)
278                 return -ENOMEM;
279
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;
284         
285         /* Add button to list and initialize some stuff */
286         ret = quickstart_acpi_config(quickstart, acpi_device_bid(device));
287         if(ret)
288                 goto fail_config;
289
290         status = acpi_install_notify_handler(device->handle,
291                                                  ACPI_ALL_NOTIFY,
292                                                  quickstart_acpi_notify, quickstart);
293         if(ACPI_FAILURE(status)){
294                 printk("quickstart: Error installing notify handler\n");
295                 ret = -ENODEV;
296                 goto fail_installnotify;
297         }       
298
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);
303
304         return 0;
305
306 fail_installnotify:
307 fail_config:
308
309         kfree(quickstart);
310
311         return ret;
312 }
313
314 static int quickstart_acpi_remove(struct acpi_device *device, int type)
315 {
316         acpi_status status = 0;
317         struct quickstart_acpi *quickstart = NULL;
318
319         if (!device || !acpi_driver_data(device))
320                 return -EINVAL;
321
322         quickstart = acpi_driver_data(device);
323
324         status = acpi_remove_notify_handler(device->handle,
325                                                  ACPI_ALL_NOTIFY,
326                                             quickstart_acpi_notify);
327         if (ACPI_FAILURE(status))
328                 printk("quickstart: Error removing notify handler\n");
329
330
331         kfree(quickstart);
332
333         return 0;
334 }
335
336 /* Module functions */
337
338 static void quickstart_exit(void)
339 {
340         device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
341         device_remove_file(&pf_device->dev, &dev_attr_buttons);
342
343         platform_device_unregister(pf_device);
344
345         platform_driver_unregister(&pf_driver);
346
347         acpi_bus_unregister_driver(&quickstart_acpi_driver);
348         
349         quickstart_btnlst_free();
350
351         return;
352 }
353
354 static int __init quickstart_init(void)
355 {
356         int ret;
357         acpi_status status = 0;
358
359         /* ACPI Check */
360         if(acpi_disabled)
361                 return -ENODEV;
362
363         /* ACPI driver register */
364         status = acpi_bus_register_driver(&quickstart_acpi_driver);
365         if (status < 0)
366                 return -ENODEV;
367
368         /* Platform driver register */
369         ret = platform_driver_register(&pf_driver);
370         if(ret)
371                 goto fail_pfdrv_reg;
372
373         /* Platform device register */
374         pf_device = platform_device_alloc(QUICKSTART_PF_DEVICE_NAME, -1);
375         if(!pf_device){
376                 ret = -ENOMEM;
377                 goto fail_pfdev_alloc;
378         }
379
380         ret = platform_device_register(pf_device);
381         if(ret)
382                 goto fail_pfdev_reg;
383
384         /* Create device sysfs file */
385         ret = device_create_file(&pf_device->dev, &dev_attr_pressed_button);
386         if(ret)
387                 goto fail_dev_file;
388
389         ret = device_create_file(&pf_device->dev, &dev_attr_buttons);
390         if(ret)
391                 goto fail_dev_file2;
392
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.");
396         
397         return 0;
398
399 fail_dev_file2:
400         device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
401
402 fail_dev_file:
403         platform_device_del(pf_device);
404
405 fail_pfdev_reg:
406         platform_device_put(pf_device);
407
408 fail_pfdev_alloc:
409         platform_driver_unregister(&pf_driver);
410
411 fail_pfdrv_reg:
412         acpi_bus_unregister_driver(&quickstart_acpi_driver);
413
414         return ret;
415 }
416
417 module_init(quickstart_init);
418 module_exit(quickstart_exit);