Linux Audio

Check our new training course

Loading...
v3.1
  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
 36MODULE_AUTHOR("Angelo Arrifano");
 37MODULE_DESCRIPTION("ACPI Direct App Launch driver");
 38MODULE_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
 56struct quickstart_btn {
 
 
 
 
 
 
 
 
 57	char *name;
 58	unsigned int id;
 59	struct quickstart_btn *next;
 60};
 61
 62static struct quickstart_driver_data {
 63	struct quickstart_btn *btn_lst;
 64	struct quickstart_btn *pressed;
 65} quickstart_data;
 66
 67/* ACPI driver Structs */
 68struct quickstart_acpi {
 69	struct acpi_device *device;
 70	struct quickstart_btn *btn;
 71};
 72static int quickstart_acpi_add(struct acpi_device *device);
 73static int quickstart_acpi_remove(struct acpi_device *device, int type);
 74static const struct acpi_device_id  quickstart_device_ids[] = {
 75	{QUICKSTART_ACPI_HID, 0},
 76	{"", 0},
 77};
 78
 79static 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 */
 90struct input_dev *quickstart_input;
 91
 92/* Platform driver structs */
 93static ssize_t buttons_show(struct device *dev,
 94					struct device_attribute *attr,
 95					char *buf);
 96static ssize_t pressed_button_show(struct device *dev,
 97					struct device_attribute *attr,
 98					char *buf);
 99static ssize_t pressed_button_store(struct device *dev,
100					struct device_attribute *attr,
101					 const char *buf,
102					 size_t count);
103static DEVICE_ATTR(pressed_button, 0666, pressed_button_show,
104					 pressed_button_store);
105static DEVICE_ATTR(buttons, 0444, buttons_show, NULL);
106static struct platform_device *pf_device;
107static 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 */
117static 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
139static 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
149static 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 */
164static 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
181static 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
200static 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 */
216static 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
234static 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
265static 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
285static 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
 
 
 
 
322fail_installnotify:
323	quickstart_btnlst_del(quickstart->btn);
324
325fail_config:
326
327	kfree(quickstart);
328
329	return ret;
330}
331
332static 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
 
356static 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
374static 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
404static 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;
457fail_input:
458	device_remove_file(&pf_device->dev, &dev_attr_buttons);
459
460fail_dev_file2:
461	device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
462
463fail_dev_file:
464	platform_device_del(pf_device);
465
466fail_pfdev_add:
467	platform_device_put(pf_device);
468
469fail_pfdev_alloc:
470	platform_driver_unregister(&pf_driver);
471
472fail_pfdrv_reg:
473	acpi_bus_unregister_driver(&quickstart_acpi_driver);
474
475	return ret;
476}
477
478module_init(quickstart_init);
479module_exit(quickstart_exit);
v3.5.6
  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 disassembled 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.04"
 27
 28#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 29
 30#include <linux/kernel.h>
 31#include <linux/module.h>
 32#include <linux/init.h>
 33#include <linux/types.h>
 34#include <acpi/acpi_drivers.h>
 35#include <linux/platform_device.h>
 36#include <linux/input.h>
 37
 38MODULE_AUTHOR("Angelo Arrifano");
 39MODULE_DESCRIPTION("ACPI Direct App Launch driver");
 40MODULE_LICENSE("GPL");
 41
 42#define QUICKSTART_ACPI_DEVICE_NAME	"quickstart"
 43#define QUICKSTART_ACPI_CLASS		"quickstart"
 44#define QUICKSTART_ACPI_HID		"PNP0C32"
 45
 46#define QUICKSTART_PF_DRIVER_NAME	"quickstart"
 47#define QUICKSTART_PF_DEVICE_NAME	"quickstart"
 
 
 
 
 
 
 
 
 
 48
 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 */
 54#define QUICKSTART_EVENT_WAKE		0x02
 55#define QUICKSTART_EVENT_RUNTIME	0x80
 56
 57struct quickstart_button {
 58	char *name;
 59	unsigned int id;
 60	struct list_head list;
 61};
 62
 
 
 
 
 
 
 63struct quickstart_acpi {
 64	struct acpi_device *device;
 65	struct quickstart_button *button;
 
 
 
 
 
 
 66};
 67
 68static LIST_HEAD(buttons);
 69static struct quickstart_button *pressed;
 
 
 
 
 
 
 
 70
 71static struct input_dev *quickstart_input;
 
 72
 73/* Platform driver functions */
 74static ssize_t quickstart_buttons_show(struct device *dev,
 75					struct device_attribute *attr,
 76					char *buf)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 77{
 78	int count = 0;
 79	struct quickstart_button *b;
 80
 81	if (list_empty(&buttons))
 82		return snprintf(buf, PAGE_SIZE, "none");
 83
 84	list_for_each_entry(b, &buttons, list) {
 85		count += snprintf(buf + count, PAGE_SIZE - count, "%u\t%s\n",
 86							b->id, b->name);
 87
 88		if (count >= PAGE_SIZE) {
 89			count = PAGE_SIZE;
 90			break;
 91		}
 
 92	}
 93
 94	return count;
 95}
 96
 97static ssize_t quickstart_pressed_button_show(struct device *dev,
 98						struct device_attribute *attr,
 99						char *buf)
