Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  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);