Linux Audio

Check our new training course

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