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