aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/hotkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/hotkey.c')
-rw-r--r--drivers/acpi/hotkey.c1018
1 files changed, 1018 insertions, 0 deletions
diff --git a/drivers/acpi/hotkey.c b/drivers/acpi/hotkey.c
new file mode 100644
index 000000000000..0aef9fc449c5
--- /dev/null
+++ b/drivers/acpi/hotkey.c
@@ -0,0 +1,1018 @@
1/*
2 * hotkey.c - ACPI Hotkey Driver ($Revision:$)
3 *
4 * Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 */
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/init.h>
27#include <linux/types.h>
28#include <linux/proc_fs.h>
29#include <linux/sched.h>
30#include <linux/kmod.h>
31#include <linux/seq_file.h>
32#include <acpi/acpi_drivers.h>
33#include <acpi/acpi_bus.h>
34#include <asm/uaccess.h>
35
36#define HOTKEY_ACPI_VERSION "0.1"
37
38#define HOTKEY_PROC "hotkey"
39#define HOTKEY_EV_CONFIG "event_config"
40#define HOTKEY_PL_CONFIG "poll_config"
41#define HOTKEY_ACTION "action"
42#define HOTKEY_INFO "info"
43
44#define ACPI_HOTK_NAME "Generic Hotkey Driver"
45#define ACPI_HOTK_CLASS "Hotkey"
46#define ACPI_HOTK_DEVICE_NAME "Hotkey"
47#define ACPI_HOTK_HID "Unknown?"
48#define ACPI_HOTKEY_COMPONENT 0x20000000
49
50#define ACPI_HOTKEY_EVENT 0x1
51#define ACPI_HOTKEY_POLLING 0x2
52#define ACPI_UNDEFINED_EVENT 0xf
53
54#define MAX_CONFIG_RECORD_LEN 80
55#define MAX_NAME_PATH_LEN 80
56#define MAX_CALL_PARM 80
57
58#define IS_EVENT(e) 0xff /* ((e) & 0x40000000) */
59#define IS_POLL(e) 0xff /* (~((e) & 0x40000000)) */
60
61#define _COMPONENT ACPI_HOTKEY_COMPONENT
62ACPI_MODULE_NAME("acpi_hotkey")
63
64 MODULE_AUTHOR("luming.yu@intel.com");
65MODULE_DESCRIPTION(ACPI_HOTK_NAME);
66MODULE_LICENSE("GPL");
67
68/* standardized internal hotkey number/event */
69enum {
70 /* Video Extension event */
71 HK_EVENT_CYCLE_OUTPUT_DEVICE = 0x80,
72 HK_EVENT_OUTPUT_DEVICE_STATUS_CHANGE,
73 HK_EVENT_CYCLE_DISPLAY_OUTPUT,
74 HK_EVENT_NEXT_DISPLAY_OUTPUT,
75 HK_EVENT_PREVIOUS_DISPLAY_OUTPUT,
76 HK_EVENT_CYCLE_BRIGHTNESS,
77 HK_EVENT_INCREASE_BRIGHTNESS,
78 HK_EVENT_DECREASE_BRIGHTNESS,
79 HK_EVENT_ZERO_BRIGHTNESS,
80 HK_EVENT_DISPLAY_DEVICE_OFF,
81
82 /* Snd Card event */
83 HK_EVENT_VOLUME_MUTE,
84 HK_EVENT_VOLUME_INCLREASE,
85 HK_EVENT_VOLUME_DECREASE,
86
87 /* running state control */
88 HK_EVENT_ENTERRING_S3,
89 HK_EVENT_ENTERRING_S4,
90 HK_EVENT_ENTERRING_S5,
91};
92
93/* procdir we use */
94static struct proc_dir_entry *hotkey_proc_dir;
95static struct proc_dir_entry *hotkey_config;
96static struct proc_dir_entry *hotkey_poll_config;
97static struct proc_dir_entry *hotkey_action;
98static struct proc_dir_entry *hotkey_info;
99
100/* linkage for all type of hotkey */
101struct acpi_hotkey_link {
102 struct list_head entries;
103 int hotkey_type; /* event or polling based hotkey */
104 int hotkey_standard_num; /* standardized hotkey(event) number */
105};
106
107/* event based hotkey */
108struct acpi_event_hotkey {
109 struct acpi_hotkey_link hotkey_link;
110 int flag;
111 acpi_handle bus_handle; /* bus to install notify handler */
112 int external_hotkey_num; /* external hotkey/event number */
113 acpi_handle action_handle; /* acpi handle attached aml action method */
114 char *action_method; /* action method */
115};
116
117/*
118 * There are two ways to poll status
119 * 1. directy call read_xxx method, without any arguments passed in
120 * 2. call write_xxx method, with arguments passed in, you need
121 * the result is saved in acpi_polling_hotkey.poll_result.
122 * anthoer read command through polling interface.
123 *
124 */
125
126/* polling based hotkey */
127struct acpi_polling_hotkey {
128 struct acpi_hotkey_link hotkey_link;
129 int flag;
130 acpi_handle poll_handle; /* acpi handle attached polling method */
131 char *poll_method; /* poll method */
132 acpi_handle action_handle; /* acpi handle attached action method */
133 char *action_method; /* action method */
134 void *poll_result; /* polling_result */
135 struct proc_dir_entry *proc;
136};
137
138/* hotkey object union */
139union acpi_hotkey {
140 struct list_head entries;
141 struct acpi_hotkey_link link;
142 struct acpi_event_hotkey event_hotkey;
143 struct acpi_polling_hotkey poll_hotkey;
144};
145
146/* hotkey object list */
147struct acpi_hotkey_list {
148 struct list_head *entries;
149 int count;
150};
151
152static int auto_hotkey_add(struct acpi_device *device);
153static int auto_hotkey_remove(struct acpi_device *device, int type);
154
155static struct acpi_driver hotkey_driver = {
156 .name = ACPI_HOTK_NAME,
157 .class = ACPI_HOTK_CLASS,
158 .ids = ACPI_HOTK_HID,
159 .ops = {
160 .add = auto_hotkey_add,
161 .remove = auto_hotkey_remove,
162 },
163};
164
165static int hotkey_open_config(struct inode *inode, struct file *file);
166static ssize_t hotkey_write_config(struct file *file,
167 const char __user * buffer,
168 size_t count, loff_t * data);
169static ssize_t hotkey_write_poll_config(struct file *file,
170 const char __user * buffer,
171 size_t count, loff_t * data);
172static int hotkey_info_open_fs(struct inode *inode, struct file *file);
173static int hotkey_action_open_fs(struct inode *inode, struct file *file);
174static ssize_t hotkey_execute_aml_method(struct file *file,
175 const char __user * buffer,
176 size_t count, loff_t * data);
177static int hotkey_config_seq_show(struct seq_file *seq, void *offset);
178static int hotkey_polling_open_fs(struct inode *inode, struct file *file);
179
180/* event based config */
181static struct file_operations hotkey_config_fops = {
182 .open = hotkey_open_config,
183 .read = seq_read,
184 .write = hotkey_write_config,
185 .llseek = seq_lseek,
186 .release = single_release,
187};
188
189/* polling based config */
190static struct file_operations hotkey_poll_config_fops = {
191 .open = hotkey_open_config,
192 .read = seq_read,
193 .write = hotkey_write_poll_config,
194 .llseek = seq_lseek,
195 .release = single_release,
196};
197
198/* hotkey driver info */
199static struct file_operations hotkey_info_fops = {
200 .open = hotkey_info_open_fs,
201 .read = seq_read,
202 .llseek = seq_lseek,
203 .release = single_release,
204};
205
206/* action */
207static struct file_operations hotkey_action_fops = {
208 .open = hotkey_action_open_fs,
209 .read = seq_read,
210 .write = hotkey_execute_aml_method,
211 .llseek = seq_lseek,
212 .release = single_release,
213};
214
215/* polling results */
216static struct file_operations hotkey_polling_fops = {
217 .open = hotkey_polling_open_fs,
218 .read = seq_read,
219 .llseek = seq_lseek,
220 .release = single_release,
221};
222
223struct acpi_hotkey_list global_hotkey_list; /* link all ev or pl hotkey */
224struct list_head hotkey_entries; /* head of the list of hotkey_list */
225
226static int hotkey_info_seq_show(struct seq_file *seq, void *offset)
227{
228 ACPI_FUNCTION_TRACE("hotkey_info_seq_show");
229
230 seq_printf(seq, "Hotkey generic driver ver: %s", HOTKEY_ACPI_VERSION);
231
232 return_VALUE(0);
233}
234
235static int hotkey_info_open_fs(struct inode *inode, struct file *file)
236{
237 return single_open(file, hotkey_info_seq_show, PDE(inode)->data);
238}
239
240static char *format_result(union acpi_object *object)
241{
242 char *buf = (char *)kmalloc(sizeof(union acpi_object), GFP_KERNEL);
243
244 memset(buf, 0, sizeof(union acpi_object));
245
246 /* Now, just support integer type */
247 if (object->type == ACPI_TYPE_INTEGER)
248 sprintf(buf, "%d", (u32) object->integer.value);
249
250 return buf;
251}
252
253static int hotkey_polling_seq_show(struct seq_file *seq, void *offset)
254{
255 struct acpi_polling_hotkey *poll_hotkey =
256 (struct acpi_polling_hotkey *)seq->private;
257
258 ACPI_FUNCTION_TRACE("hotkey_polling_seq_show");
259
260 if (poll_hotkey->poll_result)
261 seq_printf(seq, "%s", format_result(poll_hotkey->poll_result));
262
263 return_VALUE(0);
264}
265
266static int hotkey_polling_open_fs(struct inode *inode, struct file *file)
267{
268 return single_open(file, hotkey_polling_seq_show, PDE(inode)->data);
269}
270
271static int hotkey_action_open_fs(struct inode *inode, struct file *file)
272{
273 return single_open(file, hotkey_info_seq_show, PDE(inode)->data);
274}
275
276/* Mapping external hotkey number to standardized hotkey event num */
277static int hotkey_get_internal_event(int event, struct acpi_hotkey_list *list)
278{
279 struct list_head *entries, *next;
280 int val = 0;
281
282 ACPI_FUNCTION_TRACE("hotkey_get_internal_event");
283
284 list_for_each_safe(entries, next, list->entries) {
285 union acpi_hotkey *key =
286 container_of(entries, union acpi_hotkey, entries);
287 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT
288 && key->event_hotkey.external_hotkey_num == event)
289 val = key->link.hotkey_standard_num;
290 else
291 val = -1;
292 }
293
294 return_VALUE(val);
295}
296
297static void
298acpi_hotkey_notify_handler(acpi_handle handle, u32 event, void *data)
299{
300 struct acpi_device *device = NULL;
301 u32 internal_event;
302
303 ACPI_FUNCTION_TRACE("acpi_hotkey_notify_handler");
304
305 if (acpi_bus_get_device(handle, &device))
306 return_VOID;
307
308 internal_event = hotkey_get_internal_event(event, &global_hotkey_list);
309 acpi_bus_generate_event(device, event, 0);
310
311 return_VOID;
312}
313
314/* Need to invent automatically hotkey add method */
315static int auto_hotkey_add(struct acpi_device *device)
316{
317 /* Implement me */
318 return 0;
319}
320
321/* Need to invent automatically hotkey remove method */
322static int auto_hotkey_remove(struct acpi_device *device, int type)
323{
324 /* Implement me */
325 return 0;
326}
327
328/* Create a proc file for each polling method */
329static int create_polling_proc(union acpi_hotkey *device)
330{
331 struct proc_dir_entry *proc;
332
333 ACPI_FUNCTION_TRACE("create_polling_proc");
334 mode_t mode = S_IFREG | S_IRUGO | S_IWUGO;
335
336 proc = create_proc_entry(device->poll_hotkey.action_method,
337 mode, hotkey_proc_dir);
338
339 if (!proc) {
340 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
341 "Hotkey: Unable to create %s entry\n",
342 device->poll_hotkey.poll_method));
343 return_VALUE(-ENODEV);
344 } else {
345 proc->proc_fops = &hotkey_polling_fops;
346 proc->owner = THIS_MODULE;
347 proc->data = device;
348 proc->uid = 0;
349 proc->gid = 0;
350 device->poll_hotkey.proc = proc;
351 }
352 return_VALUE(0);
353}
354
355static int is_valid_acpi_path(const char *pathname)
356{
357 acpi_handle handle;
358 acpi_status status;
359 ACPI_FUNCTION_TRACE("is_valid_acpi_path");
360
361 status = acpi_get_handle(NULL, (char *)pathname, &handle);
362 return_VALUE(!ACPI_FAILURE(status));
363}
364
365static int is_valid_hotkey(union acpi_hotkey *device)
366{
367 ACPI_FUNCTION_TRACE("is_valid_hotkey");
368 /* Implement valid check */
369 return_VALUE(1);
370}
371
372static int hotkey_add(union acpi_hotkey *device)
373{
374 int status = 0;
375 struct acpi_device *dev = NULL;
376
377 ACPI_FUNCTION_TRACE("hotkey_add");
378
379 if (device->link.hotkey_type == ACPI_HOTKEY_EVENT) {
380 status =
381 acpi_bus_get_device(device->event_hotkey.bus_handle, &dev);
382 if (status)
383 return_VALUE(status);
384
385 status = acpi_install_notify_handler(dev->handle,
386 ACPI_SYSTEM_NOTIFY,
387 acpi_hotkey_notify_handler,
388 device);
389 } else /* Add polling hotkey */
390 create_polling_proc(device);
391
392 global_hotkey_list.count++;
393
394 list_add_tail(&device->link.entries, global_hotkey_list.entries);
395
396 return_VALUE(status);
397}
398
399static int hotkey_remove(union acpi_hotkey *device)
400{
401 struct list_head *entries, *next;
402
403 ACPI_FUNCTION_TRACE("hotkey_remove");
404
405 list_for_each_safe(entries, next, global_hotkey_list.entries) {
406 union acpi_hotkey *key =
407 container_of(entries, union acpi_hotkey, entries);
408 if (key->link.hotkey_standard_num ==
409 device->link.hotkey_standard_num) {
410 list_del(&key->link.entries);
411 remove_proc_entry(key->poll_hotkey.action_method,
412 hotkey_proc_dir);
413 global_hotkey_list.count--;
414 break;
415 }
416 }
417 return_VALUE(0);
418}
419
420static void hotkey_update(union acpi_hotkey *key)
421{
422 struct list_head *entries, *next;
423
424 ACPI_FUNCTION_TRACE("hotkey_update");
425
426 list_for_each_safe(entries, next, global_hotkey_list.entries) {
427 union acpi_hotkey *key =
428 container_of(entries, union acpi_hotkey, entries);
429 if (key->link.hotkey_standard_num ==
430 key->link.hotkey_standard_num) {
431 key->event_hotkey.bus_handle =
432 key->event_hotkey.bus_handle;
433 key->event_hotkey.external_hotkey_num =
434 key->event_hotkey.external_hotkey_num;
435 key->event_hotkey.action_handle =
436 key->event_hotkey.action_handle;
437 key->event_hotkey.action_method =
438 key->event_hotkey.action_method;
439 break;
440 }
441 }
442
443 return_VOID;
444}
445
446static void free_hotkey_device(union acpi_hotkey *key)
447{
448 struct acpi_device *dev;
449 int status;
450
451 ACPI_FUNCTION_TRACE("free_hotkey_device");
452
453 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) {
454 status =
455 acpi_bus_get_device(key->event_hotkey.bus_handle, &dev);
456 if (dev->handle)
457 acpi_remove_notify_handler(dev->handle,
458 ACPI_SYSTEM_NOTIFY,
459 acpi_hotkey_notify_handler);
460 } else
461 remove_proc_entry(key->poll_hotkey.action_method,
462 hotkey_proc_dir);
463 kfree(key);
464 return_VOID;
465}
466
467static int
468init_hotkey_device(union acpi_hotkey *key, char *bus_str, char *action_str,
469 char *method, int std_num, int external_num)
470{
471 ACPI_FUNCTION_TRACE("init_hotkey_device");
472
473 key->link.hotkey_type = ACPI_HOTKEY_EVENT;
474 key->link.hotkey_standard_num = std_num;
475 key->event_hotkey.flag = 0;
476 if (is_valid_acpi_path(bus_str))
477 acpi_get_handle((acpi_handle) 0,
478 bus_str, &(key->event_hotkey.bus_handle));
479 else
480 return_VALUE(-ENODEV);
481 key->event_hotkey.external_hotkey_num = external_num;
482 if (is_valid_acpi_path(action_str))
483 acpi_get_handle((acpi_handle) 0,
484 action_str, &(key->event_hotkey.action_handle));
485 key->event_hotkey.action_method = kmalloc(sizeof(method), GFP_KERNEL);
486 strcpy(key->event_hotkey.action_method, method);
487
488 return_VALUE(!is_valid_hotkey(key));
489}
490
491static int
492init_poll_hotkey_device(union acpi_hotkey *key,
493 char *poll_str,
494 char *poll_method,
495 char *action_str, char *action_method, int std_num)
496{
497 ACPI_FUNCTION_TRACE("init_poll_hotkey_device");
498
499 key->link.hotkey_type = ACPI_HOTKEY_POLLING;
500 key->link.hotkey_standard_num = std_num;
501 key->poll_hotkey.flag = 0;
502 if (is_valid_acpi_path(poll_str))
503 acpi_get_handle((acpi_handle) 0,
504 poll_str, &(key->poll_hotkey.poll_handle));
505 else
506 return_VALUE(-ENODEV);
507 key->poll_hotkey.poll_method = poll_method;
508 if (is_valid_acpi_path(action_str))
509 acpi_get_handle((acpi_handle) 0,
510 action_str, &(key->poll_hotkey.action_handle));
511 key->poll_hotkey.action_method =
512 kmalloc(sizeof(action_method), GFP_KERNEL);
513 strcpy(key->poll_hotkey.action_method, action_method);
514 key->poll_hotkey.poll_result =
515 (union acpi_object *)kmalloc(sizeof(union acpi_object), GFP_KERNEL);
516 return_VALUE(is_valid_hotkey(key));
517}
518
519static int check_hotkey_valid(union acpi_hotkey *key,
520 struct acpi_hotkey_list *list)
521{
522 ACPI_FUNCTION_TRACE("check_hotkey_valid");
523 return_VALUE(0);
524}
525
526static int hotkey_open_config(struct inode *inode, struct file *file)
527{
528 ACPI_FUNCTION_TRACE("hotkey_open_config");
529 return_VALUE(single_open
530 (file, hotkey_config_seq_show, PDE(inode)->data));
531}
532
533static int hotkey_config_seq_show(struct seq_file *seq, void *offset)
534{
535 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list;
536 struct list_head *entries, *next;
537 char bus_name[ACPI_PATHNAME_MAX] = { 0 };
538 char action_name[ACPI_PATHNAME_MAX] = { 0 };
539 struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name };
540 struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name };
541
542 ACPI_FUNCTION_TRACE(("hotkey_config_seq_show"));
543
544 if (!hotkey_list)
545 goto end;
546
547 list_for_each_safe(entries, next, hotkey_list->entries) {
548 union acpi_hotkey *key =
549 container_of(entries, union acpi_hotkey, entries);
550 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) {
551 acpi_get_name(key->event_hotkey.bus_handle,
552 ACPI_NAME_TYPE_MAX, &bus);
553 acpi_get_name(key->event_hotkey.action_handle,
554 ACPI_NAME_TYPE_MAX, &act);
555 seq_printf(seq, "%s:%s:%s:%d:%d", bus_name,
556 action_name,
557 key->event_hotkey.action_method,
558 key->link.hotkey_standard_num,
559 key->event_hotkey.external_hotkey_num);
560 } /* ACPI_HOTKEY_POLLING */
561 else {
562 acpi_get_name(key->poll_hotkey.poll_handle,
563 ACPI_NAME_TYPE_MAX, &bus);
564 acpi_get_name(key->poll_hotkey.action_handle,
565 ACPI_NAME_TYPE_MAX, &act);
566 seq_printf(seq, "%s:%s:%s:%s:%d", bus_name,
567 key->poll_hotkey.poll_method,
568 action_name,
569 key->poll_hotkey.action_method,
570 key->link.hotkey_standard_num);
571 }
572 }
573 seq_puts(seq, "\n");
574 end:
575 return_VALUE(0);
576}
577
578static int
579get_parms(char *config_record,
580 int *cmd,
581 char *bus_handle,
582 char *bus_method,
583 char *action_handle,
584 char *method, int *internal_event_num, int *external_event_num)
585{
586 char *tmp, *tmp1;
587 ACPI_FUNCTION_TRACE(("get_parms"));
588
589 sscanf(config_record, "%d", cmd);
590
591 tmp = strchr(config_record, ':');
592 tmp++;
593 tmp1 = strchr(tmp, ':');
594 strncpy(bus_handle, tmp, tmp1 - tmp);
595 bus_handle[tmp1 - tmp] = 0;
596
597 tmp = tmp1;
598 tmp++;
599 tmp1 = strchr(tmp, ':');
600 strncpy(bus_method, tmp, tmp1 - tmp);
601 bus_method[tmp1 - tmp] = 0;
602
603 tmp = tmp1;
604 tmp++;
605 tmp1 = strchr(tmp, ':');
606 strncpy(action_handle, tmp, tmp1 - tmp);
607 action_handle[tmp1 - tmp] = 0;
608
609 tmp = tmp1;
610 tmp++;
611 tmp1 = strchr(tmp, ':');
612 strncpy(method, tmp, tmp1 - tmp);
613 method[tmp1 - tmp] = 0;
614
615 sscanf(tmp1 + 1, "%d:%d", internal_event_num, external_event_num);
616 return_VALUE(6);
617}
618
619/* count is length for one input record */
620static ssize_t hotkey_write_config(struct file *file,
621 const char __user * buffer,
622 size_t count, loff_t * data)
623{
624 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list;
625 char config_record[MAX_CONFIG_RECORD_LEN];
626 char bus_handle[MAX_NAME_PATH_LEN];
627 char bus_method[MAX_NAME_PATH_LEN];
628 char action_handle[MAX_NAME_PATH_LEN];
629 char method[20];
630 int cmd, internal_event_num, external_event_num;
631 int ret = 0;
632 union acpi_hotkey *key = NULL;
633
634 ACPI_FUNCTION_TRACE(("hotkey_write_config"));
635
636 if (!hotkey_list || count > MAX_CONFIG_RECORD_LEN) {
637 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid arguments\n"));
638 return_VALUE(-EINVAL);
639 }
640
641 if (copy_from_user(config_record, buffer, count)) {
642 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data \n"));
643 return_VALUE(-EINVAL);
644 }
645 config_record[count] = '\0';
646
647 ret = get_parms(config_record,
648 &cmd,
649 bus_handle,
650 bus_method,
651 action_handle,
652 method, &internal_event_num, &external_event_num);
653 if (ret != 6) {
654 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
655 "Invalid data format ret=%d\n", ret));
656 return_VALUE(-EINVAL);
657 }
658
659 key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
660 ret = init_hotkey_device(key, bus_handle, action_handle, method,
661 internal_event_num, external_event_num);
662
663 if (ret || check_hotkey_valid(key, hotkey_list)) {
664 kfree(key);
665 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid hotkey \n"));
666 return_VALUE(-EINVAL);
667 }
668 switch (cmd) {
669 case 0:
670 hotkey_add(key);
671 break;
672 case 1:
673 hotkey_remove(key);
674 free_hotkey_device(key);
675 break;
676 case 2:
677 hotkey_update(key);
678 break;
679 default:
680 break;
681 }
682 return_VALUE(count);
683}
684
685/* count is length for one input record */
686static ssize_t hotkey_write_poll_config(struct file *file,
687 const char __user * buffer,
688 size_t count, loff_t * data)
689{
690 struct seq_file *m = (struct seq_file *)file->private_data;
691 struct acpi_hotkey_list *hotkey_list =
692 (struct acpi_hotkey_list *)m->private;
693
694 char config_record[MAX_CONFIG_RECORD_LEN];
695 char polling_handle[MAX_NAME_PATH_LEN];
696 char action_handle[MAX_NAME_PATH_LEN];
697 char poll_method[20], action_method[20];
698 int ret, internal_event_num, cmd, external_event_num;
699 union acpi_hotkey *key = NULL;
700
701 ACPI_FUNCTION_TRACE("hotkey_write_poll_config");
702
703 if (!hotkey_list || count > MAX_CONFIG_RECORD_LEN) {
704 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid arguments\n"));
705 return_VALUE(-EINVAL);
706 }
707
708 if (copy_from_user(config_record, buffer, count)) {
709 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data \n"));
710 return_VALUE(-EINVAL);
711 }
712 config_record[count] = '\0';
713
714 ret = get_parms(config_record,
715 &cmd,
716 polling_handle,
717 poll_method,
718 action_handle,
719 action_method,
720 &internal_event_num, &external_event_num);
721
722 if (ret != 6) {
723 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n"));
724 return_VALUE(-EINVAL);
725 }
726
727 key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
728 ret = init_poll_hotkey_device(key, polling_handle, poll_method,
729 action_handle, action_method,
730 internal_event_num);
731 if (ret || check_hotkey_valid(key, hotkey_list)) {
732 kfree(key);
733 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid hotkey \n"));
734 return_VALUE(-EINVAL);
735 }
736 switch (cmd) {
737 case 0:
738 hotkey_add(key);
739 break;
740 case 1:
741 hotkey_remove(key);
742 break;
743 case 2:
744 hotkey_update(key);
745 break;
746 default:
747 break;
748 }
749 return_VALUE(count);
750}
751
752/*
753 * This function evaluates an ACPI method, given an int as parameter, the
754 * method is searched within the scope of the handle, can be NULL. The output
755 * of the method is written is output, which can also be NULL
756 *
757 * returns 1 if write is successful, 0 else.
758 */
759static int write_acpi_int(acpi_handle handle, const char *method, int val,
760 struct acpi_buffer *output)
761{
762 struct acpi_object_list params; /* list of input parameters (an int here) */
763 union acpi_object in_obj; /* the only param we use */
764 acpi_status status;
765
766 ACPI_FUNCTION_TRACE("write_acpi_int");
767 params.count = 1;
768 params.pointer = &in_obj;
769 in_obj.type = ACPI_TYPE_INTEGER;
770 in_obj.integer.value = val;
771
772 status = acpi_evaluate_object(handle, (char *)method, &params, output);
773
774 return_VALUE(status == AE_OK);
775}
776
777static int read_acpi_int(acpi_handle handle, const char *method, int *val)
778{
779 struct acpi_buffer output;
780 union acpi_object out_obj;
781 acpi_status status;
782
783 ACPI_FUNCTION_TRACE("read_acpi_int");
784 output.length = sizeof(out_obj);
785 output.pointer = &out_obj;
786
787 status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
788 *val = out_obj.integer.value;
789 return_VALUE((status == AE_OK)
790 && (out_obj.type == ACPI_TYPE_INTEGER));
791}
792
793static acpi_handle
794get_handle_from_hotkeylist(struct acpi_hotkey_list *hotkey_list, int event_num)
795{
796 struct list_head *entries, *next;
797
798 list_for_each_safe(entries, next, hotkey_list->entries) {
799 union acpi_hotkey *key =
800 container_of(entries, union acpi_hotkey, entries);
801 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT
802 && key->link.hotkey_standard_num == event_num) {
803 return (key->event_hotkey.action_handle);
804 }
805 }
806 return (NULL);
807}
808
809static
810char *get_method_from_hotkeylist(struct acpi_hotkey_list *hotkey_list,
811 int event_num)
812{
813 struct list_head *entries, *next;
814
815 list_for_each_safe(entries, next, hotkey_list->entries) {
816 union acpi_hotkey *key =
817 container_of(entries, union acpi_hotkey, entries);
818
819 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT &&
820 key->link.hotkey_standard_num == event_num)
821 return (key->event_hotkey.action_method);
822 }
823 return (NULL);
824}
825
826static struct acpi_polling_hotkey *get_hotkey_by_event(struct
827 acpi_hotkey_list
828 *hotkey_list, int event)
829{
830 struct list_head *entries, *next;
831
832 list_for_each_safe(entries, next, hotkey_list->entries) {
833 union acpi_hotkey *key =
834 container_of(entries, union acpi_hotkey, entries);
835 if (key->link.hotkey_type == ACPI_HOTKEY_POLLING
836 && key->link.hotkey_standard_num == event) {
837 return (&key->poll_hotkey);
838 }
839 }
840 return (NULL);
841}
842
843/*
844 * user call AML method interface:
845 * Call convention:
846 * echo "event_num: arg type : value"
847 * example: echo "1:1:30" > /proc/acpi/action
848 * Just support 1 integer arg passing to AML method
849 */
850
851static ssize_t hotkey_execute_aml_method(struct file *file,
852 const char __user * buffer,
853 size_t count, loff_t * data)
854{
855 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list;
856 char arg[MAX_CALL_PARM];
857 int event, type, value;
858
859 char *method;
860 acpi_handle handle;
861
862 ACPI_FUNCTION_TRACE("hotkey_execte_aml_method");
863
864 if (!hotkey_list || count > MAX_CALL_PARM) {
865 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 1"));
866 return_VALUE(-EINVAL);
867 }
868
869 if (copy_from_user(arg, buffer, count)) {
870 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 2"));
871 return_VALUE(-EINVAL);
872 }
873
874 arg[count] = '\0';
875
876 if (sscanf(arg, "%d:%d:%d", &event, &type, &value) != 3) {
877 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 3"));
878 return_VALUE(-EINVAL);
879 }
880
881 if (type == ACPI_TYPE_INTEGER) {
882 handle = get_handle_from_hotkeylist(hotkey_list, event);
883 method = (char *)get_method_from_hotkeylist(hotkey_list, event);
884 if (IS_EVENT(event))
885 write_acpi_int(handle, method, value, NULL);
886 else if (IS_POLL(event)) {
887 struct acpi_polling_hotkey *key;
888 key = (struct acpi_polling_hotkey *)
889 get_hotkey_by_event(hotkey_list, event);
890 read_acpi_int(handle, method, key->poll_result);
891 }
892 } else {
893 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Not supported"));
894 return_VALUE(-EINVAL);
895 }
896
897 return_VALUE(count);
898}
899
900static int __init hotkey_init(void)
901{
902 int result;
903 mode_t mode = S_IFREG | S_IRUGO | S_IWUGO;
904
905 ACPI_FUNCTION_TRACE("hotkey_init");
906
907 if (acpi_disabled)
908 return -ENODEV;
909
910 if (acpi_specific_hotkey_enabled) {
911 printk("Using specific hotkey driver\n");
912 return -ENODEV;
913 }
914
915 hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir);
916 if (!hotkey_proc_dir) {
917 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
918 "Hotkey: Unable to create %s entry\n",
919 HOTKEY_PROC));
920 return (-ENODEV);
921 }
922 hotkey_proc_dir->owner = THIS_MODULE;
923
924 hotkey_config =
925 create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir);
926 if (!hotkey_config) {
927 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
928 "Hotkey: Unable to create %s entry\n",
929 HOTKEY_EV_CONFIG));
930 return (-ENODEV);
931 } else {
932 hotkey_config->proc_fops = &hotkey_config_fops;
933 hotkey_config->data = &global_hotkey_list;
934 hotkey_config->owner = THIS_MODULE;
935 hotkey_config->uid = 0;
936 hotkey_config->gid = 0;
937 }
938
939 hotkey_poll_config =
940 create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir);
941 if (!hotkey_poll_config) {
942 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
943 "Hotkey: Unable to create %s entry\n",
944 HOTKEY_EV_CONFIG));
945 return (-ENODEV);
946 } else {
947 hotkey_poll_config->proc_fops = &hotkey_poll_config_fops;
948 hotkey_poll_config->data = &global_hotkey_list;
949 hotkey_poll_config->owner = THIS_MODULE;
950 hotkey_poll_config->uid = 0;
951 hotkey_poll_config->gid = 0;
952 }
953
954 hotkey_action = create_proc_entry(HOTKEY_ACTION, mode, hotkey_proc_dir);
955 if (!hotkey_action) {
956 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
957 "Hotkey: Unable to create %s entry\n",
958 HOTKEY_ACTION));
959 return (-ENODEV);
960 } else {
961 hotkey_action->proc_fops = &hotkey_action_fops;
962 hotkey_action->owner = THIS_MODULE;
963 hotkey_action->uid = 0;
964 hotkey_action->gid = 0;
965 }
966
967 hotkey_info = create_proc_entry(HOTKEY_INFO, mode, hotkey_proc_dir);
968 if (!hotkey_info) {
969 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
970 "Hotkey: Unable to create %s entry\n",
971 HOTKEY_INFO));
972 return (-ENODEV);
973 } else {
974 hotkey_info->proc_fops = &hotkey_info_fops;
975 hotkey_info->owner = THIS_MODULE;
976 hotkey_info->uid = 0;
977 hotkey_info->gid = 0;
978 }
979
980 result = acpi_bus_register_driver(&hotkey_driver);
981 if (result < 0) {
982 remove_proc_entry(HOTKEY_PROC, acpi_root_dir);
983 return (-ENODEV);
984 }
985 global_hotkey_list.count = 0;
986 global_hotkey_list.entries = &hotkey_entries;
987
988 INIT_LIST_HEAD(&hotkey_entries);
989
990 return (0);
991}
992
993static void __exit hotkey_exit(void)
994{
995 struct list_head *entries, *next;
996
997 ACPI_FUNCTION_TRACE("hotkey_remove");
998
999 list_for_each_safe(entries, next, global_hotkey_list.entries) {
1000 union acpi_hotkey *key =
1001 container_of(entries, union acpi_hotkey, entries);
1002
1003 acpi_os_wait_events_complete(NULL);
1004 list_del(&key->link.entries);
1005 global_hotkey_list.count--;
1006 free_hotkey_device(key);
1007 }
1008 acpi_bus_unregister_driver(&hotkey_driver);
1009 remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir);
1010 remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir);
1011 remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir);
1012 remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir);
1013 remove_proc_entry(HOTKEY_PROC, acpi_root_dir);
1014 return;
1015}
1016
1017module_init(hotkey_init);
1018module_exit(hotkey_exit);