100{
101	return scnprintf(buf, PAGE_SIZE, "%s\n",
102					(pressed ? pressed->name : "none"));
 
103}
104
105
106static ssize_t quickstart_pressed_button_store(struct device *dev,
107						struct device_attribute *attr,
108						const char *buf, size_t count)
109{
110	if (count < 2)
111		return -EINVAL;
112
113	if (strncasecmp(buf, "none", 4) != 0)
114		return -EINVAL;
115
116	pressed = NULL;
117	return count;
118}
119
120/* Helper functions */
121static struct quickstart_button *quickstart_buttons_add(void)
122{
123	struct quickstart_button *b;
124
125	b = kzalloc(sizeof(*b), GFP_KERNEL);
126	if (!b)
127		return NULL;
128
129	list_add_tail(&b->list, &buttons);
 
 
 
 
 
130
131	return b;
132}
133
134static void quickstart_button_del(struct quickstart_button *data)
135{
 
 
136	if (!data)
137		return;
138
139	list_del(&data->list);
140	kfree(data->name);
141	kfree(data);
 
 
 
 
 
 
 
142}
143
144static void quickstart_buttons_free(void)
145{
146	struct quickstart_button *b, *n;
 
 
 
 
 
 
 
 
147
148	list_for_each_entry_safe(b, n, &buttons, list)
149		quickstart_button_del(b);
150}
151
152/* ACPI Driver functions */
153static void quickstart_acpi_notify(acpi_handle handle, u32 event, void *data)
154{
155	struct quickstart_acpi *quickstart = data;
156
157	if (!quickstart)
158		return;
159
160	switch (event) {
161	case QUICKSTART_EVENT_WAKE:
162		pressed = quickstart->button;
163		break;
164	case QUICKSTART_EVENT_RUNTIME:
165		input_report_key(quickstart_input, quickstart->button->id, 1);
166		input_sync(quickstart_input);
167		input_report_key(quickstart_input, quickstart->button->id, 0);
168		input_sync(quickstart_input);
169		break;
170	default:
171		pr_err("Unexpected ACPI event notify (%u)\n", event);
172		break;
173	}
 
174}
175
176static int quickstart_acpi_ghid(struct quickstart_acpi *quickstart)
177{
178	acpi_status status;
179	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
180	int ret = 0;
 
 
 
181
182	/*
183	 * This returns a buffer telling the button usage ID,
184	 * and triggers pending notify events (The ones before booting).
185	 */
186	status = acpi_evaluate_object(quickstart->device->handle, "GHID", NULL,
187								&buffer);
188	if (ACPI_FAILURE(status)) {
189		pr_err("%s GHID method failed\n", quickstart->button->name);
190		return -EINVAL;
191	}
192
193	/*
194	 * <<The GHID method can return a BYTE, WORD, or DWORD.
 
 
195	 * The value must be encoded in little-endian byte
196	 * order (least significant byte first).>>
197	 */
198	switch (buffer.length) {
199	case 1:
200		quickstart->button->id = *(uint8_t *)buffer.pointer;
201		break;
202	case 2:
203		quickstart->button->id = *(uint16_t *)buffer.pointer;
204		break;
205	case 4:
206		quickstart->button->id = *(uint32_t *)buffer.pointer;
207		break;
208	case 8:
209		quickstart->button->id = *(uint64_t *)buffer.pointer;
210		break;
211	default:
212		pr_err("%s GHID method returned buffer of unexpected length %lu\n",
213				quickstart->button->name,
214				(unsigned long)buffer.length);
215		ret = -EINVAL;
216		break;
217	}
218
219	kfree(buffer.pointer);
220
221	return ret;
222}
223
224static int quickstart_acpi_config(struct quickstart_acpi *quickstart)
225{
226	char *bid = acpi_device_bid(quickstart->device);
227	char *name;
228
229	name = kmalloc(strlen(bid) + 1, GFP_KERNEL);
230	if (!name)
231		return -ENOMEM;
 
232
233	/* Add new button to list */
234	quickstart->button = quickstart_buttons_add();
235	if (!quickstart->button) {
236		kfree(name);
237		return -ENOMEM;
238	}
239
240	quickstart->button->name = name;
241	strcpy(quickstart->button->name, bid);
242
243	return 0;
244}
245
246static int quickstart_acpi_add(struct acpi_device *device)
247{
248	int ret;
249	acpi_status status;
250	struct quickstart_acpi *quickstart;
251
252	if (!device)
253		return -EINVAL;
254
255	quickstart = kzalloc(sizeof(*quickstart), GFP_KERNEL);
256	if (!quickstart)
257		return -ENOMEM;
258
259	quickstart->device = device;
260
261	strcpy(acpi_device_name(device), QUICKSTART_ACPI_DEVICE_NAME);
262	strcpy(acpi_device_class(device), QUICKSTART_ACPI_CLASS);
263	device->driver_data = quickstart;
264
265	/* Add button to list and initialize some stuff */
266	ret = quickstart_acpi_config(quickstart);
267	if (ret < 0)
268		goto fail_config;
269
270	status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY,
 
271						quickstart_acpi_notify,
272						quickstart);
273	if (ACPI_FAILURE(status)) {
274		pr_err("Notify handler install error\n");
275		ret = -ENODEV;
276		goto fail_installnotify;
277	}
278
279	ret = quickstart_acpi_ghid(quickstart);
280	if (ret < 0)
281		goto fail_ghid;
282
283	return 0;
284
285fail_ghid:
286	acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY,
287						quickstart_acpi_notify);
288
289fail_installnotify:
290	quickstart_button_del(quickstart->button);
291
292fail_config:
293
294	kfree(quickstart);
295
296	return ret;
297}
298
299static int quickstart_acpi_remove(struct acpi_device *device, int type)
300{
301	acpi_status status;
302	struct quickstart_acpi *quickstart;
303
304	if (!device)
305		return -EINVAL;
306
307	quickstart = acpi_driver_data(device);
308	if (!quickstart)
309		return -EINVAL;
310
311	status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY,
312						quickstart_acpi_notify);
 
