1 /*
2  *  quickstart.c - ACPI Direct App Launch driver
3  *
4  *
5  *  Copyright (C) 2007-2010 Angelo Arrifano <miknix@gmail.com>
6  *
7  *  Information gathered from disassebled dsdt and from here:
8  *  <http://www.microsoft.com/whdc/system/platform/firmware/DirAppLaunch.mspx>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
25 
26 #define QUICKSTART_VERSION "1.03"
27 
28 #include <linux/kernel.h>
29 #include <linux/module.h>
30 #include <linux/init.h>
31 #include <linux/types.h>
32 #include <acpi/acpi_drivers.h>
33 #include <linux/platform_device.h>
34 #include <linux/input.h>
35 
36 MODULE_AUTHOR("Angelo Arrifano");
37 MODULE_DESCRIPTION("ACPI Direct App Launch driver");
38 MODULE_LICENSE("GPL");
39 
40 #define QUICKSTART_ACPI_DEVICE_NAME   "quickstart"
41 #define QUICKSTART_ACPI_CLASS         "quickstart"
42 #define QUICKSTART_ACPI_HID           "PNP0C32"
43 
44 #define QUICKSTART_PF_DRIVER_NAME     "quickstart"
45 #define QUICKSTART_PF_DEVICE_NAME     "quickstart"
46 #define QUICKSTART_PF_DEVATTR_NAME    "pressed_button"
47 
48 #define QUICKSTART_MAX_BTN_NAME_LEN   16
49 
50 /* There will be two events:
51 	 * 0x02 - A hot button was pressed while device was off/sleeping.
52 	 * 0x80 - A hot button was pressed while device was up. */
53 #define QUICKSTART_EVENT_WAKE         0x02
54 #define QUICKSTART_EVENT_RUNTIME      0x80
55 
56 struct quickstart_btn {
57 	char *name;
58 	unsigned int id;
59 	struct quickstart_btn *next;
60 };
61 
62 static struct quickstart_driver_data {
63 	struct quickstart_btn *btn_lst;
64 	struct quickstart_btn *pressed;
65 } quickstart_data;
66 
67 /* ACPI driver Structs */
68 struct quickstart_acpi {
69 	struct acpi_device *device;
70 	struct quickstart_btn *btn;
71 };
72 static int quickstart_acpi_add(struct acpi_device *device);
73 static int quickstart_acpi_remove(struct acpi_device *device, int type);
74 static const struct acpi_device_id  quickstart_device_ids[] = {
75 	{QUICKSTART_ACPI_HID, 0},
76 	{"", 0},
77 };
78 
79 static struct acpi_driver quickstart_acpi_driver = {
80 	.name = "quickstart",
81 	.class = QUICKSTART_ACPI_CLASS,
82 	.ids = quickstart_device_ids,
83 	.ops = {
84 			.add = quickstart_acpi_add,
85 			.remove = quickstart_acpi_remove,
86 		},
87 };
88 
89 /* Input device structs */
90 struct input_dev *quickstart_input;
91 
92 /* Platform driver structs */
93 static ssize_t buttons_show(struct device *dev,
94 					struct device_attribute *attr,
95 					char *buf);
96 static ssize_t pressed_button_show(struct device *dev,
97 					struct device_attribute *attr,
98 					char *buf);
99 static ssize_t pressed_button_store(struct device *dev,
100 					struct device_attribute *attr,
101 					 const char *buf,
102 					 size_t count);
103 static DEVICE_ATTR(pressed_button, 0666, pressed_button_show,
104 					 pressed_button_store);
105 static DEVICE_ATTR(buttons, 0444, buttons_show, NULL);
106 static struct platform_device *pf_device;
107 static struct platform_driver pf_driver = {
108 	.driver = {
109 		.name = QUICKSTART_PF_DRIVER_NAME,
110 		.owner = THIS_MODULE,
111 	}
112 };
113 
114 /*
115  * Platform driver functions
116  */
buttons_show(struct device * dev,struct device_attribute * attr,char * buf)117 static ssize_t buttons_show(struct device *dev,
118 					 struct device_attribute *attr,
119 					 char *buf)
120 {
121 	int count = 0;
122 	struct quickstart_btn *ptr = quickstart_data.btn_lst;
123 
124 	if (!ptr)
125 		return snprintf(buf, PAGE_SIZE, "none");
126 
127 	while (ptr && (count < PAGE_SIZE)) {
128 		if (ptr->name) {
129 			count += snprintf(buf + count,
130 					PAGE_SIZE - count,
131 					"%d\t%s\n", ptr->id, ptr->name);
132 		}
133 		ptr = ptr->next;
134 	}
135 
136 	return count;
137 }
138 
pressed_button_show(struct device * dev,struct device_attribute * attr,char * buf)139 static ssize_t pressed_button_show(struct device *dev,
140 					struct device_attribute *attr,
141 					char *buf)
142 {
143 	return snprintf(buf, PAGE_SIZE, "%s\n",
144 			(quickstart_data.pressed ?
145 			 quickstart_data.pressed->name : "none"));
146 }
147 
148 
pressed_button_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)149 static ssize_t pressed_button_store(struct device *dev,
150 					 struct device_attribute *attr,
151 					 const char *buf, size_t count)
152 {
153 	if (count < 2)
154 		return -EINVAL;
155 
156 	if (strncasecmp(buf, "none", 4) != 0)
157 		return -EINVAL;
158 
159 	quickstart_data.pressed = NULL;
160 	return count;
161 }
162 
163 /* Hotstart Helper functions */
quickstart_btnlst_add(struct quickstart_btn ** data)164 static int quickstart_btnlst_add(struct quickstart_btn **data)
165 {
166 	struct quickstart_btn **ptr = &quickstart_data.btn_lst;
167 
168 	while (*ptr)
169 		ptr = &((*ptr)->next);
170 
171 	*ptr = kzalloc(sizeof(struct quickstart_btn), GFP_KERNEL);
172 	if (!*ptr) {
173 		*data = NULL;
174 		return -ENOMEM;
175 	}
176 	*data = *ptr;
177 
178 	return 0;
179 }
180 
quickstart_btnlst_del(struct quickstart_btn * data)181 static void quickstart_btnlst_del(struct quickstart_btn *data)
182 {
183 	struct quickstart_btn **ptr = &quickstart_data.btn_lst;
184 
185 	if (!data)
186 		return;
187 
188 	while (*ptr) {
189 		if (*ptr == data) {
190 			*ptr = (*ptr)->next;
191 			kfree(data);
192 			return;
193 		}
194 		ptr = &((*ptr)->next);
195 	}
196 
197 	return;
198 }
199 
quickstart_btnlst_free(void)200 static void quickstart_btnlst_free(void)
201 {
202 	struct quickstart_btn *ptr = quickstart_data.btn_lst;
203 	struct quickstart_btn *lptr = NULL;
204 
205 	while (ptr) {
206 		lptr = ptr;
207 		ptr = ptr->next;
208 		kfree(lptr->name);
209 		kfree(lptr);
210 	}
211 
212 	return;
213 }
214 
215 /* ACPI Driver functions */
quickstart_acpi_notify(acpi_handle handle,u32 event,void * data)216 static void quickstart_acpi_notify(acpi_handle handle, u32 event, void *data)
217 {
218 	struct quickstart_acpi *quickstart = data;
219 
220 	if (!quickstart)
221 		return;
222 
223 	if (event == QUICKSTART_EVENT_WAKE)
224 		quickstart_data.pressed = quickstart->btn;
225 	else if (event == QUICKSTART_EVENT_RUNTIME) {
226 		input_report_key(quickstart_input, quickstart->btn->id, 1);
227 		input_sync(quickstart_input);
228 		input_report_key(quickstart_input, quickstart->btn->id, 0);
229 		input_sync(quickstart_input);
230 	}
231 	return;
232 }
233 
quickstart_acpi_ghid(struct quickstart_acpi * quickstart)234 static void quickstart_acpi_ghid(struct quickstart_acpi *quickstart)
235 {
236 	acpi_status status;
237 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
238 	uint32_t usageid = 0;
239 
240 	if (!quickstart)
241 		return;
242 
243 	/* This returns a buffer telling the button usage ID,
244 	 * and triggers pending notify events (The ones before booting). */
245 	status = acpi_evaluate_object(quickstart->device->handle,
246 					"GHID", NULL, &buffer);
247 	if (ACPI_FAILURE(status) || !buffer.pointer) {
248 		printk(KERN_ERR "quickstart: %s GHID method failed.\n",
249 		       quickstart->btn->name);
250 		return;
251 	}
252 
253 	if (buffer.length < 8)
254 		return;
255 
256 	/* <<The GHID method can return a BYTE, WORD, or DWORD.
257 	 * The value must be encoded in little-endian byte
258 	 * order (least significant byte first).>> */
259 	usageid = *((uint32_t *)(buffer.pointer + (buffer.length - 8)));
260 	quickstart->btn->id = usageid;
261 
262 	kfree(buffer.pointer);
263 }
264 
quickstart_acpi_config(struct quickstart_acpi * quickstart,char * bid)265 static int quickstart_acpi_config(struct quickstart_acpi *quickstart, char *bid)
266 {
267 	int len = strlen(bid);
268 	int ret;
269 
270 	/* Add button to list */
271 	ret = quickstart_btnlst_add(&quickstart->btn);
272 	if (ret)
273 		return ret;
274 
275 	quickstart->btn->name = kzalloc(len + 1, GFP_KERNEL);
276 	if (!quickstart->btn->name) {
277 		quickstart_btnlst_free();
278 		return -ENOMEM;
279 	}
280 	strcpy(quickstart->btn->name, bid);
281 
282 	return 0;
283 }
284 
quickstart_acpi_add(struct acpi_device * device)285 static int quickstart_acpi_add(struct acpi_device *device)
286 {
287 	int ret = 0;
288 	acpi_status status = AE_OK;
289 	struct quickstart_acpi *quickstart = NULL;
290 
291 	if (!device)
292 		return -EINVAL;
293 
294 	quickstart = kzalloc(sizeof(struct quickstart_acpi), GFP_KERNEL);
295 	if (!quickstart)
296 		return -ENOMEM;
297 
298 	quickstart->device = device;
299 	strcpy(acpi_device_name(device), QUICKSTART_ACPI_DEVICE_NAME);
300 	strcpy(acpi_device_class(device), QUICKSTART_ACPI_CLASS);
301 	device->driver_data = quickstart;
302 
303 	/* Add button to list and initialize some stuff */
304 	ret = quickstart_acpi_config(quickstart, acpi_device_bid(device));
305 	if (ret)
306 		goto fail_config;
307 
308 	status = acpi_install_notify_handler(device->handle,
309 						ACPI_ALL_NOTIFY,
310 						quickstart_acpi_notify,
311 						quickstart);
312 	if (ACPI_FAILURE(status)) {
313 		printk(KERN_ERR "quickstart: Notify handler install error\n");
314 		ret = -ENODEV;
315 		goto fail_installnotify;
316 	}
317 
318 	quickstart_acpi_ghid(quickstart);
319 
320 	return 0;
321 
322 fail_installnotify:
323 	quickstart_btnlst_del(quickstart->btn);
324 
325 fail_config:
326 
327 	kfree(quickstart);
328 
329 	return ret;
330 }
331 
quickstart_acpi_remove(struct acpi_device * device,int type)332 static int quickstart_acpi_remove(struct acpi_device *device, int type)
333 {
334 	acpi_status status = 0;
335 	struct quickstart_acpi *quickstart = NULL;
336 
337 	if (!device || !acpi_driver_data(device))
338 		return -EINVAL;
339 
340 	quickstart = acpi_driver_data(device);
341 
342 	status = acpi_remove_notify_handler(device->handle,
343 						 ACPI_ALL_NOTIFY,
344 					    quickstart_acpi_notify);
345 	if (ACPI_FAILURE(status))
346 		printk(KERN_ERR "quickstart: Error removing notify handler\n");
347 
348 
349 	kfree(quickstart);
350 
351 	return 0;
352 }
353 
354 /* Module functions */
355 
quickstart_exit(void)356 static void quickstart_exit(void)
357 {
358 	input_unregister_device(quickstart_input);
359 
360 	device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
361 	device_remove_file(&pf_device->dev, &dev_attr_buttons);
362 
363 	platform_device_unregister(pf_device);
364 
365 	platform_driver_unregister(&pf_driver);
366 
367 	acpi_bus_unregister_driver(&quickstart_acpi_driver);
368 
369 	quickstart_btnlst_free();
370 
371 	return;
372 }
373 
quickstart_init_input(void)374 static int __init quickstart_init_input(void)
375 {
376 	struct quickstart_btn **ptr = &quickstart_data.btn_lst;
377 	int count;
378 	int ret;
379 
380 	quickstart_input = input_allocate_device();
381 
382 	if (!quickstart_input)
383 		return -ENOMEM;
384 
385 	quickstart_input->name = "Quickstart ACPI Buttons";
386 	quickstart_input->id.bustype = BUS_HOST;
387 
388 	while (*ptr) {
389 		count++;
390 		set_bit(EV_KEY, quickstart_input->evbit);
391 		set_bit((*ptr)->id, quickstart_input->keybit);
392 		ptr = &((*ptr)->next);
393 	}
394 
395 	ret = input_register_device(quickstart_input);
396 	if (ret) {
397 		input_free_device(quickstart_input);
398 		return ret;
399 	}
400 
401 	return 0;
402 }
403 
quickstart_init(void)404 static int __init quickstart_init(void)
405 {
406 	int ret;
407 
408 	/* ACPI Check */
409 	if (acpi_disabled)
410 		return -ENODEV;
411 
412 	/* ACPI driver register */
413 	ret = acpi_bus_register_driver(&quickstart_acpi_driver);
414 	if (ret)
415 		return ret;
416 
417 	/* If existing bus with no devices */
418 	if (!quickstart_data.btn_lst) {
419 		ret = -ENODEV;
420 		goto fail_pfdrv_reg;
421 	}
422 
423 	/* Platform driver register */
424 	ret = platform_driver_register(&pf_driver);
425 	if (ret)
426 		goto fail_pfdrv_reg;
427 
428 	/* Platform device register */
429 	pf_device = platform_device_alloc(QUICKSTART_PF_DEVICE_NAME, -1);
430 	if (!pf_device) {
431 		ret = -ENOMEM;
432 		goto fail_pfdev_alloc;
433 	}
434 	ret = platform_device_add(pf_device);
435 	if (ret)
436 		goto fail_pfdev_add;
437 
438 	/* Create device sysfs file */
439 	ret = device_create_file(&pf_device->dev, &dev_attr_pressed_button);
440 	if (ret)
441 		goto fail_dev_file;
442 
443 	ret = device_create_file(&pf_device->dev, &dev_attr_buttons);
444 	if (ret)
445 		goto fail_dev_file2;
446 
447 
448 	/* Input device */
449 	ret = quickstart_init_input();
450 	if (ret)
451 		goto fail_input;
452 
453 	printk(KERN_INFO "quickstart: ACPI Direct App Launch ver %s\n",
454 						QUICKSTART_VERSION);
455 
456 	return 0;
457 fail_input:
458 	device_remove_file(&pf_device->dev, &dev_attr_buttons);
459 
460 fail_dev_file2:
461 	device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
462 
463 fail_dev_file:
464 	platform_device_del(pf_device);
465 
466 fail_pfdev_add:
467 	platform_device_put(pf_device);
468 
469 fail_pfdev_alloc:
470 	platform_driver_unregister(&pf_driver);
471 
472 fail_pfdrv_reg:
473 	acpi_bus_unregister_driver(&quickstart_acpi_driver);
474 
475 	return ret;
476 }
477 
478 module_init(quickstart_init);
479 module_exit(quickstart_exit);
480