]> err.no Git - linux-2.6/blob - drivers/acpi/hotkey.c
[ACPI] generic Hot Key support
[linux-2.6] / drivers / acpi / hotkey.c
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
62 ACPI_MODULE_NAME("acpi_hotkey")
63
64     MODULE_AUTHOR("luming.yu@intel.com");
65 MODULE_DESCRIPTION(ACPI_HOTK_NAME);
66 MODULE_LICENSE("GPL");
67
68 /*  standardized internal hotkey number/event  */
69 enum {
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 */
94 static struct proc_dir_entry *hotkey_proc_dir;
95 static struct proc_dir_entry *hotkey_config;
96 static struct proc_dir_entry *hotkey_poll_config;
97 static struct proc_dir_entry *hotkey_action;
98 static struct proc_dir_entry *hotkey_info;
99
100 /* linkage for all type of hotkey */
101 struct 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 */
108 struct 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 */
127 struct 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 */
139 union 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 */
147 struct acpi_hotkey_list {
148         struct list_head *entries;
149         int count;
150 };
151
152 static int auto_hotkey_add(struct acpi_device *device);
153 static int auto_hotkey_remove(struct acpi_device *device, int type);
154
155 static 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
165 static int hotkey_open_config(struct inode *inode, struct file *file);
166 static ssize_t hotkey_write_config(struct file *file,
167                                    const char __user * buffer,
168                                    size_t count, loff_t * data);
169 static ssize_t hotkey_write_poll_config(struct file *file,
170                                         const char __user * buffer,
171                                         size_t count, loff_t * data);
172 static int hotkey_info_open_fs(struct inode *inode, struct file *file);
173 static int hotkey_action_open_fs(struct inode *inode, struct file *file);
174 static ssize_t hotkey_execute_aml_method(struct file *file,
175                                          const char __user * buffer,
176                                          size_t count, loff_t * data);
177 static int hotkey_config_seq_show(struct seq_file *seq, void *offset);
178 static int hotkey_polling_open_fs(struct inode *inode, struct file *file);
179
180 /* event based config */
181 static 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 */
190 static 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 */
199 static 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 */
207 static 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 */
216 static 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
223 struct acpi_hotkey_list global_hotkey_list;     /* link all ev or pl hotkey  */
224 struct list_head hotkey_entries;        /* head of the list of hotkey_list */
225
226 static 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
235 static 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
240 static 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
253 static 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
266 static 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
271 static 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 */
277 static 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
297 static void
298 acpi_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 */
315 static 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 */
322 static 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 */
329 static 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
355 static 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
365 static 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
372 static 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
399 static 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
420 static 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
446 static 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
467 static int
468 init_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
491 static int
492 init_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
519 static 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
526 static 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
533 static 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
578 static int
579 get_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 */
620 static 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 */
686 static 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  */
759 static 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
777 static 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
793 static acpi_handle
794 get_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
809 static
810 char *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
826 static 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
851 static 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
900 static 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
993 static 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
1017 module_init(hotkey_init);
1018 module_exit(hotkey_exit);