313	if (ACPI_FAILURE(status))
314		pr_err("Error removing notify handler\n");
 
315
316	kfree(quickstart);
317
318	return 0;
319}
320
321/* Platform driver structs */
322static DEVICE_ATTR(pressed_button, 0666, quickstart_pressed_button_show,
323					 quickstart_pressed_button_store);
324static DEVICE_ATTR(buttons, 0444, quickstart_buttons_show, NULL);
325static struct platform_device *pf_device;
326static struct platform_driver pf_driver = {
327	.driver = {
328		.name = QUICKSTART_PF_DRIVER_NAME,
329		.owner = THIS_MODULE,
330	}
331};
332
333static const struct acpi_device_id quickstart_device_ids[] = {
334	{QUICKSTART_ACPI_HID, 0},
335	{"", 0},
336};
337
338static struct acpi_driver quickstart_acpi_driver = {
339	.name = "quickstart",
340	.class = QUICKSTART_ACPI_CLASS,
341	.ids = quickstart_device_ids,
342	.ops = {
343			.add = quickstart_acpi_add,
344			.remove = quickstart_acpi_remove,
345		},
346};
347
348/* Module functions */
349static void quickstart_exit(void)
350{
351	input_unregister_device(quickstart_input);
352
353	device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
354	device_remove_file(&pf_device->dev, &dev_attr_buttons);
355
356	platform_device_unregister(pf_device);
357
358	platform_driver_unregister(&pf_driver);
359
360	acpi_bus_unregister_driver(&quickstart_acpi_driver);
361
362	quickstart_buttons_free();
 
 
363}
364
365static int __init quickstart_init_input(void)
366{
367	struct quickstart_button *b;
 
368	int ret;
369
370	quickstart_input = input_allocate_device();
371
372	if (!quickstart_input)
373		return -ENOMEM;
374
375	quickstart_input->name = "Quickstart ACPI Buttons";
376	quickstart_input->id.bustype = BUS_HOST;
377
378	list_for_each_entry(b, &buttons, list) {
 
379		set_bit(EV_KEY, quickstart_input->evbit);
380		set_bit(b->id, quickstart_input->keybit);
 
381	}
382
383	ret = input_register_device(quickstart_input);
384	if (ret) {
385		input_free_device(quickstart_input);
386		return ret;
387	}
388
389	return 0;
390}
391
392static int __init quickstart_init(void)
393{
394	int ret;
395
396	/* ACPI Check */
397	if (acpi_disabled)
398		return -ENODEV;
399
400	/* ACPI driver register */
401	ret = acpi_bus_register_driver(&quickstart_acpi_driver);
402	if (ret)
403		return ret;
404
405	/* If existing bus with no devices */
406	if (list_empty(&buttons)) {
407		ret = -ENODEV;
408		goto fail_pfdrv_reg;
409	}
410
411	/* Platform driver register */
412	ret = platform_driver_register(&pf_driver);
413	if (ret)
414		goto fail_pfdrv_reg;
415
416	/* Platform device register */
417	pf_device = platform_device_alloc(QUICKSTART_PF_DEVICE_NAME, -1);
418	if (!pf_device) {
419		ret = -ENOMEM;
420		goto fail_pfdev_alloc;
421	}
422	ret = platform_device_add(pf_device);
423	if (ret)
424		goto fail_pfdev_add;
425
426	/* Create device sysfs file */
427	ret = device_create_file(&pf_device->dev, &dev_attr_pressed_button);
428	if (ret)
429		goto fail_dev_file;
430
431	ret = device_create_file(&pf_device->dev, &dev_attr_buttons);
432	if (ret)
433		goto fail_dev_file2;
434
 
435	/* Input device */
436	ret = quickstart_init_input();
437	if (ret)
438		goto fail_input;
439
440	pr_info("ACPI Direct App Launch ver %s\n", QUICKSTART_VERSION);
 
441
442	return 0;
443fail_input:
444	device_remove_file(&pf_device->dev, &dev_attr_buttons);
445
446fail_dev_file2:
447	device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
448
449fail_dev_file:
450	platform_device_del(pf_device);
451
452fail_pfdev_add:
453	platform_device_put(pf_device);
454
455fail_pfdev_alloc:
456	platform_driver_unregister(&pf_driver);
457
458fail_pfdrv_reg:
459	acpi_bus_unregister_driver(&quickstart_acpi_driver);
460
461	return ret;
462}
463
464module_init(quickstart_init);
465module_exit(quickstart_exit);